Skip to content

Commit

Permalink
Merge ba4410b into 9b0a4a2
Browse files Browse the repository at this point in the history
  • Loading branch information
mayn committed Jun 6, 2018
2 parents 9b0a4a2 + ba4410b commit 259b8f0
Show file tree
Hide file tree
Showing 16 changed files with 114 additions and 80 deletions.
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# top-most EditorConfig file
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
trim_trailing_whitespace = true

[*.py]
max_line_length = 119
indent_size = 4
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@
*htmlcov
.DS_Store
.localized
*.idea
.tox
.pytest_cache
dist
.cache
10 changes: 6 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
language: python
python:
- "2.7"
- "2.6"
- "2.7"
- "3.6"

install:
- python setup.py install
- pip install coveralls
- pip install tox-travis
- pip install tox==2.6.0

before_script:
- export PYTHONPATH=$PYTHONPATH:$PWD

script:
- coverage run --source License2Deploy setup.py test
- tox

after_success:
coveralls
- if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then coveralls; fi
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
v0.0.1 First working version
v0.2.0 python 3 compatibility, add cli entrypoint
9 changes: 0 additions & 9 deletions CustomInstall.py

This file was deleted.

9 changes: 4 additions & 5 deletions License2Deploy/AWSConn.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#!/usr/bin/python

import boto.ec2 as ec2
import boto.ec2.autoscale as a
import boto.ec2.elb as elb
Expand All @@ -8,6 +6,7 @@
import logging
import yaml


class AWSConn(object):

@staticmethod
Expand All @@ -20,7 +19,7 @@ def aws_conn_auto(region, profile='default'):

@staticmethod
def aws_conn_ec2(region, profile='default'):
try:
try:
conn = ec2.connect_to_region(region, profile_name=profile)
return conn
except Exception as e:
Expand All @@ -36,7 +35,7 @@ def aws_conn_elb(region, profile='default'):

@staticmethod
def aws_conn_cloudwatch(region, profile='default'):
try:
try:
conn = cloudwatch.connect_to_region(region, profile_name=profile)
return conn
except Exception as e:
Expand All @@ -51,7 +50,7 @@ def get_boto3_client(client_type, region, profile='default', session=None):
@staticmethod
def load_config(config):
with open(config, 'r') as stream:
return yaml.load(stream)
return yaml.safe_load(stream)

@staticmethod
def determine_region(reg):
Expand Down
1 change: 0 additions & 1 deletion License2Deploy/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
#
21 changes: 10 additions & 11 deletions License2Deploy/rolling_deploy.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
#!/usr/bin/env python

import logging
import argparse
from sys import exit, argv
from time import sleep, time
from AWSConn import AWSConn
from set_logging import SetLogging
from .AWSConn import AWSConn
from .set_logging import SetLogging
from retry.api import retry_call


class RollingDeploy(object):

MAX_RETRIES = 10
Expand Down Expand Up @@ -94,7 +93,7 @@ def get_autoscale_group_name(self):
''' Search for project in autoscale groups and return autoscale group name '''
if self.stack_name:
return self.get_autoscaling_group_name_from_cloudformation()
return next((instance.name for instance in filter(lambda n: n.name, self.get_group_info()) if self.project in instance.name and self.env in instance.name), None)
return next((instance.name for instance in [n for n in self.get_group_info() if n.name] if self.project in instance.name and self.env in instance.name), None)

def get_autoscaling_group_name_from_cloudformation(self):
if not self.autoscaling_group:
Expand Down Expand Up @@ -133,7 +132,7 @@ def calculate_autoscale_desired_instance_count(self, group_name, desired_state):
except Exception as e:
logging.error("Please make sure the desired_state is set to either increase or decrease: {0}".format(e))
exit(self.exit_error_code)

