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

Validate scheduling strategy for Fargate services #2559

Merged
merged 7 commits into from
Feb 21, 2023
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
1 change: 1 addition & 0 deletions docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ The following **151** rules are applied by this linter:
| [E3041<a name="E3041"></a>](../src/cfnlint/rules/resources/route53/RecordSetName.py) | RecordSet HostedZoneName is a superdomain of Name | In a RecordSet, the HostedZoneName must be a superdomain of the Name being validated | | [Source](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-recordset.html#cfn-route53-recordset-name) | `resource`,`properties`,`route53` |
| [E3042<a name="E3042"></a>](../src/cfnlint/rules/resources/ecs/TaskDefinitionEssentialContainer.py) | Check at least one essential container is specified | Check that every TaskDefinition specifies at least one essential container | | [Source](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-taskdefinition-containerdefinitions.html#cfn-ecs-taskdefinition-containerdefinition-essential) | `properties`,`ecs`,`task`,`container`,`fargate` |
| [E3043<a name="E3043"></a>](../src/cfnlint/rules/resources/cloudformation/NestedStackParameters.py) | Validate parameters for in a nested stack | Evalute if parameters for a nested stack are specified and if parameters are specified for a nested stack that aren't required. | | [Source](https://github.com/awslabs/cfn-python-lint) | `resources`,`cloudformation` |
| [E3044<a name="E3044"></a>](../src/cfnlint/rules/resources/ecs/FargateDeploymentSchedulingStrategy.py) | Validate scheduling strategy for Fargate services | Fargate services currently only support REPLICA scheduling strategy | | [Source](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-service.html#cfn-ecs-service-schedulingstrategy) | `resources`,`container`, `fargate`, `service` |
| [E3050<a name="E3050"></a>](../src/cfnlint/rules/resources/iam/RefWithPath.py) | Check if REFing to a IAM resource with path set | Some resources don't support looking up the IAM resource by name. This check validates when a REF is being used and the Path is not '/' | | [Source](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html) | `properties`,`iam` |
| [E3502<a name="E3502"></a>](../src/cfnlint/rules/resources/properties/JsonSize.py) | Check if a JSON Object is within size limits | Validate properties that are JSON values so that their length is within the limits | | [Source](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html) | `resources`,`limits`,`json` |
| [E3503<a name="E3503"></a>](../src/cfnlint/rules/resources/certificatemanager/DomainValidationOptions.py) | ValidationDomain is superdomain of DomainName | In ValidationDomainOptions, the ValidationDomain must be a superdomain of the DomainName being validated | | [Source](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-certificatemanager-certificate-domainvalidationoption.html#cfn-certificatemanager-certificate-domainvalidationoption-validationdomain) | `certificate`,`certificatemanager`,`domainvalidationoptions`,`validationdomain` |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: MIT-0
"""
from cfnlint.rules import CloudFormationLintRule, RuleMatch


class FargateDeploymentSchedulingStrategy(CloudFormationLintRule):
id = "E3044"
shortdesc = "Check Fargate service scheduling strategy"
description = "Check that Fargate service scheduling strategy is REPLICA"
source_url = "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-service.html#cfn-ecs-service-schedulingstrategy"
tags = ["properties", "ecs", "service", "container", "fargate"]

def match(self, cfn):
matches = []
ecs_services = cfn.get_resource_properties(["AWS::ECS::Service"])

for ecs_service in ecs_services:
path = ecs_service["Path"]
properties = ecs_service["Value"]
if isinstance(properties, dict):
launch_type = properties.get("LaunchType", None)
if isinstance(launch_type, str) and launch_type == "Fargate":
scheduling_strategy = properties.get("SchedulingStrategy", None)
if (
isinstance(scheduling_strategy, str)
and scheduling_strategy != "REPLICA"
):
error_message = f"Fargate service only support REPLICA as scheduling strategy at {'/'.join(map(str, path))}"
matches.append(RuleMatch(path, error_message))
return matches
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ def match(self, cfn):
has_essential_container = True

if not has_essential_container:
message = "No essential containers defined for {0}"
rule_match = RuleMatch(path, message.format("/".join(map(str, path))))
error_message = (
f"No essential containers defined for {'/'.join(map(str, path))}"
)
rule_match = RuleMatch(path, error_message)
matches.append(rule_match)

return matches
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Resources:
Service1:
Type: AWS::ECS::Service
Properties:
LaunchType: Fargate
SchedulingStrategy: DAEMON
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Resources:
Service1:
Type: AWS::ECS::Service
Properties:
LaunchType: Fargate
SchedulingStrategy: REPLICA
Service2:
Type: AWS::ECS::Service
Properties:
LaunchType: EC2
SchedulingStrategy: DAEMON
Service3:
Type: AWS::ECS::Service
Properties:
LaunchType: EXTERNAL
SchedulingStrategy: DAEMON
Service4:
Type: AWS::ECS::Service
Properties:
LaunchType: !Join ["", ["FAR", "GATE"]]
SchedulingStrategy: DAEMON
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: MIT-0
"""
from test.unit.rules import BaseRuleTestCase

from cfnlint.rules.resources.ecs.FargateDeploymentSchedulingStrategy import (
FargateDeploymentSchedulingStrategy,
)


class TestFargateServiceSchedulingStrategy(BaseRuleTestCase):
"""
Test that Fargate service has correct scheduling strategy (REPLICA)
"""

def setUp(self):
super(TestFargateServiceSchedulingStrategy, self).setUp()
self.collection.register(FargateDeploymentSchedulingStrategy())
self.success_templates = [
"test/fixtures/templates/good/resources/ecs/test_fargate_scheduling_strategy.yaml"
]

def test_file_positive(self):
self.helper_file_positive()

def test_file_negative(self):
self.helper_file_negative(
"test/fixtures/templates/bad/resources/ecs/test_fargate_scheduling_strategy.yaml",
1,
)