Skip to content

Commit a5d842d

Browse files
authored
Merge pull request #23 from CU-CloudCollab/lambda-rds
Lambda rds
2 parents d1eb67b + f1e6793 commit a5d842d

15 files changed

Lines changed: 500 additions & 43 deletions

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ Note - the cucloud library assumes that environment credentials are available to
4848
Utilities that use this API:
4949

5050
* Autoscale AMI Updater: https://github.com/CU-CloudCollab/asg-ami-update
51+
* Collection of Handy Cloud Utilities: https://github.com/CU-CloudCollab/cucloud_utils
5152

5253

5354
## Development

lib/cucloud.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ module Cucloud
1313
require 'cucloud/config_service_utils'
1414
require 'cucloud/cloud_trail_utils'
1515
require 'cucloud/rds_utils'
16+
require 'cucloud/lambda_utils'
17+
require 'cucloud/cfn_utils'
1618

1719
# This is the default region API calls are made against
1820
DEFAULT_REGION = 'us-east-1'.freeze

lib/cucloud/cfn_utils.rb

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
module Cucloud
2+
# CFNUtils - Utilities for CloudFormation
3+
class CfnUtils
4+
# Define some error classes
5+
class UnknownServiceError < StandardError
6+
end
7+
8+
def initialize(cfn = Aws::CloudFormation::Client.new)
9+
@cfn = cfn
10+
end
11+
12+
# Create cloud formation stack from template
13+
# @param stack_name [string] name of the the cfn stack
14+
# @param template_json [string] file path to cfn template json
15+
# @return [String] representing the stack events from the run
16+
def create_stack(stack_name, template_json)
17+
manage_stack(stack_name, template_json)
18+
end
19+
20+
# Update cloud formation stack from template
21+
# @param stack_name [string] name of the the cfn stack
22+
# @param template_json [string] file path to cfn template json
23+
# @return [String] representing the stack events from the run
24+
def update_stack(stack_name, template_json)
25+
manage_stack(stack_name, template_json, :update_stack)
26+
end
27+
28+
private
29+
30+
# Manage cloud formation stack from template,
31+
# abstracts logic for both the create and update
32+
# @param stack_name [string] name of the the cfn stack
33+
# @param template_json [string] file path to cfn template json
34+
# @return [String] representing the stack events from the run
35+
def manage_stack(stack_name, template_json, action = :create_stack)
36+
template = IO.read(template_json)
37+
38+
response = @cfn.send(action, stack_name: stack_name,
39+
template_body: template,
40+
capabilities: %w(CAPABILITY_IAM CAPABILITY_NAMED_IAM))
41+
42+
raise UnknownServiceError unless response.successful?
43+
44+
wait_event = action == :create_stack ? :stack_create_complete : :stack_update_complete
45+
46+
@cfn.wait_until(wait_event, stack_name: stack_name)
47+
@cfn.describe_stack_events(stack_name: stack_name)
48+
end
49+
end
50+
end

lib/cucloud/elb_utils.rb

Lines changed: 0 additions & 36 deletions
This file was deleted.

lib/cucloud/iam_utils.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,5 +157,16 @@ def get_active_keys_older_than_n_days(n)
157157
get_user_access_keys(user[:base_data].user_name).select { |k| k[:days_old] > n && k[:active] }
158158
end.flatten
159159
end
160+
161+
# Gets the ARN for a given certificate
162+
# @param [String] cert_name The name of the certificate
163+
# @param [String] The ARN for the certificate
164+
# @raise [ArgumentError] If the provided certificate name is nil
165+
def get_cert_arn(cert_name)
166+
raise ArgumentError, '"cert_name" may not be nil' if cert_name.nil?
167+
168+
cert = @iam.get_server_certificate(server_certificate_name: cert_name)
169+
cert.server_certificate.server_certificate_metadata.arn
170+
end
160171
end
161172
end

lib/cucloud/kms_utils.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
module Cucloud
22
# Utilities library for interacting with KMS.
33
class KmsUtils
4+
# Class to represent missing key error
45
class MissingKmsKey < StandardError
56
end
67

8+
# This is used in a sttuct to denote an encrypted field
79
ENCRYPTED_SUFFIX = '_encrypted'.freeze
10+
# This is used in a sttuct to denote an decrypted field
811
DECRYPTED_SUFFIX = '_decrypted'.freeze
912

1013
attr_accessor :kms_key_id

