class Chef::Knife::BmcsServerCreate

Server Create Command: Launch an instance and bootstrap it.

Public Instance Methods

bootstrap(name) click to toggle source
# File lib/chef/knife/bmcs_server_create.rb, line 173
def bootstrap(name)
  bootstrap = Chef::Knife::Bootstrap.new

  bootstrap.name_args = [name]
  bootstrap.config[:chef_node_name] = config[:chef_node_name]
  bootstrap.config[:ssh_user] = config[:ssh_user]
  bootstrap.config[:ssh_password] = config[:ssh_password]
  bootstrap.config[:identity_file] = config[:identity_file]
  bootstrap.config[:use_sudo] = true
  bootstrap.config[:ssh_gateway] = config[:ssh_user] + '@' + name
  bootstrap.config[:run_list] = config[:run_list]

  bootstrap.config[:yes] = true if config[:yes]

  bootstrap.run
end
can_ssh(hostname, ssh_port) click to toggle source
# File lib/chef/knife/bmcs_server_create.rb, line 229
def can_ssh(hostname, ssh_port)
  socket = TCPSocket.new(hostname, ssh_port)
  # Wait up to 5 seconds.
  readable = IO.select([socket], nil, nil, 5)
  if readable
    content = socket.gets
    # Make sure some content was actually returned.
    return true unless content.nil? || content.empty?
  else
    false
  end
rescue SocketError, IOError, Errno::ETIMEDOUT, Errno::EPERM, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, Errno::ECONNRESET, Errno::ENOTCONN
  false
ensure
  socket && socket.close
end
end_progress_indicator() click to toggle source
# File lib/chef/knife/bmcs_server_create.rb, line 315
def end_progress_indicator
  print ui.color("done\n", :magenta)
end
get_file_content(file_name_param) click to toggle source
# File lib/chef/knife/bmcs_server_create.rb, line 319
def get_file_content(file_name_param)
  file_name = config[file_name_param]
  return if file_name.nil?

  file_name = File.expand_path(file_name)
  File.open(file_name, 'r').read
end
get_vnic(instance_id, compartment) click to toggle source

Return the first VNIC found (which should be the only VNIC).

# File lib/chef/knife/bmcs_server_create.rb, line 272
def get_vnic(instance_id, compartment)
  compute_client.list_vnic_attachments(compartment, instance_id: instance_id).each do |response|
    response.data.each do |vnic_attachment|
      return network_client.get_vnic(vnic_attachment.vnic_id).data
    end
  end
end
merge_metadata() click to toggle source
# File lib/chef/knife/bmcs_server_create.rb, line 280
def merge_metadata
  metadata = config[:metadata]

  if metadata
    begin
      metadata = JSON.parse(metadata)
    rescue JSON::ParserError
      error_and_exit('Metadata value must be in JSON format. Example: \'{"key1":"value1", "key2":"value2"}\'')
    end
  else
    metadata = {}
  end

  ssh_authorized_keys = get_file_content(:ssh_authorized_keys_file)
  user_data = get_file_content(:user_data_file)
  user_data = Base64.strict_encode64(user_data) if user_data

  if ssh_authorized_keys
    error_and_exit('Cannot specify ssh-authorized-keys as part of both --ssh-authorized-keys-file and --metadata.') if metadata.key? 'ssh_authorized_keys'
    metadata['ssh_authorized_keys'] = ssh_authorized_keys
  end

  if user_data
    error_and_exit('Cannot specify CloudInit user-data as part of both --user-data-file and --metadata.') if metadata.key? 'user_data'
    metadata['user_data'] = user_data
  end

  metadata
