lambda_wrap 0.12.0 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/lambda_wrap.rb +5 -5
- data/lib/lambda_wrap/api_gateway_manager.rb +155 -155
- data/lib/lambda_wrap/dynamo_db_manager.rb +121 -121
- data/lib/lambda_wrap/lambda_manager.rb +181 -177
- 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: 41235b2c4309067bbb4c5cf4411eea32a1197f94
|
4
|
+
data.tar.gz: 82f1b8d8e6576a0a8e41c8cc1993511ab51a0dd9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57873463302ea598243046674434faffb2f2d95af09e3b063c6f2aa8c37ed601d037bb9cf5e9dbce4ca4b8a6efc490b752d070fe752d9257aee0233051d0dc62
|
7
|
+
data.tar.gz: 266f1475b719ee54b5b47ba240861b5189f9bea02bb5d4257fc78df81b3f26ea7659754a03233ebd07d0773d2b217a58c7f3ad34e4870b998e113d08f158a230
|
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,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(_, env)
|
147
|
-
puts 'Deleted API gateway stage ' + env
|
148
|
-
rescue Aws::APIGateway::Errors::NotFoundException
|
149
|
-
puts 'API Gateway stage ' + env + ' does not exist. Nothing to delete.'
|
150
|
-
end
|
151
|
-
|
152
|
-
private :get_existing_rest_api, :setup_apigateway_create_rest_api, :setup_apigateway_create_resources,
|
153
|
-
:create_stages, :delete_stage
|
154
|
-
end
|
155
|
-
end
|
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(_, env)
|
147
|
+
puts 'Deleted API gateway stage ' + env
|
148
|
+
rescue Aws::APIGateway::Errors::NotFoundException
|
149
|
+
puts 'API Gateway stage ' + env + ' does not exist. Nothing to delete.'
|
150
|
+
end
|
151
|
+
|
152
|
+
private :get_existing_rest_api, :setup_apigateway_create_rest_api, :setup_apigateway_create_resources,
|
153
|
+
:create_stages, :delete_stage
|
154
|
+
end
|
155
|
+
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,177 +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
|
-
# if
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
#
|
115
|
-
#
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
#
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
#
|
151
|
-
#
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
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
|
@@ -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.13.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-13 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: aws-sdk
|
@@ -72,7 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
72
|
version: '0'
|
73
73
|
requirements: []
|
74
74
|
rubyforge_project:
|
75
|
-
rubygems_version: 2.
|
75
|
+
rubygems_version: 2.4.7
|
76
76
|
signing_key:
|
77
77
|
specification_version: 4
|
78
78
|
summary: Easy deployment of AWS Lambda functions and dependencies.
|