Module: Wombat::Common

Instance Method Summary collapse

Instance Method Details

#audio?Boolean

Returns:

  • (Boolean)


138
139
140
# File 'lib/wombat/common.rb', line 138

def audio?
  is_mac? && conf['audio']
end

#azure_provider_tagObject

Return the Azure Provider tag that should be applied to resource



298
299
300
# File 'lib/wombat/common.rb', line 298

def azure_provider_tag
  "33194f91-eb5f-4110-827a-e95f640a9e46".upcase
end


11
12
13
# File 'lib/wombat/common.rb', line 11

def banner(msg)
  puts "==> #{msg}"
end

#bootstrap_awsObject



50
51
52
53
54
55
56
# File 'lib/wombat/common.rb', line 50

def bootstrap_aws
  @workstation_passwd = wombat['workstations']['password']
  rendered = ERB.new(File.read("#{conf['template_dir']}/bootstrap-aws.erb"), nil, '-').result(binding)
  Dir.mkdir("#{conf['packer_dir']}/scripts", 0755) unless File.exist?("#{conf['packer_dir']}/scripts")
  File.open("#{conf['packer_dir']}/scripts/bootstrap-aws.txt", 'w') { |file| file.puts rendered }
  banner("Generated: #{conf['packer_dir']}/scripts/bootstrap-aws.txt")
end

#build_nodesObject



86
87
88
89
90
91
92
# File 'lib/wombat/common.rb', line 86

def build_nodes
  build_nodes = {}
  1.upto(wombat['build-nodes']['count'].to_i) do |i|
    build_nodes["build-node-#{i}"] = i
  end
  build_nodes
end

#calculate_templatesObject



147
148
149
150
151
152
153
154
155
156
157
# File 'lib/wombat/common.rb', line 147

def calculate_templates
globs = "*.json"
  Dir.chdir(conf['packer_dir']) do
    Array(globs).
      map { |glob| result = Dir.glob("#{glob}"); result.empty? ? glob : result }.
      flatten.
      sort.
      delete_if { |file| file =~ /\.variables\./ }.
      map { |template| template.sub(/\.json$/, '') }
  end
end

#confObject



119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/wombat/common.rb', line 119

def conf
  conf = wombat['conf']
  conf ||= {}
  conf['files_dir'] ||= 'files'
  conf['key_dir'] ||= 'keys'
  conf['cookbook_dir'] ||= 'cookbooks'
  conf['packer_dir'] ||= 'packer'
  conf['log_dir'] ||= 'logs'
  conf['stack_dir'] ||= 'stacks'
  conf['template_dir'] ||= 'templates'
  conf['timeout'] ||= 7200
  conf['audio'] ||= false
  conf
end

#connect_azureObject

Connect to Azure using environment variables



305
306
307
308
309
310
311
312
313
314
# File 'lib/wombat/common.rb', line 305

def connect_azure

    # Create the connection to Azure using the information in the environment variables
    tenant_id = ENV['AZURE_TENANT_ID']
    client_id = ENV['AZURE_CLIENT_ID']
    client_secret = ENV['AZURE_CLIENT_SECRET']

    token_provider = MsRestAzure::ApplicationTokenProvider.new(tenant_id, client_id, client_secret)
    MsRest::TokenCredentials.new(token_provider)
end

#create_infranodes_jsonObject



102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/wombat/common.rb', line 102

def create_infranodes_json
  infranodes_file_path = File.join(conf['files_dir'], 'infranodes-info.json')
  if File.exists?(infranodes_file_path) && is_valid_json?(infranodes_file_path)
    current_state = JSON(File.read(infranodes_file_path))
  else
    current_state = nil
  end
  return if current_state == infranodes # yay idempotence
  File.open(infranodes_file_path, 'w') do |f|
    f.puts JSON.pretty_generate(infranodes)
  end
end

#create_resource_group(resource_management_client, name, location, owner = nil, rgtags = {}) ⇒ Object



369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
# File 'lib/wombat/common.rb', line 369

