spiceweasel 2.6.0 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (180) hide show
  1. checksums.yaml +6 -14
  2. data/README.md +33 -9
  3. data/bin/spiceweasel +3 -3
  4. data/lib/spiceweasel.rb +3 -2
  5. data/lib/spiceweasel/berksfile.rb +6 -10
  6. data/lib/spiceweasel/cli.rb +174 -129
  7. data/lib/spiceweasel/clusters.rb +31 -23
  8. data/lib/spiceweasel/command.rb +23 -5
  9. data/lib/spiceweasel/command_helper.rb +36 -2
  10. data/lib/spiceweasel/config.rb +4 -3
  11. data/lib/spiceweasel/cookbooks.rb +76 -57
  12. data/lib/spiceweasel/data_bags.rb +100 -64
  13. data/lib/spiceweasel/environments.rb +78 -56
  14. data/lib/spiceweasel/execute.rb +6 -7
  15. data/lib/spiceweasel/extract_local.rb +59 -46
  16. data/lib/spiceweasel/knife.rb +21 -20
  17. data/lib/spiceweasel/log.rb +5 -5
  18. data/lib/spiceweasel/nodes.rb +261 -193
  19. data/lib/spiceweasel/roles.rb +107 -76
  20. data/lib/spiceweasel/version.rb +5 -3
  21. data/test/chef-repo/Berksfile +4 -0
  22. data/test/chef-repo/Berksfile.lock +10 -0
  23. data/test/chef-repo/PASSWORD +1 -0
  24. data/test/chef-repo/cookbooks/abc/CHANGELOG.md +13 -0
  25. data/test/chef-repo/cookbooks/abc/README.md +68 -0
  26. data/test/chef-repo/cookbooks/abc/metadata.rb +7 -0
  27. data/test/chef-repo/cookbooks/abc/recipes/default.rb +8 -0
  28. data/test/chef-repo/cookbooks/def/CHANGELOG.md +13 -0
  29. data/test/chef-repo/cookbooks/def/README.md +68 -0
  30. data/test/chef-repo/cookbooks/def/metadata.rb +7 -0
  31. data/test/chef-repo/cookbooks/def/recipes/default.rb +8 -0
  32. data/test/chef-repo/cookbooks/fail1/README.md +68 -0
  33. data/test/chef-repo/cookbooks/fail1/metadata.rb +9 -0
  34. data/test/chef-repo/cookbooks/fail2/README.md +68 -0
  35. data/test/chef-repo/cookbooks/fail2/metadata.rb +9 -0
  36. data/test/chef-repo/cookbooks/fail3/README.md +68 -0
  37. data/test/chef-repo/cookbooks/fail3/metadata.rb +9 -0
  38. data/test/chef-repo/cookbooks/ghi/CHANGELOG.md +13 -0
  39. data/test/chef-repo/cookbooks/ghi/README.md +68 -0
  40. data/test/chef-repo/cookbooks/ghi/metadata.rb +7 -0
  41. data/test/chef-repo/cookbooks/ghi/recipes/default.rb +8 -0
  42. data/test/chef-repo/cookbooks/jkl/CHANGELOG.md +13 -0
  43. data/test/chef-repo/cookbooks/jkl/README.md +68 -0
  44. data/test/chef-repo/cookbooks/jkl/metadata.rb +10 -0
  45. data/test/chef-repo/cookbooks/jkl/recipes/default.rb +8 -0
  46. data/test/chef-repo/cookbooks/mno/CHANGELOG.md +13 -0
  47. data/test/chef-repo/cookbooks/mno/README.md +68 -0
  48. data/test/chef-repo/cookbooks/mno/metadata.rb +9 -0
  49. data/test/chef-repo/cookbooks/mno/recipes/default.rb +8 -0
  50. data/test/chef-repo/data_bags/junk/README +1 -0
  51. data/test/chef-repo/data_bags/junk/abc.json +4 -0
  52. data/test/chef-repo/data_bags/junk/ade.json +4 -0
  53. data/test/chef-repo/data_bags/junk/afg.json +4 -0
  54. data/test/chef-repo/data_bags/junk/bcd.json +4 -0
  55. data/test/chef-repo/data_bags/junk/sub1/cde1.json +4 -0
  56. data/test/chef-repo/data_bags/junk/sub1/cde2.json +4 -0
  57. data/test/chef-repo/data_bags/junk/sub2/def1.json +4 -0
  58. data/test/chef-repo/data_bags/junk/sub2/def2.json +4 -0
  59. data/test/chef-repo/data_bags/users/README +1 -0
  60. data/test/chef-repo/data_bags/users/badjson.json +8 -0
  61. data/test/chef-repo/data_bags/users/failname.json +8 -0
  62. data/test/chef-repo/data_bags/users/mray.json +8 -0
  63. data/test/chef-repo/data_bags/users/ubuntu.json +8 -0
  64. data/test/chef-repo/environments/development.rb +2 -0
  65. data/test/chef-repo/environments/fail2.rb +8 -0
  66. data/test/chef-repo/environments/fail3.rb +2 -0
  67. data/test/chef-repo/environments/production-blue.json +177 -0
  68. data/test/chef-repo/environments/production-green.json +177 -0
  69. data/test/chef-repo/environments/production-red.json +177 -0
  70. data/test/chef-repo/environments/qa.rb +2 -0
  71. data/test/chef-repo/environments/sub/efg1.rb +2 -0
  72. data/test/chef-repo/environments/sub/efg2.json +177 -0
  73. data/test/chef-repo/fail-cookbook1.yml +19 -0
  74. data/test/chef-repo/fail-cookbook2.yml +21 -0
  75. data/test/chef-repo/fail-cookbook3.yml +37 -0
  76. data/test/chef-repo/fail-cookbook4.yml +37 -0
  77. data/test/chef-repo/fail-cookbook5.yml +10 -0
  78. data/test/chef-repo/fail-cookbook6.yml +9 -0
  79. data/test/chef-repo/fail-db1.yml +12 -0
  80. data/test/chef-repo/fail-db2.yml +10 -0
  81. data/test/chef-repo/fail-db3.yml +10 -0
  82. data/test/chef-repo/fail-db4.yml +10 -0
  83. data/test/chef-repo/fail-env1.yml +27 -0
  84. data/test/chef-repo/fail-env2.yml +27 -0
  85. data/test/chef-repo/fail-env3.yml +28 -0
  86. data/test/chef-repo/fail-env4.yml +28 -0
  87. data/test/chef-repo/fail-roles1.yml +22 -0
  88. data/test/chef-repo/fail-roles2.yml +21 -0
  89. data/test/chef-repo/fail-roles3.yml +23 -0
  90. data/test/chef-repo/fail-roles4.yml +23 -0
  91. data/test/chef-repo/fail-roles5.yml +23 -0
  92. data/test/chef-repo/infrastructure.yml +43 -0
  93. data/test/chef-repo/nodes/boxy.lab.atx.json +1756 -0
  94. data/test/chef-repo/nodes/guenter.home.atx.json +2503 -0
  95. data/test/chef-repo/roles/README +1 -0
  96. data/test/chef-repo/roles/base.rb +18 -0
  97. data/test/chef-repo/roles/base2.rb +16 -0
  98. data/test/chef-repo/roles/base3.rb +17 -0
  99. data/test/chef-repo/roles/base4.rb +15 -0
  100. data/test/chef-repo/roles/fail1.rb +8 -0
  101. data/test/chef-repo/roles/fail2.rb +5 -0
  102. data/test/chef-repo/roles/fail3.rb +5 -0
  103. data/test/chef-repo/roles/sub/bw2.json +21 -0
  104. data/test/chef-repo/roles/sub/bwi1.json +20 -0
  105. data/test/chef-repo/roles/tc.rb +13 -0
  106. data/test/examples/cluster-file-example.yml +14 -0
  107. data/test/examples/example-cluster.yml +8 -0
  108. data/test/examples/example.json +127 -0
  109. data/test/examples/example.rb +128 -0
  110. data/test/examples/example.yml +67 -0
  111. data/test/examples/google-example.yml +33 -0
  112. data/test/examples/joyent-vsphere-example.yml +15 -0
  113. data/test/examples/knife.yml +7 -0
  114. data/test/examples/kvm-example.yml +22 -0
  115. data/test/examples/node-example.yml +23 -0
  116. data/test/examples/php-quick-start.yml +83 -0
  117. data/test/extract-repo/Berksfile +4 -0
  118. data/test/extract-repo/Berksfile.lock +10 -0
  119. data/test/extract-repo/cookbooks/abc/CHANGELOG.md +13 -0
  120. data/test/extract-repo/cookbooks/abc/README.md +68 -0
  121. data/test/extract-repo/cookbooks/abc/metadata.rb +7 -0
  122. data/test/extract-repo/cookbooks/abc/recipes/default.rb +8 -0
  123. data/test/extract-repo/cookbooks/def/CHANGELOG.md +13 -0
  124. data/test/extract-repo/cookbooks/def/README.md +68 -0
  125. data/test/extract-repo/cookbooks/def/metadata.rb +7 -0
  126. data/test/extract-repo/cookbooks/def/recipes/default.rb +8 -0
  127. data/test/extract-repo/cookbooks/ghi/CHANGELOG.md +13 -0
  128. data/test/extract-repo/cookbooks/ghi/README.md +68 -0
  129. data/test/extract-repo/cookbooks/ghi/metadata.rb +7 -0
  130. data/test/extract-repo/cookbooks/ghi/recipes/default.rb +8 -0
  131. data/test/extract-repo/cookbooks/jkl/CHANGELOG.md +13 -0
  132. data/test/extract-repo/cookbooks/jkl/README.md +68 -0
  133. data/test/extract-repo/cookbooks/jkl/metadata.rb +10 -0
  134. data/test/extract-repo/cookbooks/jkl/recipes/default.rb +8 -0
  135. data/test/extract-repo/cookbooks/mno/CHANGELOG.md +13 -0
  136. data/test/extract-repo/cookbooks/mno/README.md +68 -0
  137. data/test/extract-repo/cookbooks/mno/metadata.rb +9 -0
  138. data/test/extract-repo/cookbooks/mno/recipes/default.rb +8 -0
  139. data/test/extract-repo/data_bags/junk/README +1 -0
  140. data/test/extract-repo/data_bags/junk/abc.json +4 -0
  141. data/test/extract-repo/data_bags/junk/ade.json +4 -0
  142. data/test/extract-repo/data_bags/junk/afg.json +4 -0
  143. data/test/extract-repo/data_bags/junk/bcd.json +4 -0
  144. data/test/extract-repo/data_bags/junk/sub1/cde1.json +4 -0
  145. data/test/extract-repo/data_bags/junk/sub1/cde2.json +4 -0
  146. data/test/extract-repo/data_bags/junk/sub2/def1.json +4 -0
  147. data/test/extract-repo/data_bags/junk/sub2/def2.json +4 -0
  148. data/test/extract-repo/data_bags/users/README +1 -0
  149. data/test/extract-repo/data_bags/users/mray.json +8 -0
  150. data/test/extract-repo/data_bags/users/ubuntu.json +8 -0
  151. data/test/extract-repo/environments/development.rb +2 -0
  152. data/test/extract-repo/environments/production-blue.json +177 -0
  153. data/test/extract-repo/environments/production-green.json +177 -0
  154. data/test/extract-repo/environments/production-red.json +177 -0
  155. data/test/extract-repo/environments/qa.rb +2 -0
  156. data/test/extract-repo/environments/sub/efg1.rb +2 -0
  157. data/test/extract-repo/environments/sub/efg2.json +177 -0
  158. data/test/extract-repo/nodes/boxy.lab.atx.json +1756 -0
  159. data/test/extract-repo/nodes/guenter.home.atx.json +2503 -0
  160. data/test/extract-repo/roles/README +1 -0
  161. data/test/extract-repo/roles/base.rb +18 -0
  162. data/test/extract-repo/roles/base2.rb +16 -0
  163. data/test/extract-repo/roles/base3.rb +17 -0
  164. data/test/extract-repo/roles/base4.rb +16 -0
  165. data/test/extract-repo/roles/sub/bw2.json +21 -0
  166. data/test/extract-repo/roles/sub/bwi1.json +20 -0
  167. data/test/extract-repo/roles/tc.rb +13 -0
  168. data/test/extract-repo2/cookbooks/README.md +1 -0
  169. data/test/extract-repo2/data_bags/README.md +1 -0
  170. data/test/extract-repo2/environments/README.md +1 -0
  171. data/test/extract-repo2/nodes/README.md +1 -0
  172. data/test/extract-repo2/roles/README.md +1 -0
  173. metadata +357 -67
  174. data/spec/bin/chef_client_spec.rb +0 -72
  175. data/spec/bin/google_spec.rb +0 -105
  176. data/spec/bin/joyent-vsphere_spec.rb +0 -25
  177. data/spec/bin/knife_spec.rb +0 -18
  178. data/spec/bin/kvm_spec.rb +0 -29
  179. data/spec/bin/node_spec.rb +0 -108
  180. data/spec/bin/spiceweasel_spec.rb +0 -136
