Skip to content

Commit d3af627

Browse files
committed
feat: Add --no-warn-conficts flag to suppress warnings on conflicts.
When loading secrets from multiple services, chamber will warn about environment variables that get overwritten. Use the `--no-warn-conflicts` flag to suppress these warnings when conflicts are expected: chamber exec --no-warn-conflicts defaults/app production/app -- <your executable> Add tests to verify the storage systems handle invalid AWS_PROFILE variables. Fix tests which don't handle invalid profiles.
1 parent 7c8de43 commit d3af627

File tree

5 files changed

+156
-2
lines changed

5 files changed

+156
-2
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,14 @@ example, if you do `chamber exec app apptwo -- ...` and both apps have a secret
239239
named `api_key`, the `api_key` from `apptwo` will be the one set in your
240240
environment.
241241

242+
When loading secrets from multiple services, chamber will warn about environment
243+
variables that get overwritten. Use the `--no-warn-conflicts` flag to suppress
244+
these warnings when conflicts are expected:
245+
246+
```bash
247+
$ chamber exec --no-warn-conflicts app apptwo -- <your executable>
248+
```
249+
242250
### Reading
243251

244252
```bash

cmd/exec.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ var strictValue string
2424
// Default value to expect in strict mode
2525
const strictValueDefault = "chamberme"
2626

27+
// When true, suppress warnings about conflicting environment variables
28+
var noWarnConflicts bool
29+
2730
// execCmd represents the exec command
2831
var execCmd = &cobra.Command{
2932
Use: "exec <service...> -- <command> [<arg...>]",
@@ -58,6 +61,11 @@ Given a secret store like this:
5861
$ HOME=/tmp DB_USERNAME=chamberme DB_PASSWORD=chamberme chamber exec --strict --pristine service exec -- env
5962
DB_USERNAME=root
6063
DB_PASSWORD=hunter22
64+
65+
--no-warn-conflicts suppresses warnings when services overwrite environment variables
66+
67+
$ chamber exec --no-warn-conflicts service1 service2 -- env
68+
# No warnings about conflicting keys between service1 and service2
6169
`,
6270
}
6371

