lib/chef/knife/google_server_create.rb in knife-google-1.3.1 vs lib/chef/knife/google_server_create.rb in knife-google-2.0.0
- old
+ new
@@ -1,7 +1,11 @@
-# Copyright 2013 Google Inc. All Rights Reserved.
#
+# Author:: Paul Rossman (<paulrossman@google.com>)
+# Author:: Chef Partner Engineering (<partnereng@chef.io>)
+# Copyright:: Copyright 2015-2016 Google Inc., Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
@@ -9,527 +13,242 @@
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
-require 'timeout'
-require 'chef/knife/google_base'
-class Chef
- class Knife
- class GoogleServerCreate < Knife
+require "chef/knife"
+require "chef/knife/cloud/server/create_command"
+require "chef/knife/cloud/server/create_options"
+require "chef/knife/cloud/google_service"
+require "chef/knife/cloud/google_service_helpers"
+require "chef/knife/cloud/google_service_options"
- include Knife::GoogleBase
+class Chef::Knife::Cloud
+ class GoogleServerCreate < ServerCreateCommand
+ include GoogleServiceOptions
+ include GoogleServiceHelpers
+ include ServerCreateOptions
- deps do
- require 'google/compute'
- require 'chef/json_compat'
- require 'chef/knife/bootstrap'
- Chef::Knife::Bootstrap.load_deps
- end
+ banner "knife google server create NAME -m MACHINE_TYPE -I IMAGE (options)"
- banner "knife google server create NAME -m MACHINE_TYPE -I IMAGE -Z ZONE (options)"
+ option :machine_type,
+ short: "-m MACHINE_TYPE",
+ long: "--gce-machine-type MACHINE_TYPE",
+ description: "The machine type of server (n1-highcpu-2, n1-highcpu-2-d, etc)"
- attr_accessor :initial_sleep_delay
- attr_reader :instance
+ option :image,
+ short: "-I IMAGE",
+ long: "--gce-image IMAGE",
+ description: "The Image for the server"
- option :machine_type,
- :short => "-m MACHINE_TYPE",
- :long => "--gce-machine-type MACHINE_TYPE",
- :description => "The machine type of server (n1-highcpu-2, n1-highcpu-2-d, etc)",
- :required => true
+ option :image_project,
+ long: "--gce-image-project IMAGE_PROJECT",
+ description: "The project-id containing the Image (debian-cloud, centos-cloud, etc)"
- option :image,
- :short => "-I IMAGE",
- :long => "--gce-image IMAGE",
- :description => "The Image for the server",
- :required => true
+ option :boot_disk_name,
+ long: "--gce-boot-disk-name DISK",
+ description: "Name of persistent boot disk; default is to use the server name"
- option :image_project_id,
- :long => "--gce-image-project-id IMAGE_PROJECT_ID",
- :description => "The project-id containing the Image (debian-cloud, centos-cloud, etc)",
- :default => ""
+ option :boot_disk_size,
+ long: "--gce-boot-disk-size SIZE",
+ description: "Size of the persistent boot disk between 10 and 10000 GB, specified in GB; default is '10' GB",
+ default: "10"
- option :zone,
- :short => "-Z ZONE",
- :long => "--gce-zone ZONE",
- :description => "The Zone for this server"
+ option :boot_disk_ssd,
+ long: "--[no-]gce-boot-disk-ssd",
+ description: "Use pd-ssd boot disk; default is pd-standard boot disk",
+ boolean: true,
+ default: false
- option :boot_disk_name,
- :long => "--gce-boot-disk-name DISK",
- :description => "Name of persistent boot disk; default is to use the server name",
- :default => ""
+ option :boot_disk_autodelete,
+ long: "--[no-]gce-boot-disk-autodelete",
+ description: "Delete boot disk when server is deleted.",
+ boolean: true,
+ default: true
- option :boot_disk_size,
- :long => "--gce-boot-disk-size SIZE",
- :description => "Size of the persistent boot disk between 10 and 10000 GB, specified in GB; default is '10' GB",
- :default => "10"
+ option :additional_disks,
+ long: "--gce-additional-disks DISKS",
+ short: "-D DISKS",
+ description: "Names of additional disks, comma-separated, to attach to this server (NOTE: this will not create them)",
+ proc: Proc.new { |disks| disks.split(",") },
+ default: []
- option :auto_restart,
- :long => "--[no-]gce-auto-server-restart",
- :description => "Compute Engine can automatically restart your VM instance if it is terminated for non-user-initiated reasons; enabled by default.",
- :boolean => true,
- :default => true
+ option :auto_restart,
+ long: "--[no-]gce-auto-server-restart",
+ description: "GCE can automatically restart your server if it is terminated for non-user-initiated reasons; enabled by default.",
+ boolean: true,
+ default: true
- option :auto_migrate,
- :long => "--[no-]gce-auto-server-migrate",
- :description => "Compute Engine can migrate your VM instance to other hardware without downtime prior to periodic infrastructure maintenance, otherwise the server is terminated; enabled by default.",
- :boolean => true,
- :default => true
+ option :auto_migrate,
+ long: "--[no-]gce-auto-server-migrate",
+ description: "GCE can migrate your server to other hardware without downtime prior to periodic infrastructure maintenance, otherwise the server is terminated; enabled by default.",
+ boolean: true,
+ default: true
- option :network,
- :short => "-n NETWORK",
- :long => "--gce-network NETWORK",
- :description => "The network for this server; default is 'default'",
- :default => "default"
+ option :can_ip_forward,
+ long: "--[no-]gce-can-ip-forward",
+ description: "Allow server network forwarding",
+ boolean: true,
+ default: false
- option :tags,
- :short => "-T TAG1,TAG2,TAG3",
- :long => "--gce-tags TAG1,TAG2,TAG3",
- :description => "Tags for this server",
- :proc => Proc.new { |tags| tags.split(',') },
- :default => []
+ option :network,
+ long: "--gce-network NETWORK",
+ description: "The network for this server; default is 'default'",
+ default: "default"
- option :metadata,
- :long => "--gce-metadata Key=Value[,Key=Value...]",
- :description => "Additional metadata for this server",
- :proc => Proc.new { |metadata| metadata.split(',') },
- :default => []
+ option :tags,
+ short: "-T TAG1,TAG2,TAG3",
+ long: "--gce-tags TAG1,TAG2,TAG3",
+ description: "Tags for this server",
+ proc: Proc.new { |tags| tags.split(",") },
+ default: []
- option :service_account_scopes,
- :long => "--gce-service-account-scopes SCOPE1,SCOPE2,SCOPE3",
- :proc => Proc.new { |service_account_scopes| service_account_scopes.split(',') },
- :description => "Service account scopes for this server",
- :default => []
+ option :metadata,
+ long: "--gce-metadata Key=Value[,Key=Value...]",
+ description: "Additional metadata for this server",
+ proc: Proc.new { |metadata| metadata.split(",") },
+ default: []
- # GCE documentation uses the term 'service account name', the api uses the term 'email'
- option :service_account_name,
- :long => "--gce-service-account-name NAME",
- :description => "Service account name for this server, typically in the form of '123845678986@project.gserviceaccount.com'; default is 'default'",
- :default => "default"
+ option :service_account_scopes,
+ long: "--gce-service-account-scopes SCOPE1,SCOPE2,SCOPE3",
+ proc: Proc.new { |service_account_scopes| service_account_scopes.split(",") },
+ description: "Service account scopes for this server",
+ default: []
- option :instance_connect_ip,
- :long => "--gce-server-connect-ip INTERFACE",
- :description => "Whether to use PUBLIC or PRIVATE interface/address to connect; default is 'PUBLIC'",
- :default => 'PUBLIC'
+ option :service_account_name,
+ long: "--gce-service-account-name NAME",
+ description: "Service account name for this server, typically in the form of '123845678986@project.gserviceaccount.com'; default is 'default'",
+ default: "default"
- option :public_ip,
- :long=> "--gce-public-ip IP_ADDRESS",
- :description => "EPHEMERAL or static IP address or NONE; default is 'EPHEMERAL'",
- :default => "EPHEMERAL"
+ option :use_private_ip,
+ long: "--gce-use-private-ip",
+ description: "if used, Chef will attempt to bootstrap the device using the private IP; default is disabled (use public IP)",
+ boolean: true,
+ default: false
- option :chef_node_name,
- :short => "-N NAME",
- :long => "--node-name NAME",
- :description => "The Chef node name for your new node"
+ option :public_ip,
+ long: "--gce-public-ip IP_ADDRESS",
+ description: "EPHEMERAL or static IP address or NONE; default is 'EPHEMERAL'",
+ default: "EPHEMERAL"
- option :ssh_user,
- :short => "-x USERNAME",
- :long => "--ssh-user USERNAME",
- :description => "The ssh username; default is 'root'",
- :default => "root"
+ option :gce_email,
+ long: "--gce-email EMAIL_ADDRESS",
+ description: "email address of the logged-in Google Cloud user; required for bootstrapping windows hosts"
- option :ssh_password,
- :short => "-P PASSWORD",
- :long => "--ssh-password PASSWORD",
- :description => "The ssh password"
+ deps do
+ require "gcewinpass"
+ end
- option :ssh_port,
- :short => "-p PORT",
- :long => "--ssh-port PORT",
- :description => "The ssh port; default is '22'",
- :default => "22"
+ def before_exec_command
+ super
- option :ssh_gateway,
- :short => "-w GATEWAY",
- :long => "--ssh-gateway GATEWAY",
- :description => "The ssh gateway server"
+ @create_options = {
+ name: instance_name,
+ image: locate_config_value(:image),
+ image_project: locate_config_value(:image_project),
+ network: locate_config_value(:network),
+ public_ip: locate_config_value(:public_ip),
+ auto_migrate: locate_config_value(:auto_migrate),
+ auto_restart: locate_config_value(:auto_restart),
+ boot_disk_autodelete: locate_config_value(:boot_disk_autodelete),
+ boot_disk_name: locate_config_value(:boot_disk_name),
+ boot_disk_size: boot_disk_size,
+ boot_disk_ssd: locate_config_value(:boot_disk_ssd),
+ additional_disks: locate_config_value(:additional_disks),
+ can_ip_forward: locate_config_value(:can_ip_forward),
+ machine_type: locate_config_value(:machine_type),
+ service_account_scopes: locate_config_value(:service_account_scopes),
+ service_account_name: locate_config_value(:service_account_name),
+ metadata: metadata,
+ tags: locate_config_value(:tags),
+ }
+ end
- option :identity_file,
- :short => "-i IDENTITY_FILE",
- :long => "--identity-file IDENTITY_FILE",
- :description => "The SSH identity file used for authentication"
+ def set_default_config
+ # dumb hack for knife-cloud, which expects the user to pass in the WinRM password to use when bootstrapping.
+ # We won't know the password until the instance is created and we forceably reset it.
+ config[:winrm_password] = "will_change_this_later"
+ end
- option :prerelease,
- :long => "--prerelease",
- :description => "Install the pre-release chef gems"
+ def validate_params!
+ check_for_missing_config_values!(:machine_type, :image, :boot_disk_size, :network)
+ raise "You must supply an instance name." if @name_args.first.nil?
+ raise "Boot disk size must be between 10 and 10,000" unless valid_disk_size?(boot_disk_size)
+ raise "Please provide your Google Cloud console email address via --gce-email. " \
+ "It is required when resetting passwords on Windows hosts." if locate_config_value(:bootstrap_protocol) == "winrm" && locate_config_value(:gce_email).nil?
- option :bootstrap_version,
- :long => "--bootstrap-version VERSION",
- :description => "The version of Chef to install"
+ super
+ end
- option :distro,
- :short => "-d DISTRO",
- :long => "--distro DISTRO",
- :description => "Bootstrap a distro using a template; default is 'chef-full'",
- :default => 'chef-full'
+ def before_bootstrap
+ super
- option :template_file,
- :long => "--template-file TEMPLATE",
- :description => "Full path to location of template to use",
- :default => false
+ config[:chef_node_name] = locate_config_value(:chef_node_name) ? locate_config_value(:chef_node_name) : instance_name
+ config[:bootstrap_ip_address] = ip_address_for_bootstrap
- option :run_list,
- :short => "-r RUN_LIST",
- :long => "--run-list RUN_LIST",
- :description => "Comma separated list of roles/recipes to apply",
- :proc => lambda { |o| o.split(/[\s,]+/) }
-
- option :json_attributes,
- :short => "-j JSON",
- :long => "--json-attributes JSON",
- :description => "A JSON string to be added to the first run of chef-client",
- :proc => lambda { |o| JSON.parse(o) }
-
- option :host_key_verify,
- :long => "--[no-]host-key-verify",
- :description => "Verify host key, enabled by default.",
- :boolean => true,
- :default => true
-
- option :compute_user_data,
- :long => "--user-data USER_DATA_FILE",
- :short => "-u USER_DATA_FILE",
- :description => "The Google Compute User Data file to provision the server with"
-
- option :hint,
- :long => "--hint HINT_NAME[=HINT_FILE]",
- :description => "Specify Ohai Hint to be set on the bootstrap target. Use multiple --hint options to specify multiple hints.",
- :proc => Proc.new { |h|
- Chef::Config[:knife][:hints] ||= {}
- name, path = h.split("=")
- Chef::Config[:knife][:hints][name] = path ? JSON.parse(::File.read(path)) : Hash.new
- }
-
- option :secret,
- :short => "-s SECRET",
- :long => "--secret ",
- :description => "The secret key to use to encrypt data bag item values",
- :proc => lambda { |s| Chef::Config[:knife][:secret] = s }
-
- option :secret_file,
- :long => "--secret-file SECRET_FILE",
- :description => "A file containing the secret key to use to encrypt data bag item values",
- :proc => lambda { |sf| Chef::Config[:knife][:secret_file] = sf }
-
- 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
- ensure
- tcp_socket && tcp_socket.close
+ if locate_config_value(:bootstrap_protocol) == "winrm"
+ ui.msg("Resetting the Windows login password so the bootstrap can continue...")
+ config[:winrm_password] = reset_windows_password
end
+ end
- def wait_for_sshd(hostname)
- config[:ssh_gateway] ? wait_for_tunnelled_sshd(hostname) : wait_for_direct_sshd(hostname, config[:ssh_port])
- end
+ # overriding this from Chef::Knife::Cloud::ServerCreateCommand.
+ #
+ # This gets called in validate_params! in that class before our #before_bootstrap
+ # is called, in which it randomly generates a node name, which means we never default
+ # to the instance name in our #before_bootstrap method. Instead, we'll just nil this
+ # and allow our class here to do The Right Thing.
+ def get_node_name(_name, _prefix)
+ nil
+ end
- def wait_for_tunnelled_sshd(hostname)
- print(".")
- print(".") until tunnel_test_ssh(hostname) {
- sleep @initial_sleep_delay ||= 40
- puts("done")
- }
- end
+ def project
+ locate_config_value(:gce_project)
+ end
- 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
+ def zone
+ locate_config_value(:gce_zone)
+ end
- def wait_for_direct_sshd(hostname, ssh_port)
- print(".") until tcp_test_ssh(ssh_connect_host, ssh_port) {
- sleep @initial_sleep_delay ||= 40
- puts("done")
- }
- end
+ def email
+ locate_config_value(:gce_email)
+ end
- def ssh_connect_host
- @ssh_connect_host ||= if config[:instance_connect_ip] == 'PUBLIC'
- public_ips(@instance).first
- else
- private_ips(@instance).first
- end
- end
+ def ip_address_for_bootstrap
+ ip = locate_config_value(:use_private_ip) ? private_ip_for(server) : public_ip_for(server)
- def disk_exists(disk, zone)
- # if client.disks.get errors with a Google::Compute::ResourceNotFound
- # then the disk does not exist and can be created
- client.disks.get(:disk => disk, :zone => selflink2name(zone))
- rescue Google::Compute::ResourceNotFound
- # disk does not exist
- # continue provisioning
- false
- else
- true
- end
+ raise "Unable to determine instance IP address for bootstrapping" if ip == "unknown"
+ ip
+ end
- def wait_for_disk(disk, operation, zone)
- Timeout::timeout(300) do
- until disk.status == 'DONE'
- ui.info(".")
- sleep 1
- disk = client.zoneOperations.get(:name => disk,
- :operation => operation,
- :zone => selflink2name(zone))
- end
- disk.target_link
- end
- rescue Timeout::Error
- ui.error("Timeout exceeded with disk status: " + disk.status)
- exit 1
- end
+ def instance_name
+ @name_args.first
+ end
- def bootstrap_for_node(instance, ssh_host)
- bootstrap = Chef::Knife::Bootstrap.new
- bootstrap.name_args = [ssh_host]
- bootstrap.config[:run_list] = config[:run_list]
- 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]
- bootstrap.config[:chef_node_name] = config[:chef_node_name] || instance.name
- bootstrap.config[:prerelease] = config[:prerelease]
- bootstrap.config[:bootstrap_version] = config[:bootstrap_version]
- bootstrap.config[:first_boot_attributes] = config[:json_attributes]
- bootstrap.config[:distro] = config[:distro]
- bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
- bootstrap.config[:template_file] = config[:template_file]
- bootstrap.config[:environment] = config[:environment]
- 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)
-
- # may be needed for vpc_mode
- bootstrap.config[:host_key_verify] = config[:host_key_verify]
- # Modify global configuration state to ensure hint gets set by
- # knife-bootstrap
- Chef::Config[:knife][:hints] ||= {}
- Chef::Config[:knife][:hints]["gce"] ||= {}
- Chef::Config[:knife][:hints]["google"] ||= {}
- bootstrap
+ def metadata
+ locate_config_value(:metadata).each_with_object({}) do |item, memo|
+ key, value = item.split("=")
+ memo[key] = value
end
+ end
- def run
- $stdout.sync = true
- unless @name_args.size > 0
- ui.error("Please provide the name of the new server.")
- exit 1
- end
+ def boot_disk_size
+ locate_config_value(:boot_disk_size).to_i
+ end
- begin
- zone = client.zones.get(config[:zone] || Chef::Config[:knife][:gce_zone]).self_link
- rescue Google::Compute::ResourceNotFound
- ui.error("Zone '#{config[:zone] || Chef::Config[:knife][:gce_zone]}' not found.")
- exit 1
- rescue Google::Compute::ParameterValidation
- ui.error("Must specify zone in knife config file or in command line as an option. Try --help.")
- exit 1
- end
+ def reset_windows_password
+ GoogleComputeWindowsPassword.new(
+ project: project,
+ zone: zone,
+ instance_name: instance_name,
+ email: email,
+ username: locate_config_value(:winrm_user),
+ debug: gcewinpass_debug_mode
+ ).new_password
+ end
- begin
- machine_type = client.machine_types.get(:name => config[:machine_type],
- :zone => selflink2name(zone)).self_link
- rescue Google::Compute::ResourceNotFound
- ui.error("MachineType '#{config[:machine_type]}' not found")
- exit 1
- end
-
- # this parameter is a string during the post and boolean otherwise
- if config[:auto_restart] then
- auto_restart = 'true'
- else
- auto_restart = 'false'
- end
-
- if config[:auto_migrate] then
- auto_migrate = 'MIGRATE'
- else
- auto_migrate = 'TERMINATE'
- end
-
- (checked_custom, checked_all) = false
- begin
- image_project = config[:image_project_id]
- # use zone url to determine project name
- zone =~ Regexp.new('/projects/(.*?)/')
- project = $1
- if image_project.to_s.empty?
- unless checked_custom
- checked_custom = true
- ui.info("Looking for Image '#{config[:image]}' in Project '#{project}'")
- image = client.images.get(:project=>project, :name=>config[:image]).self_link
- else
- case config[:image].downcase
- when /debian/
- project = 'debian-cloud'
- ui.info("Looking for Image '#{config[:image]}' in Project '#{project}'")
- when /centos/
- project = 'centos-cloud'
- ui.info("Looking for Image '#{config[:image]}' in Project '#{project}'")
- end
- checked_all = true
- image = client.images.get(:project=>project, :name=>config[:image]).self_link
- end
- else
- checked_all = true
- project = image_project
- image = client.images.get(:project=>project, :name=>config[:image]).self_link
- end
- ui.info("Found Image '#{config[:image]}' in Project '#{project}'")
- rescue Google::Compute::ResourceNotFound
- unless checked_all then
- retry
- else
- ui.error("Image '#{config[:image]}' not found")
- exit 1
- end
- end
-
- begin
- boot_disk_size = config[:boot_disk_size].to_i
- raise if !boot_disk_size.between?(10, 10000)
- rescue
- ui.error("Size of the persistent boot disk must be between 10 and 10000 GB.")
- exit 1
- end
-
- if config[:boot_disk_name].to_s.empty? then
- boot_disk_name = @name_args.first
- else
- boot_disk_name = config[:boot_disk_name]
- end
-
- ui.info("Waiting for the disk insert operation to complete")
- boot_disk_insert = client.disks.insert(:sourceImage => image,
- :zone => selflink2name(zone),
- :name => boot_disk_name,
- :sizeGb => boot_disk_size)
- boot_disk_target_link = wait_for_disk(boot_disk_insert, boot_disk_insert.name, zone)
-
- begin
- network = client.networks.get(config[:network]).self_link
- rescue Google::Compute::ResourceNotFound
- ui.error("Network '#{config[:network]}' not found")
- exit 1
- end
-
- metadata = config[:metadata].collect{|pair| Hash[*pair.split('=')] }
- network_interface = {'network'=>network}
-
- if config[:public_ip] == 'EPHEMERAL'
- network_interface.merge!('accessConfigs' =>[{"name"=>"External NAT",
- "type"=> "ONE_TO_ONE_NAT"}])
- elsif config[:public_ip] =~ /\d+\.\d+\.\d+\.\d+/
- network_interface.merge!('accessConfigs' =>[{"name"=>"External NAT",
- "type"=>"ONE_TO_ONE_NAT", "natIP"=>config[:public_ip] }])
- elsif config[:public_ip] == 'NONE'
- # do nothing
- else
- ui.error("Invalid public ip value : #{config[:public_ip]}")
- exit 1
- end
-
- ui.info("Waiting for the create server operation to complete")
- if !config[:service_account_scopes].any?
- zone_operation = client.instances.create(:name => @name_args.first,
- :zone => selflink2name(zone),
- :machineType => machine_type,
- :disks => [{
- 'boot' => true,
- 'type' => 'PERSISTENT',
- 'mode' => 'READ_WRITE',
- 'deviceName' => selflink2name(boot_disk_target_link),
- 'source' => boot_disk_target_link
- }],
- :networkInterfaces => [network_interface],
- :scheduling => {
- 'automaticRestart' => auto_restart,
- 'onHostMaintenance' => auto_migrate
- },
- :metadata => { 'items' => metadata },
- :tags => { 'items' => config[:tags] }
- )
- else
- zone_operation = client.instances.create(:name => @name_args.first,
- :zone=> selflink2name(zone),
- :machineType => machine_type,
- :disks => [{
- 'boot' => true,
- 'type' => 'PERSISTENT',
- 'mode' => 'READ_WRITE',
- 'deviceName' => selflink2name(boot_disk_target_link),
- 'source' => boot_disk_target_link
- }],
- :networkInterfaces => [network_interface],
- :serviceAccounts => [{
- 'kind' => 'compute#serviceAccount',
- 'email' => config[:service_account_name],
- 'scopes' => config[:service_account_scopes]
- }],
- :scheduling => {
- 'automaticRestart' => auto_restart,
- 'onHostMaintenance' => auto_migrate
- },
- :metadata => { 'items'=>metadata },
- :tags => { 'items' => config[:tags] }
- )
- end
-
- until zone_operation.progress.to_i == 100
- ui.info(".")
- sleep 1
- zone_operation = client.zoneOperations.get(:name=>zone_operation, :operation=>zone_operation.name, :zone=>selflink2name(zone))
- end
-
- ui.info("Waiting for the servers to be in running state")
-
- @instance = client.instances.get(:name=>@name_args.first, :zone=>selflink2name(zone))
- msg_pair("Instance Name", @instance.name)
- msg_pair("Machine Type", selflink2name(@instance.machine_type))
- msg_pair("Image", selflink2name(config[:image]))
- msg_pair("Zone", selflink2name(@instance.zone))
- msg_pair("Tags", @instance.tags.has_key?("items") ? @instance.tags["items"].join(",") : "None")
- until @instance.status == "RUNNING"
- sleep 3
- msg_pair("Status", @instance.status.downcase)
- @instance = client.instances.get(:name=>@name_args.first, :zone=>selflink2name(zone))
- end
-
- msg_pair("Public IP Address", public_ips(@instance)) unless public_ips(@instance).empty?
- msg_pair("Private IP Address", private_ips(@instance))
- ui.info("\n#{ui.color("Waiting for server", :magenta)}")
-
- ui.info("\n")
- ui.info(ui.color("Waiting for sshd", :magenta))
- wait_for_sshd(ssh_connect_host)
- bootstrap_for_node(@instance,ssh_connect_host).run
- ui.info("\n")
- ui.info("Complete!!")
- end
+ def gcewinpass_debug_mode
+ Chef::Config[:log_level] == :debug
end
end
end