Add file-backed environment secrets
This commit is contained in:
@@ -26,6 +26,9 @@ func TestDefaults(t *testing.T) {
|
||||
if cfg.Location.ID != "home" || cfg.Location.Name != "Brentwood" || cfg.Location.Region != "St. Louis Metro" {
|
||||
t.Fatalf("Location = %#v, want home/Brentwood/St. Louis Metro", cfg.Location)
|
||||
}
|
||||
if cfg.Secrets.Directory != "" {
|
||||
t.Fatalf("Secrets.Directory = %q, want empty", cfg.Secrets.Directory)
|
||||
}
|
||||
if cfg.MissingSource.Default != MissingSourceWarn {
|
||||
t.Fatalf("MissingSource.Default = %q, want warn", cfg.MissingSource.Default)
|
||||
}
|
||||
@@ -112,3 +115,167 @@ func TestLoadAppliesOverrides(t *testing.T) {
|
||||
t.Fatalf("Timezone = %q, want +09:30", cfg.WeatherAPI.Timezone)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadSecretsDisabledLeavesEnvironmentUnchanged(t *testing.T) {
|
||||
t.Setenv("WEATHERREPORTER_DISABLED_SECRET", "original")
|
||||
|
||||
if err := loadSecrets(SecretsConfig{}); err != nil {
|
||||
t.Fatalf("loadSecrets() error = %v", err)
|
||||
}
|
||||
if got := os.Getenv("WEATHERREPORTER_DISABLED_SECRET"); got != "original" {
|
||||
t.Fatalf("environment value = %q, want original", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFileLoadsSecretsDirectory(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
secretsDir := filepath.Join(dir, "secrets")
|
||||
if err := os.Mkdir(secretsDir, 0o700); err != nil {
|
||||
t.Fatalf("create secrets directory: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(secretsDir, "WEATHERREPORTER_SECRET"), []byte("from-file"), 0o600); err != nil {
|
||||
t.Fatalf("write secret: %v", err)
|
||||
}
|
||||
path := filepath.Join(dir, "config.yml")
|
||||
if err := os.WriteFile(path, []byte("secrets:\n directory: "+secretsDir+"\n"), 0o600); err != nil {
|
||||
t.Fatalf("write config fixture: %v", err)
|
||||
}
|
||||
|
||||
t.Setenv("WEATHERREPORTER_SECRET", "")
|
||||
if _, err := LoadFile(path); err != nil {
|
||||
t.Fatalf("LoadFile() error = %v", err)
|
||||
}
|
||||
if got := os.Getenv("WEATHERREPORTER_SECRET"); got != "from-file" {
|
||||
t.Fatalf("environment value = %q, want from-file", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadSecretsOverwritesExistingEnvironment(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(dir, "WEATHERREPORTER_SECRET"), []byte("from-file"), 0o600); err != nil {
|
||||
t.Fatalf("write secret: %v", err)
|
||||
}
|
||||
t.Setenv("WEATHERREPORTER_SECRET", "existing")
|
||||
|
||||
if err := loadSecrets(SecretsConfig{Directory: dir}); err != nil {
|
||||
t.Fatalf("loadSecrets() error = %v", err)
|
||||
}
|
||||
if got := os.Getenv("WEATHERREPORTER_SECRET"); got != "from-file" {
|
||||
t.Fatalf("environment value = %q, want from-file", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadSecretsTrimsOneTrailingLineEnding(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{name: "LF", input: "value\n", want: "value"},
|
||||
{name: "CRLF", input: "value\r\n", want: "value"},
|
||||
{name: "TwoLF", input: "value\n\n", want: "value\n"},
|
||||
{name: "LoneCR", input: "value\r", want: "value\r"},
|
||||
{name: "NoNewline", input: "value", want: "value"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(dir, "WEATHERREPORTER_SECRET"), []byte(tt.input), 0o600); err != nil {
|
||||
t.Fatalf("write secret: %v", err)
|
||||
}
|
||||
t.Setenv("WEATHERREPORTER_SECRET", "")
|
||||
|
||||
if err := loadSecrets(SecretsConfig{Directory: dir}); err != nil {
|
||||
t.Fatalf("loadSecrets() error = %v", err)
|
||||
}
|
||||
if got := os.Getenv("WEATHERREPORTER_SECRET"); got != tt.want {
|
||||
t.Fatalf("environment value = %q, want %q", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadSecretsRejectsInvalidDirectoryEntries(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setup func(t *testing.T, dir string)
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "InvalidFilename",
|
||||
setup: func(t *testing.T, dir string) {
|
||||
if err := os.WriteFile(filepath.Join(dir, "1INVALID"), []byte("secret-value"), 0o600); err != nil {
|
||||
t.Fatalf("write invalid secret: %v", err)
|
||||
}
|
||||
},
|
||||
wantErr: "invalid environment variable name",
|
||||
},
|
||||
{
|
||||
name: "Subdirectory",
|
||||
setup: func(t *testing.T, dir string) {
|
||||
if err := os.Mkdir(filepath.Join(dir, "SUBDIR"), 0o700); err != nil {
|
||||
t.Fatalf("create subdirectory: %v", err)
|
||||
}
|
||||
},
|
||||
wantErr: "not a directory",
|
||||
},
|
||||
{
|
||||
name: "Symlink",
|
||||
setup: func(t *testing.T, dir string) {
|
||||
target := filepath.Join(dir, "TARGET")
|
||||
if err := os.WriteFile(target, []byte("secret-value"), 0o600); err != nil {
|
||||
t.Fatalf("write target: %v", err)
|
||||
}
|
||||
if err := os.Symlink(target, filepath.Join(dir, "SYMLINK")); err != nil {
|
||||
t.Fatalf("create symlink: %v", err)
|
||||
}
|
||||
},
|
||||
wantErr: "not a symlink",
|
||||
},
|
||||
{
|
||||
name: "Unreadable",
|
||||
setup: func(t *testing.T, dir string) {
|
||||
path := filepath.Join(dir, "UNREADABLE")
|
||||
if err := os.WriteFile(path, []byte("secret-value"), 0o600); err != nil {
|
||||
t.Fatalf("write unreadable secret: %v", err)
|
||||
}
|
||||
if err := os.Chmod(path, 0o000); err != nil {
|
||||
t.Fatalf("chmod unreadable secret: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_ = os.Chmod(path, 0o600)
|
||||
})
|
||||
},
|
||||
wantErr: "read secret file",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
tt.setup(t, dir)
|
||||
|
||||
err := loadSecrets(SecretsConfig{Directory: dir})
|
||||
if err == nil {
|
||||
t.Fatal("loadSecrets() error = nil, want error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), tt.wantErr) {
|
||||
t.Fatalf("error = %q, want %q", err.Error(), tt.wantErr)
|
||||
}
|
||||
if strings.Contains(err.Error(), "secret-value") {
|
||||
t.Fatalf("error = %q, want no secret value", err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadSecretsRejectsMissingDirectory(t *testing.T) {
|
||||
err := loadSecrets(SecretsConfig{Directory: filepath.Join(t.TempDir(), "missing")})
|
||||
if err == nil {
|
||||
t.Fatal("loadSecrets() error = nil, want missing directory error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "read secrets directory") {
|
||||
t.Fatalf("error = %q, want read secrets directory context", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user