lambda_wrap 0.27.0 → 1.0.0

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: b47d747dee8ec24d3afbf8b18ebf6fdc2359afd4
4
- data.tar.gz: 7ca7e1859e904c2e1f92da462dc427a1d1bb4168
3
+ metadata.gz: cd87dbdb65df35c7a42c352ee4b619ee1f2c263a
4
+ data.tar.gz: 7bddaa1b451694fbf78f85a85a1ea35aa69a51cc
5
5
  SHA512:
6
- metadata.gz: 128c10bf9c107c209810a3d24b107d5a1881f826caa968b9ec3c950591a197f5f65055a2305f2ecaac9ceee380f1c1bb1e617e6e7646be72e75a1da67ea9c668
7
- data.tar.gz: 94edd6cc371f977713b949d2f5c59ac4c837a63f3276bb9e346b516543633523f47dbc130ce250be22dffbc07758dd89c32733e63e84f1db798879b1a59c2da1
6
+ metadata.gz: 2ddde3fd339e8d3cd88dc8a4ba8508e246d895ffacd56477857530f9335d9226859759ff66337602ca83fdb1bd6cb5f0b774f4e41ccb4cd607f64a0cebaef1cc
7
+ data.tar.gz: 68c827f0dedea73cd533e6bb4d63d529544d337889c7db8e60147905405d9a738c6cf251ed31a32428fc6152f27b7ba4b0ca3bb10f1fdf856cb002f4369ea186
@@ -1,203 +1,153 @@
1
- require 'aws-sdk'
2
-
3
1
  module LambdaWrap
4
- # The ApiGatewayManager simplifies downloading the aws-apigateway-importer binary,
5
- # importing a {swagger configuration}[http://swagger.io], and managing API Gateway stages.
6
- # Added functionality to create APIGateway deom Swagger file. Thsi API is useful for gateway's having
7
- # custom authorization.
8
-
9
- # Note: The concept of an environment of the LambdaWrap gem matches a stage in AWS ApiGateway terms.
10
- class ApiGatewayManager
2
+ # The ApiGateway class simplifies creation, deployment, and management of API Gateway objects.
3
+ # The specification for the API MUST be detailed in a provided Open API Formatted file (fka Swagger).
4
+ #
5
+ # @!attribute [r] specification
6
+ # @return [Hash] The Swagger spec parsed into a Hash
7
+ #
8
+ # @since 1.0
9
+ class ApiGateway < AwsService
10
+ attr_reader :specification
11
+
12
+ # Initializes the APIGateway Manager Object. A significant majority of the configuration of your
13
+ # API should be configured through your Swagger File (e.g. Integrations, API Name, Version).
11
14
  #
12
- # The constructor does some basic setup
13
- # * Validating basic AWS configuration
14
- # * Creating the underlying client to interact with the AWS SDK.
15
- # * Defining the temporary path of the api-gateway-importer jar file
16
- def initialize
17
- # AWS api gateway client
18
- @client = Aws::APIGateway::Client.new
19
- # path to apigateway-importer jar
20
- @jarpath = File.join(Dir.tmpdir, 'aws-apigateway-importer-1.0.3-SNAPSHOT-jar-with-dependencies.jar')
21
- @versionpath = File.join(Dir.tmpdir, 'aws-apigateway-importer-1.0.3-SNAPSHOT-jar-with-dependencies.s3version')
15
+ # @param [Hash] options The Options initialize the API Gateway Manager with.
16
+ # @option options [String] :path_to_swagger_file File path the Swagger File to load and parse.
17
+ # @option options [String] :import_mode (overwrite) How the API Gateway Object will handle updates.
18
+ # Accepts <tt>overwrite</tt> and <tt>merge</tt>.
19
+ def initialize(options)
20
+ options_with_defaults = options.reverse_merge(import_mode: 'overwrite')
21
+ @path_to_swagger_file = options_with_defaults[:path_to_swagger_file]
22
+ @specification = extract_specification(@path_to_swagger_file)
23
+ @api_name = @specification['info']['title']
24
+ @api_version = @specification['info']['version']
25
+ @import_mode = options_with_defaults[:import_mode]
22
26
  end
