2021-05-26 21:19:29 +01:00
|
|
|
// Copyright (C) 2021 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
2021-10-20 17:25:20 +01:00
|
|
|
"path/filepath"
|
2021-08-13 20:31:04 +01:00
|
|
|
"sort"
|
|
|
|
"strings"
|
2021-05-26 21:19:29 +01:00
|
|
|
|
|
|
|
"github.com/zeebo/errs"
|
|
|
|
"github.com/zeebo/ini"
|
|
|
|
)
|
|
|
|
|
|
|
|
// loadConfig loads the configuration file from disk if it is not already loaded.
|
|
|
|
// This makes calls to loadConfig idempotent.
|
|
|
|
func (ex *external) loadConfig() error {
|
|
|
|
if ex.config.values != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
ex.config.values = make(map[string][]string)
|
|
|
|
|
2021-06-22 23:41:22 +01:00
|
|
|
fh, err := os.Open(ex.ConfigFile())
|
2021-05-26 21:19:29 +01:00
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return nil
|
|
|
|
} else if err != nil {
|
|
|
|
return errs.Wrap(err)
|
|
|
|
}
|
|
|
|
defer func() { _ = fh.Close() }()
|
|
|
|
|
|
|
|
err = ini.Read(fh, func(ent ini.Entry) error {
|
|
|
|
if ent.Section != "" {
|
|
|
|
ent.Key = ent.Section + "." + ent.Key
|
|
|
|
}
|
|
|
|
ex.config.values[ent.Key] = append(ex.config.values[ent.Key], ent.Value)
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
ex.config.loaded = true
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-08-13 20:31:04 +01:00
|
|
|
// SaveConfig writes out the config file using the provided values.
|
|
|
|
// It is only intended to be used during initial migration and setup.
|
|
|
|
func (ex *external) SaveConfig(values map[string]string) error {
|
|
|
|
entries := make([]ini.Entry, 0, len(values))
|
|
|
|
for k, v := range values {
|
|
|
|
var section string
|
|
|
|
if idx := strings.LastIndexByte(k, '.'); idx >= 0 {
|
|
|
|
section, k = k[:idx], k[idx+1:]
|
|
|
|
}
|
|
|
|
entries = append(entries, ini.Entry{
|
|
|
|
Section: section,
|
|
|
|
Key: k,
|
|
|
|
Value: v,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
sort.Slice(entries, func(i, j int) bool {
|
|
|
|
if entries[i].Section == entries[j].Section {
|
|
|
|
return entries[i].Key < entries[j].Key
|
|
|
|
}
|
|
|
|
return entries[i].Section < entries[j].Section
|
|
|
|
})
|
2022-09-27 21:32:07 +01:00
|
|
|
err := ex.saveConfig(entries)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
ex.config.values = make(map[string][]string, len(values))
|
|
|
|
for k, v := range values {
|
|
|
|
ex.config.values[k] = []string{v}
|
|
|
|
}
|
|
|
|
return nil
|
2021-08-13 20:31:04 +01:00
|
|
|
}
|
|
|
|
|
2021-05-26 21:19:29 +01:00
|
|
|
// saveConfig writes out the config file using the provided values.
|
|
|
|
// It is only intended to be used during initial migration and setup.
|
|
|
|
func (ex *external) saveConfig(entries []ini.Entry) error {
|
|
|
|
// TODO(jeff): write it atomically
|
|
|
|
|
2021-10-20 17:25:20 +01:00
|
|
|
path := ex.ConfigFile()
|
|
|
|
dir := filepath.Dir(path)
|
|
|
|
|
|
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
|
|
return errs.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
newFh, err := os.Create(path)
|
2021-05-26 21:19:29 +01:00
|
|
|
if err != nil {
|
|
|
|
return errs.Wrap(err)
|
|
|
|
}
|
|
|
|
defer func() { _ = newFh.Close() }()
|
|
|
|
|
|
|
|
err = ini.Write(newFh, func(emit func(ini.Entry)) {
|
|
|
|
for _, ent := range entries {
|
|
|
|
emit(ent)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return errs.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := newFh.Sync(); err != nil {
|
|
|
|
return errs.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := newFh.Close(); err != nil {
|
|
|
|
return errs.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|