Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

load secrets/configs from environment while parsing compose model #655

Merged
merged 1 commit into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 55 additions & 4 deletions loader/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,14 @@ import (
"github.com/compose-spec/compose-go/v2/types"
)

// Will update the environment variables for the format {- VAR} (without interpolation)
// This function should resolve context environment vars for include (passed in env_file)
func resolveServicesEnvironment(dict map[string]any, config types.ConfigDetails) {
// ResolveEnvironment update the environment variables for the format {- VAR} (without interpolation)
func ResolveEnvironment(dict map[string]any, environment types.Mapping) {
resolveServicesEnvironment(dict, environment)
resolveSecretsEnvironment(dict, environment)
resolveConfigsEnvironment(dict, environment)
}

func resolveServicesEnvironment(dict map[string]any, environment types.Mapping) {
services, ok := dict["services"].(map[string]any)
if !ok {
return
Expand All @@ -45,7 +50,7 @@ func resolveServicesEnvironment(dict map[string]any, config types.ConfigDetails)
if !ok {
continue
}
if found, ok := config.Environment[varEnv]; ok {
if found, ok := environment[varEnv]; ok {
envs = append(envs, fmt.Sprintf("%s=%s", varEnv, found))
} else {
// either does not exist or it was already resolved in interpolation
Expand All @@ -57,3 +62,49 @@ func resolveServicesEnvironment(dict map[string]any, config types.ConfigDetails)
}
dict["services"] = services
}

func resolveSecretsEnvironment(dict map[string]any, environment types.Mapping) {
secrets, ok := dict["secrets"].(map[string]any)
if !ok {
return
}

for name, cfg := range secrets {
secret, ok := cfg.(map[string]any)
if !ok {
continue
}
env, ok := secret["environment"].(string)
if !ok {
continue
}
if found, ok := environment[env]; ok {
secret["content"] = found
}
secrets[name] = secret
}
dict["secrets"] = secrets
}

func resolveConfigsEnvironment(dict map[string]any, environment types.Mapping) {
configs, ok := dict["configs"].(map[string]any)
if !ok {
return
}

for name, cfg := range configs {
config, ok := cfg.(map[string]any)
if !ok {
continue
}
env, ok := config["environment"].(string)
if !ok {
continue
}
if found, ok := environment[env]; ok {
config["content"] = found
}
configs[name] = config
}
dict["configs"] = configs
}
1 change: 1 addition & 0 deletions loader/full-struct_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,7 @@ func secrets(workingDir string) map[string]types.SecretConfig {
"secret4": {
Name: "bar",
Environment: "BAR",
Content: "this is a secret",
Extensions: map[string]interface{}{
"x-bar": "baz",
"x-foo": "bar",
Expand Down
2 changes: 1 addition & 1 deletion loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option
return nil, err
}
}
resolveServicesEnvironment(dict, config)
ResolveEnvironment(dict, config.Environment)

return dict, nil
}
Expand Down
26 changes: 26 additions & 0 deletions loader/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3288,3 +3288,29 @@ services:
build := p.Services["test"].Build
assert.DeepEqual(t, build.Entitlements, []string{"network.host", "security.insecure"})
}

func TestLoadSecretEnvironment(t *testing.T) {
config, err := loadYAMLWithEnv(`
name: load-secret-environment
configs:
config:
environment: GA
secrets:
secret:
environment: MEU
`, map[string]string{
"GA": "BU",
"MEU": "Shadoks",
})
assert.NilError(t, err)
assert.DeepEqual(t, config.Configs, types.Configs{
"config": {
Environment: "GA",
Content: "BU",
}})
assert.DeepEqual(t, config.Secrets, types.Secrets{
"secret": {
Environment: "MEU",
Content: "Shadoks",
}})
}
32 changes: 32 additions & 0 deletions types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -782,9 +782,41 @@ type ExtendsConfig struct {
// SecretConfig for a secret
type SecretConfig FileObjectConfig

// MarshalYAML makes SecretConfig implement yaml.Marshaller
func (s SecretConfig) MarshalYAML() (interface{}, error) {
// secret content is set while loading model. Never marshall it
s.Content = ""
return FileObjectConfig(s), nil
}

// MarshalJSON makes SecretConfig implement json.Marshaller
func (s SecretConfig) MarshalJSON() ([]byte, error) {
// secret content is set while loading model. Never marshall it
s.Content = ""
return json.Marshal(FileObjectConfig(s))
}

// ConfigObjConfig is the config for the swarm "Config" object
type ConfigObjConfig FileObjectConfig

// MarshalYAML makes ConfigObjConfig implement yaml.Marshaller
func (s ConfigObjConfig) MarshalYAML() (interface{}, error) {
// config content may have been set from environment while loading model. Marshall actual source
if s.Environment != "" {
s.Content = ""
}
return FileObjectConfig(s), nil
}

// MarshalJSON makes ConfigObjConfig implement json.Marshaller
func (s ConfigObjConfig) MarshalJSON() ([]byte, error) {
// config content may have been set from environment while loading model. Marshall actual source
if s.Environment != "" {
s.Content = ""
}
return json.Marshal(FileObjectConfig(s))
}

type IncludeConfig struct {
Path StringList `yaml:"path,omitempty" json:"path,omitempty"`
ProjectDirectory string `yaml:"project_directory,omitempty" json:"project_directory,omitempty"`
Expand Down