lib/chef/knife/ec2_server_create.rb in knife-ec2-0.6.6 vs lib/chef/knife/ec2_server_create.rb in knife-ec2-0.8.0
- old
+ new
@@ -69,10 +69,14 @@
option :associate_eip,
:long => "--associate-eip IP_ADDRESS",
:description => "Associate existing elastic IP address with instance after launch"
+ option :dedicated_instance,
+ :long => "--dedicated_instance",
+ :description => "Launch as a Dedicated instance (VPC ONLY)"
+
option :placement_group,
:long => "--placement-group PLACEMENT_GROUP",
:description => "The placement group to place a cluster compute instance",
:proc => Proc.new { |pg| Chef::Config[:knife][:placement_group] = pg }
@@ -135,15 +139,15 @@
option :bootstrap_version,
:long => "--bootstrap-version VERSION",
:description => "The version of Chef to install",
:proc => Proc.new { |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 }
+ :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 :distro,
:short => "-d DISTRO",
:long => "--distro DISTRO",
:description => "Bootstrap a distro using a template; default is 'chef-full'",
@@ -210,11 +214,11 @@
option :bootstrap_protocol,
:long => "--bootstrap-protocol protocol",
:description => "protocol to bootstrap windows servers. options: winrm/ssh",
:proc => Proc.new { |key| Chef::Config[:knife][:bootstrap_protocol] = key },
- :default => "winrm"
+ :default => nil
option :fqdn,
:long => "--fqdn FQDN",
:description => "Pre-defined FQDN",
:proc => Proc.new { |key| Chef::Config[:knife][:fqdn] = key },
@@ -243,106 +247,19 @@
:default => []
option :server_connect_attribute,
:long => "--server-connect-attribute ATTRIBUTE",
:short => "-a ATTRIBUTE",
- :description => "The EC2 server attribute to use for SSH connection",
+ :description => "The EC2 server attribute to use for SSH connection. Use this attr for creating VPC instances along with --associate-eip",
:default => nil
- def tcp_test_winrm(ip_addr, port)
- tcp_socket = TCPSocket.new(ip_addr, port)
- yield
- true
- rescue SocketError
- sleep 2
- false
- rescue Errno::ETIMEDOUT
- false
- rescue Errno::EPERM
- false
- rescue Errno::ECONNREFUSED
- sleep 2
- false
- rescue Errno::EHOSTUNREACH
- sleep 2
- false
- rescue Errno::ENETUNREACH
- sleep 2
- false
- ensure
- tcp_socket && tcp_socket.close
- end
+ option :associate_public_ip,
+ :long => "--associate-public-ip",
+ :description => "Associate public ip to VPC instance.",
+ :boolean => true,
+ :default => false
- 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
-
- def decrypt_admin_password(encoded_password, key)
- require 'base64'
- require 'openssl'
- private_key = OpenSSL::PKey::RSA.new(key)
- encrypted_password = Base64.decode64(encoded_password)
- password = private_key.private_decrypt(encrypted_password)
- password
- end
-
- def check_windows_password_available(server_id)
- response = connection.get_password_data(server_id)
- if not response.body["passwordData"]
- return false
- end
- response.body["passwordData"]
- end
-
- def windows_password
- if not locate_config_value(:winrm_password)
- if locate_config_value(:identity_file)
- print "\n#{ui.color("Waiting for Windows Admin password to be available", :magenta)}"
- print(".") until check_windows_password_available(@server.id) {
- sleep 1000 #typically is available after 30 mins
- puts("done")
- }
- response = connection.get_password_data(@server.id)
- data = File.read(locate_config_value(:identity_file))
- config[:winrm_password] = decrypt_admin_password(response.body["passwordData"], data)
- else
- ui.error("Cannot find SSH Identity file, required to fetch dynamically generated password")
- exit 1
- end
- else
- locate_config_value(:winrm_password)
- end
- end
-
- def load_winrm_deps
- require 'winrm'
- require 'em-winrm'
- require 'chef/knife/winrm'
- require 'chef/knife/bootstrap_windows_winrm'
- require 'chef/knife/bootstrap_windows_ssh'
- require 'chef/knife/core/windows_bootstrap_context'
- end
-
def run
$stdout.sync = true
validate!
@@ -396,33 +313,37 @@
# occasionally 'ready?' isn't, so retry a couple times if needed.
tries = 6
begin
create_tags(hashed_tags) unless hashed_tags.empty?
associate_eip(elastic_ip) if config[:associate_eip]
- rescue Fog::Compute::AWS::NotFound, Fog::Errors::Error => e
+ rescue Fog::Compute::AWS::NotFound, Fog::Errors::Error
raise if (tries -= 1) <= 0
ui.warn("server not ready, retrying tag application (retries left: #{tries})")
sleep 5
retry
end
if vpc_mode?
msg_pair("Subnet ID", @server.subnet_id)
+ msg_pair("Tenancy", @server.tenancy)
+ if config[:associate_public_ip]
+ msg_pair("Public DNS Name", @server.dns_name)
+ end
if elastic_ip
msg_pair("Public IP Address", @server.public_ip_address)
end
else
msg_pair("Public DNS Name", @server.dns_name)
msg_pair("Public IP Address", @server.public_ip_address)
msg_pair("Private DNS Name", @server.private_dns_name)
end
msg_pair("Private IP Address", @server.private_ip_address)
-
#Check if Server is Windows or Linux
if is_image_windows?
protocol = locate_config_value(:bootstrap_protocol)
+ protocol ||= 'winrm'
# Set distro to windows-chef-client-msi
config[:distro] = "windows-chef-client-msi" if (config[:distro].nil? || config[:distro] == "chef-full")
if protocol == 'winrm'
load_winrm_deps
print "\n#{ui.color("Waiting for winrm", :magenta)}"
@@ -437,15 +358,16 @@
sleep @initial_sleep_delay ||= (vpc_mode? ? 40 : 10)
puts("done")
}
ssh_override_winrm
end
- bootstrap_for_windows_node(@server,ssh_connect_host).run
+ bootstrap_for_windows_node(@server, ssh_connect_host).run
else
- wait_for_sshd(ssh_connect_host)
- ssh_override_winrm
- bootstrap_for_linux_node(@server,ssh_connect_host).run
+ print "\n#{ui.color("Waiting for sshd", :magenta)}"
+ wait_for_sshd(ssh_connect_host)
+ ssh_override_winrm
+ bootstrap_for_linux_node(@server, ssh_connect_host).run
end
puts "\n"
msg_pair("Instance ID", @server.id)
msg_pair("Flavor", @server.flavor_id)
@@ -478,10 +400,14 @@
if config[:ebs_optimized]
msg_pair("EBS is Optimized", @server.ebs_optimized.to_s)
end
if vpc_mode?
msg_pair("Subnet ID", @server.subnet_id)
+ msg_pair("Tenancy", @server.tenancy)
+ if config[:associate_public_ip]
+ msg_pair("Public DNS Name", @server.dns_name)
+ end
else
msg_pair("Public DNS Name", @server.dns_name)
msg_pair("Public IP Address", @server.public_ip_address)
msg_pair("Private DNS Name", @server.private_dns_name)
end
@@ -509,40 +435,39 @@
Chef::Config[:knife][:hints]["ec2"] ||= {}
bootstrap
end
def fetch_server_fqdn(ip_addr)
- require 'resolv'
- Resolv.getname(ip_addr)
+ require 'resolv'
+ Resolv.getname(ip_addr)
end
def bootstrap_for_windows_node(server, fqdn)
- if locate_config_value(:bootstrap_protocol) == 'winrm'
- if locate_config_value(:kerberos_realm)
- #Fetch AD/WINS based fqdn if any for Kerberos-based Auth
- fqdn = locate_config_value(:fqdn) || fetch_server_fqdn(server.private_ip_address)
- end
- bootstrap = Chef::Knife::BootstrapWindowsWinrm.new
- bootstrap.config[:winrm_user] = locate_config_value(:winrm_user)
- bootstrap.config[:winrm_password] = windows_password
- bootstrap.config[:winrm_transport] = locate_config_value(:winrm_transport)
- bootstrap.config[:kerberos_keytab_file] = locate_config_value(:kerberos_keytab_file)
- bootstrap.config[:kerberos_realm] = locate_config_value(:kerberos_realm)
- bootstrap.config[:kerberos_service] = locate_config_value(:kerberos_service)
- bootstrap.config[:ca_trust_file] = locate_config_value(:ca_trust_file)
- bootstrap.config[:winrm_port] = locate_config_value(:winrm_port)
-
+ if locate_config_value(:bootstrap_protocol) == 'winrm' || locate_config_value(:bootstrap_protocol) == nil
+ if locate_config_value(:kerberos_realm)
+ #Fetch AD/WINS based fqdn if any for Kerberos-based Auth
+ fqdn = locate_config_value(:fqdn) || fetch_server_fqdn(server.private_ip_address)
+ end
+ bootstrap = Chef::Knife::BootstrapWindowsWinrm.new
+ bootstrap.config[:winrm_user] = locate_config_value(:winrm_user)
+ bootstrap.config[:winrm_password] = windows_password
+ bootstrap.config[:winrm_transport] = locate_config_value(:winrm_transport)
+ bootstrap.config[:kerberos_keytab_file] = locate_config_value(:kerberos_keytab_file)
+ bootstrap.config[:kerberos_realm] = locate_config_value(:kerberos_realm)
+ bootstrap.config[:kerberos_service] = locate_config_value(:kerberos_service)
+ bootstrap.config[:ca_trust_file] = locate_config_value(:ca_trust_file)
+ bootstrap.config[:winrm_port] = locate_config_value(:winrm_port)
elsif locate_config_value(:bootstrap_protocol) == 'ssh'
- bootstrap = Chef::Knife::BootstrapWindowsSsh.new
- bootstrap.config[:ssh_user] = locate_config_value(:ssh_user)
- bootstrap.config[:ssh_password] = locate_config_value(:ssh_password)
- bootstrap.config[:ssh_port] = locate_config_value(:ssh_port)
- bootstrap.config[:identity_file] = locate_config_value(:identity_file)
- bootstrap.config[:no_host_key_verify] = locate_config_value(:no_host_key_verify)
+ bootstrap = Chef::Knife::BootstrapWindowsSsh.new
+ bootstrap.config[:ssh_user] = locate_config_value(:ssh_user)
+ bootstrap.config[:ssh_password] = locate_config_value(:ssh_password)
+ bootstrap.config[:ssh_port] = locate_config_value(:ssh_port)
+ bootstrap.config[:identity_file] = locate_config_value(:identity_file)
+ bootstrap.config[:no_host_key_verify] = locate_config_value(:no_host_key_verify)
else
- ui.error("Unsupported Bootstrapping Protocol. Supported : winrm, ssh")
- exit 1
+ ui.error("Unsupported Bootstrapping Protocol. Supported : winrm, ssh")
+ exit 1
end
bootstrap.name_args = [fqdn]
bootstrap.config[:chef_node_name] = config[:chef_node_name] || server.id
bootstrap_common_params(bootstrap)
end
@@ -570,11 +495,10 @@
def ami
@ami ||= connection.images.get(locate_config_value(:image))
end
def validate!
-
super([:image, :aws_ssh_key_id, :aws_access_key_id, :aws_secret_access_key])
if ami.nil?
ui.error("You have not provided a valid image (AMI) value. Please note the short option for this value recently changed from '-i' to '-I'.")
exit 1
@@ -582,15 +506,26 @@
if vpc_mode? and !!config[:security_groups]
ui.error("You are using a VPC, security groups specified with '-G' are not allowed, specify one or more security group ids with '-g' instead.")
exit 1
end
+
if !vpc_mode? and !!config[:private_ip_address]
ui.error("You can only specify a private IP address if you are using VPC.")
exit 1
end
+ if config[:dedicated_instance] and !vpc_mode?
+ ui.error("You can only specify a Dedicated Instance if you are using VPC.")
+ exit 1
+ end
+
+ if !vpc_mode? and config[:associate_public_ip]
+ ui.error("--associate-public-ip option only applies to VPC instances, and you have not specified a subnet id.")
+ exit 1
+ end
+
if config[:associate_eip]
eips = connection.addresses.collect{|addr| addr if addr.domain == eip_scope}.compact
unless eips.detect{|addr| addr.public_ip == config[:associate_eip] && addr.server_id == nil}
ui.error("Elastic IP requested is not available.")
@@ -627,10 +562,12 @@
}
server_def[:subnet_id] = locate_config_value(:subnet_id) if vpc_mode?
server_def[:private_ip_address] = locate_config_value(:private_ip_address) if vpc_mode?
server_def[:placement_group] = locate_config_value(:placement_group)
server_def[:iam_instance_profile_name] = locate_config_value(:iam_instance_profile)
+ server_def[:tenancy] = "dedicated" if vpc_mode? and locate_config_value(:dedicated_instance)
+ server_def[:associate_public_ip] = locate_config_value(:associate_public_ip) if vpc_mode? and config[:associate_public_ip]
if Chef::Config[:knife][:aws_user_data]
begin
server_def.merge!(:user_data => File.read(Chef::Config[:knife][:aws_user_data]))
rescue
@@ -681,13 +618,18 @@
def wait_for_sshd(hostname)
config[:ssh_gateway] ? wait_for_tunnelled_sshd(hostname) : wait_for_direct_sshd(hostname, config[:ssh_port])
end
def wait_for_tunnelled_sshd(hostname)
- print(".")
- print(".") until tunnel_test_ssh(ssh_connect_host) {
- sleep @initial_sleep_delay ||= (vpc_mode? ? 40 : 10)
+ initial = true
+ print(".") until tunnel_test_ssh(hostname) {
+ if initial
+ initial = false
+ sleep (vpc_mode? ? 40 : 10)
+ else
+ sleep 10
+ end
puts("done")
}
end
def tunnel_test_ssh(hostname, &block)
@@ -705,22 +647,32 @@
rescue Errno::EPERM, Errno::ETIMEDOUT
false
end
def wait_for_direct_sshd(hostname, ssh_port)
- print(".") until tcp_test_ssh(ssh_connect_host, ssh_port) {
- sleep @initial_sleep_delay ||= (vpc_mode? ? 40 : 10)
+ initial = true
+ print(".") until tcp_test_ssh(hostname, ssh_port) {
+ if initial
+ initial = false
+ sleep (vpc_mode? ? 40 : 10)
+ else
+ sleep 10
+ end
puts("done")
}
end
def ssh_connect_host
@ssh_connect_host ||= if config[:server_connect_attribute]
- server.send(config[:server_connect_attribute])
- else
- vpc_mode? ? server.private_ip_address : server.dns_name
- end
+ server.send(config[:server_connect_attribute])
+ else
+ if vpc_mode? && !config[:associate_public_ip]
+ server.private_ip_address
+ else
+ server.dns_name
+ end
+ end
end
def create_tags(hashed_tags)
hashed_tags.each_pair do |key,val|
connection.tags.create :key => key, :value => val, :resource_id => @server.id
@@ -751,9 +703,110 @@
# unset identity_file and set kerberos_keytab_file, override identity_file
if locate_config_value(:identity_file).nil? &&
!locate_config_value(:kerberos_keytab_file).nil?
config[:identity_file] = locate_config_value(:kerberos_keytab_file)
end
+ end
+
+ def tcp_test_winrm(ip_addr, port)
+ tcp_socket = TCPSocket.new(ip_addr, port)
+ yield
+ true
+ rescue SocketError
+ sleep 2
+ false
+ rescue Errno::ETIMEDOUT
+ false
+ rescue Errno::EPERM
+ false
+ rescue Errno::ECONNREFUSED
+ sleep 2
+ false
+ rescue Errno::EHOSTUNREACH
+ sleep 2
+ false
+ rescue Errno::ENETUNREACH
+ sleep 2
+ false
+ ensure
+ tcp_socket && tcp_socket.close
+ end
+
+ def tcp_test_ssh(hostname, ssh_port)
+ tcp_socket = TCPSocket.new(hostname, ssh_port)
+ readable = IO.select([tcp_socket], nil, nil, 5)
+ if readable
+ ssh_banner = tcp_socket.gets
+ if ssh_banner.nil? or ssh_banner.empty?
+ false
+ else
+ Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{ssh_banner}")
+ yield
+ true
+ end
+ else
+ false
+ end
+ rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError
+ Chef::Log.debug("ssh failed to connect: #{hostname}")
+ sleep 2
+ false
+ rescue Errno::EPERM, Errno::ETIMEDOUT
+ Chef::Log.debug("ssh timed out: #{hostname}")
+ false
+ # This happens on some mobile phone networks
+ rescue Errno::ECONNRESET
+ Chef::Log.debug("ssh reset its connection: #{hostname}")
+ sleep 2
+ false
+ ensure
+ tcp_socket && tcp_socket.close
+ end
+
+ def decrypt_admin_password(encoded_password, key)
+ require 'base64'
+ require 'openssl'
+ private_key = OpenSSL::PKey::RSA.new(key)
+ encrypted_password = Base64.decode64(encoded_password)
+ password = private_key.private_decrypt(encrypted_password)
+ password
+ end
+
+ def check_windows_password_available(server_id)
+ response = connection.get_password_data(server_id)
+ if not response.body["passwordData"]
+ return false
+ end
+ response.body["passwordData"]
+ end
+
+ def windows_password
+ if not locate_config_value(:winrm_password)
+ if locate_config_value(:identity_file)
+ print "\n#{ui.color("Waiting for Windows Admin password to be available", :magenta)}"
+ print(".") until check_windows_password_available(@server.id) {
+ sleep 1000 #typically is available after 30 mins
+ puts("done")
+ }
+ response = connection.get_password_data(@server.id)
+ data = File.read(locate_config_value(:identity_file))
+ config[:winrm_password] = decrypt_admin_password(response.body["passwordData"], data)
+ else
+ ui.error("Cannot find SSH Identity file, required to fetch dynamically generated password")
+ exit 1
+ end
+ else
+ locate_config_value(:winrm_password)
+ end
+ end
+
+ def load_winrm_deps
+ require 'winrm'
+ require 'em-winrm'
+ require 'chef/knife/winrm'
+ require 'chef/knife/bootstrap_windows_winrm'
+ require 'chef/knife/bootstrap_windows_ssh'
+ require 'chef/knife/core/windows_bootstrap_context'
end
end
end
end