class Chef::Knife::Bootstrap::ClientBuilder

Attributes

chef_config[RW]

@return [Hash] chef config object

client[R]

@return [Chef::ApiClient] client saved on run

knife_config[RW]

@return [Hash] knife merged config, typically @config

ui[RW]

@return [Chef::Knife::UI] ui object for output

Public Class Methods

new(knife_config: {}, chef_config: {}, ui: nil) click to toggle source

@param #knife_config [Hash] Hash of knife config settings @param #chef_config [Hash] Hash of chef config settings @param ui [Chef::Knife::UI] UI object for output

# File lib/chef/knife/bootstrap/client_builder.rb, line 43
def initialize(knife_config: {}, chef_config: {}, ui: nil)
  @knife_config = knife_config
  @chef_config  = chef_config
  @ui           = ui
end

Public Instance Methods

client_path() click to toggle source

Tempfile to use to write newly created client credentials to.

This method is public so that the knife bootstrapper can read then and pass the value into the handler for chef vault which needs the client cert we create here.

We hang onto the tmpdir as an ivar as well so that it will not get GC'd and removed

@return [String] path to the generated client.pem

# File lib/chef/knife/bootstrap/client_builder.rb, line 71
def client_path
  @client_path ||=
    begin
      @tmpdir = Dir.mktmpdir
      File.join(@tmpdir, "#{node_name}.pem")
    end
end
run() click to toggle source

Main entry. Prompt the user to clean up any old client or node objects. Then create the new client, then create the new node.

# File lib/chef/knife/bootstrap/client_builder.rb, line 51
def run
  sanity_check

  ui.info("Creating new client for #{node_name}")

  @client = create_client!

  ui.info("Creating new node for #{node_name}")

  create_node!
end

Private Instance Methods

chef_server_url() click to toggle source

@return [String] chef server url from the Chef::Config

# File lib/chef/knife/bootstrap/client_builder.rb, line 112
def chef_server_url
  chef_config[:chef_server_url]
end
client_rest() click to toggle source

@return [Chef::ServerAPI] REST client using the client credentials

# File lib/chef/knife/bootstrap/client_builder.rb, line 196
def client_rest
  @client_rest ||= Chef::ServerAPI.new(chef_server_url, :client_name => node_name, :signing_key_filename => client_path)
end
create_client!() click to toggle source

Create the client object and save it to the Chef API

# File lib/chef/knife/bootstrap/client_builder.rb, line 133
def create_client!
  Chef::ApiClient::Registration.new(node_name, client_path, http_api: rest).run
end
create_node!() click to toggle source

Create the node object (via the lazy accessor) and save it to the Chef API

# File lib/chef/knife/bootstrap/client_builder.rb, line 138
def create_node!
  node.save
end
environment() click to toggle source

@return [String] enviroment from the #knife_config

# File lib/chef/knife/bootstrap/client_builder.rb, line 87
def environment
  knife_config[:environment]
end
first_boot_attributes() click to toggle source

@return [Hash,Array] Object representation of json first-boot attributes from the #knife_config

# File lib/chef/knife/bootstrap/client_builder.rb, line 107
def first_boot_attributes
  knife_config[:first_boot_attributes]
end
node() click to toggle source

Create a new Chef::Node. Supports creating the node with its name, #run_list, attributes and environment. This injects a rest object into the Chef::Node which uses the client key for authentication so that the client creates the node and therefore we get the acls setup correctly.

@return [Chef::Node] new chef node to create

# File lib/chef/knife/bootstrap/client_builder.rb, line 148
def node
  @node ||=
    begin
      node = Chef::Node.new(chef_server_rest: client_rest)
      node.name(node_name)
      node.run_list(normalized_run_list)
      node.normal_attrs = first_boot_attributes if first_boot_attributes
      node.environment(environment) if environment
      node.policy_name = policy_name if policy_name
      node.policy_group = policy_group if policy_group
      (knife_config[:tags] || []).each do |tag|
        node.tags << tag
      end
      node
    end
end
node_name() click to toggle source

@return [String] node name from the #knife_config

# File lib/chef/knife/bootstrap/client_builder.rb, line 82
def node_name
  knife_config[:chef_node_name]
end
normalized_run_list() click to toggle source

Accesses the #run_list and coerces it into an Array, changing nils into the empty Array, and splitting strings representations of run_lists into Arrays.

@return [Array] #run_list coerced into an array

# File lib/chef/knife/bootstrap/client_builder.rb, line 121
def normalized_run_list
  case run_list
  when nil
    []
  when String
    run_list.split(/\s*,\s*/)
  when Array
    run_list
  end
end
policy_group() click to toggle source

@return [String] #policy_group from the #knife_config

# File lib/chef/knife/bootstrap/client_builder.rb, line 102
def policy_group
  knife_config[:policy_group]
end
policy_name() click to toggle source

@return [String] #policy_name from the #knife_config

# File lib/chef/knife/bootstrap/client_builder.rb, line 97
def policy_name
  knife_config[:policy_name]
end
resource_exists?(relative_path) click to toggle source

Check if an relative path exists on the chef server

@param relative_path [String] URI path relative to the chef organization @return [Boolean] if the relative path exists or returns a 404

# File lib/chef/knife/bootstrap/client_builder.rb, line 187
def resource_exists?(relative_path)
  rest.get(relative_path)
  true
rescue Net::HTTPServerException => e
  raise unless e.response.code == "404"
  false
end
rest() click to toggle source

@return [Chef::ServerAPI] REST client using the cli user's knife credentials this uses the users's credentials

# File lib/chef/knife/bootstrap/client_builder.rb, line 202
def rest
  @rest ||= Chef::ServerAPI.new(chef_server_url)
end
run_list() click to toggle source

@return [String] #run_list from the #knife_config

# File lib/chef/knife/bootstrap/client_builder.rb, line 92
def run_list
  knife_config[:run_list]
end
sanity_check() click to toggle source

Check for the existence of a node and/or client already on the server. If the node already exists, we must delete it in order to proceed so that we can create a new node object with the permissions of the new client. There is a use case for creating a new client and wiring it up to a precreated node object, but we do currently support that.

We prompt the user about what to do and will fail hard if we do not get confirmation to delete any prior node/client objects.

# File lib/chef/knife/bootstrap/client_builder.rb, line 172
def sanity_check
  if resource_exists?("nodes/#{node_name}")
    ui.confirm("Node #{node_name} exists, overwrite it")
    rest.delete("nodes/#{node_name}")
  end
  if resource_exists?("clients/#{node_name}")
    ui.confirm("Client #{node_name} exists, overwrite it")
    rest.delete("clients/#{node_name}")
  end
end