lambda_wrap 0.10.0 → 0.11.0

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: bb1eb756df3c76832fbe546394984c6342e38ee5
4
- data.tar.gz: a3016f7a3880c7137be66c68ef8baf0c0851cdc6
3
+ metadata.gz: d97a3646f6a910220435cc9631b671ef63089d0f
4
+ data.tar.gz: 3c46b725cbf0934aa83b937051bcba028a0321bd
5
5
  SHA512:
6
- metadata.gz: d65978e2e7be1c2ea4015b2fa55a1775c680ad2ca0c609386baf779a93f16f937e68e4c8e587c86cca6f15dddc1e7c119e112ce9dc48fcc66c2f91e4af7caff3
7
- data.tar.gz: 9eed6b47faf86c479bb861d6a329107d34623f606caa7c53eabc5f9d58ad4d1d8f2aa25a911af4285f8ca9521257ba57521dc0fca45afe9e2efe3115afe6ebd3
6
+ metadata.gz: 7c471f0e67052c94f555df32e17863058c05a67648daa53475db759c9a8f932f9d0ee631d2d3e98c19b48de6dc50de49ff9e3a8d673348a5124277656a5ffb2e
7
+ data.tar.gz: aa02fa599624407e03ab4c8a558f3f082e2dcea2cd53f83f571684e9c666c5bcc149b67950e29f0d49b5b24c808a25495a395dec69a511b6badabdcf8b7d8001
@@ -1,5 +1,5 @@
1
1
  # :nodoc:
2
- Dir["#{File.expand_path(File.dirname(__FILE__))}/**/*.rb"].each{|f| require f}
2
+ Dir["#{File.expand_path(File.dirname(__FILE__))}/**/*.rb"].each { |f| require f }
3
3
 
4
4
  STDOUT.sync = true
5
- STDERR.sync = true
5
+ STDERR.sync = true
@@ -1,177 +1,151 @@
1
1
  require 'aws-sdk'
2
- require_relative 'aws_setup'
3
2
 
4
3
  module LambdaWrap
