Class: ChefWorkflow::VM::KnifeProvisioner
- Inherits:
-
Object
- Object
- ChefWorkflow::VM::KnifeProvisioner
- Includes:
- DebugSupport, KnifePluginSupport
- Defined in:
- lib/chef-workflow/support/vm/knife.rb
Overview
The Knife Provisioner does three major things:
-
Bootstraps a series of machines living on IP addresses supplied to it
-
Ensures that they converged successfully (if not, raises and displays output)
-
Waits until chef has indexed their metadata
On deprovision, it deletes the nodes and clients related to this server group.
Machines are named as such: $server_group-$number, where $number starts at 0 and increases with the number of servers requested. Your node names will be named this as well as the clients associated with them.
It does as much of this as it can in parallel, but stalls the current thread until the subthreads complete. This allows is to work as quickly as possible in a ‘serial’ scheduling scenario as we know bootstrapping can always occur in parallel for the group.
Constant Summary
Constants included from DebugSupport
DebugSupport::CHEF_WORKFLOW_DEBUG_DEFAULT
Instance Attribute Summary collapse
-
#environment ⇒ Object
the chef environment to be used.
-
#ips ⇒ Object
the list of IPs to provision.
-
#name ⇒ Object
the name of this server group.
-
#password ⇒ Object
the password for SSH.
-
#port ⇒ Object
the port to contact for SSH.
-
#run_list ⇒ Object
the run list of this server group.
-
#solr_check ⇒ Object
perform the solr check to ensure the instance has converged and its metadata is ready for searching.
-
#ssh_key ⇒ Object
the ssh key to be used for SSH.
-
#template_file ⇒ Object
the bootstrap template to be used.
-
#use_sudo ⇒ Object
drive knife bootstrap’s sudo functionality.
-
#username ⇒ Object
the username for SSH.
Instance Method Summary collapse
-
#bootstrap(node_name, ip) ⇒ Object
Bootstraps a single node.
-
#check_nodes ⇒ Object
Checks that the nodes have made it into the search index.
-
#client_delete(node_name) ⇒ Object
Deletes a chef client.
- #init_nodes_db ⇒ Object
-
#initialize ⇒ KnifeProvisioner
constructor
constructor.
-
#node_delete(node_name) ⇒ Object
Deletes a chef node.
- #report ⇒ Object
-
#shutdown ⇒ Object
Deprovisions the server group.
-
#startup(*args) ⇒ Object
Runs the provisioner.
Methods included from KnifePluginSupport
Methods included from DebugSupport
Constructor Details
#initialize ⇒ KnifeProvisioner
constructor.
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 54 def initialize require 'chef/node' require 'chef/search/query' require 'chef/knife/ssh' require 'chef/knife/bootstrap' require 'chef-workflow/support/knife' require 'timeout' @ips = [] @username = nil @password = nil @ssh_key = nil @port = nil @use_sudo = nil @run_list = nil @template_file = nil @environment = nil @solr_check = true end |
Instance Attribute Details
#environment ⇒ Object
the chef environment to be used.
40 41 42 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 40 def environment @environment end |
#ips ⇒ Object
the list of IPs to provision.
44 45 46 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 44 def ips @ips end |
#name ⇒ Object
the name of this server group.
48 49 50 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 48 def name @name end |
#password ⇒ Object
the password for SSH.
32 33 34 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 32 def password @password end |
#port ⇒ Object
the port to contact for SSH
42 43 44 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 42 def port @port end |
#run_list ⇒ Object
the run list of this server group.
46 47 48 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 46 def run_list @run_list end |
#solr_check ⇒ Object
perform the solr check to ensure the instance has converged and its metadata is ready for searching.
51 52 53 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 51 def solr_check @solr_check end |
#ssh_key ⇒ Object
the ssh key to be used for SSH
36 37 38 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 36 def ssh_key @ssh_key end |
#template_file ⇒ Object
the bootstrap template to be used.
38 39 40 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 38 def template_file @template_file end |
#use_sudo ⇒ Object
drive knife bootstrap’s sudo functionality.
34 35 36 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 34 def use_sudo @use_sudo end |
#username ⇒ Object
the username for SSH.
30 31 32 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 30 def username @username end |
Instance Method Details
#bootstrap(node_name, ip) ⇒ Object
Bootstraps a single node. Validates bootstrap by checking the node metadata directly and ensuring it made it into the chef server.
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 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 178 def bootstrap(node_name, ip) args = [] args += %W[-x #{username}] if username args += %W[-P #{password}] if password args += %w[--sudo] if use_sudo args += %W[-i #{ssh_key}] if ssh_key args += %W[--template-file #{template_file}] if template_file args += %W[-p #{port}] if port args += %W[-E #{environment}] if environment args += %W[-r #{run_list.join(",")}] args += %W[-N '#{node_name}'] args += [ip] bootstrap_cli = init_knife_plugin(Chef::Knife::Bootstrap, args) Thread.new do begin bootstrap_cli.run rescue SystemExit => e # welp, looks like they finally fixed it. # can't rely on it for compat reasons, but at least we're not # dropping to a prompt when a bootstrap fails. end # knife bootstrap is the honey badger when it comes to exit status. # We can't rely on it, so we examine the run_list of the node instead # to ensure it converged. node = Chef::Node.load(node_name) rescue nil run_list_size = node ? (node.run_list.to_a.size rescue 0) : 0 unless run_list_size > 0 puts bootstrap_cli.ui.stdout.string puts bootstrap_cli.ui.stderr.string if node # # hack for bad first-converges that preserves the run list. # @run_list.each { |i| node.run_list.add(i) unless node.run_list.include?(i) } node.save end raise "bootstrap for #{node_name}/#{ip} wasn't successful." end if_debug(2) do puts bootstrap_cli.ui.stdout.string puts bootstrap_cli.ui.stderr.string end end end |
#check_nodes ⇒ Object
Checks that the nodes have made it into the search index. Will block until all nodes in this server group are found, or a 60 second timeout is reached, at which point it will raise.
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 137 def check_nodes q = Chef::Search::Query.new unchecked_node_names = @node_names.to_a # this dirty hack turns 'role[foo]' into 'roles:foo', but also works on # recipe[] too. Then joins the whole thing with AND search_query = run_list. map { |s| s.gsub(/\[/, 's:"').gsub(/\]/, '"') }. join(" AND ") Timeout.timeout(ChefWorkflow::KnifeSupport.search_index_wait) do until unchecked_node_names.empty? node_name = unchecked_node_names.shift if_debug(3) do $stderr.puts "Checking search validity for node #{node_name}" end result = q.search( :node, search_query + %Q[ AND name:"#{node_name}"] ).first unless result and result.count == 1 and result.first.name == node_name unchecked_node_names << node_name end # unfortunately if this isn't here you might as well issue kill -9 to # the rake process sleep 0.3 end end return true rescue Timeout::Error raise "Bootstrapped nodes for #{name} did not appear in Chef search index after 60 seconds." end |
#client_delete(node_name) ⇒ Object
Deletes a chef client.
232 233 234 235 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 232 def client_delete(node_name) require 'chef/knife/client_delete' init_knife_plugin(Chef::Knife::ClientDelete, [node_name, '-y']).run end |
#init_nodes_db ⇒ Object
74 75 76 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 74 def init_nodes_db @node_names = ChefWorkflow::DatabaseSupport::Set.new('nodes', name) end |
#node_delete(node_name) ⇒ Object
Deletes a chef node.
240 241 242 243 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 240 def node_delete(node_name) require 'chef/knife/node_delete' init_knife_plugin(Chef::Knife::NodeDelete, [node_name, '-y']).run end |
#report ⇒ Object
245 246 247 248 249 250 251 252 253 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 245 def report res = ["nodes:"] ChefWorkflow::IPSupport.get_role_ips(name).each_with_index do |ip, i| res += ["#{name}-#{i}: #{ip}"] end return res end |
#shutdown ⇒ Object
Deprovisions the server group. Runs node delete and client delete on all nodes that were created by this provisioner.
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 111 def shutdown t = [] init_nodes_db @node_names.each do |node_name| t.push( Thread.new do client_delete(node_name) rescue nil node_delete(node_name) rescue nil end ) end t.each(&:join) @node_names.clear return true end |
#startup(*args) ⇒ Object
Runs the provisioner. Accepts an array of IP addresses as its first argument, intended to be provided by provisioners that ran before it as their return value.
Will raise if the IPs are not supplied or the provisioner is not named with a server group.
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/chef-workflow/support/vm/knife.rb', line 86 def startup(*args) @ips = args.first #argh raise "This provisioner is unnamed, cannot continue" unless name raise "This provisioner requires ip addresses which were not supplied" unless ips init_nodes_db @run_list ||= ["role[#{name}]"] t = [] ips.each_with_index do |ip, index| node_name = "#{name}-#{index}" @node_names.add(node_name) t.push bootstrap(node_name, ip) end t.each(&:join) return solr_check ? check_nodes : true end |