From d02b9000ebd7c4aadd02e558035cffbacca288ef Mon Sep 17 00:00:00 2001 From: Adrian Herrera Date: Sun, 15 Apr 2018 03:54:45 +0000 Subject: [PATCH] initial commit --- .gitignore | 3 + .travis.yml | 29 ++++++ Makefile | 36 +++++++ defaults/main.yml | 29 ++++++ handlers/main.yml | 2 + meta/main.yml | 53 ++++++++++ tasks/main.yml | 41 ++++++++ templates/ecs_service.json.j2 | 70 ++++++++++++++ tests/consul-bootstrap-definition.json | 50 ++++++++++ tests/consul-bootstrap-volume-definition.json | 6 ++ tests/inventory | 2 + tests/requirements.yml | 8 ++ tests/test.yml | 96 +++++++++++++++++++ vars/main.yml | 2 + 14 files changed, 427 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 Makefile create mode 100644 defaults/main.yml create mode 100644 handlers/main.yml create mode 100644 meta/main.yml create mode 100644 tasks/main.yml create mode 100644 templates/ecs_service.json.j2 create mode 100644 tests/consul-bootstrap-definition.json create mode 100644 tests/consul-bootstrap-volume-definition.json create mode 100644 tests/inventory create mode 100644 tests/requirements.yml create mode 100644 tests/test.yml create mode 100644 vars/main.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8ec225b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +roles +build +tests/Dockerfile diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..36bbf62 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,29 @@ +--- +language: python +python: "2.7" + +# Use the new container infrastructure +sudo: false + +# Install ansible +addons: + apt: + packages: + - python-pip + +install: + # Install ansible + - pip install ansible + + # Check ansible version + - ansible --version + + # Create ansible.cfg with correct roles_path + - printf '[defaults]\nroles_path=../' >ansible.cfg + +script: + # Basic role syntax check + - ansible-playbook tests/test.yml -i tests/inventory --syntax-check + +notifications: + webhooks: https://galaxy.ansible.com/api/v1/notifications/ \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..eb10897 --- /dev/null +++ b/Makefile @@ -0,0 +1,36 @@ +GIT_COMMIT=$(shell git rev-parse HEAD) +MY_DIR=$(shell basename "$(CURDIR)") +TEST_LABEL_KEY=ansible-role-testing +TEST_TAG=${TEST_LABEL_KEY} +define DOCKER_BODY +ARG ANSIBLE_OPTIONS +ARG TEST_LABEL +ARG TEST_LABEL_KEY +ARG TEST_TAG +ARG GIT_COMMIT=unknown +LABEL $$TEST_LABEL_KEY=$$TEST_LABEL +LABEL git-commit=$$GIT_COMMIT +LABEL TEST_TAG=$$TEST_TAG +ADD tests /tmp/playbook +ADD . /tmp/playbook/roles/$$TEST_LABEL +WORKDIR /tmp/playbook +RUN ansible-galaxy install -r requirements.yml -p ./roles/ &&\\ + ansible-playbook $$ANSIBLE_OPTIONS -i inventory test.yml +endef +export DOCKER_BODY +.PHONY: default +testv: ANSIBLE_OPTIONS = -v +test testv: + echo 'FROM mgage/docker-ansible' > tests/Dockerfile + echo "$$DOCKER_BODY" >> tests/Dockerfile + docker build --build-arg TEST_LABEL="${MY_DIR}" \ + --build-arg TEST_LABEL_KEY=${TEST_LABEL_KEY} \ + --build-arg GIT_COMMIT=${GIT_COMMIT} \ + --build-arg TEST_TAG=${TEST_TAG} \ + --build-arg ANSIBLE_OPTIONS=${ANSIBLE_OPTIONS} \ + --force-rm -t "${MY_DIR}":${TEST_TAG} -f tests/Dockerfile . +remove: + docker rmi $(shell docker images -q --filter label=TEST_TAG=${TEST_TAG} --filter label=${TEST_LABEL_KEY}="${MY_DIR}") + rm tests/Dockerfile +clean .IGNORE: remove +all: test diff --git a/defaults/main.yml b/defaults/main.yml new file mode 100644 index 0000000..b503377 --- /dev/null +++ b/defaults/main.yml @@ -0,0 +1,29 @@ +--- +# defaults file for ansible-role-cfn-ecs-service +ecs_service_iam_role_stack_name: "{{ ecs_service_stack_name }}-iam-role" +ecs_service_tags: {} +ecs_service_stack_state: "present" +ecs_service_iam_role_cleanup: true +ecs_iam_role_stack_name: "{{ ecs_service_stack_name }}-iam-role" +ecs_service_deploy_min_healthy_percent: "50" +ecs_service_deploy_max_percent: "200" +ecs_service_use_deploy_conf: "true" +ecs_service_assume_role_policy_document: + "Version" : "2012-10-17" + "Statement": + - "Effect": "Allow" + "Principal": { "Service": [ "ecs.amazonaws.com" ] } + "Action": [ "sts:AssumeRole" ] +ecs_service_iam_role_policies: + - name: ecs_service_policy + policy: + "Version": "2012-10-17" + "Statement": + - "Effect": "Allow" + "Action": + - 'elasticloadbalancing:Describe*' + - 'elasticloadbalancing:DeregisterInstancesFromLoadBalancer' + - 'elasticloadbalancing:RegisterInstancesWithLoadBalancer' + - 'ec2:Describe*' + - 'ec2:AuthorizeSecurityGroupIngress' + "Resource": "*" diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..511ba10 --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for ansible-role-cfn-ecs-service diff --git a/meta/main.yml b/meta/main.yml new file mode 100644 index 0000000..58829bb --- /dev/null +++ b/meta/main.yml @@ -0,0 +1,53 @@ +galaxy_info: + author: your name + description: your description + company: your company (optional) + + # If the issue tracker for your role is not on github, uncomment the + # next line and provide a value + # issue_tracker_url: http://example.com/issue/tracker + + # Some suggested licenses: + # - BSD (default) + # - MIT + # - GPLv2 + # - GPLv3 + # - Apache + # - CC-BY + license: license (GPLv2, CC-BY, etc) + + min_ansible_version: 1.2 + + # Optionally specify the branch Galaxy will use when accessing the GitHub + # repo for this role. During role install, if no tags are available, + # Galaxy will use this branch. During import Galaxy will access files on + # this branch. If travis integration is cofigured, only notification for this + # branch will be accepted. Otherwise, in all cases, the repo's default branch + # (usually master) will be used. + #github_branch: + + # + # Below are all platforms currently available. Just uncomment + # the ones that apply to your role. If you don't see your + # platform on this list, let us know and we'll get it added! + # + #platforms: + + galaxy_tags: [] + # List tags for your role here, one per line. A tag is + # a keyword that describes and categorizes the role. + # Users find roles by searching for tags. Be sure to + # remove the '[]' above if you add tags to this list. + # + # NOTE: A tag is limited to a single word comprised of + # alphanumeric characters. Maximum 20 tags per role. +dependencies: + - src: https://github.com/mGageTechOps/ansible-role-cfn-iam-role + version: 0.0.5 + name: ansible-role-cfn-iam-role + when: ecs_service_iam_role is not defined and + ecs_service_elb_name is defined + iam_role_stack_name: "{{ ecs_service_iam_role_stack_name }}" + iam_role_tags: "{{ ecs_service_tags }}" + iam_role_policies: "{{ ecs_service_iam_role_policies }}" + assume_role_policy_document: "{{ ecs_service_assume_role_policy_document }}" diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..d186ed1 --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,41 @@ +--- +# tasks file for ansible-role-cfn-ecs-service +- name: Create build directory if not extant + file: + state: directory + dest: build + +- name: Template the ecs stack + template: + src: ecs_service.json.j2 + dest: "build/ecs_service.{{ ecs_service_stack_name }}.json" + +- name: Create or remove cloudformation stack + cloudformation: + template_parameters: + EcsTask: "{{ ecs_service_task }}" + ECSCluster: "{{ ecs_service_cluster_name }}" + ECSServiceRole: "{% if ecs_service_iam_role is not defined and ecs_service_elb_name is defined %}{{ iam_role_cf_output.stack_outputs.RoleName }}{% else %}{{ ecs_service_iam_role|default('') }}{% endif %}" + ECSELBName: "{{ ecs_service_elb_name|default('') }}" + ContainerName: "{{ ecs_service_container_name|default('') }}" + ContainerPort: "{{ ecs_service_container_port|default(0) }}" + DesiredCount: "{{ ecs_service_desired_count|default(1) }}" + DeploymentConfigurationMinHealthyPercent: "{{ ecs_service_deploy_min_healthy_percent }}" + DeploymentConfigurationMaxPercent: "{{ ecs_service_deploy_max_percent }}" + stack_name: "{{ ecs_service_stack_name }}" + region: "{{ region }}" + template: "build/ecs_service.{{ ecs_service_stack_name }}.json" + security_token: "{{ lookup('env','AWS_SESSION_TOKEN')|default(omit, true) }}" + tags: "{{ ecs_service_tags }}" + state: "{{ ecs_service_stack_state }}" + register: ecs_service_cf_output + +- name: cleanup the iam role + cloudformation: + stack_name: "{{ ecs_service_iam_role_stack_name }}" + state: "absent" + region: "{{ region }}" + security_token: "{{ ansible_security_token | default(omit, true) }}" + when: ecs_service_iam_role_cleanup and + ecs_service_stack_state == 'absent' and + ecs_service_iam_role is not defined diff --git a/templates/ecs_service.json.j2 b/templates/ecs_service.json.j2 new file mode 100644 index 0000000..b00524c --- /dev/null +++ b/templates/ecs_service.json.j2 @@ -0,0 +1,70 @@ +{ + "AWSTemplateFormatVersion" : "2010-09-09", + "Parameters" : { + "EcsTask": { + "Type": "String", + "Description": "ARN of an ecs task" + }, + "ECSCluster": { + "Type": "String", + "Description": "name or arn of ecs cluster" + }, + "ECSServiceRole": { + "Type": "String", + "Description": "name or arn of the iam role" + }, + "ECSELBName": { + "Type": "String", + "Description": "name of the elb(immutable)" + }, + "ContainerName": { + "Type": "String", + "Description": "name of the container(immutable)" + }, + "ContainerPort": { + "Type": "Number", + "Description": "port of the container(immutable)" + }, + "DesiredCount": { + "Type": "String", + "Description": "number of tasks" + }, + "DeploymentConfigurationMinHealthyPercent": { + "Type": "Number" + }, + "DeploymentConfigurationMaxPercent": { + "Type": "Number" + } + }, + "Resources": { + "service": { + "Type": "AWS::ECS::Service", + "Properties": { + "Cluster": { "Ref": "ECSCluster" }, + "DesiredCount": { "Ref": "DesiredCount" }, + {% if ecs_service_use_deploy_conf is defined and ecs_service_use_deploy_conf == 'true' %} + "DeploymentConfiguration": { + "MaximumPercent" : { "Ref" : "DeploymentConfigurationMaxPercent" }, + "MinimumHealthyPercent" : { "Ref" : "DeploymentConfigurationMinHealthyPercent" } + }, + {% endif %} + {% if ecs_service_elb_name is defined %} + "LoadBalancers": [ + { + "ContainerName": { "Ref": "ContainerName" }, + "ContainerPort": { "Ref": "ContainerPort" }, + "LoadBalancerName": { "Ref": "ECSELBName" } + } + ], + "Role": {"Ref":"ECSServiceRole"}, + {% endif %} + "TaskDefinition": {"Ref": "EcsTask"} + } + } + }, + "Outputs" : { + "ecsservice" : { + "Value" : { "Ref" : "service" } + } + } +} diff --git a/tests/consul-bootstrap-definition.json b/tests/consul-bootstrap-definition.json new file mode 100644 index 0000000..68bf644 --- /dev/null +++ b/tests/consul-bootstrap-definition.json @@ -0,0 +1,50 @@ +{ + "Essential": true, + "Cpu": 20, + "Memory": 128, + "Image": "majest/docker-consul-ecs:latest", + "Name": "consul-bootstrap", + "MountPoints": [{ + "ContainerPath": "/data", + "SourceVolume": "consuldata", + "ReadOnly": false + }], + "Environment": [{ + "Name": "CONSUL_PARAMS", + "Value": "-bootstrap" + }], + "PortMappings": [{ + "ContainerPort": 8300, + "HostPort": 8300, + "Protocol": "tcp" + }, { + "ContainerPort": 8301, + "HostPort": 8301, + "Protocol": "tcp" + }, { + "ContainerPort": 8301, + "HostPort": 8301, + "Protocol": "udp" + }, { + "ContainerPort": 8302, + "HostPort": 8302, + "Protocol": "tcp" + }, { + "ContainerPort": 8400, + "HostPort": 8400, + "Protocol": "tcp" + }, { + "ContainerPort": 8500, + "HostPort": 8500, + "Protocol": "tcp" + }, { + "ContainerPort": 8600, + "HostPort": 53, + "Protocol": "udp" + }], + "DnsServers": [ + "172.17.0.1", + "8.8.8.8" + ], + "DnsSearchDomains": ["service.consul"] +} diff --git a/tests/consul-bootstrap-volume-definition.json b/tests/consul-bootstrap-volume-definition.json new file mode 100644 index 0000000..d254d20 --- /dev/null +++ b/tests/consul-bootstrap-volume-definition.json @@ -0,0 +1,6 @@ +{ + "Host": { + "SourcePath": "/opt/rtc/consul" + }, + "Name": "consuldata" +} diff --git a/tests/inventory b/tests/inventory new file mode 100644 index 0000000..b591f04 --- /dev/null +++ b/tests/inventory @@ -0,0 +1,2 @@ +[test] +localhost ansible_connection=local diff --git a/tests/requirements.yml b/tests/requirements.yml new file mode 100644 index 0000000..d0c4b8b --- /dev/null +++ b/tests/requirements.yml @@ -0,0 +1,8 @@ +- src: https://github.com/mGageTechOps/ansible-role-cfn-elb + version: 0.0.4 +- src: https://github.com/mGageTechOps/ansible-role-cfn-ecs + version: 0.2.3 +- src: https://github.com/mGageTechOps/ansible-role-cfn-ecs-task + version: 0.0.2 +- src: https://github.com/mGageTechOps/ansible-role-module-cfn-lookup + version: 0.0.5 diff --git a/tests/test.yml b/tests/test.yml new file mode 100644 index 0000000..4b9193f --- /dev/null +++ b/tests/test.yml @@ -0,0 +1,96 @@ +--- +- name: test without elb + hosts: test + remote_user: root + vars: + region: "us-west-2" + ecs_service_stack_name: "test-ecs-service-stack" + ecs_service_container_name: "consul-bootstrap" + ecs_service_task: "{{ ecs_task_cf_output.stack_outputs.taskdef }}" + ecs_service_cluster_name: "{{ ecs_cluster_cf_output.stack_outputs.ecscluster }}" + ecs_asg_desired_capacity: 1 + ecs_stack_name: "test-ecs-service-ecs-stack" + ecs_host_vpc: "YOUR-VPC-ID" + ecs_subnets: "YOUR-SUBNET-ID,YOUR-OTHER-SUBNET-ID" + ecs_sg_ids: "YOUR-SECURITYGROUP-ID" + ecs_host_keypair_name: "YOUR-KEYPAIR-NAME" + ecs_task_stack_name: "test-ecs-service-task-stack" + ecs_task_container_definitions: + - "{{ lookup('file', 'consul-bootstrap-definition.json') }}" + ecs_task_volume_definitions: + - "{{ lookup('file', 'consul-bootstrap-volume-definition.json') }}" + roles: + - ansible-role-cfn-ecs + - ansible-role-cfn-ecs-task + - ansible-role-cfn-ecs-service + +- name: test without elb cleanup + hosts: test + remote_user: root + vars: + region: "us-west-2" + ecs_stack_name: "test-ecs-service-ecs-stack" + ecs_cluster_stack_state: "absent" + ecs_task_stack_name: "test-ecs-service-task-stack" + ecs_task_stack_state: "absent" + ecs_service_stack_name: "test-ecs-service-stack" + ecs_service_stack_state: "absent" + roles: + - ansible-role-cfn-ecs-service + - ansible-role-cfn-ecs-task + - ansible-role-cfn-ecs + +- name: test with elb + hosts: test + remote_user: root + vars: + region: "us-west-2" + ecs_service_stack_name: "test-ecs-service-welb-stack" + ecs_service_container_name: "consul-bootstrap" + ecs_service_container_port: 8300 + ecs_service_task: "{{ ecs_task_cf_output.stack_outputs.taskdef }}" + ecs_service_cluster_name: "{{ ecs_cluster_cf_output.stack_outputs.ecscluster }}" + ecs_service_elb_name: "{{ elb_cf_output.stack_outputs.LoadBalancerName }}" + elb_role_stack_name: "test-ecs-service-elb-stack" + ecs_host_vpc: "YOUR-VPC-ID" + ecs_subnets: "YOUR-SUBNET-ID,YOUR-OTHER-SUBNET-ID" + ecs_sg_ids: "YOUR-SECURITYGROUP-ID,{{ elb_cf_output.stack_outputs.TargetSecurityGroupId }}" + elb_vpc: "{{ ecs_host_vpc }}" + elb_subnets: "{{ ecs_subnets }}" + elb_listeners: + - port: 8300 + instancePort: 8300 + protocol: http + ecs_asg_desired_capacity: 1 + ecs_stack_name: "test-ecs-service-ecs-stack" + ecs_host_keypair_name: "YOUR-KEYPAIR-NAME" + ecs_task_stack_name: "test-ecs-service-task-stack" + ecs_task_container_definitions: + - "{{ lookup('file', 'consul-bootstrap-definition.json') }}" + ecs_task_volume_definitions: + - "{{ lookup('file', 'consul-bootstrap-volume-definition.json') }}" + roles: + - ansible-role-cfn-elb + - ansible-role-cfn-ecs + - ansible-role-cfn-ecs-task + - ansible-role-cfn-ecs-service + +- name: test with elb cleanup + hosts: test + remote_user: root + vars: + region: "us-west-2" + elb_role_stack_name: "test-ecs-service-elb-stack" + elb_role_stack_state: "absent" + ecs_stack_name: "test-ecs-service-ecs-stack" + ecs_cluster_stack_state: "absent" + ecs_task_stack_name: "test-ecs-service-task-stack" + ecs_task_stack_state: "absent" + ecs_service_stack_name: "test-ecs-service-stack" + ecs_service_stack_state: "absent" + roles: + - ansible-role-cfn-ecs-service + - ansible-role-cfn-ecs-task + - ansible-role-cfn-ecs + - ansible-role-cfn-elb + diff --git a/vars/main.yml b/vars/main.yml new file mode 100644 index 0000000..0395ad6 --- /dev/null +++ b/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for ansible-role-cfn-ecs-service