5
-
6
- ##
7
- # The ApiGatewayManager simplifies downloading the aws-apigateway-importer binary,
8
- # importing a {swagger configuration}[http://swagger.io], and managing API Gateway stages.
9
- #
10
- # Note: The concept of an environment of the LambdaWrap gem matches a stage in AWS ApiGateway terms.
11
- class ApiGatewayManager
12
-
13
- ##
14
- # The constructor does some basic setup
15
- # * Validating basic AWS configuration
16
- # * Creating the underlying client to interact with the AWS SDK.
17
- # * Defining the temporary path of the api-gateway-importer jar file
18
- def initialize()
19
- AwsSetup.new.validate()
20
- # AWS api gateway client
21
- @client = Aws::APIGateway::Client.new()
22
- # path to apigateway-importer jar
23
- @jarpath = File.join(Dir.tmpdir, 'aws-apigateway-importer-1.0.3-SNAPSHOT-jar-with-dependencies.jar')
24
- @versionpath = File.join(Dir.tmpdir, 'aws-apigateway-importer-1.0.3-SNAPSHOT-jar-with-dependencies.s3version')
25
- end
26
-
27
- ##
28
- # Downloads the aws-apigateway-importer jar from an S3 bucket.
29
- # This is a workaround since aws-apigateway-importer does not provide a binary.
30
- # Once a binary is available on the public internet, we'll start using this instead
31
- # of requiring users of this gem to upload their custom binary to an S3 bucket.
32
- #
33
- # *Arguments*
34
- # [s3_bucket] An S3 bucket from where the aws-apigateway-importer binary can be downloaded.
35
- # [s3_key] The path (key) to the aws-apigateay-importer binary on the s3 bucket.
36
- def download_apigateway_importer(s3_bucket, s3_key)
37
-
38
- s3 = Aws::S3::Client.new
39
-
40
- # current version
41
- current_s3_version = File.open(@versionpath, 'rb').read if File.exists?(@versionpath)
42
-
43
- # online s3 version
44
- desired_s3_version = s3.head_object(bucket:s3_bucket, key:s3_key).version_id
45
-
46
- # compare local with remote version
47
- if current_s3_version != desired_s3_version || !File.exists?(@jarpath)
48
- puts "Downloading aws-apigateway-importer jar with S3 version #{desired_s3_version}"
49
- obj = s3.get_object(response_target:@jarpath, bucket:s3_bucket, key:s3_key)
50
- File.write(@versionpath, desired_s3_version)
51
- end
52
-
53
- end
54
-
55
- ##
56
- # Sets up the API gateway by searching whether the API Gateway already exists
57
- # and updates it with the latest information from the swagger file.
58
- #
59
- # *Arguments*
60
- # [api_name] The name of the API to which the swagger file should be applied to.
61
- # [env] The environment where it should be published (which is matching an API gateway stage)
62
- # [swagger_file] A handle to a swagger file that should be used by aws-apigateway-importer
63
- # [api_description] The description of the API to be displayed.
64
- def setup_apigateway(api_name, env, swagger_file, api_description = "Deployed with LambdaWrap")
65
-
66
- # ensure API is created
67
- api_id = get_existing_rest_api(api_name)
68
- api_id = setup_apigateway_create_rest_api(api_name, api_description) if !api_id
69
-
70
- # create resources
71
- setup_apigateway_create_resources(api_id, swagger_file)
72
-
73
- # create stages
74
- create_stages(api_id, env)
75
-
76
- # return URI of created stage
77
- return "https://#{api_id}.execute-api.#{ENV['AWS_REGION']}.amazonaws.com/#{env}/"
78
-
79
- end
80
-
81
- ##
82
- # Shuts down an environment from the API Gateway. This basically deletes the stage
83
- # from the API Gateway, but does not delete the API Gateway itself.
84
- #
85
- # *Argument*
86
- # [api_name] The name of the API where the environment should be shut down.
87
- # [env] The environment (matching an API Gateway stage) to shutdown.
88
- def shutdown_apigateway(api_name, env)
89
-
90
- api_id = get_existing_rest_api(api_name)
91
- delete_stage(api_id, env)
92
-
93
- end
94
-
95
- ##
96
- # Gets the ID of an existing API Gateway api, or nil if it doesn't exist
97
- #
98
- # *Arguments*
99
- # [api_name] The name of the API to be checked for existance
100
- def get_existing_rest_api(api_name)
101
-
102
- apis = @client.get_rest_apis({limit: 500}).data
103
- api = apis.items.select{ |api| api.name == api_name}.first()
104
-
105
- if (api)
106
- return api.id
107
- else
108
- return nil
109
- end
110
-
111
- end
112
-
113
- ##
114
- # Creates the API with a given name and returns the id
115
- #
116
- # *Arguments*
117
- # [api_name] The name of the API to be created
118
- def setup_apigateway_create_rest_api(api_name, api_description)
119
-
120
- puts 'Creating API with name ' + api_name
121
- api = @client.create_rest_api({name: api_name, description: api_description})
122
-
123
- return api.id
124
-
125
- end
126
-
127
- ##
128
- # Invokes the aws-apigateway-importer jar with the required parameter
129
- #
130
- # *Arguments*
131
- # [api_id] The AWS ApiGateway id where the swagger file should be applied to.
132
- # [swagger_file] The handle to a swagger definition file that should be imported into API Gateway
133
- def setup_apigateway_create_resources(api_id, swagger_file)
134
-
135
- raise 'API ID not provided' if !api_id
136
-
137
- cmd = "java -jar #{@jarpath} --update #{api_id} --region #{ENV['AWS_REGION']} #{swagger_file}"
138
- raise 'API gateway not created' if !system(cmd)
139
-
140
- end
141
-
142
- ##
143
- # Creates a stage of the currently set resources
144
- #
145
- # *Arguments*
146
- # [api_id] The AWS ApiGateway id where the stage should be created at.
147
- # [env] The environment (which matches the stage in API Gateway) to create.
148
- def create_stages(api_id, env)
149
-
150
- deployment_description = 'Deployment of service to ' + env
151
- deployment = @client.create_deployment({rest_api_id: api_id, stage_name: env, cache_cluster_enabled: false, description: deployment_description, variables: { "environment" => env}}).data
152
- puts deployment
153
-
154
- end
155
-
156
- ##
157
- # Deletes a stage of the API Gateway
158
- #
159
- # *Arguments*
160
- # [api_id] The AWS ApiGateway id from which the stage should be deleted from.
161
- # [env] The environment (which matches the stage in API Gateway) to delete.
162
- def delete_stage(api_id, env)
163
-
164
- begin
165
- @client.delete_stage({rest_api_id: api_id, stage_name: env})
166
- puts 'Deleted API gateway stage ' + env
167
- rescue Aws::APIGateway::Errors::NotFoundException
168
- puts 'API Gateway stage ' + env + ' does not exist. Nothing to delete.'
169
- end
170
-
171
- end
172
-
173
- private :get_existing_rest_api, :setup_apigateway_create_rest_api, :setup_apigateway_create_resources, :create_stages, :delete_stage
174
-
175
- end
176
-
177
- end
4
+ # The ApiGatewayManager simplifies downloading the aws-apigateway-importer binary,
5
+ # importing a {swagger configuration}[http://swagger.io], and managing API Gateway stages.
6
+
7
+ # Note: The concept of an environment of the LambdaWrap gem matches a stage in AWS ApiGateway terms.
8
+ class ApiGatewayManager
9
+ #
10
+ # The constructor does some basic setup
11
+ # * Validating basic AWS configuration
12
+ # * Creating the underlying client to interact with the AWS SDK.
13
+ # * Defining the temporary path of the api-gateway-importer jar file
14
+ def initialize
15
+ # AWS api gateway client
16
+ @client = Aws::APIGateway::Client.new
17
+ # path to apigateway-importer jar
18
+ @jarpath = File.join(Dir.tmpdir, 'aws-apigateway-importer-1.0.3-SNAPSHOT-jar-with-dependencies.jar')
19
+ @versionpath = File.join(Dir.tmpdir, 'aws-apigateway-importer-1.0.3-SNAPSHOT-jar-with-dependencies.s3version')
20
+ end
21
+
22
+ ##
23
+ # Downloads the aws-apigateway-importer jar from an S3 bucket.
24
+ # This is a workaround since aws-apigateway-importer does not provide a binary.
25
+ # Once a binary is available on the public internet, we'll start using this instead
26
+ # of requiring users of this gem to upload their custom binary to an S3 bucket.
27
+ #
28
+ # *Arguments*
29
+ # [s3_bucket] An S3 bucket from where the aws-apigateway-importer binary can be downloaded.
30
+ # [s3_key] The path (key) to the aws-apigateay-importer binary on the s3 bucket.
31
+ def download_apigateway_importer(s3_bucket, s3_key)
32
+ s3 = Aws::S3::Client.new
33
+
34
+ # current version
35
+ current_s3_version = File.open(@versionpath, 'rb').read if File.exist?(@versionpath)
36
+
37
+ # online s3 version
38
+ desired_s3_version = s3.head_object(bucket: s3_bucket, key: s3_key).version_id
39
+
40
+ # compare local with remote version
41
+ if current_s3_version != desired_s3_version || !File.exist?(@jarpath)
42
+ puts "Downloading aws-apigateway-importer jar with S3 version #{desired_s3_version}"
43
+ s3.get_object(response_target: @jarpath, bucket: s3_bucket, key: s3_key)
44
+ File.write(@versionpath, desired_s3_version)
45
+ end
46
+ end
47
+
48
+ ##
49
+ # Sets up the API gateway by searching whether the API Gateway already exists
50
+ # and updates it with the latest information from the swagger file.
51
+ #
52
+ # *Arguments*
53
+ # [api_name] The name of the API to which the swagger file should be applied to.
54
+ # [env] The environment where it should be published (which is matching an API gateway stage)
55
+ # [swagger_file] A handle to a swagger file that should be used by aws-apigateway-importer
56
+ # [api_description] The description of the API to be displayed.
57
+ def setup_apigateway(api_name, env, swagger_file, api_description = 'Deployed with LambdaWrap')
58
+ # ensure API is created
59
+ api_id = get_existing_rest_api(api_name)
60
+ api_id = setup_apigateway_create_rest_api(api_name, api_description) unless api_id
61
+
62
+ # create resources
63
+ setup_apigateway_create_resources(api_id, swagger_file)
64
+
65
+ # create stages
66
+ create_stages(api_id, env)
67
+
68
+ # return URI of created stage
69
+ "https://#{api_id}.execute-api.#{ENV['AWS_REGION']}.amazonaws.com/#{env}/"
70
+ end
71
+
72
+ ##
73
+ # Shuts down an environment from the API Gateway. This basically deletes the stage
74
+ # from the API Gateway, but does not delete the API Gateway itself.
75
+ #
76
+ # *Argument*
77
+ # [api_name] The name of the API where the environment should be shut down.
78
+ # [env] The environment (matching an API Gateway stage) to shutdown.
79
+ def shutdown_apigateway(api_name, env)
80
+ api_id = get_existing_rest_api(api_name)
81
+ delete_stage(api_id, env)
82
+ end
83
+
84
+ ##
85
+ # Gets the ID of an existing API Gateway api, or nil if it doesn't exist
86
+ #
87
+ # *Arguments*
88
+ # [api_name] The name of the API to be checked for existance
89
+ def get_existing_rest_api(api_name)
90
+ apis = @client.get_rest_apis(limit: 500).data
91
+ api = apis.items.select { |a| a.name == api_name }.first
92
+
93
+ return api.id if api
94
+ # nil is returned otherwise
95
+ end
96
+
97
+ ##
98
+ # Creates the API with a given name and returns the id
99
+ #
100
+ # *Arguments*
101
+ # [api_name] The name of the API to be created
102
+ def setup_apigateway_create_rest_api(api_name, api_description)
103
+ puts 'Creating API with name ' + api_name
104
+ api = @client.create_rest_api(name: api_name, description: api_description)
105
+ api.id
106
+ end
107
+
108
+ ##
109
+ # Invokes the aws-apigateway-importer jar with the required parameter
110
+ #
111
+ # *Arguments*
112
+ # [api_id] The AWS ApiGateway id where the swagger file should be applied to.
113
+ # [swagger_file] The handle to a swagger definition file that should be imported into API Gateway
114
+ def setup_apigateway_create_resources(api_id, swagger_file)
115
+ raise 'API ID not provided' unless api_id
116
+
117
+ cmd = "java -jar #{@jarpath} --update #{api_id} #{swagger_file}"
118
+ raise 'API gateway not created' unless system(cmd)
119
+ end
120
+
121
+ ##
122
+ # Creates a stage of the currently set resources
123
+ #
124
+ # *Arguments*
125
+ # [api_id] The AWS ApiGateway id where the stage should be created at.
126
+ # [env] The environment (which matches the stage in API Gateway) to create.
127
+ def create_stages(api_id, env)
128
+ deployment_description = 'Deployment of service to ' + env
129
+ deployment = @client.create_deployment(
130
+ rest_api_id: api_id, stage_name: env, cache_cluster_enabled: false, description: deployment_description,
131
+ variables: { 'environment' => env }
132
+ ).data
133
+ puts deployment
134
+ end
135
+
136
+ ##
137
+ # Deletes a stage of the API Gateway
138
+ #
139
+ # *Arguments*
140
+ # [api_id] The AWS ApiGateway id from which the stage should be deleted from.
141
+ # [env] The environment (which matches the stage in API Gateway) to delete.
142
+ def delete_stage(_, env)
143
+ puts 'Deleted API gateway stage ' + env
144
+ rescue Aws::APIGateway::Errors::NotFoundException
145
+ puts 'API Gateway stage ' + env + ' does not exist. Nothing to delete.'
146
+ end
147
+
148
+ private :get_existing_rest_api, :setup_apigateway_create_rest_api, :setup_apigateway_create_resources,
149
+ :create_stages, :delete_stage
150
+ end
151
+ end
@@ -1,127 +1,121 @@
1
1
  require 'aws-sdk'
