enscalator 0.4.0.pre.alpha.pre.16
Sign up to get free protection for your applications and to get access to all the features.
- 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,75 @@
|
|
1
|
+
module Enscalator
|
2
|
+
module Plugins
|
3
|
+
# Amazon Elasticsearch Service (Amazon ES)
|
4
|
+
module ElasticsearchAmazon
|
5
|
+
# Create new service instance
|
6
|
+
# @param [String] cluster_name name of the cluster resource
|
7
|
+
# @param [Hash] properties additional parameters for cluster configuration
|
8
|
+
def elasticsearch_init(cluster_name, properties: {})
|
9
|
+
cluster_properties = {
|
10
|
+
AccessPolicies: {
|
11
|
+
Version: '2012-10-17',
|
12
|
+
Statement: [
|
13
|
+
{
|
14
|
+
Effect: 'Allow',
|
15
|
+
Principal: {
|
16
|
+
AWS: '*'
|
17
|
+
}
|
18
|
+
}
|
19
|
+
]
|
20
|
+
},
|
21
|
+
AdvancedOptions: {
|
22
|
+
'rest.action.multi.allow_explicit_index': 'true'
|
23
|
+
},
|
24
|
+
EBSOptions: {
|
25
|
+
EBSEnabled: true,
|
26
|
+
Iops: 0,
|
27
|
+
VolumeSize: 100,
|
28
|
+
VolumeType: 'gp2'
|
29
|
+
},
|
30
|
+
ElasticsearchClusterConfig: {
|
31
|
+
InstanceCount: '1',
|
32
|
+
InstanceType: 'm3.medium.elasticsearch'
|
33
|
+
},
|
34
|
+
SnapshotOptions: {
|
35
|
+
AutomatedSnapshotStartHour: '0'
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
# do not modify properties passed from template
|
40
|
+
props = properties.deep_dup
|
41
|
+
|
42
|
+
default_tags = [
|
43
|
+
{
|
44
|
+
Key: 'ClusterName',
|
45
|
+
Value: cluster_name.downcase
|
46
|
+
}
|
47
|
+
]
|
48
|
+
|
49
|
+
if props.key?(:Tags) && !props[:Tags].empty?
|
50
|
+
props[:Tags].concat(default_tags)
|
51
|
+
else
|
52
|
+
props[:Tags] = default_tags
|
53
|
+
end
|
54
|
+
|
55
|
+
resource cluster_name,
|
56
|
+
Type: 'AWS::Elasticsearch::Domain',
|
57
|
+
Properties: cluster_properties.merge(props)
|
58
|
+
|
59
|
+
output "#{cluster_name}ResourceID",
|
60
|
+
Description: "#{cluster_name} ResourceID",
|
61
|
+
Value: ref(cluster_name)
|
62
|
+
|
63
|
+
output "#{cluster_name}DomainArn",
|
64
|
+
Description: "#{cluster_name} DomainArn",
|
65
|
+
Value: get_att(cluster_name, 'DomainArn')
|
66
|
+
|
67
|
+
output "#{cluster_name}DomainEndpoint",
|
68
|
+
Description: "#{cluster_name} DomainEndpoint",
|
69
|
+
Value: get_att(cluster_name, 'DomainEndpoint')
|
70
|
+
|
71
|
+
cluster_name
|
72
|
+
end
|
73
|
+
end # module ElasticsearchAmazon
|
74
|
+
end # module Plugins
|
75
|
+
end # module Enscalator
|
@@ -0,0 +1,198 @@
|
|
1
|
+
module Enscalator
|
2
|
+
module Plugins
|
3
|
+
# Elasticsearch related configuration
|
4
|
+
module ElasticsearchBitnami
|
5
|
+
# Retrieves mapping for Elasticsearch Bitnami stack
|
6
|
+
class << self
|
7
|
+
# Supported storage types in AWS
|
8
|
+
STORAGE = [:ebs, :'instance-store']
|
9
|
+
|
10
|
+
# Supported Elasticsearch image architectures
|
11
|
+
ARCH = [:amd64, :i386]
|
12
|
+
|
13
|
+
# Get ami region/virtualization type mapping
|
14
|
+
#
|
15
|
+
# @param [Symbol] storage image root storage
|
16
|
+
# @param [Symbol] arch image architecture type
|
17
|
+
# @return [Hash] mapping
|
18
|
+
def get_mapping(storage: :ebs, arch: :amd64)
|
19
|
+
fail ArgumentError, "storage can only be one of #{STORAGE}" unless STORAGE.include? storage
|
20
|
+
fail ArgumentError, "arch can only be one of #{ARCH}" unless ARCH.include? arch
|
21
|
+
fetch_mapping(storage, arch)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Get ami release version string
|
25
|
+
#
|
26
|
+
# @param [Symbol] storage image root storage
|
27
|
+
# @param [Symbol] arch image architecture type
|
28
|
+
# @return [Hash] mapping
|
29
|
+
def get_release_version(storage: :ebs, arch: :amd64)
|
30
|
+
fail ArgumentError, "storage can only be one of #{STORAGE}" unless STORAGE.include? storage
|
31
|
+
fail ArgumentError, "arch can only be one of #{ARCH}" unless ARCH.include? arch
|
32
|
+
fetch_versions
|
33
|
+
.select { |r| r.root_storage == storage && r.arch == arch }
|
34
|
+
.map { |v| v.version.to_s }.uniq.first
|
35
|
+
.gsub(/[-][\w\d]/, '')
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# Structure to hold parsed record
|
41
|
+
Struct.new('ElasticSearch', :name, :version, :baseos, :root_storage, :arch, :region, :ami, :virtualization)
|
42
|
+
|
43
|
+
# Always fetches the most recent version
|
44
|
+
#
|
45
|
+
# @param [Symbol] storage image root storage
|
46
|
+
# @param [Symbol] arch image architecture type
|
47
|
+
# @return [Hash] mapping
|
48
|
+
def fetch_mapping(storage, arch)
|
49
|
+
versions = fetch_versions
|
50
|
+
versions.select { |r| r.root_storage == storage && r.arch == arch }
|
51
|
+
.group_by(&:region)
|
52
|
+
.map { |k, v| [k, v.map { |i| [i.virtualization, i.ami] }.to_h] }.to_h
|
53
|
+
.with_indifferent_access
|
54
|
+
end
|
55
|
+
|
56
|
+
# Make request to Bitnami Elasticsearch release pages, parse response and make list of versions
|
57
|
+
#
|
58
|
+
# @return [Array] list of all versions across all AWS regions
|
59
|
+
def fetch_versions
|
60
|
+
html = Nokogiri::HTML(open('https://bitnami.com/stack/elasticsearch/cloud/amazon'))
|
61
|
+
raw_entries = html.xpath('//td[@class="instance_id"]')
|
62
|
+
entries = raw_entries.xpath('a')
|
63
|
+
raw_entries.xpath('strong/a').each { |sa| entries << sa }
|
64
|
+
raw_versions = entries.map do |i|
|
65
|
+
[
|
66
|
+
i.xpath('@href').first.value.split('/').last,
|
67
|
+
i.children.first.text
|
68
|
+
]
|
69
|
+
end.to_h
|
70
|
+
parse_versions(raw_versions)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Parse list of raw strings
|
74
|
+
#
|
75
|
+
# @param entries [Array] list of strings
|
76
|
+
# @return [Array]
|
77
|
+
def parse_versions(entries)
|
78
|
+
entries.map do |rw, ami|
|
79
|
+
str, region = rw.split('?').map { |s| s.start_with?('region') ? s.split('=').last : s }
|
80
|
+
version_str = fix_entry(str).split('-')
|
81
|
+
name, version, baseos = version_str
|
82
|
+
Struct::ElasticSearch.new(name,
|
83
|
+
Semantic::Version.new(version.tr('=', '-')),
|
84
|
+
baseos,
|
85
|
+
version_str.include?('ebs') ? :ebs : :'instance-store',
|
86
|
+
version_str.include?('x64') ? :amd64 : :i386,
|
87
|
+
region,
|
88
|
+
ami,
|
89
|
+
version_str.include?('hvm') ? :hvm : :pv)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Fix elasticsearch version string to have predictable format
|
94
|
+
#
|
95
|
+
# @param [String] str raw version string
|
96
|
+
# @return [String] reformatted version string
|
97
|
+
def fix_entry(str)
|
98
|
+
pattern = '[-](?:[\w\d]+){1,3}[-]ami'
|
99
|
+
token = begin
|
100
|
+
Regexp.new(pattern.gsub('?:', '')).match(str)[1]
|
101
|
+
rescue
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
str.gsub(Regexp.new(pattern), ['=', token, '-'].join)
|
105
|
+
end
|
106
|
+
end # class << self
|
107
|
+
|
108
|
+
# Get aws account id
|
109
|
+
# @return [String] account id
|
110
|
+
def aws_account_id
|
111
|
+
# noinspection RubyArgCount
|
112
|
+
Aws::IAM::Client.new.get_user.user.arn.split(':')[4]
|
113
|
+
end
|
114
|
+
|
115
|
+
# Create new elasticsearch instance
|
116
|
+
#
|
117
|
+
# @param [String] storage_name storage name
|
118
|
+
# @param [Integer] allocated_storage size of instance primary storage
|
119
|
+
# @param [String] instance_type instance type
|
120
|
+
# @param [Hash] properties additional properties
|
121
|
+
# @param [String] zone_name route53 zone name
|
122
|
+
def elasticsearch_init(storage_name,
|
123
|
+
allocated_storage: 5,
|
124
|
+
instance_type: 't2.medium',
|
125
|
+
properties: {},
|
126
|
+
zone_name: nil)
|
127
|
+
|
128
|
+
@es_key_name = gen_ssh_key_name("Elasticsearch#{storage_name}", region, stack_name)
|
129
|
+
pre_run { create_ssh_key(@es_key_name, region, force_create: false) }
|
130
|
+
|
131
|
+
mapping 'AWSElasticsearchAMI', ElasticsearchBitnami.get_mapping
|
132
|
+
|
133
|
+
parameter_allocated_storage "Elasticsearch#{storage_name}",
|
134
|
+
default: allocated_storage,
|
135
|
+
min: 5,
|
136
|
+
max: 1024
|
137
|
+
|
138
|
+
parameter_ec2_instance_type "Elasticsearch#{storage_name}", type: instance_type
|
139
|
+
|
140
|
+
properties[:KeyName] = @es_key_name
|
141
|
+
properties[:InstanceType] = ref("Elasticsearch#{storage_name}InstanceType")
|
142
|
+
|
143
|
+
version_tag = {
|
144
|
+
Key: 'Version',
|
145
|
+
Value: ElasticsearchBitnami.get_release_version
|
146
|
+
}
|
147
|
+
|
148
|
+
cluster_name_tag = {
|
149
|
+
Key: 'ClusterName',
|
150
|
+
Value: storage_name.downcase
|
151
|
+
}
|
152
|
+
|
153
|
+
plugin_tags = [version_tag, cluster_name_tag]
|
154
|
+
|
155
|
+
# Set instance tags
|
156
|
+
if properties.key?(:Tags) && !properties[:Tags].empty?
|
157
|
+
properties[:Tags].concat(plugin_tags)
|
158
|
+
else
|
159
|
+
properties[:Tags] = plugin_tags
|
160
|
+
end
|
161
|
+
|
162
|
+
# Configure instance using user-data
|
163
|
+
if !properties.key?(:UserData) || !properties[:UserData].empty?
|
164
|
+
properties[:UserData] = Base64.encode64(read_user_data('elasticsearch'))
|
165
|
+
end
|
166
|
+
|
167
|
+
# Assign IAM role to instance
|
168
|
+
properties[:IamInstanceProfile] = iam_instance_profile_with_full_access(storage_name, *%w(ec2 s3))
|
169
|
+
|
170
|
+
storage_resource_name = "Elasticsearch#{storage_name}"
|
171
|
+
instance_vpc storage_resource_name,
|
172
|
+
find_in_map('AWSElasticsearchAMI', ref('AWS::Region'), :hvm),
|
173
|
+
ref_application_subnets.first,
|
174
|
+
[ref_private_security_group, ref_resource_security_group],
|
175
|
+
depends_on: [],
|
176
|
+
properties: properties
|
177
|
+
|
178
|
+
# create s3 bucket for cluster snapshots
|
179
|
+
account_id = aws_account_id
|
180
|
+
bucket_name = "elasticsearch-bitnami-#{region}-#{account_id}"
|
181
|
+
resource "Elasticsearch#{storage_name}S3Bucket",
|
182
|
+
Type: 'AWS::S3::Bucket',
|
183
|
+
DeletionPolicy: 'Retain',
|
184
|
+
Properties: {
|
185
|
+
BucketName: bucket_name
|
186
|
+
}
|
187
|
+
|
188
|
+
# create a DNS record in route53 for instance private ip
|
189
|
+
record_name = %W(#{storage_name.downcase.dasherize} #{region} #{zone_name}).join('.')
|
190
|
+
create_single_dns_record("#{storage_name}PrivateZone",
|
191
|
+
stack_name,
|
192
|
+
zone_name,
|
193
|
+
record_name,
|
194
|
+
resource_records: [get_att(storage_resource_name, 'PrivateIp')])
|
195
|
+
end
|
196
|
+
end # module ElasticsearchBitnami
|
197
|
+
end # module Plugins
|
198
|
+
end # module Enscalator
|
@@ -0,0 +1,225 @@
|
|
1
|
+
module Enscalator
|
2
|
+
module Plugins
|
3
|
+
# Elasticsearch related configuration
|
4
|
+
module ElasticsearchOpsworks
|
5
|
+
include Enscalator::Plugins::Elb
|
6
|
+
|
7
|
+
# Create Elasticsearch cluster using Opsworks
|
8
|
+
#
|
9
|
+
# @param [String] app_name application name
|
10
|
+
# @param [String] ssh_key name of the ssh key
|
11
|
+
# @param [String] os base operating system
|
12
|
+
# @param [String] cookbook chef cookbook
|
13
|
+
def elasticsearch_init(app_name,
|
14
|
+
ssh_key:,
|
15
|
+
es_config: {},
|
16
|
+
os: 'Amazon Linux 2015.09',
|
17
|
+
cookbook: 'https://github.com/en-japan-air/opsworks-elasticsearch-cookbook.git')
|
18
|
+
|
19
|
+
parameter "ES#{app_name}ChefCookbook",
|
20
|
+
Default: cookbook,
|
21
|
+
Description: 'GitURL',
|
22
|
+
Type: 'String'
|
23
|
+
|
24
|
+
parameter "ES#{app_name}InstanceDefaultOs",
|
25
|
+
Default: os,
|
26
|
+
Description: ['The stack\'s default operating system, which is installed',
|
27
|
+
'on every instance unless you specify a different',
|
28
|
+
'operating system when you create the instance.'].join(' '),
|
29
|
+
Type: 'String'
|
30
|
+
|
31
|
+
parameter "ES#{app_name}SshKeyName",
|
32
|
+
Default: ssh_key,
|
33
|
+
Description: 'SSH key name for EC2 instances.',
|
34
|
+
Type: 'String'
|
35
|
+
|
36
|
+
resource 'InstanceRole',
|
37
|
+
Type: 'AWS::IAM::InstanceProfile',
|
38
|
+
Properties: {
|
39
|
+
Path: '/',
|
40
|
+
Roles: [
|
41
|
+
ref('OpsWorksEC2Role')
|
42
|
+
]
|
43
|
+
}
|
44
|
+
|
45
|
+
resource 'ServiceRole',
|
46
|
+
Type: 'AWS::IAM::Role',
|
47
|
+
Properties: {
|
48
|
+
AssumeRolePolicyDocument: {
|
49
|
+
Statement: [
|
50
|
+
{
|
51
|
+
Effect: 'Allow',
|
52
|
+
Principal: {
|
53
|
+
Service: [
|
54
|
+
'opsworks.amazonaws.com'
|
55
|
+
]
|
56
|
+
},
|
57
|
+
Action: [
|
58
|
+
'sts:AssumeRole'
|
59
|
+
]
|
60
|
+
}
|
61
|
+
]
|
62
|
+
},
|
63
|
+
Path: '/',
|
64
|
+
Policies: [
|
65
|
+
{
|
66
|
+
PolicyName: "#{app_name}-opsworks-service",
|
67
|
+
PolicyDocument: {
|
68
|
+
Statement: [
|
69
|
+
{
|
70
|
+
Effect: 'Allow',
|
71
|
+
Action: %w( ec2:* iam:PassRole cloudwatch:GetMetricStatistics elasticloadbalancing:* ),
|
72
|
+
Resource: '*'
|
73
|
+
}
|
74
|
+
]
|
75
|
+
}
|
76
|
+
}
|
77
|
+
]
|
78
|
+
}
|
79
|
+
|
80
|
+
resource 'OpsWorksEC2Role',
|
81
|
+
Type: 'AWS::IAM::Role',
|
82
|
+
Properties: {
|
83
|
+
AssumeRolePolicyDocument: {
|
84
|
+
Statement: [
|
85
|
+
{
|
86
|
+
Effect: 'Allow',
|
87
|
+
Principal: {
|
88
|
+
Service: [
|
89
|
+
'ec2.amazonaws.com'
|
90
|
+
]
|
91
|
+
},
|
92
|
+
Action: [
|
93
|
+
'sts:AssumeRole'
|
94
|
+
]
|
95
|
+
}
|
96
|
+
]
|
97
|
+
},
|
98
|
+
Path: '/',
|
99
|
+
Policies: [
|
100
|
+
{
|
101
|
+
PolicyName: "#{app_name}-opsworks-ec2-role",
|
102
|
+
PolicyDocument: {
|
103
|
+
Statement: [
|
104
|
+
{
|
105
|
+
Effect: 'Allow',
|
106
|
+
Action: %w(
|
107
|
+
ec2:DescribeInstances
|
108
|
+
ec2:DescribeRegions
|
109
|
+
ec2:DescribeSecurityGroups
|
110
|
+
ec2:DescribeTags
|
111
|
+
cloudwatch:PutMetricData),
|
112
|
+
Resource: '*'
|
113
|
+
}
|
114
|
+
]
|
115
|
+
}
|
116
|
+
}
|
117
|
+
]
|
118
|
+
}
|
119
|
+
|
120
|
+
instances_security_group = security_group_vpc("ES#{app_name}", 'so that ES cluster can find other nodes', vpc.id)
|
121
|
+
|
122
|
+
ops_stack_name = "#{app_name}-ES"
|
123
|
+
resource 'ESStack',
|
124
|
+
Type: 'AWS::OpsWorks::Stack',
|
125
|
+
Properties: {
|
126
|
+
Name: ops_stack_name,
|
127
|
+
VpcId: vpc.id,
|
128
|
+
DefaultSubnetId: ref_resource_subnets.first,
|
129
|
+
ConfigurationManager: {
|
130
|
+
Name: 'Chef',
|
131
|
+
Version: '12'
|
132
|
+
},
|
133
|
+
UseCustomCookbooks: 'true',
|
134
|
+
CustomCookbooksSource: {
|
135
|
+
Type: 'git',
|
136
|
+
Url: ref("ES#{app_name}ChefCookbook")
|
137
|
+
},
|
138
|
+
DefaultOs: ref("ES#{app_name}InstanceDefaultOs"),
|
139
|
+
DefaultRootDeviceType: 'ebs',
|
140
|
+
DefaultSshKeyName: ref("ES#{app_name}SshKeyName"),
|
141
|
+
CustomJson: {
|
142
|
+
java: {
|
143
|
+
jdk_version: '8',
|
144
|
+
oracle: {
|
145
|
+
accept_oracle_download_terms: 'true'
|
146
|
+
},
|
147
|
+
accept_license_agreement: 'true',
|
148
|
+
install_flavor: 'oracle'
|
149
|
+
},
|
150
|
+
elasticsearch: {
|
151
|
+
plugins: [
|
152
|
+
'analysis-kuromoji',
|
153
|
+
'cloud-aws',
|
154
|
+
{ name: 'elasticsearch-head', url: 'mobz/elasticsearch-head' }
|
155
|
+
],
|
156
|
+
config: {
|
157
|
+
'cluster.name': "#{app_name}-elasticsearch",
|
158
|
+
'path.data': '/mnt/elasticsearch-data',
|
159
|
+
'path.logs': '/mnt/elasticsearch-data/logs/',
|
160
|
+
'network.bind_host': '0.0.0.0',
|
161
|
+
'network.publish_host': '_non_loopback_',
|
162
|
+
'index.routing.allocation.disable_allocation': 'false',
|
163
|
+
'cloud.aws.region': region,
|
164
|
+
discovery: {
|
165
|
+
type: 'ec2',
|
166
|
+
ec2: {
|
167
|
+
groups: [ref(instances_security_group)],
|
168
|
+
tag: {
|
169
|
+
'opsworks:stack': ops_stack_name
|
170
|
+
}
|
171
|
+
}
|
172
|
+
},
|
173
|
+
'cluster.routing.allocation.awareness.attributes': 'rack_id'
|
174
|
+
}.merge(es_config)
|
175
|
+
}
|
176
|
+
},
|
177
|
+
ServiceRoleArn: {
|
178
|
+
'Fn::GetAtt': %w(ServiceRole Arn)
|
179
|
+
},
|
180
|
+
DefaultInstanceProfileArn: {
|
181
|
+
'Fn::GetAtt': %w(InstanceRole Arn)
|
182
|
+
}
|
183
|
+
}
|
184
|
+
|
185
|
+
resource 'ESLayer',
|
186
|
+
Type: 'AWS::OpsWorks::Layer',
|
187
|
+
Properties: {
|
188
|
+
StackId: ref('ESStack'),
|
189
|
+
Name: 'Search',
|
190
|
+
Type: 'custom',
|
191
|
+
Shortname: 'search',
|
192
|
+
CustomRecipes: {
|
193
|
+
Setup: %w(apt ark java layer-custom::es-opsworks)
|
194
|
+
},
|
195
|
+
EnableAutoHealing: 'true',
|
196
|
+
AutoAssignElasticIps: 'false',
|
197
|
+
AutoAssignPublicIps: 'false',
|
198
|
+
VolumeConfigurations: [
|
199
|
+
{
|
200
|
+
MountPoint: '/mnt/elasticsearch-data',
|
201
|
+
NumberOfDisks: 1,
|
202
|
+
VolumeType: 'gp2',
|
203
|
+
Size: 100
|
204
|
+
}
|
205
|
+
],
|
206
|
+
CustomSecurityGroupIds: [
|
207
|
+
{
|
208
|
+
'Fn::GetAtt': %W(#{instances_security_group} GroupId)
|
209
|
+
},
|
210
|
+
ref_private_security_group
|
211
|
+
]
|
212
|
+
}
|
213
|
+
|
214
|
+
resource 'ESMainInstance',
|
215
|
+
Type: 'AWS::OpsWorks::Instance',
|
216
|
+
Properties: {
|
217
|
+
# EbsOptimized: true, # Not available for m3.medium
|
218
|
+
InstanceType: 'm3.medium',
|
219
|
+
LayerIds: [ref('ESLayer')],
|
220
|
+
StackId: ref('ESStack')
|
221
|
+
}
|
222
|
+
end
|
223
|
+
end # module Elasticsearch
|
224
|
+
end # module Plugins
|
225
|
+
end # module Enscalator
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module Enscalator
|
2
|
+
module Plugins
|
3
|
+
# Internet facing ELB instance
|
4
|
+
module Elb
|
5
|
+
# Create new ELB instance
|
6
|
+
#
|
7
|
+
# @param [String, Hash] elb_name ELB instance name - can be either String or Fn::Join
|
8
|
+
# @param [Integer] web_server_port application port to which ELB redirects traffic
|
9
|
+
# @param [String] zone_name zone name attached to the vpc
|
10
|
+
# @return [String] ELB resource name
|
11
|
+
def elb_init(elb_name: join('-', aws_stack_name, 'elb'),
|
12
|
+
web_server_port: 9000,
|
13
|
+
health_check_path: '/',
|
14
|
+
zone_name: nil,
|
15
|
+
dns_record_name: "elb.#{stack_name.dasherize}.#{zone_name}",
|
16
|
+
instances: [],
|
17
|
+
ssl: false,
|
18
|
+
internal: true)
|
19
|
+
|
20
|
+
elb_resource_name = 'LoadBalancer'
|
21
|
+
|
22
|
+
parameter 'WebServerPort',
|
23
|
+
Description: 'TCP/IP Port for the web service',
|
24
|
+
Default: web_server_port,
|
25
|
+
Type: 'Number',
|
26
|
+
MinValue: '0',
|
27
|
+
MaxValue: '65535',
|
28
|
+
ConstraintDescription: 'must be an integer between 0 and 65535.'
|
29
|
+
|
30
|
+
security_group_vpc 'ELBSecurityGroup',
|
31
|
+
'Security group of the application servers',
|
32
|
+
ref('VpcId'),
|
33
|
+
security_group_ingress: [
|
34
|
+
{
|
35
|
+
IpProtocol: 'tcp',
|
36
|
+
FromPort: '0',
|
37
|
+
ToPort: '65535',
|
38
|
+
CidrIp: '10.0.0.0/8'
|
39
|
+
}
|
40
|
+
] + (internal ? [] : [
|
41
|
+
{
|
42
|
+
IpProtocol: 'tcp',
|
43
|
+
FromPort: '80',
|
44
|
+
ToPort: '80',
|
45
|
+
CidrIp: '0.0.0.0/0'
|
46
|
+
},
|
47
|
+
{
|
48
|
+
IpProtocol: 'tcp',
|
49
|
+
FromPort: '443',
|
50
|
+
ToPort: '443',
|
51
|
+
CidrIp: '0.0.0.0/0'
|
52
|
+
},
|
53
|
+
{
|
54
|
+
IpProtocol: 'tcp',
|
55
|
+
FromPort: '465',
|
56
|
+
ToPort: '465',
|
57
|
+
CidrIp: '0.0.0.0/0'
|
58
|
+
}
|
59
|
+
]),
|
60
|
+
tags: {
|
61
|
+
Name: join('-', aws_stack_name, 'app', 'sg'),
|
62
|
+
Application: aws_stack_name
|
63
|
+
}
|
64
|
+
|
65
|
+
if ssl
|
66
|
+
parameter 'SSLCertificateId',
|
67
|
+
Description: 'Id of the SSL certificate (iam-servercertgetattributes -s certname)',
|
68
|
+
Type: 'String',
|
69
|
+
ConstraintDescription: 'must be a string'
|
70
|
+
end
|
71
|
+
|
72
|
+
properties = {
|
73
|
+
LoadBalancerName: elb_name,
|
74
|
+
Listeners: [
|
75
|
+
{
|
76
|
+
LoadBalancerPort: '80',
|
77
|
+
InstancePort: ref('WebServerPort'),
|
78
|
+
Protocol: 'HTTP'
|
79
|
+
}
|
80
|
+
] + (ssl == false ? [] : [
|
81
|
+
{ LoadBalancerPort: '443',
|
82
|
+
InstancePort: ref('WebServerPort'),
|
83
|
+
SSLCertificateId: ref('SSLCertificateId'),
|
84
|
+
Protocol: 'HTTPS' }
|
85
|
+
]),
|
86
|
+
HealthCheck: {
|
87
|
+
Target: join('', 'HTTP:', ref_web_server_port, health_check_path),
|
88
|
+
HealthyThreshold: '3',
|
89
|
+
UnhealthyThreshold: '5',
|
90
|
+
Interval: '30',
|
91
|
+
Timeout: '5'
|
92
|
+
},
|
93
|
+
SecurityGroups: [ref('ELBSecurityGroup')],
|
94
|
+
Subnets: internal ? ref_application_subnets : public_subnets,
|
95
|
+
Tags: [
|
96
|
+
{
|
97
|
+
Key: 'Name',
|
98
|
+
Value: elb_name
|
99
|
+
},
|
100
|
+
{
|
101
|
+
Key: 'Application',
|
102
|
+
Value: aws_stack_name
|
103
|
+
},
|
104
|
+
{
|
105
|
+
Key: 'Network',
|
106
|
+
Value: (internal ? 'Private' : 'Public')
|
107
|
+
}
|
108
|
+
]
|
109
|
+
}
|
110
|
+
|
111
|
+
properties[:Scheme] = 'internal' if internal
|
112
|
+
properties[:Instances] = instances if instances && !instances.empty?
|
113
|
+
|
114
|
+
resource elb_resource_name,
|
115
|
+
Type: 'AWS::ElasticLoadBalancing::LoadBalancer',
|
116
|
+
Properties: properties
|
117
|
+
|
118
|
+
# use alias target to create proper cloudformation template for Route53 side of elb configuration
|
119
|
+
alias_target = {
|
120
|
+
HostedZoneId: get_att(elb_resource_name, 'CanonicalHostedZoneNameID'),
|
121
|
+
DNSName: get_att(elb_resource_name, internal ? 'DNSName' : 'CanonicalHostedZoneName')
|
122
|
+
}
|
123
|
+
|
124
|
+
create_single_dns_record(nil,
|
125
|
+
stack_name,
|
126
|
+
zone_name,
|
127
|
+
dns_record_name,
|
128
|
+
alias_target: alias_target)
|
129
|
+
|
130
|
+
output "#{elb_resource_name}DNSName",
|
131
|
+
Description: 'LoadBalancer DNS Name',
|
132
|
+
Value: get_att(elb_resource_name, 'DNSName')
|
133
|
+
|
134
|
+
# return resource name
|
135
|
+
elb_resource_name
|
136
|
+
end
|
137
|
+
end # module Elb
|
138
|
+
end # module Plugins
|
139
|
+
end # module Enscalator
|