cucloud 0.7.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 06edeee4328a50a7b8d53d614dcfb14569958034
4
- data.tar.gz: 9f5b616df716a3823825c033403c2d1283a878b2
3
+ metadata.gz: 31e962ea920996c2b969af9640ed9f53f4be3115
4
+ data.tar.gz: 15d2df0ed1dd0e46ebe4041f3bfbaaf21403be42
5
5
  SHA512:
6
- metadata.gz: 77990c758327658b1525717a0265282684298ba3a6358182c68a8bb72308cbd40a3a9d3483feded96b6e9707635fff43c50811f3ede396d58d375a6f3ad31b1a
7
- data.tar.gz: 4422445626b608d849b697ade78ae2f28a6a13c69bb0e8330306d88ca8ce754853dbf06f0b0c5c2d12585d14e5bda3708e84914d6dbbb300c017437fd0bf98c5
6
+ metadata.gz: 176107f8a5eaa16e5a5a4f355903804314e544491bd9cfd2937d971517944dcaa712d55a5f38c18fe7f20b201fddbebfbc261cc8bd6167f890aa5bf371e42159
7
+ data.tar.gz: 81c2e58bd69ddd100c667c34fe726ccb9316383756ba1033834cf47dc32a7c0388842e720f56558d30da6935f09cfb98fe443ae341b1d995f04c72ecda12f626
data/README.md CHANGED
@@ -48,6 +48,7 @@ Note - the cucloud library assumes that environment credentials are available to
48
48
  Utilities that use this API:
49
49
 
50
50
  * Autoscale AMI Updater: https://github.com/CU-CloudCollab/asg-ami-update
51
+ * Collection of Handy Cloud Utilities: https://github.com/CU-CloudCollab/cucloud_utils
51
52
 
52
53
 
53
54
  ## Development
@@ -60,6 +61,9 @@ To run styleguide/syntax tests:
60
61
  To run unit tests:
61
62
  ``` $ bundle exec rake spec ```
62
63
 
64
+ To run unit tests for one file (kms_utils.spec.rb):
65
+ ``` $ bundle exec rake spec SPEC=spec/kms_utils_spec.rb ```
66
+
63
67
  To generate documentation:
64
68
  ``` bundle exec yard ```
65
69
 
@@ -8,10 +8,13 @@ module Cucloud
8
8
  require 'cucloud/asg_utils'
9
9
  require 'cucloud/ssm_utils'
10
10
  require 'cucloud/iam_utils'
11
+ require 'cucloud/kms_utils'
11
12
  require 'cucloud/vpc_utils'
12
13
  require 'cucloud/config_service_utils'
13
14
  require 'cucloud/cloud_trail_utils'
14
15
  require 'cucloud/rds_utils'
16
+ require 'cucloud/lambda_utils'
17
+ require 'cucloud/cfn_utils'
15
18
 
16
19
  # This is the default region API calls are made against
17
20
  DEFAULT_REGION = 'us-east-1'.freeze
@@ -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
@@ -157,5 +157,16 @@ module Cucloud
157
157
  get_user_access_keys(user[:base_data].user_name).select { |k| k[:days_old] > n && k[:active] }
158
158
  end.flatten
159
159
  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
160
171
  end
161
172
  end