2
- require_relative 'aws_setup'
3
2
 
4
3
  module LambdaWrap
5
-
6
- ##
7
- # The DynamoDBManager simplifies setting up and destroying a DynamoDB database.
8
- #
9
- # Note: In case an environment specific DynamoDB tablename such as +<baseTableName>-production+ should be used, then
10
- # it has to be injected directly to the methods since not all environments necessarily need separated databases.
11
- class DynamoDbManager
4
+ # The DynamoDBManager simplifies setting up and destroying a DynamoDB database.
5
+ #
6
+ # Note: In case an environment specific DynamoDB tablename such as +<baseTableName>-production+ should be used, then
7
+ # it has to be injected directly to the methods since not all environments necessarily need separated databases.
8
+ class DynamoDbManager
9
+ ##
10
+ # The constructor does some basic setup
11
+ # * Validating basic AWS configuration
12
+ # * Creating the underlying client to interact with the AWS SDK.
13
+ def initialize
14
+ # AWS dynamodb client
15
+ @client = Aws::DynamoDB::Client.new
16
+ end
12
17
 
13
- ##
14
- # The constructor does some basic setup
15
- # * Validating basic AWS configuration
16
- # * Creating the underlying client to interact with the AWS SDK.
17
- def initialize()
18
- AwsSetup.new.validate()
19
- # AWS dynamodb client
20
- @client = Aws::DynamoDB::Client.new()
21
- end
22
-
23
- ##
24
- # Publishes the database and awaits until it is fully available. If the table already exists, it only adjusts the read/write
25
- # capacities upwards (it doesn't downgrade them to avoid a production environment being impacted with a default setting of an
26
- # automated script).
27
- #
28
- # *Arguments*
29
- # [table_name] The table name of the dynamoDB to be created.
30
- # [attribute_definitions] The dynamoDB attribute definitions to be used when the table is created. See the AWS documentation for details.
31
- # [key_schema] The dynamoDB key definitions to be used when the table is created. See the AWS documentation for details.
32
- # [read_capacity] The read capacity to configure for the dynamoDB table.
33
- # [write_capacity] The write capacity to configure for the dynamoDB table.
34
- def publish_database(table_name, attribute_definitions, key_schema, read_capacity, write_capacity)
35
-
36
- has_updates = false
37
-
38
- # figure out whether the table exists
39
- begin
40
- table_details = @client.describe_table(table_name: table_name).table
41
- rescue Aws::DynamoDB::Errors::ResourceNotFoundException
42
- end
43
-
44
- if (table_details)
45
- wait_until_table_available(table_name) if (table_details.table_status != 'ACTIVE')
46
-
47
- if (read_capacity > table_details.provisioned_throughput.read_capacity_units ||
48
- write_capacity > table_details.provisioned_throughput.write_capacity_units)
49
- puts "Updating new read/write capacity for table #{table_name}.
50
- Read #{table_details.provisioned_throughput.read_capacity_units} ==> #{read_capacity}.
51
- Write #{table_details.provisioned_throughput.write_capacity_units} ==> #{write_capacity}."
52
- table = @client.update_table({
53
- table_name: table_name,
54
- provisioned_throughput: { read_capacity_units: read_capacity, write_capacity_units: write_capacity }
55
- })
56
- has_updates = true
57
- else
58
- puts "Table #{table_name} already exists and the desired read capacity of #{read_capacity} and write capacity of #{write_capacity} has at least been configured. Downgrading capacity units is not supported. No changes were applied."
59
- end
60
- else
61
- puts "Creating table #{table_name}."
62
- ad = attribute_definitions || [{ attribute_name: "Id", attribute_type: "S" }]
63
- ks = key_schema || [{ attribute_name: "Id", key_type: "HASH" }]
64
- table = @client.create_table({
65
- attribute_definitions: ad,
66
- table_name: table_name,
67
- key_schema: ks,
68
- provisioned_throughput: {read_capacity_units: read_capacity, write_capacity_units: write_capacity}
69
- })
70
- has_updates = true
71
- end
72
-
73
- if (has_updates)
74
- wait_until_table_available(table_name)
75
- puts "DynamoDB table #{table_name} is now fully available."
76
- end
77
-
78
- end
79
-
80
- ##
81
- # Deletes a DynamoDB table. It does not wait until the table has been deleted.
82
- #
83
- # *Arguments*
84
- # [table_name] The dynamoDB table name to delete.
85
- def delete_database(table_name)
86
-
87
- begin
88
- table_details = @client.describe_table(table_name: table_name).table
89
- wait_until_table_available(table_name) if (table_details.table_status != 'ACTIVE')
90
- @client.delete_table({table_name: table_name})
91
- rescue Aws::DynamoDB::Errors::ResourceNotFoundException
92
- puts 'Table did not exist. Nothing to delete.'
93
- end
94
-
95
- end
96
-
97
- ##
98
- # Awaits a given status of a table.
99
- #
100
- # *Arguments*
101
- # [table_name] The dynamoDB table name to watch until it reaches an active status.
102
- def wait_until_table_available(table_name)
103
-
104
- max_attempts = 24
105
- delay_between_attempts = 5
106
-
107
- # wait until the table has updated to being fully available
108
- # waiting for ~2min at most; an error will be thrown afterwards
109
- begin
110
- @client.wait_until(:table_exists, table_name: table_name) do |w|
111
- w.max_attempts = max_attempts
112
- w.delay = delay_between_attempts
113
- w.before_wait do |attempts, response|
114
- puts "Waiting until table becomes available. Attempt #{attempts}/#{max_attempts} with polling interval #{delay_between_attempts}."
115
- end
116
- end
117
- rescue Aws::Waiters::Errors::TooManyAttemptsError => e
118
- puts "Table #{table_name} did not become available after #{e.attempts} attempts. Try again later or inspect the AWS console."
119
- end
120
-
121
- end
122
-
123
- private :wait_until_table_available
18
+ def set_table_capacity(table_name, read_capacity, write_capacity)
19
+ puts "Updating new read/write capacity for table #{table_name}.
20
+ Read #{table_details.provisioned_throughput.read_capacity_units} ==> #{read_capacity}.
21
+ Write #{table_details.provisioned_throughput.write_capacity_units} ==> #{write_capacity}."
22
+ @client.update_table(
23
+ table_name: table_name,
24
+ provisioned_throughput: { read_capacity_units: read_capacity, write_capacity_units: write_capacity }
25
+ )
26
+ end
124
27
 
