From 69525162040f168b229a774515c47768e828632f Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Tue, 1 Apr 2025 21:06:31 +0200 Subject: [PATCH] chore: config fixes - remove example config file - don't set default values on read - omit empty fields - indent yaml 2 spaces --- internal/config/config.go | 4 +-- internal/config/data/config.example.yml | 24 --------------- internal/config/service.go | 41 +++++++++++++------------ internal/config/service_test.go | 28 +++-------------- internal/config/testdata/no-logfile.yml | 5 --- internal/config/testdata/no-name.yml | 6 ---- main.go | 12 +++++--- 7 files changed, 36 insertions(+), 84 deletions(-) delete mode 100644 internal/config/data/config.example.yml delete mode 100644 internal/config/testdata/no-logfile.yml delete mode 100644 internal/config/testdata/no-name.yml diff --git a/internal/config/config.go b/internal/config/config.go index 72f375b..bc3247b 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -9,13 +9,13 @@ type Destination struct { // LogFile holds the configuration for the log file. type LogFile struct { Enabled bool `yaml:"enabled"` - Path string `yaml:"path"` + Path string `yaml:"path,omitempty"` } // RTMPSource holds the configuration for the RTMP source. type RTMPSource struct { Enabled bool `yaml:"enabled"` - StreamKey string `yaml:"streamkey"` + StreamKey string `yaml:"streamkey,omitempty"` } // Sources holds the configuration for the sources. diff --git a/internal/config/data/config.example.yml b/internal/config/data/config.example.yml deleted file mode 100644 index 24065dd..0000000 --- a/internal/config/data/config.example.yml +++ /dev/null @@ -1,24 +0,0 @@ -# Octoplex is a live stream multiplexer. ---- -# -sources: - # Currently the only source type is RTMP server. - rtmp: - enabled: yes - # Your local stream key. Defaults to "live". - # - # rtmp://localhost:1935/live - streamkey: live -# -logfile: - # Change to yes to log to system location. - enabled: no - # Or, log to this absolute path: - # path: octoplex.log -# -# Define your destinations here. -destinations: - # - name: YouTube - # url: rtmp://rtmp.youtube.com/myYoutubeStreamKey - # - name: Twitch - # url: rtmp://ingest.global-contribute.live-video.net/app/myTwitchStreamKey diff --git a/internal/config/service.go b/internal/config/service.go index 5ee603e..6b7fd1c 100644 --- a/internal/config/service.go +++ b/internal/config/service.go @@ -1,6 +1,7 @@ package config import ( + "bytes" _ "embed" "errors" "fmt" @@ -8,13 +9,9 @@ import ( "path/filepath" "strings" - "git.netflux.io/rob/octoplex/internal/domain" "gopkg.in/yaml.v3" ) -//go:embed data/config.example.yml -var exampleConfig []byte - // Service provides configuration services. type Service struct { current Config @@ -100,7 +97,7 @@ func (s *Service) SetConfig(cfg Config) error { return fmt.Errorf("validate: %w", err) } - cfgBytes, err := yaml.Marshal(cfg) + cfgBytes, err := marshalConfig(cfg) if err != nil { return fmt.Errorf("marshal: %w", err) } @@ -130,8 +127,6 @@ func (s *Service) readConfig() (cfg Config, _ error) { return cfg, fmt.Errorf("unmarshal: %w", err) } - s.setDefaults(&cfg) - if err = validate(cfg); err != nil { return cfg, err } @@ -143,17 +138,31 @@ func (s *Service) readConfig() (cfg Config, _ error) { func (s *Service) writeDefaultConfig() (Config, error) { var cfg Config - if err := yaml.Unmarshal(exampleConfig, &cfg); err != nil { - return cfg, fmt.Errorf("unmarshal: %w", err) + s.setDefaults(&cfg) + + cfgBytes, err := marshalConfig(cfg) + if err != nil { + return cfg, fmt.Errorf("marshal: %w", err) } - if err := s.writeConfig(exampleConfig); err != nil { + if err := s.writeConfig(cfgBytes); err != nil { return Config{}, fmt.Errorf("write config: %w", err) } return cfg, nil } +func marshalConfig(cfg Config) ([]byte, error) { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + enc.SetIndent(2) + if err := enc.Encode(cfg); err != nil { + return nil, fmt.Errorf("encode: %w", err) + } + + return buf.Bytes(), nil +} + func (s *Service) writeConfig(cfgBytes []byte) error { if err := os.MkdirAll(s.appConfigDir, 0744); err != nil { return fmt.Errorf("mkdir: %w", err) @@ -166,18 +175,10 @@ func (s *Service) writeConfig(cfgBytes []byte) error { return nil } +// setDefaults is called to set default values for a new, empty configuration. func (s *Service) setDefaults(cfg *Config) { - if cfg.LogFile.Enabled && cfg.LogFile.Path == "" { - cfg.LogFile.Path = filepath.Join(s.appStateDir, domain.AppName+".log") - } - cfg.Sources.RTMP.Enabled = true - - for i := range cfg.Destinations { - if strings.TrimSpace(cfg.Destinations[i].Name) == "" { - cfg.Destinations[i].Name = fmt.Sprintf("Stream %d", i+1) - } - } + cfg.Sources.RTMP.StreamKey = "live" } // TODO: validate URL format diff --git a/internal/config/service_test.go b/internal/config/service_test.go index 301c967..c68daf2 100644 --- a/internal/config/service_test.go +++ b/internal/config/service_test.go @@ -4,27 +4,21 @@ import ( _ "embed" "os" "path/filepath" - "strings" "testing" "git.netflux.io/rob/octoplex/internal/config" "git.netflux.io/rob/octoplex/internal/shortid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" ) //go:embed testdata/complete.yml var configComplete []byte -//go:embed testdata/no-logfile.yml -var configNoLogfile []byte - //go:embed testdata/logfile.yml var configLogfile []byte -//go:embed testdata/no-name.yml -var configNoName []byte - //go:embed testdata/invalid-destination-url.yml var configInvalidDestinationURL []byte @@ -60,8 +54,10 @@ func TestConfigServiceCreateConfig(t *testing.T) { p := filepath.Join(systemConfigDir, "octoplex", "config.yaml") cfgBytes, err := os.ReadFile(p) require.NoError(t, err, "config file was not created") - // Ensure the example config file is written: - assert.Contains(t, string(cfgBytes), "# Octoplex is a live stream multiplexer.") + + var readCfg config.Config + require.NoError(t, yaml.Unmarshal(cfgBytes, &readCfg)) + assert.True(t, readCfg.Sources.RTMP.Enabled, "default values not set") } func TestConfigServiceReadConfig(t *testing.T) { @@ -97,13 +93,6 @@ func TestConfigServiceReadConfig(t *testing.T) { }, cfg) }, }, - { - name: "logging enabled, no logfile", - configBytes: configNoLogfile, - want: func(t *testing.T, cfg config.Config) { - assert.True(t, strings.HasSuffix(cfg.LogFile.Path, "/octoplex/octoplex.log")) - }, - }, { name: "logging enabled, logfile", configBytes: configLogfile, @@ -111,13 +100,6 @@ func TestConfigServiceReadConfig(t *testing.T) { assert.Equal(t, "/tmp/octoplex.log", cfg.LogFile.Path) }, }, - { - name: "no name", - configBytes: configNoName, - want: func(t *testing.T, cfg config.Config) { - assert.Equal(t, "Stream 1", cfg.Destinations[0].Name) - }, - }, { name: "invalid destination URL", configBytes: configInvalidDestinationURL, diff --git a/internal/config/testdata/no-logfile.yml b/internal/config/testdata/no-logfile.yml deleted file mode 100644 index 1825093..0000000 --- a/internal/config/testdata/no-logfile.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -logfile: - enabled: true -destinations: -- url: rtmp://rtmp.example.com:1935/live diff --git a/internal/config/testdata/no-name.yml b/internal/config/testdata/no-name.yml deleted file mode 100644 index 3b8c38a..0000000 --- a/internal/config/testdata/no-name.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -logfile: - enabled: true - path: test.log -destinations: -- url: rtmp://rtmp.example.com:1935/live diff --git a/main.go b/main.go index 3be2a99..c242e17 100644 --- a/main.go +++ b/main.go @@ -49,7 +49,7 @@ func run(ctx context.Context) error { } else if narg == 1 { switch flag.Arg(0) { case "edit-config": - return editConfigFile(configService.Path()) + return editConfigFile(configService) case "print-config": return printConfigPath(configService.Path()) case "version": @@ -108,7 +108,11 @@ func run(ctx context.Context) error { } // editConfigFile opens the config file in the user's editor. -func editConfigFile(configPath string) error { +func editConfigFile(configService *config.Service) error { + if _, err := configService.ReadOrCreateConfig(); err != nil { + return fmt.Errorf("read or create config: %w", err) + } + editor := os.Getenv("EDITOR") if editor == "" { editor = "vi" @@ -118,10 +122,10 @@ func editConfigFile(configPath string) error { return fmt.Errorf("look path: %w", err) } - fmt.Fprintf(os.Stderr, "Editing config file: %s\n", configPath) + fmt.Fprintf(os.Stderr, "Editing config file: %s\n", configService.Path()) fmt.Println(binary) - if err := syscall.Exec(binary, []string{"--", configPath}, os.Environ()); err != nil { + if err := syscall.Exec(binary, []string{"--", configService.Path()}, os.Environ()); err != nil { return fmt.Errorf("exec: %w", err) }