lambda_wrap 0.19.0 → 0.20.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/dynamo_db_manager.rb +121 -121
- data/lib/lambda_wrap/lambda_manager.rb +188 -182
- data/lib/lambda_wrap/zip_file_generator.rb +67 -67
- data/lib/lambda_wrap.rb +5 -5
- 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: 967cbf57b49c5c9089d2b7fb499e918e06b0c2cd
|
4
|
+
data.tar.gz: e04be077d970608457c40613e8c42cb41364822f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce0921cb5e342d7b6dee2caec10bc1c0f7ac73f240f3fd3a8afd0dff72cf7137bdd1b0203871713eac296384352a095c59fdd4a2857cd751d466dbb5f293bfe7
|
7
|
+
data.tar.gz: 722c438455fc50aabdb5528ee568679f6920aef78291591ae9b249c785f1ff09dd2989e754b5ca684443fb0f5cb2141704236e6faadca3887570edbe231f58ec
|
@@ -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,182 +1,188 @@
|
|
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
|
-
# [runtime] The runtime the code is written for.
|
79
|
-
def deploy_lambda(
|
80
|
-
bucket, key, version_id, function_name, handler, lambda_role, lambda_description = 'Deployed with LambdaWrap',
|
81
|
-
vpc_subnet_ids = [], vpc_security_group_ids = [], runtime = 'nodejs4.3'
|
82
|
-
)
|
83
|
-
# create or update function
|
84
|
-
|
85
|
-
begin
|
86
|
-
@client.get_function(function_name: function_name)
|
87
|
-
func_config = @client.update_function_code(function_name: function_name, s3_bucket: bucket, s3_key: key,
|
88
|
-
s3_object_version: version_id, publish: true).data
|
89
|
-
puts func_config
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
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
|
+
# [runtime] The runtime the code is written for.
|
79
|
+
def deploy_lambda(
|
80
|
+
bucket, key, version_id, function_name, handler, lambda_role, lambda_description = 'Deployed with LambdaWrap',
|
81
|
+
vpc_subnet_ids = [], vpc_security_group_ids = [], runtime = 'nodejs4.3'
|
82
|
+
)
|
83
|
+
# create or update function
|
84
|
+
|
85
|
+
begin
|
86
|
+
@client.get_function(function_name: function_name)
|
87
|
+
func_config = @client.update_function_code(function_name: function_name, s3_bucket: bucket, s3_key: key,
|
88
|
+
s3_object_version: version_id, publish: true).data
|
89
|
+
puts func_config
|
90
|
+
vpc_configuration = { subnet_ids: vpc_subnet_ids, security_group_ids: vpc_security_group_ids } unless (vpc_subnet_ids.empty? && vpc_security_group_ids.empty?)
|
91
|
+
func_config = @client.update_function_configuration(function_name: function_name, role: lambda_role, runtime: runtime,
|
92
|
+
handler: handler, timeout: 5, memory_size: 128,
|
93
|
+
description: lambda_description,
|
94
|
+
vpc_config: vpc_configuration).data
|
95
|
+
puts func_config
|
96
|
+
func_version = func_config.version
|
97
|
+
raise 'Error while publishing existing lambda function ' + function_name unless func_version
|
98
|
+
rescue Aws::Lambda::Errors::ResourceNotFoundException
|
99
|
+
# check if vpc_subnet_ids and vpc_security_group_ids are empty or not and set the vpc_config accordingly.
|
100
|
+
vpc_Configuration = nil
|
101
|
+
vpc_Configuration = { subnet_ids: vpc_subnet_ids, security_group_ids: vpc_security_group_ids } unless (vpc_subnet_ids.empty? && vpc_security_group_ids.empty?)
|
102
|
+
|
103
|
+
# if we cannot find it, we have to create it instead of updating it
|
104
|
+
func_config = @client.create_function(
|
105
|
+
function_name: function_name, runtime: runtime, role: lambda_role,
|
106
|
+
handler: handler, code: { s3_bucket: bucket, s3_key: key }, timeout: 5, memory_size: 128, publish: true,
|
107
|
+
description: lambda_description,
|
108
|
+
vpc_config: vpc_Configuration
|
109
|
+
).data
|
110
|
+
puts func_config
|
111
|
+
func_version = func_config.version
|
112
|
+
raise "Error while publishing new lambda function #{function_name}" unless func_version
|
113
|
+
end
|
114
|
+
|
115
|
+
add_api_gateway_permissions(function_name, nil)
|
116
|
+
|
117
|
+
func_version
|
118
|
+
end
|
119
|
+
|
120
|
+
##
|
121
|
+
# Creates an alias for a given lambda function version.
|
122
|
+
#
|
123
|
+
# *Arguments*
|
124
|
+
# [function_name] The lambda function name for which the alias should be created.
|
125
|
+
# [func_version] The lambda function versino to which the alias should point.
|
126
|
+
# [alias_name] The name of the alias, matching the LambdaWrap environment concept.
|
127
|
+
def create_alias(function_name, func_version, alias_name)
|
128
|
+
# create or update alias
|
129
|
+
func_alias = @client.list_aliases(function_name: function_name).aliases.select { |a| a.name == alias_name }.first
|
130
|
+
if !func_alias
|
131
|
+
a = @client.create_alias(
|
132
|
+
function_name: function_name, name: alias_name, function_version: func_version,
|
133
|
+
description: 'created by an automated script'
|
134
|
+
).data
|
135
|
+
else
|
136
|
+
a = @client.update_alias(
|
137
|
+
function_name: function_name, name: alias_name, function_version: func_version,
|
138
|
+
description: 'updated by an automated script'
|
139
|
+
).data
|
140
|
+
end
|
141
|
+
puts a
|
142
|
+
|
143
|
+
add_api_gateway_permissions(function_name, alias_name)
|
144
|
+
end
|
145
|
+
|
146
|
+
##
|
147
|
+
# Removes an alias for a function.
|
148
|
+
#
|
149
|
+
# *Arguments*
|
150
|
+
# [function_name] The lambda function name for which the alias should be removed.
|
151
|
+
# [alias_name] The alias to remove.
|
152
|
+
def remove_alias(function_name, alias_name)
|
153
|
+
@client.delete_alias(function_name: function_name, name: alias_name)
|
154
|
+
end
|
155
|
+
|
156
|
+
##
|
157
|
+
# Adds permissions for API gateway to execute this function.
|
158
|
+
#
|
159
|
+
# *Arguments*
|
160
|
+
# [function_name] The function name which needs to be executed from API Gateway.
|
161
|
+
# [env] The environment (matching the function's alias) which needs to be executed from API Gateway.
|
162
|
+
# => If nil, the permissions are set of the $LATEST version.
|
163
|
+
def add_api_gateway_permissions(function_name, env)
|
164
|
+
# permissions to execute lambda
|
165
|
+
suffix = (':' + env if env) || ''
|
166
|
+
func = @client.get_function(function_name: function_name + suffix).data.configuration
|
167
|
+
statement_id = func.function_name + (('-' + env if env) || '')
|
168
|
+
begin
|
169
|
+
existing_policies = @client.get_policy(function_name: func.function_arn).data
|
170
|
+
existing_policy = JSON.parse(existing_policies.policy)
|
171
|
+
policy_exists = existing_policy['Statement'].select { |s| s['Sid'] == statement_id }.any?
|
172
|
+
rescue Aws::Lambda::Errors::ResourceNotFoundException
|
173
|
+
# policy does not exist, and that is ok
|
174
|
+
policy_exists = false
|
175
|
+
end
|
176
|
+
|
177
|
+
unless policy_exists
|
178
|
+
perm_add = @client.add_permission(
|
179
|
+
function_name: func.function_arn, statement_id: statement_id,
|
180
|
+
action: 'lambda:*', principal: 'apigateway.amazonaws.com'
|
181
|
+
)
|
182
|
+
puts perm_add.data
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
private :add_api_gateway_permissions
|
187
|
+
end
|
188
|
+
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
|
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
|
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.20.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-
|
13
|
+
date: 2016-10-07 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.6.7
|
77
77
|
signing_key:
|
78
78
|
specification_version: 4
|
79
79
|
summary: Easy deployment of AWS Lambda functions and dependencies.
|