chore: cleanup environment variable option parsing and config handling

This commit is contained in:
Gareth George
2023-11-27 18:40:08 -08:00
parent 4f0a47267c
commit e76b7ef5a2
11 changed files with 91 additions and 39 deletions
+2 -1
View File
@@ -62,10 +62,11 @@ Browsing snapshots:
## Dev Depedencies
**Basic Dependencies**
**Build Dependencies**
* Node.JS for UI development
* Go 1.21 or greater for server development
* go.rice `go install github.com/GeertJohan/go.rice@latest` and `go install github.com/GeertJohan/go.rice/rice@latest`
**To Edit Protobuffers**
```sh
+5
View File
@@ -0,0 +1,5 @@
#! /bin/sh
(cd proto && ./build.sh)
go build ./..
+5
View File
@@ -0,0 +1,5 @@
#! /bin/sh
go install github.com/GeertJohan/go.rice/rice@latest
go install github.com/GeertJohan/go.rice@latest
pip install lastversion
+9 -8
View File
@@ -22,13 +22,14 @@ import (
type Server struct {
*v1.UnimplementedResticUIServer
config config.ConfigStore
orchestrator *orchestrator.Orchestrator
oplog *oplog.OpLog
}
var _ v1.ResticUIServer = &Server{}
func NewServer(orchestrator *orchestrator.Orchestrator, oplog *oplog.OpLog) *Server {
func NewServer(config config.ConfigStore, orchestrator *orchestrator.Orchestrator, oplog *oplog.OpLog) *Server {
s := &Server{
orchestrator: orchestrator,
oplog: oplog,
@@ -39,12 +40,12 @@ func NewServer(orchestrator *orchestrator.Orchestrator, oplog *oplog.OpLog) *Ser
// GetConfig implements GET /v1/config
func (s *Server) GetConfig(ctx context.Context, empty *emptypb.Empty) (*v1.Config, error) {
return config.Default.Get()
return s.config.Get()
}
// SetConfig implements POST /v1/config
func (s *Server) SetConfig(ctx context.Context, c *v1.Config) (*v1.Config, error) {
existing, err := config.Default.Get()
existing, err := s.config.Get()
if err != nil {
return nil, fmt.Errorf("failed to check current config: %w", err)
}
@@ -55,21 +56,21 @@ func (s *Server) SetConfig(ctx context.Context, c *v1.Config) (*v1.Config, error
}
c.Modno += 1
if err := config.Default.Update(c); err != nil {
if err := s.config.Update(c); err != nil {
return nil, fmt.Errorf("failed to update config: %w", err)
}
newConfig, err := config.Default.Get()
newConfig, err := s.config.Get()
if err != nil {
return nil, fmt.Errorf("failed to get newly set config: %w", err)
}
s.orchestrator.ApplyConfig(newConfig)
return config.Default.Get()
return newConfig, nil
}
// AddRepo implements POST /v1/config/repo, it includes validation that the repo can be initialized.
func (s *Server) AddRepo(ctx context.Context, repo *v1.Repo) (*v1.Config, error) {
c, err := config.Default.Get()
c, err := s.config.Get()
if err != nil {
return nil, fmt.Errorf("failed to get config: %w", err)
}
@@ -88,7 +89,7 @@ func (s *Server) AddRepo(ctx context.Context, repo *v1.Repo) (*v1.Config, error)
}
zap.L().Debug("Updating config")
if err := config.Default.Update(c); err != nil {
if err := s.config.Update(c); err != nil {
return nil, fmt.Errorf("failed to update config: %w", err)
}
+2 -10
View File
@@ -2,7 +2,6 @@ package config
import (
"errors"
"flag"
"fmt"
"os"
"path"
@@ -12,13 +11,6 @@ import (
)
var ErrConfigNotFound = fmt.Errorf("config not found")
var configDirFlag = flag.String("config_dir", "", "The directory to store the config file")
var Default ConfigStore = &CachingValidatingStore{
ConfigStore: &JsonFileStore{
Path: path.Join(configDir(*configDirFlag), "config.json"),
},
}
type ConfigStore interface {
Get() (*v1.Config, error)
@@ -51,7 +43,7 @@ func configDir(override string) string {
type CachingValidatingStore struct {
ConfigStore
mu sync.Mutex
mu sync.Mutex
config *v1.Config
}
@@ -94,4 +86,4 @@ func (c *CachingValidatingStore) Update(config *v1.Config) error {
c.config = config
return nil
}
}
+46
View File
@@ -0,0 +1,46 @@
package envopts
import (
"fmt"
"os"
"path"
"strings"
)
func ConfigFilePath() string {
if val := os.Getenv("RESTICUI_CONFIG_PATH"); val != "" {
return val
}
if val := os.Getenv("XDG_CONFIG_HOME"); val != "" {
return path.Join(val, "resticui/config.json")
}
return path.Join(getHomeDir(), ".config/resticui/config.json")
}
func DataDir() string {
if val := os.Getenv("RESTICUI_DATA_DIR"); val != "" {
return val
}
if val := os.Getenv("XDG_DATA_HOME"); val != "" {
return path.Join(val, "resticui")
}
return path.Join(getHomeDir(), ".local/share/resticui")
}
func BindAddress() string {
if val := os.Getenv("RESTICUI_PORT"); val != "" {
if !strings.Contains(val, ":") {
return ":" + val
}
return val
}
return ":9898"
}
func getHomeDir() string {
home, err := os.UserHomeDir()
if err != nil {
panic(fmt.Errorf("couldn't determine home directory: %v", err))
}
return home
}
+4 -8
View File
@@ -30,15 +30,11 @@ type Orchestrator struct {
externTasks chan Task // externTasks is a channel that externally added tasks can be added to, they will be consumed by Run()
}
func NewOrchestrator(configProvider config.ConfigStore, oplog *oplog.OpLog) (*Orchestrator, error) {
cfg, err := configProvider.Get()
if err != nil {
return nil, fmt.Errorf("failed to get config: %w", err)
}
func NewOrchestrator(cfg *v1.Config, oplog *oplog.OpLog) (*Orchestrator, error) {
return &Orchestrator{
config: cfg,
OpLog: oplog,
config: cfg,
OpLog: oplog,
// repoPool created with a memory store to ensure the config is updated in an atomic operation with the repo pool's config value.
repoPool: newResticRepoPool(&config.MemoryStore{Config: cfg}),
externTasks: make(chan Task, 2),
}, nil
+1 -1
View File
@@ -9,7 +9,7 @@ import (
func TestReadBackupProgressEntries(t *testing.T) {
t.Parallel()
testInput := `{"message_type":"status","percent_done":0,"total_files":1,"total_bytes":15}
{"message_type":"summary","files_new":0,"files_changed":0,"files_unmodified":166,"dirs_new":0,"dirs_changed":0,"dirs_unmodified":128,"data_blobs":0,"tree_blobs":0,"data_added":0,"total_files_processed":166,"total_bytes_processed":16754463,"total_duration":0.235433378,"id":"d4558b360cc1b7966e416e010382ab8feb49d14da7832266832d69a43af10147"}`
{"message_type":"summary","files_new":0,"files_changed":0,"files_unmodified":166,"dirs_new":0,"dirs_changed":0,"dirs_unmodified":128,"data_blobs":0,"tree_blobs":0,"data_added":0,"total_files_processed":166,"total_bytes_processed":16754463,"total_duration":0.235433378,"snapshot_id":"d4558b360cc1b7966e416e010382ab8feb49d14da7832266832d69a43af10147"}`
b := bytes.NewBuffer([]byte(testInput))
-2
View File
@@ -1,2 +0,0 @@
#! /bin/bash
buf generate
+3
View File
@@ -0,0 +1,3 @@
#! /bin/sh
buf generate
+14 -9
View File
@@ -3,7 +3,6 @@ package main
import (
"context"
"errors"
"fmt"
"io/fs"
"net/http"
"os"
@@ -14,6 +13,7 @@ import (
"github.com/garethgeorge/resticui/internal/api"
"github.com/garethgeorge/resticui/internal/config"
"github.com/garethgeorge/resticui/internal/envopts"
"github.com/garethgeorge/resticui/internal/oplog"
"github.com/garethgeorge/resticui/internal/orchestrator"
static "github.com/garethgeorge/resticui/webui"
@@ -25,16 +25,14 @@ import (
)
func main() {
port := os.Getenv("RESTICUI_PORT")
if port == "" {
port = "9898"
}
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
go onterm(cancel)
if _, err := config.Default.Get(); err != nil {
configStore := createConfigProvider()
cfg, err := configStore.Get()
if err != nil {
zap.S().Fatalf("Error loading config: %v", err)
}
@@ -53,7 +51,7 @@ func main() {
}
defer oplog.Close()
orchestrator, err := orchestrator.NewOrchestrator(config.Default, oplog)
orchestrator, err := orchestrator.NewOrchestrator(cfg, oplog)
if err != nil {
zap.S().Fatalf("Error creating orchestrator: %v", err)
}
@@ -68,6 +66,7 @@ func main() {
}()
apiServer := api.NewServer(
configStore,
orchestrator, // TODO: eliminate default config
oplog,
)
@@ -85,7 +84,7 @@ func main() {
// Serve the HTTP gateway
server := &http.Server{
Addr: fmt.Sprintf(":%s", port),
Addr: envopts.BindAddress(),
Handler: mux,
}
@@ -122,6 +121,12 @@ func init() {
}
}
func createConfigProvider() config.ConfigStore {
return &config.CachingValidatingStore{
ConfigStore: &config.JsonFileStore{Path: envopts.ConfigFilePath()},
}
}
func onterm(callback func()) {
sigchan := make(chan os.Signal, 1)
signal.Notify(sigchan, os.Interrupt, syscall.SIGTERM)