lib/cucloud/lambda_utils.rb

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
module Cucloud
2+
# LambdaUtils - Utilities for woking with Lambda functions
3+
class LambdaUtils
4+
require 'open-uri'
5+
6+
# Constructor for LambdaUtils class
7+
# @param lambda_client [Aws::Lambda::Client] AWS Lambda SDK Client
8+
def initialize(lambda_client = Aws::Lambda::Client.new)
9+
@lambda = lambda_client
10+
end
11+
12+
# Download the source pacakge for a given lambda function
13+
# @param function_name [String] Name of the lambda function
14+
# @param path [String] Local path to write the source pacakge to, defaults to /tmp
15+
# @param version [String] Version of the function to download, defaults to $LATEST
16+
# @return [String] Local path to the file
17+
def download_source_for_function(function_name, path = '/tmp', version = '$LATEST')
18+
lambda_function = @lambda.get_function(function_name: function_name,
19+
qualifier: version)
20+
21+
file_path = path + '/' + function_name + version + '.zip'
22+
File.open(file_path, 'wb') do |saved_file|
23+
open(lambda_function[:code][:location], 'rb') do |read_file|
24+
saved_file.write(read_file.read)
25+
end
26+
end
27+
file_path
28+
end
29+
30+
# Return all versions of a lambda function
31+
# @param function_name [String] Name of the lambda function
32+
# @return [Array] Array of strings representing the versions of the lambda function
33+
def get_all_versions_for_function(function_name)
34+
version_response = @lambda.list_versions_by_function(function_name: function_name)
35+
version_response.versions.map { |x| x[:version] }
36+
end
37+
38+
# Return all funtion names for an account
39+
# @return [Array] Array of strings representing the function names
40+
def get_all_function_names_for_account_region
41+
funtions_response = @lambda.list_functions
42+
funtions_response.functions.map { |x| x[:function_name] }
43+
end
44+
end
45+
end

lib/cucloud/rds_utils.rb

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
module Cucloud
22
# RdsUtils class - for interacting with the AWS relational database service
33
class RdsUtils
4+
# RDSInstanceAlreadyExist Class - capture erros when creating or restoring
5+
# an RDS instance which already exist
6+
class RDSInstanceAlreadyExist < StandardError
7+
end
8+
49
def initialize(rds_client = Aws::RDS::Client.new)
510
@rds = rds_client
611
end
@@ -13,6 +18,69 @@ def get_instance(db_instance_identifier)
1318
resource.db_instance(db_instance_identifier)
1419
end
1520

21+
# Determine if a givne db instance exist
22+
# @param db_instance_identifier [String] RDS instance identifier
23+
# @return [boolean]
24+
def does_db_exist?(db_instance_identifier)
25+
get_instance(db_instance_identifier).instance_create_time
26+
true
27+
rescue Aws::RDS::Errors::DBInstanceNotFound
28+
false
29+
end
30+
31+
# Delete a givne db instance
32+
# @param db_instance_identifier [String] RDS instance identifier
33+
# @param db_snapshot_identifier [String] Name for final snapshot, default is nil
34+
def delete_db_instance(db_instance_identifier, db_snapshot_identifier = nil)
35+
if does_db_exist?(db_instance_identifier)
36+
if db_snapshot_identifier.nil?
37+
@rds.delete_db_instance(db_instance_identifier: db_instance_identifier, skip_final_snapshot: true)
38+
else
39+
@rds.delete_db_instance(db_instance_identifier: db_instance_identifier,
40+
final_db_snapshot_identifier: db_snapshot_identifier)
41+
end
42+
43+
@rds.wait_until(:db_instance_deleted, db_instance_identifier: db_instance_identifier)
44+
else
45+
raise Aws::RDS::Errors::DBInstanceNotFound.new(db_instance_identifier, '')
46+
end
47+
end
48+
49+
# Restore DB from a snapshot
50+
# @param db_instance_identifier [String] RDS instance identifier
51+
# @param db_snapshot_identifier [String] Name for final snapshot, default is nil
52+
def restore_db(db_instance_identifier, restore_from, options = {})
53+
raise RDSInstanceAlreadyExist if does_db_exist?(db_instance_identifier)
54+
55+
db_snapshot_identifier =
56+
options[:db_snapshot_identifier].nil? ? find_latest_snapshot(restore_from) : options[:db_snapshot_identifier]
57+
options[:db_instance_identifier] = db_instance_identifier
58+
options[:db_snapshot_identifier] = db_snapshot_identifier
59+
@rds.restore_db_instance_from_db_snapshot(options)
60+
end
61+
62+
# Delete a givne db instance
63+
# @param db_instance_identifier [String] RDS instance identifier
64+
# @return [String] Most recent snapshot ID for given RDS instance
65+
def find_latest_snapshot(db_instance_identifier, snapshot_type = 'manual')
66+
latest_snapshot_time = Time.new(2002)
67+
latest_snap_shot = nil
68+
snapshots_info = @rds.describe_db_snapshots(
69+
db_instance_identifier: db_instance_identifier, snapshot_type: snapshot_type
70+
)[:db_snapshots]
71+
72+
snapshots_info.each do |snapshot_info|
73+
next if snapshot_info[:status] != 'available'
74+
75+
if latest_snapshot_time.to_i < snapshot_info[:snapshot_create_time].to_i
76+
latest_snapshot_time = snapshot_info[:snapshot_create_time].to_i
77+
latest_snap_shot = snapshot_info
78+
end
79+
end
80+
81+
latest_snap_shot.nil? ? nil : latest_snap_shot[:db_snapshot_identifier]
82+
end
83+
1684
# Begins the creation of a snapshot of the given RDS instance.
1785
# This is a non-blocking call so it will return before the snapshot
1886
# is created and available.

