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

Initial support of asg and alb. #11

Merged
merged 1 commit into from
Sep 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ __pycache__/

# Distribution / packaging
.Python
.vscode
env/
build/
develop-eggs/
Expand Down
3 changes: 1 addition & 2 deletions cumulus/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-

"""Top-level package for Cumulus."""

__author__ = """Brett Swift"""
__email__ = 'brettswift@gmail.com'
__version__ = '0.1.5'

Empty file added cumulus/components/__init__.py
Empty file.
Empty file.
38 changes: 38 additions & 0 deletions cumulus/components/userdata/linux.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from troposphere import (
Ref,
Join,
)


def user_data_for_cfn_init(launch_config_name, asg_name, configsets):
"""
brettswift marked this conversation as resolved.
Show resolved Hide resolved
:return: A troposphere Join object that contains userdata for use with cfn-init

:param configsets: The single 'key' value set in the cfn-init Metadata parameter: cloudformation.InitConfigSets
:type asg_name: String name of the ASG cloudformation resource
:type launch_config_name: String name of the launch config cloudformation resource
"""
default_userdata_asg_signal = (
Join('',
[
"#!/bin/bash -xe\n",
"yum update aws-cfn-bootstrap\n",
"# Install the files and packages from the metadata\n",
"/opt/aws/bin/cfn-init ",
" --stack ", Ref("AWS::StackName"),
" --resource ", launch_config_name,
" --configsets %s " % configsets,
" --region ", Ref("AWS::Region"), "\n",
# "# Get exit code of cfn init to use in cfn-signal\n",
# "export init_status=$?", "\n"
# Signal regardless of existence of an update policy.
# An error will pop up in the logs but I don't think this causes a problem.
"# Signal the ASG we are ready\n\n",
"/opt/aws/bin/cfn-signal -e 0",
# "/opt/aws/bin/cfn-signal -e $init_status",
" --resource %s" % asg_name,
" --stack ", Ref("AWS::StackName"),
" --region ", Ref("AWS::Region"),
"\n"
]))
return default_userdata_asg_signal
33 changes: 33 additions & 0 deletions cumulus/components/userdata/windows.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from troposphere import (
Ref,
Join,
)


def user_data_for_cfn_init(launch_config_name, asg_name, configsets):
"""
:return: A troposphere Join object that contains userdata for use with cfn-init
:param configsets: The single 'key' value set in the cfn-init Metadata parameter: cloudformation.InitConfigSets
:type asg_name: String name of the ASG cloudformation resource
:type launch_config_name: String name of the launch config cloudformation resource
"""
default_userdata_asg_signal = (
Join('',
[
"<powershell>\n",
"& ", "$env:ProgramFiles\Amazon\cfn-bootstrap\cfn-init.exe",
" --stack ", Ref("AWS::StackName"),
" --resource ", launch_config_name,
" --configsets %s " % configsets,
" --region ", Ref("AWS::Region"), "\n",
"# Signal the ASG we are ready\n",
"&", "$env:ProgramFiles\Amazon\cfn-signal",
brettswift marked this conversation as resolved.
Show resolved Hide resolved
" -e ",
" $LastExitCode",
" --resource %s" % asg_name,
" --stack ", Ref("AWS::StackName"),
" --region ", Ref("AWS::Region"),
"\n",
"</powershell>"
]))
return default_userdata_asg_signal
1 change: 0 additions & 1 deletion cumulus/steps/dev_tools/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ def handle(self, chain_context):
pipeline_policy
]
)

