Skip to content

Commit

Permalink
Add snapshotting for instances in the API
Browse files Browse the repository at this point in the history
  • Loading branch information
imtayadeway committed Feb 8, 2017
1 parent 6001c08 commit 8c2b3f1
Show file tree
Hide file tree
Showing 3 changed files with 265 additions and 0 deletions.
1 change: 1 addition & 0 deletions app/controllers/api/instances_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module Api
class InstancesController < BaseController
include Subcollections::LoadBalancers
include Subcollections::Snapshots

def terminate_resource(type, id = nil, _data = nil)
raise BadRequestError, "Must specify an id for terminating a #{type} resource" unless id
Expand Down
20 changes: 20 additions & 0 deletions config/api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,7 @@
- :collection
:subcollections:
- :load_balancers
- :snapshots
:collection_actions:
:get:
- :name: read
Expand Down Expand Up @@ -657,6 +658,25 @@
:get:
- :name: show
:identifier: load_balancer_show
:snapshots_subcollection_actions:
:get:
- :name: read
:identifier: cloud_volume_snapshot_view
:post:
- :name: create
:identifier: cloud_volume_snapshot_create
- :name: delete
:identifier: cloud_volume_snapshot_delete
:snapshots_subresource_actions:
:get:
- :name: read
:identifier: cloud_volume_snapshot_view
:post:
- :name: delete
:identifier: cloud_volume_snapshot_delete
:delete:
- :name: delete
:identifier: cloud_volume_snapshot_delete
:load_balancers:
:description: Load Balancers
:options:
Expand Down
244 changes: 244 additions & 0 deletions spec/requests/api/snapshots_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -240,4 +240,248 @@
end
end
end

describe "as a subcollection of instances" do
describe "GET /api/instances/:c_id/snapshots" do
it "can list the snapshots of an Instance" do
api_basic_authorize(subcollection_action_identifier(:instances, :snapshots, :read, :get))
instance = FactoryGirl.create(:vm_openstack)
snapshot = FactoryGirl.create(:snapshot, :vm_or_template => instance)
_other_snapshot = FactoryGirl.create(:snapshot)

run_get("#{instances_url(instance.id)}/snapshots")

expected = {
"count" => 2,
"name" => "snapshots",
"subcount" => 1,
"resources" => [
{"href" => a_string_matching("#{instances_url(instance.id)}/snapshots/#{snapshot.id}")}
]
}
expect(response.parsed_body).to include(expected)
expect(response).to have_http_status(:ok)
end

it "will not list snapshots unless authorized" do
api_basic_authorize
instance = FactoryGirl.create(:vm_openstack)
_snapshot = FactoryGirl.create(:snapshot, :vm_or_template => instance)

run_get("#{instances_url(instance.id)}/snapshots")

expect(response).to have_http_status(:forbidden)
end
end

describe "GET /api/instances/:c_id/snapshots/:s_id" do
it "can show an Instance's snapshot" do
api_basic_authorize(subcollection_action_identifier(:instances, :snapshots, :read, :get))
instance = FactoryGirl.create(:vm_openstack)
create_time = Time.zone.parse("2017-01-11T00:00:00Z")
snapshot = FactoryGirl.create(:snapshot, :vm_or_template => instance, :create_time => create_time)

run_get("#{instances_url(instance.id)}/snapshots/#{snapshot.id}")

expected = {
"create_time" => create_time.iso8601,
"href" => a_string_matching("#{instances_url(instance.id)}/snapshots/#{snapshot.id}"),
"id" => snapshot.id,
"vm_or_template_id" => instance.id
}
expect(response.parsed_body).to include(expected)
expect(response).to have_http_status(:ok)
end

it "will not show a snapshot unless authorized" do
api_basic_authorize
instance = FactoryGirl.create(:vm_openstack)
snapshot = FactoryGirl.create(:snapshot, :vm_or_template => instance)

run_get("#{instances_url(instance.id)}/snapshots/#{snapshot.id}")

expect(response).to have_http_status(:forbidden)
end

describe "POST /api/instances/:c_id/snapshots" do
it "can queue the creation of a snapshot" do
api_basic_authorize(subcollection_action_identifier(:instances, :snapshots, :create))
ems = FactoryGirl.create(:ems_openstack_infra)
host = FactoryGirl.create(:host_openstack_infra, :ext_management_system => ems)
instance = FactoryGirl.create(:vm_openstack, :name => "Alice's Instance", :ext_management_system => ems, :host => host)

run_post("#{instances_url(instance.id)}/snapshots", :name => "Alice's snapshot")

expected = {
"results" => [
a_hash_including(
"success" => true,
"message" => "Creating snapshot Alice's snapshot for Vm id:#{instance.id} name:'Alice's Instance'",
"task_id" => anything,
"task_href" => a_string_matching(tasks_url)
)
]
}
expect(response.parsed_body).to include(expected)
expect(response).to have_http_status(:ok)
end

it "renders a failed action response if snapshotting is not supported" do
api_basic_authorize(subcollection_action_identifier(:instances, :snapshots, :create))
instance = FactoryGirl.create(:vm_openstack)

run_post("#{instances_url(instance.id)}/snapshots", :name => "Alice's snapsnot")

expected = {
"results" => [
a_hash_including(
"success" => false,
"message" => "The VM is not connected to an active Provider"
)
]
}
expect(response.parsed_body).to include(expected)
expect(response).to have_http_status(:ok)
end

it "renders a failed action response if a name is not provided" do
api_basic_authorize(subcollection_action_identifier(:instances, :snapshots, :create))
ems = FactoryGirl.create(:ems_openstack_infra)
host = FactoryGirl.create(:host_openstack_infra, :ext_management_system => ems)
instance = FactoryGirl.create(:vm_openstack, :name => "Alice's Instance", :ext_management_system => ems, :host => host)

run_post("#{instances_url(instance.id)}/snapshots", :description => "Alice's snapshot")

expected = {
"results" => [
a_hash_including(
"success" => false,
"message" => "Must specify a name for the snapshot"
)
]
}
expect(response.parsed_body).to include(expected)
expect(response).to have_http_status(:ok)
end

it "will not create a snapshot unless authorized" do
api_basic_authorize
instance = FactoryGirl.create(:vm_openstack)

run_post("#{instances_url(instance.id)}/snapshots", :description => "Alice's snapshot")

expect(response).to have_http_status(:forbidden)
end
end

describe "POST /api/instances/:c_id/snapshots/:s_id with delete action" do
it "can queue a snapshot for deletion" do
api_basic_authorize(action_identifier(:instances, :delete, :snapshots_subresource_actions, :delete))

ems = FactoryGirl.create(:ems_openstack_infra)
host = FactoryGirl.create(:host_openstack_infra, :ext_management_system => ems)
instance = FactoryGirl.create(:vm_openstack, :name => "Alice's Instance", :ext_management_system => ems, :host => host)
snapshot = FactoryGirl.create(:snapshot, :name => "Alice's snapshot", :vm_or_template => instance)

run_post("#{instances_url(instance.id)}/snapshots/#{snapshot.id}", :action => "delete")

expected = {
"message" => "Deleting snapshot Alice's snapshot for Vm id:#{instance.id} name:'Alice's Instance'",
"success" => true,
"task_href" => a_string_matching(tasks_url),
"task_id" => anything
}
expect(response.parsed_body).to include(expected)
expect(response).to have_http_status(:ok)
end

it "renders a failed action response if deleting is not supported" do
api_basic_authorize(action_identifier(:instances, :delete, :snapshots_subresource_actions, :post))
instance = FactoryGirl.create(:vm_openstack)
snapshot = FactoryGirl.create(:snapshot, :vm_or_template => instance)

run_post("#{instances_url(instance.id)}/snapshots/#{snapshot.id}", :action => "delete")

expected = {
"success" => false,
"message" => "The VM is not connected to an active Provider"
}
expect(response.parsed_body).to include(expected)
expect(response).to have_http_status(:ok)
end

it "will not delete a snapshot unless authorized" do
api_basic_authorize
instance = FactoryGirl.create(:vm_openstack)
snapshot = FactoryGirl.create(:snapshot, :vm_or_template => instance)

run_post("#{instances_url(instance.id)}/snapshots/#{snapshot.id}", :action => "delete")

expect(response).to have_http_status(:forbidden)
end
end

describe "POST /api/instances/:c_id/snapshots with delete action" do
it "can queue multiple snapshots for deletion" do
api_basic_authorize(action_identifier(:instances, :delete, :snapshots_subresource_actions, :delete))

ems = FactoryGirl.create(:ems_openstack_infra)
host = FactoryGirl.create(:host_openstack_infra, :ext_management_system => ems)
instance = FactoryGirl.create(:vm_openstack, :name => "Alice and Bob's Instance", :ext_management_system => ems, :host => host)
snapshot1 = FactoryGirl.create(:snapshot, :name => "Alice's snapshot", :vm_or_template => instance)
snapshot2 = FactoryGirl.create(:snapshot, :name => "Bob's snapshot", :vm_or_template => instance)

run_post(
"#{instances_url(instance.id)}/snapshots",
:action => "delete",
:resources => [
{:href => "#{instances_url(instance.id)}/snapshots/#{snapshot1.id}"},
{:href => "#{instances_url(instance.id)}/snapshots/#{snapshot2.id}"}
]
)

expected = {
"results" => a_collection_containing_exactly(
a_hash_including(
"message" => "Deleting snapshot Alice's snapshot for Vm id:#{instance.id} name:'Alice and Bob's Instance'",
"success" => true,
"task_href" => a_string_matching(tasks_url),
"task_id" => anything
),
a_hash_including(
"message" => "Deleting snapshot Bob's snapshot for Vm id:#{instance.id} name:'Alice and Bob's Instance'",
"success" => true,
"task_href" => a_string_matching(tasks_url),
"task_id" => anything
)
)
}
expect(response.parsed_body).to include(expected)
expect(response).to have_http_status(:ok)
end
end

describe "DELETE /api/instances/:c_id/snapshots/:s_id" do
it "can delete a snapshot" do
api_basic_authorize(action_identifier(:instances, :delete, :snapshots_subresource_actions, :delete))
instance = FactoryGirl.create(:vm_openstack)
snapshot = FactoryGirl.create(:snapshot, :vm_or_template => instance)

run_delete("#{instances_url(instance.id)}/snapshots/#{snapshot.id}")

expect(response).to have_http_status(:no_content)
end

it "will not delete a snapshot unless authorized" do
api_basic_authorize
instance = FactoryGirl.create(:vm_openstack)
snapshot = FactoryGirl.create(:snapshot, :vm_or_template => instance)

run_delete("#{instances_url(instance.id)}/snapshots/#{snapshot.id}")

expect(response).to have_http_status(:forbidden)
end
end
end
end
end

0 comments on commit 8c2b3f1

Please sign in to comment.