def double_autoscale_instance_count(self, count):
''' Multiply current count by 2 '''
return count * 2
Expand Down Expand Up @@ -223,16 +222,16 @@ def wait_for_new_instances(self, instance_ids, retry=10, wait_time=30):
def lb_healthcheck(self, new_ids):
''' Confirm that the healthchecks report back OK in the LB. '''
instance_ids = self.conn_elb.describe_instance_health(self.load_balancer, new_ids)
status = filter(lambda instance: instance.state != "InService", instance_ids)
status = [instance for instance in instance_ids if instance.state != "InService"]
if status:
raise Exception('Must check load balancer again. Following instance(s) are not "InService": {0}'.format(status))
else:
logging.info('ELB healthcheck OK')
return True

def calculate_max_minutes(self, tries, delay):
return tries * delay / 60

def only_new_instances_check(self):
instance_ids = self.conn_elb.describe_instance_health(self.load_balancer)
for instance in instance_ids:
Expand All @@ -254,7 +253,7 @@ def tag_ami(self, ami_id, env):
''' Tagging AMI with DEPLOYED tag '''
try:
current_tag = self.conn_ec2.get_all_images(image_ids=ami_id)[0].tags.get('deployed')
if not current_tag:
if not current_tag:
logging.info("No DEPLOY tags exist, tagging with {0}".format(env))
self.conn_ec2.create_tags([self.ami_id], {"deployed": env})
elif env not in current_tag:
Expand Down Expand Up @@ -407,6 +406,6 @@ def main(): # pragma: no cover
SetLogging.setup_logging()
deployObj = RollingDeploy(args.env, args.project, args.build_number, args.ami_id, args.profile, args.config, args.stack_name, args.force_redeploy, None, args.creation_wait, args.ready_wait, args.health_wait, args.only_new_wait)
deployObj.deploy()

if __name__ == "__main__": # pragma: no cover
main()
5 changes: 1 addition & 4 deletions License2Deploy/set_logging.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
#!/usr/bin/python

import logging


class SetLogging(object):

@staticmethod
def setup_logging(): # pragma: no cover
logging.basicConfig(format='%(asctime)s: %(levelname)s: %(message)s',level=logging.INFO)
logging.info("Begin Logging...")

if __name__ == '__main__':
SetLogging.setup_logging() # pragma: no cover
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Rolling deploys for AWS AutoScale Groups
What is License2Deploy?
==================

License2Deploy is an automated solution for rolling deployments in AWS written in python.
License2Deploy is an automated solution for rolling deployments in AWS written in python.

