microsandbox-rb 0.5.7

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.
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Microsandbox
4
+ # A directory entry returned by {FS#list}.
5
+ class FsEntry
6
+ # @return [String] absolute guest path
7
+ attr_reader :path
8
+ # @return [Symbol] one of :file, :directory, :symlink, :other
9
+ attr_reader :type
10
+ # @return [Integer] size in bytes
11
+ attr_reader :size
12
+ # @return [Integer] POSIX mode bits
13
+ attr_reader :mode
14
+
15
+ def initialize(data)
16
+ @path = data["path"]
17
+ @type = data["type"].to_sym
18
+ @size = data["size"]
19
+ @mode = data["mode"]
20
+ @modified_ms = data["modified_ms"]
21
+ end
22
+
23
+ # @return [String] the final path component
24
+ def name
25
+ File.basename(@path)
26
+ end
27
+
28
+ # @return [Time, nil] last-modified time, if known
29
+ def modified
30
+ @modified_ms && Time.at(@modified_ms / 1000.0)
31
+ end
32
+
33
+ def file? = @type == :file
34
+ def directory? = @type == :directory
35
+ def symlink? = @type == :symlink
36
+
37
+ def inspect
38
+ "#<Microsandbox::FsEntry path=#{@path.inspect} type=#{@type} size=#{@size}>"
39
+ end
40
+ end
41
+
42
+ # File/directory metadata returned by {FS#stat}.
43
+ class FsMetadata
44
+ # @return [Symbol] one of :file, :directory, :symlink, :other
45
+ attr_reader :type
46
+ # @return [Integer] size in bytes
47
+ attr_reader :size
48
+ # @return [Integer] POSIX mode bits
49
+ attr_reader :mode
50
+
51
+ def initialize(data)
52
+ @type = data["type"].to_sym
53
+ @size = data["size"]
54
+ @mode = data["mode"]
55
+ @readonly = data["readonly"]
56
+ @modified_ms = data["modified_ms"]
57
+ @created_ms = data["created_ms"]
58
+ end
59
+
60
+ def readonly? = @readonly
61
+ def file? = @type == :file
62
+ def directory? = @type == :directory
63
+ def symlink? = @type == :symlink
64
+
65
+ # @return [Time, nil] last-modified time, if known
66
+ def modified
67
+ @modified_ms && Time.at(@modified_ms / 1000.0)
68
+ end
69
+
70
+ # @return [Time, nil] creation time, if known
71
+ def created
72
+ @created_ms && Time.at(@created_ms / 1000.0)
73
+ end
74
+
75
+ def inspect
76
+ "#<Microsandbox::FsMetadata type=#{@type} size=#{@size} mode=#{format("%o", @mode)}>"
77
+ end
78
+ end
79
+
80
+ # Guest filesystem operations for a running sandbox. Obtain via {Sandbox#fs}.
81
+ # All paths are paths *inside* the guest VM.
82
+ class FS
83
+ def initialize(native)
84
+ @native = native
85
+ end
86
+
87
+ # Read a file as raw bytes (ASCII-8BIT).
88
+ # @return [String]
89
+ def read(path)
90
+ @native.fs_read(path.to_s)
91
+ end
92
+
93
+ # Read a file as a UTF-8 string.
94
+ # @return [String]
95
+ def read_text(path)
96
+ @native.fs_read_text(path.to_s)
97
+ end
98
+
99
+ # Write data (a String) to a file, creating or truncating it.
100
+ # @return [nil]
101
+ def write(path, data)
102
+ @native.fs_write(path.to_s, data.to_s)
103
+ nil
104
+ end
105
+
106
+ # List the entries of a directory.
107
+ # @return [Array<FsEntry>]
108
+ def list(path)
109
+ @native.fs_list(path.to_s).map { |entry| FsEntry.new(entry) }
110
+ end
111
+
112
+ # Create a directory (and any missing parents).
113
+ # @return [nil]
114
+ def mkdir(path)
115
+ @native.fs_mkdir(path.to_s)
116
+ nil
117
+ end
118
+
119
+ # Remove a single file.
120
+ # @return [nil]
121
+ def remove(path)
122
+ @native.fs_remove(path.to_s)
123
+ nil
124
+ end
125
+
126
+ # Remove a directory recursively.
127
+ # @return [nil]
128
+ def remove_dir(path)
129
+ @native.fs_remove_dir(path.to_s)
130
+ nil
131
+ end
132
+
133
+ # Copy a file within the guest.
134
+ # @return [nil]
135
+ def copy(src, dst)
136
+ @native.fs_copy(src.to_s, dst.to_s)
137
+ nil
138
+ end
139
+
140
+ # Rename/move a file or directory within the guest.
141
+ # @return [nil]
142
+ def rename(src, dst)
143
+ @native.fs_rename(src.to_s, dst.to_s)
144
+ nil
145
+ end
146
+
147
+ # @return [Boolean] whether the path exists in the guest
148
+ def exists?(path)
149
+ @native.fs_exists(path.to_s)
150
+ end
151
+
152
+ # Stat a path.
153
+ # @return [FsMetadata]
154
+ def stat(path)
155
+ FsMetadata.new(@native.fs_stat(path.to_s))
156
+ end
157
+
158
+ # Copy a file from the host into the guest.
159
+ # @return [nil]
160
+ def copy_from_host(host_path, guest_path)
161
+ @native.fs_copy_from_host(host_path.to_s, guest_path.to_s)
162
+ nil
163
+ end
164
+
165
+ # Copy a file from the guest to the host.
166
+ # @return [nil]
167
+ def copy_to_host(guest_path, host_path)
168
+ @native.fs_copy_to_host(guest_path.to_s, host_path.to_s)
169
+ nil
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Microsandbox
4
+ # Metadata for a cached OCI image, from {Image.get} / {Image.list}.
5
+ class ImageInfo
6
+ attr_reader :reference, :size_bytes, :manifest_digest, :architecture, :os, :layer_count
7
+
8
+ def initialize(data)
9
+ @reference = data["reference"]
10
+ @size_bytes = data["size_bytes"]
11
+ @manifest_digest = data["manifest_digest"]
12
+ @architecture = data["architecture"]
13
+ @os = data["os"]
14
+ @layer_count = data["layer_count"]
15
+ @created_at_ms = data["created_at_ms"]
16
+ @last_used_at_ms = data["last_used_at_ms"]
17
+ end
18
+
19
+ # @return [Time, nil]
20
+ def created_at
21
+ @created_at_ms && Time.at(@created_at_ms / 1000.0)
22
+ end
23
+
24
+ # @return [Time, nil]
25
+ def last_used_at
26
+ @last_used_at_ms && Time.at(@last_used_at_ms / 1000.0)
27
+ end
28
+
29
+ def inspect
30
+ "#<Microsandbox::ImageInfo reference=#{@reference.inspect} layers=#{@layer_count}>"
31
+ end
32
+ end
33
+
34
+ # Full inspection detail for a cached image, from {Image.inspect}.
35
+ class ImageDetail
36
+ # @return [ImageInfo]
37
+ attr_reader :handle
38
+ # @return [Hash, nil] OCI config (digest, env, cmd, entrypoint, working_dir, user, stop_signal)
39
+ attr_reader :config
40
+ # @return [Array<Hash>] layer descriptors
41
+ attr_reader :layers
42
+
43
+ def initialize(data)
44
+ @handle = ImageInfo.new(data["handle"])
45
+ @config = data["config"]
46
+ @layers = data["layers"] || []
47
+ end
48
+
49
+ def reference = @handle.reference
50
+
51
+ def inspect
52
+ "#<Microsandbox::ImageDetail reference=#{@handle.reference.inspect} layers=#{@layers.size}>"
53
+ end
54
+ end
55
+
56
+ # The result of {Image.prune}.
57
+ class ImagePruneReport
58
+ attr_reader :image_refs_removed, :manifests_removed, :layers_removed,
59
+ :fsmeta_removed, :vmdk_removed, :bytes_reclaimed
60
+
61
+ def initialize(data)
62
+ @image_refs_removed = data["image_refs_removed"]
63
+ @manifests_removed = data["manifests_removed"]
64
+ @layers_removed = data["layers_removed"]
65
+ @fsmeta_removed = data["fsmeta_removed"]
66
+ @vmdk_removed = data["vmdk_removed"]
67
+ @bytes_reclaimed = data["bytes_reclaimed"]
68
+ end
69
+ end
70
+
71
+ # Management of the local OCI image cache. Images are pulled automatically by
72
+ # {Sandbox.create}; this namespace lets you inspect and prune the cache.
73
+ class Image
74
+ class << self
75
+ # All cached images.
76
+ # @return [Array<ImageInfo>]
77
+ def list
78
+ Native::Image.list.map { |info| ImageInfo.new(info) }
79
+ end
80
+
81
+ # Metadata for one cached image.
82
+ # @return [ImageInfo]
83
+ def get(reference)
84
+ ImageInfo.new(Native::Image.get(reference.to_s))
85
+ end
86
+
87
+ # Full inspection detail for a cached image. With no argument this is the
88
+ # normal class `#inspect` (so object display still works).
89
+ # @return [ImageDetail]
90
+ def inspect(reference = nil)
91
+ return super() if reference.nil?
92
+
93
+ ImageDetail.new(Native::Image.inspect(reference.to_s))
94
+ end
95
+
96
+ # Remove a cached image.
97
+ # @param force [Boolean] remove even if referenced
98
+ # @return [nil]
99
+ def remove(reference, force: false)
100
+ Native::Image.remove(reference.to_s, force)
101
+ nil
102
+ end
103
+
104
+ # Garbage-collect unreferenced images, manifests, and layers.
105
+ # @return [ImagePruneReport]
106
+ def prune
107
+ ImagePruneReport.new(Native::Image.prune)
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Microsandbox
4
+ # A single captured log entry, returned by {Sandbox#logs}.
5
+ class LogEntry
6
+ # @return [Symbol] one of :stdout, :stderr, :output, :system
7
+ attr_reader :source
8
+ # @return [Integer, nil] relay-monotonic session id (nil for system markers)
9
+ attr_reader :session_id
10
+ # @return [String] opaque resume cursor token
11
+ attr_reader :cursor
12
+ # @return [String] raw captured bytes (ASCII-8BIT)
13
+ attr_reader :data
14
+
15
+ def initialize(entry)
16
+ @timestamp_ms = entry["timestamp_ms"]
17
+ @source = entry["source"].to_sym
18
+ @session_id = entry["session_id"]
19
+ @cursor = entry["cursor"]
20
+ @data = entry["data"]
21
+ end
22
+
23
+ # @return [Time] wall-clock capture time
24
+ def timestamp
25
+ Time.at(@timestamp_ms / 1000.0)
26
+ end
27
+
28
+ # @return [String] the captured bytes decoded as UTF-8 (lenient)
29
+ def text
30
+ @data.dup.force_encoding(Encoding::UTF_8)
31
+ end
32
+
33
+ def inspect
34
+ "#<Microsandbox::LogEntry source=#{@source} session_id=#{@session_id} " \
35
+ "len=#{@data.bytesize}>"
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Microsandbox
4
+ # A point-in-time resource-usage snapshot for a sandbox, returned by
5
+ # {Sandbox#metrics}.
6
+ class Metrics
7
+ # @return [Float] CPU usage as a percentage (0.0–100.0 * vCPUs)
8
+ attr_reader :cpu_percent
9
+ # @return [Integer] cumulative vCPU time in nanoseconds
10
+ attr_reader :vcpu_time_ns
11
+ # @return [Integer] memory currently used, in bytes
12
+ attr_reader :memory_bytes
13
+ # @return [Integer, nil] memory available to the guest, in bytes
14
+ attr_reader :memory_available_bytes
15
+ # @return [Integer, nil] host-resident memory for the VM, in bytes
16
+ attr_reader :memory_host_resident_bytes
17
+ # @return [Integer] memory limit, in bytes
18
+ attr_reader :memory_limit_bytes
19
+ # @return [Integer] cumulative bytes read from disk
20
+ attr_reader :disk_read_bytes
21
+ # @return [Integer] cumulative bytes written to disk
22
+ attr_reader :disk_write_bytes
23
+ # @return [Integer] cumulative bytes received over the network
24
+ attr_reader :net_rx_bytes
25
+ # @return [Integer] cumulative bytes transmitted over the network
26
+ attr_reader :net_tx_bytes
27
+ # @return [Float] sandbox uptime in seconds
28
+ attr_reader :uptime_secs
29
+
30
+ def initialize(data)
31
+ @cpu_percent = data["cpu_percent"]
32
+ @vcpu_time_ns = data["vcpu_time_ns"]
33
+ @memory_bytes = data["memory_bytes"]
34
+ @memory_available_bytes = data["memory_available_bytes"]
35
+ @memory_host_resident_bytes = data["memory_host_resident_bytes"]
36
+ @memory_limit_bytes = data["memory_limit_bytes"]
37
+ @disk_read_bytes = data["disk_read_bytes"]
38
+ @disk_write_bytes = data["disk_write_bytes"]
39
+ @net_rx_bytes = data["net_rx_bytes"]
40
+ @net_tx_bytes = data["net_tx_bytes"]
41
+ @uptime_secs = data["uptime_secs"]
42
+ @timestamp_ms = data["timestamp_ms"]
43
+ end
44
+
45
+ # @return [Time] when this snapshot was captured
46
+ def timestamp
47
+ Time.at(@timestamp_ms / 1000.0)
48
+ end
49
+
50
+ def inspect
51
+ "#<Microsandbox::Metrics cpu=#{@cpu_percent.round(1)}% " \
52
+ "mem=#{@memory_bytes}/#{@memory_limit_bytes}B uptime=#{@uptime_secs.round(1)}s>"
53
+ end
54
+ end
55
+ end