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