23
27
 
24
- ##
25
- # Downloads the aws-apigateway-importer jar from an S3 bucket.
26
- # This is a workaround since aws-apigateway-importer does not provide a binary.
27
- # Once a binary is available on the public internet, we'll start using this instead
28
- # of requiring users of this gem to upload their custom binary to an S3 bucket.
28
+ # Deploys the API Gateway Object to a specified environment
29
29
  #
30
- # *Arguments*
31
- # [s3_bucket] An S3 bucket from where the aws-apigateway-importer binary can be downloaded.
32
- # [s3_key] The path (key) to the aws-apigateay-importer binary on the s3 bucket.
33
- def download_apigateway_importer(s3_bucket, s3_key)
34
- s3 = Aws::S3::Client.new
35
-
36
- # current version
37
- current_s3_version = File.open(@versionpath, 'rb').read if File.exist?(@versionpath)
38
-
39
- # online s3 version
40
- desired_s3_version = s3.head_object(bucket: s3_bucket, key: s3_key).version_id
41
-
42
- # compare local with remote version
43
- if current_s3_version != desired_s3_version || !File.exist?(@jarpath)
44
- puts "Downloading aws-apigateway-importer jar with S3 version #{desired_s3_version}"
45
- s3.get_object(response_target: @jarpath, bucket: s3_bucket, key: s3_key)
46
- File.write(@versionpath, desired_s3_version)
30
+ # @param environment_options [LambdaWrap::Environment] The environment to deploy
31
+ # @param client [Aws::APIGateway::Client] Client to use with SDK. Should be passed in by the API class.
32
+ # @param region [String] AWS Region string. Should be passed in by the API class.
33
+ def deploy(environment_options, client, region = 'AWS_REGION')
34
+ super
35
+ puts "Deploying API: #{@api_name} to Environment: #{environment_options.name}"
36
+ @stage_variables = environment_options.variables || {}
37
+ @stage_variables.store('environment', environment_options.name)
38
+
39
+ api_id = get_id_for_api(@api_name)
40
+ service_response =
41
+ if api_id
42
+ @client.put_rest_api(
43
+ fail_on_warnings: false, mode: @import_mode, rest_api_id:
44
+ api_id, body: File.open(@path_to_swagger_file, 'rb').read
45
+ )
46
+ else
47
+ @client.import_rest_api(
48
+ fail_on_warnings: false,
49
+ body: File.open(@path_to_swagger_file, 'rb').read
50
+ )
51
+ end
52
+
53
+ if service_response.nil? || service_response.id.nil?
54
+ raise "Failed to create API gateway with name #{@api_name}"
47
55
  end
48
- end
49
56
 
50
- ##
51
- # Sets up the API gateway by searching whether the API Gateway already exists
52
- # and updates it with the latest information from the swagger file.
53
- #
54
- # *Arguments*
55
- # [api_name] The name of the API to which the swagger file should be applied to.
56
- # [env] The environment where it should be published (which is matching an API gateway stage)
57
- # [swagger_file] A handle to a swagger file that should be used by aws-apigateway-importer
58
- # [api_description] The description of the API to be displayed.
59
- # [stage_variables] A Hash of stage variables to be deployed with the stage. Adds an 'environment' by default.
60
- # [region] A string representing the region to deploy the API. Defaults to what is set as an environment variable.
61
- def setup_apigateway(api_name, env, swagger_file, api_description = 'Deployed with LambdaWrap',
62
- stage_variables = {}, region = ENV['AWS_REGION'])
63
- # ensure API is created
64
- api_id = get_existing_rest_api(api_name)
65
- api_id = setup_apigateway_create_rest_api(api_name, api_description) unless api_id
66
-
67
- # create resources
68
- setup_apigateway_create_resources(api_id, swagger_file, region)
69
-
70
- # create stages
71
- stage_variables.store('environment', env)
72
- create_stages(api_id, env, stage_variables)
73
-
74
- # return URI of created stage
75
- "https://#{api_id}.execute-api.#{region}.amazonaws.com/#{env}/"
76
- end
57
+ if api_id
58
+ "Created API Gateway Object: #{@api_name} having id #{service_response.id}"
59
+ else
60
+ "Updated API Gateway Object: #{@api_name} having id #{service_response.id}"
61
+ end
77
62
 
