wombat-cli 0.6.1 → 0.6.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.
- checksums.yaml +4 -4
- data/.gitignore +23 -23
- data/.travis.yml +22 -27
- data/CHANGELOG.md +438 -423
- data/DESIGN.md +49 -49
- data/Gemfile +5 -5
- data/README.md +146 -146
- data/Rakefile +26 -26
- data/bin/wombat +24 -24
- data/generator_files/Vagrantfile +120 -120
- data/generator_files/cookbooks/automate/.gitignore +16 -16
- data/generator_files/cookbooks/automate/.kitchen.ec2.yml +34 -34
- data/generator_files/cookbooks/automate/.kitchen.yml +24 -24
- data/generator_files/cookbooks/automate/Berksfile +6 -6
- data/generator_files/cookbooks/automate/README.md +4 -4
- data/generator_files/cookbooks/automate/chefignore +102 -102
- data/generator_files/cookbooks/automate/libraries/_helper.rb +52 -52
- data/generator_files/cookbooks/automate/libraries/delivery_api.rb +204 -204
- data/generator_files/cookbooks/automate/libraries/delivery_project.rb +31 -31
- data/generator_files/cookbooks/automate/libraries/dsl.rb +4 -4
- data/generator_files/cookbooks/automate/metadata.rb +11 -11
- data/generator_files/cookbooks/automate/recipes/default.rb +118 -124
- data/generator_files/cookbooks/automate/recipes/update-users.rb +48 -48
- data/generator_files/cookbooks/automate/templates/delivery.erb +6 -6
- data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/files/automate.crt +26 -26
- data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/files/automate.key +27 -27
- data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/files/chef.crt +25 -25
- data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/files/chef.key +27 -27
- data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/files/compliance.crt +26 -26
- data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/files/compliance.key +27 -27
- data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/files/private.pem +27 -27
- data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/files/public.pub +1 -1
- data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/metadata.rb +3 -3
- data/generator_files/cookbooks/automate/test/fixtures/cookbooks/mock_data/recipes/default.rb +27 -27
- data/generator_files/cookbooks/automate/test/integration/default/automate_spec.rb +56 -56
- data/generator_files/cookbooks/build_node/.gitignore +16 -16
- data/generator_files/cookbooks/build_node/.kitchen.ec2.yml +37 -37
- data/generator_files/cookbooks/build_node/.kitchen.yml +23 -23
- data/generator_files/cookbooks/build_node/Berksfile +8 -8
- data/generator_files/cookbooks/build_node/README.md +4 -4
- data/generator_files/cookbooks/build_node/chefignore +102 -102
- data/generator_files/cookbooks/build_node/metadata.rb +12 -12
- data/generator_files/cookbooks/build_node/recipes/default.rb +38 -38
- data/generator_files/cookbooks/build_node/templates/client.erb +2 -2
- data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/files/automate.crt +26 -26
- data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/files/automate.key +27 -27
- data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/files/chef.crt +25 -25
- data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/files/chef.key +27 -27
- data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/files/compliance.crt +26 -26
- data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/files/compliance.key +27 -27
- data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/files/private.pem +27 -27
- data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/files/public.pub +1 -1
- data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/metadata.rb +2 -2
- data/generator_files/cookbooks/build_node/test/fixtures/cookbooks/mock_data/recipes/default.rb +18 -18
- data/generator_files/cookbooks/build_node/test/integration/default/build-node_spec.rb +40 -40
- data/generator_files/cookbooks/chef_server/.gitignore +16 -16
- data/generator_files/cookbooks/chef_server/.kitchen.ec2.yml +34 -34
- data/generator_files/cookbooks/chef_server/.kitchen.yml +24 -24
- data/generator_files/cookbooks/chef_server/Berksfile +6 -6
- data/generator_files/cookbooks/chef_server/README.md +4 -4
- data/generator_files/cookbooks/chef_server/chefignore +102 -102
- data/generator_files/cookbooks/chef_server/metadata.rb +11 -11
- data/generator_files/cookbooks/chef_server/recipes/bootstrap_users.rb +91 -91
- data/generator_files/cookbooks/chef_server/recipes/default.rb +113 -113
- data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/files/automate.crt +26 -26
- data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/files/automate.key +27 -27
- data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/files/chef.crt +25 -25
- data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/files/chef.key +27 -27
- data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/files/compliance.crt +26 -26
- data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/files/compliance.key +27 -27
- data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/files/private.pem +27 -27
- data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/files/public.pub +1 -1
- data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/metadata.rb +2 -2
- data/generator_files/cookbooks/chef_server/test/fixtures/cookbooks/mock_data/recipes/default.rb +23 -23
- data/generator_files/cookbooks/chef_server/test/integration/default/chef_server_spec.rb +50 -50
- data/generator_files/cookbooks/compliance/.gitignore +16 -16
- data/generator_files/cookbooks/compliance/.kitchen.ec2.yml +34 -34
- data/generator_files/cookbooks/compliance/.kitchen.yml +24 -24
- data/generator_files/cookbooks/compliance/Berksfile +7 -7
- data/generator_files/cookbooks/compliance/README.md +4 -4
- data/generator_files/cookbooks/compliance/chefignore +102 -102
- data/generator_files/cookbooks/compliance/metadata.rb +11 -11
- data/generator_files/cookbooks/compliance/recipes/default.rb +57 -57
- data/generator_files/cookbooks/compliance/spec/spec_helper.rb +2 -2
- data/generator_files/cookbooks/compliance/spec/unit/recipes/default_spec.rb +20 -20
- data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/files/automate.crt +26 -26
- data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/files/automate.key +27 -27
- data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/files/chef.crt +25 -25
- data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/files/chef.key +27 -27
- data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/files/compliance.crt +26 -26
- data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/files/compliance.key +27 -27
- data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/files/private.pem +27 -27
- data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/files/public.pub +1 -1
- data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/metadata.rb +4 -4
- data/generator_files/cookbooks/compliance/test/fixtures/cookbooks/mock_data/recipes/default.rb +21 -21
- data/generator_files/cookbooks/compliance/test/integration/default/compliance.rb +28 -28
- data/generator_files/cookbooks/infranodes/.gitignore +16 -16
- data/generator_files/cookbooks/infranodes/.kitchen.ec2.yml +48 -48
- data/generator_files/cookbooks/infranodes/.kitchen.yml +21 -21
- data/generator_files/cookbooks/infranodes/Berksfile +6 -6
- data/generator_files/cookbooks/infranodes/README.md +4 -4
- data/generator_files/cookbooks/infranodes/attributes/default.rb +2 -2
- data/generator_files/cookbooks/infranodes/chefignore +102 -102
- data/generator_files/cookbooks/infranodes/metadata.rb +13 -13
- data/generator_files/cookbooks/infranodes/recipes/default.rb +57 -57
- data/generator_files/cookbooks/infranodes/spec/spec_helper.rb +2 -2
- data/generator_files/cookbooks/infranodes/spec/unit/recipes/default_spec.rb +20 -20
- data/generator_files/cookbooks/infranodes/templates/default/client.rb.erb +5 -5
- data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/files/automate.crt +26 -26
- data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/files/automate.key +27 -27
- data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/files/chef.crt +25 -25
- data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/files/chef.key +27 -27
- data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/files/compliance.crt +26 -26
- data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/files/compliance.key +27 -27
- data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/files/private.pem +27 -27
- data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/files/public.pub +1 -1
- data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/metadata.rb +3 -3
- data/generator_files/cookbooks/infranodes/test/fixtures/cookbooks/mock_data/recipes/default.rb +27 -27
- data/generator_files/cookbooks/infranodes/test/integration/default/infranodes_spec.rb +22 -22
- data/generator_files/cookbooks/infranodes/test/integration/helpers/serverspec/spec_helper.rb +8 -8
- data/generator_files/cookbooks/wombat/.gitignore +16 -16
- data/generator_files/cookbooks/wombat/.kitchen.yml +43 -43
- data/generator_files/cookbooks/wombat/Berksfile +5 -5
- data/generator_files/cookbooks/wombat/README.md +4 -4
- data/generator_files/cookbooks/wombat/attributes/default.rb +79 -80
- data/generator_files/cookbooks/wombat/attributes/packer.rb +18 -18
- data/generator_files/cookbooks/wombat/chefignore +102 -102
- data/generator_files/cookbooks/wombat/metadata.rb +13 -13
- data/generator_files/cookbooks/wombat/recipes/authorized-keys.rb +20 -20
- data/generator_files/cookbooks/wombat/recipes/default.rb +111 -111
- data/generator_files/cookbooks/wombat/recipes/etc-hosts.rb +51 -51
- data/generator_files/cookbooks/workstation/.gitignore +16 -16
- data/generator_files/cookbooks/workstation/.kitchen.azure.yml +45 -0
- data/generator_files/cookbooks/workstation/.kitchen.ec2.yml +46 -30
- data/generator_files/cookbooks/workstation/.kitchen.yml +42 -22
- data/generator_files/cookbooks/workstation/Berksfile +7 -7
- data/generator_files/cookbooks/workstation/README.md +3 -3
- data/generator_files/cookbooks/workstation/chefignore +106 -102
- data/generator_files/cookbooks/workstation/files/atom.apm.list +10 -7
- data/generator_files/cookbooks/workstation/files/atom.config.cson +6 -3
- data/generator_files/cookbooks/workstation/{templates/default/ise_profile.ps1.erb → files/ise_profile.ps1} +11 -11
- data/generator_files/cookbooks/workstation/libraries/home.rb +4 -4
- data/generator_files/cookbooks/workstation/metadata.rb +14 -14
- data/generator_files/cookbooks/workstation/recipes/browser.rb +53 -58
- data/generator_files/cookbooks/workstation/recipes/certs-keys.rb +41 -45
- data/generator_files/cookbooks/workstation/recipes/chef.rb +29 -28
- data/generator_files/cookbooks/workstation/recipes/default.rb +24 -21
- data/generator_files/cookbooks/workstation/recipes/dotnet.rb +19 -17
- data/generator_files/cookbooks/workstation/recipes/editor.rb +46 -18
- data/generator_files/cookbooks/workstation/recipes/profile.rb +14 -41
- data/generator_files/cookbooks/workstation/recipes/terminal.rb +11 -11
- data/generator_files/cookbooks/workstation/templates/default/bookmarks.html.erb +23 -23
- data/generator_files/cookbooks/workstation/templates/default/data_collector.rb.erb +2 -2
- data/generator_files/cookbooks/workstation/templates/default/knife.rb.erb +10 -10
- data/generator_files/cookbooks/workstation/templates/default/master_preferences.json.erb +28 -28
- data/generator_files/cookbooks/workstation/templates/default/ssh_config.erb +16 -16
- data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/automate.crt +26 -26
- data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/automate.key +27 -27
- data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/chef.crt +26 -26
- data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/chef.key +27 -27
- data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/compliance.crt +26 -26
- data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/compliance.key +27 -27
- data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/private.pem +27 -27
- data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/public.pub +1 -1
- data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/metadata.rb +2 -2
- data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/recipes/default.rb +21 -21
- data/generator_files/cookbooks/workstation/test/integration/default/workstation_spec.rb +77 -37
- data/generator_files/packer/automate.json +136 -136
- data/generator_files/packer/build-node.json +142 -142
- data/generator_files/packer/chef-server.json +137 -137
- data/generator_files/packer/compliance.json +133 -133
- data/generator_files/packer/infranodes-windows.json +143 -143
- data/generator_files/packer/infranodes.json +134 -134
- data/generator_files/packer/scripts/PreSysprep.ps1 +9 -0
- data/generator_files/packer/workstation.json +160 -142
- data/generator_files/templates/arm.md.json.erb +754 -754
- data/generator_files/templates/arm.vhd.json.erb +630 -630
- data/generator_files/templates/bootstrap-aws.erb +39 -39
- data/generator_files/templates/cfn.json.erb +675 -674
- data/generator_files/wombat.yml +75 -74
- data/lib/wombat/aws.rb +67 -67
- data/lib/wombat/build.rb +392 -392
- data/lib/wombat/cli.rb +254 -254
- data/lib/wombat/common.rb +420 -420
- data/lib/wombat/crypto.rb +65 -65
- data/lib/wombat/delete.rb +67 -67
- data/lib/wombat/deploy.rb +128 -128
- data/lib/wombat/init.rb +32 -32
- data/lib/wombat/latest.rb +27 -27
- data/lib/wombat/output.rb +101 -101
- data/lib/wombat/update.rb +20 -20
- data/lib/wombat/version.rb +3 -3
- data/lib/wombat.rb +8 -8
- data/spec/functional/common_spec.rb +26 -26
- data/spec/spec_helper.rb +103 -103
- data/spec/unit/common_spec.rb +116 -116
- data/terraform/README.md +13 -13
- data/terraform/templates/terraform.tfvars.erb +12 -12
- data/terraform/wombat.tf +328 -328
- data/wombat-cli.gemspec +36 -36
- metadata +6 -4
data/lib/wombat/common.rb
CHANGED
@@ -1,421 +1,421 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
require 'json'
|
3
|
-
require 'erb'
|
4
|
-
require 'benchmark'
|
5
|
-
require 'fileutils'
|
6
|
-
require 'ms_rest_azure'
|
7
|
-
|
8
|
-
module Wombat
|
9
|
-
module Common
|
10
|
-
|
11
|
-
def banner(msg)
|
12
|
-
puts "==> #{msg}"
|
13
|
-
end
|
14
|
-
|
15
|
-
def info(msg)
|
16
|
-
puts " #{msg}"
|
17
|
-
end
|
18
|
-
|
19
|
-
def warn(msg)
|
20
|
-
puts ">>> #{msg}"
|
21
|
-
end
|
22
|
-
|
23
|
-
def duration(total)
|
24
|
-
total = 0 if total.nil?
|
25
|
-
minutes = (total / 60).to_i
|
26
|
-
seconds = (total - (minutes * 60))
|
27
|
-
format("%dm%.2fs", minutes, seconds)
|
28
|
-
end
|
29
|
-
|
30
|
-
def wombat
|
31
|
-
@wombat_yml ||= ENV['WOMBAT_YML'] unless ENV['WOMBAT_YML'].nil?
|
32
|
-
@wombat_yml ||= 'wombat.yml'
|
33
|
-
if !File.exist?(@wombat_yml)
|
34
|
-
warn('No wombat.yml found, copying example')
|
35
|
-
gen_dir = "#{File.expand_path("../..", File.dirname(__FILE__))}/generator_files"
|
36
|
-
FileUtils.cp_r "#{gen_dir}/wombat.yml", Dir.pwd
|
37
|
-
end
|
38
|
-
YAML.load(File.read(@wombat_yml))
|
39
|
-
end
|
40
|
-
|
41
|
-
def lock
|
42
|
-
if !File.exist?('wombat.lock')
|
43
|
-
warn('No wombat.lock found')
|
44
|
-
return 1
|
45
|
-
else
|
46
|
-
JSON.parse(File.read('wombat.lock'))
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def bootstrap_aws
|
51
|
-
@workstation_passwd = wombat['workstations']['password']
|
52
|
-
rendered = ERB.new(File.read("#{conf['template_dir']}/bootstrap-aws.erb"), nil, '-').result(binding)
|
53
|
-
Dir.mkdir("#{conf['packer_dir']}/scripts", 0755) unless File.exist?("#{conf['packer_dir']}/scripts")
|
54
|
-
File.open("#{conf['packer_dir']}/scripts/bootstrap-aws.txt", 'w') { |file| file.puts rendered }
|
55
|
-
banner("Generated: #{conf['packer_dir']}/scripts/bootstrap-aws.txt")
|
56
|
-
end
|
57
|
-
|
58
|
-
def parse_log(log, cloud)
|
59
|
-
regex = case cloud
|
60
|
-
when 'gcp'
|
61
|
-
'A disk image was created:'
|
62
|
-
when 'azure'
|
63
|
-
|
64
|
-
if !wombat['azure'].key?('use_managed_disks') || !wombat['azure']['use_managed_disks']
|
65
|
-
'^OSDiskUri:'
|
66
|
-
else
|
67
|
-
'^ManagedDiskOSDiskUri:'
|
68
|
-
end
|
69
|
-
|
70
|
-
else
|
71
|
-
"#{wombat['aws']['region']}:"
|
72
|
-
end
|
73
|
-
|
74
|
-
File.read(log).split("\n").grep(/#{regex}/) {|x| x.split[1]}.last
|
75
|
-
end
|
76
|
-
|
77
|
-
def infranodes
|
78
|
-
unless wombat['infranodes'].nil?
|
79
|
-
wombat['infranodes'].sort
|
80
|
-
else
|
81
|
-
puts 'No infranodes listed in wombat.yml'
|
82
|
-
{}
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def build_nodes
|
87
|
-
build_nodes = {}
|
88
|
-
1.upto(wombat['build-nodes']['count'].to_i) do |i|
|
89
|
-
build_nodes["build-node-#{i}"] = i
|
90
|
-
end
|
91
|
-
build_nodes
|
92
|
-
end
|
93
|
-
|
94
|
-
def workstations
|
95
|
-
workstations = {}
|
96
|
-
1.upto(wombat['workstations']['count'].to_i) do |i|
|
97
|
-
workstations["workstation-#{i}"] = i
|
98
|
-
end
|
99
|
-
workstations
|
100
|
-
end
|
101
|
-
|
102
|
-
def create_infranodes_json
|
103
|
-
infranodes_file_path = File.join(conf['files_dir'], 'infranodes-info.json')
|
104
|
-
if File.exists?(infranodes_file_path) && is_valid_json?(infranodes_file_path)
|
105
|
-
current_state = JSON(File.read(infranodes_file_path))
|
106
|
-
else
|
107
|
-
current_state = nil
|
108
|
-
end
|
109
|
-
return if current_state == infranodes # yay idempotence
|
110
|
-
File.open(infranodes_file_path, 'w') do |f|
|
111
|
-
f.puts JSON.pretty_generate(infranodes)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def linux
|
116
|
-
wombat['linux'].nil? ? 'ubuntu' : wombat['linux']
|
117
|
-
end
|
118
|
-
|
119
|
-
def conf
|
120
|
-
conf = wombat['conf']
|
121
|
-
conf ||= {}
|
122
|
-
conf['files_dir'] ||= 'files'
|
123
|
-
conf['key_dir'] ||= 'keys'
|
124
|
-
conf['cookbook_dir'] ||= 'cookbooks'
|
125
|
-
conf['packer_dir'] ||= 'packer'
|
126
|
-
conf['log_dir'] ||= 'logs'
|
127
|
-
conf['stack_dir'] ||= 'stacks'
|
128
|
-
conf['template_dir'] ||= 'templates'
|
129
|
-
conf['timeout'] ||= 7200
|
130
|
-
conf['audio'] ||= false
|
131
|
-
conf
|
132
|
-
end
|
133
|
-
|
134
|
-
def is_mac?
|
135
|
-
(/darwin/ =~ RUBY_PLATFORM) != nil
|
136
|
-
end
|
137
|
-
|
138
|
-
def audio?
|
139
|
-
is_mac? && conf['audio']
|
140
|
-
end
|
141
|
-
|
142
|
-
def logs
|
143
|
-
path = "#{conf['log_dir']}/#{cloud}*.log"
|
144
|
-
Dir.glob(path).reject { |l| !l.match(wombat['linux']) }
|
145
|
-
end
|
146
|
-
|
147
|
-
def calculate_templates
|
148
|
-
globs = "*.json"
|
149
|
-
Dir.chdir(conf['packer_dir']) do
|
150
|
-
Array(globs).
|
151
|
-
map { |glob| result = Dir.glob("#{glob}"); result.empty? ? glob : result }.
|
152
|
-
flatten.
|
153
|
-
sort.
|
154
|
-
delete_if { |file| file =~ /\.variables\./ }.
|
155
|
-
map { |template| template.sub(/\.json$/, '') }
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
def update_lock(cloud)
|
160
|
-
copy = {}
|
161
|
-
copy = wombat
|
162
|
-
|
163
|
-
# Check that the copy contains a key for the named cloud
|
164
|
-
unless copy.key?(cloud)
|
165
|
-
throw "The Cloud '#{cloud}' is not specified in Wombat"
|
166
|
-
end
|
167
|
-
|
168
|
-
# Determine the region/location/zone for the specific cloud
|
169
|
-
case cloud
|
170
|
-
when 'aws'
|
171
|
-
region = copy['aws']['region']
|
172
|
-
when 'azure'
|
173
|
-
region = copy['azure']['location']
|
174
|
-
when 'gce'
|
175
|
-
region = copy['gce']['zone']
|
176
|
-
end
|
177
|
-
|
178
|
-
linux = copy['linux']
|
179
|
-
copy['amis'] = { region => {} }
|
180
|
-
|
181
|
-
if logs.length == 0
|
182
|
-
warn('No logs found - skipping lock update')
|
183
|
-
else
|
184
|
-
logs.each do |log|
|
185
|
-
case log
|
186
|
-
when /build-node/
|
187
|
-
copy['amis'][region]['build-node'] ||= {}
|
188
|
-
num = log.split('-')[3]
|
189
|
-
copy['amis'][region]['build-node'].store(num, parse_log(log, cloud))
|
190
|
-
when /workstation/
|
191
|
-
copy['amis'][region]['workstation'] ||= {}
|
192
|
-
num = log.split('-')[2]
|
193
|
-
copy['amis'][region]['workstation'].store(num, parse_log(log, cloud))
|
194
|
-
when /infranodes/
|
195
|
-
copy['amis'][region]['infranodes'] ||= {}
|
196
|
-
name = log.split('-')[2]
|
197
|
-
copy['amis'][region]['infranodes'].store(name, parse_log(log, cloud))
|
198
|
-
else
|
199
|
-
instance = log.match("#{cloud}-(.*)-#{linux}\.log")[1]
|
200
|
-
copy['amis'][region].store(instance, parse_log(log, cloud))
|
201
|
-
end
|
202
|
-
end
|
203
|
-
copy['last_updated'] = Time.now.gmtime.strftime('%Y%m%d%H%M%S')
|
204
|
-
banner('Updating wombat.lock')
|
205
|
-
File.open('wombat.lock', 'w') do |f|
|
206
|
-
f.write(JSON.pretty_generate(copy))
|
207
|
-
end
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
def update_template(cloud)
|
212
|
-
if lock == 1
|
213
|
-
warn('No lock - skipping template creation')
|
214
|
-
else
|
215
|
-
|
216
|
-
@demo = lock['name']
|
217
|
-
@version = lock['version']
|
218
|
-
@ttl = lock['ttl']
|
219
|
-
|
220
|
-
# Determine the region/location/zone for the specific cloud
|
221
|
-
case cloud
|
222
|
-
when 'aws'
|
223
|
-
region = lock['aws']['region']
|
224
|
-
template_files = {
|
225
|
-
"cfn.json.erb": "#{conf['stack_dir']}/#{@demo}.json"
|
226
|
-
}
|
227
|
-
@chef_server_ami = lock['amis'][region]['chef-server']
|
228
|
-
@automate_ami = lock['amis'][region]['automate']
|
229
|
-
@compliance_ami = lock['amis'][region]['compliance']
|
230
|
-
@availability_zone = lock['aws']['az']
|
231
|
-
@iam_roles = lock['aws']['iam_roles']
|
232
|
-
when 'azure'
|
233
|
-
region = lock['azure']['location']
|
234
|
-
@storage_account = lock['azure']['storage_account']
|
235
|
-
|
236
|
-
template_files = {}
|
237
|
-
|
238
|
-
# determine whether to use VHD or Managed Disks
|
239
|
-
if !lock['azure'].key?('use_managed_disks') || !lock['azure']['use_managed_disks']
|
240
|
-
template_files['arm.vhd.json.erb'] = format("%s/%s.json", conf['stack_dir'], @demo)
|
241
|
-
else
|
242
|
-
template_files['arm.md.json.erb'] = format("%s/%s.json", conf['stack_dir'], @demo)
|
243
|
-
end
|
244
|
-
|
245
|
-
@chef_server_uri = lock['amis'][region]['chef-server']
|
246
|
-
@automate_uri = lock['amis'][region]['automate']
|
247
|
-
@compliance_uri = lock['amis'][region]['compliance']
|
248
|
-
@password = lock['workstations']['password']
|
249
|
-
@public_key = File.read("#{conf['key_dir']}/public.pub").chomp
|
250
|
-
|
251
|
-
# Set the Azure Tag used to identify Chef products in Azure
|
252
|
-
@chef_tag = azure_provider_tag
|
253
|
-
when 'gce'
|
254
|
-
region = lock['gce']['zone']
|
255
|
-
end
|
256
|
-
|
257
|
-
if lock['amis'][region].key?('build-node')
|
258
|
-
@build_nodes = lock['build-nodes']['count'].to_i
|
259
|
-
@build_node_ami = {}
|
260
|
-
1.upto(@build_nodes) do |i|
|
261
|
-
@build_node_ami[i] = lock['amis'][region]['build-node'][i.to_s]
|
262
|
-
end
|
263
|
-
end
|
264
|
-
|
265
|
-
@infra = {}
|
266
|
-
infranodes.each do |name, _rl|
|
267
|
-
@infra[name] = lock['amis'][region]['infranodes'][name]
|
268
|
-
end
|
269
|
-
|
270
|
-
if lock['amis'][region].key?('workstation')
|
271
|
-
@workstations = lock['workstations']['count'].to_i
|
272
|
-
@workstation_ami = {}
|
273
|
-
1.upto(@workstations) do |i|
|
274
|
-
@workstation_ami[i] = lock['amis'][region]['workstation'][i.to_s]
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
278
|
-
# Iterate around each of the template files that have been defined and render it
|
279
|
-
template_files.each do |template_file, destination|
|
280
|
-
rendered_cfn = ERB.new(File.read("#{conf['template_dir']}/#{template_file}"), nil, '-').result(binding)
|
281
|
-
Dir.mkdir(conf['stack_dir'], 0755) unless File.exist?(conf['stack_dir'])
|
282
|
-
File.open("#{destination}", 'w') { |file| file.puts rendered_cfn }
|
283
|
-
banner("Generated: #{destination}")
|
284
|
-
end
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
def is_valid_json?(file)
|
289
|
-
begin
|
290
|
-
JSON.parse(file)
|
291
|
-
true
|
292
|
-
rescue JSON::ParserError => e
|
293
|
-
false
|
294
|
-
end
|
295
|
-
end
|
296
|
-
|
297
|
-
# Return the Azure Provider tag that should be applied to resource
|
298
|
-
def azure_provider_tag
|
299
|
-
"33194f91-eb5f-4110-827a-e95f640a9e46".upcase
|
300
|
-
end
|
301
|
-
|
302
|
-
# Connect to Azure using environment variables
|
303
|
-
#
|
304
|
-
#
|
305
|
-
def connect_azure
|
306
|
-
|
307
|
-
# Create the connection to Azure using the information in the environment variables
|
308
|
-
tenant_id = ENV['AZURE_TENANT_ID']
|
309
|
-
client_id = ENV['AZURE_CLIENT_ID']
|
310
|
-
client_secret = ENV['AZURE_CLIENT_SECRET']
|
311
|
-
|
312
|
-
token_provider = MsRestAzure::ApplicationTokenProvider.new(tenant_id, client_id, client_secret)
|
313
|
-
MsRest::TokenCredentials.new(token_provider)
|
314
|
-
end
|
315
|
-
|
316
|
-
# Track the progress of the deployment in Azure
|
317
|
-
#
|
318
|
-
# ===== Attributes
|
319
|
-
#
|
320
|
-
# * +rg_name+ - Name of the resource group being deployed to
|
321
|
-
# * +deployment_name+ - Name of the deployment that is currently being processed
|
322
|
-
def follow_azure_deployment(rg_name, deployment_name)
|
323
|
-
|
324
|
-
end_provisioning_states = 'Canceled,Failed,Deleted,Succeeded'
|
325
|
-
end_provisioning_state_reached = false
|
326
|
-
|
327
|
-
until end_provisioning_state_reached
|
328
|
-
list_outstanding_deployment_operations(rg_name, deployment_name)
|
329
|
-
sleep 10
|
330
|
-
deployment_provisioning_state = deployment_state(rg_name, deployment_name)
|
331
|
-
end_provisioning_state_reached = end_provisioning_states.split(',').include?(deployment_provisioning_state)
|
332
|
-
end
|
333
|
-
info format("Resource Template deployment reached end state of %s", deployment_provisioning_state)
|
334
|
-
end
|
335
|
-
|
336
|
-
# Get a list of the outstanding deployment operations
|
337
|
-
#
|
338
|
-
# ===== Attributes
|
339
|
-
#
|
340
|
-
# * +rg_name+ - Name of the resource group being deployed to
|
341
|
-
# * +deployment_name+ - Name of the deployment that is currently being processed
|
342
|
-
def list_outstanding_deployment_operations(rg_name, deployment_name)
|
343
|
-
end_operation_states = 'Failed,Succeeded'
|
344
|
-
deployment_operations = resource_management_client.deployment_operations.list(rg_name, deployment_name)
|
345
|
-
deployment_operations.each do |val|
|
346
|
-
resource_provisioning_state = val.properties.provisioning_state
|
347
|
-
unless val.properties.target_resource.nil?
|
348
|
-
resource_name = val.properties.target_resource.resource_name
|
349
|
-
resource_type = val.properties.target_resource.resource_type
|
350
|
-
end
|
351
|
-
end_operation_state_reached = end_operation_states.split(',').include?(resource_provisioning_state)
|
352
|
-
unless end_operation_state_reached
|
353
|
-
info format("resource %s '%s' provisioning status is %s", resource_type, resource_name, resource_provisioning_state)
|
354
|
-
end
|
355
|
-
end
|
356
|
-
end
|
357
|
-
|
358
|
-
# Get the state of the specified deployment
|
359
|
-
#
|
360
|
-
# ===== Attributes
|
361
|
-
#
|
362
|
-
# * +rg_name+ - Name of the resource group being deployed to
|
363
|
-
# * +deployment_name+ - Name of the deployment that is currently being processed
|
364
|
-
def deployment_state(rg_name, deployment_name)
|
365
|
-
deployments = resource_management_client.deployments.get(rg_name, deployment_name)
|
366
|
-
deployments.properties.provisioning_state
|
367
|
-
end
|
368
|
-
|
369
|
-
def create_resource_group(resource_management_client, name, location, owner = nil, rgtags = {})
|
370
|
-
|
371
|
-
# Check that the resource group exists
|
372
|
-
banner(format("Checking for resource group: %s", name))
|
373
|
-
status = resource_management_client.resource_groups.check_existence(name)
|
374
|
-
if status
|
375
|
-
puts "resource group already exists"
|
376
|
-
else
|
377
|
-
puts format("creating new resource group in '%s'", location)
|
378
|
-
|
379
|
-
# Set the parameters for the resource group
|
380
|
-
resource_group = Azure::ARM::Resources::Models::ResourceGroup.new
|
381
|
-
resource_group.location = location
|
382
|
-
|
383
|
-
# Create hash to be used as tags on the resource group
|
384
|
-
tags = {
|
385
|
-
owner: ENV['USER'],
|
386
|
-
provider: azure_provider_tag
|
387
|
-
}
|
388
|
-
|
389
|
-
# If an owner has been specified in the wombat file override the owner value
|
390
|
-
if !owner.nil?
|
391
|
-
tags[:owner] = owner
|
392
|
-
end
|
393
|
-
|
394
|
-
# Determine if there are any tags specified in the azure wmbat section that need to be added
|
395
|
-
if !rgtags.nil? && rgtags.length > 0
|
396
|
-
|
397
|
-
# Check to see if there are more than 15 tags in which case output a warning
|
398
|
-
if rgtags.length > 14
|
399
|
-
warn ('More than 15 tags have been specified, only the first 15 will be added. This is a restriction in Azure.')
|
400
|
-
end
|
401
|
-
|
402
|
-
# Iterate around the tags and add each one to the tags array, up to 15
|
403
|
-
rgtags.each_with_index do |(key, value), index|
|
404
|
-
tags[key] = value
|
405
|
-
|
406
|
-
if index == 12
|
407
|
-
break
|
408
|
-
end
|
409
|
-
end
|
410
|
-
|
411
|
-
end
|
412
|
-
|
413
|
-
# add the tags hash to the parameters
|
414
|
-
resource_group.tags = rgtags
|
415
|
-
|
416
|
-
# Create the resource group
|
417
|
-
resource_management_client.resource_groups.create_or_update(name, resource_group)
|
418
|
-
end
|
419
|
-
end
|
420
|
-
end
|
1
|
+
require 'yaml'
|
2
|
+
require 'json'
|
3
|
+
require 'erb'
|
4
|
+
require 'benchmark'
|
5
|
+
require 'fileutils'
|
6
|
+
require 'ms_rest_azure'
|
7
|
+
|
8
|
+
module Wombat
|
9
|
+
module Common
|
10
|
+
|
11
|
+
def banner(msg)
|
12
|
+
puts "==> #{msg}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def info(msg)
|
16
|
+
puts " #{msg}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def warn(msg)
|
20
|
+
puts ">>> #{msg}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def duration(total)
|
24
|
+
total = 0 if total.nil?
|
25
|
+
minutes = (total / 60).to_i
|
26
|
+
seconds = (total - (minutes * 60))
|
27
|
+
format("%dm%.2fs", minutes, seconds)
|
28
|
+
end
|
29
|
+
|
30
|
+
def wombat
|
31
|
+
@wombat_yml ||= ENV['WOMBAT_YML'] unless ENV['WOMBAT_YML'].nil?
|
32
|
+
@wombat_yml ||= 'wombat.yml'
|
33
|
+
if !File.exist?(@wombat_yml)
|
34
|
+
warn('No wombat.yml found, copying example')
|
35
|
+
gen_dir = "#{File.expand_path("../..", File.dirname(__FILE__))}/generator_files"
|
36
|
+
FileUtils.cp_r "#{gen_dir}/wombat.yml", Dir.pwd
|
37
|
+
end
|
38
|
+
YAML.load(File.read(@wombat_yml))
|
39
|
+
end
|
40
|
+
|
41
|
+
def lock
|
42
|
+
if !File.exist?('wombat.lock')
|
43
|
+
warn('No wombat.lock found')
|
44
|
+
return 1
|
45
|
+
else
|
46
|
+
JSON.parse(File.read('wombat.lock'))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def bootstrap_aws
|
51
|
+
@workstation_passwd = wombat['workstations']['password']
|
52
|
+
rendered = ERB.new(File.read("#{conf['template_dir']}/bootstrap-aws.erb"), nil, '-').result(binding)
|
53
|
+
Dir.mkdir("#{conf['packer_dir']}/scripts", 0755) unless File.exist?("#{conf['packer_dir']}/scripts")
|
54
|
+
File.open("#{conf['packer_dir']}/scripts/bootstrap-aws.txt", 'w') { |file| file.puts rendered }
|
55
|
+
banner("Generated: #{conf['packer_dir']}/scripts/bootstrap-aws.txt")
|
56
|
+
end
|
57
|
+
|
58
|
+
def parse_log(log, cloud)
|
59
|
+
regex = case cloud
|
60
|
+
when 'gcp'
|
61
|
+
'A disk image was created:'
|
62
|
+
when 'azure'
|
63
|
+
|
64
|
+
if !wombat['azure'].key?('use_managed_disks') || !wombat['azure']['use_managed_disks']
|
65
|
+
'^OSDiskUri:'
|
66
|
+
else
|
67
|
+
'^ManagedDiskOSDiskUri:'
|
68
|
+
end
|
69
|
+
|
70
|
+
else
|
71
|
+
"#{wombat['aws']['region']}:"
|
72
|
+
end
|
73
|
+
|
74
|
+
File.read(log).split("\n").grep(/#{regex}/) {|x| x.split[1]}.last
|
75
|
+
end
|
76
|
+
|
77
|
+
def infranodes
|
78
|
+
unless wombat['infranodes'].nil?
|
79
|
+
wombat['infranodes'].sort
|
80
|
+
else
|
81
|
+
puts 'No infranodes listed in wombat.yml'
|
82
|
+
{}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def build_nodes
|
87
|
+
build_nodes = {}
|
88
|
+
1.upto(wombat['build-nodes']['count'].to_i) do |i|
|
89
|
+
build_nodes["build-node-#{i}"] = i
|
90
|
+
end
|
91
|
+
build_nodes
|
92
|
+
end
|
93
|
+
|
94
|
+
def workstations
|
95
|
+
workstations = {}
|
96
|
+
1.upto(wombat['workstations']['count'].to_i) do |i|
|
97
|
+
workstations["workstation-#{i}"] = i
|
98
|
+
end
|
99
|
+
workstations
|
100
|
+
end
|
101
|
+
|
102
|
+
def create_infranodes_json
|
103
|
+
infranodes_file_path = File.join(conf['files_dir'], 'infranodes-info.json')
|
104
|
+
if File.exists?(infranodes_file_path) && is_valid_json?(infranodes_file_path)
|
105
|
+
current_state = JSON(File.read(infranodes_file_path))
|
106
|
+
else
|
107
|
+
current_state = nil
|
108
|
+
end
|
109
|
+
return if current_state == infranodes # yay idempotence
|
110
|
+
File.open(infranodes_file_path, 'w') do |f|
|
111
|
+
f.puts JSON.pretty_generate(infranodes)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def linux
|
116
|
+
wombat['linux'].nil? ? 'ubuntu' : wombat['linux']
|
117
|
+
end
|
118
|
+
|
119
|
+
def conf
|
120
|
+
conf = wombat['conf']
|
121
|
+
conf ||= {}
|
122
|
+
conf['files_dir'] ||= 'files'
|
123
|
+
conf['key_dir'] ||= 'keys'
|
124
|
+
conf['cookbook_dir'] ||= 'cookbooks'
|
125
|
+
conf['packer_dir'] ||= 'packer'
|
126
|
+
conf['log_dir'] ||= 'logs'
|
127
|
+
conf['stack_dir'] ||= 'stacks'
|
128
|
+
conf['template_dir'] ||= 'templates'
|
129
|
+
conf['timeout'] ||= 7200
|
130
|
+
conf['audio'] ||= false
|
131
|
+
conf
|
132
|
+
end
|
133
|
+
|
134
|
+
def is_mac?
|
135
|
+
(/darwin/ =~ RUBY_PLATFORM) != nil
|
136
|
+
end
|
137
|
+
|
138
|
+
def audio?
|
139
|
+
is_mac? && conf['audio']
|
140
|
+
end
|
141
|
+
|
142
|
+
def logs
|
143
|
+
path = "#{conf['log_dir']}/#{cloud}*.log"
|
144
|
+
Dir.glob(path).reject { |l| !l.match(wombat['linux']) }
|
145
|
+
end
|
146
|
+
|
147
|
+
def calculate_templates
|
148
|
+
globs = "*.json"
|
149
|
+
Dir.chdir(conf['packer_dir']) do
|
150
|
+
Array(globs).
|
151
|
+
map { |glob| result = Dir.glob("#{glob}"); result.empty? ? glob : result }.
|
152
|
+
flatten.
|
153
|
+
sort.
|
154
|
+
delete_if { |file| file =~ /\.variables\./ }.
|
155
|
+
map { |template| template.sub(/\.json$/, '') }
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def update_lock(cloud)
|
160
|
+
copy = {}
|
161
|
+
copy = wombat
|
162
|
+
|
163
|
+
# Check that the copy contains a key for the named cloud
|
164
|
+
unless copy.key?(cloud)
|
165
|
+
throw "The Cloud '#{cloud}' is not specified in Wombat"
|
166
|
+
end
|
167
|
+
|
168
|
+
# Determine the region/location/zone for the specific cloud
|
169
|
+
case cloud
|
170
|
+
when 'aws'
|
171
|
+
region = copy['aws']['region']
|
172
|
+
when 'azure'
|
173
|
+
region = copy['azure']['location']
|
174
|
+
when 'gce'
|
175
|
+
region = copy['gce']['zone']
|
176
|
+
end
|
177
|
+
|
178
|
+
linux = copy['linux']
|
179
|
+
copy['amis'] = { region => {} }
|
180
|
+
|
181
|
+
if logs.length == 0
|
182
|
+
warn('No logs found - skipping lock update')
|
183
|
+
else
|
184
|
+
logs.each do |log|
|
185
|
+
case log
|
186
|
+
when /build-node/
|
187
|
+
copy['amis'][region]['build-node'] ||= {}
|
188
|
+
num = log.split('-')[3]
|
189
|
+
copy['amis'][region]['build-node'].store(num, parse_log(log, cloud))
|
190
|
+
when /workstation/
|
191
|
+
copy['amis'][region]['workstation'] ||= {}
|
192
|
+
num = log.split('-')[2]
|
193
|
+
copy['amis'][region]['workstation'].store(num, parse_log(log, cloud))
|
194
|
+
when /infranodes/
|
195
|
+
copy['amis'][region]['infranodes'] ||= {}
|
196
|
+
name = log.split('-')[2]
|
197
|
+
copy['amis'][region]['infranodes'].store(name, parse_log(log, cloud))
|
198
|
+
else
|
199
|
+
instance = log.match("#{cloud}-(.*)-#{linux}\.log")[1]
|
200
|
+
copy['amis'][region].store(instance, parse_log(log, cloud))
|
201
|
+
end
|
202
|
+
end
|
203
|
+
copy['last_updated'] = Time.now.gmtime.strftime('%Y%m%d%H%M%S')
|
204
|
+
banner('Updating wombat.lock')
|
205
|
+
File.open('wombat.lock', 'w') do |f|
|
206
|
+
f.write(JSON.pretty_generate(copy))
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def update_template(cloud)
|
212
|
+
if lock == 1
|
213
|
+
warn('No lock - skipping template creation')
|
214
|
+
else
|
215
|
+
|
216
|
+
@demo = lock['name']
|
217
|
+
@version = lock['version']
|
218
|
+
@ttl = lock['ttl']
|
219
|
+
|
220
|
+
# Determine the region/location/zone for the specific cloud
|
221
|
+
case cloud
|
222
|
+
when 'aws'
|
223
|
+
region = lock['aws']['region']
|
224
|
+
template_files = {
|
225
|
+
"cfn.json.erb": "#{conf['stack_dir']}/#{@demo}.json"
|
226
|
+
}
|
227
|
+
@chef_server_ami = lock['amis'][region]['chef-server']
|
228
|
+
@automate_ami = lock['amis'][region]['automate']
|
229
|
+
@compliance_ami = lock['amis'][region]['compliance']
|
230
|
+
@availability_zone = lock['aws']['az']
|
231
|
+
@iam_roles = lock['aws']['iam_roles']
|
232
|
+
when 'azure'
|
233
|
+
region = lock['azure']['location']
|
234
|
+
@storage_account = lock['azure']['storage_account']
|
235
|
+
|
236
|
+
template_files = {}
|
237
|
+
|
238
|
+
# determine whether to use VHD or Managed Disks
|
239
|
+
if !lock['azure'].key?('use_managed_disks') || !lock['azure']['use_managed_disks']
|
240
|
+
template_files['arm.vhd.json.erb'] = format("%s/%s.json", conf['stack_dir'], @demo)
|
241
|
+
else
|
242
|
+
template_files['arm.md.json.erb'] = format("%s/%s.json", conf['stack_dir'], @demo)
|
243
|
+
end
|
244
|
+
|
245
|
+
@chef_server_uri = lock['amis'][region]['chef-server']
|
246
|
+
@automate_uri = lock['amis'][region]['automate']
|
247
|
+
@compliance_uri = lock['amis'][region]['compliance']
|
248
|
+
@password = lock['workstations']['password']
|
249
|
+
@public_key = File.read("#{conf['key_dir']}/public.pub").chomp
|
250
|
+
|
251
|
+
# Set the Azure Tag used to identify Chef products in Azure
|
252
|
+
@chef_tag = azure_provider_tag
|
253
|
+
when 'gce'
|
254
|
+
region = lock['gce']['zone']
|
255
|
+
end
|
256
|
+
|
257
|
+
if lock['amis'][region].key?('build-node')
|
258
|
+
@build_nodes = lock['build-nodes']['count'].to_i
|
259
|
+
@build_node_ami = {}
|
260
|
+
1.upto(@build_nodes) do |i|
|
261
|
+
@build_node_ami[i] = lock['amis'][region]['build-node'][i.to_s]
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
@infra = {}
|
266
|
+
infranodes.each do |name, _rl|
|
267
|
+
@infra[name] = lock['amis'][region]['infranodes'][name]
|
268
|
+
end
|
269
|
+
|
270
|
+
if lock['amis'][region].key?('workstation')
|
271
|
+
@workstations = lock['workstations']['count'].to_i
|
272
|
+
@workstation_ami = {}
|
273
|
+
1.upto(@workstations) do |i|
|
274
|
+
@workstation_ami[i] = lock['amis'][region]['workstation'][i.to_s]
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
# Iterate around each of the template files that have been defined and render it
|
279
|
+
template_files.each do |template_file, destination|
|
280
|
+
rendered_cfn = ERB.new(File.read("#{conf['template_dir']}/#{template_file}"), nil, '-').result(binding)
|
281
|
+
Dir.mkdir(conf['stack_dir'], 0755) unless File.exist?(conf['stack_dir'])
|
282
|
+
File.open("#{destination}", 'w') { |file| file.puts rendered_cfn }
|
283
|
+
banner("Generated: #{destination}")
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def is_valid_json?(file)
|
289
|
+
begin
|
290
|
+
JSON.parse(file)
|
291
|
+
true
|
292
|
+
rescue JSON::ParserError => e
|
293
|
+
false
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
# Return the Azure Provider tag that should be applied to resource
|
298
|
+
def azure_provider_tag
|
299
|
+
"33194f91-eb5f-4110-827a-e95f640a9e46".upcase
|
300
|
+
end
|
301
|
+
|
302
|
+
# Connect to Azure using environment variables
|
303
|
+
#
|
304
|
+
#
|
305
|
+
def connect_azure
|
306
|
+
|
307
|
+
# Create the connection to Azure using the information in the environment variables
|
308
|
+
tenant_id = ENV['AZURE_TENANT_ID']
|
309
|
+
client_id = ENV['AZURE_CLIENT_ID']
|
310
|
+
client_secret = ENV['AZURE_CLIENT_SECRET']
|
311
|
+
|
312
|
+
token_provider = MsRestAzure::ApplicationTokenProvider.new(tenant_id, client_id, client_secret)
|
313
|
+
MsRest::TokenCredentials.new(token_provider)
|
314
|
+
end
|
315
|
+
|
316
|
+
# Track the progress of the deployment in Azure
|
317
|
+
#
|
318
|
+
# ===== Attributes
|
319
|
+
#
|
320
|
+
# * +rg_name+ - Name of the resource group being deployed to
|
321
|
+
# * +deployment_name+ - Name of the deployment that is currently being processed
|
322
|
+
def follow_azure_deployment(rg_name, deployment_name)
|
323
|
+
|
324
|
+
end_provisioning_states = 'Canceled,Failed,Deleted,Succeeded'
|
325
|
+
end_provisioning_state_reached = false
|
326
|
+
|
327
|
+
until end_provisioning_state_reached
|
328
|
+
list_outstanding_deployment_operations(rg_name, deployment_name)
|
329
|
+
sleep 10
|
330
|
+
deployment_provisioning_state = deployment_state(rg_name, deployment_name)
|
331
|
+
end_provisioning_state_reached = end_provisioning_states.split(',').include?(deployment_provisioning_state)
|
332
|
+
end
|
333
|
+
info format("Resource Template deployment reached end state of %s", deployment_provisioning_state)
|
334
|
+
end
|
335
|
+
|
336
|
+
# Get a list of the outstanding deployment operations
|
337
|
+
#
|
338
|
+
# ===== Attributes
|
339
|
+
#
|
340
|
+
# * +rg_name+ - Name of the resource group being deployed to
|
341
|
+
# * +deployment_name+ - Name of the deployment that is currently being processed
|
342
|
+
def list_outstanding_deployment_operations(rg_name, deployment_name)
|
343
|
+
end_operation_states = 'Failed,Succeeded'
|
344
|
+
deployment_operations = resource_management_client.deployment_operations.list(rg_name, deployment_name)
|
345
|
+
deployment_operations.each do |val|
|
346
|
+
resource_provisioning_state = val.properties.provisioning_state
|
347
|
+
unless val.properties.target_resource.nil?
|
348
|
+
resource_name = val.properties.target_resource.resource_name
|
349
|
+
resource_type = val.properties.target_resource.resource_type
|
350
|
+
end
|
351
|
+
end_operation_state_reached = end_operation_states.split(',').include?(resource_provisioning_state)
|
352
|
+
unless end_operation_state_reached
|
353
|
+
info format("resource %s '%s' provisioning status is %s", resource_type, resource_name, resource_provisioning_state)
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
# Get the state of the specified deployment
|
359
|
+
#
|
360
|
+
# ===== Attributes
|
361
|
+
#
|
362
|
+
# * +rg_name+ - Name of the resource group being deployed to
|
363
|
+
# * +deployment_name+ - Name of the deployment that is currently being processed
|
364
|
+
def deployment_state(rg_name, deployment_name)
|
365
|
+
deployments = resource_management_client.deployments.get(rg_name, deployment_name)
|
366
|
+
deployments.properties.provisioning_state
|
367
|
+
end
|
368
|
+
|
369
|
+
def create_resource_group(resource_management_client, name, location, owner = nil, rgtags = {})
|
370
|
+
|
371
|
+
# Check that the resource group exists
|
372
|
+
banner(format("Checking for resource group: %s", name))
|
373
|
+
status = resource_management_client.resource_groups.check_existence(name)
|
374
|
+
if status
|
375
|
+
puts "resource group already exists"
|
376
|
+
else
|
377
|
+
puts format("creating new resource group in '%s'", location)
|
378
|
+
|
379
|
+
# Set the parameters for the resource group
|
380
|
+
resource_group = Azure::ARM::Resources::Models::ResourceGroup.new
|
381
|
+
resource_group.location = location
|
382
|
+
|
383
|
+
# Create hash to be used as tags on the resource group
|
384
|
+
tags = {
|
385
|
+
owner: ENV['USER'],
|
386
|
+
provider: azure_provider_tag
|
387
|
+
}
|
388
|
+
|
389
|
+
# If an owner has been specified in the wombat file override the owner value
|
390
|
+
if !owner.nil?
|
391
|
+
tags[:owner] = owner
|
392
|
+
end
|
393
|
+
|
394
|
+
# Determine if there are any tags specified in the azure wmbat section that need to be added
|
395
|
+
if !rgtags.nil? && rgtags.length > 0
|
396
|
+
|
397
|
+
# Check to see if there are more than 15 tags in which case output a warning
|
398
|
+
if rgtags.length > 14
|
399
|
+
warn ('More than 15 tags have been specified, only the first 15 will be added. This is a restriction in Azure.')
|
400
|
+
end
|
401
|
+
|
402
|
+
# Iterate around the tags and add each one to the tags array, up to 15
|
403
|
+
rgtags.each_with_index do |(key, value), index|
|
404
|
+
tags[key] = value
|
405
|
+
|
406
|
+
if index == 12
|
407
|
+
break
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
end
|
412
|
+
|
413
|
+
# add the tags hash to the parameters
|
414
|
+
resource_group.tags = rgtags
|
415
|
+
|
416
|
+
# Create the resource group
|
417
|
+
resource_management_client.resource_groups.create_or_update(name, resource_group)
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
421
421
|
end
|