Worker: change how configuration & credentials are loaded/saved
The Worker config/credential management was a bit of a mess. It's now better structured, and also allows runtime overrides of the Manager URL, without writing that override to the config file.
This commit is contained in:
parent
c0cd3ca5ad
commit
42407865eb
@ -8,6 +8,7 @@ import (
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
@ -26,7 +27,13 @@ const (
|
||||
// WorkerConfig represents the configuration of a single worker.
|
||||
// It does not include authentication credentials.
|
||||
type WorkerConfig struct {
|
||||
Manager string `yaml:"manager_url"`
|
||||
// ConfiguredManager is the Manager URL that's in the configuration file.
|
||||
ConfiguredManager string `yaml:"manager_url"`
|
||||
|
||||
// ManagerURL is the Manager URL to use by the Worker. It could come from the
|
||||
// configuration file, but also from autodiscovery via UPnP/SSDP.
|
||||
ManagerURL string `yaml:"-"`
|
||||
|
||||
TaskTypes []string `yaml:"task_types"`
|
||||
}
|
||||
|
||||
@ -35,42 +42,59 @@ type WorkerCredentials struct {
|
||||
Secret string `yaml:"worker_secret"`
|
||||
}
|
||||
|
||||
func loadConfig(configWrangler FileConfigWrangler) (WorkerConfig, error) {
|
||||
logger := log.With().Str("filename", configFilename).Logger()
|
||||
// FileConfigWrangler is the default config wrangler that actually reads & writes files.
|
||||
type FileConfigWrangler struct {
|
||||
// In-memory copy of the worker configuration.
|
||||
wc *WorkerConfig
|
||||
creds *WorkerCredentials
|
||||
}
|
||||
|
||||
var cfg WorkerConfig
|
||||
// NewConfigWrangler returns ConfigWrangler that reads files.
|
||||
func NewConfigWrangler() FileConfigWrangler {
|
||||
return FileConfigWrangler{}
|
||||
}
|
||||
|
||||
err := configWrangler.LoadConfig(configFilename, &cfg)
|
||||
// WorkerConfig returns the worker configuration, or the default config if
|
||||
// there is no config file. Configuration is only loaded from disk once;
|
||||
// subsequent calls return the same config.
|
||||
func (fcw *FileConfigWrangler) WorkerConfig() (WorkerConfig, error) {
|
||||
if fcw.wc != nil {
|
||||
return *fcw.wc, nil
|
||||
}
|
||||
|
||||
wc := WorkerConfig{}
|
||||
err := fcw.loadConfig(configFilename, &wc)
|
||||
|
||||
// If the configuration file doesn't exist, write the defaults & retry loading them.
|
||||
if os.IsNotExist(err) {
|
||||
logger.Info().Msg("writing default configuration file")
|
||||
cfg = configWrangler.DefaultConfig()
|
||||
err = configWrangler.WriteConfig(configFilename, "Configuration", cfg)
|
||||
if err != nil {
|
||||
return cfg, fmt.Errorf("writing default config: %w", err)
|
||||
if !os.IsNotExist(err) {
|
||||
return WorkerConfig{}, err
|
||||
}
|
||||
err = configWrangler.LoadConfig(configFilename, &cfg)
|
||||
|
||||
// The config file not existing is fine; just use the defaults.
|
||||
wc = fcw.DefaultConfig()
|
||||
}
|
||||
|
||||
fcw.wc = &wc
|
||||
|
||||
man := strings.TrimSpace(wc.ConfiguredManager)
|
||||
if man != "" {
|
||||
fcw.SetManagerURL(man)
|
||||
}
|
||||
|
||||
return wc, nil
|
||||
}
|
||||
|
||||
func (fcw *FileConfigWrangler) SaveConfig() error {
|
||||
err := fcw.writeConfig(configFilename, "Configuration", fcw.wc)
|
||||
if err != nil {
|
||||
return cfg, fmt.Errorf("loading config from %s: %w", configFilename, err)
|
||||
return fmt.Errorf("writing to %s: %w", configFilename, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate the manager URL.
|
||||
if cfg.Manager != "" {
|
||||
_, err := ParseURL(cfg.Manager)
|
||||
if err != nil {
|
||||
return cfg, fmt.Errorf("parsing manager URL %s: %w", cfg.Manager, err)
|
||||
}
|
||||
logger.Debug().Str("url", cfg.Manager).Msg("parsed manager URL")
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func loadCredentials(configWrangler FileConfigWrangler) (WorkerCredentials, error) {
|
||||
func (fcw *FileConfigWrangler) WorkerCredentials() (WorkerCredentials, error) {
|
||||
var creds WorkerCredentials
|
||||
err := configWrangler.LoadConfig(credentialsFilename, &creds)
|
||||
err := fcw.loadConfig(credentialsFilename, &creds)
|
||||
if err != nil {
|
||||
return WorkerCredentials{}, err
|
||||
}
|
||||
@ -81,24 +105,34 @@ func loadCredentials(configWrangler FileConfigWrangler) (WorkerCredentials, erro
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
// FileConfigWrangler is the default config wrangler that actually reads & writes files.
|
||||
type FileConfigWrangler struct{}
|
||||
func (fcw *FileConfigWrangler) SaveCredentials(creds WorkerCredentials) error {
|
||||
fcw.creds = &creds
|
||||
|
||||
// NewConfigWrangler returns ConfigWrangler that reads files.
|
||||
func NewConfigWrangler() FileConfigWrangler {
|
||||
return FileConfigWrangler{}
|
||||
err := fcw.writeConfig(credentialsFilename, "Credentials", creds)
|
||||
if err != nil {
|
||||
return fmt.Errorf("writing to %s: %w", credentialsFilename, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetManagerURL overwrites the Manager URL in the cached configuration.
|
||||
// This is an in-memory change only, and will not be written to the config file.
|
||||
// Returns a new copy of the WorkerConfig with the Manager URL updated.
|
||||
func (fcw *FileConfigWrangler) SetManagerURL(managerURL string) WorkerConfig {
|
||||
fcw.wc.ManagerURL = managerURL
|
||||
return *fcw.wc
|
||||
}
|
||||
|
||||
// DefaultConfig returns a fairly sane default configuration.
|
||||
func (fcw FileConfigWrangler) DefaultConfig() WorkerConfig {
|
||||
return WorkerConfig{
|
||||
Manager: "",
|
||||
ConfiguredManager: "", // Auto-detect by default.
|
||||
TaskTypes: []string{"blender", "file-management", "exr-merge", "misc"},
|
||||
}
|
||||
}
|
||||
|
||||
// WriteConfig stores a struct as YAML file.
|
||||
func (fcw FileConfigWrangler) WriteConfig(filename string, filetype string, config interface{}) error {
|
||||
func (fcw FileConfigWrangler) writeConfig(filename string, filetype string, config interface{}) error {
|
||||
data, err := yaml.Marshal(config)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -144,7 +178,7 @@ func (fcw FileConfigWrangler) WriteConfig(filename string, filetype string, conf
|
||||
}
|
||||
|
||||
// LoadConfig loads a YAML configuration file into 'config'
|
||||
func (fcw FileConfigWrangler) LoadConfig(filename string, config interface{}) error {
|
||||
func (fcw FileConfigWrangler) loadConfig(filename string, config interface{}) error {
|
||||
log.Debug().Str("filename", filename).Msg("loading config file")
|
||||
f, err := os.OpenFile(filename, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
|
@ -30,19 +30,18 @@ func RegisterOrSignOn(ctx context.Context, configWrangler FileConfigWrangler) (
|
||||
client FlamencoClient, startupState api.WorkerStatus,
|
||||
) {
|
||||
// Load configuration
|
||||
cfg, err := loadConfig(configWrangler)
|
||||
cfg, err := configWrangler.WorkerConfig()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("loading configuration")
|
||||
}
|
||||
|
||||
log.Info().Interface("config", cfg).Msg("loaded configuration")
|
||||
|
||||
if cfg.Manager == "" {
|
||||
log.Fatal().Msg("no manager configured")
|
||||
if cfg.ManagerURL == "" {
|
||||
log.Fatal().Msg("no Manager configured")
|
||||
}
|
||||
|
||||
// Load credentials
|
||||
creds, err := loadCredentials(configWrangler)
|
||||
creds, err := configWrangler.WorkerCredentials()
|
||||
if err == nil {
|
||||
// Credentials can be loaded just fine, try to sign on with them.
|
||||
client = authenticatedClient(cfg, creds)
|
||||
@ -58,17 +57,16 @@ func RegisterOrSignOn(ctx context.Context, configWrangler FileConfigWrangler) (
|
||||
creds = register(ctx, cfg, client)
|
||||
|
||||
// store ID and secretKey in config file when registration is complete.
|
||||
err = configWrangler.WriteConfig(credentialsFilename, "Credentials", creds)
|
||||
err = configWrangler.SaveCredentials(creds)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Str("file", credentialsFilename).
|
||||
Msg("unable to write credentials configuration file")
|
||||
log.Fatal().Err(err).Msg("unable to write credentials file")
|
||||
}
|
||||
|
||||
// Sign-on should work now.
|
||||
client = authenticatedClient(cfg, creds)
|
||||
startupState, err = signOn(ctx, cfg, client)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Str("manager", cfg.Manager).Msg("unable to sign on after registering")
|
||||
log.Fatal().Err(err).Str("manager", cfg.ManagerURL).Msg("unable to sign on after registering")
|
||||
}
|
||||
|
||||
return
|
||||
@ -141,7 +139,7 @@ func repeatSignOnUntilAnswer(ctx context.Context, cfg WorkerConfig, client Flame
|
||||
|
||||
// signOn tells the Manager we're alive and returns the status the Manager tells us to go to.
|
||||
func signOn(ctx context.Context, cfg WorkerConfig, client FlamencoClient) (api.WorkerStatus, error) {
|
||||
logger := log.With().Str("manager", cfg.Manager).Logger()
|
||||
logger := log.With().Str("manager", cfg.ManagerURL).Logger()
|
||||
|
||||
req := api.SignOnJSONRequestBody{
|
||||
Nickname: mustHostname(),
|
||||
@ -191,7 +189,7 @@ func mustHostname() string {
|
||||
// authenticatedClient constructs a Flamenco client with the given credentials.
|
||||
func authenticatedClient(cfg WorkerConfig, creds WorkerCredentials) FlamencoClient {
|
||||
flamenco, err := api.NewClientWithResponses(
|
||||
cfg.Manager,
|
||||
cfg.ManagerURL,
|
||||
|
||||
// Add a Basic HTTP authentication header to every request to Flamenco Manager.
|
||||
api.WithRequestEditorFn(func(ctx context.Context, req *http.Request) error {
|
||||
|
Loading…
x
Reference in New Issue
Block a user