78
- ##
79
- # Shuts down an environment from the API Gateway. This basically deletes the stage
80
- # from the API Gateway, but does not delete the API Gateway itself.
81
- #
82
- # *Argument*
83
- # [api_name] The name of the API where the environment should be shut down.
84
- # [env] The environment (matching an API Gateway stage) to shutdown.
85
- def shutdown_apigateway(api_name, env)
86
- api_id = get_existing_rest_api(api_name)
87
- delete_stage(api_id, env)
88
- end
63
+ create_stage(service_response.id, environment_options)
89
64
 
90
- ##
91
- # Gets the ID of an existing API Gateway api, or nil if it doesn't exist
92
- #
93
- # *Arguments*
94
- # [api_name] The name of the API to be checked for existance
95
- def get_existing_rest_api(api_name)
96
- apis = @client.get_rest_apis(limit: 500).data
97
- api = apis.items.select { |a| a.name == api_name }.first
98
-
99
- return api.id if api
100
- # nil is returned otherwise
65
+ service_uri = "https://#{service_response.id}.execute-api.#{@region}.amazonaws.com/#{environment_options.name}/"
66
+
67
+ puts "API: #{@api_name} deployed at #{service_uri}"
68
+
69
+ service_uri
101
70
  end
102
71
 
103
- ##
104
- # Creates the API with a given name using the SDK and returns the id
72
+ # Tearsdown environment for API Gateway. Deletes stage.
105
73
  #
106
- # *Arguments*
107
- # [api_name] A String representing the name of the API Gateway Object to be created
108
- # [api_description] A String representing the description of the API
109
- def setup_apigateway_create_rest_api(api_name, api_description)
110
- puts 'Creating API with name ' + api_name
111
- api = @client.create_rest_api(name: api_name, description: api_description)
112
- api.id
74
+ # @param environment_options [LambdaWrap::Environment] The environment to teardown.
75
+ # @param client [Aws::APIGateway::Client] Client to use with SDK. Should be passed in by the API class.
76
+ # @param region [String] AWS Region string. Should be passed in by the API class.
77
+ def teardown(environment_options, client, region = 'AWS_REGION')
78
+ super
79
+ api_id = get_id_for_api(@api_name)
80
+ if api_id
81
+ delete_stage(api_id, environment_options.name)
82
+ else
83
+ puts "API Gateway Object #{@api_name} not found. No environment to tear down."
84
+ end
85
+ true
113
86
  end
114
87
 
115
- ##
116
- # Invokes the aws-apigateway-importer jar with the required parameter
117
- #
118
- # *Arguments*
119
- # [api_id] The AWS ApiGateway id where the swagger file should be applied to.
120
- # [swagger_file] The handle to a swagger definition file that should be imported into API Gateway
121
- # [region] A string representing the target region to deploy the API
122
- def setup_apigateway_create_resources(api_id, swagger_file, region)
123
- raise 'API ID not provided' unless api_id
124
-
125
- cmd = "java -jar #{@jarpath} --update #{api_id} --region #{region} #{swagger_file}"
126
- raise 'API gateway not created' unless system(cmd)
88
+ # Deletes all stages and API Gateway object.
89
+ # @param client [Aws::APIGateway::Client] Client to use with SDK. Should be passed in by the API class.
90
+ # @param region [String] AWS Region string. Should be passed in by the API class.
91
+ def delete(client, region = 'AWS_REGION')
92
+ super
93
+ api_id = get_id_for_api(@api_name)
94
+ if api_id
95
+ @client.delete_rest_api(rest_api_id: api_id)
96
+ puts "Deleted API: #{@api_name} ID:#{api_id}"
97
+ else
98
+ puts "API Gateway Object #{@api_name} not found. Nothing to delete."
99
+ end
100
+ true
127
101
  end