125
- end
28
+ ##
29
+ # Publishes the database and awaits until it is fully available. If the table already exists,
30
+ # it only adjusts the read and write
31
+ # capacities upwards (it doesn't downgrade them to avoid a production environment being impacted with
32
+ # a default setting of an automated script).
33
+ #
34
+ # *Arguments*
35
+ # [table_name] The table name of the dynamoDB to be created.
36
+ # [attribute_definitions] The dynamoDB attribute definitions to be used when the table is created.
37
+ # [key_schema] The dynamoDB key definitions to be used when the table is created.
38
+ # [read_capacity] The read capacity to configure for the dynamoDB table.
39
+ # [write_capacity] The write capacity to configure for the dynamoDB table.
40
+ def publish_database(table_name, attribute_definitions, key_schema, read_capacity, write_capacity)
41
+ has_updates = false
126
42
 
127
- end
43
+ # figure out whether the table exists
44
+ begin
45
+ table_details = @client.describe_table(table_name: table_name).table
46
+ rescue Aws::DynamoDB::Errors::ResourceNotFoundException
47
+ # skip this exception because we are using it for control flow.
48
+ table_details = nil
49
+ end
50
+
51
+ if table_details
52
+ wait_until_table_available(table_name) if table_details.table_status != 'ACTIVE'
53
+
54
+ if read_capacity > table_details.provisioned_throughput.read_capacity_units ||
55
+ write_capacity > table_details.provisioned_throughput.write_capacity_units
56
+
57
+ set_table_capacity read_capacity, write_capacity
58
+ has_updates = true
59
+ else
60
+ puts "Table #{table_name} already exists and the desired read capacity of #{read_capacity} and " \
61
+ "write capacity of #{write_capacity} has at least been configured. Downgrading capacity units is not " \
62
+ 'supported. No changes were applied.'
63
+ end
64
+ else
65
+ puts "Creating table #{table_name}."
66
+ ad = attribute_definitions || [{ attribute_name: 'Id', attribute_type: 'S' }]
67
+ ks = key_schema || [{ attribute_name: 'Id', key_type: 'HASH' }]
68
+ @client.create_table(table_name: table_name, key_schema: ks, attribute_definitions: ad,
69
+ provisioned_throughput:
70
+ { read_capacity_units: read_capacity, write_capacity_units: write_capacity })
71
+ has_updates = true
72
+ end
73
+
74
+ if has_updates
75
+ wait_until_table_available(table_name)
76
+ puts "DynamoDB table #{table_name} is now fully available."
77
+ end
78
+ end
79
+
80
+ ##
81
+ # Deletes a DynamoDB table. It does not wait until the table has been deleted.
82
+ #
83
+ # *Arguments*
84
+ # [table_name] The dynamoDB table name to delete.
85
+ def delete_database(table_name)
86
+ table_details = @client.describe_table(table_name: table_name).table
87
+ wait_until_table_available(table_name) if table_details.table_status != 'ACTIVE'
88
+ @client.delete_table(table_name: table_name)
89
+ rescue Aws::DynamoDB::Errors::ResourceNotFoundException
90
+ puts 'Table did not exist. Nothing to delete.'
91
+ end
92
+
93
+ ##
94
+ # Awaits a given status of a table.
95
+ #
96
+ # *Arguments*
97
+ # [table_name] The dynamoDB table name to watch until it reaches an active status.
98
+ def wait_until_table_available(table_name)
99
+ max_attempts = 24
100
+ delay_between_attempts = 5
101
+
102
+ # wait until the table has updated to being fully available
103
+ # waiting for ~2min at most; an error will be thrown afterwards
104
+ begin
105
+ @client.wait_until(:table_exists, table_name: table_name) do |w|
106
+ w.max_attempts = max_attempts
107
+ w.delay = delay_between_attempts
108
+ w.before_wait do |attempts, _|
109
+ puts "Waiting until table becomes available. Attempt #{attempts}/#{max_attempts} " \
110
+ "with polling interval #{delay_between_attempts}."
111
+ end
112
+ end
113
+ rescue Aws::Waiters::Errors::TooManyAttemptsError => e
114
+ puts "Table #{table_name} did not become available after #{e.attempts} attempts. " \
115
+ 'Try again later or inspect the AWS console.'
116
+ end
117
+ end
118
+
119
+ private :wait_until_table_available
120
+ end
121
+ end
@@ -1,172 +1,177 @@
1
1
  require 'aws-sdk'
