lib/cloudster/cloud.rb
module Cloudster
#Cloud objects have many resources. This class can generate the cloudformation template and provision the stack.
class Cloud
include Output
# Initialize a Cloud instance
#
# ==== Notes
# options parameter must include values for :access_key_id and
# :secret_access_key in order to create a connection
#
# ==== Parameters
# * options<~Hash>
# * :access_key_id : A string containing the AWS access key ID (Required)
# * :secret_access_key : A string containing the AWS secret access key (Required)
# * :region : A string containing the region where the stack should be created/updated (Optional). Can be one of
# us-east-1(default), us-west-1, us-west-2, eu-west-1, ap-southeast-1, ap-northeast-1, ap-southeast-2, sa-east-1
#
# ==== Examples
# cloud = Cloudster::Cloud.new(
# :access_key_id => 'aws_access_key_id',
# :secret_access_key => 'aws_secret_access_key',
# :region => 'us-east-1'
# )
def initialize(options = {})
require_options(options, [:access_key_id, :secret_access_key])
@region = options[:region]
@access_key_id = options[:access_key_id]
@secret_access_key = options[:secret_access_key]
@cloud_formation = Fog::AWS::CloudFormation.new(
:aws_access_key_id => @access_key_id,
:aws_secret_access_key => @secret_access_key,
:region => @region
)
end
# Generates CloudFormation Template for the stack
#
# ==== Examples
# cloud = Cloudster::Cloud.new(
# :access_key_id => 'aws_access_key_id',
# :secret_access_key => 'aws_secret_access_key',
# :region => 'us-east-1'
# )
#
# cloud.template(:resources => [<AWS RESOURCES ARRAY>], :description => 'This is the description for the stack template')
#
# ==== Notes
# options parameter must include values for :resources
#
# ==== Parameters
# * options<~Hash> -
# * :resources : An array of Cloudster resource instances. Defaults to {}.
# * :description : A string which will be used as the Description of the CloudFormation template.
#
# ==== Returns
# * JSON cloud formation template
def template(options = {})
require_options(options, [:resources])
resources = options[:resources]
description = options[:description] || 'This stack is created by Cloudster'
resource_template = {}
output_template = {}
resources.each do |resource|
resource_template.merge!(resource.template['Resources'])
output_template.merge!(resource.template['Outputs']) unless resource.template['Outputs'].nil?
end
cloud_template = {'AWSTemplateFormatVersion' => '2010-09-09',
'Description' => description
}
cloud_template['Resources'] = resource_template if !resource_template.empty?
cloud_template['Outputs'] = output_template if !output_template.empty?
return cloud_template.delete_nil.to_json
end
# Triggers the stack creation
#
# ==== Examples
# cloud = Cloudster::Cloud.new(
# :access_key_id => 'aws_access_key_id',
# :secret_access_key => 'aws_secret_access_key',
# :region => 'us-east-1'
# )
#
# cloud.provision(:resources => [<AWS RESOURCES ARRRAY>],
# :stack_name => 'Shitty Stack',
# :description => 'This is the description for the stack template')
#
# ==== Notes
# options parameter must include values for :resources and :stack_name
#
# ==== Parameters
# * options<~Hash>
# * :resources : An array of Cloudster resource instances. Defaults to {}.
# * :stack_name : A string which will be used to name the CloudFormation stack.
# * :description : A string which will be used as the Description of the CloudFormation template.
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'StackId'<~String> - Id of the new stack
def provision(options = {})
require_options(options, [:resources, :stack_name])
return @cloud_formation.create_stack(options[:stack_name], 'TemplateBody' => template(:resources => options[:resources],
:description => options[:description]))
end
# Updates already created stack
#
# ==== Examples
# cloud = Cloudster::Cloud.new(
# :access_key_id => 'aws_access_key_id',
# :secret_access_key => 'aws_secret_access_key',
# :region => 'us-east-1'
# )
#
# cloud.update(:resources => [<AWS RESOURCES ARRRAY>],
# :stack_name => 'Shitty Stack',
# :description => 'This is the description for the stack template')
#
# ==== Notes
# options parameter must include values for :resources and :stack_name
#
# ==== Parameters
# * options<~Hash>
# * :resources : An array of Cloudster resource instances. Defaults to {}.
# * :stack_name : A string which is the name of the CloudFormation stack which will be updated.
# * :description : A string which will be used as the Description of the CloudFormation template.
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'StackId'<~String> - Id of the new stack
def update(options = {})
require_options(options, [:resources, :stack_name])
return @cloud_formation.update_stack(options[:stack_name], 'TemplateBody' => template(:resources => options[:resources],
:description => options[:description]))
end
# Get events related to a stack
#
# ==== Examples
# cloud = Cloudster::Cloud.new(
# :access_key_id => 'aws_access_key_id',
# :secret_access_key => 'aws_secret_access_key',
# :region => 'us-east-1'
# )
# cloud.events(:stack_name => 'ShittyStack')
#
# ==== Parameters
# * options<~Hash>
# * :stack_name : A string which will contain the name of the stack for which the events will be fetched.
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
def events(options = {})
return @cloud_formation.describe_stack_events(options[:stack_name])
end
# Deletes a stack and the attached resources
#
# ==== Examples
# cloud = Cloudster::Cloud.new(
# :access_key_id => 'aws_access_key_id',
# :secret_access_key => 'aws_secret_access_key',
# :region => 'us-east-1'
# )
# cloud.delete(:stack_name => 'ShittyStack')
#
# ==== Parameters
# * options<~Hash>
# * :stack_name : A string which will contain the name of the stack for which will be deleted
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
def delete(options = {})
require_options(options, [:stack_name])
return @cloud_formation.delete_stack(options[:stack_name])
end
# Returns all RDS(database) endpoints in a stack
#
# ==== Examples
# cloud = Cloudster::Cloud.new(
# :access_key_id => 'aws_access_key_id',
# :secret_access_key => 'aws_secret_access_key',
# :region => 'us-east-1'
# )
# cloud.get_database_endpoints(:stack_name => 'ShittyStack')
#
# ==== Parameters
# * options<~Hash>
# * :stack_name : A string which will contain the name of the stack
#
# ==== Returns
# * Array of hashes, example: [{:address => 'simcoprod01.cu7u2t4uz396.us-east-1.rds.amazonaws.com', :port => '3306'}]
def get_database_endpoints(options = {})
rds_physical_ids = get_resource_ids(resources(options), "AWS::RDS::DBInstance").values
return [] if rds_physical_ids.empty?
rds = Fog::AWS::RDS.new(:aws_access_key_id => @access_key_id, :aws_secret_access_key => @secret_access_key, :region => @region)
endpoints = []
rds_physical_ids.each do |rds_physical_id|
endpoint = rds.describe_db_instances(rds_physical_id).body["DescribeDBInstancesResult"]["DBInstances"][0]["Endpoint"] rescue nil
endpoints << {:address => endpoint["Address"], :port => endpoint["Port"]} unless endpoint.nil?
end
return endpoints
end
# Get details of all RDS resources in a stack
#
# ==== Examples
# cloud = Cloudster::Cloud.new(
# :access_key_id => 'aws_access_key_id',
# :secret_access_key => 'aws_secret_access_key',
# :region => 'us-east-1'
# )
# cloud.get_rds_details(:stack_name => 'ShittyStack')
#
# ==== Parameters
# * options<~Hash>
# * :stack_name : A string which will contain the name of the stack
#
# ==== Returns
# * A hash of RDS details where the key is the logical name and value is the RDS detail
def get_rds_details(options = {})
stack_resources = resources(options)
rds_resource_ids = get_resource_ids(stack_resources, "AWS::RDS::DBInstance")
rds = Fog::AWS::RDS.new(:aws_access_key_id => @access_key_id, :aws_secret_access_key => @secret_access_key, :region => @region)
rds_details = {}
rds_resource_ids.each do |key, value|
rds_instance_details = rds.describe_db_instances(value)
rds_details[key] = rds_instance_details.body["DescribeDBInstancesResult"]["DBInstances"][0] rescue nil
end
return rds_details
end
# Get details of all EC2 instances in a stack
#
# ==== Examples
# cloud = Cloudster::Cloud.new(
# :access_key_id => 'aws_access_key_id',
# :secret_access_key => 'aws_secret_access_key',
# :region => 'us-east-1'
# )
# cloud.get_ec2_details(:stack_name => 'ShittyStack')
#
# ==== Parameters
# * options<~Hash>
# * :stack_name : A string which will contain the name of the stack
#
# ==== Returns
# * A hash of instance details where the key is the logical instance name and value is the instance detail
def get_ec2_details(options = {})
stack_resources = resources(options)
ec2_resource_ids = get_resource_ids(stack_resources, "AWS::EC2::Instance")
ec2 = Fog::Compute::AWS.new(:aws_access_key_id => @access_key_id, :aws_secret_access_key => @secret_access_key, :region => @region)
ec2_details = {}
ec2_resource_ids.each do |key, value|
ec2_instance_details = ec2.describe_instances('instance-id' => value)
ec2_details[key] = ec2_instance_details.body["reservationSet"][0]["instancesSet"][0] rescue nil
end
return ec2_details
end
# Get details of all Elastic Load Balancers in the stack
#
# ==== Examples
# cloud = Cloudster::Cloud.new(
# :access_key_id => 'aws_access_key_id',
# :secret_access_key => 'aws_secret_access_key',
# :region => 'us-east-1'
# )
# cloud.get_elb_details(:stack_name => 'ShittyStack')
#
# ==== Parameters
# * options<~Hash>
# * :stack_name : A string which will contain the name of the stack
#
# ==== Returns
# * A hash containing elb details where the key is the logical name and value contains the details
def get_elb_details(options = {})
stack_resources = resources(options)
elb_resource_ids = get_resource_ids(stack_resources, "AWS::ElasticLoadBalancing::LoadBalancer")
elb = Fog::AWS::ELB.new(:aws_access_key_id => @access_key_id, :aws_secret_access_key => @secret_access_key, :region => @region)
elb_details = {}
elb_resource_ids.each do |key, value|
elb_instance_details = elb.describe_load_balancers("LoadBalancerNames" => [value])
elb_details[key] = elb_instance_details.body["DescribeLoadBalancersResult"]["LoadBalancerDescriptions"][0] rescue nil
end
return elb_details
end
# Returns an array containing a list of Resources in a stack
#
# ==== Examples
# cloud = Cloudster::Cloud.new(
# :access_key_id => 'aws_access_key_id',
# :secret_access_key => 'aws_secret_access_key',
# :region => 'us-east-1'
# )
# cloud.resources(:stack_name => 'RDSStack')
#
# ==== Parameters
# * options<~Hash>
# * :stack_name : A string which will contain the name of the stack
#
# ==== Returns
# * Array of hashes, example: [{"Timestamp"=>2012-11-16 14:31:55 UTC, "ResourceStatus"=>"CREATE_COMPLETE", "StackId"=>"arn:aws::asd", "LogicalResourceId"=>"TestDB", "StackName"=>"RDSStack", "PhysicalResourceId"=>"rtad", "ResourceType"=>"AWS::RDS::DBInstance"}]
def resources(options = {})
require_options(options, [:stack_name])
return @cloud_formation.describe_stack_resources('StackName' => options[:stack_name]).body["StackResources"] rescue []
end
# Describes the attributes of a Stack
#
# ==== Examples
# cloud = Cloudster::Cloud.new(
# :access_key_id => 'aws_access_key_id',
# :secret_access_key => 'aws_secret_access_key',
# :region => 'us-east-1'
# )
# cloud.describe(:stack_name => 'RDSStack')
#
# ==== Parameters
# * options<~Hash>
# * :stack_name : A string which will contain the name of the stack
#
# ==== Returns
# * Hash containing description of the stack
def describe(options = {})
require_options(options, [:stack_name])
return @cloud_formation.describe_stacks('StackName' => options[:stack_name]).body["Stacks"][0] rescue nil
end
# Returns a hash containing the output values of each resource in the Stack
#
# ==== Examples
# cloud = Cloudster::Cloud.new(
# :access_key_id => 'aws_access_key_id',
# :secret_access_key => 'aws_secret_access_key',
# :region => 'us-east-1'
# )
# cloud.outputs(:stack_name => 'RDSStack')
#
# ==== Parameters
# * options<~Hash>
# * :stack_name : A string which will contain the name of the stack
#
# ==== Returns
# * Hash containing outputs of the stack
def outputs(options = {})
require_options(options, [:stack_name])
stack_description = describe(:stack_name => options[:stack_name])
outputs = stack_description["Outputs"] rescue []
outputs_hash = {}
outputs.each do |output|
outputs_hash[output["OutputKey"]] = parse_outputs(output["OutputValue"])
end
return outputs_hash
end
# Returns the status of the stack
#
# ==== Examples
# cloud = Cloudster::Cloud.new(
# :access_key_id => 'aws_access_key_id',
# :secret_access_key => 'aws_secret_access_key',
# :region => 'us-east-1'
# )
# cloud.status(:stack_name => 'RDSStack')
#
# ==== Parameters
# * options<~Hash>
# * :stack_name : A string which will contain the name of the stack
#
# ==== Returns
# * One of these strings :
# * CREATE_IN_PROGRESS
# * CREATE_FAILED
# * CREATE_COMPLETE
# * ROLLBACK_IN_PROGRESS
# * ROLLBACK_FAILED
# * ROLLBACK_COMPLETE
# * DELETE_IN_PROGRESS
# * DELETE_FAILED
def status(options = {})
require_options(options, [:stack_name])
description = describe(options)
return description["StackStatus"] rescue nil
end
# Returns all keypairs created in the AWS account
#
# ==== Examples
# cloud = Cloudster::Cloud.new(
# :access_key_id => 'aws_access_key_id',
# :secret_access_key => 'aws_secret_access_key',
# :region => 'us-east-1'
# )
# cloud.get_key_pairs
#
# ==== Parameters
# * none
#
# ==== Returns
# * Array of hashes, example: [{"keyName"=>"default", "keyFingerprint"=>"84:67:e2:f8:04:c1:5f:d4:ff"}]
def get_key_pairs
ec2 = Fog::Compute::AWS.new(:aws_access_key_id => @access_key_id, :aws_secret_access_key => @secret_access_key, :region => @region)
return ec2.describe_key_pairs.body["keySet"] rescue []
end
# Returns all SecurityGroups created in the AWS account
#
# ==== Examples
# cloud = Cloudster::Cloud.new(
# :access_key_id => 'aws_access_key_id',
# :secret_access_key => 'aws_secret_access_key',
# :region => 'us-east-1'
# )
# cloud.get_security_groups
#
# ==== Parameters
# * none
#
# ==== Returns
# * Array of hashes containing the security group details
def get_security_groups
ec2 = Fog::Compute::AWS.new(:aws_access_key_id => @access_key_id, :aws_secret_access_key => @secret_access_key, :region => @region)
return ec2.describe_security_groups.body["securityGroupInfo"] rescue []
end
# Returns true if S3 bucket name is available, else returns false
#
# ==== Examples
# cloud = Cloudster::Cloud.new(
# :access_key_id => 'aws_access_key_id',
# :secret_access_key => 'aws_secret_access_key',
# :region => 'us-east-1'
# )
# cloud.is_s3_bucket_name_available?('test-bucket-name')
#
# ==== Parameter
# * String containing the bucket name
#
# ==== Returns
# * true - If bucket name is available
# * false - If bucket name is not available
def is_s3_bucket_name_available?(bucket_name)
s3 = Fog::Storage::AWS.new(:aws_access_key_id => @access_key_id, :aws_secret_access_key => @secret_access_key)
begin
response = s3.get_bucket(bucket_name)
rescue Exception => e
response = e.response
end
not_found_status = 404
return response[:status] == not_found_status
end
private
#Returns a hash {<logical_resource_id> => <physical_resource_id>}
def get_resource_ids(stack_resources, resource_type)
resource_ids = {}
stack_resources.each do |resource|
if resource["ResourceType"] == resource_type
key = resource["LogicalResourceId"]
value = resource["PhysicalResourceId"]
resource_ids[key] = value
end
end
return resource_ids
end
end
end