# Copyright 2013 Google Inc. All Rights Reserved. # # 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 # # 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 "log4r" require 'vagrant/util/retryable' require 'vagrant-google/util/timer' module VagrantPlugins module Google module Action # This runs the configured instance. class RunInstance include Vagrant::Util::Retryable def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant_google::action::run_instance") end def call(env) # Initialize metrics if they haven't been env[:metrics] ||= {} # Get the zone we're going to booting up in zone = env[:machine].provider_config.zone # Get the configs zone_config = env[:machine].provider_config.get_zone_config(zone) image = zone_config.image name = zone_config.name machine_type = zone_config.machine_type disk_size = zone_config.disk_size disk_name = zone_config.disk_name disk_type = zone_config.disk_type network = zone_config.network metadata = zone_config.metadata tags = zone_config.tags can_ip_forward = zone_config.can_ip_forward external_ip = zone_config.external_ip preemptible = zone_config.preemptible auto_restart = zone_config.auto_restart on_host_maintenance = zone_config.on_host_maintenance autodelete_disk = zone_config.autodelete_disk service_accounts = zone_config.service_accounts # Launch! env[:ui].info(I18n.t("vagrant_google.launching_instance")) env[:ui].info(" -- Name: #{name}") env[:ui].info(" -- Type: #{machine_type}") env[:ui].info(" -- Disk type: #{disk_type}") env[:ui].info(" -- Disk size: #{disk_size} GB") env[:ui].info(" -- Disk name: #{disk_name}") env[:ui].info(" -- Image: #{image}") env[:ui].info(" -- Zone: #{zone}") if zone env[:ui].info(" -- Network: #{network}") if network env[:ui].info(" -- Metadata: '#{metadata}'") env[:ui].info(" -- Tags: '#{tags}'") env[:ui].info(" -- IP Forward: #{can_ip_forward}") env[:ui].info(" -- External IP: #{external_ip}") env[:ui].info(" -- Preemptible: #{preemptible}") env[:ui].info(" -- Auto Restart: #{auto_restart}") env[:ui].info(" -- On Maintenance: #{on_host_maintenance}") env[:ui].info(" -- Autodelete Disk: #{autodelete_disk}") env[:ui].info(" -- Scopes: #{service_accounts}") begin request_start_time = Time.now().to_i # Warn on ssh-key overrides if env[:machine].config.ssh.username.nil? env[:ui].warn(I18n.t("vagrant_google.warn_ssh_vagrant_user")) end # Check if specified external ip is available external_ip = get_external_ip(env, external_ip) if external_ip # Check if disk type is available in the zone if !disk_type.nil? disk_type_obj = env[:google_compute].list_disk_types(zone).body['items'].select { |dt| dt['name'] == disk_type } || [] if !disk_type_obj.empty? disk_type = disk_type_obj[0]["selfLink"] else raise Errors::DiskTypeError, :disktype => disk_type end end if disk_name.nil? # no disk_name... disk_name defaults to instance name disk = env[:google_compute].disks.create( name: name, size_gb: disk_size, type: disk_type, zone_name: zone, source_image: image ) disk.wait_for { disk.ready? } else disk = env[:google_compute].disks.get(disk_name, zone) if disk.nil? # disk not found... create it with name disk = env[:google_compute].disks.create( name: disk_name, size_gb: disk_size, type: disk_type, zone_name: zone, source_image: image ) disk.wait_for { disk.ready? } end end defaults = { :name => name, :zone_name => zone, :machine_type => machine_type, :disk_size => disk_size, :disk_type => disk_type, :image => image, :network => network, :metadata => metadata, :tags => tags, :can_ip_forward => can_ip_forward, :external_ip => external_ip, :preemptible => preemptible, :auto_restart => auto_restart, :on_host_maintenance => on_host_maintenance, :disks => [disk.get_as_boot_disk(true, autodelete_disk)], :service_accounts => service_accounts } server = env[:google_compute].servers.create(defaults) @logger.info("Machine '#{zone}:#{name}' created.") rescue Fog::Compute::Google::NotFound => e raise rescue Fog::Compute::Google::Error => e raise Errors::FogError, :message => e.message end # Immediately save the name since the instance has been created env[:machine].id = server.name server.reload env[:ui].info(I18n.t("vagrant_google.waiting_for_ready")) begin server.wait_for { ready? } env[:metrics]["instance_ready_time"] = Time.now().to_i - request_start_time @logger.info("Time for instance ready: #{env[:metrics]["instance_ready_time"]}") env[:ui].info(I18n.t("vagrant_google.ready")) rescue env[:interrupted] = true end if !env[:terminated] env[:metrics]["instance_ssh_time"] = Util::Timer.time do # Wait for SSH to be ready. env[:ui].info(I18n.t("vagrant_google.waiting_for_ssh")) while true # If we're interrupted just back out break if env[:interrupted] break if env[:machine].communicate.ready? sleep 2 end end @logger.info("Time for SSH ready: #{env[:metrics]["instance_ssh_time"]}") env[:ui].info(I18n.t("vagrant_google.ready_ssh")) end # Terminate the instance if we were interrupted terminate(env) if env[:interrupted] @app.call(env) end def recover(env) return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError) if env[:machine].provider.state.id != :not_created # Undo the import terminate(env) end end def terminate(env) destroy_env = env.dup destroy_env.delete(:interrupted) destroy_env[:config_validate] = false destroy_env[:force_confirm_destroy] = true env[:action_runner].run(Action.action_destroy, destroy_env) end def get_external_ip(env, external_ip) address = env[:google_compute].addresses.get_by_ip_address_or_name(external_ip) if address.nil? raise Errors::ExternalIpDoesNotExistError, :externalip => external_ip end if address.in_use? raise Errors::ExternalIpInUseError, :externalip => external_ip end # Resolve the name to IP address address.address end end end end end