class Chef::Knife::JoyentServerCreate

Public Instance Methods

bootstrap_for_node(server, bootstrap_ip) click to toggle source

Run Chef bootstrap script

# File lib/chef/knife/joyent_server_create.rb, line 228
def bootstrap_for_node(server, bootstrap_ip)
  bootstrap = Chef::Knife::Bootstrap.new
  Chef::Log.debug("Bootstrap name_args = [ #{bootstrap_ip} ]")
  bootstrap.name_args = [ bootstrap_ip ]

  Chef::Log.debug("Bootstrap run_list = #{config[:run_list]}")
  bootstrap.config[:run_list] = config[:run_list]

  Chef::Log.debug("Bootstrap ssh_user = #{config[:ssh_user]}")
  bootstrap.config[:ssh_user] = config[:ssh_user]

  Chef::Log.debug("Bootstrap chef_node_name = #{config[:chef_node_name]}")
  bootstrap.config[:chef_node_name] = config[:chef_node_name] || server.id

  Chef::Log.debug("Bootstrap prerelease = #{config[:prerelease]}")
  bootstrap.config[:prerelease] = config[:prerelease]

  Chef::Log.debug("Bootstrap distro = #{config[:distro]}")
  bootstrap.config[:distro] = config[:distro]

  if config[:ssh_user] == 'root'
    bootstrap.config[:use_sudo] = false
  else
    bootstrap.config[:use_sudo] = true
  end

  Chef::Log.debug("Bootstrap use_sudo = #{bootstrap.config[:use_sudo]}")

  bootstrap.config[:environment] = config[:environment]
  Chef::Log.debug("Bootstrap environment = #{bootstrap.config[:environment]}")

  bootstrap.config[:no_host_key_verify] = config[:no_host_key_verify]
  Chef::Log.debug("Bootstrap no_host_key_verify = #{bootstrap.config[:no_host_key_verify]}")

  bootstrap.config[:identity_file] = config[:identity_file]
  Chef::Log.debug("Bootstrap identity_file = #{bootstrap.config[:identity_file]}")

  bootstrap.config[:ssh_gateway] = determine_ssh_gateway(bootstrap_ip)
  Chef::Log.debug("Bootstrap ssh_gateway= #{bootstrap.config[:ssh_gateway]}")

  bootstrap.config[:first_boot_attributes] = config[:json_attributes]
  Chef::Log.debug("Bootstrap json_attributes = #{bootstrap.config[:json_attributes]}")

  bootstrap
end
determine_bootstrap_ip(server) click to toggle source
# File lib/chef/knife/joyent_server_create.rb, line 302
def determine_bootstrap_ip(server)
  server_ips = server.ips.select{ |ip|
    ip and not(is_loopback(ip) or is_linklocal(ip))
  }
  if server_ips.count === 1
    server_ips.first
  else
    if server_ips.all? {|ip| is_private(ip)} || config[:private_network]
      server_ips.find{|ip| is_private(ip)}
    else
      server_ips.find{|ip| not is_private(ip)}
    end
  end
end
determine_ssh_gateway(hostname) click to toggle source

src: github.com/chef/knife-ec2/blob/master/lib/chef/knife/ec2_server_create.rb#L741-L770

# File lib/chef/knife/joyent_server_create.rb, line 275
def determine_ssh_gateway(hostname)
  # ssh_gateway config takes precedence over derived value
  if config[:ssh_gateway]
    Chef::Log.debug("Using ssh gateway #{config[:ssh_gateway]} from knife config")
    return config[:ssh_gateway]
  end

  ssh_proxy = Net::SSH::Config.for(hostname)[:proxy]
  if ssh_proxy.respond_to?(:command_line_template)
    # ssh gateway_hostname nc %h %p
    proxy_pattern = /ssh\s+(\S+)\s+nc/
    matchdata = proxy_pattern.match(ssh_proxy.command_line_template)
    if matchdata.nil?
      Chef::Log.debug("Unable to determine ssh gateway for '#{hostname}' from ssh config template: #{ssh_proxy.command_line_template}")
      nil
    else
      # Return hostname extracted from command line template
      Chef::Log.debug("Using ssh gateway #{matchdata[1]} from ssh config")
      matchdata[1]
    end
  else
    # Return nil if we cannot find an ssh_gateway
    Chef::Log.debug("No ssh gateway found, making a direct connection")
    nil
  end
end
is_linklocal(ip) click to toggle source
# File lib/chef/knife/joyent_server_create.rb, line 118
def is_linklocal(ip)
  linklocal = IPAddr.new "169.254.0.0/16"
  return linklocal.include?(ip)
end
is_loopback(ip) click to toggle source
# File lib/chef/knife/joyent_server_create.rb, line 123
def is_loopback(ip)
  loopback = IPAddr.new "127.0.0.0/8"
  return loopback.include?(ip)
end
is_private(ip) click to toggle source
# File lib/chef/knife/joyent_server_create.rb, line 128
def is_private(ip)
  block_a = IPAddr.new "10.0.0.0/8"
  block_b = IPAddr.new "172.16.0.0/12"
  block_c = IPAddr.new "192.168.0.0/16"
  return (block_a.include?(ip) or block_b.include?(ip) or block_c.include?(ip))
