1
0
Fork 0
mirror of https://github.com/fog/fog.git synced 2022-11-09 13:51:43 -05:00

Merge branch 'auto_scale'

This commit is contained in:
jamiehannaford 2013-08-20 15:29:11 +02:00
commit 8e4d18a8bd
20 changed files with 777 additions and 0 deletions

View file

@ -3,6 +3,8 @@ class Rackspace < Fog::Bin
def class_for(key)
case key
when :auto_scale
Fog::Rackspace::AutoScale
when :block_storage
Fog::Rackspace::BlockStorage
when :cdn
@ -31,6 +33,8 @@ class Rackspace < Fog::Bin
def [](service)
@@connections ||= Hash.new do |hash, key|
hash[key] = case key
when :auto_scale
Fog::Rackspace::AutoScale.new
when :cdn
Fog::Logger.warning("Rackspace[:cdn] is not recommended, use CDN[:rackspace] for portability")
Fog::CDN.new(:provider => 'Rackspace')

View file

@ -81,6 +81,7 @@ module Fog
end
end
service(:auto_scale, 'rackspace/auto_scale', 'AutoScale')
service(:block_storage, 'rackspace/block_storage', 'BlockStorage')
service(:cdn, 'rackspace/cdn', 'CDN')
service(:compute, 'rackspace/compute', 'Compute')

View file

@ -0,0 +1,143 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'rackspace'))
module Fog
module Rackspace
class AutoScale < Fog::Service
include Fog::Rackspace::Errors
class ServiceError < Fog::Rackspace::Errors::ServiceError; end
class InternalServerError < Fog::Rackspace::Errors::InternalServerError; end
class MissingArgumentException < InvalidStateException
def initialize(resource, argument)
@resource = resource
@argument = argument
end
def to_s
"This #{resource} resource requires the #{argument} argument"
end
end
class InvalidImageStateException < InvalidStateException
def to_s
"Image should have transitioned to '#{desired_state}' not '#{current_state}'"
end
end
class BadRequest < Fog::Rackspace::Errors::BadRequest
attr_reader :validation_errors
def self.slurp(error)
if error && error.response
status_code = error.response.status
if error.response.body
body = Fog::JSON.decode(error.response.body)
message = "#{body['type']} - #{body['message']}"
details = error.response.body['details']
end
end
new_error = new(message)
new_error.set_backtrace(error.backtrace)
new_error.instance_variable_set(:@validation_errors, details)
new_error.instance_variable_set(:@status_code, status_code)
new_error
end
end
requires :rackspace_username, :rackspace_api_key
recognizes :rackspace_auth_url
recognizes :rackspace_region
recognizes :rackspace_auto_scale_url
model_path 'fog/rackspace/models/auto_scale'
model :group
collection :groups
model :policy
collection :policies
model :group_config
model :launch_config
model :webhook
request_path 'fog/rackspace/requests/auto_scale'
request :list_groups
request :create_group
request :get_group
request :delete_group
request :get_group_state
request :pause_group_state
request :resume_group_state
request :get_group_config
request :update_group_config
request :get_launch_config
request :replace_launch_config
request :get_policies
request :create_policy
request :get_policy
request :update_policy
request :delete_policy
request :execute_policy
request :execute_anonymous_webhook
request :get_webhook
request :update_webhook
request :delete_webhook
class Mock < Fog::Rackspace::Service
def initialize(options)
@rackspace_api_key = options[:rackspace_api_key]
end
def request(params)
Fog::Mock.not_implemented
end
end
class Real < Fog::Rackspace::Service
def initialize(options = {})
@options = options
@options[:connection_options] ||= {}
@options[:persistent] ||= false
authenticate
@connection = Fog::Connection.new(endpoint_uri.to_s, @options[:persistent], @options[:connection_options])
end
def request(params, parse_json = true, &block)
super(params, parse_json, &block)
rescue Excon::Errors::NotFound => error
raise NotFound.slurp(error, region)
rescue Excon::Errors::BadRequest => error
raise BadRequest.slurp error
rescue Excon::Errors::InternalServerError => error
raise InternalServerError.slurp error
rescue Excon::Errors::HTTPStatusError => error
raise ServiceError.slurp error
end
def endpoint_uri(service_endpoint_url=nil)
@uri = super(@options[:rackspace_auto_scale_url], :rackspace_auto_scale_url)
end
def authenticate(options={})
super(select_options([:rackspace_username, :rackspace_api_key, :rackspace_auth_url, :connection_options]))
end
def service_name
:autoscale
end
def region
@options[:rackspace_region]
end
end
end
end
end

