From 758ff062beb6156de33bc70f23123c2693a91a40 Mon Sep 17 00:00:00 2001 From: Matt Loberg Date: Thu, 16 May 2024 16:14:21 -0500 Subject: [PATCH 1/2] feat(validate): add products field to dependencies Allow different dependencies in different products. Requires SERVICE_PRODUCT to be set. --- config.go | 2 ++ main.go | 7 ++++++- validate.go | 11 ++++++----- validate_test.go | 37 +++++++++++++++++++++++++++++++++---- 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/config.go b/config.go index 38c0ad9..bdc9966 100644 --- a/config.go +++ b/config.go @@ -10,6 +10,7 @@ import ( type Config struct { Service string Environment string + Product string Region string ServiceDefinition string SkipValidation bool @@ -20,6 +21,7 @@ func NewFromEnv() *Config { cfg := &Config{ Service: os.Getenv("SERVICE_NAME"), Environment: os.Getenv("SERVICE_ENV"), + Product: os.Getenv("SERVICE_PRODUCT"), Region: os.Getenv("AWS_REGION"), ServiceDefinition: os.Getenv("SERVICE_DEFINITION"), SkipValidation: false, diff --git a/main.go b/main.go index e002888..be2a1cd 100644 --- a/main.go +++ b/main.go @@ -33,7 +33,12 @@ func main() { } cfg := NewFromEnv() - logger = logger.With("env", cfg.Environment, "service", cfg.Service, "region", cfg.Region) + logger = logger.With( + slog.String("env", cfg.Environment), + slog.String("service", cfg.Service), + slog.String("product", cfg.Product), + slog.String("region", cfg.Region), + ) slog.SetDefault(logger) if len(os.Args) < 2 { diff --git a/validate.go b/validate.go index c15e433..609eebc 100644 --- a/validate.go +++ b/validate.go @@ -26,16 +26,17 @@ type ( Partial bool `json:"-"` } dependencyInner struct { - Key string `json:"key"` - Regions []string `json:"regions"` + Key string `json:"key"` + Regions []string `json:"regions"` + Products []string `json:"products"` } ) var ErrMissingEnvVars = errors.New("missing required environment variables") // Required returns true if the dependency is required for the given region -func (d *dependency) Required(region string) bool { - return d.Regions == nil || lo.Contains(d.Regions, region) +func (d *dependency) Required(product, region string) bool { + return (d.Products == nil || lo.Contains(d.Products, product)) && (d.Regions == nil || lo.Contains(d.Regions, region)) } // UnmarshalJSON handles the dependency being a string or an object @@ -91,7 +92,7 @@ func validate(ctx context.Context, c *Config, e *EnvMap, l *slog.Logger) error { func missing(deps []dependency, c *Config, e *EnvMap) []string { res := []string{} for _, d := range deps { - if !d.Required(c.Region) { + if !d.Required(c.Product, c.Region) { continue } diff --git a/validate_test.go b/validate_test.go index b6f4e0d..9062f09 100644 --- a/validate_test.go +++ b/validate_test.go @@ -16,12 +16,30 @@ func TestDependency_Required(t *testing.T) { Regions: []string{"us-east-1"}, }, } - assert.True(t, d.Required("us-east-1")) - assert.False(t, d.Required("us-west-2")) + assert.True(t, d.Required("test", "us-east-1")) + assert.False(t, d.Required("test", "us-west-2")) d = dependency{} - assert.True(t, d.Required("us-east-1")) - assert.True(t, d.Required("us-west-2")) + assert.True(t, d.Required("test", "us-east-1")) + assert.True(t, d.Required("test", "us-west-2")) + + d = dependency{ + dependencyInner: dependencyInner{ + Products: []string{"test"}, + }, + } + assert.True(t, d.Required("test", "us-east-1")) + assert.False(t, d.Required("prod", "us-east-1")) + + d = dependency{ + dependencyInner: dependencyInner{ + Regions: []string{"us-east-1"}, + Products: []string{"test"}, + }, + } + assert.False(t, d.Required("prod", "us-east-1")) + assert.False(t, d.Required("test", "us-west-2")) + assert.True(t, d.Required("test", "us-east-1")) } func TestValidate(t *testing.T) { //nolint:funlen @@ -37,6 +55,10 @@ func TestValidate(t *testing.T) { //nolint:funlen }, { "key":"BAZ" + }, + { + "key": "FIZZ", + "products": ["test"] } ], "optional": [ @@ -107,4 +129,11 @@ func TestValidate(t *testing.T) { //nolint:funlen require.NoError(t, err) assert.NotContains(t, log.String(), "Missing required environment variables") assert.NotContains(t, log.String(), "Missing optional environment variables") + + // Missing required env vars for product + log.Reset() + c.Product = "test" + err = validate(context.TODO(), c, e, l) + require.ErrorIs(t, err, ErrMissingEnvVars) + assert.Contains(t, log.String(), `Missing required environment variables","env_vars":["FIZZ"]`) } From 4c0b3993dcbcef619297dfc3515b25baf8348dc5 Mon Sep 17 00:00:00 2001 From: Matt Loberg Date: Thu, 16 May 2024 16:18:09 -0500 Subject: [PATCH 2/2] feat: add program to log To help determine where the log is coming from, add a program field to logs --- config.go | 7 +++++++ main.go | 1 + 2 files changed, 8 insertions(+) diff --git a/config.go b/config.go index bdc9966..badf86b 100644 --- a/config.go +++ b/config.go @@ -4,6 +4,7 @@ import ( "fmt" "log/slog" "os" + "path/filepath" "strconv" ) @@ -14,6 +15,7 @@ type Config struct { Region string ServiceDefinition string SkipValidation bool + Program string } // NewFromEnv creates a new Config from environment variables and defaults @@ -25,6 +27,7 @@ func NewFromEnv() *Config { Region: os.Getenv("AWS_REGION"), ServiceDefinition: os.Getenv("SERVICE_DEFINITION"), SkipValidation: false, + Program: "docker-bootstrap", } if s, err := strconv.ParseBool(os.Getenv("BOOTSTRAP_SKIP_VALIDATION")); err == nil { @@ -48,6 +51,10 @@ func NewFromEnv() *Config { cfg.ServiceDefinition = "service.json" } + if ex, err := os.Executable(); err == nil { + cfg.Program = filepath.Base(ex) + } + return cfg } diff --git a/main.go b/main.go index be2a1cd..e6f925b 100644 --- a/main.go +++ b/main.go @@ -38,6 +38,7 @@ func main() { slog.String("service", cfg.Service), slog.String("product", cfg.Product), slog.String("region", cfg.Region), + slog.String("program", cfg.Program), ) slog.SetDefault(logger)