Skip to content

Commit 6cf8182

Browse files
authored
fix: honor env-specific image override (#2415)
This PR: 1. implements custom`merge.Merge` logic for `image` field of workload manifest. 2. `ApplyEnv` override immediately after reading manifest, so that the image is built based on env-specific image override. Fixes: #1702 and #2382. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
1 parent 670ef52 commit 6cf8182

18 files changed

+895
-110
lines changed

internal/pkg/cli/job_deploy.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type deployJobOpts struct {
3838

3939
store store
4040
ws wsJobDirReader
41-
unmarshal func(in []byte) (interface{}, error)
41+
unmarshal func(in []byte) (manifest.WorkloadManifest, error)
4242
cmd runner
4343
addons templater
4444
appCFN appResourcesGetter

internal/pkg/cli/svc_deploy.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ type deploySvcOpts struct {
5151
store store
5252
ws wsSvcDirReader
5353
imageBuilderPusher imageBuilderPusher
54-
unmarshal func([]byte) (interface{}, error)
54+
unmarshal func([]byte) (manifest.WorkloadManifest, error)
5555
s3 artifactUploader
5656
cmd runner
5757
addons templater
@@ -375,7 +375,11 @@ func (o *deploySvcOpts) manifest() (interface{}, error) {
375375
if err != nil {
376376
return nil, fmt.Errorf("unmarshal service %s manifest: %w", o.name, err)
377377
}
378-
return mft, nil
378+
envMft, err := mft.ApplyEnv(o.envName)
379+
if err != nil {
380+
return nil, fmt.Errorf("apply environment %s override: %s", o.envName, err)
381+
}
382+
return envMft, nil
379383
}
380384

381385
func (o *deploySvcOpts) runtimeConfig(addonsURL string) (*stack.RuntimeConfig, error) {

internal/pkg/cli/svc_deploy_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@ func TestSvcDeployOpts_stackConfiguration(t *testing.T) {
651651
appVersionGetter: mockAppVersionGetter,
652652
targetApp: tc.inApp,
653653
targetEnvironment: tc.inEnvironment,
654-
unmarshal: func(b []byte) (interface{}, error) {
654+
unmarshal: func(b []byte) (manifest.WorkloadManifest, error) {
655655
return &manifest.LoadBalancedWebService{
656656
Workload: manifest.Workload{
657657
Name: aws.String(mockSvcName),

internal/pkg/deploy/cloudformation/stack/backend_svc.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,24 +44,20 @@ func NewBackendService(mft *manifest.BackendService, env, app string, rc Runtime
4444
if err != nil {
4545
return nil, fmt.Errorf("new addons: %w", err)
4646
}
47-
envManifest, err := mft.ApplyEnv(env) // Apply environment overrides to the manifest values.
48-
if err != nil {
49-
return nil, fmt.Errorf("apply environment %s override: %w", env, err)
50-
}
5147
return &BackendService{
5248
ecsWkld: &ecsWkld{
5349
wkld: &wkld{
5450
name: aws.StringValue(mft.Name),
5551
env: env,
5652
app: app,
5753
rc: rc,
58-
image: envManifest.ImageConfig,
54+
image: mft.ImageConfig,
5955
parser: parser,
6056
addons: addons,
6157
},
62-
tc: envManifest.TaskConfig,
58+
tc: mft.TaskConfig,
6359
},
64-
manifest: envManifest,
60+
manifest: mft,
6561

6662
parser: parser,
6763
}, nil

internal/pkg/deploy/cloudformation/stack/lb_web_service_integration_test.go

Lines changed: 41 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -25,79 +25,59 @@ import (
2525

2626
const (
2727
svcManifestPath = "svc-manifest.yml"
28+
svcStackPath = "svc-test.stack.yml"
29+
svcParamsPath = "svc-test.params.json"
2830
)
2931

3032
func TestLoadBalancedWebService_Template(t *testing.T) {
31-
testCases := map[string]struct {
32-
envName string
33-
svcStackPath string
34-
svcParamsPath string
35-
}{
36-
"default env": {
37-
envName: "test",
38-
svcStackPath: "svc-test.stack.yml",
39-
svcParamsPath: "svc-test.params.json",
40-
},
41-
"staging env": {
42-
envName: "staging",
43-
svcStackPath: "svc-staging.stack.yml",
44-
svcParamsPath: "svc-staging.params.json",
45-
},
46-
"prod env": {
47-
envName: "prod",
48-
svcStackPath: "svc-prod.stack.yml",
49-
svcParamsPath: "svc-prod.params.json",
50-
},
51-
}
5233
path := filepath.Join("testdata", "workloads", svcManifestPath)
5334
manifestBytes, err := ioutil.ReadFile(path)
5435
require.NoError(t, err)
5536
mft, err := manifest.UnmarshalWorkload(manifestBytes)
5637
require.NoError(t, err)
57-
v, ok := mft.(*manifest.LoadBalancedWebService)
38+
envMft, err := mft.ApplyEnv(envName)
39+
require.NoError(t, err)
40+
v, ok := envMft.(*manifest.LoadBalancedWebService)
5841
require.True(t, ok)
5942

60-
for name, tc := range testCases {
61-
62-
serializer, err := stack.NewHTTPSLoadBalancedWebService(v, tc.envName, appName, stack.RuntimeConfig{})
43+
serializer, err := stack.NewHTTPSLoadBalancedWebService(v, envName, appName, stack.RuntimeConfig{})
6344

64-
tpl, err := serializer.Template()
65-
require.NoError(t, err, "template should render")
66-
regExpGUID := regexp.MustCompile(`([a-f\d]{8}-)([a-f\d]{4}-){3}([a-f\d]{12})`) // Matches random guids
67-
testName := fmt.Sprintf("CF Template should be equal/%s", name)
68-
parser := template.New()
69-
envController, err := parser.Read(envControllerPath)
45+
tpl, err := serializer.Template()
46+
require.NoError(t, err, "template should render")
47+
regExpGUID := regexp.MustCompile(`([a-f\d]{8}-)([a-f\d]{4}-){3}([a-f\d]{12})`) // Matches random guids
48+
testName := fmt.Sprintf("CF Template should be equal")
49+
parser := template.New()
50+
envController, err := parser.Read(envControllerPath)
51+
require.NoError(t, err)
52+
zipFile := envController.String()
53+
t.Run(testName, func(t *testing.T) {
54+
actualBytes := []byte(tpl)
55+
// Cut random GUID from template.
56+
actualBytes = regExpGUID.ReplaceAll(actualBytes, []byte("RandomGUID"))
57+
actualString := string(actualBytes)
58+
// Cut out zip file from EnvControllerAction for more readable output
59+
actualString = strings.ReplaceAll(actualString, zipFile, "Abracadabra")
60+
actualBytes = []byte(actualString)
61+
mActual := make(map[interface{}]interface{})
62+
require.NoError(t, yaml.Unmarshal(actualBytes, mActual))
63+
64+
expected, err := ioutil.ReadFile(filepath.Join("testdata", "workloads", svcStackPath))
65+
require.NoError(t, err, "should be able to read expected bytes")
66+
expectedBytes := []byte(expected)
67+
mExpected := make(map[interface{}]interface{})
68+
require.NoError(t, yaml.Unmarshal(expectedBytes, mExpected))
69+
require.Equal(t, mExpected, mActual)
70+
})
71+
72+
testName = fmt.Sprintf("Parameter values should render properly")
73+
t.Run(testName, func(t *testing.T) {
74+
actualParams, err := serializer.SerializedParameters()
7075
require.NoError(t, err)
71-
zipFile := envController.String()
72-
t.Run(testName, func(t *testing.T) {
73-
actualBytes := []byte(tpl)
74-
// Cut random GUID from template.
75-
actualBytes = regExpGUID.ReplaceAll(actualBytes, []byte("RandomGUID"))
76-
actualString := string(actualBytes)
77-
// Cut out zip file from EnvControllerAction for more readable output
78-
actualString = strings.ReplaceAll(actualString, zipFile, "Abracadabra")
79-
actualBytes = []byte(actualString)
80-
mActual := make(map[interface{}]interface{})
81-
require.NoError(t, yaml.Unmarshal(actualBytes, mActual))
8276

83-
expected, err := ioutil.ReadFile(filepath.Join("testdata", "workloads", tc.svcStackPath))
84-
require.NoError(t, err, "should be able to read expected bytes")
85-
expectedBytes := []byte(expected)
86-
mExpected := make(map[interface{}]interface{})
87-
require.NoError(t, yaml.Unmarshal(expectedBytes, mExpected))
88-
require.Equal(t, mExpected, mActual)
89-
})
90-
91-
testName = fmt.Sprintf("Parameter values should render properly/%s", name)
92-
t.Run(testName, func(t *testing.T) {
93-
actualParams, err := serializer.SerializedParameters()
94-
require.NoError(t, err)
95-
96-
path := filepath.Join("testdata", "workloads", tc.svcParamsPath)
97-
wantedCFNParamsBytes, err := ioutil.ReadFile(path)
98-
require.NoError(t, err)
77+
path := filepath.Join("testdata", "workloads", svcParamsPath)
78+
wantedCFNParamsBytes, err := ioutil.ReadFile(path)
79+
require.NoError(t, err)
9980

100-
require.Equal(t, string(wantedCFNParamsBytes), actualParams)
101-
})
102-
}
81+
require.Equal(t, string(wantedCFNParamsBytes), actualParams)
82+
})
10383
}

internal/pkg/deploy/cloudformation/stack/lb_web_svc.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,24 +52,20 @@ func NewLoadBalancedWebService(mft *manifest.LoadBalancedWebService, env, app st
5252
if err != nil {
5353
return nil, fmt.Errorf("new addons: %w", err)
5454
}
55-
envManifest, err := mft.ApplyEnv(env) // Apply environment overrides to the manifest values.
56-
if err != nil {
57-
return nil, fmt.Errorf("apply environment %s override: %s", env, err)
58-
}
5955
return &LoadBalancedWebService{
6056
ecsWkld: &ecsWkld{
6157
wkld: &wkld{
6258
name: aws.StringValue(mft.Name),
6359
env: env,
6460
app: app,
6561
rc: rc,
66-
image: envManifest.ImageConfig,
62+
image: mft.ImageConfig,
6763
parser: parser,
6864
addons: addons,
6965
},
70-
tc: envManifest.TaskConfig,
66+
tc: mft.TaskConfig,
7167
},
72-
manifest: envManifest,
68+
manifest: mft,
7369
httpsEnabled: false,
7470

7571
parser: parser,

internal/pkg/deploy/cloudformation/stack/rd_web_svc.go

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,26 +32,22 @@ func NewRequestDrivenWebService(mft *manifest.RequestDrivenWebService, env, app
3232
if err != nil {
3333
return nil, fmt.Errorf("new addons: %w", err)
3434
}
35-
envManifest, err := mft.ApplyEnv(env) // Apply environment overrides to the manifest values.
36-
if err != nil {
37-
return nil, fmt.Errorf("apply environment %s override: %s", env, err)
38-
}
3935
return &RequestDrivenWebService{
4036
appRunnerWkld: &appRunnerWkld{
4137
wkld: &wkld{
4238
name: aws.StringValue(mft.Name),
4339
env: env,
4440
app: app,
4541
rc: rc,
46-
image: envManifest.ImageConfig,
42+
image: mft.ImageConfig,
4743
addons: addons,
4844
parser: parser,
4945
},
50-
instanceConfig: envManifest.InstanceConfig,
51-
imageConfig: envManifest.ImageConfig,
52-
healthCheckConfig: envManifest.HealthCheckConfiguration,
46+
instanceConfig: mft.InstanceConfig,
47+
imageConfig: mft.ImageConfig,
48+
healthCheckConfig: mft.HealthCheckConfiguration,
5349
},
54-
manifest: envManifest,
50+
manifest: mft,
5551
parser: parser,
5652
}, nil
5753
}

internal/pkg/deploy/cloudformation/stack/scheduled_job.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,24 +95,20 @@ func NewScheduledJob(mft *manifest.ScheduledJob, env, app string, rc RuntimeConf
9595
if err != nil {
9696
return nil, fmt.Errorf("new addons: %w", err)
9797
}
98-
envManifest, err := mft.ApplyEnv(env)
99-
if err != nil {
100-
return nil, fmt.Errorf("apply environment %s override: %w", env, err)
101-
}
10298
return &ScheduledJob{
10399
ecsWkld: &ecsWkld{
104100
wkld: &wkld{
105101
name: aws.StringValue(mft.Name),
106102
env: env,
107103
app: app,
108104
rc: rc,
109-
image: envManifest.ImageConfig,
105+
image: mft.ImageConfig,
110106
parser: parser,
111107
addons: addons,
112108
},
113-
tc: envManifest.TaskConfig,
109+
tc: mft.TaskConfig,
114110
},
115-
manifest: envManifest,
111+
manifest: mft,
116112

117113
parser: parser,
118114
}, nil

internal/pkg/deploy/cloudformation/stack/scheduled_job_integration_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,11 @@ func TestScheduledJob_Template(t *testing.T) {
3434
require.NoError(t, err)
3535
mft, err := manifest.UnmarshalWorkload(manifestBytes)
3636
require.NoError(t, err)
37-
v, ok := mft.(*manifest.ScheduledJob)
37+
envMft, err := mft.ApplyEnv(envName)
38+
require.NoError(t, err)
39+
v, ok := envMft.(*manifest.ScheduledJob)
3840
require.True(t, ok)
41+
3942
serializer, err := stack.NewScheduledJob(v, envName, appName, stack.RuntimeConfig{})
4043

4144
tpl, err := serializer.Template()

internal/pkg/manifest/backend_svc.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func (s *BackendService) BuildArgs(wsRoot string) *DockerBuildArgs {
104104

105105
// ApplyEnv returns the service manifest with environment overrides.
106106
// If the environment passed in does not have any overrides then it returns itself.
107-
func (s BackendService) ApplyEnv(envName string) (*BackendService, error) {
107+
func (s BackendService) ApplyEnv(envName string) (WorkloadManifest, error) {
108108
overrideConfig, ok := s.Environments[envName]
109109
if !ok {
110110
return &s, nil
@@ -122,7 +122,8 @@ func (s BackendService) ApplyEnv(envName string) (*BackendService, error) {
122122
// Apply overrides to the original service s.
123123
err := mergo.Merge(&s, BackendService{
124124
BackendServiceConfig: *overrideConfig,
125-
}, mergo.WithOverride, mergo.WithOverwriteWithEmptyValue)
125+
}, mergo.WithOverride, mergo.WithOverwriteWithEmptyValue, mergo.WithTransformers(workloadTransformer{}))
126+
126127
if err != nil {
127128
return nil, err
128129
}

0 commit comments

Comments
 (0)