elevage 0.1.0 → 0.1.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/README.md +33 -11
  4. data/elevage.gemspec +15 -15
  5. data/features/archive +3 -22
  6. data/features/build.feature +2 -1
  7. data/features/generate.feature +3 -9
  8. data/features/health_env_failure.feature +2 -8
  9. data/features/health_failure.feature +1 -2
  10. data/features/health_success.feature +3 -9
  11. data/features/list.feature +3 -9
  12. data/features/step_definitions/elevage_steps.rb +2 -2
  13. data/lib/elevage/build.rb +4 -1
  14. data/lib/elevage/constants.rb +2 -2
  15. data/lib/elevage/environment.rb +66 -39
  16. data/lib/elevage/generate.rb +4 -3
  17. data/lib/elevage/health.rb +4 -0
  18. data/lib/elevage/new.rb +4 -2
  19. data/lib/elevage/platform.rb +11 -3
  20. data/lib/elevage/provisioner.rb +61 -21
  21. data/lib/elevage/provisionerrunqueue.rb +25 -10
  22. data/lib/elevage/templates/vcenter.yml.tt +7 -12
  23. data/lib/elevage/version.rb +1 -1
  24. data/lib/elevage.rb +5 -0
  25. metadata +58 -88
  26. data/.ruby-version +0 -1
  27. data/.yardoc/checksums +0 -12
  28. data/.yardoc/object_types +0 -0
  29. data/.yardoc/objects/root.dat +0 -0
  30. data/.yardoc/proxy_types +0 -0
  31. data/doc/Elevage/Build.html +0 -435
  32. data/doc/Elevage/CLI.html +0 -282
  33. data/doc/Elevage/Environment.html +0 -950
  34. data/doc/Elevage/Generate.html +0 -346
  35. data/doc/Elevage/Health.html +0 -359
  36. data/doc/Elevage/New.html +0 -411
  37. data/doc/Elevage/Platform.html +0 -1119
  38. data/doc/Elevage/Provisioner.html +0 -804
  39. data/doc/Elevage/ProvisionerRunQueue.html +0 -765
  40. data/doc/Elevage/Runner.html +0 -319
  41. data/doc/Elevage.html +0 -501
  42. data/doc/_index.html +0 -239
  43. data/doc/class_list.html +0 -58
  44. data/doc/css/common.css +0 -1
  45. data/doc/css/full_list.css +0 -57
  46. data/doc/css/style.css +0 -339
  47. data/doc/file.README.html +0 -187
  48. data/doc/file_list.html +0 -60
  49. data/doc/frames.html +0 -26
  50. data/doc/index.html +0 -187
  51. data/doc/js/app.js +0 -219
  52. data/doc/js/full_list.js +0 -181
  53. data/doc/js/jquery.js +0 -4
  54. data/doc/method_list.html +0 -369
  55. data/doc/top-level-namespace.html +0 -112
@@ -5,6 +5,8 @@ require 'English'
5
5
 
6
6
  module Elevage
7
7
  # Platform class
8
+ #
9
+ # This represents the overall description of the platform
8
10
  class Platform
9
11
  attr_accessor :name, :description
10
12
  attr_accessor :environments
@@ -17,6 +19,9 @@ module Elevage
17
19
  attr_accessor :compute
18
20
 
19
21
  # rubocop:disable MethodLength
22
+
23
+ # Create a new platform object
24
+ # @return [Elevage::Platform]
20
25
  def initialize
21
26
  fail unless platform_files_exists?
22
27
  platform = YAML.load_file(YML_PLATFORM).fetch('platform')
@@ -34,6 +39,9 @@ module Elevage
34
39
  # rubocop:enable MethodLength
35
40
 
36
41
  # rubocop:disable MethodLength, LineLength, CyclomaticComplexity, PerceivedComplexity, AmbiguousOperator
42
+
43
+ # Determine whether the platform definition is considered correct
44
+ # return [Boolean]
37
45
  def healthy?