@@ -1,7 +1,8 @@
1
+ # encoding: UTF-8
1
2
  #
2
- # Author:: Matt Ray (<matt@opscode.com>)
3
+ # Author:: Matt Ray (<matt@getchef.com>)
3
4
  #
4
- # Copyright:: 2011-2013, Opscode, Inc <legal@opscode.com>
5
+ # Copyright:: 2011-2014, Chef Software, Inc <legal@getchef.com>
5
6
  #
6
7
  # Licensed under the Apache License, Version 2.0 (the "License");
7
8
  # you may not use this file except in compliance with the License.
@@ -17,97 +18,98 @@
17
18
  #
18
19
 
19
20
  module Spiceweasel
21
+ # manages parsing of Nodes
20
22
  class Nodes
21
-
22
23
  include CommandHelper
23
24
 
24
- PROVIDERS = %w{bluebox clodo cs digital_ocean ec2 gandi google hp joyent kvm linode lxc openstack rackspace slicehost terremark vagrant voxel vsphere}
25
+ PROVIDERS = %w(bluebox clodo cs digital_ocean ec2 gandi google hp joyent kvm linode lxc openstack rackspace vagrant vcair vsphere)
25
26
 
26
27
  attr_reader :create, :delete
27
28
 
28
- def initialize(nodes, cookbooks, environments, roles, knifecommands)
29
- @create = Array.new
30
- @delete = Array.new
31
- chefclient = Array.new
29
+ def initialize(nodes, cookbooks, environments, roles, knifecommands, rootoptions) # rubocop:disable CyclomaticComplexity
30
+ @create = []
31
+ @delete = []
32
+ chefclient = []
32
33
  create_command_options = {}
