microsandbox-rb 0.5.9 → 0.5.10
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 +4 -4
- data/CHANGELOG.md +77 -0
- data/Cargo.lock +90 -45
- data/DESIGN.md +7 -3
- data/README.md +91 -26
- data/ext/microsandbox/Cargo.toml +4 -4
- data/ext/microsandbox/extconf.rb +6 -2
- data/ext/microsandbox/src/backend.rs +170 -0
- data/ext/microsandbox/src/error.rs +6 -0
- data/ext/microsandbox/src/image.rs +7 -7
- data/ext/microsandbox/src/lib.rs +27 -4
- data/ext/microsandbox/src/sandbox.rs +172 -58
- data/ext/microsandbox/src/volume.rs +6 -1
- data/lib/microsandbox/errors.rb +6 -0
- data/lib/microsandbox/exec_handle.rb +14 -11
- data/lib/microsandbox/fs.rb +7 -7
- data/lib/microsandbox/image.rb +1 -1
- data/lib/microsandbox/network.rb +19 -19
- data/lib/microsandbox/patch.rb +8 -8
- data/lib/microsandbox/sandbox.rb +199 -75
- data/lib/microsandbox/snapshot.rb +2 -2
- data/lib/microsandbox/ssh.rb +2 -2
- data/lib/microsandbox/version.rb +2 -2
- data/lib/microsandbox/volume.rb +3 -3
- data/lib/microsandbox.rb +61 -0
- data/sig/microsandbox.rbs +31 -13
- metadata +2 -1
|
@@ -22,25 +22,28 @@ module Microsandbox
|
|
|
22
22
|
|
|
23
23
|
# @return [String, nil] {#data} decoded as UTF-8 (lenient)
|
|
24
24
|
def text
|
|
25
|
-
@data
|
|
25
|
+
@data&.dup&.force_encoding(Encoding::UTF_8)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
def started?
|
|
29
|
-
def stdout?
|
|
30
|
-
def stderr?
|
|
31
|
-
def exited?
|
|
32
|
-
def failed?
|
|
33
|
-
def stdin_error?
|
|
28
|
+
def started? = @type == :started
|
|
29
|
+
def stdout? = @type == :stdout
|
|
30
|
+
def stderr? = @type == :stderr
|
|
31
|
+
def exited? = @type == :exited
|
|
32
|
+
def failed? = @type == :failed
|
|
33
|
+
def stdin_error? = @type == :stdin_error
|
|
34
34
|
|
|
35
35
|
def inspect
|
|
36
|
-
"#<Microsandbox::ExecEvent type=#{@type}#{
|
|
37
|
-
"#{
|
|
36
|
+
"#<Microsandbox::ExecEvent type=#{@type}#{" pid=#{@pid}" if @pid}" \
|
|
37
|
+
"#{" code=#{@code}" if @code}#{" data=#{@data.bytesize}B" if @data}>"
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
# The terminal status of a streamed execution
|
|
41
|
+
# The terminal status of a streamed execution ({ExecHandle#wait}) or of a
|
|
42
|
+
# sandbox process ({Sandbox#wait}, {Sandbox#stop_and_wait}).
|
|
42
43
|
class ExitStatus
|
|
43
|
-
# @return [Integer]
|
|
44
|
+
# @return [Integer, nil] the exit code, or nil when the process was
|
|
45
|
+
# terminated by a signal and so carries no code (e.g. a SIGKILL'd sandbox
|
|
46
|
+
# from {Sandbox#kill} then {Sandbox#wait}) — check {#success?} in that case.
|
|
44
47
|
attr_reader :exit_code
|
|
45
48
|
|
|
46
49
|
def initialize(data)
|
data/lib/microsandbox/fs.rb
CHANGED
|
@@ -30,9 +30,9 @@ module Microsandbox
|
|
|
30
30
|
@modified_ms && Time.at(@modified_ms / 1000.0)
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
def file?
|
|
34
|
-
def directory?
|
|
35
|
-
def symlink?
|
|
33
|
+
def file? = @type == :file
|
|
34
|
+
def directory? = @type == :directory
|
|
35
|
+
def symlink? = @type == :symlink
|
|
36
36
|
|
|
37
37
|
def inspect
|
|
38
38
|
"#<Microsandbox::FsEntry path=#{@path.inspect} type=#{@type} size=#{@size}>"
|
|
@@ -57,10 +57,10 @@ module Microsandbox
|
|
|
57
57
|
@created_ms = data["created_ms"]
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
-
def readonly?
|
|
61
|
-
def file?
|
|
62
|
-
def directory?
|
|
63
|
-
def symlink?
|
|
60
|
+
def readonly? = @readonly
|
|
61
|
+
def file? = @type == :file
|
|
62
|
+
def directory? = @type == :directory
|
|
63
|
+
def symlink? = @type == :symlink
|
|
64
64
|
|
|
65
65
|
# @return [Time, nil] last-modified time, if known
|
|
66
66
|
def modified
|
data/lib/microsandbox/image.rb
CHANGED
|
@@ -56,7 +56,7 @@ module Microsandbox
|
|
|
56
56
|
# The result of {Image.prune}.
|
|
57
57
|
class ImagePruneReport
|
|
58
58
|
attr_reader :image_refs_removed, :manifests_removed, :layers_removed,
|
|
59
|
-
|
|
59
|
+
:fsmeta_removed, :vmdk_removed, :bytes_reclaimed
|
|
60
60
|
|
|
61
61
|
def initialize(data)
|
|
62
62
|
@image_refs_removed = data["image_refs_removed"]
|
data/lib/microsandbox/network.rb
CHANGED
|
@@ -16,23 +16,23 @@ module Microsandbox
|
|
|
16
16
|
module_function
|
|
17
17
|
|
|
18
18
|
# Match any destination.
|
|
19
|
-
def any = {
|
|
19
|
+
def any = {"destination_kind" => "any"}
|
|
20
20
|
|
|
21
21
|
# A single IP address (stored as a /32 or /128).
|
|
22
|
-
def ip(value) = {
|
|
22
|
+
def ip(value) = {"destination_kind" => "ip", "destination" => value.to_s}
|
|
23
23
|
|
|
24
24
|
# An IP network in CIDR notation (e.g. "10.0.0.0/8").
|
|
25
|
-
def cidr(value) = {
|
|
25
|
+
def cidr(value) = {"destination_kind" => "cidr", "destination" => value.to_s}
|
|
26
26
|
|
|
27
27
|
# An exact domain name (matched against the resolved-hostname cache / SNI).
|
|
28
|
-
def domain(value) = {
|
|
28
|
+
def domain(value) = {"destination_kind" => "domain", "destination" => value.to_s}
|
|
29
29
|
|
|
30
30
|
# A domain suffix — matches the apex and any subdomain (e.g. ".internal").
|
|
31
|
-
def domain_suffix(value) = {
|
|
31
|
+
def domain_suffix(value) = {"destination_kind" => "domain_suffix", "destination" => value.to_s}
|
|
32
32
|
|
|
33
33
|
# A predefined group: :public, :loopback, :private, :link_local, :metadata,
|
|
34
34
|
# :multicast, or :host.
|
|
35
|
-
def group(value) = {
|
|
35
|
+
def group(value) = {"destination_kind" => "group", "destination" => value.to_s.tr("_", "-")}
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
# Factory for a single network-policy **rule**. A rule pairs an action
|
|
@@ -64,7 +64,7 @@ module Microsandbox
|
|
|
64
64
|
|
|
65
65
|
# @api private
|
|
66
66
|
def build(action, destination, direction, protocol, protocols, port, ports)
|
|
67
|
-
rule = {
|
|
67
|
+
rule = {"action" => action, "direction" => direction.to_s}
|
|
68
68
|
rule.merge!(normalize_destination(destination))
|
|
69
69
|
protos = (Array(protocols) + Array(protocol)).compact.map(&:to_s)
|
|
70
70
|
rule["protocols"] = protos unless protos.empty?
|
|
@@ -78,7 +78,7 @@ module Microsandbox
|
|
|
78
78
|
case dest
|
|
79
79
|
when nil then {}
|
|
80
80
|
when Hash then dest.each_with_object({}) { |(k, v), a| a[k.to_s] = v }
|
|
81
|
-
when String, Symbol then {
|
|
81
|
+
when String, Symbol then {"destination" => dest.to_s}
|
|
82
82
|
else raise ArgumentError, "invalid rule destination: #{dest.inspect}"
|
|
83
83
|
end
|
|
84
84
|
end
|
|
@@ -149,7 +149,7 @@ module Microsandbox
|
|
|
149
149
|
# @param deny_domain_suffixes [Array<String>] domain suffixes to deny
|
|
150
150
|
# @return [NetworkPolicy]
|
|
151
151
|
def custom(default_egress: :deny, default_ingress: :allow, rules: [],
|
|
152
|
-
|
|
152
|
+
deny_domains: [], deny_domain_suffixes: [])
|
|
153
153
|
h = {}
|
|
154
154
|
h["default_egress"] = action_str(default_egress) unless default_egress.nil?
|
|
155
155
|
h["default_ingress"] = action_str(default_ingress) unless default_ingress.nil?
|
|
@@ -163,12 +163,12 @@ module Microsandbox
|
|
|
163
163
|
def coerce(network)
|
|
164
164
|
case network
|
|
165
165
|
when NetworkPolicy then network.to_h
|
|
166
|
-
when String, Symbol then {
|
|
166
|
+
when String, Symbol then {"preset" => canonical_preset(network)}
|
|
167
167
|
when Hash then from_hash(network)
|
|
168
168
|
else
|
|
169
169
|
raise ArgumentError,
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
"network: expects a preset name, a Microsandbox::NetworkPolicy, or a Hash " \
|
|
171
|
+
"(got #{network.class})"
|
|
172
172
|
end
|
|
173
173
|
end
|
|
174
174
|
|
|
@@ -186,11 +186,11 @@ module Microsandbox
|
|
|
186
186
|
if sym.key?(:preset)
|
|
187
187
|
if sym.key?(:rules) || sym.key?(:default_egress) || sym.key?(:default_ingress)
|
|
188
188
|
raise ArgumentError,
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
189
|
+
"network preset: cannot be combined with rules:/default_egress:/" \
|
|
190
|
+
"default_ingress: (the preset already defines its rules and defaults); " \
|
|
191
|
+
"only deny_domains:/deny_domain_suffixes: may be layered on a preset"
|
|
192
192
|
end
|
|
193
|
-
h = {
|
|
193
|
+
h = {"preset" => canonical_preset(sym[:preset])}
|
|
194
194
|
add_deny_lists(h, sym[:deny_domains], sym[:deny_domain_suffixes])
|
|
195
195
|
h
|
|
196
196
|
elsif sym.key?(:rules) || sym.key?(:default_egress) || sym.key?(:default_ingress)
|
|
@@ -213,7 +213,7 @@ module Microsandbox
|
|
|
213
213
|
ds = Array(sym[:deny_domain_suffixes]).map(&:to_s)
|
|
214
214
|
return {} if dd.empty? && ds.empty?
|
|
215
215
|
|
|
216
|
-
h = {
|
|
216
|
+
h = {"default_egress" => "allow", "default_ingress" => "allow"}
|
|
217
217
|
add_deny_lists(h, dd, ds)
|
|
218
218
|
h
|
|
219
219
|
end
|
|
@@ -233,8 +233,8 @@ module Microsandbox
|
|
|
233
233
|
key = name.to_s.downcase
|
|
234
234
|
PRESET_ALIASES[key] ||
|
|
235
235
|
raise(ArgumentError,
|
|
236
|
-
|
|
237
|
-
|
|
236
|
+
"unknown network preset #{name.inspect} " \
|
|
237
|
+
"(expected one of public_only/none/allow_all/non_local)")
|
|
238
238
|
end
|
|
239
239
|
|
|
240
240
|
def action_str(action)
|
data/lib/microsandbox/patch.rb
CHANGED
|
@@ -31,7 +31,7 @@ module Microsandbox
|
|
|
31
31
|
# @param replace [Boolean] allow shadowing a path already in the rootfs
|
|
32
32
|
# @return [Hash]
|
|
33
33
|
def text(path, content, mode: nil, replace: false)
|
|
34
|
-
h = {
|
|
34
|
+
h = {"kind" => "text", "path" => path.to_s, "content" => content.to_s, "replace" => replace ? true : false}
|
|
35
35
|
h["mode"] = Integer(mode) unless mode.nil?
|
|
36
36
|
h
|
|
37
37
|
end
|
|
@@ -43,7 +43,7 @@ module Microsandbox
|
|
|
43
43
|
# @param replace [Boolean]
|
|
44
44
|
# @return [Hash]
|
|
45
45
|
def file(path, content, mode: nil, replace: false)
|
|
46
|
-
h = {
|
|
46
|
+
h = {"kind" => "file", "path" => path.to_s, "content" => content.to_s, "replace" => replace ? true : false}
|
|
47
47
|
h["mode"] = Integer(mode) unless mode.nil?
|
|
48
48
|
h
|
|
49
49
|
end
|
|
@@ -52,7 +52,7 @@ module Microsandbox
|
|
|
52
52
|
# lower image layer is copied up first, then appended.
|
|
53
53
|
# @return [Hash]
|
|
54
54
|
def append(path, content)
|
|
55
|
-
{
|
|
55
|
+
{"kind" => "append", "path" => path.to_s, "content" => content.to_s}
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
# Copy a host file into the rootfs.
|
|
@@ -62,7 +62,7 @@ module Microsandbox
|
|
|
62
62
|
# @param replace [Boolean]
|
|
63
63
|
# @return [Hash]
|
|
64
64
|
def copy_file(src, dst, mode: nil, replace: false)
|
|
65
|
-
h = {
|
|
65
|
+
h = {"kind" => "copy_file", "src" => src.to_s, "dst" => dst.to_s, "replace" => replace ? true : false}
|
|
66
66
|
h["mode"] = Integer(mode) unless mode.nil?
|
|
67
67
|
h
|
|
68
68
|
end
|
|
@@ -70,13 +70,13 @@ module Microsandbox
|
|
|
70
70
|
# Copy a host directory (recursively) into the rootfs.
|
|
71
71
|
# @return [Hash]
|
|
72
72
|
def copy_dir(src, dst, replace: false)
|
|
73
|
-
{
|
|
73
|
+
{"kind" => "copy_dir", "src" => src.to_s, "dst" => dst.to_s, "replace" => replace ? true : false}
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
# Create a symlink at +link+ pointing to +target+.
|
|
77
77
|
# @return [Hash]
|
|
78
78
|
def symlink(target, link, replace: false)
|
|
79
|
-
{
|
|
79
|
+
{"kind" => "symlink", "target" => target.to_s, "link" => link.to_s, "replace" => replace ? true : false}
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
# Create a directory (idempotent — no error if it already exists).
|
|
@@ -84,7 +84,7 @@ module Microsandbox
|
|
|
84
84
|
# @param mode [Integer, nil] directory mode (e.g. 0o755)
|
|
85
85
|
# @return [Hash]
|
|
86
86
|
def mkdir(path, mode: nil)
|
|
87
|
-
h = {
|
|
87
|
+
h = {"kind" => "mkdir", "path" => path.to_s}
|
|
88
88
|
h["mode"] = Integer(mode) unless mode.nil?
|
|
89
89
|
h
|
|
90
90
|
end
|
|
@@ -92,7 +92,7 @@ module Microsandbox
|
|
|
92
92
|
# Remove a file or directory (idempotent — no error if absent).
|
|
93
93
|
# @return [Hash]
|
|
94
94
|
def remove(path)
|
|
95
|
-
{
|
|
95
|
+
{"kind" => "remove", "path" => path.to_s}
|
|
96
96
|
end
|
|
97
97
|
end
|
|
98
98
|
end
|