15003: Merge branch 'master'
[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
173         Driver           string
174         DriverParameters json.RawMessage
175 }
176
177 type InstanceTypeMap map[string]InstanceType
178
179 var errDuplicateInstanceTypeName = errors.New("duplicate instance type name")
180
181 // UnmarshalJSON handles old config files that provide an array of
182 // instance types instead of a hash.
183 func (it *InstanceTypeMap) UnmarshalJSON(data []byte) error {
184         if len(data) > 0 && data[0] == '[' {
185                 var arr []InstanceType
186                 err := json.Unmarshal(data, &arr)
187                 if err != nil {
188                         return err
189                 }
190                 if len(arr) == 0 {
191                         *it = nil
192                         return nil
193                 }
194                 *it = make(map[string]InstanceType, len(arr))
195                 for _, t := range arr {
196                         if _, ok := (*it)[t.Name]; ok {
197                                 return errDuplicateInstanceTypeName
198                         }
199                         if t.ProviderType == "" {
200                                 t.ProviderType = t.Name
201                         }
202                         if t.Scratch == 0 {
203                                 t.Scratch = t.IncludedScratch + t.AddedScratch
204                         } else if t.AddedScratch == 0 {
205                                 t.AddedScratch = t.Scratch - t.IncludedScratch
206                         } else if t.IncludedScratch == 0 {
207                                 t.IncludedScratch = t.Scratch - t.AddedScratch
208                         }
209
210                         if t.Scratch != (t.IncludedScratch + t.AddedScratch) {
211                                 return fmt.Errorf("%v: Scratch != (IncludedScratch + AddedScratch)", t.Name)
212                         }
213                         (*it)[t.Name] = t
214                 }
215                 return nil
216         }
217         var hash map[string]InstanceType
218         err := json.Unmarshal(data, &hash)
219         if err != nil {
220                 return err
221         }
222         // Fill in Name field (and ProviderType field, if not
223         // specified) using hash key.
224         *it = InstanceTypeMap(hash)
225         for name, t := range *it {
226                 t.Name = name
227                 if t.ProviderType == "" {
228                         t.ProviderType = name
229                 }
230                 (*it)[name] = t
231         }
232         return nil
233 }
234
235 type ServiceName string
236
237 const (
238         ServiceNameRailsAPI      ServiceName = "arvados-api-server"
239         ServiceNameController    ServiceName = "arvados-controller"
240         ServiceNameDispatchCloud ServiceName = "arvados-dispatch-cloud"
241         ServiceNameHealth        ServiceName = "arvados-health"
242         ServiceNameNodemanager   ServiceName = "arvados-node-manager"
243         ServiceNameWorkbench1    ServiceName = "arvados-workbench1"
244         ServiceNameWorkbench2    ServiceName = "arvados-workbench2"
245         ServiceNameWebsocket     ServiceName = "arvados-ws"
246         ServiceNameKeepbalance   ServiceName = "keep-balance"
247         ServiceNameKeepweb       ServiceName = "keep-web"
248         ServiceNameKeepproxy     ServiceName = "keepproxy"
249         ServiceNameKeepstore     ServiceName = "keepstore"
250 )
251
252 // Map returns all services as a map, suitable for iterating over all
253 // services or looking up a service by name.
254 func (svcs Services) Map() map[ServiceName]Service {
255         return map[ServiceName]Service{
256                 ServiceNameRailsAPI:      svcs.RailsAPI,
257                 ServiceNameController:    svcs.Controller,
258                 ServiceNameDispatchCloud: svcs.DispatchCloud,
259                 ServiceNameHealth:        svcs.Health,
260                 ServiceNameNodemanager:   svcs.Nodemanager,
261                 ServiceNameWorkbench1:    svcs.Workbench1,
262                 ServiceNameWorkbench2:    svcs.Workbench2,
263                 ServiceNameWebsocket:     svcs.Websocket,
264                 ServiceNameKeepbalance:   svcs.Keepbalance,
265                 ServiceNameKeepweb:       svcs.WebDAV,
266                 ServiceNameKeepproxy:     svcs.Keepproxy,
267                 ServiceNameKeepstore:     svcs.Keepstore,
268         }
269 }
270
271 type TLS struct {
272         Certificate string
273         Key         string
274         Insecure    bool
275 }