From 70862b3de37be808cb158e103ebec043b9a0dd55 Mon Sep 17 00:00:00 2001 From: crazed Date: Sat, 26 Feb 2011 15:18:42 -0500 Subject: [PATCH] [aws|cloudformation] add basic CloudFormation support --- lib/fog/aws/cloud_formation.rb | 108 ++++++++++++++++++ lib/fog/aws/parsers/cloud_formation/basic.rb | 12 ++ .../parsers/cloud_formation/create_stack.rb | 19 +++ .../cloud_formation/describe_stack_events.rb | 27 +++++ .../describe_stack_resources.rb | 27 +++++ .../cloud_formation/describe_stacks.rb | 61 ++++++++++ .../parsers/cloud_formation/get_template.rb | 19 +++ .../cloud_formation/validate_template.rb | 19 +++ .../requests/cloud_formation/create_stack.rb | 36 ++++++ .../requests/cloud_formation/delete_stack.rb | 30 +++++ .../cloud_formation/describe_stack_events.rb | 43 +++++++ .../describe_stack_resources.rb | 42 +++++++ .../cloud_formation/describe_stacks.rb | 42 +++++++ .../requests/cloud_formation/get_template.rb | 32 ++++++ .../cloud_formation/validate_template.rb | 34 ++++++ lib/fog/bin/aws.rb | 2 + lib/fog/providers/aws.rb | 19 +-- 17 files changed, 563 insertions(+), 9 deletions(-) create mode 100644 lib/fog/aws/cloud_formation.rb create mode 100644 lib/fog/aws/parsers/cloud_formation/basic.rb create mode 100644 lib/fog/aws/parsers/cloud_formation/create_stack.rb create mode 100644 lib/fog/aws/parsers/cloud_formation/describe_stack_events.rb create mode 100644 lib/fog/aws/parsers/cloud_formation/describe_stack_resources.rb create mode 100644 lib/fog/aws/parsers/cloud_formation/describe_stacks.rb create mode 100644 lib/fog/aws/parsers/cloud_formation/get_template.rb create mode 100644 lib/fog/aws/parsers/cloud_formation/validate_template.rb create mode 100644 lib/fog/aws/requests/cloud_formation/create_stack.rb create mode 100644 lib/fog/aws/requests/cloud_formation/delete_stack.rb create mode 100644 lib/fog/aws/requests/cloud_formation/describe_stack_events.rb create mode 100644 lib/fog/aws/requests/cloud_formation/describe_stack_resources.rb create mode 100644 lib/fog/aws/requests/cloud_formation/describe_stacks.rb create mode 100644 lib/fog/aws/requests/cloud_formation/get_template.rb create mode 100644 lib/fog/aws/requests/cloud_formation/validate_template.rb diff --git a/lib/fog/aws/cloud_formation.rb b/lib/fog/aws/cloud_formation.rb new file mode 100644 index 000000000..f6c30792e --- /dev/null +++ b/lib/fog/aws/cloud_formation.rb @@ -0,0 +1,108 @@ +module Fog + module AWS + class CloudFormation < Fog::Service + + requires :aws_access_key_id, :aws_secret_access_key + recognizes :host, :path, :port, :scheme, :persistent + + request_path 'fog/aws/requests/cloud_formation' + request :describe_stacks + request :describe_stack_events + request :describe_stack_resources + request :create_stack + request :delete_stack + request :get_template + request :validate_template + + class Mock + + def initialize(options={}) + end + + end + + class Real + + # Initialize connection to CloudFormation + # + # ==== Notes + # options parameter must include values for :aws_access_key_id and + # :aws_secret_access_key in order to create a connection + # + # ==== Examples + # cf = CloudFormation.new( + # :aws_access_key_id => your_aws_access_key_id, + # :aws_secret_access_key => your_aws_secret_access_key + # ) + # + # ==== Parameters + # * options<~Hash> - config arguments for connection. Defaults to {}. + # + # ==== Returns + # * CloudFormation object with connection to AWS. + def initialize(options={}) + require 'fog/core/parser' + require 'json' + + @aws_access_key_id = options[:aws_access_key_id] + @aws_secret_access_key = options[:aws_secret_access_key] + @hmac = Fog::HMAC.new('sha256', @aws_secret_access_key) + @host = options[:host] || 'cloudformation.us-east-1.amazonaws.com' + @path = options[:path] || '/' + @port = options[:port] || 443 + @scheme = options[:scheme] || 'https' + @connection = Fog::Connection.new("#{@scheme}://#{@host}:#{@port}#{@path}", options[:persistent]) + end + + def reload + @connection.reset + end + + private + + def request(params) + idempotent = params.delete(:idempotent) + parser = params.delete(:parser) + + body = AWS.signed_params( + params, + { + :aws_access_key_id => @aws_access_key_id, + :hmac => @hmac, + :host => @host, + :path => @path, + :port => @port, + :version => '2010-05-15' + } + ) + + begin + response = @connection.request({ + :body => body, + :expects => 200, + :idempotent => idempotent, + :headers => { 'Content-Type' => 'application/x-www-form-urlencoded' }, + :host => @host, + :method => 'POST', + :parser => parser + }) + rescue Excon::Errors::HTTPStatusError => error + if match = error.message.match(/(.*)<\/Code>(.*)<\/Message>/) + raise case match[1].split('.').last + when 'NotFound' + Fog::AWS::Compute::NotFound.slurp(error, match[2]) + else + Fog::AWS::Compute::Error.slurp(error, "#{match[1]} => #{match[2]}") + end + else + raise error + end + end + + response + end + + end + end + end +end diff --git a/lib/fog/aws/parsers/cloud_formation/basic.rb b/lib/fog/aws/parsers/cloud_formation/basic.rb new file mode 100644 index 000000000..b73b5d4a2 --- /dev/null +++ b/lib/fog/aws/parsers/cloud_formation/basic.rb @@ -0,0 +1,12 @@ +module Fog + module Parsers + module AWS + module CloudFormation + + class Basic < Fog::Parsers::Base + + end + end + end + end +end diff --git a/lib/fog/aws/parsers/cloud_formation/create_stack.rb b/lib/fog/aws/parsers/cloud_formation/create_stack.rb new file mode 100644 index 000000000..b686af448 --- /dev/null +++ b/lib/fog/aws/parsers/cloud_formation/create_stack.rb @@ -0,0 +1,19 @@ +module Fog + module Parsers + module AWS + module CloudFormation + + class CreateStack < Fog::Parsers::Base + + def end_element(name) + case name + when 'StackId' + @response[name] = @value + end + end + + end + end + end + end +end diff --git a/lib/fog/aws/parsers/cloud_formation/describe_stack_events.rb b/lib/fog/aws/parsers/cloud_formation/describe_stack_events.rb new file mode 100644 index 000000000..bb1b7d2fa --- /dev/null +++ b/lib/fog/aws/parsers/cloud_formation/describe_stack_events.rb @@ -0,0 +1,27 @@ +module Fog + module Parsers + module AWS + module CloudFormation + + class DescribeStackEvents < Fog::Parsers::Base + + def reset + @event = {} + @response = { 'Events' => [] } + end + + def end_element(name) + case name + when 'EventId', 'StackId', 'StackName', 'LogicalResourceId', 'PhysicalResourceId', 'ResourceType', 'Timestamp', 'ResourceStatus', 'ResourceStatusReason' + @event[name] = @value + when 'member' + @response['Events'] << @event + @event = {} + end + end + + end + end + end + end +end diff --git a/lib/fog/aws/parsers/cloud_formation/describe_stack_resources.rb b/lib/fog/aws/parsers/cloud_formation/describe_stack_resources.rb new file mode 100644 index 000000000..a5744ae60 --- /dev/null +++ b/lib/fog/aws/parsers/cloud_formation/describe_stack_resources.rb @@ -0,0 +1,27 @@ +module Fog + module Parsers + module AWS + module CloudFormation + + class DescribeStackResources < Fog::Parsers::Base + + def reset + @resource = {} + @response = { 'Resources' => [] } + end + + def end_element(name) + case name + when 'StackId', 'StackName', 'LogicalResourceId', 'PhysicalResourceId', 'ResourceType', 'TimeStamp', 'ResourceStatus' + @resource[name] = @value + when 'member' + @response['Resources'] << @resource + @resource = {} + end + end + + end + end + end + end +end diff --git a/lib/fog/aws/parsers/cloud_formation/describe_stacks.rb b/lib/fog/aws/parsers/cloud_formation/describe_stacks.rb new file mode 100644 index 000000000..5500682f2 --- /dev/null +++ b/lib/fog/aws/parsers/cloud_formation/describe_stacks.rb @@ -0,0 +1,61 @@ +module Fog + module Parsers + module AWS + module CloudFormation + + class DescribeStacks < Fog::Parsers::Base + + def reset + @stack = { 'Outputs' => [], 'Parameters' => [] } + @output = {} + @parameter = {} + @response = { 'Stacks' => [] } + end + + def start_element(name, attrs = []) + super + case name + when 'Outputs' + @in_outputs = true + when 'Parameters' + @in_parameters = true + end + end + + def end_element(name) + if @in_outputs + case name + when 'OutputKey', 'OutputValue' + @output[name] = @value + when 'member' + @stack['Outputs'] << @output + @output = {} + when 'Outputs' + @in_outputs = false + end + elsif @in_parameters + case name + when 'ParameterKey', 'ParameterValue' + @parameter[name] = @value + when 'member' + @stack['Parameters'] << @parameter + @parameter = {} + when 'Parameters' + @in_parameters = false + end + else + case name + when 'StackName', 'StackId', 'CreationTime', 'StackStatus', 'DisableRollback' + @stack[name] = @value + when 'member' + @response['Stacks'] << @stack + @stack = { 'Outputs' => [], 'Parameters' => [] } + end + end + end + + end + end + end + end +end diff --git a/lib/fog/aws/parsers/cloud_formation/get_template.rb b/lib/fog/aws/parsers/cloud_formation/get_template.rb new file mode 100644 index 000000000..0208edc4f --- /dev/null +++ b/lib/fog/aws/parsers/cloud_formation/get_template.rb @@ -0,0 +1,19 @@ +module Fog + module Parsers + module AWS + module CloudFormation + + class GetTemplate < Fog::Parsers::Base + + def end_element(name) + case name + when 'TemplateBody' + @response[name] = @value + end + end + + end + end + end + end +end diff --git a/lib/fog/aws/parsers/cloud_formation/validate_template.rb b/lib/fog/aws/parsers/cloud_formation/validate_template.rb new file mode 100644 index 000000000..9dcac35ee --- /dev/null +++ b/lib/fog/aws/parsers/cloud_formation/validate_template.rb @@ -0,0 +1,19 @@ +module Fog + module Parsers + module AWS + module CloudFormation + + class ValidateTemplate < Fog::Parsers::Base + + def end_element(name) + case name + when 'TemplateParameter', 'Description' + @response[name] = @value + end + end + + end + end + end + end +end diff --git a/lib/fog/aws/requests/cloud_formation/create_stack.rb b/lib/fog/aws/requests/cloud_formation/create_stack.rb new file mode 100644 index 000000000..d28f70ccc --- /dev/null +++ b/lib/fog/aws/requests/cloud_formation/create_stack.rb @@ -0,0 +1,36 @@ +module Fog + module AWS + class CloudFormation + class Real + + require 'fog/aws/parsers/cloud_formation/create_stack' + + # Create a stack + # + # ==== Parameters + # * stack_name<~String>: name of the stack to create + # * options<~Hash> + # * TemplateBody<~String>: structure containing the template body + # or + # * TemplateURL<~String>: URL of file containing the template body + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'StackId'<~String> - Id of the new stack + # + # ==== See Also + # http://docs.amazonwebservices.com/AWSCloudFormation/latest/APIReference/API_CreateStack.html + # + def create_stack(stack_name, options = {}) + request({ + 'Action' => 'CreateStack', + 'StackName' => stack_name, + :parser => Fog::Parsers::AWS::CloudFormation::CreateStack.new + }.merge!(options)) + end + + end + end + end +end diff --git a/lib/fog/aws/requests/cloud_formation/delete_stack.rb b/lib/fog/aws/requests/cloud_formation/delete_stack.rb new file mode 100644 index 000000000..85251a682 --- /dev/null +++ b/lib/fog/aws/requests/cloud_formation/delete_stack.rb @@ -0,0 +1,30 @@ +module Fog + module AWS + class CloudFormation + class Real + + require 'fog/aws/parsers/cloud_formation/basic' + + # Delete a stack + # + # ==== Parameters + # * stack_name<~String>: name of the stack to create + # + # ==== Returns + # * response<~Excon::Response>: + # + # ==== See Also + # http://docs.amazonwebservices.com/AWSCloudFormation/latest/APIReference/API_DeleteStack.html + # + def delete_stack(stack_name) + request( + 'Action' => 'DeleteStack', + 'StackName' => stack_name, + :parser => Fog::Parsers::AWS::CloudFormation::Basic.new + ) + end + + end + end + end +end diff --git a/lib/fog/aws/requests/cloud_formation/describe_stack_events.rb b/lib/fog/aws/requests/cloud_formation/describe_stack_events.rb new file mode 100644 index 000000000..e2833c650 --- /dev/null +++ b/lib/fog/aws/requests/cloud_formation/describe_stack_events.rb @@ -0,0 +1,43 @@ +module Fog + module AWS + class CloudFormation + class Real + + require 'fog/aws/parsers/cloud_formation/describe_stack_events' + + # Describe stack events + # + # ==== Parameters + # * options<~Hash>: + # * StackName<~String>: only return events related to this stack name + # * NextToken<~String>: identifies the start of the next list of events, if there is one + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'Events'<~Array> - Matching resources + # * event<~Hash>: + # * 'EventId'<~String> - + # * 'StackId'<~String> - + # * 'StackName'<~String> - + # * 'LogicalResourceId'<~String> - + # * 'PhysicalResourceId'<~String> - + # * 'ResourceType'<~String> - + # * 'Timestamp'<~String> - + # * 'ResourceStatus'<~String> - + # * 'ResourceStatusReason'<~String> - + # + # ==== See Also + # http://docs.amazonwebservices.com/AWSCloudFormation/latest/APIReference/API_DescribeStackEvents.html + # + def describe_stack_events(options = {}) + request({ + 'Action' => 'DescribeStackEvents', + :parser => Fog::Parsers::AWS::CloudFormation::DescribeStackEvents.new + }.merge!(options)) + end + + end + end + end +end diff --git a/lib/fog/aws/requests/cloud_formation/describe_stack_resources.rb b/lib/fog/aws/requests/cloud_formation/describe_stack_resources.rb new file mode 100644 index 000000000..4fbda535e --- /dev/null +++ b/lib/fog/aws/requests/cloud_formation/describe_stack_resources.rb @@ -0,0 +1,42 @@ +module Fog + module AWS + class CloudFormation + class Real + + require 'fog/aws/parsers/cloud_formation/describe_stack_resources' + + # Describe stack resources + # + # ==== Parameters + # * options<~Hash>: + # * 'StackName'<~String>: only return events related to this stack name + # * 'LogicalResourceId'<~String>: logical name of the resource as specified in the template + # * 'PhysicalResourceId'<~String>: name or unique identifier that corresponds to a physical instance ID + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'Resources'<~Array> - Matching resources + # * resource<~Hash>: + # * 'StackId'<~String> - + # * 'StackName'<~String> - + # * 'LogicalResourceId'<~String> - + # * 'PhysicalResourceId'<~String> - + # * 'ResourceType'<~String> - + # * 'Timestamp'<~String> - + # * 'ResourceStatus'<~String> - + # + # ==== See Also + # http://docs.amazonwebservices.com/AWSCloudFormation/latest/APIReference/API_DescribeStackResources.html + # + def describe_stack_resources(options = {}) + request({ + 'Action' => 'DescribeStackResources', + :parser => Fog::Parsers::AWS::CloudFormation::DescribeStackResources.new + }.merge!(options)) + end + + end + end + end +end diff --git a/lib/fog/aws/requests/cloud_formation/describe_stacks.rb b/lib/fog/aws/requests/cloud_formation/describe_stacks.rb new file mode 100644 index 000000000..20176bdfb --- /dev/null +++ b/lib/fog/aws/requests/cloud_formation/describe_stacks.rb @@ -0,0 +1,42 @@ +module Fog + module AWS + class CloudFormation + class Real + + require 'fog/aws/parsers/cloud_formation/describe_stacks' + + # Describe stacks + # + # ==== Parameters + # * options<~Hash>: + # * 'StackName'<~String>: name of the stack to describe + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'Stacks'<~Array> - Matching stacks + # * stack<~Hash>: + # * 'StackName'<~String> - + # * 'StackId'<~String> - + # * 'CreationTime'<~String> - + # * 'StackStatus'<~String> - + # * 'DisableRollback'<~String> - + # * 'Outputs'<~Array> - + # * output<~Hash>: + # * 'OutputKey'<~String> - + # * 'OutputValue'<~String> - + # + # ==== See Also + # http://docs.amazonwebservices.com/AWSCloudFormation/latest/APIReference/API_DescribeStacks.html + # + def describe_stacks(options = {}) + request({ + 'Action' => 'DescribeStacks', + :parser => Fog::Parsers::AWS::CloudFormation::DescribeStacks.new + }.merge!(options)) + end + + end + end + end +end diff --git a/lib/fog/aws/requests/cloud_formation/get_template.rb b/lib/fog/aws/requests/cloud_formation/get_template.rb new file mode 100644 index 000000000..18e235248 --- /dev/null +++ b/lib/fog/aws/requests/cloud_formation/get_template.rb @@ -0,0 +1,32 @@ +module Fog + module AWS + class CloudFormation + class Real + + require 'fog/aws/parsers/cloud_formation/get_template' + + # Describe stacks + # + # ==== Parameters + # * stack_name<~String> - stack name to get template from + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'TemplateBody'<~String> - structure containing the template body (json) + # + # ==== See Also + # http://docs.amazonwebservices.com/AWSCloudFormation/latest/APIReference/API_GetTemplate.html + # + def get_template(stack_name) + request( + 'Action' => 'GetTemplate', + 'StackName' => stack_name, + :parser => Fog::Parsers::AWS::CloudFormation::GetTemplate.new + ) + end + + end + end + end +end diff --git a/lib/fog/aws/requests/cloud_formation/validate_template.rb b/lib/fog/aws/requests/cloud_formation/validate_template.rb new file mode 100644 index 000000000..8a110b7b3 --- /dev/null +++ b/lib/fog/aws/requests/cloud_formation/validate_template.rb @@ -0,0 +1,34 @@ +module Fog + module AWS + class CloudFormation + class Real + + require 'fog/aws/parsers/cloud_formation/get_template' + + # Describe stacks + # + # ==== Parameters + # * options<~Hash>: + # * 'TemplateBody'<~String> - template structure + # * 'TemplateURL'<~String> - template url + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'Description'<~String> - description found within the template + # * 'Parameters'<~String> - list of template parameter structures + # + # ==== See Also + # http://docs.amazonwebservices.com/AWSCloudFormation/latest/APIReference/API_ValidateTemplate.html + # + def validate_template(options = {}) + request({ + 'Action' => 'GetTemplate', + :parser => Fog::Parsers::AWS::CloudFormation::GetTemplate.new + }.merge!(options)) + end + + end + end + end +end diff --git a/lib/fog/bin/aws.rb b/lib/fog/bin/aws.rb index cd837db79..7c875efb5 100644 --- a/lib/fog/bin/aws.rb +++ b/lib/fog/bin/aws.rb @@ -5,6 +5,8 @@ class AWS < Fog::Bin case key when :cdn Fog::AWS::CDN + when :cloud_formation + Fog::AWS::CloudFormation when :compute Fog::AWS::Compute when :dns diff --git a/lib/fog/providers/aws.rb b/lib/fog/providers/aws.rb index 848ce6f7c..9e8733e09 100644 --- a/lib/fog/providers/aws.rb +++ b/lib/fog/providers/aws.rb @@ -6,15 +6,16 @@ module Fog extend Fog::Provider - service(:cdn, 'cdn/aws') - service(:compute, 'compute/aws') - service(:dns, 'dns/aws') - service(:elb, 'aws/elb') - service(:iam, 'aws/iam') - service(:ses, 'aws/ses') - service(:simpledb, 'aws/simpledb') - service(:storage, 'storage/aws') - service(:rds, 'aws/rds') + service(:cdn, 'cdn/aws') + service(:compute, 'compute/aws') + service(:cloud_formation, 'aws/cloud_formation') + service(:dns, 'dns/aws') + service(:elb, 'aws/elb') + service(:iam, 'aws/iam') + service(:rds, 'aws/rds') + service(:ses, 'aws/ses') + service(:simpledb, 'aws/simpledb') + service(:storage, 'storage/aws') def self.indexed_param(key, values) params = {}