require 'google-cloud-compute' # rubocop:disable Rails/SkipsModelValidations, Metrics/ClassLength module ForemanGoogle class GoogleComputeAdapter def initialize(auth_json_string:) @auth_json = JSON.parse(auth_json_string) end def project_id @auth_json['project_id'] end # ------ RESOURCES ------ def insert_instance(zone, attrs = {}) response = resource_client('instances').insert(project: project_id, zone: zone, instance_resource: attrs) operation_attrs = { zone: zone, operation: response.operation.id.to_s } wait_for do get('zone_operations', **operation_attrs).status == :DONE end e = get('zone_operations', **operation_attrs).error return response unless e raise ::Google::Cloud::Error, e.errors.first.message end # Returns an Google::Instance identified by instance_identity within given zone. # @param zone [String] eighter full url or just zone name # @param instance_identity [String] eighter an instance name or its id def instance(zone, instance_identity) get('instances', instance: instance_identity, zone: zone) end def instances(zone, **attrs) list('instances', zone: zone, **attrs) end def zones list('zones') end def networks list('networks') end def machine_types(zone) list('machine_types', zone: zone) end def start(zone, instance_identity) manage_instance(:start, zone: zone, instance: instance_identity) end def stop(zone, instance_identity) manage_instance(:stop, zone: zone, instance: instance_identity) end def delete_instance(zone, instance_identity) manage_instance(:delete, zone: zone, instance: instance_identity) end # Setting filter to '(deprecated.state != "DEPRECATED") AND (deprecated.state != "OBSOLETE")' # doesn't work and returns empty array, no idea what is happening there def images(filter: nil) projects = [project_id] + all_projects all_images = projects.map { |project| list_images(project, filter: filter) } all_images.flatten.reject(&:deprecated) end def image(uuid) images.find { |img| img.id == uuid } end def insert_disk(zone, disk_attrs = {}) insert('disks', zone, disk_resource: disk_attrs) end def disk(zone, name) get('disks', disk: name, zone: zone) end def delete_disk(zone, disk_name) delete('disks', zone, disk: disk_name) end def set_disk_auto_delete(zone, instance_identity) instance = instance(zone, instance_identity) instance.disks.each do |disk| manage_instance :set_disk_auto_delete, zone: zone, device_name: disk.device_name, instance: instance_identity, auto_delete: true end end def serial_port_output(zone, instance_identity) manage_instance(:get_serial_port_output, zone: zone, instance: instance_identity) end def wait_for timeout = 60 duration = 0 start = Time.zone.now loop do break if yield raise "The specified wait_for timeout (#{timeout} seconds) was exceeded" if duration > timeout sleep(1) duration = Time.zone.now - start end { duration: duration } end private def list(resource_name, **opts) response = resource_client(resource_name).list(project: project_id, **opts).response response.items rescue ::Google::Cloud::Error => e raise Foreman::WrappedException.new(e, 'Cannot list Google resource %s', resource_name) end def get(resource_name, **opts) resource_client(resource_name).get(project: project_id, **opts) rescue Google::Cloud::NotFoundError => e Foreman::Logging.exception("Could not fetch Google instance [#{opts[:instance]}]", e) raise ActiveRecord::RecordNotFound rescue ::Google::Cloud::Error => e raise Foreman::WrappedException.new(e, 'Could not fetch Google resource %s', resource_name) end def insert(resource_name, zone, **opts) resource_client(resource_name).insert(project: project_id, zone: zone, **opts) rescue ::Google::Cloud::Error => e raise Foreman::WrappedException.new(e, 'Could not create Google resource %s', resource_name) end def delete(resource_name, zone, **opts) resource_client(resource_name).delete(project: project_id, zone: zone, **opts) rescue ::Google::Cloud::Error => e raise Foreman::WrappedException.new(e, 'Could not delete Google resource %s', resource_name) end def list_images(project, **opts) resource_name = 'images' response = resource_client(resource_name).list(project: project, **opts).response response.items rescue ::Google::Cloud::Error => e raise Foreman::WrappedException.new(e, 'Cannot list Google resource %s', resource_name) end def manage_instance(action, **opts) resource_client('instances').send(action, project: project_id, **opts) rescue ::Google::Cloud::Error => e raise Foreman::WrappedException.new(e, 'Could not %s Google resource %s', action.to_s, resource_name) end def resource_client(resource_name) ::Google::Cloud::Compute.public_send(resource_name) do |config| config.credentials = @auth_json end end def all_projects %w[centos-cloud cos-cloud coreos-cloud debian-cloud opensuse-cloud rhel-cloud rhel-sap-cloud suse-cloud suse-sap-cloud ubuntu-os-cloud windows-cloud windows-sql-cloud].freeze end end end # rubocop:enable Rails/SkipsModelValidations, Metrics/ClassLength