128
102
 
129
- ##
130
- # Creates a stage of the currently set resources
131
- #
132
- # *Arguments*
133
- # [api_id] The AWS ApiGateway id where the stage should be created at.
134
- # [env] The environment (which matches the stage in API Gateway) to create.
135
- # [stage_variables] A Hash of stage variables to deploy with the stage
136
- def create_stages(api_id, env, stage_variables)
137
- deployment_description = 'Deployment of service to ' + env
138
- deployment = @client.create_deployment(
139
- rest_api_id: api_id, stage_name: env, cache_cluster_enabled: false, description: deployment_description,
140
- variables: stage_variables
141
- ).data
142
- puts deployment
103
+ def to_s
104
+ return @api_name if @api_name && @api_name.is_a?(String)
105
+ super
143
106
  end
144
107
 
145
- ##
146
- # Deletes a stage of the API Gateway
147
- #
148
- # *Arguments*
149
- # [api_id] The AWS ApiGateway id from which the stage should be deleted from.
150
- # [env]The environment (which matches the stage in API Gateway) to delete.
108
+ private
109
+
151
110
  def delete_stage(api_id, env)
152
111
  @client.delete_stage(rest_api_id: api_id, stage_name: env)
153
112
  puts 'Deleted API gateway stage ' + env
154
113
  rescue Aws::APIGateway::Errors::NotFoundException
155
- puts 'API Gateway stage ' + env + ' does not exist. Nothing to delete.'
114
+ puts "API Gateway stage #{env} does not exist. Nothing to delete."
156
115
  end
157
116
 
158
- ##
159
- # Generate or Update the API Gateway by using the swagger file and the SDK importer
160
- #
161
- # *Arguments*
162
- # [api_name] API Gateway name
163
- # [local_swagger_file] Path of the local swagger file
164
- # [env] Environment identifier
165
- # [stage_variables] hash of stage variables
166
- def setup_apigateway_by_swagger_file(api_name, local_swagger_file, env, stage_variables = {})
167
- # If API gateway with the name is already present then update it else create a new one
168
- api_id = get_existing_rest_api(api_name)
169
- swagger_file_content = File.read(local_swagger_file)
170
-
171
- gateway_response = nil
172
- if api_id.nil?
173
- # Create a new APIGateway
174
- gateway_response = @client.import_rest_api(fail_on_warnings: false, body: swagger_file_content)
175
- else
176
- # Update the exsiting APIgateway. By Merge the exsisting gateway will be merged with the new
177
- # one supplied in the Swagger file.
178
- gateway_response = @client.put_rest_api(rest_api_id: api_id, mode: 'merge', fail_on_warnings: false,
179
- body: swagger_file_content)
180
- end
181
-
182
- raise "Failed to create API gateway with name #{api_name}" if gateway_response.nil? && gateway_response.id.nil?
117
+ def create_stage(api_id, environment_options)
118
+ deployment_description = "Deploying API #{@api_name} v#{@api_version}\
119
+ to Environment:#{environment_options.name}"
120
+ stage_description = "#{environment_options.name} - #{environment_options.description}"
121
+ @client.create_deployment(
122
+ rest_api_id: api_id, stage_name: environment_options.name,
123
+ stage_description: stage_description, description: deployment_description,
124
+ cache_cluster_enabled: false, variables: environment_options.variables
125
+ )
126
+ end
183
127
 
184
- if api_id.nil?
185
- puts "Created api gateway #{api_name} having id #{gateway_response.id}"
186
- else
187
- puts "Updated api gateway #{api_name} having id #{gateway_response.id}"
128
+ def extract_specification(file_path)
129
+ unless File.exist?(file_path)
130
+ raise ArgumentError, "Open API Spec (Swagger) File does not exist: #{file_path}!"
188
131
  end