def create_resource_group(resource_management_client, name, location, owner = nil, rgtags = {})

  # Check that the resource group exists
  banner(format("Checking for resource group: %s", name))
  status = resource_management_client.resource_groups.check_existence(name)
  if status
    puts "resource group already exists"
  else
    puts format("creating new resource group in '%s'", location)

    # Set the parameters for the resource group
    resource_group = Azure::ARM::Resources::Models::ResourceGroup.new
    resource_group.location = location

    # Create hash to be used as tags on the resource group
    tags = {
      owner: ENV['USER'],
      provider: azure_provider_tag
    }

    # If an owner has been specified in the wombat file override the owner value
    if !owner.nil?
      tags[:owner] = owner
    end

    # Determine if there are any tags specified in the azure wmbat section that need to be added
    if !rgtags.nil? && rgtags.length > 0

      # Check to see if there are more than 15 tags in which case output a warning
      if rgtags.length > 14
        warn ('More than 15 tags have been specified, only the first 15 will be added.  This is a restriction in Azure.')
      end

      # Iterate around the tags and add each one to the tags array, up to 15
      rgtags.each_with_index do |(key, value), index|
        tags[key] = value

        if index == 12
          break
        end
      end

    end

    # add the tags hash to the parameters
    resource_group.tags = rgtags

    # Create the resource group
    resource_management_client.resource_groups.create_or_update(name, resource_group)
  end    
end

#deployment_state(rg_name, deployment_name) ⇒ Object

Get the state of the specified deployment

Attributes
  • rg_name - Name of the resource group being deployed to

  • deployment_name - Name of the deployment that is currently being processed



364
365
366
367
# File 'lib/wombat/common.rb', line 364

def deployment_state(rg_name, deployment_name)
  deployments = resource_management_client.deployments.get(rg_name, deployment_name)
  deployments.properties.provisioning_state
end

#duration(total) ⇒ Object



23
24
25
26
27
28
# File 'lib/wombat/common.rb', line 23

def duration(total)
  total = 0 if total.nil?
  minutes = (total / 60).to_i
  seconds = (total - (minutes * 60))
  format("%dm%.2fs", minutes, seconds)
end

#follow_azure_deployment(rg_name, deployment_name) ⇒ Object

Track the progress of the deployment in Azure

Attributes
  • rg_name - Name of the resource group being deployed to

  • deployment_name - Name of the deployment that is currently being processed



322
323
324
325
326
327
328
329
330
331
332
333
334
# File 'lib/wombat/common.rb', line 322

def follow_azure_deployment(rg_name, deployment_name)

  end_provisioning_states = 'Canceled,Failed,Deleted,Succeeded'
  end_provisioning_state_reached = false

  until end_provisioning_state_reached
    list_outstanding_deployment_operations(rg_name, deployment_name)
    sleep 10
    deployment_provisioning_state = deployment_state(rg_name, deployment_name)
    end_provisioning_state_reached = end_provisioning_states.split(',').include?(deployment_provisioning_state)
  end
  info format("Resource Template deployment reached end state of %s", deployment_provisioning_state)
end

#info(msg) ⇒ Object



15
16
17
# File 'lib/wombat/common.rb', line 15

def info(msg)
  puts "    #{msg}"
end

#infranodesObject



77
78
79
80
81
82
83
84
# File 'lib/wombat/common.rb', line 77

def infranodes
  unless wombat['infranodes'].nil?
    wombat['infranodes'].sort
  else
    puts 'No infranodes listed in wombat.yml'
    {}
  end
end

#is_mac?Boolean

Returns:

  • (Boolean)


134
135
136
# File 'lib/wombat/common.rb', line 134

def is_mac?
  (/darwin/ =~ RUBY_PLATFORM) != nil
end

#is_valid_json?(file) ⇒ Boolean

Returns:

  • (Boolean)


288
289
290
291
292
293
294
295
# File 'lib/wombat/common.rb', line 288

def is_valid_json?(file)
  begin
    JSON.parse(file)
    true
  rescue JSON::ParserError => e
    false
  end