38
46
  health = ''
39
47
  # Array of string checked for empty values
@@ -60,7 +68,7 @@ module Elevage
60
68
  health += MSG[:invalid_destfolder] if v['destfolder'].nil?
61
69
  health += MSG[:invalid_appendenv] unless v['appendenv'] == true || v['appendenv'] == false
62
70
  health += MSG[:invalid_appenddomain] unless v['appenddomain'] == true || v['appenddomain'] == false
63
- health += MSG[:empty_datastores] unless v['datastores'].all?
71
+ health += MSG[:empty_datastores] if v['datastore'].nil?
64
72
  health += MSG[:invalid_domain] if v['domain'].nil?
65
73
  v['dnsips'].each { |ip| health += MSG[:invalid_ip] unless Resolv::IPv4::Regex.match(ip) }
66
74
  end
@@ -85,8 +93,8 @@ module Elevage
85
93
 
86
94
  private
87
95
 
88
- # Private: confirms existence of the standard platform definition files
89
- # Returns true if all standard files present
96
+ # Confirm existence of the standard platform definition files
97
+ # @return [Boolean] True if all standard files present
90
98
  def platform_files_exists?
91
99
  fail(IOError, ERR[:no_platform_file]) unless File.file?(YML_PLATFORM)
92
100
  fail(IOError, ERR[:no_vcenter_file]) unless File.file?(YML_VCENTER)
@@ -5,7 +5,10 @@ require_relative 'platform'
5
5
  require_relative 'provisionerrunqueue'
6
6
 
7
7
  module Elevage
8
- # Provisioner class
8
+ # rubocop:disable ClassLength
9
+
10
+ # Provisioner is responsible for the actual execution of the commands to
11
+ # create the requested virtual machine.
9
12
  class Provisioner
10
13
  attr_accessor :name
11
14
  attr_accessor :component
@@ -13,7 +16,14 @@ module Elevage
13
16
  attr_accessor :environment
14
17
  attr_accessor :vcenter
15
18
 
16
- # Set us up to build the specified instance of component
19
+ # Create the Provisioner object for the requested virtual machine
20
+ # @param [String] name Name of the node to create
21
+ # @param [String] component Name of the platform component node is part of
22
+ # @param [String] instance Number representing which instance of
23
+ # `component` this node is
24
+ # @param [String] environment Name of the environnment we're provisioning to
25
+ # @param [String] options Thor `options` hash
26
+ # @return [Elevage::Provisioner]
17
27
  def initialize(name, component, instance, environment, options)
18
28
  @name = name
19
29
  @component = component
@@ -23,6 +33,8 @@ module Elevage
23
33
  @vcenter = @environment.vcenter
24
34
  end
25
35
 
36
+ # Stringify the Provisioner
37
+ # @return [String]
26
38
  def to_s
27
39
  puts "Name: #{@name}"
28
40
  puts "Instance: #{@instance}"
@@ -32,8 +44,9 @@ module Elevage
32
44
  puts @environment.to_yaml
33
45
  end
34
46
 
35
- # Public: Build the node
36
- # rubocop:disable MethodLength, LineLength, GlobalVars, CyclomaticComplexity
47
+ # rubocop:disable MethodLength, CyclomaticComplexity
48
+
49
+ # Build the the virtual machine
37
50
  def build
38
51
  knife_cmd = generate_knife_cmd
39
52
 
@@ -60,12 +73,18 @@ module Elevage
60
73
  # err_thread = Thread.new do
61
74
  Thread.new do
62
75
  while (line = stderr.gets)
63
- sem.synchronize { logfile.puts line }
76
+ sem.synchronize do
77
+ logfile.puts line
78
+ logfile.sync
79
+ end
64
80
  end
65
81
  end
66
82
  out_thread = Thread.new do
67
83
  while (line = stdout.gets)