View file

@ -0,0 +1,17 @@
require 'fog/core/model'
module Fog
module Rackspace
class AutoScale
class Config < Fog::Model
def pause
end
def resume
end
end
end
end
end

View file

@ -0,0 +1,73 @@
require 'fog/core/model'
module Fog
module Rackspace
class AutoScale
class Group < Fog::Model
identity :id
attribute :links
attribute :group_config
attribute :launch_config
attribute :policies
def initialize(attributes={})
@service = attributes[:service]
super
end
def create(options)
requires :launch_config, :group_config, :policies
data = service.create_group(launch_config, group_config, policies)
merge_attributes(data.body['group'])
true
end
def destroy
requires :identity
service.delete_server(identity)
true
end
def group_config
@group_config ||= begin
Fog::Rackspace::AutoScale::GroupConfig.new({
:service => service,
:group => self
})
end
end
def launch_config
@launch_config ||= begin
Fog::Rackspace::AutoScale::LaunchConfig.new({
:service => service,
:group => self
})
end
end
def policies
@policies ||= begin
Fog::Rackspace::Autoscale::Policies.new({
:service => service,
:group => self
})
end
end
def state
requires :identity
data = service.get_group_state(identity)
merge_attributes(data.body['group'])
end
end
end
end
end

View file

@ -0,0 +1,34 @@
require 'fog/core/model'
module Fog
module Rackspace
class AutoScale
class GroupConfig < Fog::AutoScale::Rackspace::Config
identity :id
attribute :name
attribute :cooldown
attribute :min_entities
attribute :max_entities
attribute :metadata
def update
requires :identity
options = {
'name' => name,
'cooldown' => cooldown,
'min_entities' => min_entities,
'max_entities' => max_entities,
'metadata' => metadata
}
data = service.update_group_config(identity, options)
merge_attributes(data.body)
true
end
end
end
end
end

View file

@ -0,0 +1,25 @@
require 'fog/core/collection'
require 'fog/rackspace/models/auto_scale/group'
module Fog
module Rackspace
class AutoScale
class Groups < Fog::Collection
model Fog::Rackspace::AutoScale::Group
def all
data = service.list_groups.body['groups']
load(data)
end
def get(group_id)
data = service.get_group(group_id).body['group']
new(data)
rescue Fog::Rackspace::AutoScale::NotFound
nil
end
end
end
end
end

View file

@ -0,0 +1,28 @@
require 'fog/core/model'
module Fog
module Rackspace
class AutoScale
class LaunchConfig < Fog::AutoScale::Rackspace::Config
identity :id
attribute :type
attribute :args
def update
requires :identity
options = {
'type' => type,
'args' => args
}
data = service.update_launch_config(identity, options)
merge_attributes(data.body['launchConfiguration'])
true
end
end
end
end
end

View file

@ -0,0 +1,25 @@
require 'fog/core/collection'
require 'fog/rackspace/models/auto_scale/policy'
module Fog
module Rackspace
class AutoScale
class Policies < Fog::Collection
model Fog::Rackspace::AutoScale::Policy
def all
data = service.list_policies.body['policies']
load(data)
end
def get(policy_id)
data = service.get_policy(policy_id).body['policy']
new(data)
rescue Fog::Rackspace::AutoScale::NotFound
nil
end
end
end
end
end

View file

@ -0,0 +1,98 @@
require 'fog/core/model'
module Fog
module Rackspace
class AutoScale
class Policy < Fog::Model
identity :id
attribute :group_id
attribute :links
attribute :name
# integer
attribute :change
attribute :changePercent
# integer
attribute :cooldown
# webhook|schedule|cloud_monitoring
attribute :type
# hash depending on the type chosen
# - "cron": "23 * * * *"
# - "at": "2013-06-05T03:12Z"
# - "check": {
# "label": "Website check 1",
# "type": "remote.http",
# "details": {
# "url": "http://www.example.com",
# "method": "GET"
# },
# "monitoring_zones_poll": [
# "mzA"
# ],
# "timeout": 30,
# "period": 100,
# "target_alias": "default"
# },
# "alarm_criteria": {
# "criteria": "if (metric[\"duration\"] >= 2) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);"
# }
attribute :args
attribute :desiredCapacity
def check_options(options)
if options[:type] == 'schedule'
args = options['args']
raise MissingArgumentException(self.name, "cron OR at") if args['cron'].nil? && args['at'].nil?
end
true
end
def create(options)
requires :name, :type, :cooldown
check_options
data = service.create_policy(group_id, options)
merge_attributes(data.body['group'])
true
end
def update
requires :identity
options = {
'name' => name,
'change' => change,
'changePercent' => changePercent,
'cooldown' => cooldown,
'type' => type,
'args' => args,
'desiredCapacity' => desiredCapacity
}
data = service.update_policy(identity, options)
merge_attributes(data.body)
true
end
def destroy
requires :identity
service.delete_policy(identity)
end
def execute
requires :identity
service.execute_policy(identity)
end
end
end
end
end

