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

fix: force env action when deploying service with updated environment #3957

Merged
merged 12 commits into from
Aug 30, 2022
8 changes: 6 additions & 2 deletions cf-custom-resources/lib/env-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,11 @@ const controlEnv = async function (
(param) => !envSet.has(param)
);
const exportedValues = getExportedValues(updatedEnvStack);
// Return if there are no parameter changes.
// If there are no changes in env-controller managed parameters, the custom
// resource may have been triggered because the env template is upgraded,
// and the service template is attempting to retrieve the latest Outputs
// from the env stack (see PR #3957). Return the updated Outputs instead
// of triggering an env-controller update of the environment.
const shouldUpdateAliases = needUpdateAliases(envParams, workload, aliases);
if (
parametersToRemove.length + parametersToAdd.length === 0 &&
Expand Down Expand Up @@ -314,7 +318,7 @@ const getExportedValues = function (stack) {
const exportedValues = {};
stack.Outputs.forEach((output) => {
if (ignoredEnvOutputs.has(output.OutputKey)) {
return
return;
}
exportedValues[output.OutputKey] = output.OutputValue;
});
Expand Down
3 changes: 3 additions & 0 deletions internal/pkg/cli/deploy/svc.go
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,7 @@ type StackRuntimeConfiguration struct {
RootUserARN string
Tags map[string]string
CustomResourceURLs map[string]string
EnvVersion string
}

// DeployWorkloadInput is the input of DeployWorkload.
Expand Down Expand Up @@ -1000,6 +1001,7 @@ func (d *workloadDeployer) runtimeConfig(in *StackRuntimeConfiguration) (*stack.
AccountID: d.env.AccountID,
Region: d.env.Region,
CustomResourcesURL: in.CustomResourceURLs,
EnvVersion: in.EnvVersion,
}, nil
}
return &stack.RuntimeConfig{
Expand All @@ -1015,6 +1017,7 @@ func (d *workloadDeployer) runtimeConfig(in *StackRuntimeConfiguration) (*stack.
AccountID: d.env.AccountID,
Region: d.env.Region,
CustomResourcesURL: in.CustomResourceURLs,
EnvVersion: in.EnvVersion,
}, nil
}

Expand Down
5 changes: 5 additions & 0 deletions internal/pkg/cli/job_deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ func (o *deployJobOpts) Execute() error {
if err != nil {
return fmt.Errorf("upload deploy resources for job %s: %w", o.name, err)
}
envVersion, err := o.envFeaturesDescriber.Version()
if err != nil {
return fmt.Errorf("get version of environment %q: %w", o.envName, err)
}
if _, err = deployer.DeployWorkload(&deploy.DeployWorkloadInput{
StackRuntimeConfiguration: deploy.StackRuntimeConfiguration{
ImageDigest: uploadOut.ImageDigest,
Expand All @@ -186,6 +190,7 @@ func (o *deployJobOpts) Execute() error {
RootUserARN: o.rootUserARN,
Tags: tags.Merge(o.targetApp.Tags, o.resourceTags),
CustomResourceURLs: uploadOut.CustomResourceURLs,
EnvVersion: envVersion,
},
Options: deploy.Options{
DisableRollback: o.disableRollback,
Expand Down
19 changes: 18 additions & 1 deletion internal/pkg/cli/job_deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,14 +264,31 @@ func TestJobDeployOpts_Execute(t *testing.T) {
},
}
m.mockEnvFeaturesDescriber.EXPECT().AvailableFeatures().Return([]string{"mockFeature1", "mockFeature2"}, nil)
m.mockEnvFeaturesDescriber.EXPECT().Version().Times(0)
m.mockEnvFeaturesDescriber.EXPECT().Version().Times(1)
m.mockDeployer.EXPECT().UploadArtifacts().Return(&deploy.UploadArtifactsOutput{}, nil)
m.mockDeployer.EXPECT().DeployWorkload(gomock.Any()).Return(nil, mockError)
m.mockDeployer.EXPECT().IsServiceAvailableInRegion("").Return(false, nil)
},

wantedError: fmt.Errorf("deploy job upload to environment prod-iad: some error"),
},
"error if unable to get env version": {
mock: func(m *deployMocks) {
m.mockWsReader.EXPECT().ReadWorkloadManifest(mockJobName).Return([]byte(""), nil)
m.mockInterpolator.EXPECT().Interpolate("").Return("", nil)
m.mockMft = &mockWorkloadMft{
mockRequiredEnvironmentFeatures: func() []string {
return []string{"mockFeature1"}
},
}
m.mockEnvFeaturesDescriber.EXPECT().AvailableFeatures().Return([]string{"mockFeature1", "mockFeature2"}, nil)
m.mockDeployer.EXPECT().IsServiceAvailableInRegion("").Return(false, nil)
m.mockDeployer.EXPECT().UploadArtifacts().Return(&deploy.UploadArtifactsOutput{}, nil)
m.mockEnvFeaturesDescriber.EXPECT().Version().Return("", errors.New("some error"))
},

wantedError: fmt.Errorf(`get version of environment "prod-iad": some error`),
},
}

for name, tc := range testCases {
Expand Down
5 changes: 5 additions & 0 deletions internal/pkg/cli/svc_deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,10 @@ func (o *deploySvcOpts) Execute() error {
if err != nil {
return err
}
envVersion, err := o.envFeaturesDescriber.Version()
if err != nil {
return fmt.Errorf("get version of environment %q: %w", o.envName, err)
}
deployRecs, err := deployer.DeployWorkload(&clideploy.DeployWorkloadInput{
StackRuntimeConfiguration: clideploy.StackRuntimeConfiguration{
ImageDigest: uploadOut.ImageDigest,
Expand All @@ -226,6 +230,7 @@ func (o *deploySvcOpts) Execute() error {
RootUserARN: o.rootUserARN,
Tags: tags.Merge(targetApp.Tags, o.resourceTags),
CustomResourceURLs: uploadOut.CustomResourceURLs,
EnvVersion: envVersion,
},
Options: clideploy.Options{
ForceNewUpdate: o.forceNewUpdate,
Expand Down
19 changes: 18 additions & 1 deletion internal/pkg/cli/svc_deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,14 +209,31 @@ func TestSvcDeployOpts_Execute(t *testing.T) {
},
}
m.mockEnvFeaturesDescriber.EXPECT().AvailableFeatures().Return([]string{"mockFeature1", "mockFeature2"}, nil)
m.mockEnvFeaturesDescriber.EXPECT().Version().Times(0)
m.mockEnvFeaturesDescriber.EXPECT().Version().Times(1)
m.mockDeployer.EXPECT().UploadArtifacts().Return(&deploy.UploadArtifactsOutput{}, nil)
m.mockDeployer.EXPECT().DeployWorkload(gomock.Any()).Return(nil, mockError)
m.mockDeployer.EXPECT().IsServiceAvailableInRegion("").Return(false, nil)
},

wantedError: fmt.Errorf("deploy service frontend to environment prod-iad: some error"),
},
"error if unable to get env version": {
mock: func(m *deployMocks) {
m.mockWsReader.EXPECT().ReadWorkloadManifest(mockSvcName).Return([]byte(""), nil)
m.mockInterpolator.EXPECT().Interpolate("").Return("", nil)
m.mockMft = &mockWorkloadMft{
mockRequiredEnvironmentFeatures: func() []string {
return []string{"mockFeature1"}
},
}
m.mockEnvFeaturesDescriber.EXPECT().AvailableFeatures().Return([]string{"mockFeature1", "mockFeature2"}, nil)
m.mockDeployer.EXPECT().IsServiceAvailableInRegion("").Return(false, nil)
m.mockDeployer.EXPECT().UploadArtifacts().Return(&deploy.UploadArtifactsOutput{}, nil)
m.mockEnvFeaturesDescriber.EXPECT().Version().Return("", errors.New("some error"))
},

wantedError: fmt.Errorf(`get version of environment "prod-iad": some error`),
},
}

for name, tc := range testCases {
Expand Down
5 changes: 5 additions & 0 deletions internal/pkg/cli/svc_package.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,10 @@ func (o *packageSvcOpts) getWorkloadStack(generator workloadStackGenerator) (*cf
}
uploadOut = *out
}
envVersion, err := o.envFeaturesDescriber.Version()
if err != nil {
return nil, fmt.Errorf("get version of environment %q: %w", o.envName, err)
}
output, err := generator.GenerateCloudFormationTemplate(&clideploy.GenerateCloudFormationTemplateInput{
StackRuntimeConfiguration: clideploy.StackRuntimeConfiguration{
RootUserARN: o.rootUserARN,
Expand All @@ -333,6 +337,7 @@ func (o *packageSvcOpts) getWorkloadStack(generator workloadStackGenerator) (*cf
EnvFileARN: uploadOut.EnvFileARN,
AddonsURL: uploadOut.AddonsURL,
CustomResourceURLs: uploadOut.CustomResourceURLs,
EnvVersion: envVersion,
},
})
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions internal/pkg/cli/svc_package_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,13 +207,15 @@ count: 1`
StackRuntimeConfiguration: deploy.StackRuntimeConfiguration{
ImageDigest: aws.String(mockDigest),
RootUserARN: mockARN,
EnvVersion: "v1.42.0",
},
}).Return(&deploy.GenerateCloudFormationTemplateOutput{
Template: "mystack",
Parameters: "myparams",
}, nil)
m.interpolator.EXPECT().Interpolate(lbwsMft).Return(lbwsMft, nil)
m.generator.EXPECT().AddonsTemplate().Return("", nil)
m.envFeaturesDescriber.EXPECT().Version().Return("v1.42.0", nil)
},
wantedStack: "mystack",
wantedParams: "myparams",
Expand All @@ -230,10 +232,12 @@ count: 1`
m.ws.EXPECT().ReadWorkloadManifest("api").Return([]byte(rdwsMft), nil)
m.interpolator.EXPECT().Interpolate(rdwsMft).Return(rdwsMft, nil)
m.generator.EXPECT().AddonsTemplate().Return("", nil)
m.envFeaturesDescriber.EXPECT().Version().Return("v1.42.0", nil)
m.generator.EXPECT().GenerateCloudFormationTemplate(&deploy.GenerateCloudFormationTemplateInput{
StackRuntimeConfiguration: deploy.StackRuntimeConfiguration{
ImageDigest: aws.String(""),
RootUserARN: mockARN,
EnvVersion: "v1.42.0",
},
}).Return(&deploy.GenerateCloudFormationTemplateOutput{
Template: "mystack",
Expand Down
1 change: 1 addition & 0 deletions internal/pkg/deploy/cloudformation/stack/backend_svc.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ func (s *BackendService) Template() (string, error) {
EnvName: s.env,
WorkloadName: s.name,
SerializedManifest: string(s.rawManifest),
EnvVersion: s.rc.EnvVersion,

Variables: s.manifest.BackendServiceConfig.Variables,
Secrets: convertSecrets(s.manifest.BackendServiceConfig.Secrets),
Expand Down
1 change: 1 addition & 0 deletions internal/pkg/deploy/cloudformation/stack/lb_web_svc.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ func (s *LoadBalancedWebService) Template() (string, error) {
EnvName: s.env,
WorkloadName: s.name,
SerializedManifest: string(s.rawManifest),
EnvVersion: s.rc.EnvVersion,

Variables: s.manifest.TaskConfig.Variables,
Secrets: convertSecrets(s.manifest.TaskConfig.Secrets),
Expand Down
1 change: 1 addition & 0 deletions internal/pkg/deploy/cloudformation/stack/rd_web_svc.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ func (s *RequestDrivenWebService) Template() (string, error) {
EnvName: s.env,
WorkloadName: s.name,
SerializedManifest: string(s.rawManifest),
EnvVersion: s.rc.EnvVersion,

Variables: s.manifest.Variables,
StartCommand: s.manifest.StartCommand,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ func (j *ScheduledJob) Template() (string, error) {
ServiceDiscoveryEndpoint: j.rc.ServiceDiscoveryEndpoint,
Publish: publishers,
Platform: convertPlatform(j.manifest.Platform),
EnvVersion: j.rc.EnvVersion,

CustomResources: crs,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,7 @@ Resources:
Workload: !Ref WorkloadName
EnvStack: !Sub "${AppName}-${EnvName}"
Parameters: ["InternalALBWorkloads"]
EnvVersion:
EnvControllerFunction:
Type: AWS::Lambda::Function
Properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ Resources:
Workload: !Ref WorkloadName
EnvStack: !Sub "${AppName}-${EnvName}"
Parameters: ["InternalALBWorkloads"]
EnvVersion:
EnvControllerFunction:
Type: AWS::Lambda::Function
Properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ Resources:
Workload: !Ref WorkloadName
EnvStack: !Sub "${AppName}-${EnvName}"
Parameters: ["InternalALBWorkloads"]
EnvVersion:
EnvControllerFunction:
Type: AWS::Lambda::Function
Properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ Resources:
Aliases: ["example.com", "foobar.com", "*.foobar.com"]
EnvStack: !Sub '${AppName}-${EnvName}'
Parameters: [InternalALBWorkloads]
EnvVersion:
EnvControllerFunction:
Type: AWS::Lambda::Function
Properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ Resources:
Workload: !Ref WorkloadName
EnvStack: !Sub "${AppName}-${EnvName}"
Parameters: []
EnvVersion:
EnvControllerFunction:
Type: AWS::Lambda::Function
Properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Resources:
EnvStack: !Sub '${AppName}-${EnvName}'
Parameters:
- EFSWorkloads
EnvVersion:

EnvControllerFunction:
Type: AWS::Lambda::Function
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ Resources:
Workload: !Ref WorkloadName
EnvStack: !Sub '${AppName}-${EnvName}'
Parameters: [NATWorkloads,]
EnvVersion:

EnvControllerFunction:
Type: AWS::Lambda::Function
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ Resources: # If a bucket URL is specified, that means the template exists.
Aliases: ["example.com"]
EnvStack: !Sub '${AppName}-${EnvName}'
Parameters: [ALBWorkloads, Aliases]
EnvVersion:
EnvControllerFunction:
Type: AWS::Lambda::Function
Properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ Resources: # If a bucket URL is specified, that means the template exists.
Workload: !Ref WorkloadName
EnvStack: !Sub '${AppName}-${EnvName}'
Parameters: [ALBWorkloads, Aliases]
EnvVersion:
EnvControllerFunction:
Type: AWS::Lambda::Function
Properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ Resources: # If a bucket URL is specified, that means the template exists.
Workload: !Ref WorkloadName
EnvStack: !Sub '${AppName}-${EnvName}'
Parameters: [Aliases]
EnvVersion:
EnvControllerFunction:
Type: AWS::Lambda::Function
Properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ Resources: # If a bucket URL is specified, that means the template exists.
Workload: !Ref WorkloadName
EnvStack: !Sub '${AppName}-${EnvName}'
Parameters: [Aliases]
EnvVersion:
EnvControllerFunction:
Type: AWS::Lambda::Function
Properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ Resources: # If a bucket URL is specified, that means the template exists.
Aliases: ["example.com"]
EnvStack: !Sub '${AppName}-${EnvName}'
Parameters: [ALBWorkloads, Aliases]
EnvVersion:
EnvControllerFunction:
Type: AWS::Lambda::Function
Properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ Resources: # If a bucket URL is specified, that means the template exists.
Aliases: ["example.com"]
EnvStack: !Sub '${AppName}-${EnvName}'
Parameters: [ALBWorkloads, Aliases]
EnvVersion:
EnvControllerFunction:
Type: AWS::Lambda::Function
Properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ Resources: # If a bucket URL is specified, that means the template exists.
Aliases: ["example.com"]
EnvStack: !Sub '${AppName}-${EnvName}'
Parameters: [ALBWorkloads, Aliases]
EnvVersion:
EnvControllerFunction:
Type: AWS::Lambda::Function
Properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ Resources: # If a bucket URL is specified, that means the template exists.
Workload: !Ref WorkloadName
EnvStack: !Sub '${AppName}-${EnvName}'
Parameters: [ALBWorkloads, Aliases]
EnvVersion:
EnvControllerFunction:
Type: AWS::Lambda::Function
Properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,7 @@ Resources:
Workload: !Ref WorkloadName
EnvStack: !Sub '${AppName}-${EnvName}'
Parameters: []
EnvVersion:
EnvControllerFunction:
Type: AWS::Lambda::Function
Properties:
Expand Down
1 change: 1 addition & 0 deletions internal/pkg/deploy/cloudformation/stack/worker_svc.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ func (s *WorkerService) Template() (string, error) {
EnvName: s.env,
WorkloadName: s.name,
SerializedManifest: string(s.rawManifest),
EnvVersion: s.rc.EnvVersion,

Variables: s.manifest.WorkerServiceConfig.Variables,
Secrets: convertSecrets(s.manifest.WorkerServiceConfig.Secrets),
Expand Down
1 change: 1 addition & 0 deletions internal/pkg/deploy/cloudformation/stack/workload.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ type RuntimeConfig struct {
ServiceDiscoveryEndpoint string // Endpoint for the service discovery namespace in the environment.
AccountID string
Region string
EnvVersion string
}

// ECRImage represents configuration about the pushed ECR image that is needed to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ EnvControllerAction:
{{- end}}
EnvStack: !Sub '${AppName}-${EnvName}'
Parameters: {{ envControllerParams . }}
EnvVersion: {{.EnvVersion}}

EnvControllerFunction:
Type: AWS::Lambda::Function
Expand Down
1 change: 1 addition & 0 deletions internal/pkg/template/workload.go
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,7 @@ type WorkloadOpts struct {
EnvName string
WorkloadName string
SerializedManifest string // Raw manifest file used to deploy the workload.
EnvVersion string

// Additional options that are common between **all** workload templates.
Variables map[string]string
Expand Down