@@ -0,0 +1,151 @@
1
+ module Cucloud
2
+ # Utilities library for interacting with KMS.
3
+ class KmsUtils
4
+ # Class to represent missing key error
5
+ class MissingKmsKey < StandardError
6
+ end
7
+
8
+ # This is used in a sttuct to denote an encrypted field
9
+ ENCRYPTED_SUFFIX = '_encrypted'.freeze
10
+ # This is used in a sttuct to denote an decrypted field
11
+ DECRYPTED_SUFFIX = '_decrypted'.freeze
12
+
13
+ attr_accessor :kms_key_id
14
+
15
+ # Initialize the class optionally providing an existing Aws::KMS::Client
16
+ # @param kms_client [Aws::KMS::Client] optional
17
+ def initialize(kms_client = Aws::KMS::Client.new)
18
+ @kms = kms_client
19
+ end
20
+
21
+ # Decrypt the given Base64-strict-encoded ciphertext.
22
+ # @param ciphertext [String] encrypted and Base64 strict encoded string
23
+ # @return [String] decrypted string (i.e. plaintext)
24
+ def decrypt(ciphertext)
25
+ return nil if ciphertext.nil?
26
+ return '' if ciphertext.empty?
27
+ @kms.decrypt(ciphertext_blob: Base64.strict_decode64(ciphertext)).plaintext
28
+ end
29
+
30
+ # Encrypt the given plaintext. Uses provided the KMS key provided,
31
+ # or the KMS key configured at initialization if none is provided.
32
+ # @param plaintext [String] plaintext string to be encrypted
33
+ # @param key_id [String] KMS key id to use for encryption (optional)
34
+ # @return [String] Encrypted and Base64 strict encoded ciphertext
35
+ def encrypt(plaintext, key_id = @kms_key_id)
36
+ return nil if plaintext.nil?
37
+ return '' if plaintext.empty?
38
+ Base64.strict_encode64(@kms.encrypt(key_id: key_id, plaintext: plaintext).ciphertext_blob)
39
+ end
40
+
41
+ # Process the given structure and decrypt the values of any
42
+ # attributes with names suffixed by "\_encrypted". For each such encrypted
43
+ # atttribute-value pair, adds a new attribute with suffix "_decrypted"
44
+ # and value consisting of the plaintext (i.e. decrypted value)
45
+ # of the encrypted value.
46
+ # @example
47
+ # decrypt_struct({ x_encrypted: <encrypted_value> }) =>
48
+ # { x_encrypted: <encrypted_value>, x_decrypted: <plaintext> }
49
+ # decrypt_struct([{ x_encrypted: <encrypted_value> } ]) =>
50
+ # [{ x_encrypted: <encrypted_value>, x_decrypted: <plaintext> }]
51
+ # @param main_node the structure (Hash, Array) to decrypt
52
+ # @return a copy of the structure with additional atttribute-value pairs for the decrypted values
53
+ def decrypt_struct(main_node)
54
+ return nil if main_node.nil?
55
+ return main_node if main_node.is_a?(String)
56
+ if main_node.is_a?(Hash)
57
+ new_hash = {}
58
+ main_node.each_pair do |key, value|
59
+ if key_to_decrypt?(key)
60
+ plaintext = decrypt(value)
61
+ new_hash[decrypted_key_label(key)] = plaintext
62
+ new_hash[key] = value
63
+ else
64
+ result = decrypt_struct(value)
65
+ new_hash[key] = result
66
+ end
67
+ end
68
+ return new_hash
69
+ elsif main_node.is_a?(Array)
70
+ new_array = []
71
+ main_node.each do |element|
72
+ result = decrypt_struct(element)
73
+ new_array << result
74
+ end
75
+ return new_array
76
+ else
77
+ return main_node
78
+ end
79
+ end
80
+
81
+ # Process the given structure and encrypt the values of any attributes
82
+ # with names suffixed by "_decrypted". For each such plaintext
83
+ # atttribute-value pair, adds a new attribute with suffix "\_encrypted"
84
+ # and value consisting of the encrypted value. The "_decrypted"
85
+ # atttribute-value pair is removed from the structure. Uses the
86
+ # provided the KMS key provided,
87
+ # or the KMS key configured at initialization if none is provided.
88
+ # @example
89
+ # encrypt_struct({ x_decrypted: <plaintext> }) =>
90
+ # { x_encrypted: <encrypted_value> }
91
+ # encrypt_struct([{ x_decrypted: <plaintext> }]) =>
92
+ # [{ x_encrypted: <encrypted_value> }]
93
+ # @param main_node the structure (Hash, Array) to encrypt_struct
94
+ # @param key_id [String] KMS key id to use for encryption (optional)
95
+ # @return a copy of the structure with decrypted atttribute-value pairs replaced by encrypted atttribute-value pairs
96
+ def encrypt_struct(main_node, key_id = @kms_key_id)
97
+ return nil if main_node.nil?
98
+ if main_node.is_a?(Hash)
99
+ new_hash = {}
100
+ remove_keys = []
101
+ main_node.each_pair do |key, value|
102
+ if key_to_encrypt?(key)
103
+ ciphertext = encrypt(value, key_id)
104
+ new_hash[encrypted_key_label(key)] = ciphertext
105
+ remove_keys << key
106
+ else
107
+ result = encrypt_struct(value, key_id)
108
+ new_hash[key] = result
109
+ end
110
+ end
111
+ main_node.merge!(new_hash)
112
+ main_node.delete_if do |key, _|
113
+ remove_keys.include?(key)
114
+ end
115
+ return main_node
116
+ elsif main_node.is_a?(Array)
117
+ main_node.map do |element|
118
+ encrypt_struct(element, key_id)
119
+ end
120
+ else
121
+ return main_node
122
+ end
123
+ end
124
+
125
+ private
126
+
127
+ def key_to_decrypt?(key)
128
+ key.to_s.end_with?(ENCRYPTED_SUFFIX)
129
+ end
130
+
131
+ def key_to_encrypt?(key)
132
+ key.to_s.end_with?(DECRYPTED_SUFFIX)
133
+ end
134
+
135
+ def encrypted_key_label(key)
136
+ if key.is_a?(Symbol)
137
+ key.to_s.sub(DECRYPTED_SUFFIX, ENCRYPTED_SUFFIX).to_sym
138
+ else
139
+ key.sub(DECRYPTED_SUFFIX, ENCRYPTED_SUFFIX)
140
+ end
141
+ end
142
+
143
+ def decrypted_key_label(key)
144
+ if key.is_a?(Symbol)
145
+ key.to_s.sub(ENCRYPTED_SUFFIX, DECRYPTED_SUFFIX).to_sym
146
+ else
147
+ key.sub(ENCRYPTED_SUFFIX, DECRYPTED_SUFFIX)
148
+ end
149
+ end
150
+ end
151
+ end
@@ -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
@@ -1,6 +1,11 @@
1
1
  module Cucloud
