formatron 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +12 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +3 -0
  6. data/.simplecov +7 -0
  7. data/.travis.yml +17 -0
  8. data/CODE_OF_CONDUCT.md +13 -0
  9. data/Gemfile +6 -0
  10. data/Guardfile +16 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +93 -0
  13. data/Rakefile +16 -0
  14. data/bin/console +14 -0
  15. data/bin/setup +7 -0
  16. data/exe/formatron +20 -0
  17. data/formatron.gemspec +52 -0
  18. data/lib/formatron.rb +357 -0
  19. data/lib/formatron/aws.rb +197 -0
  20. data/lib/formatron/chef.rb +156 -0
  21. data/lib/formatron/chef/berkshelf.rb +55 -0
  22. data/lib/formatron/chef/keys.rb +48 -0
  23. data/lib/formatron/chef/knife.rb +169 -0
  24. data/lib/formatron/chef_clients.rb +73 -0
  25. data/lib/formatron/cli.rb +33 -0
  26. data/lib/formatron/cli/completion.rb +26 -0
  27. data/lib/formatron/cli/deploy.rb +57 -0
  28. data/lib/formatron/cli/destroy.rb +57 -0
  29. data/lib/formatron/cli/generators/bootstrap.rb +250 -0
  30. data/lib/formatron/cli/generators/credentials.rb +100 -0
  31. data/lib/formatron/cli/generators/instance.rb +118 -0
  32. data/lib/formatron/cli/provision.rb +59 -0
  33. data/lib/formatron/cloud_formation.rb +54 -0
  34. data/lib/formatron/cloud_formation/resources/cloud_formation.rb +27 -0
  35. data/lib/formatron/cloud_formation/resources/ec2.rb +336 -0
  36. data/lib/formatron/cloud_formation/resources/iam.rb +94 -0
  37. data/lib/formatron/cloud_formation/resources/route53.rb +54 -0
  38. data/lib/formatron/cloud_formation/scripts.rb +128 -0
  39. data/lib/formatron/cloud_formation/template.rb +114 -0
  40. data/lib/formatron/cloud_formation/template/parameters.rb +20 -0
  41. data/lib/formatron/cloud_formation/template/vpc.rb +181 -0
  42. data/lib/formatron/cloud_formation/template/vpc/subnet.rb +187 -0
  43. data/lib/formatron/cloud_formation/template/vpc/subnet/acl.rb +147 -0
  44. data/lib/formatron/cloud_formation/template/vpc/subnet/bastion.rb +66 -0
  45. data/lib/formatron/cloud_formation/template/vpc/subnet/chef_server.rb +205 -0
  46. data/lib/formatron/cloud_formation/template/vpc/subnet/instance.rb +162 -0
  47. data/lib/formatron/cloud_formation/template/vpc/subnet/instance/policy.rb +74 -0
  48. data/lib/formatron/cloud_formation/template/vpc/subnet/instance/security_group.rb +117 -0
  49. data/lib/formatron/cloud_formation/template/vpc/subnet/instance/setup.rb +68 -0
  50. data/lib/formatron/cloud_formation/template/vpc/subnet/nat.rb +94 -0
  51. data/lib/formatron/completion.rb +26 -0
  52. data/lib/formatron/completion/completion.sh.erb +35 -0
  53. data/lib/formatron/config.rb +31 -0
  54. data/lib/formatron/config/reader.rb +29 -0
  55. data/lib/formatron/dsl.rb +15 -0
  56. data/lib/formatron/dsl/formatron.rb +25 -0
  57. data/lib/formatron/dsl/formatron/global.rb +19 -0
  58. data/lib/formatron/dsl/formatron/global/ec2.rb +17 -0
  59. data/lib/formatron/dsl/formatron/vpc.rb +17 -0
  60. data/lib/formatron/dsl/formatron/vpc/subnet.rb +27 -0
  61. data/lib/formatron/dsl/formatron/vpc/subnet/acl.rb +18 -0
  62. data/lib/formatron/dsl/formatron/vpc/subnet/chef_server.rb +32 -0
  63. data/lib/formatron/dsl/formatron/vpc/subnet/chef_server/organization.rb +22 -0
  64. data/lib/formatron/dsl/formatron/vpc/subnet/instance.rb +29 -0
  65. data/lib/formatron/dsl/formatron/vpc/subnet/instance/chef.rb +22 -0
  66. data/lib/formatron/dsl/formatron/vpc/subnet/instance/policy.rb +21 -0
  67. data/lib/formatron/dsl/formatron/vpc/subnet/instance/policy/statement.rb +23 -0
  68. data/lib/formatron/dsl/formatron/vpc/subnet/instance/security_group.rb +21 -0
  69. data/lib/formatron/dsl/formatron/vpc/subnet/instance/setup.rb +22 -0
  70. data/lib/formatron/dsl/formatron/vpc/subnet/instance/setup/variable.rb +23 -0
  71. data/lib/formatron/external.rb +61 -0
  72. data/lib/formatron/external/dsl.rb +171 -0
  73. data/lib/formatron/external/outputs.rb +25 -0
  74. data/lib/formatron/generators/bootstrap.rb +90 -0
  75. data/lib/formatron/generators/bootstrap/config.rb +62 -0
  76. data/lib/formatron/generators/bootstrap/ec2.rb +17 -0
  77. data/lib/formatron/generators/bootstrap/formatronfile.rb +52 -0
  78. data/lib/formatron/generators/bootstrap/formatronfile/Formatronfile.erb +79 -0
  79. data/lib/formatron/generators/bootstrap/ssl.rb +35 -0
  80. data/lib/formatron/generators/credentials.rb +17 -0
  81. data/lib/formatron/generators/instance.rb +64 -0
  82. data/lib/formatron/generators/instance/config.rb +47 -0
  83. data/lib/formatron/generators/instance/formatronfile.rb +47 -0
  84. data/lib/formatron/generators/instance/formatronfile/Formatronfile.erb +16 -0
  85. data/lib/formatron/generators/util.rb +14 -0
  86. data/lib/formatron/generators/util/cookbook.rb +65 -0
  87. data/lib/formatron/generators/util/gitignore.rb +16 -0
  88. data/lib/formatron/generators/util/readme.rb +18 -0
  89. data/lib/formatron/logger.rb +8 -0
  90. data/lib/formatron/s3/chef_server_cert.rb +85 -0
  91. data/lib/formatron/s3/chef_server_keys.rb +103 -0
  92. data/lib/formatron/s3/cloud_formation_template.rb +61 -0
  93. data/lib/formatron/s3/configuration.rb +58 -0
  94. data/lib/formatron/s3/path.rb +30 -0
  95. data/lib/formatron/util/dsl.rb +107 -0
  96. data/lib/formatron/util/shell.rb +20 -0
  97. data/lib/formatron/util/vpc.rb +15 -0
  98. data/lib/formatron/version.rb +4 -0
  99. data/support/cloudformation_describe_stacks_response.rb +36 -0
  100. data/support/dsl_test.rb +123 -0
  101. data/support/route53_get_hosted_zone_response.rb +21 -0
  102. data/support/s3_get_object_response.rb +21 -0
  103. data/support/template_test.rb +41 -0
  104. metadata +414 -0
