Don't merge configurations.

In practice, this introduced more problems than it solved.
This commit is contained in:
Kyle Isom
2016-07-15 11:01:35 -07:00
parent 79eda1eea1
commit 8735061583
3 changed files with 37 additions and 156 deletions

View File

@@ -1,3 +1,5 @@
// Package config implements configuration structures for Red
// October.
package config package config
import ( import (
@@ -5,12 +7,6 @@ import (
"io/ioutil" "io/ioutil"
) )
func setIfNotEmpty(a *string, b string) {
if b != "" {
*a = b
}
}
// Server contains the configuration information required to start a // Server contains the configuration information required to start a
// redoctober server. // redoctober server.
type Server struct { type Server struct {
@@ -33,25 +29,6 @@ type Server struct {
Systemd bool `json:"use_systemd,omitempty"` Systemd bool `json:"use_systemd,omitempty"`
} }
// Merge copies over non-empty string values from other into the
// current Server config.
func (s *Server) Merge(other *Server) {
setIfNotEmpty(&s.Addr, other.Addr)
setIfNotEmpty(&s.CAPath, other.CAPath)
if len(other.KeyPaths) != 0 {
s.KeyPaths = other.KeyPaths
}
if len(other.CertPaths) != 0 {
s.CertPaths = other.CertPaths
}
if other.Systemd {
s.Systemd = true
}
}
// UI contains the configuration information for the WWW API. // UI contains the configuration information for the WWW API.
type UI struct { type UI struct {
// Root contains the base URL for the UI. // Root contains the base URL for the UI.
@@ -62,13 +39,6 @@ type UI struct {
Static string `json:"static"` Static string `json:"static"`
} }
// Merge copies over non-empty string values from other into the
// current UI config.
func (ui *UI) Merge(other *UI) {
setIfNotEmpty(&ui.Root, other.Root)
setIfNotEmpty(&ui.Static, other.Static)
}
// HipChat contains the settings for Hipchat integration. // HipChat contains the settings for Hipchat integration.
type HipChat struct { type HipChat struct {
Host string `json:"host"` Host string `json:"host"`
@@ -76,14 +46,6 @@ type HipChat struct {
APIKey string `json:"api_key"` APIKey string `json:"api_key"`
} }
// Merge copies over non-empty settings from other into the current
// HipChat config.
func (hc *HipChat) Merge(other *HipChat) {
setIfNotEmpty(&hc.Host, other.Host)
setIfNotEmpty(&hc.Room, other.Room)
setIfNotEmpty(&hc.APIKey, other.APIKey)
}
// Valid returns true if the HipChat config is ready to be used for // Valid returns true if the HipChat config is ready to be used for
// HipChat notifications. // HipChat notifications.
func (hc *HipChat) Valid() bool { func (hc *HipChat) Valid() bool {
@@ -109,13 +71,6 @@ type Metrics struct {
Port string `json:"port"` Port string `json:"port"`
} }
// Merge copies over non-empty settings from other into the current
// Metrics config.
func (m *Metrics) Merge(other *Metrics) {
setIfNotEmpty(&m.Host, other.Host)
setIfNotEmpty(&m.Port, other.Port)
}
// Delegations contains configuration for persisting delegations. // Delegations contains configuration for persisting delegations.
type Delegations struct { type Delegations struct {
// Persist controls whether delegations are persisted or not. // Persist controls whether delegations are persisted or not.
@@ -126,14 +81,6 @@ type Delegations struct {
Policy string `json:"policy"` Policy string `json:"policy"`
} }
// Merge copies over non-empty settings from other into the current
// Delegations config.
func (d *Delegations) Merge(other *Delegations) {
setIfNotEmpty(&d.Policy, other.Policy)
d.Persist = d.Persist || other.Persist
}
// Config contains all the configuration options for a redoctober // Config contains all the configuration options for a redoctober
// instance. // instance.
type Config struct { type Config struct {
@@ -144,16 +91,6 @@ type Config struct {
Delegations *Delegations `json:"delegations"` Delegations *Delegations `json:"delegations"`
} }
// Merge copies over the non-empty settings from other into the
// current Config.
func (c *Config) Merge(other *Config) {
c.Server.Merge(other.Server)
c.UI.Merge(other.UI)
c.HipChat.Merge(other.HipChat)
c.Metrics.Merge(other.Metrics)
c.Delegations.Merge(other.Delegations)
}
// Valid ensures that the config has enough data to start a Red // Valid ensures that the config has enough data to start a Red
// October process. // October process.
func (c *Config) Valid() bool { func (c *Config) Valid() bool {

View File

@@ -100,84 +100,6 @@ func TestEmptyEqual(t *testing.T) {
} }
} }
// TestMergeEmpty verifies the behaviour where merging a config into
// an empty config results in the empty config becoming the same
// config as the config being merged.
func TestMergeEmpty(t *testing.T) {
empty := New()
testConfig := &Config{
Server: &Server{
Addr: "localhost:8080",
CAPath: "",
KeyPaths: "testdata/server.key",
CertPaths: "testdata/server.pem",
Systemd: true,
},
UI: &UI{
Root: "https://ro.example.net",
},
Metrics: &Metrics{
Host: "127.0.0.1",
Port: "8081",
},
HipChat: &HipChat{
Host: "hipchat.example.net",
Room: "redoctober",
APIKey: "i don't this key will work",
},
Delegations: &Delegations{
Persist: true,
Policy: "NONE",
},
}
if empty.equal(testConfig) {
}
empty.Merge(testConfig)
if !empty.equal(testConfig) {
t.Fatal("merging should result in equivalent configs")
}
}
// TestMergeOverride verifies that merges will combine two configs.
func TestMergeOverride(t *testing.T) {
config := New()
config.Server = &Server{
Addr: "localhost:443",
CAPath: "",
KeyPaths: "testdata/server.key",
CertPaths: "testdata/server.pem",
}
merge := New()
merge.Server = &Server{
Addr: "localhost:8000",
}
expected := New()
expected.Server = &Server{
Addr: "localhost:8000",
CAPath: "",
KeyPaths: "testdata/server.key",
CertPaths: "testdata/server.pem",
}
if config.equal(merge) {
t.Fatal("configurations shouldn't match")
}
if config.equal(expected) {
t.Fatal("configurations shouldn't match")
}
config.Merge(merge)
if !config.equal(expected) {
t.Fatal("configurations don't match")
}
}
// TestLoadFile validates loading a configuration from disk. // TestLoadFile validates loading a configuration from disk.
func TestLoadFile(t *testing.T) { func TestLoadFile(t *testing.T) {
goodConfig := "testdata/config.json" goodConfig := "testdata/config.json"

View File

@@ -237,12 +237,22 @@ var (
vaultPath string vaultPath string
) )
const (
defaultAddr = "localhost:8080"
defaultMetricsHost = "localhost"
defaultMetricsPort = "8081"
)
func init() { func init() {
// cli contains the configuration set by the command line // cli contains the configuration set by the command line
// options, and cfg is the actual Red October config. // options, and cfg is the actual Red October config.
cli = config.New() cli = config.New()
cfg = config.New() cfg = config.New()
cli.Server.Addr = defaultAddr
cli.Metrics.Host = defaultMetricsHost
cli.Metrics.Port = defaultMetricsPort
flag.Usage = func() { flag.Usage = func() {
fmt.Fprint(os.Stderr, "main usage dump\n") fmt.Fprint(os.Stderr, "main usage dump\n")
fmt.Fprint(os.Stderr, usage) fmt.Fprint(os.Stderr, usage)
@@ -251,18 +261,29 @@ func init() {
} }
flag.StringVar(&confFile, "f", "", "path to config file") flag.StringVar(&confFile, "f", "", "path to config file")
flag.StringVar(&cli.Server.Addr, "addr", "localhost:8080", "Server and port separated by :") flag.StringVar(&cli.Server.Addr, "addr", cli.Server.Addr,
flag.StringVar(&cli.Server.CAPath, "ca", "", "Path of TLS CA for client authentication (optional)") "Server and port separated by :")
flag.StringVar(&cli.Server.CertPaths, "certs", "", "Path(s) of TLS certificate in PEM format, comma-separated") flag.StringVar(&cli.Server.CAPath, "ca", cli.Server.CAPath,
flag.StringVar(&cli.HipChat.Host, "hchost", "", "Hipchat Url Base (ex: hipchat.com)") "Path of TLS CA for client authentication (optional)")
flag.StringVar(&cli.HipChat.APIKey, "hckey", "", "Hipchat API Key") flag.StringVar(&cli.Server.CertPaths, "certs", cli.Server.CertPaths,
flag.StringVar(&cli.HipChat.Room, "hcroom", "", "Hipchat Room Id") "Path(s) of TLS certificate in PEM format, comma-separated")
flag.StringVar(&cli.Server.KeyPaths, "keys", "", "Path(s) of TLS private key in PEM format, comma-separated, must me in the same order as the certs") flag.StringVar(&cli.HipChat.Host, "hchost", cli.HipChat.Host,
flag.StringVar(&cli.Metrics.Host, "metrics-host", "localhost", "The `host` the metrics endpoint should listen on.") "Hipchat Url Base (ex: hipchat.com)")
flag.StringVar(&cli.Metrics.Port, "metrics-port", "8081", "The `port` the metrics endpoint should listen on.") flag.StringVar(&cli.HipChat.APIKey, "hckey", cli.HipChat.APIKey,
flag.StringVar(&cli.UI.Root, "rohost", "", "RedOctober Url Base (ex: localhost:8080)") "Hipchat API Key")
flag.StringVar(&cli.UI.Static, "static", "", "Path to override built-in index.html") flag.StringVar(&cli.HipChat.Room, "hcroom", cli.HipChat.Room,
flag.BoolVar(&cli.Server.Systemd, "systemdfds", false, "Use systemd socket activation to listen on a file. Useful for binding privileged sockets.") "Hipchat Room ID")
flag.StringVar(&cli.Server.KeyPaths, "keys", cli.Server.KeyPaths,
"Comma-separated list of PEM-encoded TLS private keys in the same order as certs")
flag.StringVar(&cli.Metrics.Host, "metrics-host", cli.Metrics.Host,
"The `host` the metrics endpoint should listen on.")
flag.StringVar(&cli.Metrics.Port, "metrics-port", cli.Metrics.Port,
"The `port` the metrics endpoint should listen on.")
flag.StringVar(&cli.UI.Root, "rohost", cli.UI.Root, "RedOctober URL Base (ex: localhost:8080)")
flag.StringVar(&cli.UI.Static, "static", cli.UI.Static,
"Path to override built-in index.html")
flag.BoolVar(&cli.Server.Systemd, "systemdfds", cli.Server.Systemd,
"Use systemd socket activation to listen on a file. Useful for binding privileged sockets.")
flag.StringVar(&vaultPath, "vaultpath", "diskrecord.json", "Path to the the disk vault") flag.StringVar(&vaultPath, "vaultpath", "diskrecord.json", "Path to the the disk vault")
flag.Parse() flag.Parse()
@@ -277,8 +298,9 @@ func main() {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
} else {
cfg = cli
} }
cfg.Merge(cli)
if vaultPath == "" || !cfg.Valid() { if vaultPath == "" || !cfg.Valid() {
fmt.Fprint(os.Stderr, usage) fmt.Fprint(os.Stderr, usage)