git » chasquid » commit 4c28efc

config: Allow overrides from the command line

author Alberto Bertogli
2020-05-16 22:22:00 UTC
committer Alberto Bertogli
2020-05-16 23:10:06 UTC
parent 7909b479ebdcaa84e3d847927929d8f4505f20a6

config: Allow overrides from the command line

This patch allows the configuration values to be overridden from the
command-line, with a new -config_overrides flag.

There is a fairly specific use case for this, when editing the
configuration file is not feasible or convenient (e.g. running an
user-supplied configuration in a managed environment).

chasquid.go +3 -1
cmd/chasquid-util/chasquid-util.go +4 -4
docs/man/chasquid.1 +6 -1
docs/man/chasquid.1.pod +6 -0
internal/config/config.go +10 -2
internal/config/config_test.go +25 -6

diff --git a/chasquid.go b/chasquid.go
index f992c0b..059d2df 100644
--- a/chasquid.go
+++ b/chasquid.go
@@ -37,6 +37,8 @@ import (
 var (
 	configDir = flag.String("config_dir", "/etc/chasquid",
 		"configuration directory")
+	configOverrides = flag.String("config_overrides", "",
+		"override configuration values (in text protobuf format)")
 	showVer = flag.Bool("version", false, "show version and exit")
 )
 
@@ -70,7 +72,7 @@ func main() {
 	// Seed the PRNG, just to prevent for it to be totally predictable.
 	rand.Seed(time.Now().UnixNano())
 
-	conf, err := config.Load(*configDir + "/chasquid.conf")
+	conf, err := config.Load(*configDir+"/chasquid.conf", *configOverrides)
 	if err != nil {
 		log.Fatalf("Error loading config: %v", err)
 	}
diff --git a/cmd/chasquid-util/chasquid-util.go b/cmd/chasquid-util/chasquid-util.go
index fa4be3d..f8eae2d 100644
--- a/cmd/chasquid-util/chasquid-util.go
+++ b/cmd/chasquid-util/chasquid-util.go
@@ -204,7 +204,7 @@ func userRemove() {
 
 // chasquid-util aliases-resolve <address>
 func aliasesResolve() {
-	conf, err := config.Load(configDir + "/chasquid.conf")
+	conf, err := config.Load(configDir+"/chasquid.conf", "")
 	if err != nil {
 		Fatalf("Error loading config: %v", err)
 	}
@@ -248,7 +248,7 @@ func aliasesResolve() {
 
 // chasquid-util print-config
 func printConfig() {
-	conf, err := config.Load(configDir + "/chasquid.conf")
+	conf, err := config.Load(configDir+"/chasquid.conf", "")
 	if err != nil {
 		Fatalf("Error loading config: %v", err)
 	}
@@ -260,7 +260,7 @@ func printConfig() {
 func domaininfoRemove() {
 	domain := args["<domain>"].(string)
 
-	conf, err := config.Load(configDir + "/chasquid.conf")
+	conf, err := config.Load(configDir+"/chasquid.conf", "")
 	if err != nil {
 		Fatalf("Error loading config: %v", err)
 	}
@@ -290,7 +290,7 @@ func aliasesAdd() {
 		Fatalf("Domain doesn't exist")
 	}
 
-	conf, err := config.Load(configDir + "/chasquid.conf")
+	conf, err := config.Load(configDir+"/chasquid.conf", "")
 	if err != nil {
 		Fatalf("Error loading config: %v", err)
 	}
diff --git a/docs/man/chasquid.1 b/docs/man/chasquid.1
index 0a9afb7..1394a43 100644
--- a/docs/man/chasquid.1
+++ b/docs/man/chasquid.1
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "chasquid 1"
-.TH chasquid 1 "2018-07-22" "" ""
+.TH chasquid 1 "2020-05-16" "" ""
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -154,6 +154,11 @@ It's written in Go, and distributed under the Apache license 2.0.
 .IP "\fB\-config_dir\fR \fIdir\fR" 8
 .IX Item "-config_dir dir"
 configuration directory (default \fI/etc/chasquid\fR)
+.IP "\fB\-config_overrides\fR \fIconfig\fR" 8
+.IX Item "-config_overrides config"
+configuration values (in text protobuf format) to override the on-disk
+configuration with. This should only be needed in very specific cases for
+deployments where editing the configuration file is not feasible.
 .IP "\fB\-alsologtostderr\fR" 8
 .IX Item "-alsologtostderr"
 also log to stderr, in addition to the file
diff --git a/docs/man/chasquid.1.pod b/docs/man/chasquid.1.pod
index 56035d1..8f328e1 100644
--- a/docs/man/chasquid.1.pod
+++ b/docs/man/chasquid.1.pod
@@ -22,6 +22,12 @@ It's written in Go, and distributed under the Apache license 2.0.
 
 configuration directory (default F</etc/chasquid>)
 
+=item B<-config_overrides> I<config>
+
+configuration values (in text protobuf format) to override the on-disk
+configuration with. This should only be needed in very specific cases for
+deployments where editing the configuration file is not feasible.
+
 =item B<-alsologtostderr>
 
 also log to stderr, in addition to the file
diff --git a/internal/config/config.go b/internal/config/config.go
index bb0564d..3731dc5 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -33,8 +33,8 @@ var defaultConfig = &Config{
 	MailLogPath: "<syslog>",
 }
 
-// Load the config from the given file.
-func Load(path string) (*Config, error) {
+// Load the config from the given file, with the given overrides.
+func Load(path, overrides string) (*Config, error) {
 	// Start with a copy of the default config.
 	c := proto.Clone(defaultConfig).(*Config)
 
@@ -51,6 +51,14 @@ func Load(path string) (*Config, error) {
 	}
 	override(c, fromFile)
 
+	// Handle command line overrides.
+	fromOverrides := &Config{}
+	err = prototext.Unmarshal([]byte(overrides), fromOverrides)
+	if err != nil {
+		return nil, fmt.Errorf("parsing override: %v", err)
+	}
+	override(c, fromOverrides)
+
 	// Handle hostname separate, because if it is set, we don't need to call
 	// os.Hostname which can fail.
 	if c.Hostname == "" {
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index b0e54c2..d539187 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -27,7 +27,7 @@ func mustCreateConfig(t *testing.T, contents string) (string, string) {
 func TestEmptyConfig(t *testing.T) {
 	tmpDir, path := mustCreateConfig(t, "")
 	defer testlib.RemoveIfOk(t, tmpDir)
-	c, err := Load(path)
+	c, err := Load(path, "")
 	if err != nil {
 		t.Fatalf("error loading empty config: %v", err)
 	}
@@ -59,12 +59,18 @@ func TestFullConfig(t *testing.T) {
 	tmpDir, path := mustCreateConfig(t, confStr)
 	defer testlib.RemoveIfOk(t, tmpDir)
 
+	overrideStr := `
+		hostname: "proust"
+		submission_address: ":999"
+		dovecot_auth: true
+	`
+
 	expected := &Config{
-		Hostname:      "joust",
+		Hostname:      "proust",
 		MaxDataSizeMb: 26,
 
 		SmtpAddress:              []string{":1234", ":5678"},
-		SubmissionAddress:        []string{":10001", ":10002"},
+		SubmissionAddress:        []string{":999"},
 		SubmissionOverTlsAddress: []string{"systemd"},
 		MonitoringAddress:        ":1111",
 
@@ -77,9 +83,11 @@ func TestFullConfig(t *testing.T) {
 		DropCharacters:   ".",
 
 		MailLogPath: "<syslog>",
+
+		DovecotAuth: true,
 	}
 
-	c, err := Load(path)
+	c, err := Load(path, overrideStr)
 	if err != nil {
 		t.Fatalf("error loading non-existent config: %v", err)
 	}
@@ -93,7 +101,7 @@ func TestFullConfig(t *testing.T) {
 }
 
 func TestErrorLoading(t *testing.T) {
-	c, err := Load("/does/not/exist")
+	c, err := Load("/does/not/exist", "")
 	if err == nil {
 		t.Fatalf("loaded a non-existent config: %v", c)
 	}
@@ -104,7 +112,18 @@ func TestBrokenConfig(t *testing.T) {
 		t, "<invalid> this is not a valid protobuf")
 	defer testlib.RemoveIfOk(t, tmpDir)
 
-	c, err := Load(path)
+	c, err := Load(path, "")
+	if err == nil {
+		t.Fatalf("loaded an invalid config: %v", c)
+	}
+}
+
+func TestBrokenOverride(t *testing.T) {
+	tmpDir, path := mustCreateConfig(
+		t, `hostname: "test"`)
+	defer testlib.RemoveIfOk(t, tmpDir)
+
+	c, err := Load(path, "broken override")
 	if err == nil {
 		t.Fatalf("loaded an invalid config: %v", c)
 	}