68
- sem.synchronize { logfile.puts line }
84
+ sem.synchronize do
85
+ logfile.puts line
86
+ logfile.sync
87
+ end
69
88
  end
70
89
  end
71
90
  out_thread.join
@@ -80,22 +99,41 @@ module Elevage
80
99
  # exit status is a failure, and the details will be in the logfile
81
100
  status.exitstatus == 0 ? true : false
82
101
  end
83
- # rubocop:enable MethodLength, LineLength, GlobalVars, CyclomaticComplexity
102
+ # rubocop:enable MethodLength, CyclomaticComplexity
84
103
 
85
104
  private
86
105
 
87
- # Private: Determine which datastore to use for this specific
88
- # provisioning.
106
+ # rubocop:disable LineLength
107
+
108
+ # Private
109
+ #
110
+ # Determine which datastore to use for this specific provisioning.
111
+ # @return [String] Name of datastore
89
112
  def select_datastore
90
- if @options['dry-run']
91
- @vcenter['datastores'][0]
92
- else
93
- @vcenter['datastores'][rand(@vcenter['datastores'].count)]
94
- end
113
+ knife_cmd = 'knife vsphere datastore maxfree --vsinsecure'
114
+
115
+ # Authentication and host
116
+ knife_cmd << " --vsuser #{@options[:vsuser]}"
117
+ knife_cmd << " --vspass #{@options[:vspass]}"
118
+ knife_cmd << " --vshost #{@vcenter['host']}"
119
+
120
+ # vSphere destination information (where the clone will end up)
121
+ knife_cmd << " --vsdc '#{@vcenter['datacenter']}'"
122
+
123
+ # datastore prefix
124
+ knife_cmd << " --regex #{@vcenter['datastore']}"
125
+
126
+ # get result and clean up
127
+ @options['dry-run'] ? @vcenter['datastore'] : `#{knife_cmd}`.to_s.gsub!("\n", '')
95
128
  end
129
+ # rubocop:enable LineLength
96
130
 
97
- # Private: Build the knife command that will do the provisioning.
98
131
  # rubocop:disable MethodLength, LineLength
132
+
133
+ # Private
134
+ #
135
+ # Build the knife command that will do the provisioning.
136
+ # @return [String] knife command line string
99
137
  def generate_knife_cmd
100
138
  knife_cmd = 'knife vsphere vm clone --vsinsecure --start'
101
139
 
@@ -110,7 +148,9 @@ module Elevage
110
148
 
111
149
  # vSphere destination information (where the clone will end up)
112
150
  knife_cmd << " --vsdc '#{@vcenter['datacenter']}'"
113
- knife_cmd << " --dest-folder '#{@vcenter['destfolder']}'"
151
+ knife_cmd << " --dest-folder '#{@vcenter['destfolder']}"
152
+ knife_cmd << "/#{@component['tier']}" if @vcenter['appendtier']
153
+ knife_cmd << '\''
114
154
  knife_cmd << " --resource-pool '#{@vcenter['resourcepool']}'"
115
155
  knife_cmd << " --datastore '#{select_datastore}'"
116
156
 
@@ -143,7 +183,6 @@ module Elevage
143
183
 
144
184
  # What the node should be identified as in Chef
145
185
  nodename = String.new(@name)
146
- nodename << '.' << @environment.name if @vcenter['appendenv']
147
186
  nodename << @vcenter['domain'] if @vcenter['appenddomain']
148
187
  knife_cmd << " --node-name '#{nodename}'"
149
188
 
@@ -153,17 +192,18 @@ module Elevage
153
192
  # Assign the Chef environment
154
193
  knife_cmd << " --environment '#{@environment.name}'"
155
194
 
156
- # What version of chef-client are we bootstrapping (not sure this is necessary)
195
+ # What version of chef-client are we bootstrapping (not sure
196
+ # this is necessary)
157
197
  knife_cmd << " --bootstrap-version #{@options['bootstrap-version']}"
158
198
 
