lambda-microvms 0.0.0 → 0.2.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/CHANGELOG.md +10 -1
- data/README.md +39 -4
- data/exe/lambda-microvms +41 -10
- data/lib/lambda/microvms/adapters/microvm_sdk.rb +59 -0
- data/lib/lambda/microvms/client.rb +81 -8
- data/lib/lambda/microvms/deployer.rb +27 -5
- data/lib/lambda/microvms/doctor.rb +15 -2
- data/lib/lambda/microvms/endpoint.rb +26 -4
- data/lib/lambda/microvms/function_client.rb +90 -0
- data/lib/lambda/microvms/image.rb +22 -0
- data/lib/lambda/microvms/microvm.rb +68 -1
- data/lib/lambda/microvms/packager.rb +9 -2
- data/lib/lambda/microvms/project.rb +93 -4
- data/lib/lambda/microvms/scaffold.rb +3 -0
- data/lib/lambda/microvms/session.rb +15 -0
- data/lib/lambda/microvms/util.rb +9 -0
- data/lib/lambda/microvms/version.rb +2 -1
- data/lib/lambda/microvms/waiter.rb +8 -0
- data/lib/lambda/microvms.rb +7 -0
- data/sig/lambda/microvms.rbs +150 -3
- metadata +5 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c2278501b187e57ed03eacd396649c19e09bd5c6e07e890f5ce0403588c8f2fb
|
|
4
|
+
data.tar.gz: c8f240355cdc41e80e5d1d31a231e31cac3e6fd77d488f584e1027df0a47db0a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 22bc5c257c1650f571cdf560a4c32af34cd119341776cb42a707104620255bbd0e26091b4967acc88462ca873e8319c5109aa9ee46fa26ceb0634207c32526bd
|
|
7
|
+
data.tar.gz: 942370b040e49ece5f159226f0da4094f05b054c7636210150f2858d768c863e9deb3997d72dff212c707542812134a230850f2ebef06b8478c9af089c288f72
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.2.0
|
|
6
|
+
|
|
7
|
+
- Added `lambda-microvms sdk-contract` and doctor output for the experimental MicroVM SDK operation contract.
|
|
8
|
+
- Added an adapter boundary for MicroVM SDK operations.
|
|
9
|
+
- Added `Lambda::MicroVMs::FunctionClient` and `lambda-microvms function-invoke` for standard Lambda function APIs.
|
|
10
|
+
- Made MicroVM deploy/run fail before packaging or uploading when the installed `aws-sdk-lambda` lacks MicroVM operations.
|
|
11
|
+
|
|
12
|
+
## 0.1.0
|
|
4
13
|
|
|
5
14
|
- Initial Ruby development kit for AWS Lambda MicroVMs.
|
|
6
15
|
- Client wrapper over `aws-sdk-lambda`.
|
data/README.md
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
[](https://www.ruby-lang.org/en/)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
-
A Ruby development kit for AWS Lambda
|
|
8
|
+
A Ruby development kit for AWS Lambda MicroVM experiments and standard Lambda function workflows.
|
|
9
9
|
|
|
10
|
-
`lambda-microvms` builds on the official `aws-sdk-lambda` gem. It provides Ruby resource objects, lifecycle helpers, endpoint calls, project scaffolding, packaging,
|
|
10
|
+
`lambda-microvms` builds on the official `aws-sdk-lambda` gem. It provides Ruby resource objects, lifecycle helpers, endpoint calls, project scaffolding, packaging, experimental MicroVM deployment commands, and a supported Lambda function client for APIs that exist today.
|
|
11
11
|
|
|
12
12
|
It gives Ruby developers a higher-level workflow:
|
|
13
13
|
|
|
@@ -77,7 +77,15 @@ gem "aws_lambda_ric"
|
|
|
77
77
|
lambda-microvms doctor
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
Checks Ruby, Docker, AWS CLI, project config, Dockerfile, deployment bucket, runtime role, and `
|
|
80
|
+
Checks Ruby, Docker, AWS CLI, project config, Dockerfile, deployment bucket, runtime role, `aws_lambda_ric` presence, and whether the installed `aws-sdk-lambda` exposes the experimental MicroVM operation contract.
|
|
81
|
+
|
|
82
|
+
## Check the MicroVM SDK contract
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
lambda-microvms sdk-contract
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The public `aws-sdk-lambda` gem may not expose MicroVM lifecycle operations such as `create_microvm_image` or `run_microvm`. When those methods are missing, MicroVM `deploy` and `run` fail before packaging or uploading artifacts.
|
|
81
89
|
|
|
82
90
|
## Package
|
|
83
91
|
|
|
@@ -113,6 +121,30 @@ lambda-microvms run
|
|
|
113
121
|
|
|
114
122
|
Runs the configured image ARN with the configured role ARN and runtime payload.
|
|
115
123
|
|
|
124
|
+
## Standard Lambda function APIs
|
|
125
|
+
|
|
126
|
+
The gem also includes a supported path for regular Lambda function APIs available in `aws-sdk-lambda` today.
|
|
127
|
+
|
|
128
|
+
```ruby
|
|
129
|
+
client = Lambda::MicroVMs::FunctionClient.new(region: "us-east-1")
|
|
130
|
+
|
|
131
|
+
client.create_function(
|
|
132
|
+
name: "ruby-worker",
|
|
133
|
+
role_arn: "arn:aws:iam::123456789012:role/lambda-runtime",
|
|
134
|
+
handler: "app.Handler.process",
|
|
135
|
+
runtime: "ruby3.4",
|
|
136
|
+
zip_file: "tmp/function.zip"
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
result = client.invoke(function_name: "ruby-worker", payload: { hello: "world" })
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
You can also invoke an existing function from the CLI:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
lambda-microvms function-invoke ruby-worker '{"hello":"world"}'
|
|
146
|
+
```
|
|
147
|
+
|
|
116
148
|
## Ruby SDK usage
|
|
117
149
|
|
|
118
150
|
```ruby
|
|
@@ -169,6 +201,7 @@ Lambda::MicroVMs::Client
|
|
|
169
201
|
├── MicroVM
|
|
170
202
|
├── Endpoint
|
|
171
203
|
├── Session
|
|
204
|
+
├── FunctionClient
|
|
172
205
|
├── Scaffold
|
|
173
206
|
├── Packager
|
|
174
207
|
└── Deployer
|
|
@@ -176,4 +209,6 @@ Lambda::MicroVMs::Client
|
|
|
176
209
|
|
|
177
210
|
## Status
|
|
178
211
|
|
|
179
|
-
|
|
212
|
+
MicroVM lifecycle support is experimental and contract-gated. The current public `aws-sdk-lambda` may not expose the MicroVM methods this gem wraps. Use `lambda-microvms sdk-contract` to verify your installed SDK.
|
|
213
|
+
|
|
214
|
+
Standard Lambda function operations are available through `Lambda::MicroVMs::FunctionClient`.
|
data/exe/lambda-microvms
CHANGED
|
@@ -7,23 +7,33 @@ module Lambda
|
|
|
7
7
|
module MicroVMs
|
|
8
8
|
# Minimal CLI for the Ruby Lambda MicroVM development kit.
|
|
9
9
|
class CLI
|
|
10
|
+
COMMANDS = {
|
|
11
|
+
'version' => :version,
|
|
12
|
+
'--version' => :version,
|
|
13
|
+
'-v' => :version,
|
|
14
|
+
'new' => :new_project,
|
|
15
|
+
'package' => :package,
|
|
16
|
+
'deploy' => :deploy,
|
|
17
|
+
'run' => :run_microvm,
|
|
18
|
+
'doctor' => :doctor,
|
|
19
|
+
'sdk-contract' => :sdk_contract,
|
|
20
|
+
'function-invoke' => :function_invoke,
|
|
21
|
+
'help' => :help
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
10
24
|
def initialize(argv, out: $stdout, err: $stderr)
|
|
11
25
|
@argv = argv
|
|
12
26
|
@out = out
|
|
13
27
|
@err = err
|
|
14
28
|
end
|
|
15
29
|
|
|
16
|
-
# rubocop:disable Metrics/MethodLength
|
|
17
30
|
def run
|
|
18
31
|
command = @argv.shift
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
when 'run' then run_microvm
|
|
25
|
-
when 'doctor' then doctor
|
|
26
|
-
when 'help', nil then help
|
|
32
|
+
action = COMMANDS[command]
|
|
33
|
+
if action
|
|
34
|
+
send(action)
|
|
35
|
+
elsif command.nil?
|
|
36
|
+
help
|
|
27
37
|
else
|
|
28
38
|
@err.puts "unknown command: #{command}"
|
|
29
39
|
help
|
|
@@ -33,7 +43,6 @@ module Lambda
|
|
|
33
43
|
@err.puts "error: #{e.message}"
|
|
34
44
|
1
|
|
35
45
|
end
|
|
36
|
-
# rubocop:enable Metrics/MethodLength
|
|
37
46
|
|
|
38
47
|
private
|
|
39
48
|
|
|
@@ -81,6 +90,25 @@ module Lambda
|
|
|
81
90
|
checks.all?(&:ok) ? 0 : 1
|
|
82
91
|
end
|
|
83
92
|
|
|
93
|
+
def sdk_contract
|
|
94
|
+
unsupported = Client.unsupported_operations
|
|
95
|
+
if unsupported.empty?
|
|
96
|
+
@out.puts 'Aws::Lambda::Client exposes all Lambda MicroVM operations'
|
|
97
|
+
0
|
|
98
|
+
else
|
|
99
|
+
@err.puts "missing Aws::Lambda::Client operations: #{unsupported.join(', ')}"
|
|
100
|
+
1
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def function_invoke
|
|
105
|
+
function_name = @argv.shift || raise(ArgumentError, 'usage: lambda-microvms function-invoke NAME [JSON]')
|
|
106
|
+
payload = @argv.empty? ? nil : JSON.parse(@argv.join(' '))
|
|
107
|
+
result = FunctionClient.new.invoke(function_name:, payload:)
|
|
108
|
+
@out.puts(result.is_a?(String) ? result : JSON.generate(result))
|
|
109
|
+
0
|
|
110
|
+
end
|
|
111
|
+
|
|
84
112
|
def help
|
|
85
113
|
@out.puts <<~HELP
|
|
86
114
|
lambda-microvms #{VERSION}
|
|
@@ -91,6 +119,9 @@ module Lambda
|
|
|
91
119
|
package Create deployable source artifact
|
|
92
120
|
deploy Package, upload to S3, and create a MicroVM image
|
|
93
121
|
run Run the configured MicroVM image
|
|
122
|
+
sdk-contract Check whether aws-sdk-lambda exposes MicroVM operations
|
|
123
|
+
function-invoke NAME [JSON]
|
|
124
|
+
Invoke a standard Lambda function with supported AWS APIs
|
|
94
125
|
version Print version
|
|
95
126
|
HELP
|
|
96
127
|
0
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lambda
|
|
4
|
+
module MicroVMs
|
|
5
|
+
# Internal adapter implementations for provider-specific SDK surfaces.
|
|
6
|
+
module Adapters
|
|
7
|
+
# Adapter for the experimental Lambda MicroVM SDK operation surface.
|
|
8
|
+
class MicroVMSdk
|
|
9
|
+
# SDK method names required by the experimental MicroVM lifecycle wrapper.
|
|
10
|
+
REQUIRED_OPERATIONS = %i[
|
|
11
|
+
create_microvm_image
|
|
12
|
+
get_microvm_image
|
|
13
|
+
delete_microvm_image
|
|
14
|
+
run_microvm
|
|
15
|
+
get_microvm
|
|
16
|
+
list_microvms
|
|
17
|
+
suspend_microvm
|
|
18
|
+
resume_microvm
|
|
19
|
+
terminate_microvm
|
|
20
|
+
create_microvm_auth_token
|
|
21
|
+
].freeze
|
|
22
|
+
|
|
23
|
+
attr_reader :sdk
|
|
24
|
+
|
|
25
|
+
def initialize(sdk)
|
|
26
|
+
@sdk = sdk
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Return MicroVM SDK operations missing from the wrapped SDK client.
|
|
30
|
+
#
|
|
31
|
+
# @return [Array<Symbol>] missing SDK operation names
|
|
32
|
+
def unsupported_operations
|
|
33
|
+
REQUIRED_OPERATIONS.reject { |operation| sdk.respond_to?(operation) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Check whether the wrapped SDK client exposes all MicroVM operations.
|
|
37
|
+
#
|
|
38
|
+
# @return [Boolean]
|
|
39
|
+
def supported?
|
|
40
|
+
unsupported_operations.empty?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Dispatch a MicroVM operation to the wrapped SDK client.
|
|
44
|
+
#
|
|
45
|
+
# @param operation [Symbol] SDK method name
|
|
46
|
+
# @param params [Hash] SDK request parameters
|
|
47
|
+
# @return [Object] raw SDK response
|
|
48
|
+
# @raise [UnsupportedOperationError] when the SDK does not expose the operation
|
|
49
|
+
def call(operation, **params)
|
|
50
|
+
unless sdk.respond_to?(operation)
|
|
51
|
+
raise UnsupportedOperationError, "Aws::Lambda::Client does not expose ##{operation}; run sdk-contract"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
sdk.public_send(operation, **params)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -10,72 +10,145 @@ module Lambda
|
|
|
10
10
|
module MicroVMs
|
|
11
11
|
# Ruby wrapper over Aws::Lambda::Client for Lambda MicroVM lifecycle operations.
|
|
12
12
|
class Client
|
|
13
|
-
|
|
13
|
+
# SDK methods this wrapper expects from Aws::Lambda::Client.
|
|
14
|
+
REQUIRED_OPERATIONS = Adapters::MicroVMSdk::REQUIRED_OPERATIONS
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
attr_reader :sdk, :adapter
|
|
17
|
+
|
|
18
|
+
def initialize(region: nil, profile: nil, sdk: nil, adapter: nil, **)
|
|
19
|
+
@sdk = sdk || build_sdk(region: region, profile: profile, **)
|
|
20
|
+
@adapter = adapter || Adapters::MicroVMSdk.new(@sdk)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Return wrapper operations missing from the given SDK client.
|
|
24
|
+
#
|
|
25
|
+
# @param sdk [Object, nil] SDK client to inspect
|
|
26
|
+
# @return [Array<Symbol>] missing SDK operation names
|
|
27
|
+
def self.unsupported_operations(sdk = nil)
|
|
28
|
+
sdk ||= Aws::Lambda::Client.new(stub_responses: true) if defined?(Aws::Lambda::Client)
|
|
29
|
+
return REQUIRED_OPERATIONS unless sdk
|
|
30
|
+
|
|
31
|
+
Adapters::MicroVMSdk.new(sdk).unsupported_operations
|
|
17
32
|
end
|
|
18
33
|
|
|
34
|
+
# Check whether the given SDK client exposes all MicroVM operations.
|
|
35
|
+
#
|
|
36
|
+
# @param sdk [Object, nil] SDK client to inspect
|
|
37
|
+
# @return [Boolean]
|
|
38
|
+
def self.sdk_contract_supported?(sdk = nil)
|
|
39
|
+
unsupported_operations(sdk).empty?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Build an image resource wrapper without fetching it.
|
|
43
|
+
#
|
|
44
|
+
# @param arn [String] MicroVM image ARN
|
|
45
|
+
# @return [Image] image resource wrapper
|
|
19
46
|
def image(arn)
|
|
20
47
|
Image.new(client: self, arn: arn)
|
|
21
48
|
end
|
|
22
49
|
|
|
50
|
+
# Build a MicroVM resource wrapper without fetching it.
|
|
51
|
+
#
|
|
52
|
+
# @param id_or_arn [String] MicroVM id or ARN
|
|
53
|
+
# @return [MicroVM] MicroVM resource wrapper
|
|
23
54
|
def microvm(id_or_arn)
|
|
24
55
|
MicroVM.new(client: self, id: id_or_arn)
|
|
25
56
|
end
|
|
26
57
|
|
|
58
|
+
# Create a MicroVM image through the Lambda SDK.
|
|
59
|
+
#
|
|
60
|
+
# @param params [Hash] SDK request parameters
|
|
61
|
+
# @return [Image] created image resource
|
|
27
62
|
def create_image(**params)
|
|
28
63
|
response = call_sdk(:create_microvm_image, **params)
|
|
29
64
|
Image.from_response(client: self, response: response)
|
|
30
65
|
end
|
|
31
66
|
|
|
67
|
+
# Fetch a MicroVM image and wrap the SDK response.
|
|
68
|
+
#
|
|
69
|
+
# @param params [Hash] SDK request parameters
|
|
70
|
+
# @return [Image] fetched image resource
|
|
32
71
|
def get_image(**params)
|
|
33
72
|
response = call_sdk(:get_microvm_image, **params)
|
|
34
73
|
Image.from_response(client: self, response: response)
|
|
35
74
|
end
|
|
36
75
|
|
|
76
|
+
# Delete a MicroVM image.
|
|
77
|
+
#
|
|
78
|
+
# @param params [Hash] SDK request parameters
|
|
79
|
+
# @return [Object] raw SDK response
|
|
37
80
|
def delete_image(**params)
|
|
38
81
|
call_sdk(:delete_microvm_image, **params)
|
|
39
82
|
end
|
|
40
83
|
|
|
84
|
+
# Run a MicroVM from an image.
|
|
85
|
+
#
|
|
86
|
+
# @param params [Hash] SDK request parameters
|
|
87
|
+
# @return [MicroVM] started MicroVM resource
|
|
41
88
|
def run(**params)
|
|
42
89
|
response = call_sdk(:run_microvm, **params)
|
|
43
90
|
MicroVM.from_response(client: self, response: response)
|
|
44
91
|
end
|
|
45
92
|
alias run_microvm run
|
|
46
93
|
|
|
94
|
+
# Fetch a MicroVM and wrap the SDK response.
|
|
95
|
+
#
|
|
96
|
+
# @param params [Hash] SDK request parameters
|
|
97
|
+
# @return [MicroVM] fetched MicroVM resource
|
|
47
98
|
def get_microvm(**params)
|
|
48
99
|
response = call_sdk(:get_microvm, **params)
|
|
49
100
|
MicroVM.from_response(client: self, response: response)
|
|
50
101
|
end
|
|
51
102
|
|
|
103
|
+
# List MicroVMs using the underlying Lambda SDK client.
|
|
104
|
+
#
|
|
105
|
+
# @param params [Hash] SDK request parameters
|
|
106
|
+
# @return [Object] raw SDK response
|
|
52
107
|
def list_microvms(**params)
|
|
53
108
|
call_sdk(:list_microvms, **params)
|
|
54
109
|
end
|
|
55
110
|
|
|
111
|
+
# Suspend a MicroVM.
|
|
112
|
+
#
|
|
113
|
+
# @param params [Hash] SDK request parameters
|
|
114
|
+
# @return [Object] raw SDK response
|
|
56
115
|
def suspend_microvm(**params)
|
|
57
116
|
call_sdk(:suspend_microvm, **params)
|
|
58
117
|
end
|
|
59
118
|
|
|
119
|
+
# Resume a suspended MicroVM.
|
|
120
|
+
#
|
|
121
|
+
# @param params [Hash] SDK request parameters
|
|
122
|
+
# @return [Object] raw SDK response
|
|
60
123
|
def resume_microvm(**params)
|
|
61
124
|
call_sdk(:resume_microvm, **params)
|
|
62
125
|
end
|
|
63
126
|
|
|
127
|
+
# Terminate a MicroVM.
|
|
128
|
+
#
|
|
129
|
+
# @param params [Hash] SDK request parameters
|
|
130
|
+
# @return [Object] raw SDK response
|
|
64
131
|
def terminate_microvm(**params)
|
|
65
132
|
call_sdk(:terminate_microvm, **params)
|
|
66
133
|
end
|
|
67
134
|
|
|
135
|
+
# Create an auth token for direct MicroVM endpoint access.
|
|
136
|
+
#
|
|
137
|
+
# @param params [Hash] SDK request parameters
|
|
138
|
+
# @return [Object] raw SDK response
|
|
68
139
|
def create_auth_token(**params)
|
|
69
140
|
call_sdk(:create_microvm_auth_token, **params)
|
|
70
141
|
end
|
|
71
142
|
alias create_microvm_auth_token create_auth_token
|
|
72
143
|
|
|
144
|
+
# Dispatch a supported operation to the underlying SDK client.
|
|
145
|
+
#
|
|
146
|
+
# @param operation [Symbol] SDK method name
|
|
147
|
+
# @param params [Hash] SDK request parameters
|
|
148
|
+
# @return [Object] raw SDK response
|
|
149
|
+
# @raise [UnsupportedOperationError] when the SDK does not expose the operation
|
|
73
150
|
def call_sdk(operation, **params)
|
|
74
|
-
|
|
75
|
-
raise UnsupportedOperationError, "Aws::Lambda::Client does not expose ##{operation}; upgrade aws-sdk-lambda"
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
@sdk.public_send(operation, **params)
|
|
151
|
+
adapter.call(operation, **params)
|
|
79
152
|
end
|
|
80
153
|
|
|
81
154
|
private
|
|
@@ -15,29 +15,44 @@ module Lambda
|
|
|
15
15
|
attr_reader :project, :client, :s3
|
|
16
16
|
|
|
17
17
|
def initialize(project:, client: nil, s3: nil) # rubocop:disable Naming/MethodParameterName
|
|
18
|
-
@project = project
|
|
18
|
+
@project = project.validate!
|
|
19
19
|
@client = client || Client.new(region: project.region, profile: project.profile)
|
|
20
20
|
@s3 = s3 || build_s3
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
# Package the project into a deployable zip artifact.
|
|
24
|
+
#
|
|
25
|
+
# @return [String] artifact path
|
|
23
26
|
def package
|
|
24
27
|
Packager.new(project).package
|
|
25
28
|
end
|
|
26
29
|
|
|
30
|
+
# Upload an artifact to the configured S3 bucket and prefix.
|
|
31
|
+
#
|
|
32
|
+
# @param path [String] local artifact path
|
|
33
|
+
# @return [String] S3 URI
|
|
27
34
|
def upload(path)
|
|
28
35
|
bucket = project.require!('deployment.bucket', project.s3_bucket)
|
|
29
36
|
key = [project.s3_prefix.sub(%r{/\z}, ''), File.basename(path)].join('/')
|
|
30
|
-
s3.put_object(bucket: bucket, key: key, body:
|
|
37
|
+
File.open(path, 'rb') { |body| s3.put_object(bucket: bucket, key: key, body: body) }
|
|
31
38
|
"s3://#{bucket}/#{key}"
|
|
32
39
|
end
|
|
33
40
|
|
|
41
|
+
# Package, upload, and create a MicroVM image.
|
|
42
|
+
#
|
|
43
|
+
# @return [Image] created image resource
|
|
34
44
|
def deploy
|
|
45
|
+
ensure_microvm_contract!
|
|
35
46
|
artifact = package
|
|
36
47
|
artifact_uri = upload(artifact)
|
|
37
48
|
client.create_image(**project.create_image_params(artifact_uri: artifact_uri))
|
|
38
49
|
end
|
|
39
50
|
|
|
51
|
+
# Run the configured image with configured runtime parameters.
|
|
52
|
+
#
|
|
53
|
+
# @return [MicroVM] started MicroVM resource
|
|
40
54
|
def run
|
|
55
|
+
ensure_microvm_contract!
|
|
41
56
|
image_arn = project.require!('image.arn', project.image_arn)
|
|
42
57
|
role_arn = project.require!('role_arn', project.role_arn)
|
|
43
58
|
client.image(image_arn).run(**project.run_params, role_arn: role_arn)
|
|
@@ -48,11 +63,18 @@ module Lambda
|
|
|
48
63
|
def build_s3
|
|
49
64
|
raise LoadError, 'install aws-sdk-s3 to use deployment helpers' unless defined?(Aws::S3::Client)
|
|
50
65
|
|
|
51
|
-
args = {}
|
|
52
|
-
args[:region] = project.region if project.region
|
|
53
|
-
args[:profile] = project.profile if project.profile
|
|
66
|
+
args = { region: project.region, profile: project.profile }.compact
|
|
54
67
|
Aws::S3::Client.new(**args)
|
|
55
68
|
end
|
|
69
|
+
|
|
70
|
+
def ensure_microvm_contract!
|
|
71
|
+
return unless client.respond_to?(:adapter)
|
|
72
|
+
|
|
73
|
+
missing = client.adapter.unsupported_operations
|
|
74
|
+
return if missing.empty?
|
|
75
|
+
|
|
76
|
+
raise UnsupportedOperationError, "Aws::Lambda::Client is missing MicroVM operations: #{missing.join(', ')}"
|
|
77
|
+
end
|
|
56
78
|
end
|
|
57
79
|
end
|
|
58
80
|
end
|
|
@@ -5,6 +5,7 @@ module Lambda
|
|
|
5
5
|
module MicroVMs
|
|
6
6
|
# Performs lightweight local project readiness checks.
|
|
7
7
|
class Doctor
|
|
8
|
+
# Result object for a single doctor check.
|
|
8
9
|
Check = Struct.new(:name, :ok, :detail, keyword_init: true)
|
|
9
10
|
|
|
10
11
|
attr_reader :project, :runner
|
|
@@ -14,6 +15,9 @@ module Lambda
|
|
|
14
15
|
@runner = runner
|
|
15
16
|
end
|
|
16
17
|
|
|
18
|
+
# Run all local readiness checks.
|
|
19
|
+
#
|
|
20
|
+
# @return [Array<Check>] check results
|
|
17
21
|
def checks
|
|
18
22
|
[
|
|
19
23
|
check('Ruby', RUBY_VERSION >= '3.2', RUBY_VERSION),
|
|
@@ -23,7 +27,8 @@ module Lambda
|
|
|
23
27
|
file_check('Dockerfile', project.dockerfile),
|
|
24
28
|
config_check('role_arn', project.role_arn),
|
|
25
29
|
config_check('deployment.bucket', project.s3_bucket),
|
|
26
|
-
ric_check
|
|
30
|
+
ric_check,
|
|
31
|
+
sdk_contract_check
|
|
27
32
|
]
|
|
28
33
|
end
|
|
29
34
|
|
|
@@ -49,7 +54,8 @@ module Lambda
|
|
|
49
54
|
end
|
|
50
55
|
|
|
51
56
|
def config_check(name, value)
|
|
52
|
-
|
|
57
|
+
ok = value && value.to_s.strip != ''
|
|
58
|
+
Check.new(name:, ok: ok, detail: ok ? value : 'missing')
|
|
53
59
|
end
|
|
54
60
|
|
|
55
61
|
def ric_check
|
|
@@ -57,6 +63,13 @@ module Lambda
|
|
|
57
63
|
ok = File.exist?(gemfile) && File.read(gemfile).include?('aws_lambda_ric')
|
|
58
64
|
Check.new(name: 'aws_lambda_ric', ok: ok, detail: ok ? 'present in Gemfile' : 'missing from Gemfile')
|
|
59
65
|
end
|
|
66
|
+
|
|
67
|
+
def sdk_contract_check
|
|
68
|
+
missing = Client.unsupported_operations
|
|
69
|
+
ok = missing.empty?
|
|
70
|
+
detail = ok ? 'MicroVM operations available' : "missing: #{missing.join(', ')}"
|
|
71
|
+
Check.new(name: 'aws-sdk-lambda MicroVM contract', ok: ok, detail: detail)
|
|
72
|
+
end
|
|
60
73
|
end
|
|
61
74
|
end
|
|
62
75
|
end
|
|
@@ -16,17 +16,39 @@ module Lambda
|
|
|
16
16
|
@http = http
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
# Send an authenticated GET request to the MicroVM endpoint.
|
|
20
|
+
#
|
|
21
|
+
# @param path [String] endpoint path
|
|
22
|
+
# @param headers [Hash] additional HTTP headers
|
|
23
|
+
# @return [Hash,String,nil] parsed JSON response or raw body
|
|
19
24
|
def get(path, headers: {})
|
|
20
25
|
request(Net::HTTP::Get, path, headers:)
|
|
21
26
|
end
|
|
22
27
|
|
|
28
|
+
# Send an authenticated POST request to the MicroVM endpoint.
|
|
29
|
+
#
|
|
30
|
+
# @param path [String] endpoint path
|
|
31
|
+
# @param json [Hash,Array,nil] JSON body to encode
|
|
32
|
+
# @param body [String,nil] raw request body
|
|
33
|
+
# @param headers [Hash] additional HTTP headers
|
|
34
|
+
# @return [Hash,String,nil] parsed JSON response or raw body
|
|
23
35
|
def post(path, json: nil, body: nil, headers: {})
|
|
24
36
|
request(Net::HTTP::Post, path, json:, body:, headers:)
|
|
25
37
|
end
|
|
26
38
|
|
|
39
|
+
# Build and execute an authenticated HTTP request.
|
|
40
|
+
#
|
|
41
|
+
# @param klass [Class] Net::HTTP request class
|
|
42
|
+
# @param path [String] endpoint path
|
|
43
|
+
# @param json [Hash,Array,nil] JSON body to encode
|
|
44
|
+
# @param body [String,nil] raw request body
|
|
45
|
+
# @param headers [Hash] additional HTTP headers
|
|
46
|
+
# @return [Hash,String,nil] parsed JSON response or raw body
|
|
47
|
+
# @raise [EndpointError] when the endpoint returns a non-2xx status
|
|
27
48
|
def request(klass, path, json: nil, body: nil, headers: {})
|
|
28
|
-
|
|
29
|
-
|
|
49
|
+
req = build_request(klass, path, headers:, body:, json:)
|
|
50
|
+
response = @http.start(req.uri.host, req.uri.port, use_ssl: req.uri.scheme == 'https') do |http|
|
|
51
|
+
http.request(req)
|
|
30
52
|
end
|
|
31
53
|
|
|
32
54
|
success = response.code.to_i.between?(200, 299)
|
|
@@ -41,10 +63,10 @@ module Lambda
|
|
|
41
63
|
private
|
|
42
64
|
|
|
43
65
|
def build_uri(path)
|
|
44
|
-
base = URI(@url)
|
|
66
|
+
base = ::URI.parse(@url)
|
|
45
67
|
return base if path.nil? || path.empty? || path == '/'
|
|
46
68
|
|
|
47
|
-
joined = [base.path.sub(%r{/\z}, ''), path.sub(%r{\A/}, '')].reject(&:empty?).join('/')
|
|
69
|
+
joined = [base.path.sub(%r{/\z}, ''), path.sub(%r{\A/}, '')].reject(&:empty?).join('/').sub(%r{\A/+}, '')
|
|
48
70
|
base.path = "/#{joined}"
|
|
49
71
|
base
|
|
50
72
|
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module Lambda
|
|
6
|
+
module MicroVMs
|
|
7
|
+
# Thin wrapper over stable Aws::Lambda::Client function APIs.
|
|
8
|
+
class FunctionClient
|
|
9
|
+
attr_reader :sdk
|
|
10
|
+
|
|
11
|
+
def initialize(region: nil, profile: nil, sdk: nil, **)
|
|
12
|
+
@sdk = sdk || build_sdk(region: region, profile: profile, **)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Create a Lambda function from a zip file using supported AWS APIs.
|
|
16
|
+
#
|
|
17
|
+
# @param name [String] function name
|
|
18
|
+
# @param role_arn [String] IAM role ARN
|
|
19
|
+
# @param handler [String] handler name
|
|
20
|
+
# @param runtime [String] Lambda runtime identifier
|
|
21
|
+
# @param zip_file [String] path to deployment zip
|
|
22
|
+
# @param params [Hash] additional create_function parameters
|
|
23
|
+
# @return [Object] raw SDK response
|
|
24
|
+
def create_function(name:, role_arn:, handler:, runtime:, zip_file:, **params) # rubocop:disable Metrics/ParameterLists
|
|
25
|
+
sdk.create_function(
|
|
26
|
+
**params,
|
|
27
|
+
function_name: name,
|
|
28
|
+
role: role_arn,
|
|
29
|
+
handler: handler,
|
|
30
|
+
runtime: runtime,
|
|
31
|
+
code: { zip_file: File.binread(zip_file) }
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Update Lambda function code from a zip file.
|
|
36
|
+
#
|
|
37
|
+
# @param function_name [String] function name or ARN
|
|
38
|
+
# @param zip_file [String] path to deployment zip
|
|
39
|
+
# @param params [Hash] additional update_function_code parameters
|
|
40
|
+
# @return [Object] raw SDK response
|
|
41
|
+
def update_function_code(function_name:, zip_file:, **params)
|
|
42
|
+
sdk.update_function_code(**params, function_name: function_name, zip_file: File.binread(zip_file))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Invoke a Lambda function and parse JSON payloads when possible.
|
|
46
|
+
#
|
|
47
|
+
# @param function_name [String] function name or ARN
|
|
48
|
+
# @param payload [Object] request payload
|
|
49
|
+
# @param params [Hash] additional invoke parameters
|
|
50
|
+
# @return [Object] parsed response payload or raw response body
|
|
51
|
+
def invoke(function_name:, payload: nil, **params)
|
|
52
|
+
response = sdk.invoke(**params, function_name: function_name, payload: encode_payload(payload))
|
|
53
|
+
parse_payload(response.payload)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def build_sdk(region:, profile:, **options)
|
|
59
|
+
unless defined?(Aws::Lambda::Client)
|
|
60
|
+
raise LoadError, 'install aws-sdk-lambda to use Lambda::MicroVMs::FunctionClient'
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
args = options.dup
|
|
64
|
+
args[:region] = region if region
|
|
65
|
+
args[:profile] = profile if profile
|
|
66
|
+
Aws::Lambda::Client.new(**args)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def encode_payload(payload)
|
|
70
|
+
case payload
|
|
71
|
+
when nil
|
|
72
|
+
nil
|
|
73
|
+
when String
|
|
74
|
+
payload
|
|
75
|
+
else
|
|
76
|
+
JSON.generate(payload)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def parse_payload(payload)
|
|
81
|
+
body = payload.respond_to?(:read) ? payload.read : payload.to_s
|
|
82
|
+
return nil if body.empty?
|
|
83
|
+
|
|
84
|
+
JSON.parse(body)
|
|
85
|
+
rescue JSON::ParserError
|
|
86
|
+
body
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|