33
- if nodes
34
- Spiceweasel::Log.debug("nodes: #{nodes}")
35
- nodes.each do |node|
36
- name = node.keys.first
37
- names = name.split
38
- Spiceweasel::Log.debug("node: '#{name}' '#{node[name]}'")
39
- # get the node's run_list and options
40
- if node[name]
41
- run_list = process_run_list(node[name]['run_list'])
42
- Spiceweasel::Log.debug("node: '#{name}' run_list: '#{run_list}'")
43
- validate_run_list(name, run_list, cookbooks, roles) unless Spiceweasel::Config[:novalidation]
44
- options = node[name]['options'] || ''
45
- Spiceweasel::Log.debug("node: '#{name}' options: '#{options}'")
46
- validate_options(name, options, environments) unless Spiceweasel::Config[:novalidation]
47
- %w(allow_create_failure timeout).each do |key|
48
- if(node[name].has_key?(key))
49
- create_command_options[key] = node[name][key]
50
- end
34
+
35
+ return unless nodes
36
+
37
+ Spiceweasel::Log.debug("nodes: #{nodes}")
38
+ nodes.each do |node|
39
+ name = node.keys.first
40
+ names = name.split
41
+ Spiceweasel::Log.debug("node: '#{name}' '#{node[name]}'")
42
+ # get the node's run_list and options
43
+ if node[name]
44
+ run_list = process_run_list(node[name]['run_list'])
45
+ Spiceweasel::Log.debug("node: '#{name}' run_list: '#{run_list}'")
46
+ validate_run_list(name, run_list, cookbooks, roles) unless Spiceweasel::Config[:novalidation]
47
+ options = ((node[name]['options'] || '') + ' ' + (rootoptions || '')).rstrip
48
+ Spiceweasel::Log.debug("node: '#{name}' options: '#{options}'")
49
+ validate_options(name, options, environments) unless Spiceweasel::Config[:novalidation]
50
+ %w(allow_create_failure timeout).each do |key|
51
+ if node[name].key?(key)
52
+ create_command_options[key] = node[name][key]
51
53
  end
52
- additional_commands = node[name]['additional_commands'] || []
53
54
  end
