Class: Daytona::SnapshotService

Inherits:
Object
  • Object
show all
Includes:
Instrumentation
Defined in:
lib/daytona/snapshot_service.rb

Constant Summary collapse

SNAPSHOTS_FETCH_LIMIT =
200

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Instrumentation

included

Constructor Details

#initialize(snapshots_api:, object_storage_api:, default_region_id: nil, otel_state: nil) ⇒ SnapshotService

Returns a new instance of SnapshotService.

Parameters:

  • snapshots_api (DaytonaApiClient::SnapshotsApi)

    The snapshots API client

  • object_storage_api (DaytonaApiClient::ObjectStorageApi)

    The object storage API client

  • default_region_id (String, nil) (defaults to: nil)

    Default region ID for snapshot creation

  • otel_state (Daytona::OtelState, nil) (defaults to: nil)


18
19
20
21
22
23
# File 'lib/daytona/snapshot_service.rb', line 18

def initialize(snapshots_api:, object_storage_api:, default_region_id: nil, otel_state: nil)
  @snapshots_api = snapshots_api
  @object_storage_api = object_storage_api
  @default_region_id = default_region_id
  @otel_state = otel_state
end

Class Method Details

.process_image_context(object_storage_api, image) ⇒ Array<String>

Processes the image context by uploading it to object storage

Parameters:

Returns:

  • (Array<String>)

    List of context hashes stored in object storage



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/daytona/snapshot_service.rb', line 151

def self.process_image_context(object_storage_api, image) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
  return [] unless image.context_list && !image.context_list.empty?

  push_access_creds = object_storage_api.get_push_access

  object_storage = ObjectStorage.new(
    endpoint_url: push_access_creds.storage_url,
    aws_access_key_id: push_access_creds.access_key,
    aws_secret_access_key: push_access_creds.secret,
    aws_session_token: push_access_creds.session_token,
    bucket_name: push_access_creds.bucket,
    region: push_access_creds.region
  )

  image.context_list.map do |context|
    object_storage.upload(
      context.source_path,
      push_access_creds.organization_id,
      context.archive_path
    )
  end
end

Instance Method Details

#activate(snapshot) ⇒ Daytona::Snapshot

Activate a snapshot.

Parameters:

  • snapshot (Daytona::Snapshot, String)

    The Snapshot instance, or its ID or name. Call cost matches delete: 1 for an object or UUID string, 2 for a name, up to 3 for a UUID-formatted name (optimistic 404 then resolve + activate).

Returns:



141
142
143
# File 'lib/daytona/snapshot_service.rb', line 141

def activate(snapshot)
  Snapshot.from_dto(call_with_resolved_id(snapshot) { |id| snapshots_api.activate_snapshot(id) })
end

#create(params, on_logs: nil) ⇒ Daytona::Snapshot

Creates and registers a new snapshot from the given Image definition.

Examples:

image = Image.debianSlim('3.12').pipInstall('numpy')
params = CreateSnapshotParams.new(name: 'my-snapshot', image: image)
snapshot = daytona.snapshot.create(params) do |chunk|
  print chunk
end

Parameters:

  • params (Daytona::CreateSnapshotParams)

    Parameters for snapshot creation

  • on_logs (Proc, Nil) (defaults to: nil)

    Callback proc handling snapshot creation logs

Returns:



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
# File 'lib/daytona/snapshot_service.rb', line 92

def create(params, on_logs: nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
  create_snapshot_req = DaytonaApiClient::CreateSnapshot.new(name: params.name)

  if params.image.is_a?(String)
    create_snapshot_req.image_name = params.image
    create_snapshot_req.entrypoint = params.entrypoint
  else
    create_snapshot_req.build_info = DaytonaApiClient::CreateBuildInfo.new(
      context_hashes: self.class.process_image_context(object_storage_api, params.image),
      dockerfile_content: if params.entrypoint
                            params.image.entrypoint(params.entrypoint).dockerfile
                          else
                            params.image.dockerfile
                          end
    )
  end

  if params.resources
    create_snapshot_req.cpu = params.resources.cpu
    create_snapshot_req.gpu = params.resources.gpu
    if params.resources.gpu_type
      create_snapshot_req.gpu_type =
        params.resources.gpu_type.is_a?(Array) ? params.resources.gpu_type : [params.resources.gpu_type]
    end
    create_snapshot_req.memory = params.resources.memory
    create_snapshot_req.disk = params.resources.disk
  end

  create_snapshot_req.region_id = params.region_id || @default_region_id
  create_snapshot_req.sandbox_class = params.sandbox_class

  snapshot = snapshots_api.create_snapshot(create_snapshot_req)

  # Always wait for snapshot to be ready, regardless of on_logs
  snapshot = wait_for_snapshot(snapshot, on_logs:)

  if [DaytonaApiClient::SnapshotState::ERROR, DaytonaApiClient::SnapshotState::BUILD_FAILED].include?(snapshot.state)
    raise Sdk::Error, "Failed to create snapshot #{snapshot.name}, reason: #{snapshot.error_reason}"
  end

  Snapshot.from_dto(snapshot)
end

#delete(snapshot) ⇒ void

This method returns an undefined value.

Delete a Snapshot.

Examples:

daytona = Daytona::Daytona.new
snapshot = daytona.snapshot.get("demo")
daytona.snapshot.delete(snapshot)
puts "Snapshot deleted"

Parameters:

  • snapshot (Daytona::Snapshot, String)

    Snapshot to delete, or its ID or name. Call cost: 1 API call for a Snapshot object or UUID string; 2 for a name (resolve, then delete); up to 3 in the worst case for a UUID-formatted name (the optimistic delete 404s, then resolve + delete).



65
66
67
# File 'lib/daytona/snapshot_service.rb', line 65

def delete(snapshot)
  call_with_resolved_id(snapshot) { |id| snapshots_api.remove_snapshot(id) }
end

#get(name) ⇒ Daytona::Snapshot

Get a Snapshot by ID or name.

Examples:

daytona = Daytona::Daytona.new
snapshot = daytona.snapshot.get("demo")
puts "#{snapshot.name} (#{snapshot.image_name})"

Parameters:

  • name (String)

    ID or name of the Snapshot to get

Returns:



78
# File 'lib/daytona/snapshot_service.rb', line 78

def get(name) = Snapshot.from_dto(snapshots_api.get_snapshot(name))

#list(page: nil, limit: nil, source_sandbox_id: nil) ⇒ Daytona::PaginatedResource

List all Snapshots.

Examples:

daytona = Daytona::Daytona.new
page = daytona.snapshot.list(page: 2, limit: 10)
puts "Page #{page.page} of #{page.total_pages} (#{page.total} snapshots total)"
page.items.each { |snapshot| puts "#{snapshot.name} (#{snapshot.image_name})" }

Parameters:

  • page (Integer, Nil) (defaults to: nil)
  • limit (Integer, Nil) (defaults to: nil)
  • source_sandbox_id (String, Nil) (defaults to: nil)

    Filter by the ID of the sandbox the snapshot was created from

Returns:

Raises:



38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/daytona/snapshot_service.rb', line 38

def list(page: nil, limit: nil, source_sandbox_id: nil)
  raise Sdk::Error, 'page must be positive integer' if page && page < 1

  raise Sdk::Error, 'limit must be positive integer' if limit && limit < 1

  response = snapshots_api.get_all_snapshots(page:, limit:, source_sandbox_id:)
  PaginatedResource.new(
    total: response.total,
    page: response.page,
    total_pages: response.total_pages,
    items: response.items.map { |snapshot_dto| Snapshot.from_dto(snapshot_dto) }
  )
end