lambda_wrap 0.14.0 → 0.15.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 +4 -4
- data/lib/lambda_wrap.rb +5 -5
- data/lib/lambda_wrap/api_gateway_manager.rb +158 -155
- data/lib/lambda_wrap/dynamo_db_manager.rb +121 -121
- data/lib/lambda_wrap/lambda_manager.rb +181 -181
- data/lib/lambda_wrap/s3_bucket_manager.rb +1 -1
- data/lib/lambda_wrap/zip_file_generator.rb +67 -67
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 79ae4cfdc2ed44b009d04a29372cb322ed040130
|
4
|
+
data.tar.gz: ebda47ad69d8a654f692889ef1244e97322edd96
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5be6e9c59c7d93797b868e12dba43aa2f44d2e4e000c8da59edb222c36e17b1ece6cd830b0a03bf5a36962ee0eeea63cd31c72f25da1bcc6c3380ebba284a31b
|
7
|
+
data.tar.gz: b36aeade9223a9dac371dfc544a998dbee00a1c5ef0456aa51a84bc9b39e1704b970aef166ac9a645d4d9e03cf0304094697b6ea2e469b3b57da15979e09d50d
|
data/lib/lambda_wrap.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
# :nodoc:
|
2
|
-
Dir["#{File.expand_path(File.dirname(__FILE__))}/**/*.rb"].each { |f| require f }
|
3
|
-
|
4
|
-
STDOUT.sync = true
|
5
|
-
STDERR.sync = true
|
1
|
+
# :nodoc:
|
2
|
+
Dir["#{File.expand_path(File.dirname(__FILE__))}/**/*.rb"].each { |f| require f }
|
3
|
+
|
4
|
+
STDOUT.sync = true
|
5
|
+
STDERR.sync = true
|
@@ -1,155 +1,158 @@
|
|
1
|
-
require 'aws-sdk'
|
2
|
-
|
3
|
-
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
|
-
|
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
|
-
# [stage_variables] A Hash of stage variables to be deployed with the stage. Adds an 'environment' by default.
|
58
|
-
# [region] The region to deploy the API. Defaults to what is set as an environment variable.
|
59
|
-
def setup_apigateway(api_name, env, swagger_file, api_description = 'Deployed with LambdaWrap',
|
60
|
-
stage_variables = {}, region = ENV['AWS_REGION'])
|
61
|
-
# ensure API is created
|
62
|
-
api_id = get_existing_rest_api(api_name)
|
63
|
-
api_id = setup_apigateway_create_rest_api(api_name, api_description) unless api_id
|
64
|
-
|
65
|
-
# create resources
|
66
|
-
setup_apigateway_create_resources(api_id, swagger_file, region)
|
67
|
-
|
68
|
-
# create stages
|
69
|
-
stage_variables.store('environment', env)
|
70
|
-
create_stages(api_id, env, stage_variables)
|
71
|
-
|
72
|
-
# return URI of created stage
|
73
|
-
"https://#{api_id}.execute-api.#{region}.amazonaws.com/#{env}/"
|
74
|
-
end
|
75
|
-
|
76
|
-
##
|
77
|
-
# Shuts down an environment from the API Gateway. This basically deletes the stage
|
78
|
-
# from the API Gateway, but does not delete the API Gateway itself.
|
79
|
-
#
|
80
|
-
# *Argument*
|
81
|
-
# [api_name] The name of the API where the environment should be shut down.
|
82
|
-
# [env] The environment (matching an API Gateway stage) to shutdown.
|
83
|
-
def shutdown_apigateway(api_name, env)
|
84
|
-
api_id = get_existing_rest_api(api_name)
|
85
|
-
delete_stage(api_id, env)
|
86
|
-
end
|
87
|
-
|
88
|
-
##
|
89
|
-
# Gets the ID of an existing API Gateway api, or nil if it doesn't exist
|
90
|
-
#
|
91
|
-
# *Arguments*
|
92
|
-
# [api_name] The name of the API to be checked for existance
|
93
|
-
def get_existing_rest_api(api_name)
|
94
|
-
apis = @client.get_rest_apis(limit: 500).data
|
95
|
-
api = apis.items.select { |a| a.name == api_name }.first
|
96
|
-
|
97
|
-
return api.id if api
|
98
|
-
# nil is returned otherwise
|
99
|
-
end
|
100
|
-
|
101
|
-
##
|
102
|
-
# Creates the API with a given name and returns the id
|
103
|
-
#
|
104
|
-
# *Arguments*
|
105
|
-
# [api_name] The name of the API to be created
|
106
|
-
def setup_apigateway_create_rest_api(api_name, api_description)
|
107
|
-
puts 'Creating API with name ' + api_name
|
108
|
-
api = @client.create_rest_api(name: api_name, description: api_description)
|
109
|
-
api.id
|
110
|
-
end
|
111
|
-
|
112
|
-
##
|
113
|
-
# Invokes the aws-apigateway-importer jar with the required parameter
|
114
|
-
#
|
115
|
-
# *Arguments*
|
116
|
-
# [api_id] The AWS ApiGateway id where the swagger file should be applied to.
|
117
|
-
# [swagger_file] The handle to a swagger definition file that should be imported into API Gateway
|
118
|
-
def setup_apigateway_create_resources(api_id, swagger_file, region)
|
119
|
-
raise 'API ID not provided' unless api_id
|
120
|
-
|
121
|
-
cmd = "java -jar #{@jarpath} --update #{api_id} --region #{region} #{swagger_file}"
|
122
|
-
raise 'API gateway not created' unless system(cmd)
|
123
|
-
end
|
124
|
-
|
125
|
-
##
|
126
|
-
# Creates a stage of the currently set resources
|
127
|
-
#
|
128
|
-
# *Arguments*
|
129
|
-
# [api_id] The AWS ApiGateway id where the stage should be created at.
|
130
|
-
# [env] The environment (which matches the stage in API Gateway) to create.
|
131
|
-
def create_stages(api_id, env, stage_variables)
|
132
|
-
deployment_description = 'Deployment of service to ' + env
|
133
|
-
deployment = @client.create_deployment(
|
134
|
-
rest_api_id: api_id, stage_name: env, cache_cluster_enabled: false, description: deployment_description,
|
135
|
-
variables: stage_variables
|
136
|
-
).data
|
137
|
-
puts deployment
|
138
|
-
end
|
139
|
-
|
140
|
-
##
|
141
|
-
# Deletes a stage of the API Gateway
|
142
|
-
#
|
143
|
-
# *Arguments*
|
144
|
-
# [api_id] The AWS ApiGateway id from which the stage should be deleted from.
|
145
|
-
# [env] The environment (which matches the stage in API Gateway) to delete.
|
146
|
-
def delete_stage(
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
1
|
+
require 'aws-sdk'
|
2
|
+
|
3
|
+
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
|
+
|
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
|
+
# [stage_variables] A Hash of stage variables to be deployed with the stage. Adds an 'environment' by default.
|
58
|
+
# [region] The region to deploy the API. Defaults to what is set as an environment variable.
|
59
|
+
def setup_apigateway(api_name, env, swagger_file, api_description = 'Deployed with LambdaWrap',
|
60
|
+
stage_variables = {}, region = ENV['AWS_REGION'])
|
61
|
+
# ensure API is created
|
62
|
+
api_id = get_existing_rest_api(api_name)
|
63
|
+
api_id = setup_apigateway_create_rest_api(api_name, api_description) unless api_id
|
64
|
+
|
65
|
+
# create resources
|
66
|
+
setup_apigateway_create_resources(api_id, swagger_file, region)
|
67
|
+
|
68
|
+
# create stages
|
69
|
+
stage_variables.store('environment', env)
|
70
|
+
create_stages(api_id, env, stage_variables)
|
71
|
+
|
72
|
+
# return URI of created stage
|
73
|
+
"https://#{api_id}.execute-api.#{region}.amazonaws.com/#{env}/"
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# Shuts down an environment from the API Gateway. This basically deletes the stage
|
78
|
+
# from the API Gateway, but does not delete the API Gateway itself.
|
79
|
+
#
|
80
|
+
# *Argument*
|
81
|
+
# [api_name] The name of the API where the environment should be shut down.
|
82
|
+
# [env] The environment (matching an API Gateway stage) to shutdown.
|
83
|
+
def shutdown_apigateway(api_name, env)
|
84
|
+
api_id = get_existing_rest_api(api_name)
|
85
|
+
delete_stage(api_id, env)
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Gets the ID of an existing API Gateway api, or nil if it doesn't exist
|
90
|
+
#
|
91
|
+
# *Arguments*
|
92
|
+
# [api_name] The name of the API to be checked for existance
|
93
|
+
def get_existing_rest_api(api_name)
|
94
|
+
apis = @client.get_rest_apis(limit: 500).data
|
95
|
+
api = apis.items.select { |a| a.name == api_name }.first
|
96
|
+
|
97
|
+
return api.id if api
|
98
|
+
# nil is returned otherwise
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# Creates the API with a given name and returns the id
|
103
|
+
#
|
104
|
+
# *Arguments*
|
105
|
+
# [api_name] The name of the API to be created
|
106
|
+
def setup_apigateway_create_rest_api(api_name, api_description)
|
107
|
+
puts 'Creating API with name ' + api_name
|
108
|
+
api = @client.create_rest_api(name: api_name, description: api_description)
|
109
|
+
api.id
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Invokes the aws-apigateway-importer jar with the required parameter
|
114
|
+
#
|
115
|
+
# *Arguments*
|
116
|
+
# [api_id] The AWS ApiGateway id where the swagger file should be applied to.
|
117
|
+
# [swagger_file] The handle to a swagger definition file that should be imported into API Gateway
|
118
|
+
def setup_apigateway_create_resources(api_id, swagger_file, region)
|
119
|
+
raise 'API ID not provided' unless api_id
|
120
|
+
|
121
|
+
cmd = "java -jar #{@jarpath} --update #{api_id} --region #{region} #{swagger_file}"
|
122
|
+
raise 'API gateway not created' unless system(cmd)
|
123
|
+
end
|
124
|
+
|
125
|
+
##
|
126
|
+
# Creates a stage of the currently set resources
|
127
|
+
#
|
128
|
+
# *Arguments*
|
129
|
+
# [api_id] The AWS ApiGateway id where the stage should be created at.
|
130
|
+
# [env] The environment (which matches the stage in API Gateway) to create.
|
131
|
+
def create_stages(api_id, env, stage_variables)
|
132
|
+
deployment_description = 'Deployment of service to ' + env
|
133
|
+
deployment = @client.create_deployment(
|
134
|
+
rest_api_id: api_id, stage_name: env, cache_cluster_enabled: false, description: deployment_description,
|
135
|
+
variables: stage_variables
|
136
|
+
).data
|
137
|
+
puts deployment
|
138
|
+
end
|
139
|
+
|
140
|
+
##
|
141
|
+
# Deletes a stage of the API Gateway
|
142
|
+
#
|
143
|
+
# *Arguments*
|
144
|
+
# [api_id] The AWS ApiGateway id from which the stage should be deleted from.
|
145
|
+
# [env] The environment (which matches the stage in API Gateway) to delete.
|
146
|
+
def delete_stage(api_id, env)
|
147
|
+
begin
|
148
|
+
@client.delete_stage({rest_api_id: api_id, stage_name: env})
|
149
|
+
puts 'Deleted API gateway stage ' + env
|
150
|
+
rescue Aws::APIGateway::Errors::NotFoundException
|
151
|
+
puts 'API Gateway stage ' + env + ' does not exist. Nothing to delete.'
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
private :get_existing_rest_api, :setup_apigateway_create_rest_api, :setup_apigateway_create_resources,
|
156
|
+
:create_stages, :delete_stage
|
157
|
+
end
|
158
|
+
end
|
@@ -1,121 +1,121 @@
|
|
1
|
-
require 'aws-sdk'
|
2
|
-
|
3
|
-
module LambdaWrap
|
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
|
17
|
-
|
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
|
27
|
-
|
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
|
42
|
-
|
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
|
+
require 'aws-sdk'
|
2
|
+
|
3
|
+
module LambdaWrap
|
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
|
17
|
+
|
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
|
27
|
+
|
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
|
42
|
+
|
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,181 +1,181 @@
|
|
1
|
-
require 'aws-sdk'
|
2
|
-
|
3
|
-
module LambdaWrap
|
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
|
-
# check if vpc_subnet_ids and vpc_security_group_ids are empty or not and set the vpc_config accordingly.
|
93
|
-
vpc_Configuration = nil
|
94
|
-
vpc_Configuration = { subnet_ids: vpc_subnet_ids, security_group_ids: vpc_security_group_ids } unless (vpc_subnet_ids.empty? && vpc_security_group_ids.empty?)
|
95
|
-
|
96
|
-
# if we cannot find it, we have to create it instead of updating it
|
97
|
-
func_config = @client.create_function(
|
98
|
-
function_name: function_name, runtime: 'nodejs4.3', role: lambda_role,
|
99
|
-
handler: handler, code: { s3_bucket: bucket, s3_key: key }, timeout: 5, memory_size: 128, publish: true,
|
100
|
-
description: lambda_description,
|
101
|
-
vpc_config: vpc_Configuration
|
102
|
-
).data
|
103
|
-
puts func_config
|
104
|
-
func_version = func_config.version
|
105
|
-
raise "Error while publishing new lambda function #{function_name}" unless func_version
|
106
|
-
end
|
107
|
-
|
108
|
-
add_api_gateway_permissions(function_name, nil)
|
109
|
-
|
110
|
-
func_version
|
111
|
-
end
|
112
|
-
|
113
|
-
##
|
114
|
-
# Creates an alias for a given lambda function version.
|
115
|
-
#
|
116
|
-
# *Arguments*
|
117
|
-
# [function_name] The lambda function name for which the alias should be created.
|
118
|
-
# [func_version] The lambda function versino to which the alias should point.
|
119
|
-
# [alias_name] The name of the alias, matching the LambdaWrap environment concept.
|
120
|
-
def create_alias(function_name, func_version, alias_name)
|
121
|
-
# create or update alias
|
122
|
-
func_alias = @client.list_aliases(function_name: function_name).aliases.select { |a| a.name == alias_name }.first
|
123
|
-
if !func_alias
|
124
|
-
a = @client.create_alias(
|
125
|
-
function_name: function_name, name: alias_name, function_version: func_version,
|
126
|
-
description: 'created by an automated script'
|
127
|
-
).data
|
128
|
-
else
|
129
|
-
a = @client.update_alias(
|
130
|
-
function_name: function_name, name: alias_name, function_version: func_version,
|
131
|
-
description: 'updated by an automated script'
|
132
|
-
).data
|
133
|
-
end
|
134
|
-
puts a
|
135
|
-
|
136
|
-
add_api_gateway_permissions(function_name, alias_name)
|
137
|
-
end
|
138
|
-
|
139
|
-
##
|
140
|
-
# Removes an alias for a function.
|
141
|
-
#
|
142
|
-
# *Arguments*
|
143
|
-
# [function_name] The lambda function name for which the alias should be removed.
|
144
|
-
# [alias_name] The alias to remove.
|
145
|
-
def remove_alias(function_name, alias_name)
|
146
|
-
@client.delete_alias(function_name: function_name, name: alias_name)
|
147
|
-
end
|
148
|
-
|
149
|
-
##
|
150
|
-
# Adds permissions for API gateway to execute this function.
|
151
|
-
#
|
152
|
-
# *Arguments*
|
153
|
-
# [function_name] The function name which needs to be executed from API Gateway.
|
154
|
-
# [env] The environment (matching the function's alias) which needs to be executed from API Gateway.
|
155
|
-
# => If nil, the permissions are set of the $LATEST version.
|
156
|
-
def add_api_gateway_permissions(function_name, env)
|
157
|
-
# permissions to execute lambda
|
158
|
-
suffix = (':' + env if env) || ''
|
159
|
-
func = @client.get_function(function_name: function_name + suffix).data.configuration
|
160
|
-
statement_id = func.function_name + (('-' + env if env) || '')
|
161
|
-
begin
|
162
|
-
existing_policies = @client.get_policy(function_name: func.function_arn).data
|
163
|
-
existing_policy = JSON.parse(existing_policies.policy)
|
164
|
-
policy_exists = existing_policy['Statement'].select { |s| s['Sid'] == statement_id }.any?
|
165
|
-
rescue Aws::Lambda::Errors::ResourceNotFoundException
|
166
|
-
# policy does not exist, and that is ok
|
167
|
-
policy_exists = false
|
168
|
-
end
|
169
|
-
|
170
|
-
unless policy_exists
|
171
|
-
perm_add = @client.add_permission(
|
172
|
-
function_name: func.function_arn, statement_id: statement_id,
|
173
|
-
action: 'lambda:*', principal: 'apigateway.amazonaws.com'
|
174
|
-
)
|
175
|
-
puts perm_add.data
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
private :add_api_gateway_permissions
|
180
|
-
end
|
181
|
-
end
|
1
|
+
require 'aws-sdk'
|
2
|
+
|
3
|
+
module LambdaWrap
|
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
|
+
# check if vpc_subnet_ids and vpc_security_group_ids are empty or not and set the vpc_config accordingly.
|
93
|
+
vpc_Configuration = nil
|
94
|
+
vpc_Configuration = { subnet_ids: vpc_subnet_ids, security_group_ids: vpc_security_group_ids } unless (vpc_subnet_ids.empty? && vpc_security_group_ids.empty?)
|
95
|
+
|
96
|
+
# if we cannot find it, we have to create it instead of updating it
|
97
|
+
func_config = @client.create_function(
|
98
|
+
function_name: function_name, runtime: 'nodejs4.3', role: lambda_role,
|
99
|
+
handler: handler, code: { s3_bucket: bucket, s3_key: key }, timeout: 5, memory_size: 128, publish: true,
|
100
|
+
description: lambda_description,
|
101
|
+
vpc_config: vpc_Configuration
|
102
|
+
).data
|
103
|
+
puts func_config
|
104
|
+
func_version = func_config.version
|
105
|
+
raise "Error while publishing new lambda function #{function_name}" unless func_version
|
106
|
+
end
|
107
|
+
|
108
|
+
add_api_gateway_permissions(function_name, nil)
|
109
|
+
|
110
|
+
func_version
|
111
|
+
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# Creates an alias for a given lambda function version.
|
115
|
+
#
|
116
|
+
# *Arguments*
|
117
|
+
# [function_name] The lambda function name for which the alias should be created.
|
118
|
+
# [func_version] The lambda function versino to which the alias should point.
|
119
|
+
# [alias_name] The name of the alias, matching the LambdaWrap environment concept.
|
120
|
+
def create_alias(function_name, func_version, alias_name)
|
121
|
+
# create or update alias
|
122
|
+
func_alias = @client.list_aliases(function_name: function_name).aliases.select { |a| a.name == alias_name }.first
|
123
|
+
if !func_alias
|
124
|
+
a = @client.create_alias(
|
125
|
+
function_name: function_name, name: alias_name, function_version: func_version,
|
126
|
+
description: 'created by an automated script'
|
127
|
+
).data
|
128
|
+
else
|
129
|
+
a = @client.update_alias(
|
130
|
+
function_name: function_name, name: alias_name, function_version: func_version,
|
131
|
+
description: 'updated by an automated script'
|
132
|
+
).data
|
133
|
+
end
|
134
|
+
puts a
|
135
|
+
|
136
|
+
add_api_gateway_permissions(function_name, alias_name)
|
137
|
+
end
|
138
|
+
|
139
|
+
##
|
140
|
+
# Removes an alias for a function.
|
141
|
+
#
|
142
|
+
# *Arguments*
|
143
|
+
# [function_name] The lambda function name for which the alias should be removed.
|
144
|
+
# [alias_name] The alias to remove.
|
145
|
+
def remove_alias(function_name, alias_name)
|
146
|
+
@client.delete_alias(function_name: function_name, name: alias_name)
|
147
|
+
end
|
148
|
+
|
149
|
+
##
|
150
|
+
# Adds permissions for API gateway to execute this function.
|
151
|
+
#
|
152
|
+
# *Arguments*
|
153
|
+
# [function_name] The function name which needs to be executed from API Gateway.
|
154
|
+
# [env] The environment (matching the function's alias) which needs to be executed from API Gateway.
|
155
|
+
# => If nil, the permissions are set of the $LATEST version.
|
156
|
+
def add_api_gateway_permissions(function_name, env)
|
157
|
+
# permissions to execute lambda
|
158
|
+
suffix = (':' + env if env) || ''
|
159
|
+
func = @client.get_function(function_name: function_name + suffix).data.configuration
|
160
|
+
statement_id = func.function_name + (('-' + env if env) || '')
|
161
|
+
begin
|
162
|
+
existing_policies = @client.get_policy(function_name: func.function_arn).data
|
163
|
+
existing_policy = JSON.parse(existing_policies.policy)
|
164
|
+
policy_exists = existing_policy['Statement'].select { |s| s['Sid'] == statement_id }.any?
|
165
|
+
rescue Aws::Lambda::Errors::ResourceNotFoundException
|
166
|
+
# policy does not exist, and that is ok
|
167
|
+
policy_exists = false
|
168
|
+
end
|
169
|
+
|
170
|
+
unless policy_exists
|
171
|
+
perm_add = @client.add_permission(
|
172
|
+
function_name: func.function_arn, statement_id: statement_id,
|
173
|
+
action: 'lambda:*', principal: 'apigateway.amazonaws.com'
|
174
|
+
)
|
175
|
+
puts perm_add.data
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
private :add_api_gateway_permissions
|
180
|
+
end
|
181
|
+
end
|
@@ -2,7 +2,7 @@ require 'aws-sdk'
|
|
2
2
|
|
3
3
|
module LambdaWrap
|
4
4
|
##
|
5
|
-
# The S3BucketManager
|
5
|
+
# The S3BucketManager would have functions to help add policies, CORS etc to S3 bucket.
|
6
6
|
class S3BucketManager
|
7
7
|
#
|
8
8
|
# The constructor creates an instance of s3 bucket
|
@@ -1,67 +1,67 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'zip'
|
3
|
-
|
4
|
-
module LambdaWrap
|
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
|
21
|
-
##
|
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
|
27
|
-
|
28
|
-
##
|
29
|
-
# Zip the input directory.
|
30
|
-
def write
|
31
|
-
entries = Dir.entries(@input_dir) - %w(. ..)
|
32
|
-
|
33
|
-
Zip::File.open(@output_file, Zip::File::CREATE) do |io|
|
34
|
-
write_entries entries, '', io
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
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}"
|
46
|
-
|
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)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
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
|
59
|
-
end
|
60
|
-
|
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
|
1
|
+
require 'rubygems'
|
2
|
+
require 'zip'
|
3
|
+
|
4
|
+
module LambdaWrap
|
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
|
21
|
+
##
|
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
|
27
|
+
|
28
|
+
##
|
29
|
+
# Zip the input directory.
|
30
|
+
def write
|
31
|
+
entries = Dir.entries(@input_dir) - %w(. ..)
|
32
|
+
|
33
|
+
Zip::File.open(@output_file, Zip::File::CREATE) do |io|
|
34
|
+
write_entries entries, '', io
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
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}"
|
46
|
+
|
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)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
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
|
59
|
+
end
|
60
|
+
|
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.
|
4
|
+
version: 0.15.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-07-
|
13
|
+
date: 2016-07-26 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: aws-sdk
|
@@ -73,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
73
73
|
version: '0'
|
74
74
|
requirements: []
|
75
75
|
rubyforge_project:
|
76
|
-
rubygems_version: 2.
|
76
|
+
rubygems_version: 2.2.5
|
77
77
|
signing_key:
|
78
78
|
specification_version: 4
|
79
79
|
summary: Easy deployment of AWS Lambda functions and dependencies.
|