Skip to content
4 changes: 2 additions & 2 deletions internal/pkg/deploy/cloudformation/stack/backend_svc.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,8 @@ func (s *BackendService) Template() (string, error) {
Port: targetContainerPort,
Name: targetContainer,
},
HTTPHealthCheck: convertHTTPHealthCheck(&s.manifest.HTTP.Main.HealthCheck),
ALBListener: albListenerConfig,
GracePeriod: s.convertGracePeriod(),
ALBListener: albListenerConfig,

// Custom Resource Config.
CustomResources: crs,
Expand Down
20 changes: 4 additions & 16 deletions internal/pkg/deploy/cloudformation/stack/backend_svc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,10 +311,7 @@ Outputs:
Port: "8080",
Name: "api",
},
HTTPHealthCheck: template.HTTPHealthCheckOpts{
HealthCheckPath: manifest.DefaultHealthCheckPath,
GracePeriod: manifest.DefaultHealthCheckGracePeriod,
},
GracePeriod: aws.Int64(manifest.DefaultHealthCheckGracePeriod),
CustomResources: map[string]template.S3ObjectLocation{
"EnvControllerFunction": {
Bucket: "my-bucket",
Expand Down Expand Up @@ -515,16 +512,6 @@ Outputs:
Name: "envoy",
Port: "443",
},
HTTPHealthCheck: template.HTTPHealthCheckOpts{
HealthCheckPath: "/healthz",
Port: "4200",
SuccessCodes: "418",
HealthyThreshold: aws.Int64(64),
UnhealthyThreshold: aws.Int64(63),
Timeout: aws.Int64(62),
Interval: aws.Int64(61),
GracePeriod: 60,
},
CustomResources: map[string]template.S3ObjectLocation{
"EnvControllerFunction": {
Bucket: "my-bucket",
Expand Down Expand Up @@ -553,8 +540,9 @@ Outputs:
MinHealthyPercent: 0,
MaxPercent: 100,
},
EntryPoint: []string{"enter", "from"},
Command: []string{"here"},
EntryPoint: []string{"enter", "from"},
Command: []string{"here"},
GracePeriod: aws.Int64(60),
ALBListener: &template.ALBListener{
Rules: []template.ALBListenerRule{
{
Expand Down
4 changes: 2 additions & 2 deletions internal/pkg/deploy/cloudformation/stack/lb_web_svc.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,8 @@ func (s *LoadBalancedWebService) Template() (string, error) {
Port: targetContainerPort,
Name: targetContainer,
},
HTTPHealthCheck: convertHTTPHealthCheck(&s.manifest.HTTPOrBool.Main.HealthCheck),
ALBListener: albListenerConfig,
GracePeriod: s.convertGracePeriod(),
ALBListener: albListenerConfig,

// NLB configs.
AppDNSName: nlbConfig.appDNSName,
Expand Down
15 changes: 6 additions & 9 deletions internal/pkg/deploy/cloudformation/stack/lb_web_svc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,14 +262,10 @@ Outputs:
// THEN
require.NoError(t, err)
require.Equal(t, template.WorkloadOpts{
AppName: "phonetool",
EnvName: "test",
WorkloadName: "frontend",
WorkloadType: manifestinfo.LoadBalancedWebServiceType,
HTTPHealthCheck: template.HTTPHealthCheckOpts{
HealthCheckPath: "/",
GracePeriod: 60,
},
AppName: "phonetool",
EnvName: "test",
WorkloadName: "frontend",
WorkloadType: manifestinfo.LoadBalancedWebServiceType,
HTTPTargetContainer: template.HTTPTargetContainer{
Name: "frontend",
Port: "80",
Expand Down Expand Up @@ -344,7 +340,8 @@ Outputs:
},
},
},
ALBEnabled: true,
GracePeriod: aws.Int64(int64(60)),
ALBEnabled: true,
PortMappings: []*template.PortMapping{
{
Protocol: "tcp",
Expand Down
21 changes: 21 additions & 0 deletions internal/pkg/deploy/cloudformation/stack/transformers.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ func convertNLBHealthCheck(nlbHC *manifest.NLBHealthCheckArgs) template.NLBHealt
hc := template.NLBHealthCheck{
HealthyThreshold: nlbHC.HealthyThreshold,
UnhealthyThreshold: nlbHC.UnhealthyThreshold,
GracePeriod: aws.Int64(int64(manifest.DefaultHealthCheckGracePeriod)),
}
if nlbHC.Port != nil {
hc.Port = strconv.Itoa(aws.IntValue(nlbHC.Port))
Expand All @@ -408,6 +409,9 @@ func convertNLBHealthCheck(nlbHC *manifest.NLBHealthCheckArgs) template.NLBHealt
if nlbHC.Interval != nil {
hc.Interval = aws.Int64(int64(nlbHC.Interval.Seconds()))
}
if nlbHC.GracePeriod != nil {
hc.GracePeriod = aws.Int64(int64(nlbHC.GracePeriod.Seconds()))
}
return hc
}

Expand Down Expand Up @@ -550,6 +554,13 @@ func (s *BackendService) convertALBListener() (*template.ALBListener, error) {
}, nil
}

func (s *BackendService) convertGracePeriod() *int64 {
if s.manifest.HTTP.Main.HealthCheck.Advanced.GracePeriod != nil {
return aws.Int64(int64(s.manifest.HTTP.Main.HealthCheck.Advanced.GracePeriod.Seconds()))
}
return aws.Int64(int64(manifest.DefaultHealthCheckGracePeriod))
}

type loadBalancerTargeter interface {
MainContainerPort() string
ExposedPorts() (manifest.ExposedPortsIndex, error)
Expand Down Expand Up @@ -678,6 +689,16 @@ func (s *LoadBalancedWebService) convertNetworkLoadBalancer() (networkLoadBalanc
return config, nil
}

func (s *LoadBalancedWebService) convertGracePeriod() *int64 {
if s.manifest.HTTPOrBool.Main.HealthCheck.Advanced.GracePeriod != nil {
return aws.Int64(int64(s.manifest.HTTPOrBool.Main.HealthCheck.Advanced.GracePeriod.Seconds()))
}
if s.manifest.NLBConfig.Listener.HealthCheck.GracePeriod != nil {
return aws.Int64(int64(s.manifest.NLBConfig.Listener.HealthCheck.GracePeriod.Seconds()))
}
return aws.Int64(int64(manifest.DefaultHealthCheckGracePeriod))
}

func convertExecuteCommand(e *manifest.ExecuteCommand) *template.ExecuteCommandOpts {
if e.Config.IsEmpty() && !aws.BoolValue(e.Enable) {
return nil
Expand Down
36 changes: 36 additions & 0 deletions internal/pkg/manifest/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,42 @@ func (e *errFieldMutualExclusive) Error() string {
return fmt.Sprintf(`must specify one, not both, of "%s" and "%s"`, e.firstField, e.secondField)
}

type errGracePeriodsInBothALBAndNLB struct {
errFieldMutualExclusive
}

func (e *errGracePeriodsInBothALBAndNLB) RecommendedActions() string {
return `"grace_period" is a configuration shared by "http" and "nlb". Specify it only once under either "http" or "nlb".`
}

type errGracePeriodSpecifiedInAdditionalListener struct {
index int
}

func (e *errGracePeriodSpecifiedInAdditionalListener) Error() string {
return fmt.Sprintf(`"grace_period" specified for "nlb.additional_listeners[%d]"`, e.index)
}

// RecommendActions returns recommended actions to be taken after the error.
func (e *errGracePeriodSpecifiedInAdditionalListener) RecommendActions() string {
return fmt.Sprintf(`Instead of under "nlb.additional_listeners[%d].healthcheck", specify "grace_period" under the top-level "nlb.healthcheck".`, e.index)

}

type errGracePeriodSpecifiedInAdditionalRule struct {
index int
}

func (e *errGracePeriodSpecifiedInAdditionalRule) Error() string {
return fmt.Sprintf(`"grace_period" specified for "http.additional_rules[%d]"`, e.index)
}

// RecommendActions returns recommended actions to be taken after the error.
func (e *errGracePeriodSpecifiedInAdditionalRule) RecommendActions() string {
return fmt.Sprintf(`Instead of under "http.additional_rules[%d].healthcheck", specify "grace_period" under the top-level "http.healthcheck".`, e.index)

}

type errSpecifiedBothIngressFields struct {
firstField string
secondField string
Expand Down
1 change: 1 addition & 0 deletions internal/pkg/manifest/svc.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ type NLBHealthCheckArgs struct {
UnhealthyThreshold *int64 `yaml:"unhealthy_threshold"`
Timeout *time.Duration `yaml:"timeout"`
Interval *time.Duration `yaml:"interval"`
GracePeriod *time.Duration `yaml:"grace_period"`
}

func (h *NLBHealthCheckArgs) isEmpty() bool {
Expand Down
56 changes: 56 additions & 0 deletions internal/pkg/manifest/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ func (l LoadBalancedWebServiceConfig) validate() error {
missingFields: []string{"http", "nlb"},
}
}
if err = l.validateGracePeriod(); err != nil {
return fmt.Errorf(`validate "grace_period": %w`, err)
}
if l.HTTPOrBool.Disabled() && (!l.Count.AdvancedCount.Requests.IsEmpty() || !l.Count.AdvancedCount.ResponseTime.IsEmpty()) {
return errors.New(`scaling based on "nlb" requests or response time is not supported`)
}
Expand Down Expand Up @@ -800,6 +803,59 @@ func (r HTTPOrBool) validate() error {
return r.HTTP.validate()
}

func (l LoadBalancedWebServiceConfig) validateGracePeriod() error {
Comment thread
paragbhingre marked this conversation as resolved.
gracePeriodForALB, err := l.validateGracePeriodForALB()
if err != nil {
return err
}
gracePeriodForNLB, err := l.validateGracePeriodForNLB()
if err != nil {
return err
}
if gracePeriodForALB && gracePeriodForNLB {
return &errGracePeriodsInBothALBAndNLB{
errFieldMutualExclusive: errFieldMutualExclusive{
firstField: "http.healthcheck.grace_period",
secondField: "nlb.healthcheck.grace_period",
},
}
}

return nil
}

// validateGracePeriodForALB validates if ALB has grace period mentioned in their additional listeners rules.
func (cfg *LoadBalancedWebServiceConfig) validateGracePeriodForALB() (bool, error) {
var exist bool
if cfg.HTTPOrBool.Main.HealthCheck.Advanced.GracePeriod != nil {
exist = true
}
for idx, rule := range cfg.HTTPOrBool.AdditionalRoutingRules {
if rule.HealthCheck.Advanced.GracePeriod != nil {
return exist, &errGracePeriodSpecifiedInAdditionalRule{
index: idx,
}
}
}
return exist, nil
}

// validateGracePeriodForNLB validates if NLB has grace period mentioned in their additional listeners.
func (cfg *LoadBalancedWebServiceConfig) validateGracePeriodForNLB() (bool, error) {
var exist bool
if cfg.NLBConfig.Listener.HealthCheck.GracePeriod != nil {
exist = true
}
for idx, listener := range cfg.NLBConfig.AdditionalListeners {
if listener.HealthCheck.GracePeriod != nil {
return exist, &errGracePeriodSpecifiedInAdditionalListener{
index: idx,
}
}
}
return exist, nil
}

// validate returns nil if HTTP is configured correctly.
func (r RoutingRule) validate() error {
if r.Path == nil {
Expand Down
93 changes: 93 additions & 0 deletions internal/pkg/manifest/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,99 @@ func TestLoadBalancedWebService_validate(t *testing.T) {
},
wantedErrorMsgPrefix: `validate "image": `,
},
"error if fail to validate grace_period when specified in the additional listener rules of ALB": {
lbConfig: LoadBalancedWebService{
LoadBalancedWebServiceConfig: LoadBalancedWebServiceConfig{
ImageConfig: testImageConfig,
HTTPOrBool: HTTPOrBool{
HTTP: HTTP{
Main: RoutingRule{
Path: stringP("/"),
HealthCheck: HealthCheckArgsOrString{
Union: AdvancedToUnion[string](HTTPHealthCheckArgs{
Path: aws.String("/testing"),
HealthyThreshold: aws.Int64(5),
UnhealthyThreshold: aws.Int64(6),
Interval: durationp(78 * time.Second),
Timeout: durationp(9 * time.Second),
}),
},
},
AdditionalRoutingRules: []RoutingRule{
{
Path: stringP("/"),
HealthCheck: HealthCheckArgsOrString{
Union: AdvancedToUnion[string](HTTPHealthCheckArgs{
Path: aws.String("/testing"),
HealthyThreshold: aws.Int64(5),
UnhealthyThreshold: aws.Int64(6),
Interval: durationp(78 * time.Second),
Timeout: durationp(9 * time.Second),
GracePeriod: durationp(9 * time.Second),
}),
},
},
},
},
},
},
},
wantedError: fmt.Errorf(`validate "grace_period": %w`, &errGracePeriodSpecifiedInAdditionalRule{0}),
},
"error if fail to validate grace_period when specified the additional listener of NLB": {
lbConfig: LoadBalancedWebService{
LoadBalancedWebServiceConfig: LoadBalancedWebServiceConfig{
ImageConfig: testImageConfig,
HTTPOrBool: HTTPOrBool{
Enabled: aws.Bool(false),
},
NLBConfig: NetworkLoadBalancerConfiguration{
Listener: NetworkLoadBalancerListener{
Port: stringP("80"),
HealthCheck: NLBHealthCheckArgs{GracePeriod: durationp(9 * time.Second)},
},
AdditionalListeners: []NetworkLoadBalancerListener{
{
Port: stringP("80"),
HealthCheck: NLBHealthCheckArgs{GracePeriod: durationp(9 * time.Second)},
},
},
},
},
},
wantedError: fmt.Errorf(`validate "grace_period": %w`, &errGracePeriodSpecifiedInAdditionalListener{0}),
},
"error if fail to validate grace_period when specified in ALB and NLB at the same time": {
lbConfig: LoadBalancedWebService{
LoadBalancedWebServiceConfig: LoadBalancedWebServiceConfig{
ImageConfig: testImageConfig,
HTTPOrBool: HTTPOrBool{
HTTP: HTTP{
Main: RoutingRule{
Path: stringP("/"),
HealthCheck: HealthCheckArgsOrString{
Union: AdvancedToUnion[string](HTTPHealthCheckArgs{
Path: aws.String("/testing"),
HealthyThreshold: aws.Int64(5),
UnhealthyThreshold: aws.Int64(6),
Interval: durationp(78 * time.Second),
Timeout: durationp(9 * time.Second),
GracePeriod: durationp(9 * time.Second),
}),
},
},
},
},
NLBConfig: NetworkLoadBalancerConfiguration{
Listener: NetworkLoadBalancerListener{
Port: stringP("80"),
HealthCheck: NLBHealthCheckArgs{GracePeriod: durationp(9 * time.Second)},
},
},
},
},
wantedError: fmt.Errorf(`validate "grace_period": %w`, &errGracePeriodsInBothALBAndNLB{errFieldMutualExclusive{firstField: "http.healthcheck.grace_period", secondField: "nlb.healthcheck.grace_period"}}),
},
"error if fail to validate http": {
lbConfig: LoadBalancedWebService{
LoadBalancedWebServiceConfig: LoadBalancedWebServiceConfig{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ Resources:
{{- "\n"}}{{ include "service-base-properties" . | indent 6 }}
ServiceRegistries: !If [ExposePort, [{RegistryArn: !GetAtt DiscoveryService.Arn, Port: !Ref TargetPort}], !Ref "AWS::NoValue"]
{{- if .ALBListener}}
{{- if .HTTPHealthCheck.GracePeriod }}
HealthCheckGracePeriodSeconds: {{.HTTPHealthCheck.GracePeriod}}
{{- if .GracePeriod }}
HealthCheckGracePeriodSeconds: {{.GracePeriod}}
{{- end }}
LoadBalancers:
{{- range $i, $rule := .ALBListener.Rules}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ Resources:
Properties:
{{include "service-base-properties" . | indent 6}}
# This may need to be adjusted if the container takes a while to start up
{{- if .HTTPHealthCheck.GracePeriod }}
HealthCheckGracePeriodSeconds: {{.HTTPHealthCheck.GracePeriod}}
{{- if .GracePeriod }}
HealthCheckGracePeriodSeconds: {{.GracePeriod}}
{{- end }}
LoadBalancers:
{{- if .ALBListener}}
Expand Down
Loading