class Chef::Knife::Cloud::GoogleService
Constants
- IMAGE_ALIAS_MAP
- PUBLIC_PROJECTS
- SCOPE_ALIAS_MAP
Attributes
max_page_size[R]
max_pages[R]
project[R]
refresh_rate[R]
wait_time[R]
zone[R]
Public Class Methods
new(options = {})
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 101 def initialize(options = {}) @project = options[:project] @zone = options[:zone] @wait_time = options[:wait_time] @refresh_rate = options[:refresh_rate] @max_pages = options[:max_pages] @max_page_size = options[:max_page_size] end
Public Instance Methods
adding_local_ssd(options)
click to toggle source
To create a instance with an attached local SSD
# File lib/chef/knife/cloud/google_service.rb, line 345 def adding_local_ssd(options) disk, params = common_operation(options, "local-ssd") disk.type = "SCRATCH" disk.interface = options[:interface] disk.initialize_params = params disk end
available_projects()
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 648 def available_projects [project] | PUBLIC_PROJECTS end
boot_disk_name_for(options)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 422 def boot_disk_name_for(options) options[:boot_disk_name].nil? ? options[:name] : options[:boot_disk_name] end
boot_disk_source_image(image, image_project)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 366 def boot_disk_source_image(image, image_project) @boot_disk_source ||= image_search_for(image, image_project) end
boot_disk_type_for(options)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 362 def boot_disk_type_for(options) options[:boot_disk_ssd] ? "pd-ssd" : "pd-standard" end
check_api_call() { || ... }
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 247 def check_api_call yield rescue Google::Apis::ClientError false else true end
common_operation(options, disk_type)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 354 def common_operation(options, disk_type) disk = Google::Apis::ComputeV1::AttachedDisk.new params = Google::Apis::ComputeV1::AttachedDiskInitializeParams.new disk.auto_delete = options[:boot_disk_autodelete] params.disk_type = disk_type_url_for(disk_type) [disk, params] end
connection()
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 110 def connection return @connection unless @connection.nil? @connection = Google::Apis::ComputeV1::ComputeService.new @connection.authorization = authorization @connection.client_options = Google::Apis::ClientOptions.new.tap do |opts| opts.application_name = "knife-google" opts.application_version = Knife::Google::VERSION end @connection end
create_disk(name, size, type, source_image = nil)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 168 def create_disk(name, size, type, source_image = nil) disk = Google::Apis::ComputeV1::Disk.new disk.name = name disk.size_gb = size disk.type = disk_type_url_for(type) ui.msg("Creating a #{size} GB disk named #{name}...") wait_for_operation(connection.insert_disk(project, zone, disk, source_image: source_image)) ui.msg("Waiting for disk to be ready...") wait_for_status("READY") { connection.get_disk(project, zone, name) } ui.msg("Disk created successfully.") end
create_server(options = {})
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 132 def create_server(options = {}) validate_server_create_options!(options) ui.msg("Creating instance...") instance_object = instance_object_for(options) wait_for_operation(connection.insert_instance(project, zone, instance_object)) wait_for_status("RUNNING") { get_server(options[:name]) } ui.msg("Instance created!") get_server(options[:name]) end
delete_disk(name)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 185 def delete_disk(name) begin connection.get_disk(project, zone, name) rescue Google::Apis::ClientError ui.warn("Unable to locate disk #{name} in project #{project}, zone #{zone}") return end ui.confirm("Do you really want to delete disk #{name}") ui.msg("Deleting disk #{name}...") wait_for_operation(connection.delete_disk(project, zone, name)) ui.msg("Disk #{name} deleted successfully.") end
delete_server(instance_name)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 146 def delete_server(instance_name) begin instance = get_server(instance_name) rescue Google::Apis::ClientError ui.warn("Unable to locate instance #{instance_name} in project #{project}, zone #{zone}") return end server_summary(instance) ui.confirm("Do you really want to delete this instance") ui.msg("Deleting instance #{instance_name}...") wait_for_operation(connection.delete_instance(project, zone, instance_name)) ui.msg("Instance #{instance_name} deleted successfully.") end
disk_type_url_for(type)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 559 def disk_type_url_for(type) "zones/#{zone}/diskTypes/#{type}" end
get_server(instance_name)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 164 def get_server(instance_name) connection.get_instance(project, zone, instance_name) end
image_alias_url(image_alias)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 407 def image_alias_url(image_alias) return unless IMAGE_ALIAS_MAP.key?(image_alias) image_project = IMAGE_ALIAS_MAP[image_alias][:project] image_prefix = IMAGE_ALIAS_MAP[image_alias][:prefix] latest_image = connection.list_images(image_project).items .select { |image| image.name.start_with?(image_prefix) } .max_by(&:name) return if latest_image.nil? latest_image.self_link end
image_exist?(image_project, image_name)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 273 def image_exist?(image_project, image_name) check_api_call { connection.get_image(image_project, image_name) } end
image_search_for(image, image_project)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 370 def image_search_for(image, image_project) # if the user provided an image_project, assume they want it, no questions asked unless image_project.nil? ui.msg("Searching project #{image_project} for image #{image}") return image_url_for(image_project, image) end # no image project has been provided. Check to see if the image is a known alias. alias_url = image_alias_url(image) unless alias_url.nil? ui.msg("image #{image} is a known alias - using image URL: #{alias_url}") return alias_url end # Doesn't match an alias. Let's check the user's project for the image. url = image_url_for(project, image) unless url.nil? ui.msg("Located image #{image} in project #{project} - using image URL: #{url}") return url end # Image not found in user's project. Is there a public project this image might exist in? public_project = public_project_for_image(image) if public_project ui.msg("Searching public image project #{public_project} for image #{image}") return image_url_for(public_project, image) end # No image in user's project or public project, so it doesn't exist. ui.error("Image search failed for image #{image} - no suitable image located") nil end
image_url_for(image_project, image_name)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 403 def image_url_for(image_project, image_name) return "projects/#{image_project}/global/images/#{image_name}" if image_exist?(image_project, image_name) end
instance_access_configs_for(public_ip)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 454 def instance_access_configs_for(public_ip) return [] if public_ip.nil? || public_ip.match(/none/i) access_config = Google::Apis::ComputeV1::AccessConfig.new access_config.name = "External NAT" access_config.type = "ONE_TO_ONE_NAT" access_config.nat_ip = public_ip if valid_ip_address?(public_ip) Array(access_config) end
instance_boot_disk_for(options)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 333 def instance_boot_disk_for(options) disk, params = common_operation(options, boot_disk_type_for(options)) disk.boot = true params.disk_name = boot_disk_name_for(options) params.disk_size_gb = options[:boot_disk_size] params.source_image = boot_disk_source_image(options[:image], options[:image_project]) disk.initialize_params = params disk end
instance_disks_for(options)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 315 def instance_disks_for(options) disks = [] disks << instance_boot_disk_for(options) options[:number_of_local_ssd].to_i.times { disks << adding_local_ssd(options) } if options[:local_ssd] options[:additional_disks].each do |disk_name| begin disk = connection.get_disk(project, zone, disk_name) rescue Google::Apis::ClientError => e ui.error("Unable to attach disk #{disk_name} to the instance: #{e.message}") raise end disks << Google::Apis::ComputeV1::AttachedDisk.new.tap { |x| x.source = disk.self_link } end disks end
instance_metadata_for(metadata)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 430 def instance_metadata_for(metadata) return if metadata.nil? || metadata.empty? metadata_obj = Google::Apis::ComputeV1::Metadata.new metadata_obj.items = metadata.each_with_object([]) do |(k, v), memo| metadata_item = Google::Apis::ComputeV1::Metadata::Item.new metadata_item.key = k metadata_item.value = v memo << metadata_item end metadata_obj end
instance_network_interfaces_for(options)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 445 def instance_network_interfaces_for(options) interface = Google::Apis::ComputeV1::NetworkInterface.new interface.network = network_url_for(options[:network]) interface.subnetwork = subnet_url_for(options[:subnet]) if options[:subnet] interface.access_configs = instance_access_configs_for(options[:public_ip]) Array(interface) end
instance_object_for(options)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 300 def instance_object_for(options) inst_obj = Google::Apis::ComputeV1::Instance.new inst_obj.name = options[:name] inst_obj.can_ip_forward = options[:can_ip_forward] inst_obj.disks = instance_disks_for(options) inst_obj.machine_type = machine_type_url_for(options[:machine_type]) inst_obj.metadata = instance_metadata_for(options[:metadata]) inst_obj.network_interfaces = instance_network_interfaces_for(options) inst_obj.scheduling = instance_scheduling_for(options) inst_obj.service_accounts = instance_service_accounts_for(options) unless instance_service_accounts_for(options).nil? inst_obj.tags = instance_tags_for(options[:tags]) inst_obj end
instance_scheduling_for(options)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 473 def instance_scheduling_for(options) scheduling = Google::Apis::ComputeV1::Scheduling.new scheduling.automatic_restart = options[:auto_restart].to_s scheduling.on_host_maintenance = migrate_setting_for(options[:auto_migrate]) scheduling.preemptible = options[:preemptible].to_s scheduling end
instance_service_accounts_for(options)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 486 def instance_service_accounts_for(options) return if options[:service_account_scopes].nil? || options[:service_account_scopes].empty? service_account = Google::Apis::ComputeV1::ServiceAccount.new service_account.email = options[:service_account_name] service_account.scopes = options[:service_account_scopes].map { |scope| service_account_scope_url(scope) } Array(service_account) end
list_disks()
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 226 def list_disks paginated_results(:list_disks, :items, project, zone) || [] end
list_images()
click to toggle source
Retrieves the list of custom images and public images. Custom images are images you create that belong to your project.
# File lib/chef/knife/cloud/google_service.rb, line 222 def list_images available_projects.map { |project| paginated_results(:list_images, :items, project) || [] }.flatten end
list_project_quotas()
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 234 def list_project_quotas connection.get_project(project).quotas || [] end
list_regions()
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 230 def list_regions paginated_results(:list_regions, :items, project) || [] end
list_servers()
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 200 def list_servers instances = paginated_results(:list_instances, :items, project, zone) return [] if instances.nil? instances.each_with_object([]) do |instance, memo| memo << OpenStruct.new( name: instance.name, status: instance.status, machine_type: machine_type_for(instance), network: network_for(instance), private_ip: private_ip_for(instance), public_ip: public_ip_for(instance) ) end end
list_zones()
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 216 def list_zones paginated_results(:list_zones, :items, project) || [] end
machine_type_for(instance)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 521 def machine_type_for(instance) instance.machine_type.split("/").last end
machine_type_url_for(machine_type)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 426 def machine_type_url_for(machine_type) "zones/#{zone}/machineTypes/#{machine_type}" end
migrate_setting_for(auto_migrate)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 482 def migrate_setting_for(auto_migrate) auto_migrate ? "MIGRATE" : "TERMINATE" end
network_for(instance)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 515 def network_for(instance) instance.network_interfaces.first.network.split("/").last rescue NoMethodError "unknown" end
network_url_for(network)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 465 def network_url_for(network) "projects/#{project}/global/networks/#{network}" end
operation_errors(operation_name)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 641 def operation_errors(operation_name) operation = zone_operation(operation_name) return [] if operation.error.nil? operation.error.errors end
paginated_results(api_method, items_method, *args)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 563 def paginated_results(api_method, items_method, *args) items = [] next_token = nil loop_num = 0 loop do loop_num += 1 response = connection.send(api_method.to_sym, *args, max_results: max_page_size, page_token: next_token) response_items = response.send(items_method.to_sym) break if response_items.nil? items += response_items next_token = response.next_page_token break if next_token.nil? if loop_num >= max_pages ui.warn("Max pages (#{max_pages}) reached, but more results exist - truncating results...") break end end items end
public_project_for_image(image)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 536 def public_project_for_image(image) case image when /centos/ "centos-cloud" when /container-vm/ "google-containers" when /coreos/ "coreos-cloud" when /debian/ "debian-cloud" when /opensuse-cloud/ "opensuse-cloud" when /rhel/ "rhel-cloud" when /sles/ "suse-cloud" when /ubuntu/ "ubuntu-os-cloud" when /windows/ "windows-cloud" end end
region()
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 296 def region @region ||= connection.get_zone(project, zone).region.split("/").last end
server_summary(server, _columns_with_info = nil)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 525 def server_summary(server, _columns_with_info = nil) msg_pair("Instance Name", server.name) msg_pair("Status", server.status) msg_pair("Machine Type", machine_type_for(server)) msg_pair("Project", project) msg_pair("Zone", zone) msg_pair("Network", network_for(server)) msg_pair("Private IP", private_ip_for(server)) msg_pair("Public IP", public_ip_for(server)) end
service_account_scope_url(scope)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 496 def service_account_scope_url(scope) return scope if scope.start_with?("https://www.googleapis.com/auth/") "https://www.googleapis.com/auth/#{translate_scope_alias(scope)}" end
subnet_url_for(subnet)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 469 def subnet_url_for(subnet) "projects/#{project}/regions/#{region}/subnetworks/#{subnet}" end
translate_scope_alias(scope_alias)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 502 def translate_scope_alias(scope_alias) SCOPE_ALIAS_MAP.fetch(scope_alias, scope_alias) end
valid_ip_address?(ip_address)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 288 def valid_ip_address?(ip_address) IPAddr.new(ip_address) rescue IPAddr::InvalidAddressError false else true end
valid_machine_type?(machine_type)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 255 def valid_machine_type?(machine_type) return false if machine_type.nil? check_api_call { connection.get_machine_type(project, zone, machine_type) } end
valid_network?(network)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 261 def valid_network?(network) return false if network.nil? check_api_call { connection.get_network(project, network) } end
valid_public_ip_setting?(public_ip)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 277 def valid_public_ip_setting?(public_ip) case when public_ip.nil? || public_ip.match(/(ephemeral|none)/i) true when valid_ip_address?(public_ip) true else false end end
valid_subnet?(subnet)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 267 def valid_subnet?(subnet) return false if subnet.nil? check_api_call { connection.get_subnetwork(project, region, subnet) } end
validate_server_create_options!(options)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 238 def validate_server_create_options!(options) raise "Invalid machine type: #{options[:machine_type]}" unless valid_machine_type?(options[:machine_type]) raise "Invalid network: #{options[:network]}" unless valid_network?(options[:network]) raise "Invalid subnet: #{options[:subnet]}" if options[:subnet] && !valid_subnet?(options[:subnet]) raise "Invalid Public IP setting: #{options[:public_ip]}" unless valid_public_ip_setting?(options[:public_ip]) raise "Invalid image: #{options[:image]} - check your image name, or set an image project if needed" if boot_disk_source_image(options[:image], options[:image_project]).nil? raise "Maximum number of local SSDs for an instance should be 8, while #{options[:number_of_local_ssd]} is requested." if options[:number_of_local_ssd].to_i > 8 end
wait_for_operation(operation)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 622 def wait_for_operation(operation) operation_name = operation.name wait_for_status("DONE") { zone_operation(operation_name) } errors = operation_errors(operation_name) return if errors.empty? errors.each do |error| ui.error("#{ui.color(error.code, :bold)}: #{error.message}") end raise "Operation #{operation_name} failed." end
wait_for_status(requested_status) { || ... }
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 590 def wait_for_status(requested_status) last_status = "" begin Timeout.timeout(wait_time) do loop do item = yield current_status = item.status if current_status == requested_status print "\n" break end if last_status == current_status print "." else last_status = current_status print "\n" print "Current status: #{current_status}." end sleep refresh_rate end end rescue Timeout::Error ui.msg("") ui.error("Request did not complete in #{wait_time} seconds. Check the Google Cloud Console for more info.") exit 1 end end
zone_operation(operation_name)
click to toggle source
# File lib/chef/knife/cloud/google_service.rb, line 637 def zone_operation(operation_name) connection.get_zone_operation(project, zone, operation_name) end