189
-
190
- # Deploy the service
191
- stage_variables.store('environment', env)
192
- create_stages(gateway_response.id, env, stage_variables)
193
-
194
- service_uri = "https://#{gateway_response.id}.execute-api.#{ENV['AWS_REGION']}.amazonaws.com/#{env}/"
195
- puts "Service deployed at #{service_uri}"
196
-
197
- service_uri
132
+ spec = Psych.load_file(file_path)
133
+ raise ArgumentError, 'LambdaWrap only supports swagger v2.0' unless spec['swagger'] == '2.0'
134
+ raise ArgumentError, 'Invalid Title field in the OAPISpec' unless spec['info']['title'] =~ /[A-Za-z0-9]{1,1024}/
135
+ spec
198
136
  end
199
137
 
200
- private :get_existing_rest_api, :setup_apigateway_create_rest_api, :setup_apigateway_create_resources,
201
- :create_stages, :delete_stage
138
+ def get_id_for_api(api_name)
139
+ response = nil
140
+ loop do
141
+ response =
142
+ if !response || response.position.nil?
143
+ @client.get_rest_apis(limit: 500)
144
+ else
145
+ @client.get_rest_apis(limit: 500, position: response.position)
146
+ end
147
+ api = response.items.detect { |item| item.name == api_name }
148
+ return api.id if api
149
+ return if response.items.empty? || response.position.nil? || response.position.empty?
150
+ end
151
+ end
202
152
  end
203
153
  end