2
- require_relative 'aws_setup'
3
2
 
4
3
  module LambdaWrap
5
-
6
- ##
7
- # The LambdaManager simplifies creating a package, publishing it to S3, deploying a new version, and setting permissions.
8
- #
9
- # Note: The concept of an environment of the LambdaWrap gem matches an alias of AWS Lambda.
10
- class LambdaManager
11
-
12
- ##
13
- # The constructor does some basic setup
14
- # * Validating basic AWS configuration
15
- # * Creating the underlying client to interace with the AWS SDK
16
- def initialize()
17
- AwsSetup.new.validate()
18
- # AWS lambda client
19
- @client = Aws::Lambda::Client.new()
20
- end
21
-
22
- ##
23
- # Packages a set of files and node modules into a deployable package.
24
- #
25
- # *Arguments*
26
- # [directory] A temporary directory to copy all related files before they are packages into a single zip file.
27
- # [zipfile] A path where the deployable package, a zip file, should be stored.
28
- # [input_filenames] A list of file names that contain the source code.
29
- # [node_modules] A list of node modules that need to be included in the package.
30
- def package(directory, zipfile, input_filenames, node_modules)
31
-
32
- FileUtils::mkdir_p directory
33
- FileUtils::mkdir_p File.join(directory, 'node_modules')
34
-
35
- input_filenames.each do |filename|
36
- FileUtils::copy_file(File.join(filename), File.join(directory, File.basename(filename)))
37
- end
38
-
39
- node_modules.each do |dir|
40
- FileUtils::cp_r(File.join('node_modules', dir), File.join(directory, 'node_modules'))
41
- end
42
-
43
- ZipFileGenerator.new(directory, zipfile).write
44
-
45
- end
46
-
47
- ##
48
- # Publishes a package to S3 so it can be deployed as a lambda function.
49
- #
50
- # *Arguments*
51
- # [local_lambda_file] The location of the package that needs to be deployed.
52
- # [bucket] The s3 bucket where the file needs to be uploaded to.
53
- # [key] The S3 path (key) where the package should be stored.
54
- def publish_lambda_to_s3(local_lambda_file, bucket, key)
55
-
56
- # get s3 object
57
- s3 = Aws::S3::Resource.new()
58
- obj = s3.bucket(bucket).object(key)
59
-
60
- # upload
61
- version_id = nil
62
- File.open(local_lambda_file, 'rb') do |file|
63
- version_id = obj.put({body: file}).version_id
64
- end
65
- raise 'Upload to S3 failed' if !version_id
66
-
67
- puts 'Uploaded object to S3 with version ' + version_id
68
- return version_id
69
-
70
- end
71
-
72
- ##
73
- # Deploys a package that has been uploaded to S3.
74
- #
75
- # *Arguments*
76
- # [bucket] The S3 bucket where the package can be retrieved from.
77
- # [key] The S3 path (key) where the package can be retrieved from.
78
- # [version_id] The version of the file on S3 to retrieve.
79
- # [function_name] The name of the lambda function.
80
- # [handler] The handler that should be executed for this lambda function.
81
- # [lambda_role] The arn of the IAM role that should be used when executing the lambda function.
82
- # [lambda_description] The description of the lambda function.
83
- # [vpc_subnet_ids] A list of subnet ids for the lambda's VPC configuration. All of the subnets must be on the same VPC.
84
- # [vpc_security_group_ids] A list of security group ids for the lambda's VPC configuration. All of the security_group_ids must be on the same VPC.
85
- def deploy_lambda(bucket, key, version_id, function_name, handler, lambda_role, lambda_description = "Deployed with LambdaWrap", vpc_subnet_ids = [], vpc_security_group_ids = [])
86
-
87
- # create or update function
88
-
89
- begin
90
- func = @client.get_function({function_name: function_name})
91
- func_config = @client.update_function_code({function_name: function_name, s3_bucket: bucket, s3_key: key, s3_object_version: version_id, publish: true}).data
92
- puts func_config
93
- func_version = func_config.version
94
- raise 'Error while publishing existing lambda function ' + function_name if !func_config.version
95
- rescue Aws::Lambda::Errors::ResourceNotFoundException
96
- func_config = @client.create_function({function_name: function_name, runtime: 'nodejs4.3', role: lambda_role, handler: handler, code: { s3_bucket: bucket, s3_key: key }, timeout: 5, memory_size: 128, publish: true, description: lambda_description, vpc_config: {subnet_ids: vpc_subnet_ids, security_group_ids: vpc_security_group_ids} }).data
97
- puts func_config
98
- func_version = func_config.version
99
- raise 'Error while publishing new lambda function ' + function_name if !func_config.version
100
- end
101
-
102
- add_api_gateway_permissions(function_name, nil)
103
-
104
- return func_version
105
-
106
- end
107
-
108
- ##
109
- # Creates an alias for a given lambda function version.
110
- #
111
- # *Arguments*
112
- # [function_name] The lambda function name for which the alias should be created.
113
- # [func_version] The lambda function versino to which the alias should point.
114
- # [alias_name] The name of the alias, matching the LambdaWrap environment concept.
115
- def create_alias(function_name, func_version, alias_name)
116
-
117
- # create or update alias
118
- func_alias = @client.list_aliases({function_name: function_name}).aliases.select{ |a| a.name == alias_name }.first()
119
- if (!func_alias)
120
- a = @client.create_alias({function_name: function_name, name: alias_name, function_version: func_version, description: 'created by an automated script'}).data
121
- puts a
122
- else
123
- a = @client.update_alias({function_name: function_name, name: alias_name, function_version: func_version, description: 'updated by an automated script'}).data
124
- puts a
125
- end
126
-
127
- add_api_gateway_permissions(function_name, alias_name)
128
-
129
- end
130
-
131
- ##
132
- # Removes an alias for a function.
133
- #
134
- # *Arguments*
135
- # [function_name] The lambda function name for which the alias should be removed.
136
- # [alias_name] The alias to remove.
137
- def remove_alias(function_name, alias_name)
138
-
139
- @client.delete_alias({function_name: function_name, name: alias_name})
140
-
141
- end
142
-
143
- ##
144
- # Adds permissions for API gateway to execute this function.
145
- #
146
- # *Arguments*
147
- # [function_name] The function name which needs to be executed from API Gateway.
148
- # [env] The environment (matching the function's alias) which needs to be executed from API Gateway. If nil, the permissions are set of the $LATEST version.
149
- def add_api_gateway_permissions(function_name, env)
150
- # permissions to execute lambda
151
- suffix = (':' + env if env) || ''
152
- func = @client.get_function({function_name: function_name + suffix}).data.configuration
153
- statement_id = func.function_name + (('-' + env if env) || '')
154
- policy_exists = false
155
- begin
156
- existing_policies = @client.get_policy({function_name: func.function_arn}).data
157
- existing_policy = JSON.parse(existing_policies.policy)
158
- policy_exists = existing_policy['Statement'].select{ |s| s['Sid'] == statement_id}.any?
159
- rescue Aws::Lambda::Errors::ResourceNotFoundException
160
- end
161
-
162
- if !policy_exists
163
- perm_add = @client.add_permission({function_name: func.function_arn, statement_id: statement_id, action: 'lambda:*', principal: 'apigateway.amazonaws.com'})
164
- puts perm_add.data
165
- end
166
- end
167
-
168
- private :add_api_gateway_permissions
169
-
170
- end
171
-
172
- end
4
+ ##
5
+ # The LambdaManager simplifies creating a package, publishing to S3, deploying a new version, & setting permissions.
6
+ #
7
+ # Note: The concept of an environment of the LambdaWrap gem matches an alias of AWS Lambda.
8
+ class LambdaManager
9
+ ##
10
+ # The constructor does some basic setup
11
+ # * Validating basic AWS configuration
12
+ # * Creating the underlying client to interace with the AWS SDK
13
+ def initialize
14
+ # AWS lambda client
15
+ @client = Aws::Lambda::Client.new
16
+ end
17
+
18
+ ##
19
+ # Packages a set of files and node modules into a deployable package.
20
+ #
21
+ # *Arguments*
22
+ # [directory] A temporary directory to copy all related files before they are packages into a single zip file.
23
+ # [zipfile] A path where the deployable package, a zip file, should be stored.
24
+ # [input_filenames] A list of file names that contain the source code.
25
+ # [node_modules] A list of node modules that need to be included in the package.
26
+ def package(directory, zipfile, input_filenames, node_modules)
27
+ FileUtils.mkdir_p directory
28
+ FileUtils.mkdir_p File.join(directory, 'node_modules')
29
+
30
+ input_filenames.each do |filename|
31
+ FileUtils.copy_file(File.join(filename), File.join(directory, File.basename(filename)))
32
+ end
33
+
34
+ node_modules.each do |dir|
35
+ FileUtils.cp_r(File.join('node_modules', dir), File.join(directory, 'node_modules'))
36
+ end
37
+
38
+ ZipFileGenerator.new(directory, zipfile).write
39
+ end
40
+
41
+ ##
42
+ # Publishes a package to S3 so it can be deployed as a lambda function.
43
+ #
44
+ # *Arguments*
45
+ # [local_lambda_file] The location of the package that needs to be deployed.
46
+ # [bucket] The s3 bucket where the file needs to be uploaded to.
47
+ # [key] The S3 path (key) where the package should be stored.
48
+ def publish_lambda_to_s3(local_lambda_file, bucket, key)
49
+ # get s3 object
50
+ s3 = Aws::S3::Resource.new
51
+ obj = s3.bucket(bucket).object(key)
52
+
53
+ # upload
54
+ version_id = nil
55
+ File.open(local_lambda_file, 'rb') do |file|
56
+ version_id = obj.put(body: file).version_id
57
+ end
58
+ raise 'Upload to S3 failed' unless version_id
59
+
60
+ puts 'Uploaded object to S3 with version ' + version_id
61
+ version_id
62
+ end
63
+
64
+ ##
65
+ # Deploys a package that has been uploaded to S3.
66
+ #
67
+ # *Arguments*
68
+ # [bucket] The S3 bucket where the package can be retrieved from.
69
+ # [key] The S3 path (key) where the package can be retrieved from.
70
+ # [version_id] The version of the file on S3 to retrieve.
71
+ # [function_name] The name of the lambda function.
72
+ # [handler] The handler that should be executed for this lambda function.
73
+ # [lambda_role] The arn of the IAM role that should be used when executing the lambda function.
74
+ # [lambda_description] The description of the lambda function.
75
+ # [vpc_subnet_ids] A list of subnet ids for the lambda's VPC configuration. All subnets must be on the same VPC.
76
+ # [vpc_security_group_ids] A list of security group ids for the lambda's VPC configuration. All of the
77
+ # security_group_ids must be on the same VPC.
78
+ def deploy_lambda(
79
+ bucket, key, version_id, function_name, handler, lambda_role,
80
+ lambda_description = 'Deployed with LambdaWrap', vpc_subnet_ids = [], vpc_security_group_ids = []
81
+ )
82
+ # create or update function
83
+
84
+ begin
85
+ @client.get_function(function_name: function_name)
86
+ func_config = @client.update_function_code(function_name: function_name, s3_bucket: bucket, s3_key: key,
87
+ s3_object_version: version_id, publish: true).data
88
+ puts func_config
89
+ func_version = func_config.version
90
+ raise 'Error while publishing existing lambda function ' + function_name unless func_version
91
+ rescue Aws::Lambda::Errors::ResourceNotFoundException
92
+ # if we cannot find it, we have to create it instead of updating it
93
+ func_config = @client.create_function(
94
+ function_name: function_name, runtime: 'nodejs4.3', role: lambda_role,
95
+ handler: handler, code: { s3_bucket: bucket, s3_key: key }, timeout: 5, memory_size: 128, publish: true,
96
+ description: lambda_description,
97
+ vpc_config: { subnet_ids: vpc_subnet_ids, security_group_ids: vpc_security_group_ids }
98
+ ).data
99
+ puts func_config
100
+ func_version = func_config.version
101
+ raise "Error while publishing new lambda function #{function_name}" unless func_version
102
+ end
103
+
104
+ add_api_gateway_permissions(function_name, nil)
105
+
106
+ func_version
107
+ end
108
+
109
+ ##
110
+ # Creates an alias for a given lambda function version.
111
+ #
112
+ # *Arguments*
113
+ # [function_name] The lambda function name for which the alias should be created.
114
+ # [func_version] The lambda function versino to which the alias should point.
115
+ # [alias_name] The name of the alias, matching the LambdaWrap environment concept.
116
+ def create_alias(function_name, func_version, alias_name)
117
+ # create or update alias
118
+ func_alias = @client.list_aliases(function_name: function_name).aliases.select { |a| a.name == alias_name }.first
119
+ if !func_alias
120
+ a = @client.create_alias(
121
+ function_name: function_name, name: alias_name, function_version: func_version,
122
+ description: 'created by an automated script'
123
+ ).data
124
+ else
125
+ a = @client.update_alias(
126
+ function_name: function_name, name: alias_name, function_version: func_version,
127
+ description: 'updated by an automated script'
128
+ ).data
129
+ end
130
+ puts a
131
+
132
+ add_api_gateway_permissions(function_name, alias_name)
133
+ end
134
+
135
+ ##
136
+ # Removes an alias for a function.
137
+ #
138
+ # *Arguments*
139
+ # [function_name] The lambda function name for which the alias should be removed.
140
+ # [alias_name] The alias to remove.
141
+ def remove_alias(function_name, alias_name)
142
+ @client.delete_alias(function_name: function_name, name: alias_name)
143
+ end
144
+
145
+ ##
146
+ # Adds permissions for API gateway to execute this function.
147
+ #
148
+ # *Arguments*
149
+ # [function_name] The function name which needs to be executed from API Gateway.
150
+ # [env] The environment (matching the function's alias) which needs to be executed from API Gateway.
151
+ # => If nil, the permissions are set of the $LATEST version.
152
+ def add_api_gateway_permissions(function_name, env)
153
+ # permissions to execute lambda
154
+ suffix = (':' + env if env) || ''
155
+ func = @client.get_function(function_name: function_name + suffix).data.configuration
156
+ statement_id = func.function_name + (('-' + env if env) || '')
157
+ begin
158
+ existing_policies = @client.get_policy(function_name: func.function_arn).data
159
+ existing_policy = JSON.parse(existing_policies.policy)
160
+ policy_exists = existing_policy['Statement'].select { |s| s['Sid'] == statement_id }.any?
161
+ rescue Aws::Lambda::Errors::ResourceNotFoundException
162
+ # policy does not exist, and that is ok
163
+ policy_exists = false
164
+ end
165
+
166
+ unless policy_exists
167
+ perm_add = @client.add_permission(
168
+ function_name: func.function_arn, statement_id: statement_id,
169
+ action: 'lambda:*', principal: 'apigateway.amazonaws.com'
170
+ )
171
+ puts perm_add.data
172
+ end
173
+ end
174
+
175
+ private :add_api_gateway_permissions
176
+ end
177
+ end
@@ -2,69 +2,66 @@ require 'rubygems'
2
2
  require 'zip'