end
run() click to toggle source
# File lib/chef/knife/bmcs_server_create.rb, line 116
def run
  $stdout.sync = true
  validate_required_params(%i[availability_domain image_id shape subnet_id identity_file ssh_authorized_keys_file], config)
  validate_wait_options

  metadata = merge_metadata
  error_and_exit 'SSH authorized keys must be specified.' unless metadata['ssh_authorized_keys']

  request = OracleBMC::Core::Models::LaunchInstanceDetails.new
  request.availability_domain = config[:availability_domain]
  request.compartment_id = compartment_id
  request.display_name = config[:display_name]
  request.hostname_label = config[:hostname_label]
  request.image_id = config[:image_id]
  request.metadata = metadata
  request.shape = config[:shape]
  request.subnet_id = config[:subnet_id]

  response = compute_client.launch_instance(request)
  instance = response.data

  ui.msg "Launched instance '#{instance.display_name}' [#{instance.id}]"
  show_value('Display Name', instance.display_name)
  show_value('Instance ID', instance.id)
  show_value('Availability Domain', instance.availability_domain)
  show_value('Compartment ID', instance.compartment_id)
  show_value('Region', instance.region)
  show_value('Image ID', instance.image_id)
  show_value('Shape', instance.shape)

  instance = wait_for_instance_running(instance.id)

  ui.msg "Instance '#{instance.display_name}' is now running."

  vnic = get_vnic(instance.id, instance.compartment_id)
  show_value('Public IP Address', vnic.public_ip)
  show_value('Private IP Address', vnic.private_ip)

  unless wait_for_ssh(vnic.public_ip, SSH_PORT, WAIT_FOR_SSH_INTERVAL_SECONDS, config[:wait_for_ssh_max])
    error_and_exit 'Timed out while waiting for SSH access.'
  end

  wait_to_stabilize

  config[:chef_node_name] = instance.display_name unless config[:chef_node_name]

  ui.msg "Bootstrapping with node name '#{config[:chef_node_name]}'."

  # TODO: Consider adding a use_private_ip option.
  bootstrap(vnic.public_ip)

  ui.msg "Created and bootstrapped node '#{config[:chef_node_name]}'."
  ui.msg "\n"

  display_server_info(config, instance, [vnic])
end
show_progress() click to toggle source
# File lib/chef/knife/bmcs_server_create.rb, line 310
def show_progress
  print ui.color('.', :magenta)
  $stdout.flush
end
validate_wait_option(p, default) click to toggle source
# File lib/chef/knife/bmcs_server_create.rb, line 190
def validate_wait_option(p, default)
  arg_name = "--#{p.to_s.tr('_', '-')}"
  config[p] = config[p].to_s.empty? ? default : Integer(config[p])
  error_and_exit "#{arg_name} must be 0 or greater" if config[p] < 0
rescue
  error_and_exit "#{arg_name} must be numeric"
end
validate_wait_options() click to toggle source
# File lib/chef/knife/bmcs_server_create.rb, line 198
def validate_wait_options
  validate_wait_option(:wait_to_stabilize, DEFAULT_WAIT_TO_STABILIZE_SECONDS)
  validate_wait_option(:wait_for_ssh_max, DEFAULT_WAIT_FOR_SSH_MAX_SECONDS)
end
wait_for_instance_running(instance_id) click to toggle source
# File lib/chef/knife/bmcs_server_create.rb, line 246
def wait_for_instance_running(instance_id)
  print ui.color('Waiting for instance to reach running state...', :magenta)

  begin
    response = compute_client.get_instance(instance_id).wait_until(:lifecycle_state,
                                                                   OracleBMC::Core::Models::Instance::LIFECYCLE_STATE_RUNNING,
                                                                   max_interval_seconds: 3) do |poll_response|
      if poll_response.data.lifecycle_state == OracleBMC::Core::Models::Instance::LIFECYCLE_STATE_TERMINATED ||
         poll_response.data.lifecycle_state == OracleBMC::Core::Models::Instance::LIFECYCLE_STATE_TERMINATING
        throw :stop_succeed
      end

      show_progress
    end
  ensure
    end_progress_indicator
  end

  if response.data.lifecycle_state != OracleBMC::Core::Models::Instance::LIFECYCLE_STATE_RUNNING
    error_and_exit 'Instance failed to provision.'
  end

  response.data
end
wait_for_ssh(hostname, ssh_port, interval_seconds, max_time_seconds) click to toggle source
# File lib/chef/knife/bmcs_server_create.rb, line 210
def wait_for_ssh(hostname, ssh_port, interval_seconds, max_time_seconds)
  print ui.color('Waiting for ssh access...', :magenta)

  end_time = Time.now + max_time_seconds

  begin
    while Time.now < end_time
      return true if can_ssh(hostname, ssh_port)

      show_progress
      sleep interval_seconds
    end
  ensure
    end_progress_indicator
  end

  false
end
wait_to_stabilize() click to toggle source
# File lib/chef/knife/bmcs_server_create.rb, line 203
def wait_to_stabilize
  # This extra sleep even after getting SSH access is necessary. It's not clear why, but without it we often get
  # errors about missing a password for ssh, or sometimes errors during bootstrapping. (Note that plugins for other
  # cloud providers have similar sleeps.)
  Kernel.sleep(config[:wait_to_stabilize])
end