@@ -0,0 +1,100 @@
1
+ require 'formatron/generators/credentials'
2
+ require 'formatron/aws'
3
+
4
+ class Formatron
5
+ class CLI
6
+ module Generators
7
+ # CLI command for credentials generator
8
+ module Credentials
9
+ def self.dot_credentials
10
+ File.join '.formatron', 'credentials.json'
11
+ end
12
+
13
+ def self.global_credentials
14
+ File.join Dir.home, dot_credentials
15
+ end
16
+
17
+ def self.local_credentials(directory)
18
+ File.join directory, dot_credentials
19
+ end
20
+
21
+ def self.default_credentials(directory)
22
+ local = local_credentials directory
23
+ if File.file?(local)
24
+ local
25
+ else
26
+ global_credentials
27
+ end
28
+ end
29
+
30
+ def self.default_generated_credentials(directory)
31
+ if File.file?(File.join(directory, 'Formatronfile'))
32
+ local_credentials(directory)
33
+ else
34
+ global_credentials
35
+ end
36
+ end
37
+
38
+ def credentials_options(c)
39
+ c.option '-r', '--region STRING', 'The AWS region'
40
+ c.option '-a', '--access-key-id STRING', 'The AWS access key ID'
41
+ c.option(
42
+ '-s',
43
+ '--secret-access-key STRING',
44
+ 'The AWS secret access key'
45
+ )
46
+ end
47
+
48
+ def credentials_directory(options)
49
+ options.directory || Dir.pwd
50
+ end
51
+
52
+ def credentials_credentials(options)
53
+ options.credentials ||
54
+ ask('Credentials file? ') do |q|
55
+ q.default =
56
+ Credentials.default_generated_credentials(
57
+ credentials_directory(options)
58
+ )
59
+ end
60
+ end
61
+
62
+ def credentials_region(options)
63
+ options.region || choose(
64
+ 'Region:',
65
+ *Formatron::AWS::REGIONS.keys
66
+ )
67
+ end
68
+
69
+ def credentials_access_key_id(options)
70
+ options.access_key_id || ask('Access Key ID? ')
71
+ end
72
+
73
+ def credentials_secret_access_key(options)
74
+ options.secret_access_key || password('Secret Access Key? ')
75
+ end
76
+
77
+ def credentials_action(c)
78
+ c.action do |_args, options|
79
+ Formatron::Generators::Credentials.generate(
80
+ credentials_credentials(options),
81
+ credentials_region(options),
82
+ credentials_access_key_id(options),
83
+ credentials_secret_access_key(options)
84
+ )
85
+ end
86
+ end
87
+
88
+ def credentials_formatron_command
89
+ command :'generate credentials' do |c|
90
+ c.syntax = 'formatron generate credentials [options]'
91
+ c.summary = 'Generate a credentials JSON file'
92
+ c.description = 'Generate a credentials JSON file'
93
+ credentials_options c
94
+ credentials_action c
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,118 @@
1
+ require 'formatron/generators/instance'
2
+
3
+ class Formatron
4
+ class CLI
5
+ module Generators
6
+ # CLI command for instance generator
7
+ module Instance
8
+ # rubocop:disable Metrics/MethodLength
9
+ def instance_options(c)
10
+ c.option '-n', '--name STRING', 'The name for the configuration'
11
+ c.option '-i', '--instance-name STRING', 'The name for the instance'
12
+ c.option(
13
+ '-s',
14
+ '--s3-bucket STRING',
15
+ 'The S3 bucket to store encrypted configuration'
16
+ )
17
+ c.option(
18
+ '-b',
19
+ '--bootstrap-configuration STRING',
20
+ 'The name of the bootstrap configuration to depend on'
21
+ )
22
+ c.option(
23
+ '-p',
24
+ '--vpc STRING',
25
+ 'The name of the VPC to add the instance to'
26
+ )
27
+ c.option(
28
+ '-u',
29
+ '--subnet STRING',
30
+ 'The name of the subnet to add the instance to'
31
+ )
32
+ c.option(
33
+ '-x',
34
+ '--targets LIST',
35
+ Array,
36
+ 'The targets (eg. production test)'
37
+ )
38
+ end
39
+ # rubocop:enable Metrics/MethodLength
40
+
41
+ def instance_directory(options)
42
+ options.directory || ask('Directory? ') do |q|
43
+ q.default = Dir.pwd
44
+ end
45
+ end
46
+
47
+ def instance_name(options, directory)
48
+ options.name || ask('Name? ') do |q|
49
+ q.default = File.basename directory
50
+ end
51
+ end
52
+
53
+ def instance_instance_name(options, name)
54
+ options.instance_name || ask('Instance Name? ') do |q|
55
+ q.default = name
56
+ end
57
+ end
58
+
59
+ def instance_s3_bucket(options)
60
+ options.s3_bucket || ask('S3 Bucket? ')
61
+ end
62
+
63
+ def instance_bootstrap_configuration(options)
64
+ options.bootstrap_configuration ||
65
+ ask('Bootstrap configuration? ')
66
+ end
67
+
68
+ def instance_vpc(options)
69
+ options.vpc || ask('VPC? ') do |q|
70
+ q.default = 'vpc'
71
+ end
72
+ end
73
+
74
+ def instance_subnet(options)
75
+ options.subnet || ask('Subnet? ') do |q|
76
+ q.default = 'private'
77
+ end
78
+ end
79
+
80
+ def instance_targets(options)
81
+ options.targets || ask('Targets? ', Array) do |q|
82
+ q.default = 'production test'
83
+ end
84
+ end
85
+
86
+ # rubocop:disable Metrics/MethodLength
87
+ def instance_action(c)
88
+ c.action do |_args, options|
89
+ directory = instance_directory options
90
+ name = instance_name options, directory
91
+ Formatron::Generators::Instance.generate(
92
+ directory,
93
+ name: name,
94
+ instance_name: instance_instance_name(options, name),
95
+ s3_bucket: instance_s3_bucket(options),
96
+ bootstrap_configuration:
97
+ instance_bootstrap_configuration(options),
98
+ vpc: instance_vpc(options),
99
+ subnet: instance_subnet(options),
100
+ targets: instance_targets(options)
101
+ )
102
+ end
103
+ end
104
+ # rubocop:enable Metrics/MethodLength
105
+
106
+ def instance_formatron_command
107
+ command :'generate instance' do |c|
108
+ c.syntax = 'formatron generate instance [options]'
109
+ c.summary = 'Generate an instance configuration'
110
+ c.description = 'Generate an instance configuration'
111
+ instance_options c
112
+ instance_action c
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,59 @@
1
+ require 'formatron'
2
+ require 'formatron/config'
3
+
4
+ class Formatron
5
+ class CLI
6
+ # CLI command for provision
7
+ module Provision
8
+ def provision_directory(options)
9
+ options.directory || Dir.pwd
10
+ end
11
+
12
+ def provision_credentials(options)
13
+ options.credentials ||
14
+ Generators::Credentials.default_credentials(
15
+ deploy_directory(options)
16
+ )
17
+ end
18
+
19
+ def provision_target(target, directory)
20
+ target || choose(
21
+ 'Target?',
22
+ *Config.targets(directory: directory)
23
+ )
24
+ end
25
+
26
+ def provision_ok(formatron, target)
27
+ !formatron.protected? || agree(
28
+ "Are you sure you wish to provision protected target: #{target}?"
29
+ ) do |q|
30
+ q.default = 'no'
31
+ end
32
+ end
33
+
34
+ def provision_action(c)
35
+ c.action do |args, options|
36
+ directory = provision_directory options
37
+ target = provision_target args[0], directory
38
+ formatron = Formatron.new(
39
+ credentials: provision_credentials(options),
40
+ directory: directory,
41
+ target: target
42
+ )
43
+ formatron.provision if provision_ok formatron, target
44
+ end
45
+ end
46
+
47
+ def provision_formatron_command
48
+ command :provision do |c|
49
+ c.syntax = 'formatron provision [options] [TARGET]'
50
+ c.summary = 'Provision the instances in a Formatron ' \
51
+ 'stack using Opscode Chef'
52
+ c.description = 'Provision the instances in a Formatron ' \
53
+ 'stack using Opscode Chef'
54
+ provision_action c
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,54 @@
1
+ require_relative 's3/cloud_formation_template'
2
+ require 'formatron/logger'
3
+
4
+ class Formatron
5
+ # manage the CloudFormation stack
6
+ module CloudFormation
7
+ # rubocop:disable Metrics/MethodLength
8
+ def self.deploy(aws:, bucket:, name:, target:, parameters:)
9
+ stack_name = _stack_name name, target
10
+ Formatron::LOG.info do
11
+ "Deploy CloudFormation stack: #{stack_name}"
12
+ end
13
+ aws.deploy_stack(
14
+ stack_name: stack_name,
15
+ template_url: S3::CloudFormationTemplate.url(
16
+ region: aws.region,
17
+ bucket: bucket,
18
+ name: name,
19
+ target: target
20
+ ),
21
+ parameters: parameters
22
+ )
23
+ end
24
+ # rubocop:enable Metrics/MethodLength
25
+
26
+ def self.destroy(aws:, name:, target:)
27
+ stack_name = _stack_name name, target
28
+ Formatron::LOG.info do
29
+ "Destroy CloudFormation stack: #{stack_name}"
30
+ end
31
+ aws.delete_stack stack_name: stack_name
32
+ end
33
+
34
+ def self.outputs(aws:, name:, target:)
35
+ stack_name = _stack_name name, target
36
+ Formatron::LOG.info do
37
+ "Query CloudFormation stack outputs: #{stack_name}"
38
+ end
39
+ aws.stack_outputs stack_name: stack_name
40
+ end
41
+
42
+ def self.stack_ready!(aws:, name:, target:)
43
+ aws.stack_ready! stack_name: _stack_name(name, target)
44
+ end
45
+
46
+ def self._stack_name(name, target)
47
+ "#{name}-#{target}"
48
+ end
49
+
50
+ private_class_method(
51
+ :_stack_name
52
+ )
53
+ end
54
+ end
@@ -0,0 +1,27 @@
1
+ require_relative '../template'
2
+
3
+ class Formatron
4
+ module CloudFormation
5
+ module Resources
6
+ # Generates CloudFormation template CloudFormation resources
7
+ module CloudFormation
8
+ def self.wait_condition_handle
9
+ {
10
+ Type: 'AWS::CloudFormation::WaitConditionHandle'
11
+ }
12
+ end
13
+
14
+ def self.wait_condition(instance:, wait_condition_handle:)
15
+ {
16
+ Type: 'AWS::CloudFormation::WaitCondition',
17
+ DependsOn: instance,
18
+ Properties: {
19
+ Handle: Template.ref(wait_condition_handle),
20
+ Timeout: '1800'
21
+ }
22
+ }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,336 @@
1
+ require_relative '../template'
2
+
3
+ class Formatron
4
+ module CloudFormation
5
+ module Resources
6
+ # Generates CloudFormation template EC2 resources
7
+ # rubocop:disable Metrics/ModuleLength
8
+ module EC2
9
+ def self.vpc(cidr:)
10
+ {
11
+ Type: 'AWS::EC2::VPC',
12
+ Properties: {
13
+ CidrBlock: cidr,
14
+ EnableDnsSupport: true,
15
+ EnableDnsHostnames: true,
16
+ InstanceTenancy: 'default'
17
+ }
18
+ }
19
+ end
20
+
21
+ def self.internet_gateway
22
+ {
23
+ Type: 'AWS::EC2::InternetGateway'
24
+ }
25
+ end
26
+
27
+ def self.vpc_gateway_attachment(vpc:, gateway:)
28
+ {
29
+ Type: 'AWS::EC2::VPCGatewayAttachment',
30
+ Properties: {
31
+ InternetGatewayId: Template.ref(gateway),
32
+ VpcId: Template.ref(vpc)
33
+ }
34
+ }
35
+ end
36
+
37
+ def self.route_table(vpc:)
38
+ {
39
+ Type: 'AWS::EC2::RouteTable',
40
+ Properties: {
41
+ VpcId: Template.ref(vpc)
42
+ }
43
+ }
44
+ end
45
+
46
+ # rubocop:disable Metrics/MethodLength
47
+ def self.route(
48
+ route_table:,
49
+ instance: nil,
50
+ internet_gateway: nil,
51
+ vpc_gateway_attachment: nil
52
+ )
53
+ properties = {
54
+ RouteTableId: Template.ref(route_table),
55
+ DestinationCidrBlock: '0.0.0.0/0'
56
+ }
57
+ properties[:GatewayId] =
58
+ Template.ref internet_gateway unless internet_gateway.nil?
59
+ properties[:InstanceId] =
60
+ Template.ref instance unless instance.nil?
61
+ route = {
62
+ Type: 'AWS::EC2::Route',
63
+ Properties: properties
64
+ }
65
+ route[:DependsOn] =
66
+ vpc_gateway_attachment unless vpc_gateway_attachment.nil?
67
+ route
68
+ end
69
+ # rubocop:enable Metrics/MethodLength
70
+
71
+ # rubocop:disable Metrics/MethodLength
72
+ def self.subnet(
73
+ vpc:,
74
+ cidr:,
75
+ availability_zone:,
76
+ map_public_ip_on_launch:
77
+ )
78
+ {
79
+ Type: 'AWS::EC2::Subnet',
80
+ Properties: {
81
+ VpcId: Template.ref(vpc),
82
+ CidrBlock: cidr,
83
+ MapPublicIpOnLaunch: map_public_ip_on_launch,
84
+ AvailabilityZone: Template.join(
85
+ Template.ref('AWS::Region'),
86
+ availability_zone
87
+ )
88
+ }
89
+ }
90
+ end
91
+ # rubocop:enable Metrics/MethodLength
92
+
93
+ def self.subnet_route_table_association(route_table:, subnet:)
94
+ {
95
+ Type: 'AWS::EC2::SubnetRouteTableAssociation',
96
+ Properties: {
97
+ RouteTableId: Template.ref(route_table),
98
+ SubnetId: Template.ref(subnet)
99
+ }
100
+ }
101
+ end
102
+
103
+ def self.network_acl(vpc:)
104
+ {
105
+ Type: 'AWS::EC2::NetworkAcl',
106
+ Properties: {
107
+ VpcId: Template.ref(vpc)
108
+ }
109
+ }
110
+ end
111
+
112
+ def self.subnet_network_acl_association(subnet:, network_acl:)
113
+ {
114
+ Type: 'AWS::EC2::SubnetNetworkAclAssociation',
115
+ Properties: {
116
+ SubnetId: Template.ref(subnet),
117
+ NetworkAclId: Template.ref(network_acl)
118
+ }
119
+ }
120
+ end
121
+
122
+ # rubocop:disable Metrics/MethodLength
123
+ # rubocop:disable Metrics/ParameterLists
124
+ def self.network_acl_entry(
125
+ network_acl:,
126
+ cidr:,
127
+ egress:,
128
+ protocol:,
129
+ action:,
130
+ icmp_code: nil,
131
+ icmp_type: nil,
132
+ start_port: nil,
133
+ end_port: nil,
134
+ number:
135
+ )
136
+ resource = {
137
+ Type: 'AWS::EC2::NetworkAclEntry',
138
+ Properties: {
139
+ NetworkAclId: Template.ref(network_acl),
140
+ CidrBlock: cidr,
141
+ Egress: egress,
142
+ Protocol: protocol,
143
+ RuleAction: action,
144
+ RuleNumber: number
145
+ }
146
+ }
147
+ resource[:Properties][:Icmp] = {
148
+ Code: icmp_code,
149
+ Type: icmp_type
150
+ } unless icmp_code.nil?
151
+ resource[:Properties][:PortRange] = {
152
+ From: start_port,
153
+ To: end_port
154
+ } unless start_port.nil?
155
+ resource
156
+ end
157
+ # rubocop:enable Metrics/ParameterLists
158
+ # rubocop:enable Metrics/MethodLength
159
+
160
+ # rubocop:disable Metrics/MethodLength
161
+ def self.security_group(
162
+ group_description:,
163
+ vpc:,
164
+ egress:,
165
+ ingress:
166
+ )
167
+ {
168
+ Type: 'AWS::EC2::SecurityGroup',
169
+ Properties: {
170
+ GroupDescription: group_description,
171
+ VpcId: Template.ref(vpc),
172
+ SecurityGroupEgress: egress.collect do |rule|
173
+ {
174
+ CidrIp: rule[:cidr],
175
+ IpProtocol: rule[:protocol],
176
+ FromPort: rule[:from_port],
177
+ ToPort: rule[:to_port]
178
+ }
179
+ end,
180
+ SecurityGroupIngress: ingress.collect do |rule|
181
+ {
182
+ CidrIp: rule[:cidr],
183
+ IpProtocol: rule[:protocol],
184
+ FromPort: rule[:from_port],
185
+ ToPort: rule[:to_port]
186
+ }
187
+ end
188
+ }
189
+ }
190
+ end
191
+ # rubocop:enable Metrics/MethodLength
192
+
193
+ # rubocop:disable Metrics/MethodLength
194
+ def self.security_group_egress(
195
+ security_group:,
196
+ cidr:,
197
+ protocol:,
198
+ from_port:,
199
+ to_port:
200
+ )
201
+ {
202
+ Type: 'AWS::EC2::SecurityGroupEgress',
203
+ Properties: {
204
+ GroupId: Template.ref(security_group),
205
+ CidrIp: cidr,
206
+ IpProtocol: protocol,
207
+ FromPort: from_port,
208
+ ToPort: to_port
209
+ }
210
+ }
211
+ end
212
+ # rubocop:enable Metrics/MethodLength
213
+
214
+ # rubocop:disable Metrics/MethodLength
215
+ def self.security_group_ingress(
216
+ security_group:,
217
+ cidr:,
218
+ protocol:,
219
+ from_port:,
220
+ to_port:
221
+ )
222
+ {
223
+ Type: 'AWS::EC2::SecurityGroupIngress',
224
+ Properties: {
225
+ GroupId: Template.ref(security_group),
226
+ CidrIp: cidr,
227
+ IpProtocol: protocol,
228
+ FromPort: from_port,
229
+ ToPort: to_port
230
+ }
231
+ }
232
+ end
233
+ # rubocop:enable Metrics/MethodLength
234
+
235
+ # rubocop:disable Metrics/MethodLength
236
+ # rubocop:disable Metrics/ParameterLists
237
+ # rubocop:disable Metrics/AbcSize
238
+ def self.instance(
239
+ scripts: nil,
240
+ script_variables: nil,
241
+ files: nil,
242
+ instance_profile:,
243
+ availability_zone:,
244
+ instance_type:,
245
+ key_name:,
246
+ subnet:,
247
+ name:,
248
+ wait_condition_handle:,
249
+ security_group:,
250
+ logical_id:,
251
+ source_dest_check:
252
+ )
253
+ files ||= {}
254
+ scripts.each_index do |index|
255
+ files["/tmp/formatron/script-#{index}.sh"] = {
256
+ content: scripts[index],
257
+ mode: '000755',
258
+ owner: 'root',
259
+ group: 'root'
260
+ }
261
+ end unless scripts.nil?
262
+ script_variables_content =
263
+ script_variables.reduce([]) do |content, (key, value)|
264
+ content.concat(["#{key}=", value, "\n"])
265
+ end unless script_variables.nil?
266
+ files['/tmp/formatron/script-variables'] = {
267
+ content: Template.join(*script_variables_content),
268
+ mode: '000644',
269
+ owner: 'root',
270
+ group: 'root'
271
+ } unless script_variables_content.nil?
272
+ {
273
+ Type: 'AWS::EC2::Instance',
274
+ Metadata: {
275
+ Comment1: 'Create setup scripts',
276
+ 'AWS::CloudFormation::Init' => {
277
+ config: {
278
+ files: files
279
+ }
280
+ }
281
+ },
282
+ Properties: {
283
+ IamInstanceProfile: Template.ref(instance_profile),
284
+ AvailabilityZone: Template.join(
285
+ Template.ref('AWS::Region'),
286
+ availability_zone
287
+ ),
288
+ ImageId: Template.find_in_map(
289
+ Template::REGION_MAP,
290
+ Template.ref('AWS::Region'),
291
+ 'ami'
292
+ ),
293
+ SourceDestCheck: source_dest_check,
294
+ InstanceType: instance_type,
295
+ KeyName: key_name,
296
+ SubnetId: Template.ref(subnet),
297
+ SecurityGroupIds: [Template.ref(security_group)],
298
+ Tags: [{
299
+ Key: 'Name',
300
+ Value: name
301
+ }],
302
+ UserData: Template.base_64(
303
+ Template.join(
304
+ # rubocop:disable Metrics/LineLength
305
+ "#!/bin/bash -v\n",
306
+ "function error_exit\n",
307
+ "{\n",
308
+ " cfn-signal -e 1 -r \"$1\" '", Template.ref(wait_condition_handle), "'\n",
309
+ " exit 1\n",
310
+ "}\n",
311
+ "apt-get -y update\n",
312
+ "apt-get -y install python-setuptools\n",
313
+ "easy_install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz\n",
314
+ "export PATH=$PATH:/opt/aws/bin\n",
315
+ 'cfn-init --region ', Template.ref('AWS::Region'),
316
+ ' -v -s ', Template.ref('AWS::StackName'), " -r #{logical_id} ",
317
+ " || error_exit 'Failed to run cfn-init'\n",
318
+ "for file in /tmp/formatron/script-*.sh; do\n",
319
+ " $file || error_exit \"failed to run Formatron setup script: $file\"\n",
320
+ "done\n",
321
+ "# If all went well, signal success\n",
322
+ "cfn-signal -e $? -r 'Formatron instance configuration complete' '", Template.ref(wait_condition_handle), "'\n"
323
+ # rubocop:enable Metrics/LineLength
324
+ )
325
+ )
326
+ }
327
+ }
328
+ end
329
+ # rubocop:enable Metrics/AbcSize
330
+ # rubocop:enable Metrics/ParameterLists
331
+ # rubocop:enable Metrics/MethodLength
332
+ end
333
+ # rubocop:enable Metrics/ModuleLength
334
+ end
335
+ end
336
+ end