54
- if Spiceweasel::Config[:chefclient]
55
- chefclient.push(process_chef_client(names, options, run_list))
56
- elsif Spiceweasel::Config[:node_only]
57
- process_nodes_only(names, options, run_list, create_command_options)
58
- else #create/delete
59
- #provider support
60
- if PROVIDERS.member?(names[0])
61
- count = names.length == 2 ? names[1] : 1
62
- process_providers(names, count, node[name]['name'], options, run_list, create_command_options, knifecommands)
63
- elsif names[0].start_with?("windows_")
64
- #windows node bootstrap support
65
- protocol = names.shift.split('_') #split on 'windows_ssh' etc
66
- names.each do |server|
67
- servercommand = "knife bootstrap #{protocol[0]} #{protocol[1]}#{Spiceweasel::Config[:knife_options]} #{server} #{options}"
68
- servercommand += " -r '#{run_list}'" unless run_list.empty?
69
- create_command(servercommand, create_command_options)
70
- delete_command("knife node#{Spiceweasel::Config[:knife_options]} delete #{server} -y")
71
- delete_command("knife client#{Spiceweasel::Config[:knife_options]} delete #{server} -y")
72
- end
73
- else
74
- #node bootstrap support
75
- name.split.each_with_index do |server, i|
76
- servercommand = "knife bootstrap#{Spiceweasel::Config[:knife_options]} #{server} #{options}".gsub(/\{\{n\}\}/, (i + 1).to_s)
77
- servercommand += " -r '#{run_list}'" unless run_list.empty?
78
- create_command(servercommand, create_command_options)
79
- delete_command("knife node#{Spiceweasel::Config[:knife_options]} delete #{server} -y")
80
- delete_command("knife client#{Spiceweasel::Config[:knife_options]} delete #{server} -y")
81
- end
55
+ additional_commands = node[name]['additional_commands'] || []
56
+ end
57
+ if Spiceweasel::Config[:chefclient]
58
+ chefclient.push(process_chef_client(names, options, run_list))
59
+ elsif Spiceweasel::Config[:node_only]
60
+ process_nodes_only(names, options, run_list, create_command_options)
61
+ else # create/delete
62
+ # provider support
63
+ if PROVIDERS.member?(names[0])
64
+ count = names.length == 2 ? names[1] : 1
65
+ process_providers(names, count, node[name]['name'], options, run_list, create_command_options, knifecommands)
66
+ elsif names[0].start_with?('windows_')
67
+ # windows node bootstrap support
68
+ protocol = names.shift.split('_') # split on 'windows_ssh' etc
69
+ names.each do |server|
70
+ servercommand = "knife bootstrap #{protocol[0]} #{protocol[1]}#{Spiceweasel::Config[:knife_options]} #{server} #{options}"
71
+ servercommand += " -r '#{run_list}'" unless run_list.empty?
72
+ create_command(servercommand, create_command_options)
73
+ delete_command("knife node#{Spiceweasel::Config[:knife_options]} delete #{server} -y")
74
+ delete_command("knife client#{Spiceweasel::Config[:knife_options]} delete #{server} -y")
75
+ end
76
+ else
77
+ # node bootstrap support
78
+ name.split.each_with_index do |server, i|
79
+ servercommand = node_numerate("knife bootstrap#{Spiceweasel::Config[:knife_options]} #{server} #{options}", i + 1, count)
80
+ servercommand += " -r '#{run_list}'" unless run_list.empty?
81
+ create_command(servercommand, create_command_options)
82
+ delete_command("knife node#{Spiceweasel::Config[:knife_options]} delete #{server} -y")
83
+ delete_command("knife client#{Spiceweasel::Config[:knife_options]} delete #{server} -y")
82
84
  end
83
- unless additional_commands.empty?
84
- additional_commands.each do |cmd|
85
- create_command(cmd, create_command_options)
86
- end
85
+ end
86
+ unless additional_commands.empty?
87
+ additional_commands.each do |cmd|
88
+ create_command(cmd, create_command_options)
87
89
  end
88
90
  end
89
91
  end
90
- if Spiceweasel::Config[:bulkdelete]
91
- delete_command("knife node#{Spiceweasel::Config[:knife_options]} bulk delete .* -y")
92
- end
93
- #remove repeats in chefclient and push into create_command
94
- chefclient.flatten.each_with_index {|x,i| create_command(x, create_command_options) unless x.eql?(chefclient[i-1])} if Spiceweasel::Config[:chefclient]
95
- #nodeonly
96
92
  end
93
+ if Spiceweasel::Config[:bulkdelete]
94
+ delete_command("knife node#{Spiceweasel::Config[:knife_options]} bulk delete .* -y")
95
+ end
96
+ # remove repeats in chefclient and push into create_command
97
+ chefclient.flatten.each_with_index { |x, i| create_command(x, create_command_options) unless x.eql?(chefclient[i - 1]) } if Spiceweasel::Config[:chefclient]
98
+ # nodeonly
97
99
  end
98
100
 
99
- #ensure run_list contents are listed previously.
101
+ # ensure run_list contents are listed previously.
100
102
  def validate_run_list(node, run_list, cookbooks, roles)
101
103
  run_list.split(',').each do |item|
102
- if item.start_with?("recipe[")
103
- #recipe[foo] or recipe[foo::bar]
104
+ if item.start_with?('recipe[')
105
+ # recipe[foo] or recipe[foo::bar]
104
106
  cb = item.split(/\[|\]/)[1].split(':')[0]
105
107
  unless cookbooks.member?(cb)
106
108
  STDERR.puts "ERROR: '#{node}' run list cookbook '#{cb}' is missing from the list of cookbooks in the manifest."
107
109
  exit(-1)
108
110
  end
109
- elsif item.start_with?("role[")
110
- #role[blah]
111
+ elsif item.start_with?('role[')
112
+ # role[blah]
111
113
  role = item.split(/\[|\]/)[1]
112
114
  unless roles.member?(role)
113
115
  STDERR.puts "ERROR: '#{node}' run list role '#{role}' is missing from the list of roles in the manifest."
@@ -120,10 +122,10 @@ module Spiceweasel
120
122
  end
121
123
  end
122
124
 
123
- #for now, just check that -E is legit
125
+ # for now, just check that -E is legit
124
126
  def validate_options(node, options, environments)
125
- if options =~ /-E/ #check for environments
126
- env = options.split('-E')[1].split()[0]
127
+ if options =~ /-E/ # check for environments
128
+ env = options.split('-E')[1].split[0]
127
129
  unless environments.member?(env)
128
130
  STDERR.puts "ERROR: '#{node}' environment '#{env}' is missing from the list of environments in the manifest."
129
131
  exit(-1)
@@ -132,153 +134,213 @@ module Spiceweasel
132
134
  end
133
135
 
134
136
  # handle --nodes-only
135
- def process_nodes_only(names, options, run_list, create_command_options)
137
+ def process_nodes_only(names, options, run_list, create_command_options) # rubocop:disable CyclomaticComplexity
136
138
  nodenames = []