2
2
  # RdsUtils class - for interacting with the AWS relational database service
3
3
  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
+
4
9
  def initialize(rds_client = Aws::RDS::Client.new)
5
10
  @rds = rds_client
6
11
  end
@@ -13,6 +18,69 @@ module Cucloud
13
18
  resource.db_instance(db_instance_identifier)
14
19
  end
15
20
 
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
+
16
84
  # Begins the creation of a snapshot of the given RDS instance.
17
85
  # This is a non-blocking call so it will return before the snapshot
18
86
  # is created and available.
@@ -1,5 +1,5 @@
1
1
  module Cucloud
2
2
  # Disable mutable constant warning - freezing this oddly breaks bundler
3
3
  # rubocop:disable Style/MutableConstant
4
- VERSION = '0.7.1'
4
+ VERSION = '0.7.2'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cucloud
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - sbower
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2016-12-13 00:00:00.000000000 Z
13
+ date: 2017-02-24 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: aws-sdk
@@ -149,12 +149,14 @@ files:
149
149
  - cucloud.gemspec
150
150
  - lib/cucloud.rb
151
151
  - lib/cucloud/asg_utils.rb
152
+ - lib/cucloud/cfn_utils.rb
152
153
  - lib/cucloud/cloud_trail_utils.rb
153
154
  - lib/cucloud/config_service_utils.rb
154
155
  - lib/cucloud/ec2_utils.rb
155
156
  - lib/cucloud/ecs_utils.rb
156
- - lib/cucloud/elb_utils.rb
157
157
  - lib/cucloud/iam_utils.rb
158
+ - lib/cucloud/kms_utils.rb
159
+ - lib/cucloud/lambda_utils.rb
158
160
  - lib/cucloud/rds_utils.rb
159
161
  - lib/cucloud/ssm_utils.rb
160
162
  - lib/cucloud/version.rb
@@ -1,36 +0,0 @@
1
- module Cucloud
2
- # ElbUtils class - methods related to elb
3
- class ElbUtils
4
- def initialize(s3 = Aws::S3::Client.new)
5
- @s3 = s3
6
- end
7
-
8
- # Enable logging to a s3 bucket for an ELB
9
- # @param elb_name [string] name of the elastic load balancer
10
- # @param app_name [string] name of the application, used as prefix inside s3 bucket
11
- # @param policy [string] IAM policy to be applied to the bucket
12
- # @return [boolean]
13
- def enable_logging(elb_name, app_name, policy, _elb = Aws::ElasticLoadBalancing::Client.new)
14
- ## Added by Scott Ross
15
- ## Stand alone script found here: https://github.com/CU-CloudCollab/elb-logging/
16
- ## Manual process: http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/enable-access-logs.html
17
-
18
- bucket_name = "#{elb_name}-logging"
19
-
20
- @s3.create_bucket(bucket: bucket_name)
21
- s3.put_bucket_policy(bucket: bucket_name,
22
- policy: policy.to_json)
23
-
24
- elb_client.modify_load_balancer_attributes(load_balancer_name: elb_name, # required
25
- load_balancer_attributes: {
26
- access_log: {
27
- enabled: true, # required
28
- s3_bucket_name: bucket_name,
29
- emit_interval: 5,
30
- s3_bucket_prefix: app_name
31
- }
32
- })
33
- s3.list_objects(bucket: bucket_name).contents.length == 1 ? 0 : 1
34
- end
35
- end
36
- end