-
-
Notifications
You must be signed in to change notification settings - Fork 149
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
DeployBlueGreenAction class to handle CodeDeploy deployments #121
base: develop
Are you sure you want to change the base?
Changes from all commits
b2a43a7
cda4f9c
581992e
ec827f8
1efafc4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
from datetime import datetime | ||
import time | ||
import json | ||
import re | ||
|
||
|
@@ -30,6 +31,7 @@ def __init__(self, access_key_id=None, secret_access_key=None, | |
profile_name=profile) | ||
self.boto = session.client(u'ecs') | ||
self.events = session.client(u'events') | ||
self.codedeploy = session.client(u'codedeploy') | ||
|
||
def describe_services(self, cluster_name, service_name): | ||
return self.boto.describe_services( | ||
|
@@ -541,7 +543,7 @@ def _get_secrets_diffs(container, secrets, old_secrets): | |
|
||
|
||
class EcsAction(object): | ||
def __init__(self, client, cluster_name, service_name): | ||
def __init__(self, client, cluster_name, service_name, **kwargs): | ||
self._client = client | ||
self._cluster_name = cluster_name | ||
self._service_name = service_name | ||
|
@@ -663,6 +665,88 @@ def deploy(self, task_definition): | |
raise EcsError(str(e)) | ||
|
||
|
||
class DeployBlueGreenAction(EcsAction): | ||
def __init__(self, *args, **kwargs): | ||
super(DeployBlueGreenAction, self).__init__(*args, **kwargs) | ||
self.cd_application_name = kwargs['cd_application_name'] | ||
self._cd_group_name = None | ||
self._deployment_target_id = None | ||
self._deployment_id = None | ||
self._traffic_health_percentage = 90 | ||
|
||
@property | ||
def deployment_id(self): | ||
if not self._deployment_id: | ||
raise EcsError('CodeDeploy deployment not defined') | ||
return self._deployment_id | ||
|
||
@property | ||
def cd_group_name(self): | ||
if not self._cd_group_name: | ||
self._cd_group_name = self.client.codedeploy.list_deployment_groups( | ||
applicationName=self.cd_application_name, | ||
)['deploymentGroups'][0] | ||
return self._cd_group_name | ||
|
||
@property | ||
def deployment_target_id(self): | ||
while not self._deployment_target_id: | ||
try: | ||
self._deployment_target_id = self.client.codedeploy.list_deployment_targets( | ||
deploymentId=self.deployment_id | ||
)['targetIds'][0] | ||
except ClientError as e: | ||
if e.response['Error']['Code'] == 'DeploymentNotStartedException': | ||
time.sleep(1) | ||
continue | ||
raise e | ||
return self._deployment_target_id | ||
|
||
def deploy(self, task_definition): | ||
response = self.client.codedeploy.create_deployment( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually this like makes the class to a "codedeploy-deploy" tool, rather then an "ecs-deploy" too. |
||
applicationName=self.cd_application_name, | ||
deploymentGroupName=self.cd_group_name, | ||
revision={ | ||
'revisionType': 'String', | ||
'string': { | ||
'content': self._get_revision_content(task_definition) | ||
} | ||
} | ||
) | ||
self._deployment_id = response['deploymentId'] | ||
return self._deployment_id | ||
|
||
def is_deployed(self, service): | ||
deployment_target = self.client.codedeploy.get_deployment_target( | ||
deploymentId=self.deployment_id, | ||
targetId=self.deployment_target_id | ||
)['deploymentTarget'] | ||
|
||
if deployment_target['ecsTarget']['status'] == 'Failed': | ||
raise EcsError('CodeDeploy Deployment Failed.') | ||
|
||
for task_set in deployment_target['ecsTarget']['taskSetsInfo']: | ||
if task_set['taskSetLabel'] == 'Green': | ||
return task_set["trafficWeight"] > self._traffic_health_percentage | ||
|
||
def _get_revision_content(self, task_definition): | ||
return json.dumps({ | ||
'version': 1, | ||
'Resources': [{ | ||
'TargetService': { | ||
'Type': 'AWS::ECS::Service', | ||
'Properties': { | ||
'TaskDefinition': task_definition.arn, | ||
'LoadBalancerInfo': { | ||
'ContainerName': self.service['loadBalancers'][0]['containerName'], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this imply, that a task definition does not have more than one containers? |
||
'ContainerPort': int(self.service['loadBalancers'][0]['containerPort']) | ||
} | ||
} | ||
} | ||
}] | ||
}) | ||
|
||
|
||
class ScaleAction(EcsAction): | ||
def scale(self, desired_count): | ||
try: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@joaoricardo000 Will it always be
us-east-1
? Or should it be the same region as you are deploying to?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, this needs to be configurable, like the regions are in the
deploy
,scale
, etc. commands.In addition this should be configured and falling back to a default inside the Action, not in the CLI controller, please.