137
139
  if PROVIDERS.member?(names[0])
138
140
  count = names.length == 2 ? names[1] : 1
139
- options.split().each do |opt|
140
- if opt =~ /^-N|^--node-name/
141
- optname = opt.sub(/-N|--node-name/,'').lstrip
142
- optname = options.split[options.split.find_index(opt)+1] if optname.empty?
143
- count.to_i.times do |i|
144
- nodenames.push(optname.gsub(/\{\{n\}\}/, (i + 1).to_s))
145
- end
146
- end
147
- end
148
- elsif names[0].start_with?("windows_")
141
+ do_provider_members(count, nodenames, options)
142
+ elsif names[0].start_with?('windows_')
149
143
  nodenames.push(names[1..-1])
150
- else #standard nodes
144
+ else # standard nodes
151
145
  nodenames.push(names)
152
146
  end
153
147
  nodenames.flatten.each do |node|
154
- if File.directory?("nodes/")
155
- if File.exists?("nodes/#{node}.json")
156
- validate_node_file(node) unless Spiceweasel::Config[:novalidation]
157
- servercommand = "knife node from file #{node}.json #{Spiceweasel::Config[:knife_options]}".rstrip
158
- else
159
- STDERR.puts "'nodes/#{node}.json' not found, unable to validate or load node. Using 'knife node create' instead."
160
- servercommand = "knife node create -d #{node} #{Spiceweasel::Config[:knife_options]}".rstrip
148
+ node_names_flatten(create_command_options, node, run_list)
149
+ end
150
+ end
151
+
152
+ def do_provider_members(count, nodenames, options)
153
+ options.split.each do |opt|
154
+ if opt =~ /^-N|^--node-name/
155
+ optname = opt.sub(/-N|--node-name/, '').lstrip
156
+ optname = options.split[options.split.find_index(opt) + 1] if optname.empty?
157
+ count.to_i.times do |i|
158
+ nodenames.push(node_numerate(optname, i + 1, count))
161
159
  end
160
+ end
161
+ end
162
+ end
163
+
164
+ def node_names_flatten(create_command_options, node, run_list)
165
+ if File.directory?('nodes/')
166
+ if File.exist?("nodes/#{node}.json")
167
+ validate_node_file(node) unless Spiceweasel::Config[:novalidation]
168
+ servercommand = "knife node from file #{node}.json #{Spiceweasel::Config[:knife_options]}".rstrip
162
169
  else
163
- STDERR.puts "'nodes' directory not found, unable to validate or load nodes. Using 'knife node create' instead."
170
+ STDERR.puts "'nodes/#{node}.json' not found, unable to validate or load node. Using 'knife node create' instead."
164
171
  servercommand = "knife node create -d #{node} #{Spiceweasel::Config[:knife_options]}".rstrip
165
172
  end
166
- create_command(servercommand, create_command_options)
167
- create_command("knife node run_list set #{node} '#{run_list}'", create_command_options) unless run_list.empty?
168
- delete_command("knife node#{Spiceweasel::Config[:knife_options]} delete #{node} -y")
169
- delete_command("knife client#{Spiceweasel::Config[:knife_options]} delete #{node} -y")
173
+ else
174
+ STDERR.puts "'nodes' directory not found, unable to validate or load nodes. Using 'knife node create' instead."
175
+ servercommand = "knife node create -d #{node} #{Spiceweasel::Config[:knife_options]}".rstrip
170
176
  end
177
+ create_command(servercommand, create_command_options)
178
+ create_command("knife node run_list set #{node} '#{run_list}'", create_command_options) unless run_list.empty?
179
+ delete_command("knife node#{Spiceweasel::Config[:knife_options]} delete #{node} -y")
180
+ delete_command("knife client#{Spiceweasel::Config[:knife_options]} delete #{node} -y")
171
181
  end
172
182
 
173
183
  # validate individual node files
174
184
  def validate_node_file(name)
175
185
  # read in the file
176
186
  node = Chef::JSONCompat.from_json(IO.read("nodes/#{name}.json"))
187
+
177
188
  # check the node name vs. contents of the file
178
- if(node['name'] != name)
179
- STDERR.puts "ERROR: Node '#{name}' listed in the manifest does not match the name '#{node['name']}' within the nodes/#{name}.json file."
180
- exit(-1)
181
- end
189
+ return unless node['name'] != name
190
+
191
+ STDERR.puts "ERROR: Node '#{name}' listed in the manifest does not match the name '#{node['name']}' within the nodes/#{name}.json file."
192
+ exit(-1)
182
193
  end
183
194
 
184
- #manage all the provider logic
185
- def process_providers(names, count, name, options, run_list, create_command_options, knifecommands)
195
+ # manage all the provider logic
196
+ def process_providers(names, count, name, options, run_list, create_command_options, knifecommands) # rubocop:disable CyclomaticComplexity
186
197
  provider = names[0]
187
198
  validate_provider(provider, names, count, options, knifecommands) unless Spiceweasel::Config[:novalidation]
188
199
  provided_names = []
189
- if name.nil? && options.split.index('-N') #pull this out for deletes
190
- name = options.split[options.split.index('-N')+1]
191
- count.to_i.times {|i| provided_names << name.gsub('{{n}}', (i + 1).to_s)} if name
200
+ if name.nil? && options.split.index('-N') # pull this out for deletes
201
+ name = options.split[options.split.index('-N') + 1]
202
+ count.to_i.times { |i| provided_names << node_numerate(name, i + 1, count) } if name
192
203
  end
204
+
193
205
  # google can have names or numbers
194
206
  if provider.eql?('google') && names[1].to_i == 0
195
- names[1..-1].each do |gname|
196
- server = "knife google#{Spiceweasel::Config[:knife_options]} server create #{gname} #{options}"
197
- server += " -r '#{run_list}'" unless run_list.empty?
198
- create_command(server, create_command_options)
199
- provided_names << gname
200
- end
207
+ do_google_numeric_provider(create_command_options, names, options, provided_names, run_list)
208
+ elsif Spiceweasel::Config[:parallel]
209
+ process_parallel(count, create_command_options, name, options, provider, run_list)
201
210
  else