end

#linuxObject



115
116
117
# File 'lib/wombat/common.rb', line 115

def linux
  wombat['linux'].nil? ? 'ubuntu' : wombat['linux']
end

#list_outstanding_deployment_operations(rg_name, deployment_name) ⇒ Object

Get a list of the outstanding deployment operations

Attributes
  • rg_name - Name of the resource group being deployed to

  • deployment_name - Name of the deployment that is currently being processed



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/wombat/common.rb', line 342

def list_outstanding_deployment_operations(rg_name, deployment_name)
  end_operation_states = 'Failed,Succeeded'
  deployment_operations = resource_management_client.deployment_operations.list(rg_name, deployment_name)
  deployment_operations.each do |val|
    resource_provisioning_state = val.properties.provisioning_state
    unless val.properties.target_resource.nil?
      resource_name = val.properties.target_resource.resource_name
      resource_type = val.properties.target_resource.resource_type
    end
    end_operation_state_reached = end_operation_states.split(',').include?(resource_provisioning_state)
    unless end_operation_state_reached
      info format("resource %s '%s' provisioning status is %s", resource_type, resource_name, resource_provisioning_state)
    end
  end
end

#lockObject



41
42
43
44
45
46
47
48
# File 'lib/wombat/common.rb', line 41

def lock
  if !File.exist?('wombat.lock')
    warn('No wombat.lock found')
    return 1
  else
    JSON.parse(File.read('wombat.lock'))
  end
end

#logsObject



142
143
144
145
# File 'lib/wombat/common.rb', line 142

def logs
  path = "#{conf['log_dir']}/#{cloud}*.log"
  Dir.glob(path).reject { |l| !l.match(wombat['linux']) }
end

#parse_log(log, cloud) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/wombat/common.rb', line 58

