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