14931: Add configurable prefix for built-in tags.
[arvados.git] / sdk / go / arvados / config.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: Apache-2.0
4
5 package arvados
6
7 import (
8         "encoding/json"
9         "errors"
10         "fmt"
11         "net/url"
12
13         "git.curoverse.com/arvados.git/sdk/go/config"
14 )
15
16 const DefaultConfigFile = "/etc/arvados/config.yml"
17
18 type Config struct {
19         Clusters map[string]Cluster
20 }
21
22 // GetConfig returns the current system config, loading it from
23 // configFile if needed.
24 func GetConfig(configFile string) (*Config, error) {
25         var cfg Config
26         err := config.LoadFile(&cfg, configFile)
27         return &cfg, err
28 }
29
30 // GetCluster returns the cluster ID and config for the given
31 // cluster, or the default/only configured cluster if clusterID is "".
32 func (sc *Config) GetCluster(clusterID string) (*Cluster, error) {
33         if clusterID == "" {
34                 if len(sc.Clusters) == 0 {
35                         return nil, fmt.Errorf("no clusters configured")
36                 } else if len(sc.Clusters) > 1 {
37                         return nil, fmt.Errorf("multiple clusters configured, cannot choose")
38                 } else {
39                         for id, cc := range sc.Clusters {
40                                 cc.ClusterID = id
41                                 return &cc, nil
42                         }
43                 }
44         }
45         if cc, ok := sc.Clusters[clusterID]; !ok {
46                 return nil, fmt.Errorf("cluster %q is not configured", clusterID)
47         } else {
48                 cc.ClusterID = clusterID
49                 return &cc, nil
50         }
51 }
52
53 type API struct {
54         MaxItemsPerResponse     int
55         MaxRequestAmplification int
56         RequestTimeout          Duration
57 }
58
59 type Cluster struct {
60         ClusterID       string `json:"-"`
61         ManagementToken string
62         SystemRootToken string
63         Services        Services
64         InstanceTypes   InstanceTypeMap
65         Containers      ContainersConfig
66         RemoteClusters  map[string]RemoteCluster
67         PostgreSQL      PostgreSQL
68         API             API
69         SystemLogs      SystemLogs
70         TLS             TLS
71 }
72
73 type Services struct {
74         Controller    Service
75         DispatchCloud Service
76         Health        Service
77         Keepbalance   Service
78         Keepproxy     Service
79         Keepstore     Service
80         Nodemanager   Service
81         RailsAPI      Service
82         WebDAV        Service
83         Websocket     Service
84         Workbench1    Service
85         Workbench2    Service
86 }
87
88 type Service struct {
89         InternalURLs map[URL]ServiceInstance `json:",omitempty"`
90         ExternalURL  URL
91 }
92
93 // URL is a url.URL that is also usable as a JSON key/value.
94 type URL url.URL
95
96 // UnmarshalText implements encoding.TextUnmarshaler so URL can be
97 // used as a JSON key/value.
98 func (su *URL) UnmarshalText(text []byte) error {
99         u, err := url.Parse(string(text))
100         if err == nil {
101                 *su = URL(*u)
102         }
103         return err
104 }
105
106 func (su URL) MarshalText() ([]byte, error) {
107         return []byte(fmt.Sprintf("%s", (*url.URL)(&su).String())), nil
108 }
109
110 type ServiceInstance struct{}
111
112 type SystemLogs struct {
113         LogLevel                string
114         Format                  string
115         MaxRequestLogParamsSize int
116 }
117
118 type PostgreSQL struct {
119         Connection     PostgreSQLConnection
120         ConnectionPool int
121 }
122
123 type PostgreSQLConnection map[string]string
124
125 type RemoteCluster struct {
126         // API endpoint host or host:port; default is {id}.arvadosapi.com
127         Host string
128         // Perform a proxy request when a local client requests an
129         // object belonging to this remote.
130         Proxy bool
131         // Scheme, default "https". Can be set to "http" for testing.
132         Scheme string
133         // Disable TLS verify. Can be set to true for testing.
134         Insecure bool
135 }
136
137 type InstanceType struct {
138         Name            string
139         ProviderType    string
140         VCPUs           int
141         RAM             ByteSize
142         Scratch         ByteSize
143         IncludedScratch ByteSize
144         AddedScratch    ByteSize
145         Price           float64
146         Preemptible     bool
147 }
148
149 type ContainersConfig struct {
150         CloudVMs           CloudVMsConfig
151         DispatchPrivateKey string
152         StaleLockTimeout   Duration
153 }
154
155 type CloudVMsConfig struct {
156         Enable bool
157
158         BootProbeCommand     string
159         ImageID              string
160         MaxCloudOpsPerSecond int
161         MaxProbesPerSecond   int
162         PollInterval         Duration
163         ProbeInterval        Duration
164         SSHPort              string
165         SyncInterval         Duration
166         TimeoutBooting       Duration
167         TimeoutIdle          Duration
168         TimeoutProbe         Duration
169         TimeoutShutdown      Duration
170         TimeoutSignal        Duration
171         TimeoutTERM          Duration
172         ResourceTags         map[string]string
173         TagKeyPrefix         string
174
175         Driver           string
176         DriverParameters json.RawMessage
177 }
178
179 type InstanceTypeMap map[string]InstanceType
180
181 var errDuplicateInstanceTypeName = errors.New("duplicate instance type name")
182
183 // UnmarshalJSON handles old config files that provide an array of
184 // instance types instead of a hash.
185 func (it *InstanceTypeMap) UnmarshalJSON(data []byte) error {
186         if len(data) > 0 && data[0] == '[' {
187                 var arr []InstanceType
188                 err := json.Unmarshal(data, &arr)
189                 if err != nil {
190                         return err
191                 }
192                 if len(arr) == 0 {
193                         *it = nil
194                         return nil
195                 }
196                 *it = make(map[string]InstanceType, len(arr))
197                 for _, t := range arr {
198                         if _, ok := (*it)[t.Name]; ok {
199                                 return errDuplicateInstanceTypeName
200                         }
201                         if t.ProviderType == "" {
202                                 t.ProviderType = t.Name
203                         }
204                         if t.Scratch == 0 {
205                                 t.Scratch = t.IncludedScratch + t.AddedScratch
206                         } else if t.AddedScratch == 0 {
207                                 t.AddedScratch = t.Scratch - t.IncludedScratch
208                         } else if t.IncludedScratch == 0 {
209                                 t.IncludedScratch = t.Scratch - t.AddedScratch
210                         }
211
212                         if t.Scratch != (t.IncludedScratch + t.AddedScratch) {
213                                 return fmt.Errorf("%v: Scratch != (IncludedScratch + AddedScratch)", t.Name)
214                         }
215                         (*it)[t.Name] = t
216                 }
217                 return nil
218         }
219         var hash map[string]InstanceType
220         err := json.Unmarshal(data, &hash)
221         if err != nil {
222                 return err
223         }
224         // Fill in Name field (and ProviderType field, if not
225         // specified) using hash key.
226         *it = InstanceTypeMap(hash)
227         for name, t := range *it {
228                 t.Name = name
229                 if t.ProviderType == "" {
230                         t.ProviderType = name
231                 }
232                 (*it)[name] = t
233         }
234         return nil
235 }
236
237 type ServiceName string
238
239 const (
240         ServiceNameRailsAPI      ServiceName = "arvados-api-server"
241         ServiceNameController    ServiceName = "arvados-controller"
242         ServiceNameDispatchCloud ServiceName = "arvados-dispatch-cloud"
243         ServiceNameHealth        ServiceName = "arvados-health"
244         ServiceNameNodemanager   ServiceName = "arvados-node-manager"
245         ServiceNameWorkbench1    ServiceName = "arvados-workbench1"
246         ServiceNameWorkbench2    ServiceName = "arvados-workbench2"
247         ServiceNameWebsocket     ServiceName = "arvados-ws"
248         ServiceNameKeepbalance   ServiceName = "keep-balance"
249         ServiceNameKeepweb       ServiceName = "keep-web"
250         ServiceNameKeepproxy     ServiceName = "keepproxy"
251         ServiceNameKeepstore     ServiceName = "keepstore"
252 )
253
254 // Map returns all services as a map, suitable for iterating over all
255 // services or looking up a service by name.
256 func (svcs Services) Map() map[ServiceName]Service {
257         return map[ServiceName]Service{
258                 ServiceNameRailsAPI:      svcs.RailsAPI,
259                 ServiceNameController:    svcs.Controller,
260                 ServiceNameDispatchCloud: svcs.DispatchCloud,
261                 ServiceNameHealth:        svcs.Health,
262                 ServiceNameNodemanager:   svcs.Nodemanager,
263                 ServiceNameWorkbench1:    svcs.Workbench1,
264                 ServiceNameWorkbench2:    svcs.Workbench2,
265                 ServiceNameWebsocket:     svcs.Websocket,
266                 ServiceNameKeepbalance:   svcs.Keepbalance,
267                 ServiceNameKeepweb:       svcs.WebDAV,
268                 ServiceNameKeepproxy:     svcs.Keepproxy,
269                 ServiceNameKeepstore:     svcs.Keepstore,
270         }
271 }
272
273 type TLS struct {
274         Certificate string
275         Key         string
276         Insecure    bool
277 }