1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
15 "git.curoverse.com/arvados.git/sdk/go/arvados"
16 "github.com/prometheus/client_golang/prometheus"
17 "github.com/sirupsen/logrus"
31 BlobSignatureTTL arvados.Duration
32 BlobSigningKeyFile string
33 RequireSignatures bool
34 SystemAuthTokenFile string
36 TrashLifetime arvados.Duration
37 TrashCheckInterval arvados.Duration
41 TLSCertificateFile string
47 systemAuthToken string
48 debugLogf func(string, ...interface{})
50 ManagementToken string
54 theConfig = DefaultConfig()
55 formatter = map[string]logrus.Formatter{
56 "text": &logrus.TextFormatter{
58 TimestampFormat: rfc3339NanoFixed,
60 "json": &logrus.JSONFormatter{
61 TimestampFormat: rfc3339NanoFixed,
64 log = logrus.StandardLogger()
67 const rfc3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
69 // DefaultConfig returns the default configuration.
70 func DefaultConfig() *Config {
75 RequireSignatures: true,
76 BlobSignatureTTL: arvados.Duration(14 * 24 * time.Hour),
77 TrashLifetime: arvados.Duration(14 * 24 * time.Hour),
78 TrashCheckInterval: arvados.Duration(24 * time.Hour),
83 // Start should be called exactly once: after setting all public
84 // fields, and before using the config.
85 func (cfg *Config) Start(reg *prometheus.Registry) error {
87 log.Level = logrus.DebugLevel
88 cfg.debugLogf = log.Printf
89 cfg.debugLogf("debugging enabled")
91 log.Level = logrus.InfoLevel
92 cfg.debugLogf = func(string, ...interface{}) {}
95 f := formatter[strings.ToLower(cfg.LogFormat)]
97 return fmt.Errorf(`unsupported log format %q (try "text" or "json")`, cfg.LogFormat)
101 if cfg.MaxBuffers < 0 {
102 return fmt.Errorf("MaxBuffers must be greater than zero")
104 bufs = newBufferPool(cfg.MaxBuffers, BlockSize)
106 if cfg.MaxRequests < 1 {
107 cfg.MaxRequests = cfg.MaxBuffers * 2
108 log.Printf("MaxRequests <1 or not specified; defaulting to MaxBuffers * 2 == %d", cfg.MaxRequests)
111 if cfg.BlobSigningKeyFile != "" {
112 buf, err := ioutil.ReadFile(cfg.BlobSigningKeyFile)
114 return fmt.Errorf("reading blob signing key file: %s", err)
116 cfg.blobSigningKey = bytes.TrimSpace(buf)
117 if len(cfg.blobSigningKey) == 0 {
118 return fmt.Errorf("blob signing key file %q is empty", cfg.BlobSigningKeyFile)
120 } else if cfg.RequireSignatures {
121 return fmt.Errorf("cannot enable RequireSignatures (-enforce-permissions) without a blob signing key")
123 log.Println("Running without a blob signing key. Block locators " +
124 "returned by this server will not be signed, and will be rejected " +
125 "by a server that enforces permissions.")
126 log.Println("To fix this, use the BlobSigningKeyFile config entry.")
129 if fn := cfg.SystemAuthTokenFile; fn != "" {
130 buf, err := ioutil.ReadFile(fn)
132 return fmt.Errorf("cannot read system auth token file %q: %s", fn, err)
134 cfg.systemAuthToken = strings.TrimSpace(string(buf))
137 if cfg.EnableDelete {
138 log.Print("Trash/delete features are enabled. WARNING: this has not " +
139 "been extensively tested. You should disable this unless you can afford to lose data.")
142 if len(cfg.Volumes) == 0 {
143 if (&unixVolumeAdder{cfg}).Discover() == 0 {
144 return fmt.Errorf("no volumes found")
147 vm := newVolumeMetricsVecs(reg)
148 for _, v := range cfg.Volumes {
149 if err := v.Start(vm); err != nil {
150 return fmt.Errorf("volume %s: %s", v, err)
152 log.Printf("Using volume %v (writable=%v)", v, v.Writable())
157 // VolumeTypes is built up by init() funcs in the source files that
158 // define the volume types.
159 var VolumeTypes = []func() VolumeWithExamples{}
161 type VolumeList []Volume
163 // UnmarshalJSON -- given an array of objects -- deserializes each
164 // object as the volume type indicated by the object's Type field.
165 func (vl *VolumeList) UnmarshalJSON(data []byte) error {
166 typeMap := map[string]func() VolumeWithExamples{}
167 for _, factory := range VolumeTypes {
168 t := factory().Type()
169 if _, ok := typeMap[t]; ok {
170 log.Fatalf("volume type %+q is claimed by multiple VolumeTypes", t)
175 var mapList []map[string]interface{}
176 err := json.Unmarshal(data, &mapList)
180 for _, mapIn := range mapList {
181 typeIn, ok := mapIn["Type"].(string)
183 return fmt.Errorf("invalid volume type %+v", mapIn["Type"])
185 factory, ok := typeMap[typeIn]
187 return fmt.Errorf("unsupported volume type %+q", typeIn)
189 data, err := json.Marshal(mapIn)
194 err = json.Unmarshal(data, vol)
198 *vl = append(*vl, vol)
203 // MarshalJSON adds a "Type" field to each volume corresponding to its
205 func (vl *VolumeList) MarshalJSON() ([]byte, error) {
207 for _, vs := range *vl {
208 j, err := json.Marshal(vs)
213 data = append(data, byte(','))
215 t, err := json.Marshal(vs.Type())
219 data = append(data, j[0])
220 data = append(data, []byte(`"Type":`)...)
221 data = append(data, t...)
222 data = append(data, byte(','))
223 data = append(data, j[1:]...)
225 return append(data, byte(']')), nil