159
199
  # Finally, the name of the VM as seen by vSphere.
160
- # Whereas nodename will optionally append the domain name, VM names should *always* have the domain name
161
- # appended. The only optional bit is including the chef environment in the name.
200
+ # Whereas nodename will optionally append the domain name, VM names
201
+ # should *always* have the domain name appended.
162
202
  vmname = String.new(@name)
163
- vmname << '.' << @environment.name if @vcenter['appendenv']
164
203
  vmname << @vcenter['domain']
165
204
  knife_cmd << " #{vmname}"
166
205
  end
167
206
  # rubocop:enable MethodLength, LineLength
168
207
  end
208
+ # rubocop:enable ClassLength
169
209
  end
@@ -1,7 +1,9 @@
1
1
  require_relative 'constants'
2
2
 
3
3
  module Elevage
4
- # ProvisionerRunQueue class
4
+ # ProvisionerRunQueue
5
+ #
6
+ # Manage multiple queued and running `Elevage::Provisioner` objects/threads
5
7
  class ProvisionerRunQueue
6
8
  attr_reader :running_tasks
7
9
  attr_accessor :provisioners
@@ -9,7 +11,8 @@ module Elevage
9
11
  attr_accessor :busy_wait_timeout
10
12
  attr_accessor :build_status_interval
11
13
 
12
- # Public: Initialize the object
14
+ # Create a new run queue
15
+ # @return [Elevage::ProvisionerRunQueue]
13
16
  def initialize
14
17
  @running_tasks = 0 # We start out with nothing running
15
18
  @max_concurrent = BUILD_CONCURRENT_DEFAULT
@@ -19,8 +22,9 @@ module Elevage
19
22
  @children = {}
20
23
  end
21
24
 
22
- # Public: run() - Process the queue
23
25
  # rubocop:disable MethodLength
26
+
27
+ # Process the queue
24
28
  def run
25
29
  puts "#{Time.now} [#{$$}]: Provisioning started."
26
30
  @provisioners.each do |provisioner|
@@ -38,8 +42,10 @@ module Elevage
38
42
  end
39
43
  # rubocop:enable MethodLength
40
44
 
41
- # Public: Display a string representation
42
45
  # rubocop:disable MethodLength
46
+
47
+ # Display a string representation
48
+ # @return [String]
43
49
  def to_s
44
50
  puts "Running Tasks: #{@running_tasks}"
45
51
  puts "Max Concurrency: #{@max_concurrent}"
@@ -57,9 +63,13 @@ module Elevage
57
63
 
58
64
  private
59
65
 
60
- # rubocop:disable LineLength, GlobalVars
61
- # Private: provision_task is the method that should execute in the child
66
+ # rubocop:disable LineLength
67
+
68
+ # Private
69
+ #
70
+ # provision_task is the method that should execute in the child
62
71
  # process, and contain all the logic for the child process.
72
+ # @param [Elevage::Provisioner] task a Provisioner from the queue
63
73
  def provision_task(task: nil)
64
74
  start_time = Time.now
65
75
  print "#{Time.now} [#{$$}]: #{task.name} Provisioning...\n"
@@ -67,15 +77,20 @@ module Elevage
67
77
  run_time = Time.now - start_time
68
78
  print "#{Time.now} [#{$$}]: #{task.name} #{status} in #{run_time.round(2)} seconds.\n"
69
79
  end
70
- # rubocop:enable LineLength, GlobalVars
80
+ # rubocop:enable LineLength
81
+
82
+ # rubocop:disable MethodLength, LineLength, CyclomaticComplexity
71
83
 
72
- # rubocop:disable MethodLength, LineLength, CyclomaticComplexity, GlobalVars
73
- # Private: Wait for child tasks to return
84
+ # Private
85
+ #
86
+ # Wait for child tasks to return
74
87
  # Since our trap for SIGCHLD will clean up the @running_tasks count and
