class Chef::Knife::VagrantServerCreate

Public Instance Methods

bootstrap_node(server,ssh_host) click to toggle source
# File lib/chef/knife/vagrant_server_create.rb, line 281
def bootstrap_node(server,ssh_host)
  bootstrap = Chef::Knife::Bootstrap.new
  bootstrap.name_args = [ssh_host]
  bootstrap.config[:ssh_user] = config[:ssh_user]
  bootstrap.config[:ssh_port] = config[:ssh_port]
  bootstrap.config[:ssh_gateway] = config[:ssh_gateway]
  bootstrap.config[:chef_node_name] = locate_config_value(:chef_node_name) || server.ip

  # Starting from version 1.7 Vagrant replaces the fixed insecure key with a new,
  # random one the first time it is brought up. If that's the case we need to use
  # it to bootstrap.
  msg_pair("Vagrant version", vagrant_version)
  if config[:identity_file]
    bootstrap.config[:identity_file] = config[:identity_file]
  elsif vagrant_version_cmp('1.7') < 0
    write_insecure_key
    bootstrap.config[:identity_file] = File.join(locate_config_value(:vagrant_dir), 'insecure_key')
  else
    # Vagrant puts the private key under the provider (i.e. 'virtualbox') so we search for it
    path = File.join(locate_config_value(:vagrant_dir), server.name, '.vagrant/')
    Find.find(path) do |f|
      if File.basename(f) == 'private_key'
        bootstrap.config[:identity_file] = f
      end
    end
  end

  bootstrap.config[:distro] = locate_config_value(:distro) || "chef-full"
  bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
  bootstrap.config[:host_key_verify] = config[:host_key_verify]

  bootstrap.config[:run_list] = config[:run_list]
  bootstrap.config[:prerelease] = config[:prerelease]
  bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
  bootstrap.config[:distro] = locate_config_value(:distro)
  bootstrap.config[:template_file] = locate_config_value(:template_file)
  bootstrap.config[:environment] = locate_config_value(:environment)
  bootstrap.config[:prerelease] = config[:prerelease]
  bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
  bootstrap.config[:first_boot_attributes] = locate_config_value(:json_attributes) || {}
  bootstrap.config[:encrypted_data_bag_secret] = locate_config_value(:encrypted_data_bag_secret)
  bootstrap.config[:encrypted_data_bag_secret_file] = locate_config_value(:encrypted_data_bag_secret_file)
  bootstrap.config[:secret] = locate_config_value(:secret)
  bootstrap.config[:secret_file] = locate_config_value(:secret_file)
  # Modify global configuration state to ensure hint gets set by
  # knife-bootstrap
  Chef::Config[:knife][:hints] ||= {}
  Chef::Config[:knife][:hints]["vagrant"] ||= {} # Cargo Cult programming FTW?

  msg_pair("SSH User", bootstrap.config[:ssh_user])
  msg_pair("SSH identity file", bootstrap.config[:identity_file])
  bootstrap
end
build_port_forwards(ports) click to toggle source
# File lib/chef/knife/vagrant_server_create.rb, line 211
def build_port_forwards(ports)
  ports.collect { |k, v| "config.vm.network :forwarded_port, host: #{k}, guest: #{v}, host_ip: '127.0.0.1'" }.join("\n")
end
build_shares(share_folders) click to toggle source
# File lib/chef/knife/vagrant_server_create.rb, line 223
def build_shares(share_folders)
  share_folders.collect do |share|
    host, guest = share.chomp.split "::"
    "config.vm.synced_folder '#{host}', '#{guest}'"
  end.join("\n")
end
build_vb_customize(customize) click to toggle source
# File lib/chef/knife/vagrant_server_create.rb, line 215
def build_vb_customize(customize)
  customize.split(/::/).collect { |k| "vb.customize [ #{k} ]" }.join("\n") if customize
end
build_vmx_customize(customize) click to toggle source
# File lib/chef/knife/vagrant_server_create.rb, line 219
def build_vmx_customize(customize)
  customize.split(/::/).collect { |k| "v.vmx[#{k.split('=')[0]}] = #{k.split('=')[1]}" }.join("\n") if customize