lib/cucloud/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module Cucloud
22
# Disable mutable constant warning - freezing this oddly breaks bundler
33
# rubocop:disable Style/MutableConstant
4-
VERSION = '0.7.1'
4+
VERSION = '0.7.2'
55
end

spec/cfn_utils_spec.rb

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
require 'spec_helper'
2+
3+
describe Cucloud::CfnUtils do
4+
let(:cfn_client) do
5+
Aws::CloudFormation::Client.new(stub_responses: true)
6+
end
7+
8+
let(:cfn_util) do
9+
Cucloud::CfnUtils.new cfn_client
10+
end
11+
12+
let(:template_json) { StringIO.new('some template') }
13+
14+
context 'while create_stack returns an error' do
15+
before do
16+
cfn_client.stub_responses(
17+
:create_stack,
18+
Aws::CloudFormation::Errors::ServiceError.new('', 'test')
19+
)
20+
cfn_client.stub_responses(
21+
:update_stack,
22+
Aws::CloudFormation::Errors::ServiceError.new('', 'test')
23+
)
24+
cfn_client.stub_responses(
25+
:describe_stack_events,
26+
stack_events: [
27+
stack_id: 'stack_id',
28+
event_id: 'event_id',
29+
stack_name: 'teststack',
30+
timestamp: Time.new
31+
]
32+
)
33+
end
34+
35+
describe '#create_stack' do
36+
it 'raises an error' do
37+
allow(IO).to receive(:read).with('file').and_return('some_data')
38+
expect { cfn_util.create_stack('teststack', 'file') }.to raise_error Aws::CloudFormation::Errors::ServiceError
39+
end
40+
end
41+
42+
describe '#update_stack' do
43+
it 'raises an error' do
44+
allow(IO).to receive(:read).with('file').and_return('some_data')
45+
expect { cfn_util.update_stack('teststack', 'file') }.to raise_error Aws::CloudFormation::Errors::ServiceError
46+
end
47+
end
48+
end
49+
50+
context 'while create_stack is stubbed out' do
51+
before do
52+
cfn_client.stub_responses(
53+
:create_stack,
54+
stack_id: 'stack_id'
55+
)
56+
cfn_client.stub_responses(
57+
:describe_stacks,
58+
stacks: [
59+
stack_id: 'stack_id',
60+
stack_name: 'teststack',
61+
creation_time: Time.new,
62+
stack_status: 'CREATE_COMPLETE'
63+
]
64+
)
65+
cfn_client.stub_responses(
66+
:describe_stack_events,
67+
stack_events: [
68+
stack_id: 'stack_id',
69+
event_id: 'event_id',
70+
stack_name: 'teststack',
71+
timestamp: Time.new
72+
]
73+
)
74+
end
75+
76+
describe '#create_stack' do
77+
it 'does not raise an error' do
78+
allow(IO).to receive(:read).with('file').and_return('some_data')
79+
expect { cfn_util.create_stack('teststack', 'file') }.not_to raise_error
80+
end
81+
end
82+
end
83+
end

0 commit comments

Comments
 (0)