Created
April 8, 2014 19:34
-
-
Save popsikle/10177600 to your computer and use it in GitHub Desktop.
Quick and dirty method of outputting the rendered bootstrap template (knife modification)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# 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