end
create_server_def() click to toggle source
# File lib/chef/knife/vagrant_server_create.rb, line 356
def create_server_def
  server_def = {
    :box => locate_config_value(:box),
    :box_url => locate_config_value(:box_url),
    :memsize => locate_config_value(:memsize),
    :share_folders => config[:share_folders],
    :port_forward => config[:port_forward],
    :use_cachier => config[:use_cachier],
    :vb_customize => locate_config_value(:vb_customize),
    :vmx_customize => locate_config_value(:vmx_customize),
    :vagrant_config => locate_config_value(:vagrant_config)
  }

  # Get specified IP address for new instance or pick an unused one from the subnet pool.
  server_def[:ip_address] = config[:ip_address] || find_available_ip

  collision = vagrant_instance_list.detect { |i| i[:ip_address] == server_def[:ip_address] }
  if collision
    ui.error("IP address #{server_def[:ip_address]} already in use by instance #{collision[:name]}")
    exit 1
  end

  # Derive name for vagrant instance from chef node name or IP
  server_def[:name] = locate_config_value(:chef_node_name) || server_def[:ip_address]

  # Turn it into and object like thing
  OpenStruct.new(server_def)
end
find_available_ip() click to toggle source
# File lib/chef/knife/vagrant_server_create.rb, line 342
def find_available_ip
  subnet = locate_config_value('subnet')
  IPAddr.new(subnet).to_range().each { |ip|
    # 192.168.3.0/24 should yield 192.168.3.2 through 192.168.3.254
    # 192.168.3.1 cannot be used because virtual box uses it for the router
    mask = IPAddr::IN4MASK ^ ip.instance_variable_get("@mask_addr")
    unless [0, 1, mask].include? (ip & mask) or vagrant_instance_list.detect { |i| i[:ip_address] == ip.to_s } 
      return ip.to_s
    end
  }
  ui.error("No unused IP address available in subnet #{subnet}")
  exit 1
end
run() click to toggle source
# File lib/chef/knife/vagrant_server_create.rb, line 174
def run
  $stdout.sync = true
  validate!

  @server = create_server_def

  msg_pair("Instance name", @server.name)
  msg_pair("Instance IP", @server.ip_address)
  msg_pair("Box", @server.box || @server.box_url)

  if vagrant_instance_list.detect { |i| i[:name] == @server.name } 
      ui.error("Instance #{@server.name} already exists")
      exit 1
  end

  # Create Vagrant file for new instance
  print "\n#{ui.color("Launching instance", :magenta)}\n"
  write_vagrantfile
  cmd = "up --provider #{locate_config_value(:provider)}"
  vagrant_exec(@server.name, cmd)

  print "\n#{ui.color("Waiting for sshd", :magenta)}"
  wait_for_sshd(@server.ip_address)

  print "\n#{ui.color("Bootstraping instance", :magenta)}\n"
  bootstrap_node(@server,@server.ip_address).run


  puts "\n"
  msg_pair("Instance Name", @server.name)
  msg_pair("Box", @server.box || @server.box_url)
  msg_pair("IP Address", @server.ip_address)
  msg_pair("Environment", locate_config_value(:environment) || '_default')
  msg_pair("Run List", (config[:run_list] || []).join(', '))
  msg_pair("JSON Attributes", config[:json_attributes]) unless !config[:json_attributes] || config[:json_attributes].empty?
end
tcp_test_ssh(hostname, ssh_port) { || ... } click to toggle source
# File lib/chef/knife/vagrant_server_create.rb, line 420
def tcp_test_ssh(hostname, ssh_port)
  tcp_socket = TCPSocket.new(hostname, ssh_port)
  readable = IO.select([tcp_socket], nil, nil, 5)
  if readable
    Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{tcp_socket.gets}")
    yield
    true
  else
    false
  end
rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError
  sleep 2
  false
rescue Errno::EPERM, Errno::ETIMEDOUT
  false
