lambda-microvms 0.0.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 +7 -0
- data/CHANGELOG.md +14 -0
- data/LICENSE.txt +21 -0
- data/README.md +179 -0
- data/exe/lambda-microvms +102 -0
- data/lib/lambda/microvms/client.rb +93 -0
- data/lib/lambda/microvms/deployer.rb +58 -0
- data/lib/lambda/microvms/doctor.rb +62 -0
- data/lib/lambda/microvms/endpoint.rb +73 -0
- data/lib/lambda/microvms/error.rb +31 -0
- data/lib/lambda/microvms/image.rb +48 -0
- data/lib/lambda/microvms/microvm.rb +111 -0
- data/lib/lambda/microvms/packager.rb +35 -0
- data/lib/lambda/microvms/project.rb +104 -0
- data/lib/lambda/microvms/scaffold.rb +162 -0
- data/lib/lambda/microvms/session.rb +31 -0
- data/lib/lambda/microvms/util.rb +34 -0
- data/lib/lambda/microvms/version.rb +7 -0
- data/lib/lambda/microvms/waiter.rb +33 -0
- data/lib/lambda/microvms.rb +27 -0
- data/sig/lambda/microvms.rbs +153 -0
- metadata +113 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lambda
|
|
4
|
+
module MicroVMs
|
|
5
|
+
# Represents one running, suspended, or terminated Lambda MicroVM session.
|
|
6
|
+
class MicroVM
|
|
7
|
+
attr_reader :client, :id, :data
|
|
8
|
+
|
|
9
|
+
def self.from_response(client:, response:)
|
|
10
|
+
id = Util.extract(response, :microvm_id, :microvm_arn, :id, :arn)
|
|
11
|
+
new(client: client, id: id, data: response)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def initialize(client:, id:, data: nil)
|
|
15
|
+
@client = client
|
|
16
|
+
@id = id
|
|
17
|
+
@data = data
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def refresh
|
|
21
|
+
fresh = client.get_microvm(microvm_id: id)
|
|
22
|
+
@data = fresh.data
|
|
23
|
+
self
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def state
|
|
27
|
+
Util.normalize_state(Util.extract(@data, :state, :status, :microvm_state))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def running?
|
|
31
|
+
state == :running
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def suspended?
|
|
35
|
+
state == :suspended
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def terminated?
|
|
39
|
+
state == :terminated
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def pending?
|
|
43
|
+
state == :pending
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def endpoint_url
|
|
47
|
+
endpoint = Util.extract(@data, :endpoint, :endpoint_url, :url)
|
|
48
|
+
return endpoint if endpoint.is_a?(String)
|
|
49
|
+
|
|
50
|
+
Util.extract(endpoint, :url, :endpoint_url) if endpoint
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def auth_token(ports: nil, ttl_seconds: nil, **params)
|
|
54
|
+
request = params.merge(microvm_id: id)
|
|
55
|
+
request[:ports] = ports if ports
|
|
56
|
+
request[:ttl_seconds] = ttl_seconds if ttl_seconds
|
|
57
|
+
response = client.create_auth_token(**request)
|
|
58
|
+
Util.extract(response, :token, :auth_token, :microvm_auth_token)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def endpoint(token: nil, **token_params)
|
|
62
|
+
Endpoint.new(url: endpoint_url, token: token || auth_token(**token_params))
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def get(path, token: nil, **)
|
|
66
|
+
endpoint(token: token).get(path, **)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def post(path, json: nil, body: nil, token: nil, **)
|
|
70
|
+
endpoint(token: token).post(path, json: json, body: body, **)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def suspend(**params)
|
|
74
|
+
client.suspend_microvm(**params, microvm_id: id)
|
|
75
|
+
self
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def resume(**params)
|
|
79
|
+
response = client.resume_microvm(**params, microvm_id: id)
|
|
80
|
+
@data = response if response
|
|
81
|
+
self
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def terminate(**params)
|
|
85
|
+
client.terminate_microvm(**params, microvm_id: id)
|
|
86
|
+
self
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def wait_until_running(delay: Waiter::DEFAULT_DELAY, timeout: Waiter::DEFAULT_TIMEOUT)
|
|
90
|
+
Waiter.new(delay: delay, timeout: timeout).wait(message: "MicroVM #{id} to be running") do
|
|
91
|
+
refresh.running?
|
|
92
|
+
end
|
|
93
|
+
self
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def wait_until_suspended(delay: Waiter::DEFAULT_DELAY, timeout: Waiter::DEFAULT_TIMEOUT)
|
|
97
|
+
Waiter.new(delay: delay, timeout: timeout).wait(message: "MicroVM #{id} to be suspended") do
|
|
98
|
+
refresh.suspended?
|
|
99
|
+
end
|
|
100
|
+
self
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def wait_until_terminated(delay: Waiter::DEFAULT_DELAY, timeout: Waiter::DEFAULT_TIMEOUT)
|
|
104
|
+
Waiter.new(delay: delay, timeout: timeout).wait(message: "MicroVM #{id} to be terminated") do
|
|
105
|
+
refresh.terminated?
|
|
106
|
+
end
|
|
107
|
+
self
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'open3'
|
|
5
|
+
require 'pathname'
|
|
6
|
+
|
|
7
|
+
module Lambda
|
|
8
|
+
module MicroVMs
|
|
9
|
+
# Creates a deployable source artifact for Lambda MicroVM image creation.
|
|
10
|
+
class Packager
|
|
11
|
+
DEFAULT_EXCLUDES = ['.git/*', 'tmp/*', 'vendor/bundle/*', '*.gem'].freeze
|
|
12
|
+
|
|
13
|
+
attr_reader :project
|
|
14
|
+
|
|
15
|
+
def initialize(project)
|
|
16
|
+
@project = project
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def package(output: project.artifact_path)
|
|
20
|
+
FileUtils.mkdir_p(File.dirname(output))
|
|
21
|
+
relative_output = begin
|
|
22
|
+
Pathname.new(output).relative_path_from(Pathname.new(project.root)).to_s
|
|
23
|
+
rescue StandardError
|
|
24
|
+
output
|
|
25
|
+
end
|
|
26
|
+
excludes = DEFAULT_EXCLUDES + [relative_output]
|
|
27
|
+
command = ['zip', '-q', '-r', output, '.'] + excludes.flat_map { |pattern| ['-x', pattern] }
|
|
28
|
+
stdout, stderr, status = Open3.capture3(*command, chdir: project.root)
|
|
29
|
+
raise CommandError, "zip failed: #{stderr.empty? ? stdout : stderr}" unless status.success?
|
|
30
|
+
|
|
31
|
+
output
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require 'tmpdir'
|
|
6
|
+
|
|
7
|
+
module Lambda
|
|
8
|
+
module MicroVMs
|
|
9
|
+
# Reads lambda-microvms project configuration from microvm.yml.
|
|
10
|
+
class Project
|
|
11
|
+
DEFAULT_CONFIG = 'microvm.yml'
|
|
12
|
+
|
|
13
|
+
attr_reader :root, :config_path, :config
|
|
14
|
+
|
|
15
|
+
def self.load(path = DEFAULT_CONFIG)
|
|
16
|
+
config_path = File.expand_path(path)
|
|
17
|
+
root = File.dirname(config_path)
|
|
18
|
+
new(root: root, config_path: config_path,
|
|
19
|
+
config: YAML.safe_load_file(config_path, permitted_classes: [Symbol], aliases: true) || {})
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def initialize(root:, config_path:, config:)
|
|
23
|
+
@root = root
|
|
24
|
+
@config_path = config_path
|
|
25
|
+
@config = stringify_keys(config)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def name = fetch('name', default: File.basename(root))
|
|
29
|
+
|
|
30
|
+
def region = fetch('region', default: ENV.fetch('AWS_REGION', nil))
|
|
31
|
+
|
|
32
|
+
def profile = fetch('profile', default: ENV.fetch('AWS_PROFILE', nil))
|
|
33
|
+
|
|
34
|
+
def role_arn = fetch('role_arn', 'role', default: nil)
|
|
35
|
+
|
|
36
|
+
def image_arn = fetch('image_arn', 'image.arn', default: nil)
|
|
37
|
+
|
|
38
|
+
def image_name = fetch('image.name', default: name)
|
|
39
|
+
|
|
40
|
+
def dockerfile = File.expand_path(fetch('build.dockerfile', default: 'Dockerfile'), root)
|
|
41
|
+
|
|
42
|
+
def build_context = File.expand_path(fetch('build.context', default: '.'), root)
|
|
43
|
+
|
|
44
|
+
def artifact_path = File.expand_path(fetch('build.artifact', default: "tmp/#{name}-microvm.zip"), root)
|
|
45
|
+
|
|
46
|
+
def s3_bucket = fetch('deployment.bucket', 's3.bucket', default: nil)
|
|
47
|
+
|
|
48
|
+
def s3_prefix = fetch('deployment.prefix', 's3.prefix', default: "lambda-microvms/#{name}")
|
|
49
|
+
|
|
50
|
+
def lifecycle_after = fetch('runtime.after', default: 'suspend').to_sym
|
|
51
|
+
|
|
52
|
+
def payload
|
|
53
|
+
fetch('runtime.payload', default: {}) || {}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def create_image_params(artifact_uri:)
|
|
57
|
+
params = fetch('image.create', default: {}) || {}
|
|
58
|
+
params = stringify_keys(params)
|
|
59
|
+
symbolized = params.to_h { |key, value| [key.to_sym, value] }
|
|
60
|
+
symbolized[:name] ||= image_name
|
|
61
|
+
symbolized[:code_artifact] ||= artifact_uri
|
|
62
|
+
symbolized
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def run_params
|
|
66
|
+
params = stringify_keys(fetch('runtime.run', default: {}) || {})
|
|
67
|
+
symbolized = params.to_h { |key, value| [key.to_sym, value] }
|
|
68
|
+
symbolized[:role_arn] ||= role_arn if role_arn
|
|
69
|
+
symbolized[:payload] ||= payload unless payload.empty?
|
|
70
|
+
symbolized
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def require!(field, value)
|
|
74
|
+
return value if value && value != ''
|
|
75
|
+
|
|
76
|
+
raise ConfigurationError, "missing required project configuration: #{field}"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def fetch(*paths, default:)
|
|
82
|
+
paths.each do |path|
|
|
83
|
+
current = @config
|
|
84
|
+
path.to_s.split('.').each do |part|
|
|
85
|
+
current = current[part] if current.respond_to?(:[])
|
|
86
|
+
end
|
|
87
|
+
return current unless current.nil?
|
|
88
|
+
end
|
|
89
|
+
default
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def stringify_keys(value)
|
|
93
|
+
case value
|
|
94
|
+
when Hash
|
|
95
|
+
value.to_h { |key, child| [key.to_s, stringify_keys(child)] }
|
|
96
|
+
when Array
|
|
97
|
+
value.map { |child| stringify_keys(child) }
|
|
98
|
+
else
|
|
99
|
+
value
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
|
|
5
|
+
module Lambda
|
|
6
|
+
module MicroVMs
|
|
7
|
+
# Generates Ruby Lambda MicroVM starter projects.
|
|
8
|
+
class Scaffold
|
|
9
|
+
attr_reader :name, :directory
|
|
10
|
+
|
|
11
|
+
def initialize(name, directory: name, force: false)
|
|
12
|
+
@name = name
|
|
13
|
+
@directory = directory
|
|
14
|
+
@force = force
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def create
|
|
18
|
+
ensure_target!
|
|
19
|
+
FileUtils.mkdir_p(directory)
|
|
20
|
+
write('Gemfile', gemfile)
|
|
21
|
+
write('Dockerfile', dockerfile)
|
|
22
|
+
write('app.rb', app_rb)
|
|
23
|
+
write('microvm.yml', microvm_yml)
|
|
24
|
+
write('README.md', readme)
|
|
25
|
+
write('.env.example', env_example)
|
|
26
|
+
directory
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def ensure_target!
|
|
32
|
+
return if @force || !File.exist?(directory) || Dir.empty?(directory)
|
|
33
|
+
|
|
34
|
+
raise ConfigurationError, "target directory is not empty: #{directory}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def write(path, content)
|
|
38
|
+
File.write(File.join(directory, path), content)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def gemfile
|
|
42
|
+
<<~GEMFILE
|
|
43
|
+
source "https://rubygems.org"
|
|
44
|
+
|
|
45
|
+
gem "aws_lambda_ric"
|
|
46
|
+
gem "json"
|
|
47
|
+
GEMFILE
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def dockerfile
|
|
51
|
+
<<~DOCKERFILE
|
|
52
|
+
FROM ruby:3.4-slim
|
|
53
|
+
|
|
54
|
+
ENV LAMBDA_TASK_ROOT=/var/task
|
|
55
|
+
WORKDIR ${LAMBDA_TASK_ROOT}
|
|
56
|
+
|
|
57
|
+
RUN apt-get update \\
|
|
58
|
+
&& apt-get install -y --no-install-recommends build-essential \\
|
|
59
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
60
|
+
|
|
61
|
+
COPY Gemfile ${LAMBDA_TASK_ROOT}/Gemfile
|
|
62
|
+
RUN bundle install
|
|
63
|
+
|
|
64
|
+
COPY app.rb ${LAMBDA_TASK_ROOT}/app.rb
|
|
65
|
+
|
|
66
|
+
ENTRYPOINT ["bundle", "exec", "aws_lambda_ric"]
|
|
67
|
+
CMD ["app.App::Handler.process"]
|
|
68
|
+
DOCKERFILE
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def app_rb
|
|
72
|
+
<<~RUBY
|
|
73
|
+
# frozen_string_literal: true
|
|
74
|
+
|
|
75
|
+
require "json"
|
|
76
|
+
|
|
77
|
+
module App
|
|
78
|
+
class Handler
|
|
79
|
+
def self.process(event:, context:)
|
|
80
|
+
{
|
|
81
|
+
ok: true,
|
|
82
|
+
message: "Hello from #{name}",
|
|
83
|
+
event: event,
|
|
84
|
+
request_id: context.respond_to?(:aws_request_id) ? context.aws_request_id : nil
|
|
85
|
+
}
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
RUBY
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def microvm_yml
|
|
93
|
+
<<~YAML
|
|
94
|
+
name: #{name}
|
|
95
|
+
region: us-east-1
|
|
96
|
+
|
|
97
|
+
# Optional: AWS profile to use for CLI-driven commands.
|
|
98
|
+
# profile: default
|
|
99
|
+
|
|
100
|
+
# IAM role used when running the MicroVM.
|
|
101
|
+
role_arn: arn:aws:iam::123456789012:role/lambda-microvm-runtime
|
|
102
|
+
|
|
103
|
+
build:
|
|
104
|
+
dockerfile: Dockerfile
|
|
105
|
+
context: .
|
|
106
|
+
artifact: tmp/#{name}-microvm.zip
|
|
107
|
+
|
|
108
|
+
deployment:
|
|
109
|
+
bucket: replace-me-lambda-microvm-artifacts
|
|
110
|
+
prefix: lambda-microvms/#{name}
|
|
111
|
+
|
|
112
|
+
image:
|
|
113
|
+
name: #{name}
|
|
114
|
+
# arn: arn:aws:lambda:us-east-1:123456789012:microvm-image:#{name}
|
|
115
|
+
create:
|
|
116
|
+
# Keep this hash close to the AWS Lambda MicroVM create image API shape.
|
|
117
|
+
# lambda-microvms deploy injects code_artifact after S3 upload.
|
|
118
|
+
name: #{name}
|
|
119
|
+
|
|
120
|
+
runtime:
|
|
121
|
+
after: suspend
|
|
122
|
+
payload:
|
|
123
|
+
source: lambda-microvms
|
|
124
|
+
YAML
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def readme
|
|
128
|
+
<<~README
|
|
129
|
+
# #{name}
|
|
130
|
+
|
|
131
|
+
Ruby AWS Lambda MicroVM application scaffold generated by `lambda-microvms`.
|
|
132
|
+
|
|
133
|
+
This project uses `aws_lambda_ric` inside the image so the Ruby handler can speak the Lambda Runtime API.
|
|
134
|
+
|
|
135
|
+
## Local setup
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
bundle install
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Build / deploy from the parent toolkit
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
lambda-microvms doctor
|
|
145
|
+
lambda-microvms package
|
|
146
|
+
lambda-microvms deploy
|
|
147
|
+
lambda-microvms run
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Edit `microvm.yml` before deploying.
|
|
151
|
+
README
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def env_example
|
|
155
|
+
<<~ENV
|
|
156
|
+
AWS_REGION=us-east-1
|
|
157
|
+
AWS_PROFILE=default
|
|
158
|
+
ENV
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lambda
|
|
4
|
+
module MicroVMs
|
|
5
|
+
# Convenience API for run/use/cleanup MicroVM sessions.
|
|
6
|
+
module Session
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def session(image_arn:, role_arn:, after: :suspend, client: Client.new, **run_options)
|
|
10
|
+
vm = client.image(image_arn).run(role_arn: role_arn, **run_options)
|
|
11
|
+
vm.wait_until_running
|
|
12
|
+
yield vm
|
|
13
|
+
ensure
|
|
14
|
+
cleanup(vm, after) if vm
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def cleanup(vm, after) # rubocop:disable Naming/MethodParameterName
|
|
18
|
+
case after
|
|
19
|
+
when :keep, nil
|
|
20
|
+
nil
|
|
21
|
+
when :suspend
|
|
22
|
+
vm.suspend
|
|
23
|
+
when :terminate
|
|
24
|
+
vm.terminate
|
|
25
|
+
else
|
|
26
|
+
raise ArgumentError, "unknown cleanup policy: #{after.inspect}"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lambda
|
|
4
|
+
module MicroVMs
|
|
5
|
+
# Internal helpers shared by resource objects.
|
|
6
|
+
module Util
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def extract(value, *keys)
|
|
10
|
+
keys.each do |key|
|
|
11
|
+
if value.respond_to?(key)
|
|
12
|
+
result = value.public_send(key)
|
|
13
|
+
return result unless result.nil?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
next unless value.respond_to?(:[])
|
|
17
|
+
|
|
18
|
+
begin
|
|
19
|
+
result = value[key]
|
|
20
|
+
return result unless result.nil?
|
|
21
|
+
rescue KeyError, TypeError
|
|
22
|
+
nil
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def normalize_state(value)
|
|
30
|
+
value.to_s.downcase.to_sym
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lambda
|
|
4
|
+
module MicroVMs
|
|
5
|
+
# Polls a resource until a desired lifecycle state is reached.
|
|
6
|
+
class Waiter
|
|
7
|
+
DEFAULT_DELAY = 1.0
|
|
8
|
+
DEFAULT_TIMEOUT = 60.0
|
|
9
|
+
|
|
10
|
+
def initialize(delay: DEFAULT_DELAY, timeout: DEFAULT_TIMEOUT, sleeper: Kernel)
|
|
11
|
+
@delay = delay
|
|
12
|
+
@timeout = timeout
|
|
13
|
+
@sleeper = sleeper
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def wait(message: 'condition')
|
|
17
|
+
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + @timeout
|
|
18
|
+
|
|
19
|
+
loop do
|
|
20
|
+
value = yield
|
|
21
|
+
return value if value
|
|
22
|
+
|
|
23
|
+
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
24
|
+
break if now >= deadline
|
|
25
|
+
|
|
26
|
+
@sleeper.sleep([@delay, deadline - now].min)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
raise WaitTimeoutError, "timed out waiting for #{message}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'microvms/version'
|
|
4
|
+
require_relative 'microvms/error'
|
|
5
|
+
require_relative 'microvms/util'
|
|
6
|
+
require_relative 'microvms/waiter'
|
|
7
|
+
require_relative 'microvms/endpoint'
|
|
8
|
+
require_relative 'microvms/client'
|
|
9
|
+
require_relative 'microvms/image'
|
|
10
|
+
require_relative 'microvms/microvm'
|
|
11
|
+
require_relative 'microvms/session'
|
|
12
|
+
require_relative 'microvms/project'
|
|
13
|
+
require_relative 'microvms/scaffold'
|
|
14
|
+
require_relative 'microvms/packager'
|
|
15
|
+
require_relative 'microvms/deployer'
|
|
16
|
+
require_relative 'microvms/doctor'
|
|
17
|
+
|
|
18
|
+
module Lambda
|
|
19
|
+
# Idiomatic Ruby lifecycle helpers for AWS Lambda MicroVMs.
|
|
20
|
+
module MicroVMs
|
|
21
|
+
module_function
|
|
22
|
+
|
|
23
|
+
def session(...)
|
|
24
|
+
Session.session(...)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
module Lambda
|
|
2
|
+
module MicroVMs
|
|
3
|
+
VERSION: String
|
|
4
|
+
|
|
5
|
+
def self.session: (image_arn: String, role_arn: String, ?after: Symbol, ?client: Client, **untyped) { (MicroVM) -> untyped } -> untyped
|
|
6
|
+
|
|
7
|
+
class Error < StandardError
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class WaitTimeoutError < Error
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class UnsupportedOperationError < Error
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class ConfigurationError < Error
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class CommandError < Error
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class EndpointError < Error
|
|
23
|
+
attr_reader status: Integer
|
|
24
|
+
attr_reader body: String?
|
|
25
|
+
def initialize: (String message, status: Integer, body: String?) -> void
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class Client
|
|
29
|
+
attr_reader sdk: untyped
|
|
30
|
+
def initialize: (?region: String?, ?profile: String?, ?sdk: untyped, **untyped) -> void
|
|
31
|
+
def image: (String arn) -> Image
|
|
32
|
+
def microvm: (String id_or_arn) -> MicroVM
|
|
33
|
+
def create_image: (**untyped) -> Image
|
|
34
|
+
def get_image: (**untyped) -> Image
|
|
35
|
+
def delete_image: (**untyped) -> untyped
|
|
36
|
+
def run: (**untyped) -> MicroVM
|
|
37
|
+
def run_microvm: (**untyped) -> MicroVM
|
|
38
|
+
def get_microvm: (**untyped) -> MicroVM
|
|
39
|
+
def list_microvms: (**untyped) -> untyped
|
|
40
|
+
def suspend_microvm: (**untyped) -> untyped
|
|
41
|
+
def resume_microvm: (**untyped) -> untyped
|
|
42
|
+
def terminate_microvm: (**untyped) -> untyped
|
|
43
|
+
def create_auth_token: (**untyped) -> untyped
|
|
44
|
+
def create_microvm_auth_token: (**untyped) -> untyped
|
|
45
|
+
def call_sdk: (Symbol operation, **untyped) -> untyped
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class Image
|
|
49
|
+
attr_reader client: Client
|
|
50
|
+
attr_reader arn: String?
|
|
51
|
+
attr_reader data: untyped
|
|
52
|
+
def self.from_response: (client: Client, response: untyped) -> Image
|
|
53
|
+
def initialize: (client: Client, arn: String?, ?data: untyped) -> void
|
|
54
|
+
def refresh: () -> Image
|
|
55
|
+
def state: () -> Symbol
|
|
56
|
+
def ready?: () -> bool
|
|
57
|
+
def wait_until_ready: (?delay: Numeric, ?timeout: Numeric) -> Image
|
|
58
|
+
def run: (role_arn: String, ?payload: untyped, **untyped) -> MicroVM
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
class MicroVM
|
|
62
|
+
attr_reader client: Client
|
|
63
|
+
attr_reader id: String?
|
|
64
|
+
attr_reader data: untyped
|
|
65
|
+
def self.from_response: (client: Client, response: untyped) -> MicroVM
|
|
66
|
+
def initialize: (client: Client, id: String?, ?data: untyped) -> void
|
|
67
|
+
def refresh: () -> MicroVM
|
|
68
|
+
def state: () -> Symbol
|
|
69
|
+
def running?: () -> bool
|
|
70
|
+
def suspended?: () -> bool
|
|
71
|
+
def terminated?: () -> bool
|
|
72
|
+
def pending?: () -> bool
|
|
73
|
+
def endpoint_url: () -> String?
|
|
74
|
+
def auth_token: (?ports: untyped, ?ttl_seconds: Integer?, **untyped) -> untyped
|
|
75
|
+
def endpoint: (?token: String?, **untyped) -> Endpoint
|
|
76
|
+
def get: (String path, ?token: String?, **untyped) -> untyped
|
|
77
|
+
def post: (String path, ?json: untyped, ?body: String?, ?token: String?, **untyped) -> untyped
|
|
78
|
+
def suspend: (**untyped) -> MicroVM
|
|
79
|
+
def resume: (**untyped) -> MicroVM
|
|
80
|
+
def terminate: (**untyped) -> MicroVM
|
|
81
|
+
def wait_until_running: (?delay: Numeric, ?timeout: Numeric) -> MicroVM
|
|
82
|
+
def wait_until_suspended: (?delay: Numeric, ?timeout: Numeric) -> MicroVM
|
|
83
|
+
def wait_until_terminated: (?delay: Numeric, ?timeout: Numeric) -> MicroVM
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class Endpoint
|
|
87
|
+
attr_reader url: String
|
|
88
|
+
def initialize: (url: String, token: String?, ?http: untyped) -> void
|
|
89
|
+
def get: (String path, ?headers: Hash[String, String]) -> untyped
|
|
90
|
+
def post: (String path, ?json: untyped, ?body: String?, ?headers: Hash[String, String]) -> untyped
|
|
91
|
+
def request: (singleton(Net::HTTPGenericRequest) klass, String path, ?json: untyped, ?body: String?, ?headers: Hash[String, String]) -> untyped
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
class Project
|
|
95
|
+
DEFAULT_CONFIG: String
|
|
96
|
+
attr_reader root: String
|
|
97
|
+
attr_reader config_path: String
|
|
98
|
+
attr_reader config: Hash[String, untyped]
|
|
99
|
+
def self.load: (?String path) -> Project
|
|
100
|
+
def initialize: (root: String, config_path: String, config: Hash[untyped, untyped]) -> void
|
|
101
|
+
def name: () -> String
|
|
102
|
+
def region: () -> String?
|
|
103
|
+
def profile: () -> String?
|
|
104
|
+
def role_arn: () -> String?
|
|
105
|
+
def image_arn: () -> String?
|
|
106
|
+
def image_name: () -> String
|
|
107
|
+
def dockerfile: () -> String
|
|
108
|
+
def build_context: () -> String
|
|
109
|
+
def artifact_path: () -> String
|
|
110
|
+
def s3_bucket: () -> String?
|
|
111
|
+
def s3_prefix: () -> String
|
|
112
|
+
def lifecycle_after: () -> Symbol
|
|
113
|
+
def payload: () -> Hash[String, untyped]
|
|
114
|
+
def create_image_params: (artifact_uri: String) -> Hash[Symbol, untyped]
|
|
115
|
+
def run_params: () -> Hash[Symbol, untyped]
|
|
116
|
+
def require!: (String field, untyped value) -> untyped
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
class Scaffold
|
|
120
|
+
attr_reader name: String
|
|
121
|
+
attr_reader directory: String
|
|
122
|
+
def initialize: (String name, ?directory: String, ?force: bool) -> void
|
|
123
|
+
def create: () -> String
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
class Packager
|
|
127
|
+
DEFAULT_EXCLUDES: Array[String]
|
|
128
|
+
attr_reader project: Project
|
|
129
|
+
def initialize: (Project project) -> void
|
|
130
|
+
def package: (?output: String) -> String
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
class Deployer
|
|
134
|
+
attr_reader project: Project
|
|
135
|
+
attr_reader client: Client
|
|
136
|
+
attr_reader s3: untyped
|
|
137
|
+
def initialize: (project: Project, ?client: untyped, ?s3: untyped) -> void
|
|
138
|
+
def package: () -> String
|
|
139
|
+
def upload: (String path) -> String
|
|
140
|
+
def deploy: () -> Image
|
|
141
|
+
def run: () -> MicroVM
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
class Doctor
|
|
145
|
+
Check: untyped
|
|
146
|
+
attr_reader project: Project
|
|
147
|
+
attr_reader runner: untyped
|
|
148
|
+
def initialize: (project: Project, ?runner: untyped) -> void
|
|
149
|
+
def checks: () -> Array[untyped]
|
|
150
|
+
def ok?: () -> bool
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|