3
3
 
4
4
  module LambdaWrap
5
-
5
+ ##
6
+ # Allows to easily zip a directory recursively. It's intended for gem internal use only.
7
+ #
8
+ # From the original example:
9
+ # This is a simple example which uses rubyzip to
10
+ # recursively generate a zip file from the contents of
11
+ # a specified directory. The directory itself is not
12
+ # included in the archive, rather just its contents.
13
+ #
14
+ # Usage:
15
+ # require /path/to/the/ZipFileGenerator/Class
16
+ # directoryToZip = "/tmp/input"
17
+ # outputFile = "/tmp/out.zip"
18
+ # zf = ZipFileGenerator.new(directoryToZip, outputFile)
19
+ # zf.write()
20
+ class ZipFileGenerator
6
21
  ##
7
- # Allows to easily zip a directory recursively. It's intended for gem internal use only.
8
- #
9
- # From the original example:
10
- # This is a simple example which uses rubyzip to
11
- # recursively generate a zip file from the contents of
12
- # a specified directory. The directory itself is not
13
- # included in the archive, rather just its contents.
14
- #
15
- # Usage:
16
- # require /path/to/the/ZipFileGenerator/Class
17
- # directoryToZip = "/tmp/input"
18
- # outputFile = "/tmp/out.zip"
19
- # zf = ZipFileGenerator.new(directoryToZip, outputFile)
20
- # zf.write()
21
- class ZipFileGenerator
22
-
23
- ##
24
- # Initialize with the directory to zip and the location of the output archive.
25
- def initialize(input_dir, output_file)
26
- @input_dir = input_dir
27
- @output_file = output_file
28
- end
29
-
30
- ##
31
- # Zip the input directory.
32
- def write
33
- entries = Dir.entries(@input_dir) - %w(. ..)
22
+ # Initialize with the directory to zip and the location of the output archive.
23
+ def initialize(input_dir, output_file)
24
+ @input_dir = input_dir
25
+ @output_file = output_file
26
+ end
34
27
 