# This happens on some mobile phone networks
rescue Errno::ECONNRESET
  sleep 2
  false
ensure
  tcp_socket && tcp_socket.close
end
tunnel_test_ssh(hostname, &block) click to toggle source
# File lib/chef/knife/vagrant_server_create.rb, line 397
def tunnel_test_ssh(hostname, &block)
  gw_host, gw_user = config[:ssh_gateway].split('@').reverse
  gw_host, gw_port = gw_host.split(':')
  gateway = Net::SSH::Gateway.new(gw_host, gw_user, :port => gw_port || 22)
  status = false
  gateway.open(hostname, config[:ssh_port]) do |local_tunnel_port|
    status = tcp_test_ssh('localhost', local_tunnel_port, &block)
  end
  status
rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError
  sleep 2
  false
rescue Errno::EPERM, Errno::ETIMEDOUT
  false
end
validate!() click to toggle source
# File lib/chef/knife/vagrant_server_create.rb, line 335
def validate!
  unless locate_config_value(:box) || locate_config_value(:box_url)
    ui.error("You need to either specify --box or --box-url")
    exit 1
  end
end
wait_for_direct_sshd(hostname, ssh_port) click to toggle source
# File lib/chef/knife/vagrant_server_create.rb, line 413
def wait_for_direct_sshd(hostname, ssh_port)
  print(".") until tcp_test_ssh(hostname, ssh_port) {
    sleep @initial_sleep_delay ||= 2
    puts("done")
  }
end
wait_for_sshd(hostname) click to toggle source
# File lib/chef/knife/vagrant_server_create.rb, line 385
def wait_for_sshd(hostname)
  config[:ssh_gateway] ? wait_for_tunnelled_sshd(hostname) : wait_for_direct_sshd(hostname, config[:ssh_port])
end
wait_for_tunnelled_sshd(hostname) click to toggle source
# File lib/chef/knife/vagrant_server_create.rb, line 389
def wait_for_tunnelled_sshd(hostname)
  print(".")
  print(".") until tunnel_test_ssh(hostname) {
    sleep @initial_sleep_delay ||= 2
    puts("done")
  }
end
write_vagrantfile() click to toggle source
# File lib/chef/knife/vagrant_server_create.rb, line 230
      def write_vagrantfile
        additions = []
        if @server.use_cachier
          additions << 'config.cache.auto_detect = true' # enable vagarant-cachier
        end
        if @server.vagrant_config
          additions << @server.vagrant_config.split(/::/)
        end
        unless config[:identity_file].nil?
          additions << 'config.ssh.private_key_path = "' + config[:identity_file] + '"'
        end

        file = <<-EOF
Vagrant.configure("2") do |config|
  config.vm.box = "#{@server.box}"
  config.vm.box_url = "#{@server.box_url}"
  config.vm.hostname = "#{@server.name}"

  config.vm.network :private_network, ip: "#{@server.ip_address}"
  #{build_port_forwards(@server.port_forward)}

  #{build_shares(@server.share_folders)}
 
  config.vm.provider :virtualbox do |vb|
    vb.customize [ "modifyvm", :id, "--memory", #{@server.memsize} ]
    #{build_vb_customize(@server.vb_customize)}
  end
  
  config.vm.provider :vmware_fusion do |v|
    v.vmx["memsize"] = "#{@server.memsize}"
    #{build_vmx_customize(@server.vmx_customize)}
  end

  config.vm.provider :vmware_workstation do |v|
    v.vmx["memsize"] = "#{@server.memsize}"
    #{build_vmx_customize(@server.vmx_customize)}
  end

  #{additions.join("\n")}
end
        EOF
        file

        # Create folder and write Vagrant file
        instance_dir = File.join(locate_config_value(:vagrant_dir), @server.name)
        instance_file = File.join(instance_dir, 'Vagrantfile')
        ui.msg("Creating #{instance_file}")
        FileUtils.mkdir_p(instance_dir)
        File.open(instance_file, 'w') { |f| f.write(file) }
      end