View file

@ -0,0 +1,34 @@
require 'fog/core/model'
module Fog
module Rackspace
class AutoScale
class Webhook < Fog::Model
identity :id
attribute :name
attribute :metadata
attribute :links
def update
required :identity
options = {
'name' => name,
'metadata' => metadata
}
data = service.update_webhook(identity, options)
merge_attribute(data.body)
end
def destroy
required :identity
service.delete_webhook(identity)
end
end
end
end
end

View file

@ -0,0 +1,40 @@
module Fog
module Rackspace
class AutoScale
class Real
def create_group(launch_config, group_config, policies)
body['launchConfiguration'] = {
'args' => launch_config.args,
'type' => launch_config.type
}
body['groupConfiguration'] = {
'name' => group_config.name,
'cooldown' => group_config.cooldown,
'maxEntities' => group_config.max_entities,
'minEntities' => group_config.min_entities,
'metadata' => group_config.metadata
}
body['scalingPolicies'] = policies.collect { |p| p.to_a }
request(
:expects => [201],
:method => 'POST',
:path => 'groups',
:body => Fog::JSON.encode(body)
)
end
end
class Mock
def create_group
Fog::Mock.not_implemented
end
end
end
end
end

View file

@ -0,0 +1,22 @@
module Fog
module Rackspace
class AutoScale
class Real
def delete_group(group_id)
request(
:expects => [204],
:method => 'DELETE',
:path => "groups/#{group_id}"
)
end
end
class Mock
def delete_group(group_id)
Fog::Mock.not_implemented
end
end
end
end
end

View file

@ -0,0 +1,24 @@
module Fog
module Rackspace
class AutoScale
class Real
def get_config(group_id)
request(
:expects => [200],
:method => 'GET',
:path => "groups/#{group_id}/config",
)
end
end
class Mock
def get_config(group_id)
Fog::Mock.not_implemented
end
end
end
end
end

View file

@ -0,0 +1,24 @@
module Fog
module Rackspace
class AutoScale
class Real
def get_group(group_id)
request(
:expects => [200],
:method => 'GET',
:path => "groups/#{group_id}",
)
end
end
class Mock
def get_group(group_id)
Fog::Mock.not_implemented
end
end
end
end
end

View file

@ -0,0 +1,24 @@
module Fog
module Rackspace
class AutoScale
class Real
def get_group_state(group_id)
request(
:expects => [200],
:method => 'GET',
:path => "groups/#{group_id}/state",
)
end
end
class Mock
def get_group_state(group_id)
Fog::Mock.not_implemented
end
end
end
end
end

View file

@ -0,0 +1,35 @@
module Fog
module Rackspace
class AutoScale
class Real
# Retrieves a list of images
# @return [Excon::Response] response:
# * body [Hash]:
# * images [Array]:
# * [Hash]:
# * id [String] - flavor id
# * links [Array] - image links
# * name [String] - image name
# @raise [Fog::Compute::RackspaceV2::NotFound] - HTTP 404
# @raise [Fog::Compute::RackspaceV2::BadRequest] - HTTP 400
# @raise [Fog::Compute::RackspaceV2::InternalServerError] - HTTP 500
# @raise [Fog::Compute::RackspaceV2::ServiceError]
# @see http://docs.rackspace.com/servers/api/v2/cs-devguide/content/List_Flavors-d1e4188.html
def list_groups
request(
:expects => [200],
:method => 'GET',
:path => 'groups'
)
end
end
class Mock
def list_groups
Fog::Mock.not_implemented
end
end
end
end
end

View file

