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,169 @@
1
+ require 'formatron/util/shell'
2
+ require 'English'
3
+ require 'json'
4
+
5
+ class Formatron
6
+ class Chef
7
+ # Wrapper for the knife cli
8
+ # rubocop:disable Metrics/ClassLength
9
+ class Knife
10
+ # rubocop:disable Metrics/MethodLength
11
+ # rubocop:disable Metrics/ParameterLists
12
+ def initialize(
13
+ keys:,
14
+ chef_server_url:,
15
+ username:,
16
+ organization:,
17
+ ssl_verify:,
18
+ name:,
19
+ databag_secret:,
20
+ configuration:
21
+ )
22
+ @keys = keys
23
+ @chef_server_url = chef_server_url
24
+ @username = username
25
+ @organization = organization
26
+ @ssl_verify = ssl_verify
27
+ @name = name
28
+ @databag_secret = databag_secret
29
+ @configuration = configuration
30
+ end
31
+ # rubocop:enable Metrics/ParameterLists
32
+ # rubocop:enable Metrics/MethodLength
33
+
34
+ # rubocop:disable Metrics/MethodLength
35
+ def init
36
+ @knife_file = Tempfile.new 'formatron-knife-'
37
+ @knife_file.write <<-EOH.gsub(/^ {10}/, '')
38
+ chef_server_url '#{@chef_server_url}'
39
+ validation_client_name '#{@organization}-validator'
40
+ validation_key '#{@keys.organization_key}'
41
+ node_name '#{@username}'
42
+ client_key '#{@keys.user_key}'
43
+ verify_api_cert #{@ssl_verify}
44
+ ssl_verify_mode #{@ssl_verify ? ':verify_peer' : ':verify_none'}
45
+ EOH
46
+ @knife_file.close
47
+ @databag_secret_file = Tempfile.new 'formatron-databag-secret-'
48
+ @databag_secret_file.write @databag_secret
49
+ @databag_secret_file.close
50
+ @databag_file = Tempfile.new ['formatron-databag-', '.json']
51
+ @databag_file.write @configuration.merge(id: @name).to_json
52
+ @databag_file.close
53
+ end
54
+ # rubocop:enable Metrics/MethodLength
55
+
56
+ def deploy_databag
57
+ _attempt_to_create_databag unless _databag_exists
58
+ _attempt_to_create_databag_item
59
+ end
60
+
61
+ def _databag_exists
62
+ Util::Shell.exec "knife data bag show formatron -c #{@knife_file.path}"
63
+ end
64
+
65
+ def _attempt_to_create_databag
66
+ fail 'failed to create data bag: formatron' unless _create_databag
67
+ end
68
+
69
+ def _create_databag
70
+ # rubocop:disable Metrics/LineLength
71
+ Util::Shell.exec "knife data bag create formatron -c #{@knife_file.path}"
72
+ # rubocop:enable Metrics/LineLength
73
+ end
74
+
75
+ def _attempt_to_create_databag_item
76
+ # rubocop:disable Metrics/LineLength
77
+ fail "failed to create data bag item: #{@name}" unless _create_databag_item
78
+ # rubocop:enable Metrics/LineLength
79
+ end
80
+
81
+ def _create_databag_item
82
+ # rubocop:disable Metrics/LineLength
83
+ Util::Shell.exec "knife data bag from file formatron #{@databag_file.path} --secret-file #{@databag_secret_file.path} -c #{@knife_file.path}"
84
+ # rubocop:enable Metrics/LineLength
85
+ end
86
+
87
+ def create_environment(environment:)
88
+ # rubocop:disable Metrics/LineLength
89
+ _attempt_to_create_environment environment unless _environment_exists environment
90
+ # rubocop:enable Metrics/LineLength
91
+ end
92
+
93
+ def _environment_exists(environment)
94
+ # rubocop:disable Metrics/LineLength
95
+ Util::Shell.exec "knife environment show #{environment} -c #{@knife_file.path}"
96
+ # rubocop:enable Metrics/LineLength
97
+ end
98
+
99
+ def _attempt_to_create_environment(environment)
100
+ # rubocop:disable Metrics/LineLength
101
+ fail "failed to create opscode environment: #{environment}" unless _create_environment environment
102
+ # rubocop:enable Metrics/LineLength
103
+ end
104
+
105
+ def _create_environment(environment)
106
+ # rubocop:disable Metrics/LineLength
107
+ Util::Shell.exec "knife environment create #{environment} -c #{@knife_file.path} -d '#{environment} environment created by formatron'"
108
+ # rubocop:enable Metrics/LineLength
109
+ end
110
+
111
+ def bootstrap(
112
+ environment:,
113
+ bastion_hostname:,
114
+ cookbook:,
115
+ hostname:
116
+ )
117
+ # rubocop:disable Metrics/LineLength
118
+ command = "knife bootstrap #{hostname} --sudo -x ubuntu -i #{@keys.ec2_key} -E #{environment} -r #{cookbook} -N #{environment} -c #{@knife_file.path}#{@ssl_verify ? '' : ' --node-ssl-verify-mode none'} --secret-file #{@databag_secret_file.path}"
119
+ command = "#{command} -G ubuntu@#{bastion_hostname}" unless bastion_hostname.eql? hostname
120
+ fail "failed to bootstrap instance: #{hostname}" unless Util::Shell.exec command
121
+ # rubocop:enable Metrics/LineLength
122
+ end
123
+
124
+ def delete_databag
125
+ # rubocop:disable Metrics/LineLength
126
+ command = "knife data bag delete formatron #{@name} -y -c #{@knife_file.path}"
127
+ fail "failed to delete data bag item: #{@name}" unless Util::Shell.exec command
128
+ # rubocop:enable Metrics/LineLength
129
+ end
130
+
131
+ def delete_node(node:)
132
+ command = "knife node delete #{node} -y -c #{@knife_file.path}"
133
+ fail "failed to delete node: #{node}" unless Util::Shell.exec command
134
+ end
135
+
136
+ def delete_client(client:)
137
+ # rubocop:disable Metrics/LineLength
138
+ command = "knife client delete #{client} -y -c #{@knife_file.path}"
139
+ fail "failed to delete client: #{client}" unless Util::Shell.exec command
140
+ # rubocop:enable Metrics/LineLength
141
+ end
142
+
143
+ def delete_environment(environment:)
144
+ # rubocop:disable Metrics/LineLength
145
+ command = "knife environment delete #{environment} -y -c #{@knife_file.path}"
146
+ fail "failed to delete environment: #{environment}" unless Util::Shell.exec command
147
+ # rubocop:enable Metrics/LineLength
148
+ end
149
+
150
+ def unlink
151
+ @knife_file.unlink unless @knife_file.nil?
152
+ @databag_secret_file.unlink unless @databag_secret_file.nil?
153
+ @databag_file.unlink unless @databag_file.nil?
154
+ end
155
+
156
+ private(
157
+ :_create_databag,
158
+ :_create_databag_item,
159
+ :_attempt_to_create_databag,
160
+ :_attempt_to_create_databag_item,
161
+ :_databag_exists,
162
+ :_create_environment,
163
+ :_attempt_to_create_environment,
164
+ :_environment_exists
165
+ )
166
+ end
167
+ # rubocop:enable Metrics/ClassLength
168
+ end
169
+ end
@@ -0,0 +1,73 @@
1
+ class Formatron
2
+ # creates chef clients
3
+ class ChefClients
4
+ # rubocop:disable Metrics/ParameterLists
5
+ # rubocop:disable Metrics/AbcSize
6
+ # rubocop:disable Metrics/MethodLength
7
+ def initialize(
8
+ aws:,
9
+ bucket:,
10
+ name:,
11
+ target:,
12
+ ec2_key:,
13
+ hosted_zone_name:,
14
+ vpc:,
15
+ external:,
16
+ configuration:,
17
+ databag_secret:
18
+ )
19
+ @chef_clients = {}
20
+ if external.nil?
21
+ bastions = Util::VPC.instances :bastion, vpc
22
+ chef_servers = Util::VPC.instances :chef_server, vpc
23
+ else
24
+ bastions = Util::VPC.instances :bastion, external, vpc
25
+ chef_servers = Util::VPC.instances :chef_server, external, vpc
26
+ end
27
+ bastions = Hash[bastions.map { |k, v| [k, v.sub_domain] }]
28
+ chef_servers.each do |key, chef_server|
29
+ @chef_clients[key] = Chef.new(
30
+ aws: aws,
31
+ bucket: bucket,
32
+ name: name,
33
+ target: target,
34
+ username: chef_server.username,
35
+ organization: chef_server.organization.short_name,
36
+ ssl_verify: chef_server.ssl_verify,
37
+ chef_sub_domain: chef_server.sub_domain,
38
+ ec2_key: ec2_key,
39
+ bastions: bastions,
40
+ hosted_zone_name: hosted_zone_name,
41
+ server_stack: chef_server.stack || name,
42
+ guid: chef_server.guid,
43
+ configuration: configuration,
44
+ databag_secret: databag_secret
45
+ )
46
+ end
47
+ end
48
+ # rubocop:enable Metrics/MethodLength
49
+ # rubocop:enable Metrics/AbcSize
50
+ # rubocop:enable Metrics/ParameterLists
51
+
52
+ def get(key = nil)
53
+ key ||= @chef_clients.keys[0]
54
+ @chef_clients[key]
55
+ end
56
+
57
+ def init
58
+ @chef_clients.values.each(&:init)
59
+ end
60
+
61
+ def unlink
62
+ @chef_clients.values.each(&:unlink)
63
+ end
64
+
65
+ def deploy_databags
66
+ @chef_clients.values.each(&:deploy_databag)
67
+ end
68
+
69
+ def delete_databags
70
+ @chef_clients.values.each(&:delete_databag)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,33 @@
1
+ require 'commander'
2
+ require 'formatron/version'
3
+
4
+ class Formatron
5
+ # CLI interface
6
+ class CLI
7
+ include Commander::Methods
8
+
9
+ def global_options
10
+ global_option '-c', '--credentials FILE', 'The credentials file'
11
+ global_option(
12
+ '-d',
13
+ '--directory DIRECTORY',
14
+ 'The Formatron configuration directory'
15
+ )
16
+ end
17
+
18
+ def commands
19
+ self.class.instance_methods.each do |method|
20
+ send(method) if method =~ /_formatron_command$/
21
+ end
22
+ end
23
+
24
+ def run
25
+ program :version, Formatron::VERSION
26
+ program :description, 'Quickly deploy AWS CloudFormation ' \
27
+ 'stacks backed by a Chef Server'
28
+ global_options
29
+ commands
30
+ run!
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,26 @@
1
+ require 'formatron/completion'
2
+
3
+ class Formatron
4
+ class CLI
5
+ # CLI command for completion enabling script
6
+ module Completion
7
+ def completion_script_action(c)
8
+ c.action do |args|
9
+ command = args[0] || 'formatron'
10
+ print Formatron::Completion.script command, defined_commands.keys
11
+ end
12
+ end
13
+
14
+ def completion_script_formatron_command
15
+ command :'completion-script' do |c|
16
+ c.syntax = 'formatron completion-script [COMMAND]'
17
+ c.summary = 'Output a bash script to ' \
18
+ 'enable command completion'
19
+ c.description = 'Output a bash script to ' \
20
+ 'enable command completion'
21
+ completion_script_action c
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,57 @@
1
+ require 'formatron/config'
2
+ require 'formatron'
3
+
4
+ class Formatron
5
+ class CLI
6
+ # CLI command for deploy
7
+ module Deploy
8
+ def deploy_directory(options)
9
+ options.directory || Dir.pwd
10
+ end
11
+
12
+ def deploy_credentials(options)
13
+ options.credentials ||
14
+ Generators::Credentials.default_credentials(
15
+ deploy_directory(options)
16
+ )
17
+ end
18
+
19
+ def deploy_target(target, directory)
20
+ target || choose(
21
+ 'Target?',
22
+ *Config.targets(directory: directory)
23
+ )
24
+ end
25
+
26
+ def deploy_ok(formatron, target)
27
+ !formatron.protected? || agree(
28
+ "Are you sure you wish to deploy protected target: #{target}?"
29
+ ) do |q|
30
+ q.default = 'no'
31
+ end
32
+ end
33
+
34
+ def deploy_action(c)
35
+ c.action do |args, options|
36
+ directory = deploy_directory options
37
+ target = deploy_target args[0], directory
38
+ formatron = Formatron.new(
39
+ credentials: deploy_credentials(options),
40
+ directory: directory,
41
+ target: target
42
+ )
43
+ formatron.deploy if deploy_ok(formatron, target)
44
+ end
45
+ end
46
+
47
+ def deploy_formatron_command
48
+ command :deploy do |c|
49
+ c.syntax = 'formatron deploy [options] [TARGET]'
50
+ c.summary = 'Deploy or update a Formatron stack'
51
+ c.description = 'Deploy or update a Formatron stack'
52
+ deploy_action c
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,57 @@
1
+ require 'formatron'
2
+ require 'formatron/config'
3
+
4
+ class Formatron
5
+ class CLI
6
+ # CLI command for destroy
7
+ module Destroy
8
+ def destroy_directory(options)
9
+ options.directory || Dir.pwd
10
+ end
11
+
12
+ def destroy_credentials(options)
13
+ options.credentials ||
14
+ Generators::Credentials.default_credentials(
15
+ destroy_directory(options)
16
+ )
17
+ end
18
+
19
+ def destroy_target(target, directory)
20
+ target || choose(
21
+ 'Target?',
22
+ *Config.targets(directory: directory)
23
+ )
24
+ end
25
+
26
+ def destroy_ok(formatron, target)
27
+ !formatron.protected? || agree(
28
+ "Are you sure you wish to destroy protected target: #{target}?"
29
+ ) do |q|
30
+ q.default = 'no'
31
+ end
32
+ end
33
+
34
+ def destroy_action(c)
35
+ c.action do |args, options|
36
+ directory = destroy_directory options
37
+ target = destroy_target args[0], directory
38
+ formatron = Formatron.new(
39
+ credentials: destroy_credentials(options),
40
+ directory: directory,
41
+ target: target
42
+ )
43
+ formatron.destroy if destroy_ok formatron, target
44
+ end
45
+ end
46
+
47
+ def destroy_formatron_command
48
+ command :destroy do |c|
49
+ c.syntax = 'formatron destroy [options] [TARGET]'
50
+ c.summary = 'Destroy a Formatron stack'
51
+ c.description = 'Destroy a Formatron stack'
52
+ destroy_action c
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,250 @@
1
+ require 'formatron/generators/bootstrap'
2
+
3
+ class Formatron
4
+ class CLI
5
+ module Generators
6
+ # CLI command for bootstrap generator
7
+ # rubocop:disable Metrics/ModuleLength
8
+ module Bootstrap
9
+ # rubocop:disable Metrics/MethodLength
10
+ # rubocop:disable Metrics/AbcSize
11
+ def bootstrap_options(c)
12
+ c.option '-n', '--name STRING', 'The name for the configuration'
13
+ c.option(
14
+ '-s',
15
+ '--s3-bucket STRING',
16
+ 'The S3 bucket to store encrypted configuration'
17
+ )
18
+ c.option(
19
+ '-k',
20
+ '--kms-key STRING',
21
+ 'The KMS key to use for encryption'
22
+ )
23
+ c.option(
24
+ '-e',
25
+ '--ec2-key-pair STRING',
26
+ 'The EC2 key pair to associate with EC2 instances'
27
+ )
28
+ c.option(
29
+ '-z',
30
+ '--hosted-zone-id STRING',
31
+ 'The Route53 Hosted Zone ID for the public hosted zone'
32
+ )
33
+ c.option(
34
+ '-a',
35
+ '--availability-zone STRING',
36
+ 'The AWS availability zone letter (region is already taken ' \
37
+ 'from the AWS credentials)'
38
+ )
39
+ c.option(
40
+ '-b',
41
+ '--cookbooks-bucket-prefix STRING',
42
+ 'Used to generate target specific S3 bucket names ' \
43
+ '(PREFIX-TARGET) for the Chef Server to store its ' \
44
+ 'cookbook library'
45
+ )
46
+ c.option(
47
+ '-o',
48
+ '--organization-short-name STRING',
49
+ 'The short name of the organization to create on the ' \
50
+ 'Chef Server (should not contain spaces, etc)'
51
+ )
52
+ c.option(
53
+ '-w',
54
+ '--organization-full-name STRING',
55
+ 'The full name of the organization to create on the Chef Server'
56
+ )
57
+ c.option(
58
+ '-u',
59
+ '--username STRING',
60
+ 'The username to create on the Chef Server'
61
+ )
62
+ c.option(
63
+ '-p',
64
+ '--password STRING',
65
+ 'The password for the Chef Server user'
66
+ )
67
+ c.option(
68
+ '-m',
69
+ '--email STRING',
70
+ 'The email address for the Chef Server user'
71
+ )
72
+ c.option(
73
+ '-f',
74
+ '--first-name STRING',
75
+ 'The first name of the Chef Server user'
76
+ )
77
+ c.option(
78
+ '-l',
79
+ '--last-name STRING',
80
+ 'The last name of the Chef Server user'
81
+ )
82
+ c.option(
83
+ '-i',
84
+ '--instance-cookbook STRING',
85
+ 'The instance cookbook to apply additional ' \
86
+ 'configuration to the Chef Server'
87
+ )
88
+ c.option(
89
+ '-x',
90
+ '--protected-targets LIST',
91
+ Array,
92
+ 'The protected targets (eg. production)'
93
+ )
94
+ c.option(
95
+ '-y',
96
+ '--unprotected-targets LIST',
97
+ Array,
98
+ 'The unprotected targets (eg. test)'
99
+ )
100
+ end
101
+ # rubocop:enable Metrics/AbcSize
102
+ # rubocop:enable Metrics/MethodLength
103
+
104
+ def bootstrap_directory(options)
105
+ options.directory || ask('Directory? ') do |q|
106
+ q.default = Dir.pwd
107
+ end
108
+ end
109
+
110
+ def bootstrap_name(options, directory)
111
+ options.name || ask('Name? ') do |q|
112
+ q.default = File.basename directory
113
+ end
114
+ end
115
+
116
+ def bootstrap_s3_bucket(options)
117
+ options.s3_bucket || ask('S3 Bucket? ')
118
+ end
119
+
120
+ def bootstrap_kms_key(options)
121
+ options.kms_key || ask('KMS Key? ')
122
+ end
123
+
124
+ def bootstrap_ec2_key_pair(options)
125
+ options.ec2_key_pair || ask('EC2 Key Pair? ')
126
+ end
127
+
128
+ def bootstrap_hosted_zone_id(options)
129
+ options.hosted_zone_id || ask('Hosted Zone ID? ')
130
+ end
131
+
132
+ def bootstrap_availability_zone(options)
133
+ options.availability_zone || ask('Availability Zone? ')
134
+ end
135
+
136
+ def bootstrap_cookbooks_bucket_prefix(options)
137
+ options.cookbooks_bucket_prefix || ask(
138
+ 'Chef Server Cookbooks Bucket Prefix? '
139
+ ) do |q|
140
+ q.default = "#{options.s3_bucket}-cookbooks"
141
+ end
142
+ end
143
+
144
+ def bootstrap_organization_short_name(options)
145
+ options.organization_short_name ||
146
+ ask('Chef Server Organization Short Name? ')
147
+ end
148
+
149
+ def bootstrap_organization_full_name(options)
150
+ options.organization_full_name ||
151
+ ask('Chef Server Organization Full Name? ')
152
+ end
153
+
154
+ def bootstrap_username(options)
155
+ options.username || ask('Chef Server Username? ')
156
+ end
157
+
158
+ def bootstrap_password(options)
159
+ options.password || password('Chef Server Password? ')
160
+ end
161
+
162
+ def bootstrap_email(options)
163
+ options.email || ask('Chef Server User Email? ')
164
+ end
165
+
166
+ def bootstrap_first_name(options)
167
+ options.first_name || ask('Chef Server User First Name? ')
168
+ end
169
+
170
+ def bootstrap_last_name(options)
171
+ options.last_name || ask('Chef Server User Last Name? ')
172
+ end
173
+
174
+ def bootstrap_protected_targets(options)
175
+ options.protected_targets || ask('Protected Targets? ', Array) do |q|
176
+ q.default = 'production'
177
+ end
178
+ end
179
+
180
+ def bootstrap_unprotected_targets(options)
181
+ options.unprotected_targets ||
182
+ ask('Unprotected Targets? ', Array) do |q|
183
+ q.default = 'test'
184
+ end
185
+ end
186
+
187
+ def bootstrap_targets(options)
188
+ protected_targets = bootstrap_protected_targets options
189
+ unprotected_targets = bootstrap_unprotected_targets options
190
+ targets = {}
191
+ protected_targets.each do |target|
192
+ targets[target.to_sym] = { protect: true }
193
+ end
194
+ unprotected_targets.each do |target|
195
+ targets[target.to_sym] = { protect: false }
196
+ end
197
+ targets
198
+ end
199
+
200
+ # rubocop:disable Metrics/MethodLength
201
+ def bootstrap_params(options, directory)
202
+ {
203
+ name: bootstrap_name(options, directory),
204
+ s3_bucket: bootstrap_s3_bucket(options),
205
+ kms_key: bootstrap_kms_key(options),
206
+ ec2_key_pair: bootstrap_ec2_key_pair(options),
207
+ hosted_zone_id: bootstrap_hosted_zone_id(options),
208
+ availability_zone: bootstrap_availability_zone(options),
209
+ chef_server: {
210
+ cookbooks_bucket_prefix:
211
+ bootstrap_cookbooks_bucket_prefix(options),
212
+ organization: {
213
+ short_name: bootstrap_organization_short_name(options),
214
+ full_name: bootstrap_organization_full_name(options)
215
+ },
216
+ username: bootstrap_username(options),
217
+ password: bootstrap_password(options),
218
+ email: bootstrap_email(options),
219
+ first_name: bootstrap_first_name(options),
220
+ last_name: bootstrap_last_name(options)
221
+ },
222
+ targets: bootstrap_targets(options)
223
+ }
224
+ end
225
+ # rubocop:enable Metrics/MethodLength
226
+
227
+ def bootstrap_action(c)
228
+ c.action do |_args, options|
229
+ directory = bootstrap_directory options
230
+ Formatron::Generators::Bootstrap.generate(
231
+ directory,
232
+ bootstrap_params(options, directory)
233
+ )
234
+ end
235
+ end
236
+
237
+ def bootstrap_formatron_command
238
+ command :'generate bootstrap' do |c|
239
+ c.syntax = 'formatron generate bootstrap [options]'
240
+ c.summary = 'Generate a bootstrap configuration'
241
+ c.description = 'Generate a bootstrap configuration'
242
+ bootstrap_options c
243
+ bootstrap_action c
244
+ end
245
+ end
246
+ end
247
+ # rubocop:enable Metrics/ModuleLength
248
+ end
249
+ end
250
+ end