202
- if Spiceweasel::Config[:parallel]
203
- parallel = "seq #{count} | parallel -u -j 0 -v \""
204
- if provider.eql?('vsphere')
205
- parallel += "knife #{provider}#{Spiceweasel::Config[:knife_options]} vm clone #{options}".gsub(/\{\{n\}\}/, '{}')
206
- elsif provider.eql?('kvm')
207
- parallel += "knife #{provider}#{Spiceweasel::Config[:knife_options]} vm create #{options}".gsub(/\{\{n\}\}/, '{}')
208
- elsif provider.eql?('digital_ocean')
209
- parallel += "knife #{provider}#{Spiceweasel::Config[:knife_options]} droplet create #{options}".gsub(/\{\{n\}\}/, '{}')
210
- elsif provider.eql?('google')
211
- parallel += "knife #{provider}#{Spiceweasel::Config[:knife_options]} server create #{name} #{options}".gsub(/\{\{n\}\}/, '{}')
212
- else
213
- parallel += "knife #{provider}#{Spiceweasel::Config[:knife_options]} server create #{options}".gsub(/\{\{n\}\}/, '{}')
214
- end
215
- parallel += " -r '#{run_list}'" unless run_list.empty?
216
- parallel += "\""
217
- create_command(parallel, create_command_options)
211
+ determine_cloud_provider(count, create_command_options, name, options, provider, run_list)
212
+ end
213
+ if Spiceweasel::Config[:bulkdelete] && provided_names.empty?
214
+ do_bulk_delete(provider)
215
+ else
216
+ provided_names.each do |p_name|
217
+ do_provided_names(p_name, provider)
218
+ end
219
+ end
220
+ end
221
+
222
+ def determine_cloud_provider(count, create_command_options, name, options, provider, run_list)
223
+ count.to_i.times do |i|
224
+ if provider.eql?('vsphere')
225
+ server = node_numerate("knife #{provider}#{Spiceweasel::Config[:knife_options]} vm clone #{options}", i + 1, count)
226
+ elsif provider.eql?('kvm')
227
+ server = node_numerate("knife #{provider}#{Spiceweasel::Config[:knife_options]} vm create #{options}", i + 1, count)
228
+ elsif provider.eql?('digital_ocean')
229
+ server = node_numerate("knife #{provider}#{Spiceweasel::Config[:knife_options]} droplet create #{options}", i + 1, count)
230
+ elsif provider.eql?('google')
231
+ server = node_numerate("knife #{provider}#{Spiceweasel::Config[:knife_options]} server create #{name} #{options}", i + 1, count)
218
232
  else
219
- count.to_i.times do |i|
220
- if provider.eql?('vsphere')
221
- server = "knife #{provider}#{Spiceweasel::Config[:knife_options]} vm clone #{options}".gsub(/\{\{n\}\}/, (i + 1).to_s)
222
- elsif provider.eql?('kvm')
223
- server = "knife #{provider}#{Spiceweasel::Config[:knife_options]} vm create #{options}".gsub(/\{\{n\}\}/, (i + 1).to_s)
224
- elsif provider.eql?('digital_ocean')
225
- server = "knife #{provider}#{Spiceweasel::Config[:knife_options]} droplet create #{options}".gsub(/\{\{n\}\}/, (i + 1).to_s)
226
- elsif provider.eql?('google')
227
- server = "knife #{provider}#{Spiceweasel::Config[:knife_options]} server create #{name} #{options}".gsub(/\{\{n\}\}/, (i + 1).to_s)
228
- else
229
- server = "knife #{provider}#{Spiceweasel::Config[:knife_options]} server create #{options}".gsub(/\{\{n\}\}/, (i + 1).to_s)
230
- end
231
- server += " -r '#{run_list}'" unless run_list.empty?
232
- create_command(server, create_command_options)
233
- end
233
+ server = node_numerate("knife #{provider}#{Spiceweasel::Config[:knife_options]} server create #{options}", i + 1, count)
234
234
  end
235
+ server += " -r '#{run_list}'" unless run_list.empty?
236
+ create_command(server, create_command_options)
235
237
  end
236
- if Spiceweasel::Config[:bulkdelete] && provided_names.empty?
237
- if ['kvm','vsphere'].member?(provider)
238
+ end
239
+
240
+ def do_google_numeric_provider(create_command_options, names, options, provided_names, run_list)
241
+ names[1..-1].each do |gname|
242
+ server = "knife google#{Spiceweasel::Config[:knife_options]} server create #{gname} #{options}"
243
+ server += " -r '#{run_list}'" unless run_list.empty?
244
+ create_command(server, create_command_options)
245
+ provided_names << gname
246
+ end
247
+ end
248
+
249
+ def do_provided_names(p_name, provider)
250
+ if ['kvm', 'vsphere'].member?(provider)
251
+ delete_command("knife #{provider} vm delete #{p_name} -y")
252
+ elsif ['digital_ocean'].member?(provider)
253
+ delete_command("knife #{provider} droplet destroy #{p_name} -y")
254
+ else
255
+ delete_command("knife #{provider} server delete #{p_name} -y")
256
+ end
257
+ delete_command("knife node#{Spiceweasel::Config[:knife_options]} delete #{p_name} -y")
258
+ delete_command("knife client#{Spiceweasel::Config[:knife_options]} delete #{p_name} -y")
259
+ end
260
+
261
+ def do_bulk_delete(provider)
262
+ if ['kvm', 'vsphere'].member?(provider)
263
+ if bundler?
264
+ delete_command("knife node#{Spiceweasel::Config[:knife_options]} list | xargs bundle exec knife #{provider} vm delete -y")
265
+ else
238
266
  delete_command("knife node#{Spiceweasel::Config[:knife_options]} list | xargs knife #{provider} vm delete -y")
