Skip to content

Instantly share code, notes, and snippets.

@popsikle
Created April 8, 2014 19:34
Show Gist options
  • Save popsikle/10177600 to your computer and use it in GitHub Desktop.
Save popsikle/10177600 to your computer and use it in GitHub Desktop.
Quick and dirty method of outputting the rendered bootstrap template (knife modification)
#
# Author:: Adam Jacob (<adam@opscode.com>)
# Copyright:: Copyright (c) 2010 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'chef/knife'
require 'erubis'
class Chef
class Knife
class Bootstrap < Knife
deps do
require 'chef/knife/core/bootstrap_context'
require 'chef/json_compat'
require 'tempfile'
require 'highline'
require 'net/ssh'
require 'net/ssh/multi'
require 'chef/knife/ssh'
Chef::Knife::Ssh.load_deps
end
banner "knife bootstrap FQDN (options)"
option :gen_template,
:short => "-o",
:long => "--render-template",
:description => "Render the template and then exit",
:boolean => false
option :ssh_user,
:short => "-x USERNAME",
:long => "--ssh-user USERNAME",
:description => "The ssh username",
:default => "root"
option :ssh_password,
:short => "-P PASSWORD",
:long => "--ssh-password PASSWORD",
:description => "The ssh password"
option :ssh_port,
:short => "-p PORT",
:long => "--ssh-port PORT",
:description => "The ssh port",
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key }
option :ssh_gateway,
:short => "-G GATEWAY",
:long => "--ssh-gateway GATEWAY",
:description => "The ssh gateway",
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key }
option :forward_agent,
:short => "-A",
:long => "--forward-agent",
:description => "Enable SSH agent forwarding",
:boolean => true
option :identity_file,
:short => "-i IDENTITY_FILE",
:long => "--identity-file IDENTITY_FILE",
:description => "The SSH identity file used for authentication"
option :chef_node_name,
:short => "-N NAME",
:long => "--node-name NAME",
:description => "The Chef node name for your new node"
option :prerelease,
:long => "--prerelease",
:description => "Install the pre-release chef gems"
option :bootstrap_version,
:long => "--bootstrap-version VERSION",
:description => "The version of Chef to install",
:proc => lambda { |v| Chef::Config[:knife][:bootstrap_version] = v }
option :bootstrap_proxy,
:long => "--bootstrap-proxy PROXY_URL",
:description => "The proxy server for the node being bootstrapped",
:proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p }
option :bootstrap_no_proxy,
:long => "--bootstrap-no-proxy [NO_PROXY_URL|NO_PROXY_IP]",
:description => "Do not proxy locations for the node being bootstrapped",
:proc => Proc.new { |np| Chef::Config[:knife][:bootstrap_no_proxy] = np }
option :distro,
:short => "-d DISTRO",
:long => "--distro DISTRO",
:description => "Bootstrap a distro using a template",
:default => "chef-full"
option :use_sudo,
:long => "--sudo",
:description => "Execute the bootstrap via sudo",
:boolean => true
option :use_sudo_password,
:long => "--use-sudo-password",
:description => "Execute the bootstrap via sudo with password",
:boolean => false
option :template_file,
:long => "--template-file TEMPLATE",
:description => "Full path to location of template to use",
:default => false
option :run_list,
:short => "-r RUN_LIST",
:long => "--run-list RUN_LIST",
:description => "Comma separated list of roles/recipes to apply",
:proc => lambda { |o| o.split(/[\s,]+/) },
:default => []
option :first_boot_attributes,
:short => "-j JSON_ATTRIBS",
:long => "--json-attributes",
:description => "A JSON string to be added to the first run of chef-client",
:proc => lambda { |o| JSON.parse(o) },
:default => {}
option :host_key_verify,
:long => "--[no-]host-key-verify",
:description => "Verify host key, enabled by default.",
:boolean => true,
:default => true
option :hint,
:long => "--hint HINT_NAME[=HINT_FILE]",
:description => "Specify Ohai Hint to be set on the bootstrap target. Use multiple --hint options to specify multiple hints.",
:proc => Proc.new { |h|
Chef::Config[:knife][:hints] ||= Hash.new
name, path = h.split("=")
Chef::Config[:knife][:hints][name] = path ? JSON.parse(::File.read(path)) : Hash.new }
option :secret,
:short => "-s SECRET",
:long => "--secret ",
:description => "The secret key to use to encrypt data bag item values",
:proc => Proc.new { |s| Chef::Config[:knife][:secret] = s }
option :secret_file,
:long => "--secret-file SECRET_FILE",
:description => "A file containing the secret key to use to encrypt data bag item values",
:proc => Proc.new { |sf| Chef::Config[:knife][:secret_file] = sf }
def find_template(template=nil)
# Are we bootstrapping using an already shipped template?
if config[:template_file]
bootstrap_files = config[:template_file]
else
bootstrap_files = []
bootstrap_files << File.join(File.dirname(__FILE__), 'bootstrap', "#{config[:distro]}.erb")
bootstrap_files << File.join(Knife.chef_config_dir, "bootstrap", "#{config[:distro]}.erb") if Knife.chef_config_dir
bootstrap_files << File.join(ENV['HOME'], '.chef', 'bootstrap', "#{config[:distro]}.erb") if ENV['HOME']
bootstrap_files << Gem.find_files(File.join("chef","knife","bootstrap","#{config[:distro]}.erb"))
bootstrap_files.flatten!
end
template = Array(bootstrap_files).find do |bootstrap_template|
Chef::Log.debug("Looking for bootstrap template in #{File.dirname(bootstrap_template)}")
File.exists?(bootstrap_template)
end
unless template
ui.info("Can not find bootstrap definition for #{config[:distro]}")
raise Errno::ENOENT
end
Chef::Log.debug("Found bootstrap template in #{File.dirname(template)}")
template
end
def render_template(template=nil)
context = Knife::Core::BootstrapContext.new(config, config[:run_list], Chef::Config)
Erubis::Eruby.new(template).evaluate(context)
end
def read_template
IO.read(@template_file).chomp
end
def run
validate_name_args!
warn_chef_config_secret_key
@template_file = find_template(config[:bootstrap_template])
@node_name = Array(@name_args).first
# back compat--templates may use this setting:
config[:server_name] = @node_name
$stdout.sync = true
ui.info("Bootstrapping Chef on #{ui.color(@node_name, :bold)}")
if config[:gen_template]
ui.info(ssh_command)
exit 0
end
begin
knife_ssh.run
rescue Net::SSH::AuthenticationFailed
unless config[:ssh_password]
ui.info("Failed to authenticate #{config[:ssh_user]} - trying password auth")
knife_ssh_with_password_auth.run
end
end
end
def validate_name_args!
if Array(@name_args).first.nil?
ui.error("Must pass an FQDN or ip to bootstrap")
exit 1
end
end
def server_name
Array(@name_args).first
end
def knife_ssh
ssh = Chef::Knife::Ssh.new
ssh.ui = ui
ssh.name_args = [ server_name, ssh_command ]
ssh.config[:ssh_user] = Chef::Config[:knife][:ssh_user] || config[:ssh_user]
ssh.config[:ssh_password] = config[:ssh_password]
ssh.config[:ssh_port] = Chef::Config[:knife][:ssh_port] || config[:ssh_port]
ssh.config[:ssh_gateway] = Chef::Config[:knife][:ssh_gateway] || config[:ssh_gateway]
ssh.config[:forward_agent] = Chef::Config[:knife][:forward_agent] || config[:forward_agent]
ssh.config[:identity_file] = Chef::Config[:knife][:identity_file] || config[:identity_file]
ssh.config[:manual] = true
ssh.config[:host_key_verify] = Chef::Config[:knife][:host_key_verify] || config[:host_key_verify]
ssh.config[:on_error] = :raise
ssh
end
def knife_ssh_with_password_auth
ssh = knife_ssh
ssh.config[:identity_file] = nil
ssh.config[:ssh_password] = ssh.get_password
ssh
end
def ssh_command
command = render_template(read_template)
if config[:use_sudo]
command = config[:use_sudo_password] ? "echo '#{config[:ssh_password]}' | sudo -S #{command}" : "sudo #{command}"
end
command
end
def warn_chef_config_secret_key
unless Chef::Config[:encrypted_data_bag_secret].nil?
ui.warn "* " * 40
ui.warn(<<-WARNING)
Specifying the encrypted data bag secret key using an 'encrypted_data_bag_secret'
entry in 'knife.rb' is deprecated. Please see CHEF-4011 for more details. You
can supress this warning and still distribute the secret key to all bootstrapped
machines by adding the following to your 'knife.rb' file:
knife[:secret_file] = "/path/to/your/secret"
If you would like to selectively distribute a secret key during bootstrap
please use the '--secret' or '--secret-file' options of this command instead.
#{ui.color('IMPORTANT:', :red, :bold)} In a future version of Chef, this
behavior will be removed and any 'encrypted_data_bag_secret' entries in
'knife.rb' will be ignored completely.
WARNING
ui.warn "* " * 40
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment