formatron 0.1.0

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