Class: Lemans::Environments::Daytona
- Defined in:
- lib/lemans/environments/daytona.rb,
lib/lemans/environments/daytona/shell.rb,
lib/lemans/environments/daytona/retries.rb,
lib/lemans/environments/daytona/sdk_tweaks.rb,
lib/lemans/environments/daytona/snapshot_store.rb
Overview
Daytona sandboxes. Daytona builds images server-side into reusable content-named snapshots and enforces the network policy itself.
Defined Under Namespace
Modules: Retries, SdkTweaks Classes: Shell, SnapshotStore
Constant Summary collapse
- TTL_MINUTES =
120- DEFAULT_BUILD_TIMEOUT_SEC =
600- TRANSFER_TIMEOUT =
Workspace tarballs ride uploads/downloads, so transfers get their own budget through the SDK's streaming API instead of the global HTTP cap.
900- TRANSFER_SLOTS =
File transfers ride the SDK's typhoeus/libcurl stack, which segfaults the VM under enough concurrent easy_perform calls (a GC race on string options libcurl is still copying). Transfers are seconds each, so capping them costs little; execs and lifecycle stay fully parallel.
Concurrent::Semaphore.new(6)
- CLIENT =
Trials run concurrently, and an unsynchronised memo would build several SDK clients and leak all but one.
Concurrent::Delay.new { ::Daytona::Daytona.new(credentials) }
Constants inherited from Base
Instance Attribute Summary collapse
-
#sandbox ⇒ Object
readonly
Returns the value of attribute sandbox.
Attributes inherited from Base
#build_timeout_sec, #env, #image, #labels, #network, #resources
Class Method Summary collapse
- .client ⇒ Object
-
.credentials ⇒ Object
The CLI stores its key as DAYTONA_TOKEN, the SDK only reads DAYTONA_API_KEY; Config also resolves .env files, so go through it.
Instance Method Summary collapse
- #download(remote_path, local_path) ⇒ Object
- #exec(command, timeout: nil, env: {}) ⇒ Object
-
#initialize(image:, resources:, network:, env: {}, labels: {}, logger: nil, build_timeout_sec: nil) ⇒ Daytona
constructor
A new instance of Daytona.
- #network_policy=(policy) ⇒ Object
- #start ⇒ Object
-
#stop ⇒ Object
Deleting is the only cleanup that stops the meter.
- #upload(local_path, remote_path) ⇒ Object
Methods inherited from Base
Constructor Details
#initialize(image:, resources:, network:, env: {}, labels: {}, logger: nil, build_timeout_sec: nil) ⇒ Daytona
Returns a new instance of Daytona.
46 47 48 49 50 |
# File 'lib/lemans/environments/daytona.rb', line 46 def initialize(image:, resources:, network:, env: {}, labels: {}, logger: nil, build_timeout_sec: nil) super(image: image, resources: resources, network: network, env: env, labels: labels, build_timeout_sec: build_timeout_sec || DEFAULT_BUILD_TIMEOUT_SEC) @logger = logger end |
Instance Attribute Details
#sandbox ⇒ Object (readonly)
Returns the value of attribute sandbox.
28 29 30 |
# File 'lib/lemans/environments/daytona.rb', line 28 def sandbox @sandbox end |
Class Method Details
.client ⇒ Object
34 |
# File 'lib/lemans/environments/daytona.rb', line 34 def self.client = CLIENT.value! |
.credentials ⇒ Object
The CLI stores its key as DAYTONA_TOKEN, the SDK only reads DAYTONA_API_KEY; Config also resolves .env files, so go through it.
38 39 40 41 42 43 44 |
# File 'lib/lemans/environments/daytona.rb', line 38 def self.credentials config = ::Daytona::Config.new config.api_key ||= config.read_env("DAYTONA_TOKEN") raise ConfigError, "no Daytona credentials: set DAYTONA_API_KEY or DAYTONA_TOKEN" unless config.api_key || config.jwt_token config end |
Instance Method Details
#download(remote_path, local_path) ⇒ Object
81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/lemans/environments/daytona.rb', line 81 def download(remote_path, local_path) local_path = Pathname(local_path) local_path.dirname.mkpath transfer do local_path.open("wb") do |file| sandbox.fs.download_file_stream(remote_path.to_s, timeout: TRANSFER_TIMEOUT) { file.write(_1) } end end rescue *Retries::SDK_ERRORS, SystemCallError => e raise InfrastructureError, "daytona: could not download #{remote_path}: #{e.}" end |
#exec(command, timeout: nil, env: {}) ⇒ Object
63 64 65 66 67 |
# File 'lib/lemans/environments/daytona.rb', line 63 def exec(command, timeout: nil, env: {}) @shell.exec(command, timeout: timeout || DEFAULT_TIMEOUT, env: env) rescue *Retries::SDK_ERRORS => e raise InfrastructureError, "daytona: exec failed: #{e.}" end |
#network_policy=(policy) ⇒ Object
93 94 95 96 97 98 |
# File 'lib/lemans/environments/daytona.rb', line 93 def network_policy=(policy) sandbox.update_network_settings(**network_kwargs(policy, for_update: true)) @network = policy rescue *Retries::SDK_ERRORS => e raise InfrastructureError, "daytona: could not apply #{policy.mode} policy: #{e.}" end |
#start ⇒ Object
52 53 54 55 56 57 58 59 60 61 |
# File 'lib/lemans/environments/daytona.rb', line 52 def start @sandbox = client.create(create_params, on_snapshot_create_logs: @logger) @shell = Shell.new(sandbox) self rescue *Retries::SDK_ERRORS => e # A sandbox created but never handed over would bill until its TTL: # the caller's ensure can only stop an environment it received. stop raise InfrastructureError, "daytona: could not start sandbox: #{e.}" end |
#stop ⇒ Object
Deleting is the only cleanup that stops the meter. Never raises: cleanup must not replace an otherwise valid result with an exception.
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/lemans/environments/daytona.rb', line 102 def stop return if @sandbox.nil? id = @sandbox.id wait = true begin @sandbox.delete(wait:) @sandbox = nil rescue StandardError => e # VM shutdown: confirming destruction needs threads Ruby no longer # grants, but the bare DELETE needs none — the meter still stops. if e.is_a?(ThreadError) && wait wait = false retry end warn "lemans: sandbox #{id} may still be running — delete failed: #{e.class}: #{e.}" end end |
#upload(local_path, remote_path) ⇒ Object
69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/lemans/environments/daytona.rb', line 69 def upload(local_path, remote_path) transfer do # An open handle, not a path string: the SDK uploads a non-existent # path AS ITS OWN BYTES, so a missing file must die here as ENOENT. Pathname(local_path).open("rb") do |file| sandbox.fs.upload_file_stream(file, remote_path.to_s, timeout: TRANSFER_TIMEOUT) end end rescue *Retries::SDK_ERRORS => e raise InfrastructureError, "daytona: could not upload #{local_path}: #{e.}" end |