// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package manifest import ( "fmt" "github.com/aws/copilot-cli/internal/pkg/aws/cloudfront" "github.com/aws/copilot-cli/internal/pkg/template" "github.com/dustin/go-humanize/english" "strconv" "strings" ) // ErrInvalidWorkloadType occurs when a user requested a manifest template type that doesn't exist. type ErrInvalidWorkloadType struct { Type string } func (e *ErrInvalidWorkloadType) Error() string { return fmt.Sprintf("invalid manifest type: %s", e.Type) } // ErrInvalidPipelineManifestVersion occurs when the pipeline.yml/manifest.yml file // contains invalid schema version during unmarshalling. type ErrInvalidPipelineManifestVersion struct { invalidVersion PipelineSchemaMajorVersion } func (e *ErrInvalidPipelineManifestVersion) Error() string { return fmt.Sprintf("pipeline manifest contains invalid schema version: %d", e.invalidVersion) } // Is compares the 2 errors. Only returns true if the errors are of the same // type and contain the same information. func (e *ErrInvalidPipelineManifestVersion) Is(target error) bool { t, ok := target.(*ErrInvalidPipelineManifestVersion) return ok && t.invalidVersion == e.invalidVersion } // ErrUnknownProvider occurs CreateProvider() is called with configurations // that do not map to any supported provider. type ErrUnknownProvider struct { unknownProviderProperties interface{} } func (e *ErrUnknownProvider) Error() string { return fmt.Sprintf("no provider found for properties: %v", e.unknownProviderProperties) } // Is compares the 2 errors. Returns true if the errors are of the same // type func (e *ErrUnknownProvider) Is(target error) bool { _, ok := target.(*ErrUnknownProvider) return ok } type errFieldMustBeSpecified struct { missingField string conditionalFields []string allMustBeSpecified bool } func (e *errFieldMustBeSpecified) Error() string { errMsg := fmt.Sprintf(`%q must be specified`, e.missingField) if len(e.conditionalFields) == 0 { return errMsg } conjunction := "or" if e.allMustBeSpecified { conjunction = "and" } return fmt.Sprintf(`%s if %s %s specified`, errMsg, english.WordSeries(quoteStringSlice(e.conditionalFields), conjunction), english.PluralWord(len(e.conditionalFields), "is", "are")) } type errInvalidAutoscalingFieldsWithWkldType struct { invalidFields []string workloadType string } func (e *errInvalidAutoscalingFieldsWithWkldType) Error() string { return fmt.Sprintf("autoscaling %v %v %v invalid with workload type %v", english.PluralWord(len(e.invalidFields), "field", "fields"), english.WordSeries(template.QuoteSliceFunc(e.invalidFields), "and"), english.PluralWord(len(e.invalidFields), "is", "are"), e.workloadType) } type errFieldMutualExclusive struct { firstField string secondField string mustExist bool } func (e *errFieldMutualExclusive) Error() string { if e.mustExist { return fmt.Sprintf(`must specify one of "%s" and "%s"`, e.firstField, e.secondField) } 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 } func (e *errSpecifiedBothIngressFields) Error() string { return fmt.Sprintf(`must specify one, not both, of "%s" and "%s"`, e.firstField, e.secondField) } // RecommendActions returns recommended actions to be taken after the error. func (e *errSpecifiedBothIngressFields) RecommendActions() string { privateOrPublicField := strings.Split(e.firstField, ".")[0] if privateOrPublicField == "public" { return ` It looks like you specified ingress under both "http.public.security_groups.ingress" and "http.public.ingress". After Copilot v1.23.0, we have deprecated "http.public.security_groups.ingress" in favor of "http.public.ingress". This means that "http.public.security_groups.ingress.cdn" is removed in favor of "http.public.ingress.cdn". With the new release manifest configuration for cdn looks like: http: public: ingress: cdn: true ` } return ` It looks like you specified ingress under both "http.private.security_groups.ingress" and "http.private.ingress". After Copilot v1.23.0, we have deprecated "http.private.security_groups.ingress" in favor of "http.private.ingress". This means that "http.private.security_groups.ingress.from_vpc" is removed in favor of "http.private.ingress.vpc". With the new release manifest configuration for vpc looks like: http: private: ingress: vpc: true ` } type errRangeValueLessThanZero struct { min int max int spotFrom int } func (e *errRangeValueLessThanZero) Error() string { return fmt.Sprintf("min value %d, max value %d, and spot_from value %d must all be positive", e.min, e.max, e.spotFrom) } type errMinGreaterThanMax struct { min int max int } func (e *errMinGreaterThanMax) Error() string { return fmt.Sprintf("min value %d cannot be greater than max value %d", e.min, e.max) } type errAtLeastOneFieldMustBeSpecified struct { missingFields []string conditionalField string } func (e *errAtLeastOneFieldMustBeSpecified) Error() string { errMsg := fmt.Sprintf("must specify at least one of %s", english.WordSeries(quoteStringSlice(e.missingFields), "or")) if e.conditionalField != "" { errMsg = fmt.Sprintf(`%s if "%s" is specified`, errMsg, e.conditionalField) } return errMsg } type errInvalidCloudFrontRegion struct{} func (e *errInvalidCloudFrontRegion) Error() string { return fmt.Sprintf(`cdn certificate must be in region %s`, cloudfront.CertRegion) } // RecommendActions returns recommended actions to be taken after the error. func (e *errInvalidCloudFrontRegion) RecommendActions() string { return fmt.Sprintf(`It looks like your CloudFront certificate is in the wrong region. CloudFront only supports certificates in %s. We recommend creating a duplicate certificate in the %s region through AWS Certificate Manager. More information: https://go.aws/3BMxY4J`, cloudfront.CertRegion, cloudfront.CertRegion) } func quoteStringSlice(in []string) []string { quoted := make([]string, len(in)) for idx, str := range in { quoted[idx] = strconv.Quote(str) } return quoted } type errContainersExposingSamePort struct { firstContainer string secondContainer string port uint16 } func (e *errContainersExposingSamePort) Error() string { return fmt.Sprintf(`containers %q and %q are exposing the same port %d`, e.firstContainer, e.secondContainer, e.port) }