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 328
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[:identity_file] = config[:identity_file] || File.join(locate_config_value(:vagrant_dir), 'insecure_key')
  bootstrap.config[:chef_node_name] = locate_config_value(:chef_node_name) || server.ip
  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 271
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,instance_dir) click to toggle source
# File lib/chef/knife/vagrant_server_create.rb, line 219
def build_vmx_customize(customize,instance_dir)
  vmx_params = ""
  if customize
    customize.chomp! if customize.end_with?("\n")
    disks = {}
    customize.split(/::/).each do |p|
      kv = p.split('=')
      k = kv[0].strip
      v = kv[1].strip
      if v
        if k.start_with?('scsi')
          ss = k.split('.')
          disks[ss[0]] ||= {}
          case ss[1]
            when 'fileName'
              if v.start_with?('/')
                disks[ss[0]]['fileName'] = v
              else
                v = File.expand_path(v,instance_dir)
                disks[ss[0]]['fileName'] = v
              end
            when 'fileSize'
              disks[ss[0]]['fileSize'] = v
              next
          end
        end
        vmx_params += "v.vmx[\"#{k}\"] = \"#{v}\"\n"
      end
    end
    if locate_config_value(:provider).start_with?('vmware')
      # Create extra disks as unlike virtual box vmware-fusion/workstation
      # will not create disks automatically based on configuration params
      disks.each_value do |f|
        vdiskmgr = %x(which vmware-vdiskmanager)
        vdiskmgr = "/Applications/VMware Fusion.app/Contents/Library/vmware-vdiskmanager" if vdiskmgr.empty?
        if File.exist?(vdiskmgr)
          disk = f['fileName']
          %x("#{vdiskmgr}" -c -t 0 -s #{f['fileSize']} -a ide #{disk}) unless File.exist?(f['fileName'])
          unless File.exist?(disk)
            ui.error("Disk #{disk} could not be created.")
            exit 1
          end
        else
          ui.error("Unable to determine path to vmware-vdiskmanager to create the requested additional disk.")
          exit 1
        end
      end
    end
  end
  vmx_params
end
create_server_def() click to toggle source
# File lib/chef/knife/vagrant_server_create.rb, line 384
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_parameters => locate_config_value(:vmx_parameters),
    :inline_config => config[:inline_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 370
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 173
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)

  write_insecure_key
  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 448
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 425
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 363
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 441
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 413
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 417
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 278
      def write_vagrantfile

        # Create folder and write Vagrant file
        instance_dir = File.join(locate_config_value(:vagrant_dir), @server.name)
        FileUtils.mkdir_p(instance_dir)

        additions = []
        if @server.use_cachier
          additions << 'config.cache.auto_detect = true' # enable vagarant-cachier
        end
        if @server.inline_config
          additions << @server.inline_config
        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_parameters,instance_dir)}
  end

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

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

        instance_file = File.join(instance_dir, 'Vagrantfile')
        ui.msg("Creating #{instance_file}")
        File.open(instance_file, 'w') { |f| f.write(file) }
      end