wombat-cli 0.4.1 → 0.4.2

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 (198) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +23 -23
  3. data/.travis.yml +27 -27
  4. data/CHANGELOG.md +77 -70
  5. data/DESIGN.md +49 -49
  6. data/Gemfile +5 -5
  7. data/README.md +146 -146
  8. data/Rakefile +26 -26
  9. data/bin/wombat +24 -24
  10. data/generator_files/Vagrantfile +120 -120
  11. data/generator_files/cookbooks/automate/.gitignore +16 -16
  12. data/generator_files/cookbooks/automate/.kitchen.ec2.yml +34 -34
  13. data/generator_files/cookbooks/automate/.kitchen.yml +24 -24
  14. data/generator_files/cookbooks/automate/Berksfile +6 -6
  15. data/generator_files/cookbooks/automate/README.md +4 -4
  16. data/generator_files/cookbooks/automate/chefignore +102 -102
  17. data/generator_files/cookbooks/automate/libraries/_helper.rb +52 -52
  18. data/generator_files/cookbooks/automate/libraries/delivery_api.rb +204 -204
  19. data/generator_files/cookbooks/automate/libraries/delivery_project.rb +31 -31
  20. data/generator_files/cookbooks/automate/libraries/dsl.rb +4 -4
  21. data/generator_files/cookbooks/automate/metadata.rb +11 -11
  22. data/generator_files/cookbooks/automate/recipes/default.rb +112 -112
  23. data/generator_files/cookbooks/automate/recipes/update-users.rb +48 -48
  24. data/generator_files/cookbooks/automate/templates/delivery.erb +6 -6
  25. data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/files/automate.crt +26 -26
  26. data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/files/automate.key +27 -27
  27. data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/files/chef.crt +25 -25
  28. data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/files/chef.key +27 -27
  29. data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/files/compliance.crt +26 -26
  30. data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/files/compliance.key +27 -27
  31. data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/files/private.pem +27 -27
  32. data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/files/public.pub +1 -1
  33. data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/metadata.rb +3 -3
  34. data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/recipes/default.rb +27 -27
  35. data/generator_files/cookbooks/automate/test/integration/default/automate_spec.rb +56 -56
  36. data/generator_files/cookbooks/build_node/.gitignore +16 -16
  37. data/generator_files/cookbooks/build_node/.kitchen.ec2.yml +37 -37
  38. data/generator_files/cookbooks/build_node/.kitchen.yml +23 -23
  39. data/generator_files/cookbooks/build_node/Berksfile +8 -8
  40. data/generator_files/cookbooks/build_node/README.md +4 -4
  41. data/generator_files/cookbooks/build_node/chefignore +102 -102
  42. data/generator_files/cookbooks/build_node/metadata.rb +12 -12
  43. data/generator_files/cookbooks/build_node/recipes/default.rb +38 -38
  44. data/generator_files/cookbooks/build_node/templates/client.erb +2 -2
  45. data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/files/automate.crt +26 -26
  46. data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/files/automate.key +27 -27
  47. data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/files/chef.crt +25 -25
  48. data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/files/chef.key +27 -27
  49. data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/files/compliance.crt +26 -26
  50. data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/files/compliance.key +27 -27
  51. data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/files/private.pem +27 -27
  52. data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/files/public.pub +1 -1
  53. data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/metadata.rb +2 -2
  54. data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/recipes/default.rb +18 -18
  55. data/generator_files/cookbooks/build_node/test/integration/default/build-node_spec.rb +40 -40
  56. data/generator_files/cookbooks/chef_server/.gitignore +16 -16
  57. data/generator_files/cookbooks/chef_server/.kitchen.ec2.yml +34 -34
  58. data/generator_files/cookbooks/chef_server/.kitchen.yml +24 -24
  59. data/generator_files/cookbooks/chef_server/Berksfile +6 -6
  60. data/generator_files/cookbooks/chef_server/README.md +4 -4
  61. data/generator_files/cookbooks/chef_server/chefignore +102 -102
  62. data/generator_files/cookbooks/chef_server/metadata.rb +11 -11
  63. data/generator_files/cookbooks/chef_server/recipes/bootstrap_users.rb +91 -91
  64. data/generator_files/cookbooks/chef_server/recipes/default.rb +113 -113
  65. data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/files/automate.crt +26 -26
  66. data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/files/automate.key +27 -27
  67. data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/files/chef.crt +25 -25
  68. data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/files/chef.key +27 -27
  69. data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/files/compliance.crt +26 -26
  70. data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/files/compliance.key +27 -27
  71. data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/files/private.pem +27 -27
  72. data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/files/public.pub +1 -1
  73. data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/metadata.rb +2 -2
  74. data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/recipes/default.rb +23 -23
  75. data/generator_files/cookbooks/chef_server/test/integration/default/chef_server_spec.rb +50 -50
  76. data/generator_files/cookbooks/compliance/.gitignore +16 -16
  77. data/generator_files/cookbooks/compliance/.kitchen.ec2.yml +34 -34
  78. data/generator_files/cookbooks/compliance/.kitchen.yml +24 -24
  79. data/generator_files/cookbooks/compliance/Berksfile +7 -7
  80. data/generator_files/cookbooks/compliance/README.md +4 -4
  81. data/generator_files/cookbooks/compliance/chefignore +102 -102
  82. data/generator_files/cookbooks/compliance/metadata.rb +11 -11
  83. data/generator_files/cookbooks/compliance/recipes/default.rb +57 -57
  84. data/generator_files/cookbooks/compliance/spec/spec_helper.rb +2 -2
  85. data/generator_files/cookbooks/compliance/spec/unit/recipes/default_spec.rb +20 -20
  86. data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/files/automate.crt +26 -26
  87. data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/files/automate.key +27 -27
  88. data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/files/chef.crt +25 -25
  89. data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/files/chef.key +27 -27
  90. data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/files/compliance.crt +26 -26
  91. data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/files/compliance.key +27 -27
  92. data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/files/private.pem +27 -27
  93. data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/files/public.pub +1 -1
  94. data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/metadata.rb +4 -4
  95. data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/recipes/default.rb +21 -21
  96. data/generator_files/cookbooks/compliance/test/integration/default/compliance.rb +28 -28
  97. data/generator_files/cookbooks/infranodes/.gitignore +16 -16
  98. data/generator_files/cookbooks/infranodes/.kitchen.ec2.yml +48 -48
  99. data/generator_files/cookbooks/infranodes/.kitchen.yml +21 -21
  100. data/generator_files/cookbooks/infranodes/Berksfile +6 -6
  101. data/generator_files/cookbooks/infranodes/README.md +4 -4
  102. data/generator_files/cookbooks/infranodes/attributes/default.rb +2 -2
  103. data/generator_files/cookbooks/infranodes/chefignore +102 -102
  104. data/generator_files/cookbooks/infranodes/metadata.rb +13 -13
  105. data/generator_files/cookbooks/infranodes/recipes/default.rb +57 -57
  106. data/generator_files/cookbooks/infranodes/spec/spec_helper.rb +2 -2
  107. data/generator_files/cookbooks/infranodes/spec/unit/recipes/default_spec.rb +20 -20
  108. data/generator_files/cookbooks/infranodes/templates/default/client.rb.erb +5 -5
  109. data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/files/automate.crt +26 -26
  110. data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/files/automate.key +27 -27
  111. data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/files/chef.crt +25 -25
  112. data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/files/chef.key +27 -27
  113. data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/files/compliance.crt +26 -26
  114. data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/files/compliance.key +27 -27
  115. data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/files/private.pem +27 -27
  116. data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/files/public.pub +1 -1
  117. data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/metadata.rb +3 -3
  118. data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/recipes/default.rb +27 -27
  119. data/generator_files/cookbooks/infranodes/test/integration/default/infranodes_spec.rb +22 -22
  120. data/generator_files/cookbooks/infranodes/test/integration/helpers/serverspec/spec_helper.rb +8 -8
  121. data/generator_files/cookbooks/wombat/.gitignore +16 -16
  122. data/generator_files/cookbooks/wombat/.kitchen.yml +43 -43
  123. data/generator_files/cookbooks/wombat/Berksfile +5 -5
  124. data/generator_files/cookbooks/wombat/README.md +4 -4
  125. data/generator_files/cookbooks/wombat/attributes/default.rb +74 -74
  126. data/generator_files/cookbooks/wombat/attributes/packer.rb +18 -18
  127. data/generator_files/cookbooks/wombat/chefignore +102 -102
  128. data/generator_files/cookbooks/wombat/metadata.rb +13 -13
  129. data/generator_files/cookbooks/wombat/recipes/authorized-keys.rb +20 -20
  130. data/generator_files/cookbooks/wombat/recipes/default.rb +111 -111
  131. data/generator_files/cookbooks/wombat/recipes/etc-hosts.rb +51 -51
  132. data/generator_files/cookbooks/workstation/.gitignore +16 -16
  133. data/generator_files/cookbooks/workstation/.kitchen.ec2.yml +30 -30
  134. data/generator_files/cookbooks/workstation/.kitchen.yml +22 -22
  135. data/generator_files/cookbooks/workstation/Berksfile +7 -7
  136. data/generator_files/cookbooks/workstation/README.md +3 -3
  137. data/generator_files/cookbooks/workstation/chefignore +102 -102
  138. data/generator_files/cookbooks/workstation/files/atom.apm.list +7 -7
  139. data/generator_files/cookbooks/workstation/files/atom.config.cson +3 -3
  140. data/generator_files/cookbooks/workstation/metadata.rb +14 -14
  141. data/generator_files/cookbooks/workstation/recipes/browser.rb +59 -59
  142. data/generator_files/cookbooks/workstation/recipes/certs-keys.rb +45 -45
  143. data/generator_files/cookbooks/workstation/recipes/chef.rb +29 -29
  144. data/generator_files/cookbooks/workstation/recipes/default.rb +20 -20
  145. data/generator_files/cookbooks/workstation/recipes/dotnet.rb +17 -17
  146. data/generator_files/cookbooks/workstation/recipes/editor.rb +19 -19
  147. data/generator_files/cookbooks/workstation/recipes/profile.rb +42 -42
  148. data/generator_files/cookbooks/workstation/recipes/terminal.rb +13 -13
  149. data/generator_files/cookbooks/workstation/templates/default/bookmarks.html.erb +23 -23
  150. data/generator_files/cookbooks/workstation/templates/default/data_collector.rb.erb +2 -2
  151. data/generator_files/cookbooks/workstation/templates/default/ise_profile.ps1.erb +11 -11
  152. data/generator_files/cookbooks/workstation/templates/default/knife.rb.erb +10 -10
  153. data/generator_files/cookbooks/workstation/templates/default/master_preferences.json.erb +28 -28
  154. data/generator_files/cookbooks/workstation/templates/default/ssh_config.erb +16 -16
  155. data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/automate.crt +26 -26
  156. data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/automate.key +27 -27
  157. data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/chef.crt +26 -26
  158. data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/chef.key +27 -27
  159. data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/compliance.crt +26 -26
  160. data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/compliance.key +27 -27
  161. data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/private.pem +27 -27
  162. data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/public.pub +1 -1
  163. data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/metadata.rb +2 -2
  164. data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/recipes/default.rb +21 -21
  165. data/generator_files/cookbooks/workstation/test/integration/default/workstation_spec.rb +37 -37
  166. data/generator_files/packer/automate.json +131 -131
  167. data/generator_files/packer/build-node.json +137 -137
  168. data/generator_files/packer/chef-server.json +132 -132
  169. data/generator_files/packer/compliance.json +128 -128
  170. data/generator_files/packer/infranodes-windows.json +138 -138
  171. data/generator_files/packer/infranodes.json +129 -129
  172. data/generator_files/packer/workstation.json +136 -136
  173. data/generator_files/templates/arm.json.erb +575 -575
  174. data/generator_files/templates/arm.tidy.json.erb +31 -31
  175. data/generator_files/templates/bootstrap-aws.erb +36 -36
  176. data/generator_files/templates/cfn.json.erb +674 -674
  177. data/generator_files/wombat.yml +71 -71
  178. data/lib/wombat/aws.rb +67 -67
  179. data/lib/wombat/build.rb +338 -338
  180. data/lib/wombat/cli.rb +238 -238
  181. data/lib/wombat/common.rb +347 -347
  182. data/lib/wombat/crypto.rb +64 -64
  183. data/lib/wombat/delete.rb +94 -94
  184. data/lib/wombat/deploy.rb +101 -101
  185. data/lib/wombat/init.rb +32 -32
  186. data/lib/wombat/latest.rb +27 -27
  187. data/lib/wombat/output.rb +86 -86
  188. data/lib/wombat/update.rb +20 -20
  189. data/lib/wombat/version.rb +3 -3
  190. data/lib/wombat.rb +8 -8
  191. data/spec/functional/common_spec.rb +26 -26
  192. data/spec/spec_helper.rb +103 -103
  193. data/spec/unit/common_spec.rb +116 -116
  194. data/terraform/README.md +13 -13
  195. data/terraform/templates/terraform.tfvars.erb +12 -12
  196. data/terraform/wombat.tf +328 -328
  197. data/wombat-cli.gemspec +34 -34
  198. metadata +2 -2
