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

Feature 372: Pipeline Triggers #392

Merged
merged 22 commits into from
Nov 17, 2021
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f7de045
Feature 372: Pipeline Triggers
StewartW Oct 8, 2021
f89f695
Undoing black linting
StewartW Oct 8, 2021
062d52c
Update user-guide.md
StewartW Oct 22, 2021
f4ac55c
Addressing PR feedback
StewartW Oct 22, 2021
e18c246
Merge branch 'master' into feature/372
StewartW Nov 10, 2021
232cd8f
Adding in documentation and code for completion triggers
StewartW Nov 10, 2021
a0085ac
Update docs/user-guide.md
StewartW Nov 10, 2021
b67e79e
Update docs/user-guide.md
StewartW Nov 10, 2021
ace8aac
Update docs/user-guide.md
StewartW Nov 10, 2021
35c4b02
Update src/lambda_codebase/initial_commit/bootstrap_repository/adf-bu…
StewartW Nov 10, 2021
0b365c5
Update src/lambda_codebase/initial_commit/bootstrap_repository/adf-bu…
StewartW Nov 10, 2021
89a4006
Update src/lambda_codebase/initial_commit/bootstrap_repository/adf-bu…
StewartW Nov 10, 2021
73de4ab
Merge branch 'master' of github.com:awslabs/aws-deployment-framework …
StewartW Nov 10, 2021
4b5eb95
Code Review Comments
StewartW Nov 10, 2021
1232e01
Apply suggestions from code review
StewartW Nov 17, 2021
50306d6
Code Review changes
StewartW Nov 17, 2021
7e9bb7f
Merge branch 'master' of github.com:awslabs/aws-deployment-framework …
StewartW Nov 17, 2021
73f8cc7
Update src/lambda_codebase/initial_commit/bootstrap_repository/adf-bu…
StewartW Nov 17, 2021
a716228
Update src/lambda_codebase/initial_commit/bootstrap_repository/adf-bu…
StewartW Nov 17, 2021
d03717d
Merge branch 'feature/372' of github.com:StewartW/aws-deployment-fram…
StewartW Nov 17, 2021
13bd91d
Added new line on adf_default_pipeline
sbkok Nov 17, 2021
0da37f5
Merge branch 'master' into feature/372
sbkok Nov 17, 2021
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
49 changes: 44 additions & 5 deletions docs/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- [Params](#params)
- [Repositories](#repositories)
- [Completion Triggers](#completion-triggers)
- [Additional Triggers](#additional-triggers)
- [Additional Deployment Maps](#additional-deployment-maps)
- [Removing Pipelines](#removing-pipelines)
- [Deploying via Pipelines](#deploying-via-pipelines)
Expand Down Expand Up @@ -206,7 +207,7 @@ The following are the available pipeline parameters:

### Completion Triggers

Pipelines can also trigger other pipelines upon completion. To do this, use the *completion_trigger* key on the pipeline definition. For example:
Pipelines can also trigger other pipelines upon completion. To do this, use the *on_complete* key on the triggers definition. For example:

```yaml
- name: ami-builder
Expand All @@ -221,9 +222,10 @@ Pipelines can also trigger other pipelines upon completion. To do this, use the
size: medium
params:
schedule: rate(7 days)
completion_trigger: # What should happen when this pipeline completes
pipelines:
- my-web-app-pipeline # Start this pipeline
triggers: # What should trigger this pipeline, and what should be triggered when it completes
on_complete:
pipelines:
- my-web-app-pipeline # Start this pipeline
StewartW marked this conversation as resolved.
Show resolved Hide resolved

- name: my-web-app-pipeline
default_providers:
Expand All @@ -239,7 +241,44 @@ Pipelines can also trigger other pipelines upon completion. To do this, use the
name: web-app-testing
```

In the above example, the *ami-builder* pipeline runs every 7 days based on its schedule. When it completes, it executes the *my-web-app-pipeline* pipeline as defined in its *completion_trigger* property.
Completion triggers can also be defined in a short handed fashion. Take the above example for the ami-builder pipeline.
```yaml
- name: ami-builder
# Default providers and parameters are the same as defined above.
# Only difference: instead of using `triggers` it uses the `completion_triggers`
params:
schedule: rate(7 days)
completion_triggers: # What should trigger this pipeline, and what should be triggered when it completes
pipelines:
- my-web-app-pipeline # Start this pipeline
StewartW marked this conversation as resolved.
Show resolved Hide resolved

- name: my-web-app-pipeline
# Same configuration as defined above.
```


### Additional Triggers

Pipelines can also be triggered by other events using the *triggered_by* key on the triggers definition. For example, a new version of a package hosted on CodeArtifact being published:

```yaml
- name: ami-builder
default_providers:
source:
provider: codecommit
properties:
account_id: 222222222222
build:
provider: codebuild
role: packer
size: medium
triggers: # What should trigger this pipeline, and what should be triggered when it completes
triggered_by:
code_artifact:
repository: my_test_repository
```

In the above example, the *ami-builder* pipeline is triggered when a new package version is published to the *my_test_repository* repository in CodeArtifact.

### Additional Deployment Maps

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

from aws_cdk import (
aws_codepipeline as _codepipeline,
aws_events as _eventbridge,
aws_events_targets as _eventbridge_targets,
core
)

Expand Down Expand Up @@ -415,6 +417,8 @@ class Pipeline(core.Construct):
'SendSlackNotificationLambdaArn'
]

_accepted_triggers = {"code_artifact": "CODEARTIFACT"}
sbkok marked this conversation as resolved.
Show resolved Hide resolved

def __init__(self, scope: core.Construct, id: str, map_params: dict, ssm_params: dict, stages, **kwargs): #pylint: disable=W0622
super().__init__(scope, id, **kwargs)
[_codepipeline_role_arn, _code_build_role_arn, _send_slack_notification_lambda_arn] = Pipeline.import_required_arns() #pylint: disable=W0632
Expand All @@ -439,7 +443,7 @@ def __init__(self, scope: core.Construct, id: str, map_params: dict, ssm_params:
),
"topic_arn": map_params.get('topic_arn'),
"name": map_params['name'],
"completion_trigger": map_params.get('completion_trigger'),
"completion_trigger": map_params.get('completion_trigger', None) or map_params.get("triggers", {}).get("on_complete", None),
StewartW marked this conversation as resolved.
Show resolved Hide resolved
"schedule": map_params.get('schedule'),
"source": {
"provider": map_params.get('default_providers', {}).get('source', {}).get('provider'),
Expand Down Expand Up @@ -482,3 +486,25 @@ def import_required_arns():
# pylint: disable=no-value-for-parameter
_output.append(core.Fn.import_value(arn))
return _output


def add_pipeline_trigger(self, trigger_type, trigger_config):
if trigger_type in self._accepted_triggers.keys():
StewartW marked this conversation as resolved.
Show resolved Hide resolved
trigger_type = self._accepted_triggers[trigger_type]
else:
raise Exception(f"{trigger_type} is not currently supported as a pipeline trigger")

if trigger_type == "CODEARTIFACT":
sbkok marked this conversation as resolved.
Show resolved Hide resolved
details = {"repositoryName": trigger_config["repository"]}
if trigger_config.get("package"):
details["packageName"] = trigger_config["package"]
_eventbridge.Rule(
self,
f"codeartifact-pipeline-trigger-{trigger_config['repository']}-{trigger_config['package'] if trigger_config.get('package') else 'all'}",
StewartW marked this conversation as resolved.
Show resolved Hide resolved
event_pattern=_eventbridge.EventPattern(
source=["aws.codeartifact"],
detail_type=["CodeArtifact Package Version State Change"],
detail=details,
),
targets=[_eventbridge_targets.CodePipeline(pipeline=_codepipeline.Pipeline.from_pipeline_arn(self, "imported", pipeline_arn=self.cfn.ref))],
)
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ def generate_adf_default_pipeline(scope: core.Stack, stack_input):
if "github" in _source_name:
adf_github.GitHub.create_webhook_when_required(scope, _pipeline.cfn, stack_input["input"])

pipeline_triggers = stack_input["input"].get("triggers", {}).get("triggered_by", None)
StewartW marked this conversation as resolved.
Show resolved Hide resolved
if pipeline_triggers:
for trigger_type, trigger_config in pipeline_triggers.items():
_pipeline.add_pipeline_trigger(trigger_type=trigger_type, trigger_config=trigger_config)

def generate_source_stage_for_pipeline(_stages, scope, stack_input):
sbkok marked this conversation as resolved.
Show resolved Hide resolved
_source_name = stack_input["input"]["default_providers"]["source"][
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,110 @@ def test_pipeline_creation_outputs_as_expected_when_source_is_codecommit_and_bui
assert build_stage_action['ActionTypeId']['Provider'] == "CodeBuild"

assert len(build_stage['Actions']) == 1


def test_pipeline_creation_outputs_with_codeartifact_trigger():
StewartW marked this conversation as resolved.
Show resolved Hide resolved
StewartW marked this conversation as resolved.
Show resolved Hide resolved
region_name = "eu-central-1"
acount_id = "123456789012"

stack_input = {
"input": {"params": {}, "default_providers": {}, "regions": {}, "triggers": {"triggered_by": {"code_artifact": {"repository": "my_test_repo"} }}},
"ssm_params": {"fake-region": {}},
}

stack_input["input"]["name"] = "test-stack"

stack_input["input"]["default_providers"]["source"] = {
"provider": "codecommit",
"properties": {"account_id": "123456789012"},
}
stack_input["input"]["default_providers"]["build"] = {
"provider": "codebuild",
"properties": {"account_id": "123456789012"},
}

stack_input["ssm_params"][region_name] = {
"modules": "fake-bucket-name",
"kms": f"arn:aws:kms:{region_name}:{acount_id}:key/my-unique-kms-key-id",
}
app = core.App()
PipelineStack(app, stack_input)

cloud_assembly = app.synth()
resources = {k[0:-8]: v for k, v in cloud_assembly.stacks[0].template['Resources'].items()}
trigger = resources['codepipelinecodeartifactpipelinetriggermytestrepoall']
assert trigger["Type"] == "AWS::Events::Rule"
assert trigger["Properties"]["EventPattern"]["detail-type"] == ["CodeArtifact Package Version State Change"]
assert trigger["Properties"]["EventPattern"]["source"] == ["aws.codeartifact"]
assert trigger["Properties"]["EventPattern"]["detail"] == {"repositoryName": "my_test_repo"}


def test_pipeline_creation_outputs_with_codeartifact_trigger_with_package_name():
region_name = "eu-central-1"
acount_id = "123456789012"

stack_input = {
"input": {"params": {}, "default_providers": {}, "regions": {}, "triggers": {"triggered_by": {"code_artifact": {"repository": "my_test_repo", "package": "my_test_package"} }}},
"ssm_params": {"fake-region": {}},
}

stack_input["input"]["name"] = "test-stack"

stack_input["input"]["default_providers"]["source"] = {
"provider": "codecommit",
"properties": {"account_id": "123456789012"},
}
stack_input["input"]["default_providers"]["build"] = {
"provider": "codebuild",
"properties": {"account_id": "123456789012"},
}

stack_input["ssm_params"][region_name] = {
"modules": "fake-bucket-name",
"kms": f"arn:aws:kms:{region_name}:{acount_id}:key/my-unique-kms-key-id",
}
app = core.App()
PipelineStack(app, stack_input)

cloud_assembly = app.synth()
resources = {k[0:-8]: v for k, v in cloud_assembly.stacks[0].template['Resources'].items()}
trigger = resources['codepipelinecodeartifactpipelinetriggermytestrepomytestpackage']
assert trigger["Type"] == "AWS::Events::Rule"
assert trigger["Properties"]["EventPattern"]["detail-type"] == ["CodeArtifact Package Version State Change"]
assert trigger["Properties"]["EventPattern"]["source"] == ["aws.codeartifact"]
assert trigger["Properties"]["EventPattern"]["detail"] == {"repositoryName": "my_test_repo", "packageName": "my_test_package"}


def test_pipeline_creation_outputs_with_invalid_trigger_type():
region_name = "eu-central-1"
acount_id = "123456789012"

stack_input = {
"input": {"params": {}, "default_providers": {}, "regions": {}, "triggers": {"triggered_by": {"infinidash": {"arn": "arn:aws:11111111:us-east-1:infinidash/dash:blahblahblah"} }}},
"ssm_params": {"fake-region": {}},
}

stack_input["input"]["name"] = "test-stack"

stack_input["input"]["default_providers"]["source"] = {
"provider": "codecommit",
"properties": {"account_id": "123456789012"},
}
stack_input["input"]["default_providers"]["build"] = {
"provider": "codebuild",
"properties": {"account_id": "123456789012"},
}

stack_input["ssm_params"][region_name] = {
"modules": "fake-bucket-name",
"kms": f"arn:aws:kms:{region_name}:{acount_id}:key/my-unique-kms-key-id",
}
app = core.App()


with pytest.raises(Exception) as e_info:
PipelineStack(app, stack_input)
cloud_assembly = app.synth()
StewartW marked this conversation as resolved.
Show resolved Hide resolved

error_message = str(e_info.value)
assert error_message.find("is not currently supported as a pipeline trigger") >= 0
Original file line number Diff line number Diff line change
Expand Up @@ -310,14 +310,25 @@
COMPLETION_TRIGGERS_SCHEMA = {
"pipelines": [str]
}
PIPELINE_TRIGGERS_SCHEMA = {
Optional("code_artifact"): {
"repository": str,
Optional("package"): str,
}
}
TRIGGERS_SCHEMA = {
Optional("on_complete"): COMPLETION_TRIGGERS_SCHEMA,
sbkok marked this conversation as resolved.
Show resolved Hide resolved
Optional("triggered_by"): [PIPELINE_TRIGGERS_SCHEMA],
}
PIPELINE_SCHEMA = {
"name": And(str, len),
"default_providers": PROVIDER_SCHEMA,
Optional("params"): PARAM_SCHEMA,
Optional("tags"): dict,
Optional("targets"): [Or(str, int, TARGET_SCHEMA, TARGET_LIST_SCHEMA)],
Optional("regions"): REGION_SCHEMA,
Optional("completion_trigger"): COMPLETION_TRIGGERS_SCHEMA
Optional("completion_trigger"): COMPLETION_TRIGGERS_SCHEMA,
Optional("triggers"): TRIGGERS_SCHEMA
}
TOP_LEVEL_SCHEMA = {
"pipelines": [PIPELINE_SCHEMA],
Expand Down