cucloud 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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