Class: Boxd::Box

Inherits:
Object
  • Object
show all
Defined in:
lib/boxd/box.rb

Overview

A Box is the handle to a single VM. Instances are returned from Compute#boxes operations; you don’t construct them directly.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attrs, backend:) ⇒ Box

Returns a new instance of Box.



11
12
13
14
# File 'lib/boxd/box.rb', line 11

def initialize(attrs, backend:)
  @backend = backend
  refresh_from(attrs)
end

Instance Attribute Details

#imageObject (readonly)

Returns the value of attribute image.



9
10
11
# File 'lib/boxd/box.rb', line 9

def image
  @image
end

#nameObject (readonly)

Returns the value of attribute name.



9
10
11
# File 'lib/boxd/box.rb', line 9

def name
  @name
end

#statusObject (readonly)

Returns the value of attribute status.



9
10
11
# File 'lib/boxd/box.rb', line 9

def status
  @status
end

#urlObject (readonly)

Returns the value of attribute url.



9
10
11
# File 'lib/boxd/box.rb', line 9

def url
  @url
end

#vm_idObject (readonly)

Returns the value of attribute vm_id.



9
10
11
# File 'lib/boxd/box.rb', line 9

def vm_id
  @vm_id
end

Instance Method Details

#destroyObject



120
121
122
123
124
# File 'lib/boxd/box.rb', line 120

def destroy
  @backend.call_raw("destroy", @name, "--confirm")
  @status = "destroyed"
  self
end

#exec(cmd, env: nil, tty: false, timeout: nil, raise_on_error: false, &block) ⇒ Object

Run a command inside the VM.

box.exec(["ls", "-la"])                            # sync, blocking
box.exec(["sleep", "3"], timeout: 1)               # raises TimeoutError
box.exec(["bash", "-lc", "for i in 1 2 3; ...]) do |stream, chunk|
  # stream is :stdout or :stderr
end

Returns Hash stderr:, exit_code:. Raises ExecError if ‘raise_on_error: true` and exit_code != 0.



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/boxd/box.rb', line 51

def exec(cmd, env: nil, tty: false, timeout: nil, raise_on_error: false, &block)
  result =
    if timeout
      run_with_timeout(timeout) { @backend.exec_stream(@name, cmd, env: env, tty: tty, &block) }
    else
      @backend.exec_stream(@name, cmd, env: env, tty: tty, &block)
    end

  if raise_on_error && result[:exit_code] != 0
    raise ExecError.new(
      "exec exited #{result[:exit_code]}: #{result[:stderr].strip.lines.last}",
      **result,
    )
  end
  result
end

#exec!(cmd, **opts) ⇒ Object

Run a command and return stdout as a String, raising on any non-zero exit. Convenience for one-liners.



70
71
72
73
# File 'lib/boxd/box.rb', line 70

def exec!(cmd, **opts)
  r = exec(cmd, **opts, raise_on_error: true)
  r[:stdout]
end

#inspectObject



139
140
141
# File 'lib/boxd/box.rb', line 139

def inspect
  "#<Boxd::Box name=#{@name.inspect} status=#{@status.inspect} url=#{@url.inspect}>"
end

#read_file(remote_path, local_path = nil) ⇒ Object

Copy a file FROM the VM to the local filesystem. Returns the local path.



98
99
100
101
102
# File 'lib/boxd/box.rb', line 98

def read_file(remote_path, local_path = nil)
  local_path ||= Tempfile.new(File.basename(remote_path)).path
  @backend.call_raw("cp", "#{@name}:#{remote_path}", local_path)
  local_path
end

#rebootObject



115
116
117
118
# File 'lib/boxd/box.rb', line 115

def reboot
  @backend.call_raw("reboot", @name)
  refresh!
end

#refresh!Object

Fetch the latest state from the API and update this Box in place.



17
18
19
20
# File 'lib/boxd/box.rb', line 17

def refresh!
  refresh_from(@backend.call_json("info", @name))
  self
end

#resumeObject



110
111
112
113
# File 'lib/boxd/box.rb', line 110

def resume
  @backend.call_raw("resume", @name)
  refresh!
end

#running?Boolean

Returns:

  • (Boolean)


33
34
35
# File 'lib/boxd/box.rb', line 33

def running?
  status == "running"
end

#suspendObject Also known as: pause



104
105
106
107
# File 'lib/boxd/box.rb', line 104

def suspend
  @backend.call_raw("pause", @name)
  refresh!
end

#suspended?Boolean

Returns:

  • (Boolean)


37
38
39
# File 'lib/boxd/box.rb', line 37

def suspended?
  %w[hibernated standby paused].include?(status)
end

#to_hObject

Convenience: hash representation of the current cached state.



23
24
25
26
27
28
29
30
31
# File 'lib/boxd/box.rb', line 23

def to_h
  {
    name: @name,
    vm_id: @vm_id,
    url: @url,
    status: @status,
    image: @image,
  }
end

#wait_for(target, timeout: 60, interval: 1) ⇒ Object

Wait until status matches ‘target` (String or Array). Polls `info`. Returns self on match; raises TimeoutError after `timeout` seconds.



128
129
130
131
132
133
134
135
136
137
# File 'lib/boxd/box.rb', line 128

def wait_for(target, timeout: 60, interval: 1)
  targets = Array(target).map(&:to_s)
  deadline = Time.now + timeout
  until targets.include?(refresh!.status)
    raise TimeoutError, "wait_for(#{targets.inspect}) timed out after #{timeout}s; status=#{status}" \
      if Time.now >= deadline
    sleep interval
  end
  self
end

#write_file(path, content) ⇒ Object

Write content to a path inside the VM.

box.write_file("/tmp/hello.txt", "hello world")
box.write_file("/etc/conf",      File.read("./conf"))
box.write_file("/tmp/blob",      io_object)         # responds to :read


80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/boxd/box.rb', line 80

def write_file(path, content)
  data =
    if content.respond_to?(:read)
      content.read
    else
      content.to_s
    end

  Tempfile.create(["boxd-write", File.extname(path)]) do |tmp|
    tmp.binmode
    tmp.write(data)
    tmp.flush
    @backend.call_raw("cp", tmp.path, "#{@name}:#{path}")
  end
  path
end