end
run() click to toggle source
# File lib/chef/knife/joyent_server_create.rb, line 151
def run
  $stdout.sync = true

  validate_server_name

  @node_name = config[:chef_node_name] || config[:server_name]

  puts ui.color("Creating machine #{@node_name}", :cyan)

  server = connection.servers.create(server_creation_options)

  puts ui.color("Waiting for Server to be Provisioned", :magenta)
  server.wait_for { print "."; ready? }

  bootstrap_ip = self.determine_bootstrap_ip(server)

  unless bootstrap_ip
    puts ui.error("No IP address available for bootstrapping.")
    exit 1
  end

  Chef::Log.debug("Bootstrap IP Address #{bootstrap_ip}")
  puts "\n"
  puts ui.color("Bootstrap IP Address #{bootstrap_ip}", :cyan)

  if config[:tags]
    tags = [ ui.color('Name', :bold), ui.color('Value', :bold) ]

    server.add_tags(config[:tags]).each do |k,v|
      tags << k
      tags << v
    end

    puts ui.color("Updated tags for #{@node_name}", :cyan)
    puts ui.list(tags, :uneven_columns_across, 2)
  end

  if Chef::Config[:knife][:provisioner]
    tags = [ ui.color('Name', :bold), ui.color('Value', :bold) ]
    # tag the provision with 'provisioner'
    server.add_tags({'provisioner' => Chef::Config[:knife][:provisioner]}).each do |k, v|
      tags << k
      tags << v
    end
    puts ui.color("Updated tags for #{@node_name}", :cyan)
    puts ui.list(tags, :uneven_columns_across, 2)
  else
    puts ui.color("No user defined in knife config for provision tagging -- continuing", :magenta)
  end

  puts ui.color("Created machine:", :cyan)
  msg_pair("ID", server.id.to_s)
  msg_pair("Name", server.name)
  msg_pair("State", server.state)
  msg_pair("Type", server.type)
  msg_pair("Dataset", server.dataset)
  msg_pair("IPs", server.ips.join(" "))
  msg_pair("Tags", config[:tags]) if config[:tags]
  msg_pair("JSON Attributes",config[:json_attributes]) unless config[:json_attributes].empty?

  puts ui.color("Waiting for server to fully initialize...", :cyan)
  sleep 20

  puts ui.color("Waiting for SSH to come up on: #{bootstrap_ip}", :cyan)
  tcp_test_ssh(bootstrap_ip)

  puts ui.color("Sleeping for #{config[:ssh_init_delay]} seconds", :cyan)
  sleep config[:ssh_init_delay]

  bootstrap_for_node(server, bootstrap_ip).run

rescue => e
  output_error(e)
  exit 1
end
tcp_test_ssh(hostname) click to toggle source
# File lib/chef/knife/joyent_server_create.rb, line 135
def tcp_test_ssh(hostname)
  ssh_test_max = 10*60
  ssh_test = 0
  until _tcp_test_ssh(hostname)
    if ssh_test < ssh_test_max
      print(".")
      ssh_test += 1
      sleep 1
    else
      puts ui.error("Unable to ssh to node (#{bootstrap_ip_address}), exiting")
      exit 1
    end
  end
end

Private Instance Methods

_tcp_test_ssh(hostname) click to toggle source
# File lib/chef/knife/joyent_server_create.rb, line 346
def _tcp_test_ssh(hostname)
  tcp_socket = TCPSocket.new(hostname, 22)
  readable = IO.select([tcp_socket], nil, nil, 5)
  if readable
    banner = tcp_socket.gets
    puts ui.color("SSHD accepting connections on #{hostname}: banner is #{banner}")
    Chef::Log.debug("SSHD accepting connections on #{hostname}: banner is #{banner}")
    true
  else
    false
  end
rescue SocketError,
       IOError,
       Errno::ETIMEDOUT,
       Errno::EPERM,
       Errno::ECONNREFUSED,
       Errno::ECONNRESET,
       Errno::ENETUNREACH,
       Errno::EHOSTUNREACH
  sleep 2
  false
ensure
  tcp_socket && tcp_socket.close
end
joyent_metadata() click to toggle source
# File lib/chef/knife/joyent_server_create.rb, line 338
def joyent_metadata
  metadata = Chef::Config[:knife][:joyent_metadata] || {}
  metadata.merge!(config[:joyent_metadata])

  return {} if metadata.empty?
  Hash[metadata.map { |k, v| ["metadata.#{k}", v] }]
end
server_creation_options() click to toggle source
# File lib/chef/knife/joyent_server_create.rb, line 319
def server_creation_options
  o = {
      :name => @node_name,
      :dataset => config[:dataset],
      :package => config[:package]
  }.merge!(joyent_metadata)
  o.merge!(:networks => config[:networks]) if config[:networks]
  o
end
validate_server_name() click to toggle source
# File lib/chef/knife/joyent_server_create.rb, line 329
def validate_server_name
  # add some validation here ala knife-ec2
  unless config[:server_name] || config[:chef_node_name]
    ui.error("You have not provided a valid server or node name.")
    puts show_usage
    exit 1
  end
end