diff --git a/lib/fog/google/compute.rb b/lib/fog/google/compute.rb index 74ae61e7a..2b2ecb5e4 100644 --- a/lib/fog/google/compute.rb +++ b/lib/fog/google/compute.rb @@ -29,13 +29,16 @@ module Fog request :get_network request :get_zone request :get_snapshot + request :get_global_operation + request :get_zone_operation request :delete_disk request :delete_firewall request :delete_image request :delete_network - request :delete_operation request :delete_server + request :delete_global_operation + request :delete_zone_operation request :insert_disk request :insert_firewall @@ -59,6 +62,9 @@ module Fog model :disk collection :disks + model :operation + collection :operations + model :snapshot collection :snapshots @@ -73,10 +79,10 @@ module Fog @api_version = 'v1beta16' end - def build_excon_response(body) + def build_excon_response(body, status=200) response = Excon::Response.new response.body = body - if response.body["error"] + if response.body and response.body["error"] response.status = response.body["error"]["code"] msg = response.body["error"]["errors"].map{|error| error["message"]}.join(", ") case response.status @@ -86,7 +92,7 @@ module Fog raise Fog::Errors::Error.new(msg) end else - response.status = 200 + response.status = status end response end @@ -763,7 +769,8 @@ module Fog } end, :images => {}, - :disks => {} + :disks => {}, + :operations => {} } end end @@ -782,6 +789,9 @@ module Fog self.class.data(api_version).delete(@project) end + def random_operation + "operation-#{Fog::Mock.random_numbers(13)}-#{Fog::Mock.random_hex(13)}-#{Fog::Mock.random_hex(8)}" + end end class Real @@ -842,7 +852,7 @@ module Fog # result = Google::APIClient::Result # returns Excon::Response def build_response(result) - build_excon_response(Fog::JSON.decode(result.body)) + build_excon_response(result.body.nil? ? nil : Fog::JSON.decode(result.body), result.status) end end diff --git a/lib/fog/google/models/compute/disk.rb b/lib/fog/google/models/compute/disk.rb index 87b72ca29..b4d843804 100644 --- a/lib/fog/google/models/compute/disk.rb +++ b/lib/fog/google/models/compute/disk.rb @@ -38,7 +38,13 @@ module Fog def destroy requires :name, :zone_name - service.delete_disk(name, zone_name) + operation = service.delete_disk(name, zone_name) + # wait until "RUNNING" or "DONE" to ensure the operation doesn't fail, raises exception on error + Fog.wait_for do + operation = service.get_zone_operation(zone_name, operation.body["name"]) + operation.body["status"] != "PENDING" + end + operation end def zone diff --git a/lib/fog/google/models/compute/operation.rb b/lib/fog/google/models/compute/operation.rb new file mode 100644 index 000000000..78e13347f --- /dev/null +++ b/lib/fog/google/models/compute/operation.rb @@ -0,0 +1,42 @@ +require 'fog/core/model' + +module Fog + module Compute + class Google + + class Operation < Fog::Model + + identity :name + + attribute :kind, :aliases => 'kind' + attribute :id, :aliases => 'id' + attribute :creation_timestamp, :aliases => 'creationTimestamp' + attribute :zone_name, :aliases => 'zone' + attribute :status, :aliases => 'status' + attribute :self_link, :aliases => 'selfLink' + + def ready? + self.status == DONE_STATE + end + + def pending? + self.status == PENDING_STATE + end + + def reload + requires :identity + + data = collection.get(identity, zone) + new_attributes = data.attributes + merge_attributes(new_attributes) + self + end + + PENDING_STATE = "PENDING" + RUNNING_STATE = "RUNNING" + DONE_STATE = "DONE" + + end + end + end +end diff --git a/lib/fog/google/models/compute/operations.rb b/lib/fog/google/models/compute/operations.rb new file mode 100644 index 000000000..274568e2c --- /dev/null +++ b/lib/fog/google/models/compute/operations.rb @@ -0,0 +1,26 @@ +require 'fog/core/collection' +require 'fog/google/models/compute/operation' + +module Fog + module Compute + class Google + + class Operations < Fog::Collection + + model Fog::Compute::Google::Operation + + def get(identity, zone=nil) + if zone.nil? + response = service.get_global_operation(identity) + else + response = service.get_zone_operation(zone, identity) + end + return nil if response.nil? + new(response.body) + end + + end + + end + end +end diff --git a/lib/fog/google/models/compute/server.rb b/lib/fog/google/models/compute/server.rb index 2b2d4c922..3f264029b 100644 --- a/lib/fog/google/models/compute/server.rb +++ b/lib/fog/google/models/compute/server.rb @@ -30,7 +30,13 @@ module Fog def destroy requires :name, :zone - service.delete_server(name, zone) + operation = service.delete_server(name, zone) + # wait until "RUNNING" or "DONE" to ensure the operation doesn't fail, raises exception on error + Fog.wait_for do + operation = service.get_zone_operation(zone_name, operation.body["name"]) + operation.body["status"] != "PENDING" + end + operation end def image diff --git a/lib/fog/google/requests/compute/delete_disk.rb b/lib/fog/google/requests/compute/delete_disk.rb index a8555db35..feb43d07a 100644 --- a/lib/fog/google/requests/compute/delete_disk.rb +++ b/lib/fog/google/requests/compute/delete_disk.rb @@ -6,23 +6,26 @@ module Fog def delete_disk(disk_name, zone_name) get_disk(disk_name, zone_name) - self.data[:disks].delete disk_name - build_response(:body => { + operation = self.random_operation + self.data[:operations][operation] = { "kind" => "compute#operation", - "id" => "7145812689701515415", - "name" => "operation-1385125998242-4ebc3c7173e70-11e1ad0b", + "id" => Fog::Mock.random_numbers(19).to_s, + "name" => operation, "zone" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}", "operationType" => "delete", "targetLink" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}/disks/#{disk_name}", - "targetId" => "6817095360746367667", - "status" => "PENDING", + "targetId" => self.data[:disks][disk_name]["id"], + "status" => Fog::Compute::Google::Operation::PENDING_STATE, "user" => "123456789012-qwertyuiopasdfghjkl1234567890qwe@developer.gserviceaccount.com", "progress" => 0, "insertTime" => Time.now.iso8601, "startTime" => Time.now.iso8601, - "selfLink" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}/operations/operation-1385125998242-4ebc3c7173e70-11e1ad0b" - }) + "selfLink" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}/operations/#{operation}" + } + self.data[:disks].delete disk_name + + build_response(:body => self.data[:operations][operation]) end end diff --git a/lib/fog/google/requests/compute/delete_global_operation.rb b/lib/fog/google/requests/compute/delete_global_operation.rb new file mode 100644 index 000000000..09829f7ed --- /dev/null +++ b/lib/fog/google/requests/compute/delete_global_operation.rb @@ -0,0 +1,29 @@ +module Fog + module Compute + class Google + + class Mock + + def delete_global_operation(operation) + Fog::Mock.not_implemented + end + + end + + class Real + # https://developers.google.com/compute/docs/reference/latest/globalOperations + + def delete_global_operation(operation) + api_method = @compute.global_operations.delete + parameters = { + 'project' => @project, + 'operation' => operation + } + + result = self.build_result(api_method, parameters) + response = self.build_response(result) + end + end + end + end +end diff --git a/lib/fog/google/requests/compute/delete_server.rb b/lib/fog/google/requests/compute/delete_server.rb index 8e2794ca7..8c1868530 100644 --- a/lib/fog/google/requests/compute/delete_server.rb +++ b/lib/fog/google/requests/compute/delete_server.rb @@ -28,21 +28,25 @@ module Fog server = self.data[:servers][server_name] server["status"] = "STOPPED" server["mock-deletionTimestamp"] = Time.now.iso8601 - build_response(:body => { + + operation = self.random_operation + self.data[:operations][operation] = { "kind" => "compute#operation", - "id" => "10035781241131638365", - "name" => "operation-1380213292196-4e74bf2fbc3c1-ae707d47", + "id" => Fog::Mock.random_numbers(19).to_s, + "name" => operation, "zone" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}", "operationType" => "delete", "targetLink" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}/instances/#{server_name}", - "targetId" => "14544909043643897380", - "status" => "PENDING", + "targetId" => self.data[:servers][server_name]["id"], + "status" => Fog::Compute::Google::Operation::PENDING_STATE, "user" => "123456789012-qwertyuiopasdfghjkl1234567890qwe@developer.gserviceaccount.com", "progress" => 0, "insertTime" => Time.now.iso8601, "startTime" => Time.now.iso8601, - "selfLink" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}/operations/operation-1380213292196-4e74bf2fbc3c1-ae707d47" - }) + "selfLink" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}/operations/#{operation}" + } + + build_response(:body => self.data[:operations][operation]) end end diff --git a/lib/fog/google/requests/compute/delete_zone_operation.rb b/lib/fog/google/requests/compute/delete_zone_operation.rb new file mode 100644 index 000000000..7b93db7eb --- /dev/null +++ b/lib/fog/google/requests/compute/delete_zone_operation.rb @@ -0,0 +1,33 @@ +module Fog + module Compute + class Google + + class Mock + + def delete_zone_operation(zone, operation) + Fog::Mock.not_implemented + end + + end + + class Real + # https://developers.google.com/compute/docs/reference/latest/zoneOperations + + def delete_zone_operation(zone_name, operation) + if zone_name.start_with? 'http' + zone_name = zone_name.split('/')[-1] + end + api_method = @compute.zone_operations.delete + parameters = { + 'project' => @project, + 'zone' => zone_name, + 'operation' => operation + } + + result = self.build_result(api_method, parameters) + response = self.build_response(result) + end + end + end + end +end diff --git a/lib/fog/google/requests/compute/delete_operation.rb b/lib/fog/google/requests/compute/get_global_operation.rb similarity index 57% rename from lib/fog/google/requests/compute/delete_operation.rb rename to lib/fog/google/requests/compute/get_global_operation.rb index f5233f243..1872e1ca1 100644 --- a/lib/fog/google/requests/compute/delete_operation.rb +++ b/lib/fog/google/requests/compute/get_global_operation.rb @@ -4,27 +4,26 @@ module Fog class Mock - def delete_operation(operation_name) + def get_global_operation(operation) Fog::Mock.not_implemented end end class Real + # https://developers.google.com/compute/docs/reference/latest/globalOperations - def delete_operation(operation_name) - api_method = @compute.operations.delete + def get_global_operation(operation) + api_method = @compute.global_operations.get parameters = { 'project' => @project, - 'operation' => operation_name + 'operation' => operation } result = self.build_result(api_method, parameters) response = self.build_response(result) end - end - end end end diff --git a/lib/fog/google/requests/compute/get_zone_operation.rb b/lib/fog/google/requests/compute/get_zone_operation.rb new file mode 100644 index 000000000..a119dcf0c --- /dev/null +++ b/lib/fog/google/requests/compute/get_zone_operation.rb @@ -0,0 +1,58 @@ +module Fog + module Compute + class Google + + class Mock + + def get_zone_operation(zone_name, operation) + operation = self.data[:operations][operation] + if operation + case operation["status"] + when Fog::Compute::Google::Operation::PENDING_STATE + operation["status"] = Fog::Compute::Google::Operation::RUNNING_STATE + operation["progress"] = 50 + else + operation["status"] = Fog::Compute::Google::Operation::DONE_STATE + operation["progress"] = 100 + end + else + operation = { + "error" => { + "errors" => [ + { + "domain" => "global", + "reason" => "notFound", + "message" => "The resource 'projects/#{project}/zones/#{zone_name}/operations/#{operation}' was not found" + } + ], + "code" => 404, + "message" => "The resource 'projects/#{project}/zones/#{zone_name}/operations/#{operation}' was not found" + } + } + end + build_response(:body => operation) + end + end + + class Real + # https://developers.google.com/compute/docs/reference/latest/zoneOperations + + def get_zone_operation(zone_name, operation) + if zone_name.start_with? 'http' + zone_name = zone_name.split('/')[-1] + end + + api_method = @compute.zone_operations.get + parameters = { + 'project' => @project, + 'zone' => zone_name, + 'operation' => operation + } + + result = self.build_result(api_method, parameters) + response = self.build_response(result) + end + end + end + end +end diff --git a/lib/fog/google/requests/compute/insert_disk.rb b/lib/fog/google/requests/compute/insert_disk.rb index 6e2ae990b..fc176614c 100644 --- a/lib/fog/google/requests/compute/insert_disk.rb +++ b/lib/fog/google/requests/compute/insert_disk.rb @@ -16,9 +16,10 @@ module Fog end get_zone(zone_name) + id = Fog::Mock.random_numbers(19).to_s self.data[:disks][disk_name] = { "kind" => "compute#disk", - "id" => Fog::Mock.random_numbers(19), + "id" => id, "creationTimestamp" => Time.now.iso8601, "zone" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}", "status" => "READY", @@ -27,20 +28,24 @@ module Fog "selfLink" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}/disks/#{disk_name}" } - build_response(:body => { + operation = self.random_operation + self.data[:operations][operation] = { "kind" => "compute#operation", - "id" => "12498846269172327286", - "name" => "operation-1385124218076-4ebc35cfbe9f1-476486c5", + "id" => Fog::Mock.random_numbers(19).to_s, + "name" => operation, "zone" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}", "operationType" => "insert", "targetLink" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}/disks/#{disk_name}", - "status" => "PENDING", + "targetId" => id, + "status" => Fog::Compute::Google::Operation::PENDING_STATE, "user" => "123456789012-qwertyuiopasdfghjkl1234567890qwe@developer.gserviceaccount.com", "progress" => 0, "insertTime" => Time.now.iso8601, "startTime" => Time.now.iso8601, - "selfLink" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}/operations/operation-1385124218076-4ebc35cfbe9f1-476486c5" - }) + "selfLink" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}/operations/#{operation}" + } + + build_response(:body => self.data[:operations][operation]) end end diff --git a/lib/fog/google/requests/compute/insert_server.rb b/lib/fog/google/requests/compute/insert_server.rb index 8a3c871de..0912e3a6f 100644 --- a/lib/fog/google/requests/compute/insert_server.rb +++ b/lib/fog/google/requests/compute/insert_server.rb @@ -37,9 +37,10 @@ module Fog end get_zone(zone_name) + id = Fog::Mock.random_numbers(19).to_s self.data[:servers][server_name] = { "kind" => "compute#instance", - "id" => Fog::Mock.random_numbers(19), + "id" => id, "creationTimestamp" => Time.now.iso8601, "zone" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}", "status" => "PROVISIONING", @@ -85,20 +86,24 @@ module Fog "selfLink" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}/instances/#{server_name}" } - build_response(:body => { + operation = self.random_operation + self.data[:operations][operation] = { "kind" => "compute#operation", - "id" => "4639689000254420481", - "name" => "operation-1380213292196-4e74bf2fbc3c1-ae707d47", + "id" => Fog::Mock.random_numbers(19).to_s, + "name" => operation, "zone" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}", "operationType" => "insert", "targetLink" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}/instances/#{server_name}", - "status" => "PENDING", + "targetId" => id, + "status" => Fog::Compute::Google::Operation::PENDING_STATE, "user" => "123456789012-qwertyuiopasdfghjkl1234567890qwe@developer.gserviceaccount.com", "progress" => 0, "insertTime" => Time.now.iso8601, "startTime" => Time.now.iso8601, - "selfLink" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}/operations/operation-1380213292196-4e74bf2fbc3c1-ae707d47" - }) + "selfLink" => "https://www.googleapis.com/compute/#{api_version}/projects/#{@project}/zones/#{zone_name}/operations/#{operation}" + } + + build_response(:body => self.data[:operations][operation]) end end diff --git a/lib/fog/google/requests/compute/list_zone_operations.rb b/lib/fog/google/requests/compute/list_zone_operations.rb index 37ae742cc..13898bc82 100644 --- a/lib/fog/google/requests/compute/list_zone_operations.rb +++ b/lib/fog/google/requests/compute/list_zone_operations.rb @@ -11,6 +11,7 @@ module Fog end class Real + # https://developers.google.com/compute/docs/reference/latest/zoneOperations def list_zone_operations(zone) api_method = @compute.zone_operations.list