enscalator 0.4.0.pre.alpha.pre.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rubocop.yml +9 -0
- data/.rubocop_todo.yml +59 -0
- data/.travis.yml +22 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +148 -0
- data/Rakefile +43 -0
- data/bin/console +11 -0
- data/bin/setup +7 -0
- data/enscalator.gemspec +57 -0
- data/exe/enscalator +13 -0
- data/lib/enscalator/core/cf_parameters.rb +146 -0
- data/lib/enscalator/core/cf_resources.rb +225 -0
- data/lib/enscalator/core/instance_type.rb +205 -0
- data/lib/enscalator/core/network_config.rb +21 -0
- data/lib/enscalator/core.rb +10 -0
- data/lib/enscalator/enapp.rb +248 -0
- data/lib/enscalator/helpers/dns.rb +62 -0
- data/lib/enscalator/helpers/stack.rb +107 -0
- data/lib/enscalator/helpers/sub_process.rb +72 -0
- data/lib/enscalator/helpers/wrappers.rb +55 -0
- data/lib/enscalator/helpers.rb +127 -0
- data/lib/enscalator/plugins/amazon_linux.rb +93 -0
- data/lib/enscalator/plugins/auto_scale.rb +80 -0
- data/lib/enscalator/plugins/core_os.rb +88 -0
- data/lib/enscalator/plugins/couchbase.rb +98 -0
- data/lib/enscalator/plugins/debian.rb +71 -0
- data/lib/enscalator/plugins/elastic_beanstalk.rb +74 -0
- data/lib/enscalator/plugins/elasticache.rb +168 -0
- data/lib/enscalator/plugins/elasticsearch_amazon.rb +75 -0
- data/lib/enscalator/plugins/elasticsearch_bitnami.rb +198 -0
- data/lib/enscalator/plugins/elasticsearch_opsworks.rb +225 -0
- data/lib/enscalator/plugins/elb.rb +139 -0
- data/lib/enscalator/plugins/nat_gateway.rb +71 -0
- data/lib/enscalator/plugins/rds.rb +141 -0
- data/lib/enscalator/plugins/redis.rb +38 -0
- data/lib/enscalator/plugins/rethink_db.rb +21 -0
- data/lib/enscalator/plugins/route53.rb +143 -0
- data/lib/enscalator/plugins/ubuntu.rb +85 -0
- data/lib/enscalator/plugins/user-data/elasticsearch +367 -0
- data/lib/enscalator/plugins/vpc_peering_connection.rb +48 -0
- data/lib/enscalator/plugins.rb +30 -0
- data/lib/enscalator/rich_template_dsl.rb +209 -0
- data/lib/enscalator/templates/vpc_peering.rb +112 -0
- data/lib/enscalator/templates.rb +20 -0
- data/lib/enscalator/version.rb +5 -0
- data/lib/enscalator/vpc.rb +11 -0
- data/lib/enscalator/vpc_with_nat_gateway.rb +311 -0
- data/lib/enscalator/vpc_with_nat_instance.rb +402 -0
- data/lib/enscalator.rb +103 -0
- metadata +427 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
module Enscalator
|
2
|
+
module Plugins
|
3
|
+
# VPC NAT Gateway plugin
|
4
|
+
module NATGateway
|
5
|
+
# Allocate new elastic IP in given VPC template
|
6
|
+
#
|
7
|
+
# @param [String] name eip resource name
|
8
|
+
# @param [Array<String>] depends_on list of resource names this resource depends on
|
9
|
+
# @return [Hash] result of Fn::GetAtt function
|
10
|
+
def allocate_new_eip(name, depends_on: [])
|
11
|
+
fail('Dependency on the VPC-gateway attachment must be provided') if depends_on.empty?
|
12
|
+
eip_resource_name = name
|
13
|
+
resource eip_resource_name,
|
14
|
+
DependsOn: depends_on,
|
15
|
+
Type: 'AWS::EC2::EIP',
|
16
|
+
Properties: {
|
17
|
+
Domain: 'vpc'
|
18
|
+
}
|
19
|
+
|
20
|
+
output eip_resource_name,
|
21
|
+
Description: 'Elastic IP address for NAT Gateway',
|
22
|
+
Value: ref(eip_resource_name)
|
23
|
+
|
24
|
+
get_att(eip_resource_name, 'AllocationId')
|
25
|
+
end
|
26
|
+
|
27
|
+
# Create new route rule
|
28
|
+
#
|
29
|
+
# @param [String] name route rule name
|
30
|
+
# @param [Array<String>] depends_on list of resource names this resource depends on
|
31
|
+
def add_route_rule(name, route_table_name, nat_gateway_name, dest_cidr_block, depends_on: [])
|
32
|
+
options = {
|
33
|
+
Type: 'AWS::EC2::Route'
|
34
|
+
}
|
35
|
+
options[:DependsOn] = depends_on unless depends_on.blank?
|
36
|
+
resource name,
|
37
|
+
options.merge(
|
38
|
+
Properties: {
|
39
|
+
RouteTableId: ref(route_table_name),
|
40
|
+
NatGatewayId: ref(nat_gateway_name),
|
41
|
+
DestinationCidrBlock: dest_cidr_block
|
42
|
+
})
|
43
|
+
end
|
44
|
+
|
45
|
+
# Create new NAT gateway
|
46
|
+
def nat_gateway_init(name, subnet_name, route_table_name, dest_cidr_block: '0.0.0.0/0', depends_on: [])
|
47
|
+
nat_gateway_eip_name = "#{name}EIP"
|
48
|
+
nat_gateway_eip = allocate_new_eip(nat_gateway_eip_name, depends_on: depends_on)
|
49
|
+
nat_gateway_name = name
|
50
|
+
nat_gateway_options = {
|
51
|
+
Type: 'AWS::EC2::NatGateway'
|
52
|
+
}
|
53
|
+
nat_gateway_options[:DependsOn] = depends_on unless depends_on.blank?
|
54
|
+
resource nat_gateway_name,
|
55
|
+
nat_gateway_options.merge(
|
56
|
+
Properties: {
|
57
|
+
AllocationId: nat_gateway_eip,
|
58
|
+
SubnetId: ref(subnet_name)
|
59
|
+
})
|
60
|
+
nat_route_rule_name = "#{name}Route"
|
61
|
+
add_route_rule(nat_route_rule_name, route_table_name, nat_gateway_name, dest_cidr_block, depends_on: depends_on)
|
62
|
+
|
63
|
+
output nat_gateway_name,
|
64
|
+
Description: 'NAT Gateway',
|
65
|
+
Value: ref(nat_gateway_name)
|
66
|
+
|
67
|
+
nat_gateway_name
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module Enscalator
|
2
|
+
module Plugins
|
3
|
+
# Amazon RDS instance
|
4
|
+
module RDS
|
5
|
+
# Create new Amazon RDS instance
|
6
|
+
#
|
7
|
+
# @param [String] db_name database name
|
8
|
+
# @param [Boolean] use_snapshot use snapshot or not
|
9
|
+
# @param [Integer] allocated_storage size of instance primary storage
|
10
|
+
# @param [String] storage_type instance storage type
|
11
|
+
# @param [String] multizone deploy as multizone or use only single availability zone
|
12
|
+
# @param [String] parameter_group RDS instance parameter group
|
13
|
+
# @param [String] instance_type instance type
|
14
|
+
# @param [Hash] properties additional properties
|
15
|
+
def rds_init(db_name,
|
16
|
+
use_snapshot: false,
|
17
|
+
allocated_storage: 5,
|
18
|
+
storage_type: 'gp2',
|
19
|
+
multizone: 'false',
|
20
|
+
engine: 'MySQL',
|
21
|
+
engine_version: '5.6',
|
22
|
+
parameter_group: 'default.mysql5.6',
|
23
|
+
instance_type: 'db.t2.small',
|
24
|
+
properties: {})
|
25
|
+
|
26
|
+
parameter_name "RDS#{db_name}"
|
27
|
+
|
28
|
+
parameter_rds_instance_type "RDS#{db_name}", type: instance_type
|
29
|
+
|
30
|
+
parameter_allocated_storage "RDS#{db_name}",
|
31
|
+
default: allocated_storage,
|
32
|
+
min: 5,
|
33
|
+
max: 1024
|
34
|
+
|
35
|
+
parameter "RDS#{db_name}Engine",
|
36
|
+
Default: engine,
|
37
|
+
Description: 'DB engine type of the DB instance',
|
38
|
+
Type: 'String'
|
39
|
+
|
40
|
+
parameter "RDS#{db_name}EngineVersion",
|
41
|
+
Default: engine_version,
|
42
|
+
Description: 'DB engine version of the DB instance',
|
43
|
+
Type: 'String'
|
44
|
+
|
45
|
+
parameter "RDS#{db_name}StorageType",
|
46
|
+
Default: storage_type,
|
47
|
+
Description: 'Storage type to be associated with the DB instance',
|
48
|
+
Type: 'String',
|
49
|
+
AllowedValues: %w( gp2 standard io1 )
|
50
|
+
|
51
|
+
parameter "RDS#{db_name}Multizone",
|
52
|
+
Default: multizone,
|
53
|
+
Description: 'Multizone deployment',
|
54
|
+
Type: 'String'
|
55
|
+
|
56
|
+
parameter "RDS#{db_name}ParameterGroup",
|
57
|
+
Default: parameter_group,
|
58
|
+
Description: 'Custom parameter group for an RDS database family',
|
59
|
+
Type: 'String'
|
60
|
+
|
61
|
+
parameter_username "RDS#{db_name}"
|
62
|
+
|
63
|
+
parameter_password "RDS#{db_name}"
|
64
|
+
|
65
|
+
resource "RDS#{db_name}SubnetGroup",
|
66
|
+
Type: 'AWS::RDS::DBSubnetGroup',
|
67
|
+
Properties: {
|
68
|
+
DBSubnetGroupDescription: 'Subnet group within VPC',
|
69
|
+
SubnetIds: ref_resource_subnets,
|
70
|
+
Tags: [
|
71
|
+
{
|
72
|
+
Key: 'Name',
|
73
|
+
Value: "RDS#{db_name}SubnetGroup"
|
74
|
+
}
|
75
|
+
]
|
76
|
+
}
|
77
|
+
|
78
|
+
# DBName and DBSnapshotIdentifier are mutually exclusive, thus
|
79
|
+
# when snapshot_id is given DBName won't be included to resource parameters
|
80
|
+
props = properties.deep_dup
|
81
|
+
if use_snapshot
|
82
|
+
parameter "RDS#{db_name}SnapshotId",
|
83
|
+
Description: 'Identifier for the DB snapshot to restore from',
|
84
|
+
Type: 'String',
|
85
|
+
MinLength: '1',
|
86
|
+
MaxLength: '64'
|
87
|
+
props[:DBSnapshotIdentifier] = ref("RDS#{db_name}SnapshotId")
|
88
|
+
else
|
89
|
+
props[:DBName] = ref("RDS#{db_name}Name")
|
90
|
+
end
|
91
|
+
|
92
|
+
rds_instance_tags = [
|
93
|
+
{
|
94
|
+
Key: 'Name',
|
95
|
+
Value: "RDS#{db_name}Instance"
|
96
|
+
}
|
97
|
+
]
|
98
|
+
|
99
|
+
# Set instance tags
|
100
|
+
if props.key?(:Tags) && !props[:Tags].empty?
|
101
|
+
props[:Tags].concat(rds_instance_tags)
|
102
|
+
else
|
103
|
+
props[:Tags] = rds_instance_tags
|
104
|
+
end
|
105
|
+
|
106
|
+
rds_props = {
|
107
|
+
PubliclyAccessible: 'false',
|
108
|
+
MultiAZ: ref("RDS#{db_name}Multizone"),
|
109
|
+
Engine: ref("RDS#{db_name}Engine"),
|
110
|
+
EngineVersion: ref("RDS#{db_name}EngineVersion"),
|
111
|
+
MasterUsername: ref("RDS#{db_name}Username"),
|
112
|
+
MasterUserPassword: ref("RDS#{db_name}Password"),
|
113
|
+
DBInstanceClass: ref("RDS#{db_name}InstanceType"),
|
114
|
+
VPCSecurityGroups: [ref_resource_security_group, ref_private_security_group],
|
115
|
+
DBSubnetGroupName: ref("RDS#{db_name}SubnetGroup"),
|
116
|
+
DBParameterGroupName: ref("RDS#{db_name}ParameterGroup"),
|
117
|
+
AllocatedStorage: ref("RDS#{db_name}AllocatedStorage"),
|
118
|
+
StorageType: ref("RDS#{db_name}StorageType")
|
119
|
+
}
|
120
|
+
|
121
|
+
rds_instance_resource_name = "RDS#{db_name}Instance"
|
122
|
+
resource rds_instance_resource_name,
|
123
|
+
Type: 'AWS::RDS::DBInstance',
|
124
|
+
Properties: rds_props.merge(props)
|
125
|
+
|
126
|
+
output "RDS#{db_name}EndpointAddress",
|
127
|
+
Description: "#{db_name} Endpoint Address",
|
128
|
+
Value: get_att("RDS#{db_name}Instance", 'Endpoint.Address')
|
129
|
+
|
130
|
+
rds_instance_resource_name
|
131
|
+
end
|
132
|
+
|
133
|
+
# Ensure that plugin using this template is a subclass of EnAppTemplateDSL
|
134
|
+
def self.included(klass)
|
135
|
+
if klass.superclass != Enscalator::EnAppTemplateDSL
|
136
|
+
fail("Plugin #{name.to_s.demodulize} requires template to be subclass of #{EnAppTemplateDSL}")
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end # RDS
|
140
|
+
end # Plugins
|
141
|
+
end # Enscalator
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Enscalator
|
2
|
+
module Plugins
|
3
|
+
# Redis on EC2 instance
|
4
|
+
module Redis
|
5
|
+
include Enscalator::Plugins::Ubuntu
|
6
|
+
|
7
|
+
# Create new Redis instance
|
8
|
+
#
|
9
|
+
# @param [String] instance_name instance name
|
10
|
+
# @param [String] key_name instance key
|
11
|
+
# @param [String] instance_type instance type
|
12
|
+
def redis_init(instance_name,
|
13
|
+
key_name:,
|
14
|
+
instance_type: 't2.medium')
|
15
|
+
|
16
|
+
parameter "Ubuntu#{instance_name}KeyName",
|
17
|
+
Default: key_name,
|
18
|
+
Description: 'Keypair name',
|
19
|
+
Type: 'String'
|
20
|
+
|
21
|
+
ubuntu_init instance_name, instance_type: instance_type, properties: { 'UserData' => redis_user_data }
|
22
|
+
end
|
23
|
+
|
24
|
+
# Install and run Redis on EC2 instance
|
25
|
+
# @return [String] user-data
|
26
|
+
def redis_user_data
|
27
|
+
Base64.encode64(%(
|
28
|
+
#!/usr/bin/env bash
|
29
|
+
apt-get update
|
30
|
+
apt-get upgrade -y
|
31
|
+
apt-get install -y redis-server
|
32
|
+
sed -i 's/bind 127.0.0.1/bind 0.0.0.0/' /etc/redis/redis.conf
|
33
|
+
service redis-server restart
|
34
|
+
).gsub(/^\s+/, ''))
|
35
|
+
end
|
36
|
+
end # Redis
|
37
|
+
end # Plugins
|
38
|
+
end # Enscalator
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Enscalator
|
2
|
+
module Plugins
|
3
|
+
# RethinkDB appliance
|
4
|
+
module RethinkDB
|
5
|
+
# Mapping for Rethinkdb x64 images
|
6
|
+
def self.mapping
|
7
|
+
{
|
8
|
+
'eu-central-1': { paravirtual: 'ami-1249740f' },
|
9
|
+
'eu-west-1': { paravirtual: 'ami-7a40f00d' },
|
10
|
+
'ap-northeast-1': { paravirtual: 'ami-90c3c491' },
|
11
|
+
'us-east-1': { paravirtual: 'ami-6ea9fb06' },
|
12
|
+
'us-west-1': { paravirtual: 'ami-7f6d7d3a' },
|
13
|
+
'us-west-2': { paravirtual: 'ami-cf0d2cff' },
|
14
|
+
'ap-southeast-1': { paravirtual: 'ami-aa5b6cf8' },
|
15
|
+
'ap-southeast-2': { paravirtual: 'ami-b9325b83' },
|
16
|
+
'sa-east-1': { paravirtual: 'ami-b38b3aae' }
|
17
|
+
}.with_indifferent_access
|
18
|
+
end
|
19
|
+
end # RethinkDB
|
20
|
+
end # Plugins
|
21
|
+
end # Enscalator
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module Enscalator
|
2
|
+
module Plugins
|
3
|
+
# Create Route53 resources
|
4
|
+
module Route53
|
5
|
+
# Valid types for Route53 healthcheck
|
6
|
+
HEALTH_CHECK_TYPE = %w(HTTP HTTPS HTTP_STR_MATCH HTTPS_STR_MATCH TCP)
|
7
|
+
|
8
|
+
# Valid types for dns records
|
9
|
+
RECORD_TYPE = %w(A AAAA CNAME MX NS PTR SOA SPF SRV TXT)
|
10
|
+
|
11
|
+
# Create Route53 healthcheck for given fqdn/ip address
|
12
|
+
#
|
13
|
+
# @param [String] app_name application name
|
14
|
+
# @param [String] stack_name stack name
|
15
|
+
# @param [String] fqdn fully qualified domain name (FQDN)
|
16
|
+
# @param [String] ip_address ip address
|
17
|
+
# @param [Integer] port number
|
18
|
+
# @param [String] type healthcheck type
|
19
|
+
# @param [String] resource_path uri path healthcheck backend would query
|
20
|
+
# @param [Integer] request_interval query intervals for healthcheck backend
|
21
|
+
# @param [Integer] failure_threshold number of accumulated failures to consider endpoint not healthy
|
22
|
+
# @param [Array] tags additional tags
|
23
|
+
def create_healthcheck(app_name,
|
24
|
+
stack_name,
|
25
|
+
fqdn: nil,
|
26
|
+
ip_address: nil,
|
27
|
+
port: 80,
|
28
|
+
type: 'HTTP',
|
29
|
+
resource_path: '/',
|
30
|
+
request_interval: 30,
|
31
|
+
failure_threshold: 3,
|
32
|
+
tags: [])
|
33
|
+
unless HEALTH_CHECK_TYPE.include?(type)
|
34
|
+
fail("Route53 healthcheck type can only be one of the following: #{HEALTH_CHECK_TYPE.join(',')}")
|
35
|
+
end
|
36
|
+
fail('Route53 healthcheck requires either fqdn or ip address') unless fqdn || ip_address
|
37
|
+
|
38
|
+
properties = {
|
39
|
+
HealthCheckConfig: {
|
40
|
+
IPAddress: ip_address,
|
41
|
+
FullyQualifiedDomainName: fqdn,
|
42
|
+
Port: port,
|
43
|
+
Type: type,
|
44
|
+
ResourcePath: resource_path,
|
45
|
+
RequestInterval: request_interval,
|
46
|
+
FailureThreshold: failure_threshold
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
properties[:HealthCheckTags] = [
|
51
|
+
{
|
52
|
+
Key: 'Application',
|
53
|
+
Value: app_name
|
54
|
+
},
|
55
|
+
{
|
56
|
+
Key: 'Stack',
|
57
|
+
Value: stack_name
|
58
|
+
}
|
59
|
+
]
|
60
|
+
|
61
|
+
properties[:HealthCheckTags].concat(tags) unless tags.blank?
|
62
|
+
|
63
|
+
resource "#{app_name}Healthcheck",
|
64
|
+
Type: 'AWS::Route53::HealthCheck',
|
65
|
+
Properties: properties
|
66
|
+
end
|
67
|
+
|
68
|
+
# [RESERVED] Create new hosted zone
|
69
|
+
def create_hosted_zone
|
70
|
+
fail('method "create_hosted_zone" is not implemented yet')
|
71
|
+
end
|
72
|
+
|
73
|
+
# TODO: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-recordset.html
|
74
|
+
|
75
|
+
# Create new single record set for given hosted zone
|
76
|
+
#
|
77
|
+
# @param [String] app_name application name
|
78
|
+
# @param [String] stack_name stack name
|
79
|
+
# @param [String] zone_name hosted zone name
|
80
|
+
# @param [String] record_name dns record name
|
81
|
+
# @param [Integer] ttl time to live
|
82
|
+
# @param [String] type dns record type
|
83
|
+
# @param [Hash] healthcheck reference to the healthcheck resource
|
84
|
+
# @param [Hash] alias_target alias target
|
85
|
+
# @param [Array] resource_records resources associated with record_name
|
86
|
+
def create_single_dns_record(app_name,
|
87
|
+
stack_name,
|
88
|
+
zone_name,
|
89
|
+
record_name,
|
90
|
+
ttl: 300,
|
91
|
+
type: 'A',
|
92
|
+
healthcheck: nil,
|
93
|
+
zone_id: nil,
|
94
|
+
alias_target: {},
|
95
|
+
resource_records: [])
|
96
|
+
if type && !RECORD_TYPE.include?(type)
|
97
|
+
fail("Route53 record type can only be one of the following: #{RECORD_TYPE.join(',')}")
|
98
|
+
end
|
99
|
+
if healthcheck && (!healthcheck.is_a?(Hash) || !healthcheck.include?(:Ref))
|
100
|
+
fail('healthcheck must be a valid cloudformation Ref function')
|
101
|
+
end
|
102
|
+
if alias_target && (!alias_target.is_a?(Hash))
|
103
|
+
fail('AliasTarget must be a Hash')
|
104
|
+
end
|
105
|
+
|
106
|
+
name = app_name || stack_name.titleize.remove(/\s/)
|
107
|
+
properties = {
|
108
|
+
Name: record_name,
|
109
|
+
Comment: "#{type} record for #{[app_name, 'in '].join(' ') if app_name}#{stack_name} stack",
|
110
|
+
Type: type
|
111
|
+
}
|
112
|
+
|
113
|
+
# HostedZoneId and HostedZoneName options are mutually exclusive
|
114
|
+
if zone_id && !zone_id.nil?
|
115
|
+
properties[:HostedZoneId] = zone_id
|
116
|
+
else
|
117
|
+
properties[:HostedZoneName] = zone_name
|
118
|
+
end
|
119
|
+
|
120
|
+
if !alias_target.blank?
|
121
|
+
fail('AliasTarget can be created only for A or AAAA type records') unless %w(A AAAA).include?(type)
|
122
|
+
unless alias_target.key?(:HostedZoneId) && alias_target.key?(:DNSName)
|
123
|
+
fail('AliasTarget must have HostedZoneId and DNSName properties')
|
124
|
+
end
|
125
|
+
properties[:AliasTarget] = alias_target
|
126
|
+
else
|
127
|
+
properties[:TTL] = ttl
|
128
|
+
properties[:HealthCheckId] = healthcheck if healthcheck
|
129
|
+
properties[:ResourceRecords] = resource_records.empty? ? ref("#{app_name}PublicIpAddress") : resource_records
|
130
|
+
end
|
131
|
+
|
132
|
+
resource "#{name}Hostname",
|
133
|
+
Type: 'AWS::Route53::RecordSet',
|
134
|
+
Properties: properties
|
135
|
+
end
|
136
|
+
|
137
|
+
# [RESERVED] Create multiple record sets for given hosted zone
|
138
|
+
def create_multiple_dns_records
|
139
|
+
fail('method "create_multiple_dns_records" is not implemented')
|
140
|
+
end
|
141
|
+
end # module Route53
|
142
|
+
end # module Plugins
|
143
|
+
end # module Enscalator
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Enscalator
|
2
|
+
module Plugins
|
3
|
+
# Ubuntu appliance
|
4
|
+
module Ubuntu
|
5
|
+
class << self
|
6
|
+
# Supported storage types in AWS
|
7
|
+
STORAGE = [:ebs, :'ebs-io1', :'ebs-ssd', :'instance-store']
|
8
|
+
|
9
|
+
# Supported Ubuntu image architectures
|
10
|
+
ARCH = [:amd64, :i386]
|
11
|
+
|
12
|
+
# Supported Ubuntu releases
|
13
|
+
RELEASE = {
|
14
|
+
vivid: '15.04',
|
15
|
+
utopic: '14.10',
|
16
|
+
trusty: '14.04',
|
17
|
+
saucy: '13.10',
|
18
|
+
raring: '13.04',
|
19
|
+
quantal: '12.10',
|
20
|
+
precise: '12.04'
|
21
|
+
}
|
22
|
+
|
23
|
+
# Structure to hold parsed record
|
24
|
+
Struct.new('Ubuntu', :name, :edition, :state, :timestamp, :root_storage, :arch, :region, :ami, :virtualization)
|
25
|
+
|
26
|
+
# Get mapping for Ubuntu images
|
27
|
+
#
|
28
|
+
# @param [Symbol, String] release a codename or version number
|
29
|
+
# @param [Symbol] storage storage kind
|
30
|
+
# @param [Symbol] arch architecture
|
31
|
+
# @raise [ArgumentError] if release is nil, empty or not one of supported values
|
32
|
+
# @raise [ArgumentError] if storage is nil, empty or not one of supported values
|
33
|
+
# @raise [ArgumentError] if arch is nil, empty or not one of supported values
|
34
|
+
# @return [Hash] mapping for Ubuntu amis
|
35
|
+
def get_mapping(release: :trusty, storage: :ebs, arch: :amd64)
|
36
|
+
fail ArgumentError, 'release can be either codename or version' unless RELEASE.to_a.flatten.include? release
|
37
|
+
fail ArgumentError, "storage can only be one of #{STORAGE}" unless STORAGE.include? storage
|
38
|
+
fail ArgumentError, "arch can only be one of #{ARCH}" unless ARCH.include? arch
|
39
|
+
begin
|
40
|
+
version = RELEASE.keys.include?(release) ? release : RELEASE.key(release)
|
41
|
+
body = open("https://cloud-images.ubuntu.com/query/#{version}/server/released.current.txt") { |f| f.read }
|
42
|
+
body.split("\n").map { |m| m.squeeze("\t").split("\t").reject { |r| r.include? 'aki' } }
|
43
|
+
.map { |l| Struct::Ubuntu.new(*l) }
|
44
|
+
.select { |r| r.root_storage == storage.to_s && r.arch == arch.to_s }
|
45
|
+
.group_by(&:region)
|
46
|
+
.map { |k, v| [k, v.map { |i| [i.virtualization, i.ami] }.to_h] }
|
47
|
+
.to_h
|
48
|
+
.with_indifferent_access
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end # class << self
|
52
|
+
|
53
|
+
# Create new Ubuntu instance
|
54
|
+
#
|
55
|
+
# @param [String] instance_name instance name
|
56
|
+
# @param [String] storage storage kind (ebs or ephemeral)
|
57
|
+
# @param [String] arch architecture (amd64 or i386)
|
58
|
+
# @param [String] instance_type instance type
|
59
|
+
def ubuntu_init(instance_name,
|
60
|
+
storage: :ebs,
|
61
|
+
arch: :amd64,
|
62
|
+
instance_type: 't2.medium', properties: {})
|
63
|
+
|
64
|
+
mapping 'AWSUbuntuAMI', Ubuntu.get_mapping(storage: storage, arch: arch)
|
65
|
+
|
66
|
+
parameter_allocated_storage "Ubuntu#{instance_name}",
|
67
|
+
default: 5,
|
68
|
+
min: 5,
|
69
|
+
max: 1024
|
70
|
+
|
71
|
+
parameter_ec2_instance_type "Ubuntu#{instance_name}", type: instance_type
|
72
|
+
|
73
|
+
instance_vpc "Ubuntu#{instance_name}",
|
74
|
+
find_in_map('AWSUbuntuAMI', ref('AWS::Region'), 'hvm'),
|
75
|
+
ref_application_subnets.first,
|
76
|
+
[ref_private_security_group, ref_application_security_group],
|
77
|
+
depends_on: [],
|
78
|
+
properties: {
|
79
|
+
KeyName: ref("Ubuntu#{instance_name}KeyName"),
|
80
|
+
InstanceType: ref("Ubuntu#{instance_name}InstanceType")
|
81
|
+
}.merge(properties)
|
82
|
+
end
|
83
|
+
end # Ubuntu
|
84
|
+
end # Plugins
|
85
|
+
end # Enscalator
|