@@ -68,6 +76,7 @@ only inject secrets for which there is a corresponding env var with value
6876
<strict-value>, and fail if there are any env vars with that value missing
6977
from secrets`)
7078
execCmd.Flags().StringVar(&strictValue, "strict-value", strictValueDefault, "value to expect in --strict mode")
79+
execCmd.Flags().BoolVar(&noWarnConflicts, "no-warn-conflicts", false, "suppress warnings when services overwrite environment variables (useful when conflicts are expected)")
7180
RootCmd.AddCommand(execCmd)
7281
}
7382

@@ -123,8 +132,10 @@ func execRun(cmd *cobra.Command, args []string) error {
123132
return fmt.Errorf("Failed to list store contents: %w", err)
124133
}
125134

126-
for _, c := range collisions {
127-
fmt.Fprintf(os.Stderr, "warning: service %s overwriting environment variable %s\n", service, c)
135+
if !noWarnConflicts {
136+
for _, c := range collisions {
137+
fmt.Fprintf(os.Stderr, "warning: service %s overwriting environment variable %s\n", service, c)
138+
}
128139
}
129140
}
130141
}

cmd/exec_test.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package cmd
2+
3+
import (
4+
"bytes"
5+
"os"
6+
"testing"
7+
8+
"github.com/spf13/cobra"
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestExecCommandFlags(t *testing.T) {
13+
t.Run("no-warn-conflicts flag should be defined", func(t *testing.T) {
14+
// Reset the flag to default state
15+
noWarnConflicts = false
16+
17+
// Create a new exec command to test flag parsing
18+
cmd := &cobra.Command{}
19+
cmd.Flags().BoolVar(&noWarnConflicts, "no-warn-conflicts", false, "suppress warnings when services overwrite environment variables")
20+
21+
// Test that the flag can be set
22+
err := cmd.Flags().Set("no-warn-conflicts", "true")
23+
assert.NoError(t, err)
24+
assert.True(t, noWarnConflicts)
25+
26+
// Test that the flag can be unset
27+
err = cmd.Flags().Set("no-warn-conflicts", "false")
28+
assert.NoError(t, err)
29+
assert.False(t, noWarnConflicts)
30+
})
31+
32+
t.Run("no-warn-conflicts flag should have correct default value", func(t *testing.T) {
33+
// Reset to default state
34+
noWarnConflicts = false
35+
assert.False(t, noWarnConflicts)
36+
})
37+
}
38+
39+
func TestWarningBehavior(t *testing.T) {
40+
// Helper function to capture stderr output
41+
captureStderr := func(fn func()) string {
42+
oldStderr := os.Stderr
43+
r, w, _ := os.Pipe()
44+
os.Stderr = w
45+
46+
fn()
47+
48+
w.Close()
49+
os.Stderr = oldStderr
50+
51+
var buf bytes.Buffer
52+
buf.ReadFrom(r)
53+
return buf.String()
54+
}
55+
56+
t.Run("should emit warnings when noWarnConflicts is false", func(t *testing.T) {
57+
// Reset to default state
58+
noWarnConflicts = false
59+
60+
// Simulate the warning logic from exec.go
61+
collisions := []string{"DB_HOST", "API_KEY"}
62+
service := "test-service"
63+
64+
output := captureStderr(func() {
65+
if !noWarnConflicts {
66+
for _, c := range collisions {
67+
os.Stderr.WriteString("warning: service " + service + " overwriting environment variable " + c + "\n")
68+
}
69+
}
70+
})
71+
72+
assert.Contains(t, output, "warning: service test-service overwriting environment variable DB_HOST")
73+
assert.Contains(t, output, "warning: service test-service overwriting environment variable API_KEY")
74+
})
75+
76+
t.Run("should not emit warnings when noWarnConflicts is true", func(t *testing.T) {
77+
// Set flag to suppress warnings
78+
noWarnConflicts = true
79+
80+
// Simulate the warning logic from exec.go
81+
collisions := []string{"DB_HOST", "API_KEY"}
82+
service := "test-service"
83+
84+
output := captureStderr(func() {
85+
if !noWarnConflicts {
86+
for _, c := range collisions {
87+
os.Stderr.WriteString("warning: service " + service + " overwriting environment variable " + c + "\n")
88+
}
89+
}
90+
})
91+
92+
assert.Empty(t, output)
93+
})
94+
95+
t.Run("should handle empty collisions list", func(t *testing.T) {
96+
// Reset to default state
97+
noWarnConflicts = false
98+
99+
// Simulate the warning logic with no collisions
100+
collisions := []string{}
101+
service := "test-service"
102+
103+
output := captureStderr(func() {
104+
if !noWarnConflicts {
105+
for _, c := range collisions {
106+
os.Stderr.WriteString("warning: service " + service + " overwriting environment variable " + c + "\n")
107+
}
108+
}
109+
})
110+
111+
assert.Empty(t, output)
112+
})
113+
}

store/secretsmanagerstore_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,17 @@ func TestNewSecretsManagerStore(t *testing.T) {
222222
secretsmanagerClient := s.svc.(*secretsmanager.Client)
223223
assert.Nil(t, secretsmanagerClient.Options().BaseEndpoint)
224224
})
225+
226+
t.Run("Should return error when AWS_PROFILE points to invalid profile", func(t *testing.T) {
227+
os.Setenv("AWS_PROFILE", "invalid-profile")
228+
defer os.Unsetenv("AWS_PROFILE")
229+
230+
s, err := NewSecretsManagerStore(context.Background(), 1)
231+
assert.NotNil(t, err)
232+
assert.Nil(t, s)
233+
// Verify it's a profile not exist error
234+
assert.Contains(t, err.Error(), "profile")
235+
})
225236
}
226237

227238
func TestSecretsManagerWrite(t *testing.T) {

store/ssmstore_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,17 @@ func TestNewSSMStore(t *testing.T) {
395395
assert.Nil(t, err)
396396
assert.Equal(t, DefaultRetryMode, s.config.RetryMode)
397397
})
398+
399+
t.Run("Should return error when AWS_PROFILE points to invalid profile", func(t *testing.T) {
400+
os.Setenv("AWS_PROFILE", "invalid-profile")
401+
defer os.Unsetenv("AWS_PROFILE")
402+
403+
s, err := NewSSMStore(context.Background(), 1)
404+
assert.NotNil(t, err)
405+
assert.Nil(t, s)
406+
// Verify it's a profile not exist error
407+
assert.Contains(t, err.Error(), "profile")
408+
})
398409
}
399410

400411
func TestNewSSMStoreWithRetryMode(t *testing.T) {

0 commit comments

Comments
 (0)