@@ -0,0 +1,270 @@
1
+ module LambdaWrap
2
+ # Top level class that manages the Serverless Microservice API deployment.
3
+ #
4
+ # @!attribute [r] lambdas
5
+ # @return [Array<LambdaWrap::Lambda>] The List of Lambdas to be deployed with the API.
6
+ #
7
+ # @!attribute [r] dynamo_tables
8
+ # @return [Array<LambdaWrap::DynamoTables>] The List of DynamoTables to be deployed with the API.
9
+ #
10
+ # @!attribute [r] api_gateways
11
+ # @return [Array<LambdaWrap::ApiGateway>] The List of API Gateways to be deployed with the API.
12
+ #
13
+ # @!attribute [r] region
14
+ # @return [String] The AWS Region to deploy the API.
15
+ #
16
+ # @since 1.0
17
+ class API
18
+ attr_reader :lambdas
19
+ attr_reader :dynamo_tables
20
+ attr_reader :api_gateways
21
+ attr_reader :region
22
+
23
+ # Constructor for the high level API Manager class.
24
+ #
25
+ # @param [Hash] options The Options to configure the API.
26
+ # @option options [String] :access_key_id The AWS Access Key Id to communicate with AWS. Will also check the
27
+ # environment variables for this value.
28
+ # @option options [String] :secret_access_key The AWS Secret Access Key to communicate with AWS. Also checks
29
+ # environment variables for this value.
30
+ # @option options [String] :region The AWS Region to deploy API to. Also checks environment variables for this
31
+ # value.
32
+ #
33
+ # @todo Allow clients to pass in a YAML file for all construction.
34
+ def initialize(options = {})
35
+ unless options[:lambda_client] && options[:dynamo_client] && options[:api_gateway_client]
36
+ access_key_id = options[:access_key_id] || ENV['AWS_ACCESS_KEY_ID'] || ENV['ACCESS_KEY'] ||
37
+ raise(ArgumentError, 'Cannot find AWS Access Key ID.')
38
+
39
+ secret_access_key = options[:secret_access_key] || ENV['AWS_SECRET_ACCESS_KEY'] || ENV['SECRET_KEY'] ||
40
+ raise(ArgumentError, 'Cannot find AWS Secret Key.')
41
+
42
+ credentials = Aws::Credentials.new(access_key_id, secret_access_key)
43
+ end
44
+
45
+ region = options[:region] || ENV['AWS_REGION'] || ENV['AMAZON_REGION'] || ENV['AWS_DEFAULT_REGION'] ||
46
+ raise(ArgumentError, 'Cannot find AWS Region.')
47
+
48
+ @lambdas = []
49
+ @dynamo_tables = []
50
+ @api_gateways = []
51
+
52
+ @region = region
53
+ @lambda_client = options[:lambda_client] ||
54
+ Aws::Lambda::Client.new(credentials: credentials, region: region)
55
+ @dynamo_client = options[:dynamo_client] ||
56
+ Aws::DynamoDB::Client.new(credentials: credentials, region: region)
57
+ @api_gateway_client = options[:api_gateway_client] ||
58
+ Aws::APIGateway::Client.new(credentials: credentials, region: region)
59
+ end
60
+
61
+ # Add Lambda Object(s) to the API.
62
+ #
63
+ # @param [LambdaWrap::Lambda Array<LambdaWrap::Lambda>] new_lambda Splat of LambdaWrap Lambda
64
+ # objects to add to the API. Overloaded as:
65
+ # add_lambda(lambda1) OR add_lambda([lambda1, lambda2]) OR add_lambda(lambda1, lambda2)
66
+ def add_lambda(*new_lambda)
67
+ flattened_lambdas = new_lambda.flatten
68
+ flattened_lambdas.each { |lambda| parameter_guard(lambda, LambdaWrap::Lambda, 'LambdaWrap::Lambda') }
69
+ lambdas.concat(flattened_lambdas)
70
+ end
71
+
72
+ # Add Dynamo Table Object(s) to the API.
73
+ #
74
+ # @param [LambdaWrap::DynamoTable, Array<LambdaWrap::DynamoTable>] new_table Splat of LambdaWrap DynamoTable
75
+ # objects to add to the API. Overloaded as:
76
+ # add_dynamo_table(table1) OR add_dynamo_table([table1, table2]) OR add_dynamo_table(table1, table2)
77
+ def add_dynamo_table(*new_table)
78
+ flattened_tables = new_table.flatten
79
+ flattened_tables.each { |table| parameter_guard(table, LambdaWrap::DynamoTable, 'LambdaWrap::DynamoTable') }
80
+ dynamo_tables.concat(flattened_tables)
81
+ end
82
+
83
+ # Add API Gateway Object(s) to the API.
84
+ #
85
+ # @param [LambdaWrap::ApiGateway, Array<LambdaWrap::ApiGateway>] new_api_gateway Splat of LambdaWrap API Gateway
86
+ # objects to add to the API. Overloaded as:
87
+ # add_api_gateway(apig1) OR add_api_gateway([apig1, apig2]) OR add_api_gateway(apig1, apig2)
88
+ def add_api_gateway(*new_api_gateway)
89
+ flattened_api_gateways = new_api_gateway.flatten
90
+ flattened_api_gateways.each { |apig| parameter_guard(apig, LambdaWrap::ApiGateway, 'LambdaWrap::ApiGateway') }
91
+ api_gateways.concat(flattened_api_gateways)
92
+ end
93
+
94
+ # Deploys all services to the specified environment.
95
+ #
96
+ # @param [LambdaWrap::Environment] environment_options the Environment to deploy
97
+ def deploy(environment_options)
98
+ environment_parameter_guard(environment_options)
99
+ if no_op?
100
+ puts 'Nothing to deploy.'
101
+ return
102
+ end
103
+
104
+ deployment_start_message = 'Deploying '
105
+ deployment_start_message += "#{dynamo_tables.length} Dynamo Tables, " unless dynamo_tables.empty?
106
+ deployment_start_message += "#{lambdas.length} Lambdas, " unless lambdas.empty?
107
+ deployment_start_message += "#{api_gateways.length} API Gateways " unless api_gateways.empty?
108
+ deployment_start_message += "to Environment: #{environment_options.name}"
109
+ puts deployment_start_message
110
+
111
+ total_time_start = Time.now
112
+
113
+ services_time_start = total_time_start
114
+ dynamo_tables.each { |table| table.deploy(environment_options, @dynamo_client, @region) }
115
+ services_time_end = Time.now
116
+
117
+ unless dynamo_tables.empty?
118
+ puts "Deploying #{dynamo_tables.length} Table(s) took: \
119
+ #{Time.at(services_time_end - services_time_start).utc.strftime('%H:%M:%S')}"
120
+ end
121
+
122
+ services_time_start = Time.now
123
+ lambdas.each { |lambda| lambda.deploy(environment_options, @lambda_client, @region) }
124
+ services_time_end = Time.now
125
+
126
+ unless lambdas.empty?
127
+ puts "Deploying #{lambdas.length} Lambda(s) took: \
128
+ #{Time.at(services_time_end - services_time_start).utc.strftime('%H:%M:%S')}"
129
+ end
130
+
131
+ services_time_start = Time.now
132
+ api_gateways.each { |apig| apig.deploy(environment_options, @api_gateway_client, @region) }
133
+ services_time_end = Time.now
134
+
135
+ unless api_gateways.empty?
136
+ puts "Deploying #{api_gateways.length} API Gateway(s) took: \
137
+ #{Time.at(services_time_end - services_time_start).utc.strftime('%H:%M:%S')}"
138
+ end
139
+
140
+ total_time_end = Time.now
141
+
142
+ puts "Total API Deployment took: \
143
+ #{Time.at(total_time_end - total_time_start).utc.strftime('%H:%M:%S')}"
144
+ puts "Successfully deployed API to #{environment_options.name}"
145
+
146
+ true
147
+ end
148
+
149
+ # Tearsdown Environment for all services.
150
+ #
151
+ # @param [LambdaWrap::Environment] environment_options the Environment to teardown
152
+ def teardown(environment_options)
153
+ environment_parameter_guard(environment_options)
154
+ if no_op?
155
+ puts 'Nothing to teardown.'
156
+ return
157
+ end
158
+
159
+ deployment_start_message = 'Tearing-down '
160
+ deployment_start_message += "#{dynamo_tables.length} Dynamo Tables, " unless dynamo_tables.empty?
161
+ deployment_start_message += "#{lambdas.length} Lambdas, " unless lambdas.empty?
162
+ deployment_start_message += "#{api_gateways.length} API Gateways " unless api_gateways.empty?
163
+ deployment_start_message += " Environment: #{environment_options.name}"
164
+ puts deployment_start_message
165
+
166
+ total_time_start = Time.now
167
+
168
+ services_time_start = total_time_start
169
+ dynamo_tables.each { |table| table.teardown(environment_options, @dynamo_client, @region) }
170
+ services_time_end = Time.now
171
+
172
+ unless dynamo_tables.empty?
173
+ puts "Tearing-down #{dynamo_tables.length} Table(s) took: \
174
+ #{Time.at(services_time_end - services_time_start).utc.strftime('%H:%M:%S')}"
175
+ end
176
+
177
+ services_time_start = Time.now
178
+ lambdas.each { |lambda| lambda.teardown(environment_options, @lambda_client, @region) }
179
+ services_time_end = Time.now
180
+
181
+ unless lambdas.empty?
182
+ puts "Tearing-down #{lambdas.length} Lambda(s) took: \
183
+ #{Time.at(services_time_end - services_time_start).utc.strftime('%H:%M:%S')}"
184
+ end
185
+
186
+ services_time_start = Time.now
187
+ api_gateways.each { |apig| apig.teardown(environment_options, @api_gateway_client, @region) }
188
+ services_time_end = Time.now
189
+
190
+ unless api_gateways.empty?
191
+ puts "Tearing-down #{api_gateways.length} API Gateway(s) took: \
192
+ #{Time.at(services_time_end - services_time_start).utc.strftime('%H:%M:%S')}"
193
+ end
194
+
195
+ total_time_end = Time.now
196
+
197
+ puts "Total API Tear-down took: \
198
+ #{Time.at(total_time_end - total_time_start).utc.strftime('%H:%M:%S')}"
199
+ puts "Successful Teardown API to #{environment_options.name}"
200
+
201
+ true
202
+ end
203
+
204
+ # Deletes all services from the cloud.
205
+ def delete
206
+ if dynamo_tables.empty? && lambdas.empty? && api_gateways.empty?
207
+ puts 'Nothing to Deleting.'
208
+ return
209
+ end
210
+
211
+ deployment_start_message = 'Deleting '
212
+ deployment_start_message += "#{dynamo_tables.length} Dynamo Tables, " unless dynamo_tables.empty?
213
+ deployment_start_message += "#{lambdas.length} Lambdas, " unless lambdas.empty?
214
+ deployment_start_message += "#{api_gateways.length} API Gateways " unless api_gateways.empty?
215
+ puts deployment_start_message
216
+
217
+ total_time_start = Time.now
218
+
219
+ services_time_start = total_time_start
220
+ dynamo_tables.each { |table| table.delete(@dynamo_client, @region) }
221
+ services_time_end = Time.now
222
+
223
+ unless dynamo_tables.empty?
224
+ puts "Deleting #{dynamo_tables.length} Table(s) took: \
225
+ #{Time.at(services_time_end - services_time_start).utc.strftime('%H:%M:%S')}"
226
+ end
227
+
228
+ services_time_start = Time.now
229
+ lambdas.each { |lambda| lambda.delete(@lambda_client, @region) }
230
+ services_time_end = Time.now
231
+
232
+ unless lambdas.empty?
233
+ puts "Deleting #{lambdas.length} Lambda(s) took: \
234
+ #{Time.at(services_time_end - services_time_start).utc.strftime('%H:%M:%S')}"
235
+ end
236
+
237
+ services_time_start = Time.now
238
+ api_gateways.each { |apig| apig.delete(@api_gateway_client, @region) }
239
+ services_time_end = Time.now
240
+
241
+ unless api_gateways.empty?
242
+ puts "Deleting #{api_gateways.length} API Gateway(s) took: \
243
+ #{Time.at(services_time_end - services_time_start).utc.strftime('%H:%M:%S')}"
244
+ end
245
+
246
+ total_time_end = Time.now
247
+
248
+ puts "Total API Deletion took: \
249
+ #{Time.at(total_time_end - total_time_start).utc.strftime('%H:%M:%S')}"
250
+ puts 'Successful Deletion of API'
251
+
252
+ true
253
+ end
254
+
255
+ private
256
+
257
+ def environment_parameter_guard(parameter)
258
+ parameter_guard(parameter, LambdaWrap::Environment, 'LambdaWrap::Environment')
259
+ end
260
+
261
+ def parameter_guard(parameter, type, type_name)
262
+ return if parameter.is_a?(type)
263
+ raise ArgumentError, "Must pass a #{type_name} to the API Manager. Got: #{parameter}"
264
+ end
265
+
266
+ def no_op?
267
+ dynamo_tables.empty? && lambdas.empty? && api_gateways.empty?
268
+ end
269
+ end
270
+ end
@@ -0,0 +1,40 @@
1
+ module LambdaWrap
2
+ # Super Abstract Class for all AWS services and their calls.
3
+ # @abstract
4
+ # @since 1.0
5
+ class AwsService
6
+ def deploy(environment, client, region = 'AWS_REGION')
7
+ unless environment.is_a?(LambdaWrap::Environment)
8
+ raise ArgumentError, 'Must pass a LambdaWrap::Environment class.'
9
+ end
10
+ @client = client
11
+ @region = region
12
+ client_guard
13
+ end
14
+
15
+ def teardown(environment, client, region = 'AWS_REGION')
16
+ unless environment.is_a?(LambdaWrap::Environment)
17
+ raise ArgumentError, 'Must pass a LambdaWrap::Environment class.'
18
+ end
19
+ @client = client
20
+ @region = region
21
+ client_guard
22
+ end
23
+
24
+ def delete(client, region = 'AWS_REGION')
25
+ @client = client
26
+ @region = region
27
+ client_guard
28
+ end
29
+
30
+ private
31
+
32
+ def client_guard
33
+ unless @client.class == Aws::Lambda::Client || @client.class == Aws::DynamoDB::Client ||
34
+ @client.class == Aws::APIGateway::Client
35
+ raise ArgumentError, 'AWS client not initialized.'
36
+ end
37
+ raise ArgumentError, 'Invalid region' if @region.empty? || !Aws.partition('aws').region(@region)
38
+ end
39
+ end
40
+ end