75
88
  # the children hash, here we can just keep checking until @running_tasks
76
89
  # is 0.
77
90
  # If we've been waiting at least a minute, print out a notice of what
78
91
  # we're still waiting for.
92
+ # @param [String] state Disposition; whether we are still `:running` or
93
+ # are trying to `:collect` still-running tasks
79
94
  def wait_for_tasks(state: :running)
80
95
  i = interval = @build_status_interval / @busy_wait_timeout
81
96
 
@@ -109,6 +124,6 @@ module Elevage
109
124
  sleep @busy_wait_timeout
110
125
  end
111
126
  end
112
- # rubocop:enable MethodLength, LineLength, CyclomaticComplexity, GlobalVars
127
+ # rubocop:enable MethodLength, LineLength, CyclomaticComplexity
113
128
  end
114
129
  end
@@ -13,7 +13,7 @@ vcenter:
13
13
  # resourcepool vcenter resource pool for the provisioned vms
14
14
  # appendenv if true the destfolder path will be appended with the environment name
15
15
  # appenddomain if true the domain will be pre-pended with environment name
16
- # datastores array list of available datastores. Elevage will attempt to evenly distribute
16
+ # datastore prefix of desired datastorecluster. knife vsphere datastorecluster maxfree will determine
17
17
  # domain domain for fqdn of host
18
18
  # dnsips ips of dns servers
19
19
  #
@@ -31,11 +31,8 @@ vcenter:
31
31
  # resourcepool: 'App-Web Linux/Corporate'
32
32
  # appendenv: true
33
33
  # appenddomain: true
34
- # datastores:
35
- # - NonProd_Cor_25
36
- # - NonProd_Cor_26
37
- # - NonProd_Cor_38
38
- # - NonProd_Cor_39
34
+ # appendtier: false
35
+ # datastore: 'NonProd_Cor_PlaSer'
39
36
  #
40
37
  # domain: dev.corp.local
41
38
  # dnsips:
@@ -46,9 +43,7 @@ vcenter:
46
43
  # <<: *vcenter
47
44
  #
48
45
  # datacenter: 'WCDC Prod'
49
- # datastores:
50
- # - Prod_Cor_03
51
- # - Prod_Cor_04
46
+ # datastore: 'Prod_Cor_PlaSer'
52
47
  #
53
48
  # domain: corp.local
54
49
  # dnsips:
@@ -66,12 +61,12 @@ vcenter:
66
61
  resourcepool:
67
62
  appendenv: false
68
63
  appenddomain: false
69
- datastores:
70
- -
64
+ appendtier: false
65
+ datastore:
71
66
 
72
67
  domain:
73
68
  dnsips:
74
69
  -
75
70
 
76
71
  prod:
77
- <<: *default
72
+ <<: *default
@@ -1,4 +1,4 @@
1
1
  # simple gem version number tracking
2
2
  module Elevage
3
- VERSION = '0.1.0'
3
+ VERSION = '0.1.2'
4
4
  end
data/lib/elevage.rb CHANGED
@@ -17,13 +17,17 @@ module Elevage
17
17
  map '-v' => :version
18
18
 
19
19
  desc 'version', DESC_VERSION
20
+ # Display the version
20
21
  def version
21
22
  puts VERSION
22
23
  end
23
24
 
24
25
  desc 'list ITEM', DESC_LIST
25
26
  method_option :nodes, aliases: '-n', desc: DESC_LIST_NODES
27
+
26
28
  # rubocop:disable LineLength
29
+
30
+ # Process the `list` command
27
31
  def list(item)
28
32
  # errors handled in class methods
29
33
  if LIST_CMDS.include?(item)
@@ -34,6 +38,7 @@ module Elevage
34
38
  puts options[:nodes] ? environment.list_nodes : environment
35
39
  end
36
40
  end
41
+
37
42
  # rubocop:enable LineLength
38
43
 
39
44
  # subcommand in Thor called as registered class