From ee169231b378cc86b0deb8fee886d36a0ebfecea Mon Sep 17 00:00:00 2001 From: Fredrik Berntsson Date: Sun, 11 Aug 2024 11:40:24 +0200 Subject: [PATCH] Add a flag for the run command to read envs from file --- cmd/run.go | 17 ++++++++++++++ go.mod | 5 ++-- go.sum | 10 ++++---- internal/config/config.go | 14 +++++++---- internal/config/config_test.go | 43 ++++++++++++++++++++++++++++++++++ testdata/.env | 22 +++++++++++++++++ testdata/.gitignore | 1 + version/version.go | 2 +- 8 files changed, 103 insertions(+), 11 deletions(-) create mode 100644 testdata/.env create mode 100644 testdata/.gitignore diff --git a/cmd/run.go b/cmd/run.go index 137e6fa..85ffd36 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -3,13 +3,18 @@ package cmd import ( "github.com/lovelaze/nebula-sync/internal/config" "github.com/lovelaze/nebula-sync/internal/service" + "github.com/rs/zerolog/log" "github.com/spf13/cobra" ) +var envFile string + var runCmd = &cobra.Command{ Use: "run", Short: "Run sync", Run: func(cmd *cobra.Command, args []string) { + readEnvFile() + conf := config.Config{} conf.Load() @@ -20,4 +25,16 @@ var runCmd = &cobra.Command{ func init() { rootCmd.AddCommand(runCmd) + + runCmd.Flags().StringVar(&envFile, "env-file", "", "Read env from `.env` file") +} + +func readEnvFile() { + if envFile == "" { + return + } + + if err := config.LoadEnvFile(envFile); err != nil { + log.Fatal().Err(err).Msg("error loading env file") + } } diff --git a/go.mod b/go.mod index 7eed283..2aef30c 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/lovelaze/nebula-sync go 1.22 require ( + github.com/joho/godotenv v1.5.1 github.com/kelseyhightower/envconfig v1.4.0 github.com/pkg/errors v0.9.1 github.com/robfig/cron/v3 v3.0.1 @@ -24,7 +25,7 @@ require ( github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/docker/docker v27.0.3+incompatible // indirect + github.com/docker/docker v27.1.1+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect @@ -64,7 +65,7 @@ require ( go.opentelemetry.io/otel/metric v1.24.0 // indirect go.opentelemetry.io/otel/trace v1.24.0 // indirect golang.org/x/crypto v0.22.0 // indirect - golang.org/x/sys v0.23.0 // indirect + golang.org/x/sys v0.24.0 // indirect golang.org/x/time v0.1.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect google.golang.org/grpc v1.59.0 // indirect diff --git a/go.sum b/go.sum index 735c022..708a005 100644 --- a/go.sum +++ b/go.sum @@ -28,8 +28,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v27.0.3+incompatible h1:aBGI9TeQ4MPlhquTQKq9XbK79rKFVwXNUAYz9aXyEBE= -github.com/docker/docker v27.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= +github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -58,6 +58,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rH github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -186,8 +188,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/internal/config/config.go b/internal/config/config.go index 1953a07..7d91634 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,9 +1,10 @@ package config import ( + "github.com/joho/godotenv" "github.com/kelseyhightower/envconfig" "github.com/lovelaze/nebula-sync/internal/pihole/model" - "log" + "github.com/rs/zerolog/log" ) type Config struct { @@ -44,7 +45,7 @@ type SyncSettings struct { func (c *Config) Load() { if err := envconfig.Process("", c); err != nil { - log.Fatal(err) + log.Fatal().Err(err).Msg("Failed to process env vars") } if !c.FullSync { @@ -55,12 +56,12 @@ func (c *Config) Load() { func (c *Config) loadSyncSettings() { manualGravity := ManualGravity{} if err := envconfig.Process("", &manualGravity); err != nil { - log.Fatal(err) + log.Fatal().Err(err).Msg("Failed to process gravity env vars") } manualConfig := ManualConfig{} if err := envconfig.Process("", &manualConfig); err != nil { - log.Fatal(err) + log.Fatal().Err(err).Msg("Failed to process config env vars") } c.SyncSettings = &SyncSettings{ @@ -68,3 +69,8 @@ func (c *Config) loadSyncSettings() { Config: &manualConfig, } } + +func LoadEnvFile(filename string) error { + log.Debug().Msgf("Loading env file: %s", filename) + return godotenv.Load(filename) +} diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 7dd52f7..9fcfd5f 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -2,6 +2,8 @@ package config import ( "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "os" "testing" ) @@ -68,3 +70,44 @@ func TestConfig_loadSyncSettings(t *testing.T) { assert.True(t, conf.SyncSettings.Gravity.Client) assert.True(t, conf.SyncSettings.Gravity.ClientByGroup) } + +func TestConfig_LoadEnvFile(t *testing.T) { + os.Clearenv() + err := LoadEnvFile("../../testdata/.env") + + require.NoError(t, err) + + assert.Equal(t, "http://ph1.example.com|password", os.Getenv("PRIMARY")) + assert.Equal(t, "http://ph2.example.com|password", os.Getenv("REPLICAS")) + assert.Equal(t, "false", os.Getenv("FULL_SYNC")) + assert.Equal(t, "* * * * *", os.Getenv("CRON")) + + assert.Equal(t, "true", os.Getenv("SYNC_CONFIG_DNS")) + assert.Equal(t, "true", os.Getenv("SYNC_CONFIG_DHCP")) + assert.Equal(t, "true", os.Getenv("SYNC_CONFIG_NTP")) + assert.Equal(t, "true", os.Getenv("SYNC_CONFIG_RESOLVER")) + assert.Equal(t, "true", os.Getenv("SYNC_CONFIG_DATABASE")) + assert.Equal(t, "true", os.Getenv("SYNC_CONFIG_MISC")) + assert.Equal(t, "true", os.Getenv("SYNC_CONFIG_DEBUG")) + + assert.Equal(t, "true", os.Getenv("SYNC_GRAVITY_DHCP_LEASES")) + assert.Equal(t, "true", os.Getenv("SYNC_GRAVITY_GROUP")) + assert.Equal(t, "true", os.Getenv("SYNC_GRAVITY_AD_LIST")) + assert.Equal(t, "true", os.Getenv("SYNC_GRAVITY_AD_LIST_BY_GROUP")) + assert.Equal(t, "true", os.Getenv("SYNC_GRAVITY_DOMAIN_LIST")) + assert.Equal(t, "true", os.Getenv("SYNC_GRAVITY_DOMAIN_LIST_BY_GROUP")) + assert.Equal(t, "true", os.Getenv("SYNC_GRAVITY_CLIENT")) + assert.Equal(t, "true", os.Getenv("SYNC_GRAVITY_CLIENT_BY_GROUP")) + + os.Clearenv() +} + +func TestConfig_LoadEnvFile_precedence(t *testing.T) { + assert.Empty(t, os.Getenv("CRON")) + t.Setenv("CRON", "0 0 * * *") + + err := LoadEnvFile("../../testdata/.env") + require.NoError(t, err) + + assert.Equal(t, "0 0 * * *", os.Getenv("CRON")) +} diff --git a/testdata/.env b/testdata/.env new file mode 100644 index 0000000..4510c6b --- /dev/null +++ b/testdata/.env @@ -0,0 +1,22 @@ +PRIMARY=http://ph1.example.com|password +REPLICAS=http://ph2.example.com|password +FULL_SYNC=false + +CRON=* * * * * + +SYNC_CONFIG_DNS=true +SYNC_CONFIG_DHCP=true +SYNC_CONFIG_NTP=true +SYNC_CONFIG_RESOLVER=true +SYNC_CONFIG_DATABASE=true +SYNC_CONFIG_MISC=true +SYNC_CONFIG_DEBUG=true + +SYNC_GRAVITY_DHCP_LEASES=true +SYNC_GRAVITY_GROUP=true +SYNC_GRAVITY_AD_LIST=true +SYNC_GRAVITY_AD_LIST_BY_GROUP=true +SYNC_GRAVITY_DOMAIN_LIST=true +SYNC_GRAVITY_DOMAIN_LIST_BY_GROUP=true +SYNC_GRAVITY_CLIENT=true +SYNC_GRAVITY_CLIENT_BY_GROUP=true \ No newline at end of file diff --git a/testdata/.gitignore b/testdata/.gitignore new file mode 100644 index 0000000..1e18f27 --- /dev/null +++ b/testdata/.gitignore @@ -0,0 +1 @@ +!.env \ No newline at end of file diff --git a/version/version.go b/version/version.go index ec1cdb3..c62f7e6 100644 --- a/version/version.go +++ b/version/version.go @@ -1,3 +1,3 @@ package version -const Version = "0.1.1" +const Version = "0.2.0"