35
- ::Zip::File.open(@output_file, ::Zip::File::CREATE) do |io|
36
- write_entries entries, '', io
37
- end
38
- end
28
+ ##
29
+ # Zip the input directory.
30
+ def write
31
+ entries = Dir.entries(@input_dir) - %w(. ..)
39
32
 
40
- private
33
+ Zip::File.open(@output_file, Zip::File::CREATE) do |io|
34
+ write_entries entries, '', io
35
+ end
36
+ end
41
37
 
42
- # A helper method to make the recursion work.
43
- def write_entries(entries, path, io)
44
- entries.each do |e|
45
- zip_file_path = path == '' ? e : File.join(path, e)
46
- disk_file_path = File.join(@input_dir, zip_file_path)
47
- puts "Deflating #{disk_file_path}"
38
+ private
48
39
 
49
- if File.directory? disk_file_path
50
- recursively_deflate_directory(disk_file_path, io, zip_file_path)
51
- else
52
- put_into_archive(disk_file_path, io, zip_file_path)
53
- end
54
- end
55
- end
40
+ # A helper method to make the recursion work.
41
+ def write_entries(entries, path, io)
42
+ entries.each do |e|
43
+ zip_file_path = path == '' ? e : File.join(path, e)
44
+ disk_file_path = File.join(@input_dir, zip_file_path)
45
+ puts "Deflating #{disk_file_path}"
56
46
 