239
- elsif ['digital_ocean'].member?(provider)
267
+ end
268
+ elsif ['digital_ocean'].member?(provider)
269
+ if bundler?
270
+ delete_command("knife node#{Spiceweasel::Config[:knife_options]} list | xargs bundle exec knife #{provider} droplet destroy -y")
271
+ else
240
272
  delete_command("knife node#{Spiceweasel::Config[:knife_options]} list | xargs knife #{provider} droplet destroy -y")
273
+ end
274
+ else
275
+ if bundler?
276
+ delete_command("knife node#{Spiceweasel::Config[:knife_options]} list | xargs bundle exec knife #{provider} server delete -y")
241
277
  else
242
278
  delete_command("knife node#{Spiceweasel::Config[:knife_options]} list | xargs knife #{provider} server delete -y")
243
279
  end
280
+ end
281
+ end
282
+
283
+ def process_parallel(count, create_command_options, name, options, provider, run_list)
284
+ parallel = "seq #{count} | parallel -u -j 0 -v -- "
285
+ if provider.eql?('vsphere')
286
+ if bundler?
287
+ parallel += "bundle exec knife #{provider}#{Spiceweasel::Config[:knife_options]} vm clone #{options}".gsub(/\{\{n\}\}/, '{}')
288
+ else
289
+ parallel += "knife #{provider}#{Spiceweasel::Config[:knife_options]} vm clone #{options}".gsub(/\{\{n\}\}/, '{}')
290
+ end
291
+ elsif provider.eql?('kvm')
292
+ if bundler?
293
+ parallel += "bundle exec knife #{provider}#{Spiceweasel::Config[:knife_options]} vm create #{options}".gsub(/\{\{n\}\}/, '{}')
294
+ else
295
+ parallel += "knife #{provider}#{Spiceweasel::Config[:knife_options]} vm create #{options}".gsub(/\{\{n\}\}/, '{}')
296
+ end
297
+ elsif provider.eql?('digital_ocean')
298
+ if bundler?
299
+ parallel += "bundle exec knife #{provider}#{Spiceweasel::Config[:knife_options]} droplet create #{options}".gsub(/\{\{n\}\}/, '{}')
300
+ else
301
+ parallel += "knife #{provider}#{Spiceweasel::Config[:knife_options]} droplet create #{options}".gsub(/\{\{n\}\}/, '{}')
302
+ end
303
+ elsif provider.eql?('google')
304
+ if bundler?
305
+ parallel += "bundle exec knife #{provider}#{Spiceweasel::Config[:knife_options]} server create #{name} #{options}".gsub(/\{\{n\}\}/, '{}')
306
+ else
307
+ parallel += "knife #{provider}#{Spiceweasel::Config[:knife_options]} server create #{name} #{options}".gsub(/\{\{n\}\}/, '{}')
308
+ end
244
309
  else
245
- provided_names.each do |p_name|
246
- if ['kvm','vsphere'].member?(provider)
247
- delete_command("knife #{provider} vm delete #{p_name} -y")
248
- elsif ['digital_ocean'].member?(provider)
249
- delete_command("knife #{provider} droplet destroy #{p_name} -y")
250
- else
251
- delete_command("knife #{provider} server delete #{p_name} -y")
252
- end
253
- delete_command("knife node#{Spiceweasel::Config[:knife_options]} delete #{p_name} -y")
254
- delete_command("knife client#{Spiceweasel::Config[:knife_options]} delete #{p_name} -y")
310
+ if bundler?
311
+ parallel += "bundle exec knife #{provider}#{Spiceweasel::Config[:knife_options]} server create #{options}".gsub(/\{\{n\}\}/, '{}')
312
+ else
313
+ parallel += "knife #{provider}#{Spiceweasel::Config[:knife_options]} server create #{options}".gsub(/\{\{n\}\}/, '{}')
255
314
  end
256
315
  end
316
+ parallel += " -r '#{run_list}'" unless run_list.empty?
317
+ create_command(parallel, create_command_options)
257
318
  end
258
319
 
259
- #check that the knife plugin is installed
260
- def validate_provider(provider, names, count, options, knifecommands)
261
- unless knifecommands.index {|x| x.start_with?("knife #{provider}")}
320
+ # check that the knife plugin is installed
321
+ def validate_provider(provider, names, _count, options, knifecommands)
322
+ unless knifecommands.index { |x| x.start_with?("knife #{provider}") }
262
323
  STDERR.puts "ERROR: 'knife #{provider}' is not a currently installed plugin for knife."
263
324
  exit(-1)
264
325
  end
265
- if provider.eql?('google')
266
- if names[1].to_i != 0 && !options.split.member?('-N')
267
- STDERR.puts "ERROR: 'knife google' currently requires providing a name. Please use -N within the options."
268
- exit(-1)
269
- end
270
- end
326
+
327
+ return unless provider.eql?('google')
328
+
329
+ return unless names[1].to_i != 0 && !options.split.member?('-N')
330
+
331
+ STDERR.puts "ERROR: 'knife google' currently requires providing a name. Please use -N within the options."
332
+ exit(-1)
271
333
  end
272
334
 
273
- def process_chef_client(names, options, run_list)
335
+ def process_chef_client(names, options, run_list) # rubocop:disable CyclomaticComplexity
274
336
  commands = []
275
337
  environment = nil
276
338
  protocol = 'ssh'
277
339
  protooptions = ''
278
- #protocol options
340
+ # protocol options
279
341
  sudo = nil
280
- value = nil #store last option for space-separated values
281
- options.split().each do |opt|
342
+ value = nil # store last option for space-separated values
343
+ options.split.each do |opt|
282
344
  sudo = 'sudo ' if opt =~ /^--sudo$/
283
345
  protooptions += '--no-host-key-verify ' if opt =~ /^--no-host-key-verify$/
284
346
  # SSH identity file used for authentication
@@ -290,8 +352,8 @@ module Spiceweasel
290
352
  if opt =~ /^-i$|^--identity-file$/
291
353
  value = '-i'
292
354
  else
