WIP: _Should_ work ;

parent 4a84b536
......@@ -10,12 +10,19 @@ from akinaka_client.aws_client import AWS_Client
from akinaka_libs import helpers
import logging
helpers.set_logger()
aws_client = AWS_Client()
class CleanupAMIs():
def __init__(self, region, role_arns, retention, not_dry_run, exceptional_amis=None, launch_templates=None):
def __init__(self,
region,
role_arns,
retention,
not_dry_run,
log_level,
exceptional_amis=None,
launch_templates=None):
self.region = region
self.role_arns = role_arns
self.retention = int(retention)
......@@ -23,6 +30,7 @@ class CleanupAMIs():
self.launch_templates = launch_templates
self.retention_end = (datetime.now(timezone.utc) + timedelta(days=-self.retention))
self.not_dry_run = not_dry_run
helpers.set_logger(log_level)
def list_amis(self, filters={}, ids=[]):
ec2_client = aws_client.create_client('ec2', self.region, self.role_arns[0])
......@@ -40,7 +48,7 @@ class CleanupAMIs():
def delist_out_of_retention_amis(self, all_amis):
amis_to_delete = set()
for ami in all_amis:
if dateutil.parser.parse(ami['CreationDate']) < self.retention_end:
amis_to_delete.add(ami['ImageId'])
......@@ -53,23 +61,23 @@ class CleanupAMIs():
unused_amis = set()
all_accounts_instances = []
instance_amis = set()
for role in self.role_arns:
ec2_client = aws_client.create_client('ec2', self.region, role)
all_accounts_instances.append(ec2_client.describe_instances()['Reservations'])
all_accounts_instances = [account for accounts in all_accounts_instances for account in accounts]
for account_instance in all_accounts_instances:
instance_amis.add(account_instance['Instances'][0]['ImageId'])
instance_amis.add(account_instance['Instances'][0]['ImageId'])
for ami in amis:
if ami in instance_amis:
continue
unused_amis.add(ami)
return unused_amis
def delist_latest_arbitrary_amis(self, amis):
def delist_latest_arbitrary_amis(self, amis):
for exceptional_ami in self.exceptional_amis:
filter = {
......@@ -77,9 +85,9 @@ class CleanupAMIs():
'Values': [ exceptional_ami ]
}
all_arbitrary_amis = self.list_amis(filter)
all_arbitrary_amis = self.list_amis(filter)
amis.discard(max(all_arbitrary_amis, key=lambda x:x['CreationDate'])['ImageId'])
return amis
def delist_launch_template_finds(self, amis):
......@@ -90,7 +98,7 @@ class CleanupAMIs():
LaunchTemplateNames=[launch_template]
)['LaunchTemplates'][0]['LatestVersionNumber']
str(latest_version)
# It shouldn't be possible to have no value for ImageId, but it is :D
try:
in_use_ami = ec2_client.describe_launch_template_versions(
......@@ -101,7 +109,7 @@ class CleanupAMIs():
in_use_ami = ""
amis.discard(in_use_ami)
return amis
def get_snapshot(self, ami):
......@@ -111,7 +119,7 @@ class CleanupAMIs():
ec2_client = aws_client.create_client('ec2', self.region, self.role_arns[0])
for ami in amis:
snapshot = self.get_snapshot(ami)
snapshot = self.get_snapshot(ami)
ec2_client.deregister_image(ImageId=ami)
ec2_client.delete_snapshot(SnapshotId=snapshot)
......@@ -120,12 +128,9 @@ class CleanupAMIs():
amis_to_delete = self.delist_in_use_amis(amis_to_delete)
amis_to_delete = self.delist_latest_arbitrary_amis(amis_to_delete)
amis_to_delete = self.delist_launch_template_finds(amis_to_delete)
if self.not_dry_run:
logging.info("Deleting the following AMIs and their snapshots: {}".format(amis_to_delete))
self.delete_amis(amis_to_delete)
else:
logging.info("These are the AMIs I would have deleted if you gave me --not-dry-run: {}".format(amis_to_delete))
......@@ -10,7 +10,12 @@ helpers.set_logger()
@click.option("--not-dry-run", is_flag=True, help="Will do nothing unless supplied")
@click.pass_context
def cleanup(ctx, region, role_arns, not_dry_run=False):
ctx.obj = {'region': region, 'role_arns': role_arns, 'not_dry_run': not_dry_run}
ctx.obj = {
'region': region,
'role_arns': role_arns,
'not_dry_run': not_dry_run,
'log_level': ctx.obj.get('log_level')
}
pass
......@@ -25,6 +30,7 @@ def ami(ctx, retention, exceptional_amis, launch_templates):
not_dry_run = ctx.obj.get('not_dry_run')
role_arns = ctx.obj.get('role_arns')
role_arns = role_arns.split(" ")
log_level = ctx.obj.get('log_level')
if exceptional_amis:
exceptional_amis = exceptional_amis.split(" ")
......@@ -37,7 +43,13 @@ def ami(ctx, retention, exceptional_amis, launch_templates):
launch_templates = []
try:
amis = cleanup_amis.CleanupAMIs(region, role_arns, retention, not_dry_run, exceptional_amis, launch_templates)
amis = cleanup_amis.CleanupAMIs(region,
role_arns,
retention,
not_dry_run,
exceptional_amis,
launch_templates,
log_level)
amis.cleanup()
exit(0)
except Exception as e:
......@@ -52,9 +64,10 @@ def ebs(ctx):
not_dry_run = ctx.obj.get('not_dry_run')
role_arns = ctx.obj.get('role_arns')
role_arns = role_arns.split(" ")
log_level = ctx.obj.get('log_level')
try:
volumes = cleanup_volumes.CleanupVolumes(region, role_arns, not_dry_run)
volumes = cleanup_volumes.CleanupVolumes(region, role_arns, not_dry_run, log_level)
volumes.cleanup()
exit(0)
except Exception as e:
......@@ -70,9 +83,16 @@ def rds(ctx, search_tags):
not_dry_run = ctx.obj.get('not_dry_run')
role_arns = ctx.obj.get('role_arns')
role_arns = role_arns
log_level = ctx.obj.get('log_level')
try:
snapshots = cleanup_snapshots.CleanupSnapshots(region, role_arns, search_tags, not_dry_run)
snapshots = cleanup_snapshots.CleanupSnapshots(
region,
role_arns,
search_tags,
not_dry_run,
log_level)
snapshots.cleanup()
exit(0)
except Exception as e:
......
......@@ -10,16 +10,17 @@ from akinaka_client.aws_client import AWS_Client
from akinaka_libs import helpers
import logging
helpers.set_logger()
aws_client = AWS_Client()
class CleanupVolumes():
def __init__(self, region, role_arns, not_dry_run):
def __init__(self, region, role_arns, not_dry_run, log_level):
self.region = region
self.role_arns = role_arns
self.not_dry_run = not_dry_run
helpers.set_logger(log_level)
def list_volumes(self, role_arn, filters={}):
ec2_client = aws_client.create_client('ec2', self.region, role_arn)
......
......@@ -5,17 +5,18 @@ from akinaka_client.aws_client import AWS_Client
from akinaka_libs import helpers
import logging
helpers.set_logger()
aws_client = AWS_Client()
class CleanupSnapshots():
def __init__(self, region, role_arns, search_tags, not_dry_run):
def __init__(self, region, role_arns, search_tags, not_dry_run, log_level):
self.region = region
self.role_arns = role_arns.split(",")
self.search_tags = search_tags.split(",")
self.not_dry_run = not_dry_run
helpers.set_logger(log_level)
def list_tagged_snapshots(self, role_arn, search_tags):
rds_client = aws_client.create_client('rds', self.region, role_arn)
......
......@@ -6,7 +6,7 @@ import click
@click.option("--role-arn", required=True, help="Role ARN which contains necessary assume permissions")
@click.pass_context
def container(ctx, region, role_arn):
ctx.obj = {'region': region, 'role_arn': role_arn}
ctx.obj = {'region': region, 'role_arn': role_arn, 'log_level': ctx.obj.get('log_level')}
pass
......@@ -16,6 +16,7 @@ def container(ctx, region, role_arn):
def bill_estimates(ctx, registry):
region = ctx.obj.get('region')
role_arn = ctx.obj.get('role_arn')
log_level = ctx.obj.get('log_level')
from .ecr import ecr_login
ecr_login.ECRLogin(region, role_arn).get_login(registry)
ecr_login.ECRLogin(region, role_arn, log_level).get_login(registry)
......@@ -4,13 +4,13 @@ from akinaka_libs import helpers
import base64
import logging
helpers.set_logger()
aws_client = AWS_Client()
class ECRLogin():
def __init__(self, region, assume_role_arn):
def __init__(self, region, assume_role_arn, log_level):
self.region = region
self.assume_role_arn = assume_role_arn
helpers.set_logger(log_level)
def get_login(self, registry):
ecr_client = aws_client.create_client('ecr', self.region, self.assume_role_arn)
......
......@@ -4,7 +4,6 @@ from akinaka_libs import helpers
from time import gmtime, strftime
import logging
helpers.set_logger()
aws_client = AWS_Client()
@click.group()
......@@ -26,17 +25,17 @@ def k8s(ctx, applications, server, token, ca_file_path, skip_auth):
configuration.debug = True
configuration.api_key={"authorization":"Bearer {}".format(token)}
ctx.obj = {'configuration': configuration, 'applications': applications}
ctx.obj = {'configuration': configuration, 'applications': applications, 'log_level': ctx.obj.get('log_level')}
@k8s.command()
@click.pass_context
def monitor_deployment(ctx):
applications = ctx.obj.get('applications')
configuration = ctx.obj.get('configuration')
log_level = ctx.obj.get('log_level')
from .monitor_deployment import monitor_deployment
k8s_monitor = monitor_deployment.MonitorDeployment(configuration, applications)
k8s_monitor = monitor_deployment.MonitorDeployment(configuration, applications, log_level)
k8s_monitor.monitor_update()
......@@ -48,8 +47,9 @@ def monitor_deployment(ctx):
@click.pass_context
def update_deployment(ctx, new_image, new_tag, file_paths, dry_run):
applications = ctx.obj.get('applications')
log_level = ctx.obj.get('log_level')
from .update_deployment import update_deployment
k8s_update = update_deployment.UpdateDeployment(applications, new_image, new_tag, file_paths, dry_run)
k8s_update = update_deployment.UpdateDeployment(applications, log_level, new_image, new_tag, file_paths, dry_run)
k8s_update.write_new_spec()
#!/usr/bin/env python3
from kubernetes import client
from akinaka_libs import helpers
class MonitorDeployment():
def __init__(self, configuration, applications):
def __init__(self, configuration, applications, log_level):
client.Configuration.set_default(configuration)
self.core_api = client.CoreV1Api()
self.apps_api = client.AppsV1Api()
helpers.set_logger(log_level)
# TODO:
# kubectl --certificate-authority ca.crt --server $K8S_URL --token $K8S_TOKEN apply -f full_spec.yml
......
......@@ -9,13 +9,15 @@ import logging
class UpdateDeployment():
"""Modify a Kubernetes YAML deployment spec"""
def __init__(self, applications, new_image=None, new_tag=None, file_paths=None, dry_run=False):
def __init__(self, applications, log_level, new_image=None, new_tag=None, file_paths=None, dry_run=False):
self.applications = [x.strip() for x in applications.split(',')]
self.new_image = new_image
self.new_tag = new_tag
self.file_paths = [x.strip() for x in file_paths.split(',')]
self.dry_run = dry_run
helpers.set_logger(log_level)
if not self.new_image and not self.new_tag:
logging.error("At least --new-image or --new-tag need to be given")
exit(1)
......
......@@ -13,5 +13,6 @@ def seconds_from_hours(hours):
def log(message):
print(message)
def set_logger(level='INFO'):
return logging.basicConfig(level=level, format='%(asctime)s [%(levelname)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S%z')
def set_logger(log_level='INFO'):
logging.basicConfig(level=log_level, format='%(asctime)s [%(levelname)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S%z')
logging.getLogger().setLevel(log_level)
......@@ -25,9 +25,19 @@ def copy(ctx, region):
@click.option("--overwrite-target", is_flag=True, help="Specify this parameter to overwrite existing instance")
@click.option("--target-security-group", required=True, help="RDS Security to be attached to the target RDS instance")
@click.option("--target-db-subnet", required=True, help="RDS DB subnet to be attached to the instance")
def rds(ctx, source_role_arn, target_role_arn, snapshot_style, source_instance_name, overwrite_target, target_security_group, target_db_subnet, target_instance_name):
def rds(ctx,
source_role_arn,
target_role_arn,
snapshot_style,
source_instance_name,
overwrite_target,
target_security_group,
target_db_subnet,
target_instance_name):
from .copy import copy_rds
region = ctx.obj.get('region')
log_level = ctx.obj.get('log_level')
try:
rds_copy = copy_rds.CopyRDS(region,
......@@ -46,7 +56,8 @@ def rds(ctx, source_role_arn, target_role_arn, snapshot_style, source_instance_n
region,
"{},{}".format(source_role_arn,target_role_arn),
"akinaka-made",
True
True,
log_level
)
snapshots.cleanup()
......
......@@ -8,11 +8,10 @@ import tabulate
from akinaka_libs import helpers
import logging
helpers.set_logger()
class BillingQueries():
def __init__(self, region, assume_role_arn):
def __init__(self, region, assume_role_arn, log_level):
self.costexplorer = akinaka_libs.costexplorer.CostExplorer(region, assume_role_arn)
helpers.set_logger(log_level)
def days_estimates(self, from_days_ago):
try:
......@@ -21,7 +20,7 @@ class BillingQueries():
except Exception as e:
logging.error("Billing estimates is not available: {}".format(e))
return e
results = []
if len(data) == 1:
amount = float(data[0]['Total']['UnblendedCost']['Amount'])
......@@ -35,9 +34,9 @@ class BillingQueries():
amount = float(d['Total']['UnblendedCost']['Amount'])
results.append([d['TimePeriod']['End'], "{} {:.2f}".format(unit, amount)])
message = "\nEstimated bill for the past {} days\n".format(str(len(data)))
message += tabulate.tabulate(results, headers=["Date", "Total"], tablefmt='psql')
message += "\n"
logging.info(message)
return
\ No newline at end of file
return
......@@ -6,7 +6,7 @@ import click
@click.option("--role-arn", required=True, help="Role ARN which contains necessary assume permissions")
@click.pass_context
def reporting(ctx, region, role_arn):
ctx.obj = {'region': region, 'role_arn': role_arn}
ctx.obj = {'region': region, 'role_arn': role_arn, 'log_level': ctx.obj.get('log_level')}
pass
......@@ -16,8 +16,7 @@ def reporting(ctx, region, role_arn):
def bill_estimates(ctx, from_days_ago):
region = ctx.obj.get('region')
role_arn = ctx.obj.get('role_arn')
log_level = ctx.obj.get('log_level')
from .billing_summary import billing_queries
billing_queries.BillingQueries(region, role_arn).days_estimates(from_days_ago)
billing_queries.BillingQueries(region, role_arn, log_level).days_estimates(from_days_ago)
......@@ -13,7 +13,7 @@ aws_client = AWS_Client()
class ASG():
"""All the methods needed to perform a blue/green deploy"""
def __init__(self, ami, region, role_arn, loadbalancer=None, asg=None, target_group=None, scale_to=None):
def __init__(self, ami, region, role_arn, loadbalancer=None, asg=None, target_group=None, scale_to=None, log_level):
self.loadbalancer = loadbalancer
self.ami = ami
self.region = region
......@@ -21,6 +21,7 @@ class ASG():
self.asg = asg
self.target_group = target_group
self.scale_to = scale_to if scale_to else 1
logging.getLogger().setLevel(log_level)
def get_application_name(self):
"""
......
......@@ -39,7 +39,6 @@ def set_deploy_status(verb, region, role_arn, application, reset=None):
)
logging.info("Deployment status for {} updated".format(application))
@update.command()
@click.pass_context
@click.option("--application-name", required=True, help="The application name (target group) to reset unlock for re-running the 'asg' subcommand on")
......@@ -62,10 +61,11 @@ def asg(ctx, ami, lb, asg_name, target_group, scale_to, skip_status_check):
region = ctx.obj.get('region')
role_arn = ctx.obj.get('role_arn')
log_level = ctx.obj.get('log_level')
from .asg import update_asg
asg = update_asg.ASG(ami, region, role_arn, lb, asg_name, target_group, scale_to)
asg = update_asg.ASG(ami, region, role_arn, lb, asg_name, target_group, scale_to, log_level)
application = asg.get_application_name()
if lb or target_group:
......@@ -75,7 +75,6 @@ def asg(ctx, ami, lb, asg_name, target_group, scale_to, skip_status_check):
asg.do_update()
exit(0)
@update.command()
@click.pass_context
@click.option("--new", "new_asg_target", help="The ASG we're switching the LB to (attaching this ASG to the LB's targetgroup)")
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment