Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Rackspace Server/Image metadata operations #1452

Merged
merged 11 commits into from

4 participants

@krames
Owner

Updated Fog::Compute::RackspaceV2::Server and Fog::Compute::RackspaceV2::Image to support metadata operations. The implementation was inspired by the OpenStack provider.

@geemus
Owner

@krames - seems good, thanks!

@bradgignac @brianhartsock - could one of you review? Thanks!

tests/rackspace/models/compute_v2/metadata_tests.rb
((16 lines not shown))
+ @server.wait_for { ready? }
+ end
+ end
+
+ tests('image') do
+ image_id = @server.create_image("fog_image_#{test_time}", :metadata => {:my_key => 'my_value'})
+ @image = service.images.get image_id
+ @image.wait_for(timeout = 1500) { ready? }
+ tests("#all").succeeds do
+ pending if Fog.mocking? && !mocks_implemented
+ @image.metadata.all
+ end
+
+ tests("#get('my_key')").succeeds do
+ pending if Fog.mocking? && !mocks_implemented
+ @image.metadata.get('my_key')
@bradgignac Collaborator

Should this test be asserting on the data returned?

@krames Owner
krames added a note

I was wondering that as well. I dug though some of the other tests out there and it didn't seem like the model tests did not assert return values. I figured that was a matter of convention. I can update these tests to test the return if you think it's necessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
tests/rackspace/models/compute_v2/metadata_tests.rb
((11 lines not shown))
+ @server.wait_for(timeout=1500) { ready? }
+ @server = service.servers.get "2ee0e1b5-3350-40ae-873a-fff4941cc400"
+
+ tests('server') do
+ collection_tests(@server.metadata, {:key => 'my_key', :value => 'my_value'}) do
+ @server.wait_for { ready? }
+ end
+ end
+
+ tests('image') do
+ image_id = @server.create_image("fog_image_#{test_time}", :metadata => {:my_key => 'my_value'})
+ @image = service.images.get image_id
+ @image.wait_for(timeout = 1500) { ready? }
+ tests("#all").succeeds do
+ pending if Fog.mocking? && !mocks_implemented
+ @image.metadata.all
@bradgignac Collaborator

Should this test be asserting on the data returned?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@bradgignac
Collaborator

Aside from one question about test, this looks good to me. I'd like to hear @brianhartsock's feedback though.

@bradgignac
Collaborator

Also, it might be worth rebasing and adding [rackspace|compute] to the beginning of commit messages. I believe @geemus uses this metadata when generating the changelog for a release.

@geemus
Owner

@bradgignac - yep, the prefix on messages wouldn't be the end of the world, but it helps group the changelog by provider instead of putting things in the "misc" catch all/default.

Kyle Rames added better acceptance criteria to server/image metadata tests; adde…
…d metadata to ignored_attributes to address bug
49d1c38
@brianhartsock
Collaborator

LGTM, just looks like conflicts with master need to be resolved.

@krames
Owner

I will take a look at this first thing this morning.

Kyle Rames added some commits
@krames
Owner

@bradgignac I updated it with the lastest master and it passes all of the tests. Let me know if you need me to look at anything else.

Thanks!

@bradgignac bradgignac merged commit a8d159f into fog:master
@alanthing alanthing referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 2, 2013
  1. refactored Fog::Compute::RackspaceV2 metadata to take collection as a…

    Kyle Rames authored
    … parameter rather than using it as part of the method name.
Commits on Jan 3, 2013
  1. fixing broken metadata tests

    Kyle Rames authored
Commits on Jan 7, 2013
  1. changed metadata implementation for Fog::Compute::RackspaceV2::Server…

    Kyle Rames authored
    … and Fog::Compute::RackspaceV2::Image
  2. changing variable names to make code clearer

    Kyle Rames authored
  3. fixing broken tests

    Kyle Rames authored
  4. added better acceptance criteria to server/image metadata tests; adde…

    Kyle Rames authored
    …d metadata to ignored_attributes to address bug
Commits on Jan 8, 2013
  1. fixing merge issues with master

    Kyle Rames authored
  2. fixing typo

    Kyle Rames authored
  3. fixing broken tests

    Kyle Rames authored
This page is out of date. Refresh to see the latest.
View
2  lib/fog/core/attributes.rb
@@ -104,7 +104,7 @@ def identity(name, options = {})
end
def ignore_attributes(*args)
- @ignored_attributes = args
+ @ignored_attributes = args.collect {|attr| attr.to_s }
end
def ignored_attributes
View
7 lib/fog/rackspace/compute_v2.rb
@@ -54,6 +54,13 @@ class BadRequest < Fog::Rackspace::Errors::BadRequest; end
request :get_attachment
request :list_attachments
request :delete_attachment
+
+ request :list_metadata
+ request :set_metadata
+ request :update_metadata
+ request :get_metadata_item
+ request :set_metadata_item
+ request :delete_metadata_item
class Mock
View
23 lib/fog/rackspace/models/compute_v2/image.rb
@@ -21,9 +21,30 @@ class Image < Fog::Model
attribute :progress
attribute :minDisk
attribute :minRam
- attribute :metadata
attribute :disk_config, :aliases => 'OS-DCF:diskConfig'
attribute :links
+
+ ignore_attributes :metadata
+
+ def initialize(attributes={})
+ @connection = attributes[:connection]
+ super
+ end
+
+ def metadata
+ raise "Please save image before accessing metadata" unless identity
+ @metadata ||= begin
+ Fog::Compute::RackspaceV2::Metadata.new({
+ :connection => connection,
+ :parent => self
+ })
+ end
+ end
+
+ def metadata=(hash={})
+ raise "Please save image before accessing metadata" unless identity
+ metadata.from_hash(hash)
+ end
def ready?
state == ACTIVE
View
33 lib/fog/rackspace/models/compute_v2/meta_parent.rb
@@ -0,0 +1,33 @@
+module Fog
+ module Compute
+ class RackspaceV2
+ module MetaParent
+
+ def parent
+ @parent
+ end
+
+ def parent=(new_parent)
+ @parent = new_parent
+ end
+
+ def collection_name
+ if parent.class == Fog::Compute::RackspaceV2::Image
+ return "images"
+ elsif parent.class == Fog::Compute::RackspaceV2::Server
+ return "servers"
+ else
+ raise "Metadata is not supported for this model type."
+ end
+ end
+
+ def metas_to_hash(metas)
+ hash = {}
+ metas.each { |meta| hash[meta.key] = meta.value }
+ hash
+ end
+
+ end
+ end
+ end
+end
View
77 lib/fog/rackspace/models/compute_v2/metadata.rb
@@ -0,0 +1,77 @@
+require 'fog/core/collection'
+require 'fog/rackspace/models/compute_v2/meta_parent'
+require 'fog/rackspace/models/compute_v2/metadatum'
+require 'fog/rackspace/models/compute_v2/image'
+require 'fog/rackspace/models/compute_v2/server'
+
+module Fog
+ module Compute
+ class RackspaceV2
+
+ class Metadata < Fog::Collection
+
+ model Fog::Compute::RackspaceV2::Metadatum
+
+ include Fog::Compute::RackspaceV2::MetaParent
+
+ def all
+ requires :parent
+ data = connection.list_metadata(collection_name, parent.id).body['metadata']
+ from_hash(data)
+ end
+
+ def get(key)
+ requires :parent
+ data = connection.get_metadata_item(collection_name, parent.id, key).body["meta"]
+ datum = data.first
+ new(:key => datum[0], :value => datum[1])
+ rescue Fog::Compute::RackspaceV2::NotFound
+ nil
+ end
+
+ def [](key)
+ return super(key) if key.is_a?(Integer)
+ return nil unless key
+ datum = self.find {|datum| datum.key == key || datum.key == key.to_sym }
+ datum ? datum.value : nil
+ end
+
+ def []=(key, value)
+ return super(key,value) if key.is_a?(Integer)
+ return nil unless key
+ datum = self.find {|datum| datum.key == key || datum.key == key.to_sym }
+ if datum
+ data.value = value
+ else
+ self << Fog::Compute::RackspaceV2::Metadatum.new(:key => key, :value => value, :connection => connection, :parent => parent)
+ end
+ value
+ end
+
+ def save
+ requires :parent
+ connection.set_metadata(collection_name, parent.id, to_hash)
+ end
+
+ def new(attributes = {})
+ requires :parent
+ super({ :parent => parent }.merge!(attributes))
+ end
+
+ def from_hash(hash)
+ return unless hash
+ metas = []
+ hash.each_pair {|k,v| metas << {:key => k, :value => v} }
+ load(metas)
+ end
+
+ def to_hash
+ h = {}
+ self.each { |datum| h[datum.key] = datum.value }
+ h
+ end
+
+ end
+ end
+ end
+end
View
29 lib/fog/rackspace/models/compute_v2/metadatum.rb
@@ -0,0 +1,29 @@
+require 'fog/core/model'
+require 'fog/rackspace/models/compute_v2/meta_parent'
+
+module Fog
+ module Compute
+ class RackspaceV2
+ class Metadatum < Fog::Model
+
+ include Fog::Compute::RackspaceV2::MetaParent
+
+ identity :key
+ attribute :value
+
+ def destroy
+ requires :identity
+ connection.delete_metadata_item(collection_name, parent.id, key)
+ true
+ end
+
+ def save
+ requires :identity, :value
+ connection.set_metadata_item(collection_name, parent.id, key, value)
+ true
+ end
+
+ end
+ end
+ end
+end
View
29 lib/fog/rackspace/models/compute_v2/server.rb
@@ -1,4 +1,5 @@
require 'fog/compute/models/server'
+require 'fog/rackspace/models/compute_v2/metadata'
module Fog
module Compute
@@ -32,7 +33,6 @@ class Server < Fog::Compute::Server
attribute :user_id
attribute :tenant_id
attribute :links
- attribute :metadata
attribute :personality
attribute :ipv4_address, :aliases => 'accessIPv4'
attribute :ipv6_address, :aliases => 'accessIPv6'
@@ -41,8 +41,29 @@ class Server < Fog::Compute::Server
attribute :addresses
attribute :flavor_id, :aliases => 'flavor', :squash => 'id'
attribute :image_id, :aliases => 'image', :squash => 'id'
-
- attr_reader :password
+
+ ignore_attributes :metadata
+
+ attr_reader :password
+ def initialize(attributes={})
+ @service = attributes[:service]
+ super
+ end
+
+ def metadata
+ raise "Please save server before accessing metadata" unless identity
+ @metadata ||= begin
+ Fog::Compute::RackspaceV2::Metadata.new({
+ :service => service,
+ :parent => self
+ })
+ end
+ end
+
+ def metadata=(hash={})
+ raise "Please save server before accessing metadata" unless identity
+ metadata.from_hash(hash)
+ end
def save
if persisted?
@@ -58,7 +79,7 @@ def create
options = {}
options[:disk_config] = disk_config unless disk_config.nil?
- options[:metadata] = metadata unless metadata.nil?
+ options[:metadata] = metadata unless @metadata.nil?
options[:personality] = personality unless personality.nil?
data = service.create_server(name, image_id, flavor_id, 1, 1, options)
View
26 lib/fog/rackspace/requests/compute_v2/delete_metadata_item.rb
@@ -0,0 +1,26 @@
+module Fog
+ module Compute
+ class RackspaceV2
+ class Real
+ def delete_metadata_item(collection, obj_id, key)
+ request(
+ :expects => 204,
+ :method => 'DELETE',
+ :path => "/#{collection}/#{obj_id}/metadata/#{key}"
+ )
+ end
+ end
+
+ class Mock
+ def delete_metadata_item(collection, obj_id, key)
+ raise Fog::Compute::RackspaceV2::NotFound if obj_id == 0
+
+ response = Excon::Response.new
+ response.body = ""
+ response.status = 204
+ response
+ end
+ end
+ end
+ end
+end
View
26 lib/fog/rackspace/requests/compute_v2/get_metadata_item.rb
@@ -0,0 +1,26 @@
+module Fog
+ module Compute
+ class RackspaceV2
+ class Real
+ def get_metadata_item(collection, obj_id, key)
+ request(
+ :expects => 200,
+ :method => 'GET',
+ :path => "/#{collection}/#{obj_id}/metadata/#{key}"
+ )
+ end
+ end
+
+ class Mock
+ def get_metadata_item(collection, obj_id, key)
+ raise Fog::Compute::RackspaceV2::NotFound if obj_id == 0
+
+ response = Excon::Response.new
+ response.status = 202
+ response.body = {"meta" => {"environment" => "dev"}}
+ response
+ end
+ end
+ end
+ end
+end
View
26 lib/fog/rackspace/requests/compute_v2/list_metadata.rb
@@ -0,0 +1,26 @@
+module Fog
+ module Compute
+ class RackspaceV2
+ class Real
+ def list_metadata(collection, obj_id)
+ request(
+ :expects => [200, 203],
+ :method => 'GET',
+ :path => "/#{collection}/#{obj_id}/metadata"
+ )
+ end
+ end
+
+ class Mock
+ def list_metadata(collection, obj_id)
+ raise Fog::Compute::RackspaceV2::NotFound if obj_id == 0
+
+ response = Excon::Response.new
+ response.status = 202
+ response.body = { "metadata"=>{"tag"=>"database"} }
+ response
+ end
+ end
+ end
+ end
+end
View
28 lib/fog/rackspace/requests/compute_v2/set_metadata.rb
@@ -0,0 +1,28 @@
+module Fog
+ module Compute
+ class RackspaceV2
+ class Real
+ def set_metadata(collection, obj_id, metadata = {})
+ request(
+ :expects => [200, 203],
+ :method => 'PUT',
+ :path => "/#{collection}/#{obj_id}/metadata",
+ :body => Fog::JSON.encode('metadata' => metadata)
+ )
+ end
+ end
+
+
+ class Mock
+ def set_metadata(collection, obj_id, metadata = {})
+ raise Fog::Compute::RackspaceV2::NotFound if obj_id == 0
+
+ response = Excon::Response.new
+ response.status = 202
+ response.body = {"metadata"=>{"environment"=>"dev"}}
+ response
+ end
+ end
+ end
+ end
+end
View
27 lib/fog/rackspace/requests/compute_v2/set_metadata_item.rb
@@ -0,0 +1,27 @@
+module Fog
+ module Compute
+ class RackspaceV2
+ class Real
+ def set_metadata_item(collection, obj_id, key, value)
+ request(
+ :expects => 200,
+ :method => 'PUT',
+ :path => "/#{collection}/#{obj_id}/metadata/#{key}",
+ :body => Fog::JSON.encode('meta' => { key => value })
+ )
+ end
+ end
+
+ class Mock
+ def set_metadata_item(collection, obj_id, key, value)
+ raise Fog::Compute::RackspaceV2::NotFound if obj_id == 0
+
+ response = Excon::Response.new
+ response.status = 202
+ response.body = {"meta" => {key => value}}
+ response
+ end
+ end
+ end
+ end
+end
View
27 lib/fog/rackspace/requests/compute_v2/update_metadata.rb
@@ -0,0 +1,27 @@
+module Fog
+ module Compute
+ class RackspaceV2
+ class Real
+ def update_metadata(collection, obj_id, metadata = {})
+ request(
+ :expects => [200, 203],
+ :method => 'POST',
+ :path => "/#{collection}/#{obj_id}/metadata",
+ :body => Fog::JSON.encode('metadata' => metadata)
+ )
+ end
+ end
+
+ class Mock
+ def update_metadata(collection, obj_id, metadata = {})
+ raise Fog::Compute::RackspaceV2::NotFound if obj_id == 0
+
+ response = Excon::Response.new
+ response.status = 202
+ response.body = {"metadata" => {"environment" => "dev", "tag" => "database"}}
+ response
+ end
+ end
+ end
+ end
+end
View
42 tests/rackspace/models/compute_v2/metadata_tests.rb
@@ -0,0 +1,42 @@
+Shindo.tests('Fog::Compute::RackspaceV2 | metadata', ['rackspace']) do
+
+ pending if Fog.mocking?
+
+ service = Fog::Compute::RackspaceV2.new
+ test_time = Time.now.to_i.to_s
+
+ tests('success') do
+ begin
+ @server = service.servers.create(:name => "fog_server_#{test_time}", :flavor_id => 2, :image_id => "3afe97b2-26dc-49c5-a2cc-a2fc8d80c001")
+ @server.wait_for(timeout=1500) { ready? }
+
+ tests('server') do
+ collection_tests(@server.metadata, {:key => 'my_key', :value => 'my_value'}) do
+ @server.wait_for { ready? }
+ end
+ end
+
+ tests('image') do
+ image_id = @server.create_image("fog_image_#{test_time}", :metadata => {:my_key => 'my_value'})
+ @image = service.images.get image_id
+ @image.wait_for(timeout = 1500) { ready? }
+ tests("#all").succeeds do
+ pending if Fog.mocking? && !mocks_implemented
+ metadata = @image.metadata.all
+ my_metadata = metadata.select {|datum| datum.key == 'my_key'}
+ returns(1) { my_metadata.size }
+ returns('my_value') {my_metadata[0].value }
+ end
+
+ tests("#get('my_key')").returns('my_value') do
+ pending if Fog.mocking? && !mocks_implemented
+ @image.metadata.get('my_key').value
+ end
+ end
+
+ ensure
+ @image.destroy if @image
+ @server.destroy if @server
+ end
+ end
+end
View
104 tests/rackspace/requests/compute_v2/metadata_tests.rb
@@ -0,0 +1,104 @@
+Shindo.tests('Fog::Compute::RackspaceV2 | metadata_tests', ['rackspace']) do
+
+ @service = Fog::Compute.new(:provider => 'Rackspace', :version => 'V2')
+
+ tests('success') do
+ begin
+ metadata = {"tag" => "database"}
+
+ unless Fog.mocking?
+ name = "fog-server-metadata-#{Time.now.to_i}"
+ @server = @service.servers.create(:name => name,
+ :flavor_id => 2,
+ :image_id => '3afe97b2-26dc-49c5-a2cc-a2fc8d80c001',
+ :metadata => metadata)
+ @server.wait_for(timeout = 1500) { ready? }
+
+
+ @server_id = @server.id
+ @image_id = @server.create_image(name, :metadata => metadata)
+ @image = @service.images.get @image_id
+ else
+ @image_id = 1
+ @server_id = 1
+ end
+
+ tests("servers") do
+ tests('list_metadata').returns("metadata" => metadata) do
+ @service.list_metadata("servers", @server_id).body
+ end
+ tests('set_metadata').returns("metadata" => {"environment" => "dev"}) do
+ @service.set_metadata("servers", @server_id, {"environment" => "dev"}).body
+ end
+ tests('update_metadata').returns("metadata" => {"environment" => "dev", "tag" => "database"}) do
+ @service.update_metadata("servers", @server_id, {"environment" => "dev", "tag" => "database"}).body
+ end
+ tests('get_metadata_item').returns("meta" => {"environment" => "dev"}) do
+ @service.get_metadata_item("servers", @server_id, "environment").body
+ end
+ tests('set_metadata_item').returns("meta" => {"environment" => "test"}) do
+ @service.set_metadata_item("servers", @server_id, "environment", "test").body
+ end
+ tests('delete_metadata_item').succeeds do
+ @service.delete_metadata_item("servers", @server_id, "environment").body
+ end
+ end
+
+ tests("images") do
+ @image.wait_for(timeout = 1500) { ready? } unless Fog.mocking?
+
+ tests('list_metadata').returns(metadata) do
+ h = @service.list_metadata("images", @image_id).body
+ h["metadata"].reject {|k,v| k.downcase != "tag"} #only look at the metadata we created
+ end
+ tests('set_metadata').returns({"environment" => "dev"}) do
+ h = @service.set_metadata("images", @image_id, {"environment" => "dev"}).body
+ h["metadata"].reject {|k,v| k.downcase != "environment"} #only look at the metadata we created
+ end
+ tests('update_metadata').returns({"environment" => "dev", "tag" => "database"}) do
+ h = @service.update_metadata("images", @image_id, {"environment" => "dev", "tag" => "database"}).body
+ h["metadata"].reject {|k,v| !['environment', 'tag'].include?(k.downcase)} #only look at the metadata we created
+ end
+ tests('get_metadata_item').returns("meta" => {"environment" => "dev"}) do
+ @service.get_metadata_item("images", @image_id, "environment").body
+ end
+ tests('set_metadata_item').returns("meta" => {"environment" => "test"}) do
+ @service.set_metadata_item("images", @image_id, "environment", "test").body
+ end
+ tests('delete_metadata_item').succeeds do
+ @service.delete_metadata_item("images", @image_id, "environment").body
+ end
+ end
+ ensure
+ @image.destroy if @image
+ @server.destroy if @server
+ end
+ end
+
+ tests('failure') do
+ ['server', 'image'].each do |collection|
+ tests(collection) do
+ tests('list_metadata').raises(Fog::Compute::RackspaceV2::NotFound) do
+ @service.list_metadata(collection, 0)
+ end
+ tests('set_server_metadata').raises(Fog::Compute::RackspaceV2::NotFound) do
+ @service.set_metadata(collection, 0, {"environment" => "dev"})
+ end
+ tests('update_server_metadata').raises(Fog::Compute::RackspaceV2::NotFound) do
+ @service.update_metadata(collection, 0, {"environment" => "dev", "tag" => "database"})
+ end
+ tests('get_server_metadata_item').raises(Fog::Compute::RackspaceV2::NotFound) do
+ @service.get_metadata_item(collection, 0, "environment")
+ end
+ tests('set_server_metadata_item').raises(Fog::Compute::RackspaceV2::NotFound) do
+ @service.set_metadata_item(collection, 0, "environment", "test")
+ end
+ tests('delete_server_metadata_item').raises(Fog::Compute::RackspaceV2::NotFound) do
+ @service.delete_metadata_item(collection, 0, "environment")
+ end
+ end
+ end
+ end
+end
+
+
Something went wrong with that request. Please try again.