57
- def recursively_deflate_directory(disk_file_path, io, zip_file_path)
58
- io.mkdir zip_file_path
59
- subdir = Dir.entries(disk_file_path) - %w(. ..)
60
- write_entries subdir, zip_file_path, io
47
+ if File.directory? disk_file_path
48
+ recursively_deflate_directory(disk_file_path, io, zip_file_path)
49
+ else
50
+ put_into_archive(disk_file_path, io, zip_file_path)
61
51
  end
52
+ end
53
+ end
62
54
 
63
- def put_into_archive(disk_file_path, io, zip_file_path)
64
- io.get_output_stream(zip_file_path) do |f|
65
- f.puts(File.open(disk_file_path, 'rb').read)
66
- end
67
- end
55
+ def recursively_deflate_directory(disk_file_path, io, zip_file_path)
56
+ io.mkdir zip_file_path
57
+ subdir = Dir.entries(disk_file_path) - %w(. ..)
58
+ write_entries subdir, zip_file_path, io
68
59
  end
69
60
 
70
- end
61
+ def put_into_archive(disk_file_path, io, zip_file_path)
62
+ io.get_output_stream(zip_file_path) do |f|
63
+ f.puts(File.open(disk_file_path, 'rb').read)
64
+ end
65
+ end
66
+ end
67
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lambda_wrap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Markus Thurner
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2016-06-13 00:00:00.000000000 Z
13
+ date: 2016-06-29 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: aws-sdk
@@ -27,20 +27,20 @@ dependencies:
27
27
  - !ruby/object:Gem::Version
28
28
  version: '2'
29
29
  - !ruby/object:Gem::Dependency
30
- name: zip
30
+ name: rubyzip
31
31
  requirement: !ruby/object:Gem::Requirement
32
32
  requirements:
33
33
  - - "~>"
34
34
  - !ruby/object:Gem::Version
35
- version: '2'
35
+ version: '1.2'
36
36
  type: :runtime
37
37
  prerelease: false
38
38
  version_requirements: !ruby/object:Gem::Requirement
39
39
  requirements:
40
40
  - - "~>"
41
41
  - !ruby/object:Gem::Version
42
- version: '2'
43
- description: This gem wraps the AWS SDK to simplify deployment of AWS Lambda functions
42
+ version: '1.2'
43
+ description: This gem wraps the AWS SDK to simplify deployment of AWSLambda functions
44
44
  backed by API Gateway and DynamoDB.
45
45
  email:
46
46
  executables: []
@@ -49,7 +49,6 @@ extra_rdoc_files: []
49
49
  files:
50
50
  - lib/lambda_wrap.rb
51
51
  - lib/lambda_wrap/api_gateway_manager.rb
52
- - lib/lambda_wrap/aws_setup.rb
53
52
  - lib/lambda_wrap/dynamo_db_manager.rb
54
53
  - lib/lambda_wrap/lambda_manager.rb
55
54
  - lib/lambda_wrap/zip_file_generator.rb
@@ -73,7 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
73
72
  version: '0'
74
73
  requirements: []
75
74
  rubyforge_project:
76
- rubygems_version: 2.4.7
75
+ rubygems_version: 2.2.5
77
76
  signing_key:
78
77
  specification_version: 4
79
78
  summary: Easy deployment of AWS Lambda functions and dependencies.
@@ -1,32 +0,0 @@
1
- require 'aws-sdk'
2
-
3
- ##
4
- # LambdaWrap is a ruby gem that simplifies deployment of AWS Lambda functions that are invoked through AWS API Gateway and optionally include a DynamoDB backend.
5
- module LambdaWrap
6
-
7
- ##
8
- # Helper class to ensure valid configuration of AWS.
9
- # This class is intended for internal use only, but clients can call it before calling any other functionality to ensure early failure.
10
- class AwsSetup
11
-
12
- ##
13
- # Validates that the setup is correct and that the bundled AWS certificate is used for subsequent calls to the AWS SDK
14
- #
15
- # *Required environment variables*
16
- # [AWS_ACCESS_KEY_ID] The AWS access key to be used when calling AWS SDK functions
17
- # [AWS_SECRET_ACCESS_KEY] The AWS secret to be used when calling AWS SDK functions
18
- # [AWS_REGION] A valid AWS region to use during configuration.
19
- def validate()
20
-
21
- # validate settings
22
- raise 'AWS_ACCESS_KEY_ID not set' if !ENV['AWS_ACCESS_KEY_ID']
23
- raise 'AWS_SECRET_ACCESS_KEY not set' if !ENV['AWS_SECRET_ACCESS_KEY']
24
- raise 'AWS_REGION not set' if !ENV['AWS_REGION']
25
-
26
- Aws::use_bundled_cert!
27
-
28
- end
29
-
30
- end
31
-
32
- end