293
- opt.sub!(/-i/,'') if opt =~ /^-i/
294
- opt.sub!(/--identity-file/,'') if opt =~ /^--identity-file/
355
+ opt.sub!(/-i/, '') if opt =~ /^-i/
356
+ opt.sub!(/--identity-file/, '') if opt =~ /^--identity-file/
295
357
  protooptions += "-i #{opt} "
296
358
  value = nil
297
359
  end
@@ -305,8 +367,8 @@ module Spiceweasel
305
367
  if opt =~ /^-G$|^--ssh-gateway$/
306
368
  value = '-G'
307
369
  else
308
- opt.sub!(/-G/,'') if opt =~ /^-G/
309
- opt.sub!(/--ssh-gateway/,'') if opt =~ /^--ssh-gateway/
370
+ opt.sub!(/-G/, '') if opt =~ /^-G/
371
+ opt.sub!(/--ssh-gateway/, '') if opt =~ /^--ssh-gateway/
310
372
  protooptions += "-G #{opt} "
311
373
  value = nil
312
374
  end
@@ -320,8 +382,8 @@ module Spiceweasel
320
382
  if opt =~ /^-P$|^--ssh-password$/
321
383
  value = '-P'
322
384
  else
323
- opt.sub!(/-P/,'') if opt =~ /^-P/
324
- opt.sub!(/--ssh-password/,'') if opt =~ /^--ssh-password/
385
+ opt.sub!(/-P/, '') if opt =~ /^-P/
386
+ opt.sub!(/--ssh-password/, '') if opt =~ /^--ssh-password/
325
387
  protooptions += "-P #{opt} "
326
388
  value = nil
327
389
  end
@@ -335,8 +397,8 @@ module Spiceweasel
335
397
  if opt =~ /^-p$|^--ssh-port$/
336
398
  value = '-p'
337
399
  else
338
- opt.sub!(/-p/,'') if opt =~ /^-p/
339
- opt.sub!(/--ssh-port/,'') if opt =~ /^--ssh-port/
400
+ opt.sub!(/-p/, '') if opt =~ /^-p/
401
+ opt.sub!(/--ssh-port/, '') if opt =~ /^--ssh-port/
340
402
  protooptions += "-p #{opt} "
341
403
  value = nil
342
404
  end
@@ -351,8 +413,8 @@ module Spiceweasel
351
413
  if opt =~ /^-x$|^--ssh-user$/
352
414
  value = '-x'
353
415
  else
354
- opt.sub!(/-x/,'') if opt =~ /^-x/
355
- opt.sub!(/--ssh-user/,'') if opt =~ /^--ssh-user/
416
+ opt.sub!(/-x/, '') if opt =~ /^-x/
417
+ opt.sub!(/--ssh-user/, '') if opt =~ /^--ssh-user/
356
418
  protooptions += "-x #{opt} "
357
419
  sudo = 'sudo ' unless opt.eql?('root')
358
420
  value = nil
@@ -367,8 +429,8 @@ module Spiceweasel
367
429
  if opt =~ /^-E$|^--environment$/
368
430
  value = '-E'
369
431
  else
370
- opt.sub!(/-E/,'') if opt =~ /^-E/
371
- opt.sub!(/--environment/,'') if opt =~ /^--environment/
432
+ opt.sub!(/-E/, '') if opt =~ /^-E/
433
+ opt.sub!(/--environment/, '') if opt =~ /^--environment/
372
434
  environment = opt
373
435
  value = nil
374
436
  end
@@ -382,16 +444,16 @@ module Spiceweasel
382
444
  if opt =~ /^-N$|^--node-name$/
383
445
  value = '-N'
384
446
  else
385
- opt.sub!(/-N|--node-name/,'') if opt =~ /^-N|^--node-name/
447
+ opt.sub!(/-N|--node-name/, '') if opt =~ /^-N|^--node-name/
386
448
  names = [opt.gsub(/{{n}}/, '*')]
387
449
  value = nil
388
450
  end
389
451
  end
390
452
  end
391
- if names[0].start_with?("windows_")
392
- #windows node bootstrap support
393
- protocol = names.shift.split('_')[1] #split on 'windows_ssh' etc
394
- sudo = nil #no sudo for Windows even if ssh is used
453
+ if names[0].start_with?('windows_')
454
+ # windows node bootstrap support
455
+ protocol = names.shift.split('_')[1] # split on 'windows_ssh' etc
456
+ sudo = nil # no sudo for Windows even if ssh is used
395
457
  end
396
458
  names = [] if PROVIDERS.member?(names[0])
397
459
  # check options for -N, override name
@@ -405,10 +467,10 @@ module Spiceweasel
405
467
  commands.push("knife #{protocol} '#{search}' '#{sudo}chef-client' #{protooptions} #{Spiceweasel::Config[:knife_options]}")
406
468
  end
407
469
  end
408
- return commands
470
+ commands
409
471
  end
410
472
 
411
- #create the knife ssh chef-client search pattern
473
+ # create the knife ssh chef-client search pattern
412
474
  def chef_client_search(name, run_list, environment)
413
475
  search = []
414
476
  search.push("name:#{name}") if name
@@ -419,16 +481,22 @@ module Spiceweasel
419
481
  item.sub!(/::/, '\:\:')
420
482
  search.push(item)
421
483
  end
422
- return "#{search.join(" and ")}"
484
+ "#{search.join(' AND ')}"
423
485
  end
424
486
 
425
- #standardize the node run_list formatting
487
+ # standardize the node run_list formatting
426
488
  def process_run_list(run_list)
427
489
  return '' if run_list.nil?
428
- run_list.gsub!(/ /,',')
429
- run_list.gsub!(/,+/,',')
430
- return run_list
490
+ run_list.gsub!(/ /, ',')
491
+ run_list.gsub!(/,+/, ',')
492
+ run_list
431
493
  end
432
494
 
495
+ # replace the {{n}} with the zero padding number
496
+ def node_numerate(name, num, count)
497
+ digits = count.to_s.length + 1
498
+ pad = sprintf("%0#{digits}i", num)
499
+ name.gsub(/\{\{n\}\}/, pad)
500
+ end
433
501
  end
434
502
  end