The rolling deployment will:
- Double your instances in your autoscale group
Expand All @@ -20,7 +20,7 @@ The rolling deployment will:
Usage
==================
```
usage: rolling_deploy.py [-h] -e ENV -p PROJECT -b BUILD_NUM -a AMI_ID
usage: rolling_deploy [-h] -e ENV -p PROJECT -b BUILD_NUM -a AMI_ID
[-P PROFILE] [-c CONFIG] [-s STACK_NAME] [-f FORCE_REDEPLOY]
[-C CREATION_WAIT] [-r READY_WAIT] [-H HEALTH_WAIT] [-o ONLY_NEW_WAIT]
Expand Down Expand Up @@ -72,7 +72,7 @@ There are a few requirements in order for the automated rolling deployments to w
4. All instances in the autoscale group need to be tagged with a build number
* This is an important step as when the script runs, it will differentiate the old builds
from the new builds based off of the build number that is passed in as a command line parameter
5. The credentials for the user need to be in the ~/.aws/credentials file and if not passed in as a
5. The credentials for the user need to be in the ~/.aws/credentials file and if not passed in as a
command line argument, the script will look at the 'default' profile.
6. The script needs the AMI ID of the instances that will be built.
* The reason for the AMI ID is to ensure that if it was just created, it is not in a pending state
Expand All @@ -84,8 +84,10 @@ There are a few requirements in order for the automated rolling deployments to w
Development
============

python setup.py install
To run unit tests:

python setup.py test
```sh
$ tox
```

python License2Deploy/rolling_deploy.py
13 changes: 13 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[coverage:run]
branch = true
source = License2Deploy

[coverage:report]
omit =
tests
.tox

[tool:pytest]
addopts = --verbose
testpaths =
tests
43 changes: 24 additions & 19 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
#!/usr/bin/python

import os
import sys
from CustomInstall import CustomInstall
from setuptools import setup, Command
from setuptools import setup

install_requires = [
"boto",
Expand All @@ -16,30 +12,39 @@
tests_require = [
"mock",
"boto",
"moto",
"moto==1.0.1",
"PyYaml",
'placebo',
'boto3',
'retry'
'retry',
'pytest',
'coverage',
]


def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read()


setup(
name = "License2Deploy",
version = "0.0.1",
author = "Dun and Bradstreet",
author_email = "license2deploy@dandb.com",
description = ("Rolling deploys by changing desired amount of instances AWS EC2 Autoscale Group"),
license = "GPLv3",
keywords = "AWS EC2 AutoScale Group AMI desired capacity",
url = "https://github.com/dandb/License2Deploy",
name="License2Deploy",
version="0.2.0",
author="Dun and Bradstreet",
author_email="license2deploy@dandb.com",
description="Rolling deploys by changing desired amount of instances AWS EC2 Autoscale Group",
license="GPLv3",
keywords="AWS EC2 AutoScale Group AMI desired capacity",
url="https://github.com/dandb/License2Deploy",
packages=['License2Deploy'],
entry_points={
'console_scripts': [
'rolling_deploy = License2Deploy.rolling_deploy:main'
]
},
include_package_data=True,
install_requires = install_requires,
tests_require = tests_require,
install_requires=install_requires,
tests_require=tests_require,
extras_require={'test': tests_require},
long_description=read('README.md') + '\n\n' + read('CHANGES'),
test_suite = 'tests'
long_description=read('README.md'),
test_suite='tests'
)
1 change: 0 additions & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
#
11 changes: 5 additions & 6 deletions tests/cloudformation_client_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
import os
import placebo
from boto3.session import Session
Expand All @@ -17,15 +16,15 @@ def setUp(self):
self.rolling_deploy = RollingDeploy('stg', 'server-gms-extender', '0', 'ami-abcd1234', None, './regions.yml', stack_name='test-stack-name', session=session)

def test_get_autoscaling_group_name_via_cloudformation(self):
self.assertEquals(self.rolling_deploy.autoscaling_group, False)
self.assertEqual(self.rolling_deploy.autoscaling_group, False)
asg_name = self.rolling_deploy.get_autoscale_group_name()
self.assertTrue(self.rolling_deploy.autoscaling_group)
self.assertEquals(asg_name, 'dnbi-backend-qa-dnbigmsextenderASGqa-1NP5ZBSVZRD0N')
self.assertEqual(asg_name, 'dnbi-backend-qa-dnbigmsextenderASGqa-1NP5ZBSVZRD0N')

def test_retrieve_project_cloudwatch_alarms(self):
self.assertEquals(self.rolling_deploy.stack_resources, False)
self.assertEquals(self.rolling_deploy.cloudwatch_alarms, False)
self.assertEqual(self.rolling_deploy.stack_resources, False)
self.assertEqual(self.rolling_deploy.cloudwatch_alarms, False)
cloudwatch_alarms = self.rolling_deploy.retrieve_project_cloudwatch_alarms()
self.assertTrue(self.rolling_deploy.stack_resources)
self.assertEquals(cloudwatch_alarms, ['dnbi-servergmsextender-SCALEDOWNALARMqa-123123', 'dnbi-servergmsextender-SCALEUPALARMqa-4asdhjks'])
self.assertEqual(cloudwatch_alarms, ['dnbi-servergmsextender-SCALEDOWNALARMqa-123123', 'dnbi-servergmsextender-SCALEUPALARMqa-4asdhjks'])

Loading

0 comments on commit 259b8f0

Please sign in to comment.