def parse_log(log, cloud)
  regex = case cloud
          when 'gcp'
            'A disk image was created:'
          when 'azure'

            if !wombat['azure'].key?('use_managed_disks') || !wombat['azure']['use_managed_disks']
              '^OSDiskUri:'
            else
              '^ManagedDiskOSDiskUri:'
            end

          else
            "#{wombat['aws']['region']}:"
          end

  File.read(log).split("\n").grep(/#{regex}/) {|x| x.split[1]}.last
end

#update_lock(cloud) ⇒ Object



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/wombat/common.rb', line 159

def update_lock(cloud)
  copy = {}
  copy = wombat

  # Check that the copy contains a key for the named cloud
  unless copy.key?(cloud)
    throw "The Cloud '#{cloud}' is not specified in Wombat"
  end

  # Determine the region/location/zone for the specific cloud
  case cloud
  when 'aws'
    region = copy['aws']['region']
  when 'azure'
    region = copy['azure']['location']
  when 'gce'
    region = copy['gce']['zone']
  end

  linux = copy['linux']
  copy['amis'] = { region => {} }

  if logs.length == 0
    warn('No logs found - skipping lock update')
  else
    logs.each do |log|
      case log
      when /build-node/
        copy['amis'][region]['build-node'] ||= {}
        num = log.split('-')[3]
        copy['amis'][region]['build-node'].store(num, parse_log(log, cloud))
      when /workstation/
        copy['amis'][region]['workstation'] ||= {}
        num = log.split('-')[2]
        copy['amis'][region]['workstation'].store(num, parse_log(log, cloud))
      when /infranodes/
        copy['amis'][region]['infranodes'] ||= {}
        name = log.split('-')[2]
        copy['amis'][region]['infranodes'].store(name, parse_log(log, cloud))
      else
        instance = log.match("#{cloud}-(.*)-#{linux}\.log")[1]
        copy['amis'][region].store(instance, parse_log(log, cloud))
      end
    end
    copy['last_updated'] = Time.now.gmtime.strftime('%Y%m%d%H%M%S')
    banner('Updating wombat.lock')
    File.open('wombat.lock', 'w') do |f|
      f.write(JSON.pretty_generate(copy))
    end
  end
end

#update_template(cloud) ⇒ Object



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/wombat/common.rb', line 211

def update_template(cloud)
  if lock == 1
    warn('No lock - skipping template creation')
  else

    @demo = lock['name']
    @version = lock['version']
    @ttl = lock['ttl']

    # Determine the region/location/zone for the specific cloud
    case cloud
    when 'aws'
      region = lock['aws']['region']
      template_files = {
        "cfn.json.erb": "#{conf['stack_dir']}/#{@demo}.json"
      }
      @chef_server_ami = lock['amis'][region]['chef-server']
      @automate_ami = lock['amis'][region]['automate']
      @compliance_ami = lock['amis'][region]['compliance']
      @availability_zone = lock['aws']['az']
      @iam_roles = lock['aws']['iam_roles']
    when 'azure'
      region = lock['azure']['location']
      @storage_account = lock['azure']['storage_account']

      template_files = {}

      # determine whether to use VHD or Managed Disks
      if !lock['azure'].key?('use_managed_disks') || !lock['azure']['use_managed_disks']
        template_files['arm.vhd.json.erb'] = format("%s/%s.json", conf['stack_dir'], @demo)
      else
        template_files['arm.md.json.erb'] = format("%s/%s.json", conf['stack_dir'], @demo)
      end

      @chef_server_uri = lock['amis'][region]['chef-server']
      @automate_uri = lock['amis'][region]['automate']
      @compliance_uri = lock['amis'][region]['compliance']
      @password = lock['workstations']['password']
      @public_key = File.read("#{conf['key_dir']}/public.pub").chomp

      # Set the Azure Tag used to identify Chef products in Azure
      @chef_tag = azure_provider_tag
    when 'gce'
      region = lock['gce']['zone']
    end

    if lock['amis'][region].key?('build-node')
      @build_nodes = lock['build-nodes']['count'].to_i
      @build_node_ami = {}
      1.upto(@build_nodes) do |i|
        @build_node_ami[i] = lock['amis'][region]['build-node'][i.to_s]
      end
    end

    @infra = {}
    infranodes.each do |name, _rl|
      @infra[name] = lock['amis'][region]['infranodes'][name]
    end

    if lock['amis'][region].key?('workstation')
      @workstations = lock['workstations']['count'].to_i
      @workstation_ami = {}
      1.upto(@workstations) do |i|
        @workstation_ami[i] = lock['amis'][region]['workstation'][i.to_s]
      end
    end

    # Iterate around each of the template files that have been defined and render it
    template_files.each do |template_file, destination|
      rendered_cfn = ERB.new(File.read("#{conf['template_dir']}/#{template_file}"), nil, '-').result(binding)
      Dir.mkdir(conf['stack_dir'], 0755) unless File.exist?(conf['stack_dir'])
      File.open("#{destination}", 'w') { |file| file.puts rendered_cfn }
      banner("Generated: #{destination}")
    end
  end
end

#warn(msg) ⇒ Object



19
20
21
# File 'lib/wombat/common.rb', line 19

def warn(msg)
  puts ">>> #{msg}"
end

#wombatObject



30
31
32
33
34
35
36
37
38
39
# File 'lib/wombat/common.rb', line 30

def wombat
  @wombat_yml ||= ENV['WOMBAT_YML'] unless ENV['WOMBAT_YML'].nil?
  @wombat_yml ||= 'wombat.yml'
  if !File.exist?(@wombat_yml)
    warn('No wombat.yml found, copying example')
    gen_dir = "#{File.expand_path("../..", File.dirname(__FILE__))}/generator_files"
    FileUtils.cp_r "#{gen_dir}/wombat.yml", Dir.pwd
  end
  YAML.load(File.read(@wombat_yml))
end

#workstationsObject



94
95
96
97
98
99
100
# File 'lib/wombat/common.rb', line 94

def workstations
  workstations = {}
  1.upto(wombat['workstations']['count'].to_i) do |i|
    workstations["workstation-#{i}"] = i
  end
  workstations
end