generic_pipeline = codepipeline.Pipeline(
"Pipeline",
# Name=chain_context.instance_name,
Expand Down
3 changes: 3 additions & 0 deletions cumulus/steps/ec2/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

META_SECURITY_GROUP_REF = 'security_group_ref'
META_TARGET_GROUP_NAME = 'target_group_ref'
127 changes: 127 additions & 0 deletions cumulus/steps/ec2/alb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
from cumulus.chain import step
from troposphere import (
Ref, Not, Equals, Join, ec2,
If, Output
)
from troposphere import elasticloadbalancingv2 as alb

CLUSTER_SG_NAME = "%sSG"
ALB_SG_NAME = "%sAlbSG"
ALB_NAME = "%sLoadBalancer"
TARGET_GROUP_DEFAULT = "%sTargetGroup"


class Alb(step.Step):

def __init__(self,
):
step.Step.__init__(self)

def handle(self, chain_context):
print(chain_context.instance_name)
self.create_conditions(chain_context.template)
self.create_security_groups(chain_context.template, chain_context.instance_name)
self.create_default_target_group(chain_context.template, chain_context.instance_name)
self.create_load_balancer_alb(chain_context.template, chain_context.instance_name)
self.add_listener(chain_context.template, chain_context.instance_name)

def create_conditions(self, template):
template.add_condition(
"UseSSL",
Not(Equals(Ref("ALBCertName"), "")))
template.add_condition(
"UseIAMCert",
Not(Equals(Ref("ALBCertType"), "acm")))

def create_security_groups(self, template, instance_name):
asg_sg = CLUSTER_SG_NAME % instance_name
alb_sg = ALB_SG_NAME % instance_name

# ALB Security group
template.add_resource(
ec2.SecurityGroup(
alb_sg,
GroupDescription=alb_sg,
VpcId=Ref("VpcId")
))

template.add_output(
Output("InternalAlbSG", Value=Ref(alb_sg))
)

# TODO: take a list of Cidr's
# Allow Internet to connect to ALB
template.add_resource(ec2.SecurityGroupIngress(
"LocalNetworkTo%sAlbPort443" % instance_name,
IpProtocol="tcp", FromPort="443", ToPort="443",
CidrIp="10.0.0.0/0",
GroupId=Ref(alb_sg),
))

def create_load_balancer_alb(self, template, instance_name):
alb_name = ALB_NAME % instance_name
alb_sg = ALB_SG_NAME % instance_name

load_balancer = template.add_resource(alb.LoadBalancer(
alb_name,
# Name=alb_name,
Scheme="internal",
Subnets=Ref("PrivateSubnets"),
SecurityGroups=[Ref(alb_sg)]
))

template.add_output(
Output(
"CanonicalHostedZoneID",
Value=load_balancer.GetAtt("CanonicalHostedZoneID")
)
)
template.add_output(
Output("DNSName", Value=load_balancer.GetAtt("DNSName"))
)

def add_listener(self, template, instance_name):
# Choose proper certificate source ?-> always acm?
acm_cert = Join("", [
"arn:aws:acm:",
Ref("AWS::Region"),
":",
Ref("AWS::AccountId"),
":certificate/", Ref("ALBCertName")])
# We probably don't need this code for an IAM Cert
iam_cert = Join("", [
"arn:aws:iam::",
Ref("AWS::AccountId"),
":server-certificate/",
Ref("ALBCertName")])
cert_id = If("UseIAMCert", iam_cert, acm_cert)
alb_name = ALB_NAME % instance_name

with_ssl = alb.Listener(
"Listener",
Port="443",
Protocol="HTTPS",
LoadBalancerArn=Ref(alb_name),
DefaultActions=[alb.Action(
Type="forward",
TargetGroupArn=Ref(TARGET_GROUP_DEFAULT % instance_name)
)],
Certificates=[alb.Certificate(
CertificateArn=cert_id
)]
)

template.add_resource(with_ssl)

template.add_output(
Output("IAlbListener", Value=with_ssl.Ref())
)

def create_default_target_group(self, template, instance_name):
print(template.__dict__)
template.add_resource(alb.TargetGroup(
TARGET_GROUP_DEFAULT % instance_name,
Port='80',
Protocol="HTTP",
VpcId=Ref("VpcId"),
))
31 changes: 31 additions & 0 deletions cumulus/steps/ec2/alb_port.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from troposphere import (
Ref, ec2, Join)
from cumulus.chain import step
from cumulus.steps.ec2 import META_SECURITY_GROUP_REF


class AlbPort(step.Step):

def __init__(self,
port_to_open,
alb_sg_name):

step.Step.__init__(self)

self.port_to_open = port_to_open
self.alb_sg_name = alb_sg_name

def handle(self, chain_context):
template = chain_context.template

name = '%sElbToASGPort%s' % (chain_context.instance_name, self.port_to_open)

template.add_resource(ec2.SecurityGroupIngress(
name,
IpProtocol="tcp",
FromPort=self.port_to_open,
ToPort=self.port_to_open,
SourceSecurityGroupId=Ref(self.alb_sg_name),
GroupId=chain_context.metadata[META_SECURITY_GROUP_REF]
))

17 changes: 17 additions & 0 deletions cumulus/steps/ec2/block_device_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from cumulus.chain import step
from cumulus.util.tropo import TemplateQuery
from troposphere import autoscaling



class BlockDeviceData(step.Step):

def __init__(self,
volume):
step.Step.__init__(self)
self.volume = volume

def handle(self, chain_context):
launchConfig = TemplateQuery.get_resource_by_type(template=chain_context.template,
type_to_find=autoscaling.LaunchConfiguration)[0]
launchConfig.properties['BlockDeviceMappings'] = [self.volume]
50 changes: 50 additions & 0 deletions cumulus/steps/ec2/dns.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from troposphere import route53
from troposphere import (
Ref, Join, ec2)
from cumulus.chain import step


class Dns(step.Step):

def __init__(self,
base_domain,
hosted_zone_id,
dns_name,
):

step.Step.__init__(self)

self.base_domain = base_domain
self.hosted_zone_id = hosted_zone_id
self.dns_name = dns_name

def handle(self, chain_context):
template = chain_context.template

name = 'AlbAlias%s' % chain_context.instance_name

template.add_resource(route53.RecordSetGroup(
"Route53Records",
RecordSets=[
route53.RecordSet(
name,
Weight=1,
SetIdentifier="original",
AliasTarget=route53.AliasTarget(
HostedZoneId=self.hosted_zone_id,
DNSName=self.dns_name,
EvaluateTargetHealth=False,
),
Name=Join("", [
Ref("namespace"),
"-",
Ref("env"),
".",
self.base_domain,
"."
]),
Type="A",
)
],
HostedZoneName=Join("", [self.base_domain, "."])
))
33 changes: 33 additions & 0 deletions cumulus/steps/ec2/ingress_rule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from troposphere import elasticloadbalancingv2 as alb
from troposphere import (
Ref, ec2)
import re
from cumulus.chain import step
from cumulus.steps.ec2 import META_SECURITY_GROUP_REF


class IngressRule(step.Step):

def __init__(self,
port_to_open,
cidr):

step.Step.__init__(self)

self.port_to_open = port_to_open
self.cidr = cidr

def handle(self, chain_context):
template = chain_context.template

clean_cidr = re.compile('[\W_]+').sub('', self.cidr)

template.add_resource(ec2.SecurityGroupIngress(
"Cidr%sToASGPort%s" % (clean_cidr, self.port_to_open),
IpProtocol="tcp",
FromPort=self.port_to_open,
ToPort=self.port_to_open,
CidrIp=self.cidr,
GroupId=chain_context.metadata[META_SECURITY_GROUP_REF]
))

32 changes: 32 additions & 0 deletions cumulus/steps/ec2/instance_profile_role.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from troposphere import Ref

from cumulus.chain import step
from troposphere.iam import InstanceProfile

from cumulus.util.tropo import TemplateQuery


class InstanceProfileRole(step.Step):

def __init__(self,
instance_profile_name,
role):
step.Step.__init__(self)
self.instance_profile_name = instance_profile_name
self.role = role

def handle(self, chain_context):
template = chain_context.template

template.add_resource(self.role)

try:
instanceProfile = TemplateQuery.get_resource_by_title(template, self.instance_profile_name)
instanceProfile.properties['Roles'].append(Ref(self.role))
except ValueError:
print('Adding new Instance Profile')
template.add_resource(InstanceProfile(
self.instance_profile_name,
Roles=[Ref(self.role)]
))

Loading