@ -0,0 +1,37 @@
module Fog
module Rackspace
class AutoScale
class Real
def update_config(group_id)
h = {
"name" => "workers",
"cooldown" => 60,
"minEntities" => 0,
"maxEntities" => 0,
"metadata" => {
"firstkey" => "this is a string",
"secondkey" => "1"
}
}
request(
:expects => [204],
:method => 'PUT',
:path => "groups/#{group_id}/config",
:body => Fog::JSON.encode(h)
)
end
end
class Mock
def update_config(group_id)
Fog::Mock.not_implemented
end
end
end
end
end

View file

@ -116,6 +116,11 @@ module Fog
@auth_token || @identity_service.auth_token
end
def select_options(keys)
return nil unless @options && keys
@options.select {|k,v| keys.include?(k)}
end
end
end
end

View file

@ -0,0 +1,84 @@
Shindo.tests('Fog::Rackspace::AutoScale', ['rackspace']) do
def assert_method(url, method)
@service.instance_variable_set "@rackspace_auth_url", url
returns(method) { @service.send :authentication_method }
end
tests('#authentication_method') do
@service = Fog::Rackspace::AutoScale.new :rackspace_region => :dfw
assert_method nil, :authenticate_v2
assert_method 'auth.api.rackspacecloud.com', :authenticate_v1 # chef's default auth endpoint
assert_method 'https://identity.api.rackspacecloud.com', :authenticate_v1
assert_method 'https://identity.api.rackspacecloud.com/v1', :authenticate_v1
assert_method 'https://identity.api.rackspacecloud.com/v1.1', :authenticate_v1
assert_method 'https://identity.api.rackspacecloud.com/v2.0', :authenticate_v2
assert_method 'https://lon.identity.api.rackspacecloud.com', :authenticate_v1
assert_method 'https://lon.identity.api.rackspacecloud.com/v1', :authenticate_v1
assert_method 'https://lon.identity.api.rackspacecloud.com/v1.1', :authenticate_v1
assert_method 'https://lon.identity.api.rackspacecloud.com/v2.0', :authenticate_v2
end
tests('current authentation') do
pending if Fog.mocking?
tests('variables populated').succeeds do
@service = Fog::Rackspace::AutoScale.new :rackspace_auth_url => 'https://identity.api.rackspacecloud.com/v2.0', :connection_options => {:ssl_verify_peer => true}, :rackspace_region => :dfw
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
returns(false, "path populated") { @service.instance_variable_get("@uri").host.nil? }
identity_service = @service.instance_variable_get("@identity_service")
returns(false, "identity service was used") { identity_service.nil? }
returns(true, "connection_options are passed") { identity_service.instance_variable_get("@connection_options").has_key?(:ssl_verify_peer) }
@service.list_groups
end
tests('dfw region').succeeds do
@service = Fog::Rackspace::AutoScale.new :rackspace_auth_url => 'https://identity.api.rackspacecloud.com/v2.0', :rackspace_region => :dfw
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
returns(true) { (@service.instance_variable_get("@uri").host =~ /dfw/) != nil }
@service.list_groups
end
tests('ord region').succeeds do
@service = Fog::Rackspace::AutoScale.new :rackspace_auth_url => 'https://identity.api.rackspacecloud.com/v2.0', :rackspace_region => :ord
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
returns(true) { (@service.instance_variable_get("@uri").host =~ /ord/) != nil }
@service.list_groups
end
tests('custom endpoint') do
@service = Fog::Rackspace::AutoScale.new :rackspace_auth_url => 'https://identity.api.rackspacecloud.com/v2.0',
:rackspace_auto_scale_url => 'https://my-custom-endpoint.com'
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
returns(true, "uses custom endpoint") { (@service.instance_variable_get("@uri").host =~ /my-custom-endpoint\.com/) != nil }
end
end
tests('default auth') do
pending if Fog.mocking?
tests('specify region').succeeds do
@service = Fog::Rackspace::AutoScale.new :rackspace_region => :ord
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
returns(true) { (@service.instance_variable_get("@uri").host =~ /ord/ ) != nil }
@service.list_groups
end
tests('custom endpoint') do
@service = Fog::Rackspace::AutoScale.new :rackspace_auto_scale_url => 'https://my-custom-endpoint.com'
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
returns(true, "uses custom endpoint") { (@service.instance_variable_get("@uri").host =~ /my-custom-endpoint\.com/) != nil }
end
end
tests('reauthentication') do
pending if Fog.mocking?
@service = Fog::Rackspace::AutoScale.new :rackspace_region => :ord
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
@service.instance_variable_set("@auth_token", "bad_token")
returns(true) { [200, 203].include? @service.list_groups.status }
end
end