data/lib/wombat/build.rb CHANGED
@@ -1,338 +1,338 @@
1
-
2
- require 'wombat/common'
3
- require 'wombat/crypto'
4
- require 'mixlib/shellout'
5
- require 'parallel'
6
- require 'ms_rest_azure'
7
- require 'azure_mgmt_resources'
8
- require 'azure_mgmt_storage'
9
-
10
- module Wombat
11
- class BuildRunner
12
- include Wombat::Common
13
- include Wombat::Crypto
14
-
15
- attr_reader :templates, :builder, :parallel
16
-
17
- def initialize(opts)
18
- @templates = opts.templates.nil? ? calculate_templates : opts.templates
19
- @builder = opts.builder.nil? ? "amazon-ebs" : opts.builder
20
- @parallel = opts.parallel
21
- @wombat_yml = opts.wombat_yml unless opts.wombat_yml.nil?
22
- @debug = opts.debug
23
- @no_vendor = opts.vendor
24
- end
25
-
26
- def start
27
- if which('packer').nil?
28
- raise "packer binary not found in path, exiting..."
29
- end
30
- banner("Generating certs (if necessary)")
31
- wombat['certs'].each do |hostname|
32
- gen_x509_cert(hostname)
33
- end
34
- banner("Generating SSH keypair (if necessary)")
35
- gen_ssh_key
36
-
37
- # If running on azure ensure that the resource group and storage account exist
38
- prepare_azure if builder == "azure-arm"
39
-
40
- time = Benchmark.measure do
41
- banner("Starting build for templates: #{templates}")
42
- aws_region_check if builder == 'amazon-ebs'
43
- templates.each do |t|
44
- vendor_cookbooks(t) unless @no_vendor
45
- end
46
-
47
- if parallel.nil?
48
- build_hash.each do |k, v|
49
- build(v['template'], v['options'])
50
- end
51
- else
52
- build_parallel(templates)
53
- end
54
- end
55
- shell_out_command("say -v fred \"Wombat has made an #{build_hash.keys}\" for you") if audio?
56
- banner("Build finished in #{duration(time.real)}.")
57
- end
58
-
59
- private
60
-
61
- def prepare_azure()
62
-
63
- # Ensure that a storage acocunt has been specified, if it has not error
64
- if wombat['azure']['storage_account'].nil?
65
- puts "\nA storage account name must be specified in wombat.yml, e.g.\n openssl rand -base64 12\nEnsure all lowercase and no special characters"
66
- exit
67
- end
68
-
69
- # Using environment variables connect to azure
70
- subscription_id = ENV['AZURE_SUBSCRIPTION_ID']
71
- tenant_id = ENV['AZURE_TENANT_ID']
72
- client_id = ENV['AZURE_CLIENT_ID']
73
- client_secret = ENV['AZURE_CLIENT_SECRET']
74
-
75
- token_provider = MsRestAzure::ApplicationTokenProvider.new(tenant_id, client_id, client_secret)
76
- azure_conn = MsRest::TokenCredentials.new(token_provider)
77
-
78
- # Create a resource to create the resource group if it does not exist
79
- resource_management_client = Azure::ARM::Resources::ResourceManagementClient.new(azure_conn)
80
- resource_management_client.subscription_id = subscription_id
81
-
82
- # Create a storage account client to create the stoarge account if it does not exist
83
- storage_management_client = Azure::ARM::Storage::StorageManagementClient.new(azure_conn)
84
- storage_management_client.subscription_id = subscription_id
85
-
86
- # Check that the resource group exists
87
- banner(format("Checking for resource group: %s", wombat['name']))
88
- status = resource_management_client.resource_groups.check_existence(wombat['name'])
89
- if status
90
- puts "resource group already exists"
91
- else
92
- puts format("creating new resource group in '%s'", wombat['azure']['location'])
93
-
94
- # Set the parameters for the resource group
95
- resource_group = Azure::ARM::Resources::Models::ResourceGroup.new
96
- resource_group.location = wombat['azure']['location']
97
-
98
- # Create hash to be used as tags on the resource group
99
- tags = {
100
- owner: ENV['USER']
101
- }
102
-
103
- # If an owner has been specified in the wombat file override the owner value
104
- if wombat.key?('owner') && !wombat['owner'].nil?
105
- tags[:owner] = wombat['owner']
106
- end
107
-
108
- # Determine if there are any tags specified in the azure wmbat section that need to be added
109
- if wombat['azure'].key?('tags') && wombat['azure']['tags'].length > 0
110
-
111
- # Check to see if there are more than 15 tags in which case output a warning
112
- if wombat['azure']['tags'].length > 14
113
- warn ('More than 15 tags have been specified, only the first 15 will be added. This is a restriction in Azure.')
114
- end
115
-
116
- # Iterate around the tags and add each one to the tags array, up to 15
117
- wombat['azure']['tags'].each_with_index do |(key, value), index|
118
- tags[key] = value
119
-
120
- if index == 13
121
- break
122
- end
123
- end
124
-
125
- end
126
-
127
- # add the tags hash to the parameters
128
- resource_group.tags = tags
129
-
130
- # Create the resource group
131
- resource_management_client.resource_groups.create_or_update(wombat['name'], resource_group)
132
- end
133
-
134
- # Check to see if the storage account already exists
135
- banner(format("Checking for storage account: %s", wombat['azure']['storage_account']))
136
-
137
- # Create the storage account in the resource group
138
- # NOTE: This should have a test to see if the storage account exists and it available however the
139
- # Azure Ruby SDK has an issue with the check_name_availability method and comes back with an error
140
- # This would normally be done through an ARM template, but in this case needs to exist before Packer can run
141
- storage_account = Azure::ARM::Storage::Models::StorageAccountCreateParameters.new
142
- storage_account.location = wombat['azure']['location']
143
- sku = Azure::ARM::Storage::Models::Sku.new
144
- sku.name = 'Standard_LRS'
145
- storage_account.sku = sku
146
- storage_account.kind = Azure::ARM::Storage::Models::Kind::Storage
147
-
148
- storage_management_client.storage_accounts.create(wombat['name'], wombat['azure']['storage_account'], storage_account)
149
-
150
- end
151
-
152
- def build(template, options)
153
- bootstrap_aws if options['os'] == 'windows'
154
- shell_out_command(packer_build_cmd(template, builder, options))
155
- end
156
-
157
- def build_parallel(templates)
158
- Parallel.map(build_hash.keys, in_threads: build_hash.count) do |name|
159
- build(build_hash[name]['template'], build_hash[name]['options'])
160
- end
161
- end
162
-
163
- def build_hash
164
- proc_hash = {}
165
- templates.each do |template_name|
166
- if template_name =~ /infranodes/
167
- infranodes.each do |name, _rl|
168
- next if name.empty?
169
- proc_hash[name] = {
170
- 'template' => template_name,
171
- 'options' => {
172
- 'node-name' => name,
173
- 'os' => wombat['infranodes'][name]['platform']
174
- }
175
- }
176
- end
177
- elsif template_name =~ /build-node/
178
- build_nodes.each do |name, num|
179
- proc_hash[name] = {
180
- 'template' => template_name,
181
- 'options' => {
182
- 'node-number' => num
183
- }
184
- }
185
- end
186
- elsif template_name =~ /workstation/
187
- workstations.each do |name, num|
188
- proc_hash[name] = {
189
- 'template' => template_name,
190
- 'options' => {
191
- 'os' => wombat['workstations']['platform'],
192
- 'workstation-number' => num
193
- }
194
- }
195
- end
196
- else
197
- proc_hash[template_name] = {
198
- 'template' => template_name,
199
- 'options' => {}
200
- }
201
- end
202
- end
203
- proc_hash
204
- end
205
-
206
- def a_to_s(*args)
207
- clean_array(*args).join(" ")
208
- end
209
-
210
- def clean_array(*args)
211
- args.flatten.reject { |i| i.nil? || i == "" }.map(&:to_s)
212
- end
213
-
214
- def b_to_c(builder)
215
- case builder
216
- when 'amazon-ebs'
217
- 'aws'
218
- when 'googlecompute'
219
- 'gce'
220
- when 'azure-arm'
221
- 'azure'
222
- end
223
- end
224
-
225
- def shell_out_command(command)
226
- cmd = Mixlib::ShellOut.new(a_to_s(command), :timeout => conf['timeout'], live_stream: STDOUT)
227
- cmd.run_command
228
- cmd
229
- end
230
-
231
- def aws_region_check
232
- if ENV['AWS_REGION']
233
- banner("Region set by environment: #{ENV['AWS_REGION']}")
234
- else
235
- banner("$AWS_REGION not set, setting to #{wombat['aws']['region']}")
236
- ENV['AWS_REGION'] = wombat['aws']['region']
237
- end
238
- end
239
-
240
- def vendor_cookbooks(template)
241
- banner "Vendoring cookbooks for #{template}"
242
-
243
- if template =~ /.*-windows/
244
- base = template.split('-')[0]
245
- else
246
- base = template.split('.json')[0].tr('-', '_')
247
- end
248
- rm_cmd = "rm -rf #{conf['cookbook_dir']}/#{base}/Berksfile.lock vendored-cookbooks/#{base}"
249
- shell_out_command(rm_cmd)
250
- vendor_cmd = "berks vendor -q -b #{conf['cookbook_dir']}/#{base}/Berksfile vendored-cookbooks/#{base}"
251
- shell_out_command(vendor_cmd)
252
- end
253
-
254
- def log(template, builder, options)
255
- cloud = b_to_c(builder)
256
- case template
257
- when /automate/
258
- log_name = "#{cloud}-automate-#{linux}"
259
- when /chef-server/
260
- log_name = "#{cloud}-chef-server-#{linux}"
261
- when /compliance/
262
- log_name = "#{cloud}-compliance-#{linux}"
263
- when /build-node/
264
- log_name = "#{cloud}-build-node-#{options['node-number']}-#{linux}"
265
- when /workstation/
266
- log_name = "#{cloud}-workstation-#{options['workstation-number']}-#{linux}"
267
- when /infranodes/
268
- if options['os'] =~ /windows/
269
- log_name = "#{cloud}-infranodes-#{options['node-name']}-windows"
270
- else
271
- log_name = "#{cloud}-infranodes-#{options['node-name']}-#{linux}"
272
- end
273
- end
274
- log_file = "#{conf['log_dir']}/#{log_name}.log"
275
- end
276
-
277
- def which(cmd)
278
- exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
279
- ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
280
- exts.each { |ext|
281
- exe = File.join(path, "#{cmd}#{ext}")
282
- return exe if File.executable?(exe) && !File.directory?(exe)
283
- }
284
- end
285
- return nil
286
- end
287
-
288
- def base_image(template, builder, options)
289
- cloud = b_to_c(builder)
290
- if template =~ /workstation/
291
- wombat[cloud]['source_image']['windows']
292
- elsif template =~ /infranodes/
293
- if options['os'] == 'windows'
294
- wombat[cloud]['source_image']['windows']
295
- else
296
- wombat[cloud]['source_image'][linux]
297
- end
298
- else
299
- wombat[cloud]['source_image'][linux]
300
- end
301
- end
302
-
303
- def packer_build_cmd(template, builder, options)
304
- create_infranodes_json
305
- Dir.mkdir(conf['log_dir'], 0755) unless File.exist?(conf['log_dir'])
306
-
307
- cmd = %W(packer build #{conf['packer_dir']}/#{template}.json | tee #{log(template, builder, options)})
308
- cmd.insert(2, "--only #{builder}")
309
- cmd.insert(2, "--var org=#{wombat['org']}")
310
- cmd.insert(2, "--var domain=#{wombat['domain']}")
311
- cmd.insert(2, "--var domain_prefix=#{wombat['domain_prefix']}")
312
- cmd.insert(2, "--var enterprise=#{wombat['enterprise']}")
313
- cmd.insert(2, "--var chefdk=#{wombat['products']['chefdk']}")
314
- cmd.insert(2, "--var chef_ver=#{wombat['products']['chef'].split('-')[1]}")
315
- cmd.insert(2, "--var chef_channel=#{wombat['products']['chef'].split('-')[0]}")
316
- cmd.insert(2, "--var automate=#{wombat['products']['automate']}")
317
- cmd.insert(2, "--var compliance=#{wombat['products']['compliance']}")
318
- cmd.insert(2, "--var chef-server=#{wombat['products']['chef-server']}")
319
- cmd.insert(2, "--var push-jobs-server=#{wombat['products']['push-jobs-server']}")
320
- cmd.insert(2, "--var manage=#{wombat['products']['manage']}")
321
- cmd.insert(2, "--var node-name=#{options['node-name']}") if template =~ /infranodes/
322
- cmd.insert(2, "--var node-number=#{options['node-number']}") if template =~ /build-node/
323
- cmd.insert(2, "--var build-nodes=#{wombat['build-nodes']['count']}")
324
- cmd.insert(2, "--var winrm_password=#{wombat['workstations']['password']}")
325
- cmd.insert(2, "--var winrm_username=Administrator")
326
- cmd.insert(2, "--var workstation-number=#{options['workstation-number']}") if template =~ /workstation/
327
- cmd.insert(2, "--var workstations=#{wombat['workstations']['count']}")
328
- cmd.insert(2, "--var aws_source_ami=#{base_image(template, builder, options)}") if builder =~ /amazon-ebs/
329
- cmd.insert(2, "--var gce_source_image=#{base_image(template, builder, options)}") if builder =~ /googlecompute/
330
- cmd.insert(2, "--var azure_location=#{wombat['azure']['location']}")
331
- cmd.insert(2, "--var ssh_username=#{linux}")
332
- cmd.insert(2, "--var azure_resource_group=#{wombat['name']}")
333
- cmd.insert(2, "--var azure_storage_account=#{wombat['azure']['storage_account']}")
334
- cmd.insert(2, "--debug") if @debug
335
- cmd.join(' ')
336
- end
337
- end
338
- end
1
+
2
+ require 'wombat/common'
3
+ require 'wombat/crypto'
4
+ require 'mixlib/shellout'
5
+ require 'parallel'
6
+ require 'ms_rest_azure'
7
+ require 'azure_mgmt_resources'
8
+ require 'azure_mgmt_storage'
9
+
10
+ module Wombat
11
+ class BuildRunner
12
+ include Wombat::Common
13
+ include Wombat::Crypto
14
+
15
+ attr_reader :templates, :builder, :parallel
16
+
17
+ def initialize(opts)
18
+ @templates = opts.templates.nil? ? calculate_templates : opts.templates
19
+ @builder = opts.builder.nil? ? "amazon-ebs" : opts.builder
20
+ @parallel = opts.parallel
21
+ @wombat_yml = opts.wombat_yml unless opts.wombat_yml.nil?
22
+ @debug = opts.debug
23
+ @no_vendor = opts.vendor
24
+ end
25
+
26
+ def start
27
+ if which('packer').nil?
28
+ raise "packer binary not found in path, exiting..."
29
+ end
30
+ banner("Generating certs (if necessary)")
31
+ wombat['certs'].each do |hostname|
32
+ gen_x509_cert(hostname)
33
+ end
34
+ banner("Generating SSH keypair (if necessary)")
35
+ gen_ssh_key
36
+
37
+ # If running on azure ensure that the resource group and storage account exist
38
+ prepare_azure if builder == "azure-arm"
39
+
40
+ time = Benchmark.measure do
41
+ banner("Starting build for templates: #{templates}")
42
+ aws_region_check if builder == 'amazon-ebs'
43
+ templates.each do |t|
44
+ vendor_cookbooks(t) unless @no_vendor
45
+ end
46
+
47
+ if parallel.nil?
48
+ build_hash.each do |k, v|
49
+ build(v['template'], v['options'])
50
+ end
51
+ else
52
+ build_parallel(templates)
53
+ end
54
+ end
55
+ shell_out_command("say -v fred \"Wombat has made an #{build_hash.keys}\" for you") if audio?
56
+ banner("Build finished in #{duration(time.real)}.")
57
+ end
58
+
59
+ private
60
+
61
+ def prepare_azure()
62
+
63
+ # Ensure that a storage acocunt has been specified, if it has not error
64
+ if wombat['azure']['storage_account'].nil?
65
+ puts "\nA storage account name must be specified in wombat.yml, e.g.\n openssl rand -base64 12\nEnsure all lowercase and no special characters"
66
+ exit
67
+ end
68
+
69
+ # Using environment variables connect to azure
70
+ subscription_id = ENV['AZURE_SUBSCRIPTION_ID']
71
+ tenant_id = ENV['AZURE_TENANT_ID']
72
+ client_id = ENV['AZURE_CLIENT_ID']
73
+ client_secret = ENV['AZURE_CLIENT_SECRET']
74
+
75
+ token_provider = MsRestAzure::ApplicationTokenProvider.new(tenant_id, client_id, client_secret)
76
+ azure_conn = MsRest::TokenCredentials.new(token_provider)
77
+
78
+ # Create a resource to create the resource group if it does not exist
79
+ resource_management_client = Azure::ARM::Resources::ResourceManagementClient.new(azure_conn)
80
+ resource_management_client.subscription_id = subscription_id
81
+
82
+ # Create a storage account client to create the stoarge account if it does not exist
83
+ storage_management_client = Azure::ARM::Storage::StorageManagementClient.new(azure_conn)
84
+ storage_management_client.subscription_id = subscription_id
85
+
86
+ # Check that the resource group exists
87
+ banner(format("Checking for resource group: %s", wombat['name']))
88
+ status = resource_management_client.resource_groups.check_existence(wombat['name'])
89
+ if status
90
+ puts "resource group already exists"
91
+ else
92
+ puts format("creating new resource group in '%s'", wombat['azure']['location'])
93
+
94
+ # Set the parameters for the resource group
95
+ resource_group = Azure::ARM::Resources::Models::ResourceGroup.new
96
+ resource_group.location = wombat['azure']['location']
97
+
98
+ # Create hash to be used as tags on the resource group
99
+ tags = {
100
+ owner: ENV['USER']
101
+ }
102
+
103
+ # If an owner has been specified in the wombat file override the owner value
104
+ if wombat.key?('owner') && !wombat['owner'].nil?
105
+ tags[:owner] = wombat['owner']
106
+ end
107
+
108
+ # Determine if there are any tags specified in the azure wmbat section that need to be added
109
+ if wombat['azure'].key?('tags') && wombat['azure']['tags'].length > 0
110
+
111
+ # Check to see if there are more than 15 tags in which case output a warning
112
+ if wombat['azure']['tags'].length > 14
113
+ warn ('More than 15 tags have been specified, only the first 15 will be added. This is a restriction in Azure.')
114
+ end
115
+
116
+ # Iterate around the tags and add each one to the tags array, up to 15
117
+ wombat['azure']['tags'].each_with_index do |(key, value), index|
118
+ tags[key] = value
119
+
120
+ if index == 13
121
+ break
122
+ end
123
+ end
124
+
125
+ end
126
+
127
+ # add the tags hash to the parameters
128
+ resource_group.tags = tags
129
+
130
+ # Create the resource group
131
+ resource_management_client.resource_groups.create_or_update(wombat['name'], resource_group)
132
+ end
133
+
134
+ # Check to see if the storage account already exists
135
+ banner(format("Checking for storage account: %s", wombat['azure']['storage_account']))
136
+
137
+ # Create the storage account in the resource group
138
+ # NOTE: This should have a test to see if the storage account exists and it available however the
139
+ # Azure Ruby SDK has an issue with the check_name_availability method and comes back with an error
140
+ # This would normally be done through an ARM template, but in this case needs to exist before Packer can run
141
+ storage_account = Azure::ARM::Storage::Models::StorageAccountCreateParameters.new
142
+ storage_account.location = wombat['azure']['location']
143
+ sku = Azure::ARM::Storage::Models::Sku.new
144
+ sku.name = 'Standard_LRS'
145
+ storage_account.sku = sku
146
+ storage_account.kind = Azure::ARM::Storage::Models::Kind::Storage
147
+
148
+ storage_management_client.storage_accounts.create(wombat['name'], wombat['azure']['storage_account'], storage_account)
149
+
150
+ end
151
+
152
+ def build(template, options)
153
+ bootstrap_aws if options['os'] == 'windows'
154
+ shell_out_command(packer_build_cmd(template, builder, options))
155
+ end
156
+
157
+ def build_parallel(templates)
158
+ Parallel.map(build_hash.keys, in_threads: build_hash.count) do |name|
159
+ build(build_hash[name]['template'], build_hash[name]['options'])
160
+ end
161
+ end
162
+
163
+ def build_hash
164
+ proc_hash = {}
165
+ templates.each do |template_name|
166
+ if template_name =~ /infranodes/
167
+ infranodes.each do |name, _rl|
168
+ next if name.empty?
169
+ proc_hash[name] = {
170
+ 'template' => template_name,
171
+ 'options' => {
172
+ 'node-name' => name,
173
+ 'os' => wombat['infranodes'][name]['platform']
174
+ }
175
+ }
176
+ end
177
+ elsif template_name =~ /build-node/
178
+ build_nodes.each do |name, num|
179
+ proc_hash[name] = {
180
+ 'template' => template_name,
181
+ 'options' => {
182
+ 'node-number' => num
183
+ }
184
+ }
185
+ end
186
+ elsif template_name =~ /workstation/
187
+ workstations.each do |name, num|
188
+ proc_hash[name] = {
189
+ 'template' => template_name,
190
+ 'options' => {
191
+ 'os' => wombat['workstations']['platform'],
192
+ 'workstation-number' => num
193
+ }
194
+ }
195
+ end
196
+ else
197
+ proc_hash[template_name] = {
198
+ 'template' => template_name,
199
+ 'options' => {}
200
+ }
201
+ end
202
+ end
203
+ proc_hash
204
+ end
205
+
206
+ def a_to_s(*args)
207
+ clean_array(*args).join(" ")
208
+ end
209
+
210
+ def clean_array(*args)
211
+ args.flatten.reject { |i| i.nil? || i == "" }.map(&:to_s)
212
+ end
213
+
214
+ def b_to_c(builder)
215
+ case builder
216
+ when 'amazon-ebs'
217
+ 'aws'
218
+ when 'googlecompute'
219
+ 'gce'
220
+ when 'azure-arm'
221
+ 'azure'
222
+ end
223
+ end
224
+
225
+ def shell_out_command(command)
226
+ cmd = Mixlib::ShellOut.new(a_to_s(command), :timeout => conf['timeout'], live_stream: STDOUT)
227
+ cmd.run_command
228
+ cmd
229
+ end
230
+
231
+ def aws_region_check
232
+ if ENV['AWS_REGION']
233
+ banner("Region set by environment: #{ENV['AWS_REGION']}")
234
+ else
235
+ banner("$AWS_REGION not set, setting to #{wombat['aws']['region']}")
236
+ ENV['AWS_REGION'] = wombat['aws']['region']
237
+ end
238
+ end
239
+
240
+ def vendor_cookbooks(template)
241
+ banner "Vendoring cookbooks for #{template}"
242
+
243
+ if template =~ /.*-windows/
244
+ base = template.split('-')[0]
245
+ else
246
+ base = template.split('.json')[0].tr('-', '_')
247
+ end
248
+ rm_cmd = "rm -rf #{conf['cookbook_dir']}/#{base}/Berksfile.lock vendored-cookbooks/#{base}"
249
+ shell_out_command(rm_cmd)
250
+ vendor_cmd = "berks vendor -q -b #{conf['cookbook_dir']}/#{base}/Berksfile vendored-cookbooks/#{base}"
251
+ shell_out_command(vendor_cmd)
252
+ end
253
+
254
+ def log(template, builder, options)
255
+ cloud = b_to_c(builder)
256
+ case template
257
+ when /automate/
258
+ log_name = "#{cloud}-automate-#{linux}"
259
+ when /chef-server/
260
+ log_name = "#{cloud}-chef-server-#{linux}"
261
+ when /compliance/
262
+ log_name = "#{cloud}-compliance-#{linux}"
263
+ when /build-node/
264
+ log_name = "#{cloud}-build-node-#{options['node-number']}-#{linux}"
265
+ when /workstation/
266
+ log_name = "#{cloud}-workstation-#{options['workstation-number']}-#{linux}"
267
+ when /infranodes/
268
+ if options['os'] =~ /windows/
269
+ log_name = "#{cloud}-infranodes-#{options['node-name']}-windows"
270
+ else
271
+ log_name = "#{cloud}-infranodes-#{options['node-name']}-#{linux}"
272
+ end
273
+ end
274
+ log_file = "#{conf['log_dir']}/#{log_name}.log"
275
+ end
276
+
277
+ def which(cmd)
278
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
279
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
280
+ exts.each { |ext|
281
+ exe = File.join(path, "#{cmd}#{ext}")
282
+ return exe if File.executable?(exe) && !File.directory?(exe)
283
+ }
284
+ end
285
+ return nil
286
+ end
287
+
288
+ def base_image(template, builder, options)
289
+ cloud = b_to_c(builder)
290
+ if template =~ /workstation/
291
+ wombat[cloud]['source_image']['windows']
292
+ elsif template =~ /infranodes/
293
+ if options['os'] == 'windows'
294
+ wombat[cloud]['source_image']['windows']
295
+ else
296
+ wombat[cloud]['source_image'][linux]
297
+ end
298
+ else
299
+ wombat[cloud]['source_image'][linux]
300
+ end
301
+ end
302
+
303
+ def packer_build_cmd(template, builder, options)
304
+ create_infranodes_json
305
+ Dir.mkdir(conf['log_dir'], 0755) unless File.exist?(conf['log_dir'])
306
+
307
+ cmd = %W(packer build #{conf['packer_dir']}/#{template}.json | tee #{log(template, builder, options)})
308
+ cmd.insert(2, "--only #{builder}")
309
+ cmd.insert(2, "--var org=#{wombat['org']}")
310
+ cmd.insert(2, "--var domain=#{wombat['domain']}")
311
+ cmd.insert(2, "--var domain_prefix=#{wombat['domain_prefix']}")
312
+ cmd.insert(2, "--var enterprise=#{wombat['enterprise']}")
313
+ cmd.insert(2, "--var chefdk=#{wombat['products']['chefdk']}")
314
+ cmd.insert(2, "--var chef_ver=#{wombat['products']['chef'].split('-')[1]}")
315
+ cmd.insert(2, "--var chef_channel=#{wombat['products']['chef'].split('-')[0]}")
316
+ cmd.insert(2, "--var automate=#{wombat['products']['automate']}")
317
+ cmd.insert(2, "--var compliance=#{wombat['products']['compliance']}")
318
+ cmd.insert(2, "--var chef-server=#{wombat['products']['chef-server']}")
319
+ cmd.insert(2, "--var push-jobs-server=#{wombat['products']['push-jobs-server']}")
320
+ cmd.insert(2, "--var manage=#{wombat['products']['manage']}")
321
+ cmd.insert(2, "--var node-name=#{options['node-name']}") if template =~ /infranodes/
322
+ cmd.insert(2, "--var node-number=#{options['node-number']}") if template =~ /build-node/
323
+ cmd.insert(2, "--var build-nodes=#{wombat['build-nodes']['count']}")
324
+ cmd.insert(2, "--var winrm_password=#{wombat['workstations']['password']}")
325
+ cmd.insert(2, "--var winrm_username=Administrator")
326
+ cmd.insert(2, "--var workstation-number=#{options['workstation-number']}") if template =~ /workstation/
327
+ cmd.insert(2, "--var workstations=#{wombat['workstations']['count']}")
328
+ cmd.insert(2, "--var aws_source_ami=#{base_image(template, builder, options)}") if builder =~ /amazon-ebs/
329
+ cmd.insert(2, "--var gce_source_image=#{base_image(template, builder, options)}") if builder =~ /googlecompute/
330
+ cmd.insert(2, "--var azure_location=#{wombat['azure']['location']}")
331
+ cmd.insert(2, "--var ssh_username=#{linux}")
332
+ cmd.insert(2, "--var azure_resource_group=#{wombat['name']}")
333
+ cmd.insert(2, "--var azure_storage_account=#{wombat['azure']['storage_account']}")
334
+ cmd.insert(2, "--debug") if @debug
335
+ cmd.join(' ')
336
+ end
337
+ end
338
+ end