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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: de35523913c4cd5c11fc48549081c7901db9fdacfef2a72618264885596bb838
4
- data.tar.gz: 960ec7995dc496da3f358c6ad484eb0a420140afa727275b92e5eadf7201ce2b
3
+ metadata.gz: c2278501b187e57ed03eacd396649c19e09bd5c6e07e890f5ce0403588c8f2fb
4
+ data.tar.gz: c8f240355cdc41e80e5d1d31a231e31cac3e6fd77d488f584e1027df0a47db0a
5
5
  SHA512:
6
- metadata.gz: 055d951f5d0a5dc948195eff437d7b67070faf74e5dba4eee65b583aa70fda29a0452d93ecbe4e311bf3e7f5f18054d999776c6c5294bc2c88b061c8a777c77c
7
- data.tar.gz: 6948f65e7391fa14734dcd1f89757bfb6a1b2a04ee200566409de0851da3a80f41a7173607cbd6efa40a5bc31d09d1033237512eb145bf23358459871dd77da5
6
+ metadata.gz: 22bc5c257c1650f571cdf560a4c32af34cd119341776cb42a707104620255bbd0e26091b4967acc88462ca873e8319c5109aa9ee46fa26ceb0634207c32526bd
7
+ data.tar.gz: 942370b040e49ece5f159226f0da4094f05b054c7636210150f2858d768c863e9deb3997d72dff212c707542812134a230850f2ebef06b8478c9af089c288f72
data/CHANGELOG.md CHANGED
@@ -1,6 +1,15 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.0 Unreleased
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
  [![Ruby Version](https://img.shields.io/badge/ruby-%3E%3D%203.2-ruby.svg)](https://www.ruby-lang.org/en/)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
- A Ruby development kit for AWS Lambda MicroVMs.
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, and deployment commands for Lambda MicroVM applications.
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 `aws_lambda_ric` presence.
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
- This is an early implementation. Lambda MicroVMs is new, so generated AWS SDK operation shapes may evolve. Unsupported low-level operations raise `Lambda::MicroVMs::UnsupportedOperationError` with an upgrade hint.
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
- case command
20
- when 'version', '--version', '-v' then version
21
- when 'new' then new_project
22
- when 'package' then package
23
- when 'deploy' then deploy
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
- attr_reader :sdk
13
+ # SDK methods this wrapper expects from Aws::Lambda::Client.
14
+ REQUIRED_OPERATIONS = Adapters::MicroVMSdk::REQUIRED_OPERATIONS
14
15
 
15
- def initialize(region: nil, profile: nil, sdk: nil, **)
16
- @sdk = sdk || build_sdk(region:, profile:, **)
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
- unless @sdk.respond_to?(operation)
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: File.open(path, 'rb'))
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
- Check.new(name:, ok: value && value != '', detail: value || 'missing')
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
- response = @http.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
29
- http.request(build_request(klass, path, headers:, body:, json:))
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