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,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
|