Skip to content

Commit

Permalink
Validate scheduling strategy for Fargate services (#2559)
Browse files Browse the repository at this point in the history
* Validate scheduling strategy for Fargate services

* Update test/fixtures/templates/good/resources/ecs/test_fargate_scheduling_strategy.yaml

Co-authored-by: Pat Myron <[email protected]>

* Fix Fargate strategy rule to handle non string properties

Change:

Intrinsic functions can be used for resource properties and previously
this rule checker doesn't handle such scenario.

---------

Co-authored-by: Pat Myron <[email protected]>
  • Loading branch information
lejiati and PatMyron authored Feb 21, 2023
1 parent bf30a9b commit bf334a2
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 2 deletions.
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,
)

0 comments on commit bf334a2

Please sign in to comment.