Module: OsCtld::ContainerControl::Utils::Runscript::Runner

Included in:
Commands::Exec::Runner, Commands::Runscript::Runner
Defined in:
lib/osctld/container_control/utils/runscript.rb

Instance Method Summary collapse

Instance Method Details

#osctld_wrapper_callbackObject

Callback to osctld to relocate self-process from container’s wrapper cgroup



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

def osctld_wrapper_callback
  s = UNIXSocket.new("/run/osctl/user-control/#{Process.uid}.sock")

  payload = {
    cmd: :ct_wrapper_start,
    opts: {
      id: ctid,
      pool:,
      pid: Process.pid
    }
  }

  s.send("#{payload.to_json}\n", 0)

  ret = JSON.parse(s.readline, symbolize_names: true)
  s.close

  return if ret[:status]

  raise "Error during ct_wrapper_start callback: #{ret[:message]}"
end

#runscript_run(opts) ⇒ Object

Execute script in a stopped container

Parameters:

  • opts (Hash)

Options Hash (opts):

  • :script (String)

    path to the script relative to the rootfs

  • :stdin (IO)
  • :stdout (IO)
  • :stderr (IO)
  • :close_fds (Array<IO>)
  • :wait (Boolean)


47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/osctld/container_control/utils/runscript.rb', line 47

def runscript_run(opts)
  pid = Process.fork do
    cur_stdin = opts.fetch(:stdin, stdin)
    cur_stdout = opts.fetch(:stdout, stdout)
    cur_stderr = opts.fetch(:stderr, stderr)

    if cur_stdin
      $stdin.reopen(cur_stdin)
    else
      $stdin.close
    end

    $stdout.reopen(cur_stdout)
    $stderr.reopen(cur_stderr) if cur_stderr

    opts[:close_fds] && opts[:close_fds].each(&:close)

    setup_exec_run_env
    osctld_wrapper_callback

    cmd = [
      'lxc-execute',
      '-P', lxc_home,
      '-n', ctid,
      '-o', log_file,
      '-s', "lxc.environment=PATH=#{system_path.join(':')}",
      '-s', 'lxc.environment=HOME=/root',
      '-s', 'lxc.environment=USER=root',
      '--',
      opts[:script]
    ]

    # opts[:cmd] can contain an arbitrary command with multiple arguments
    # and quotes, so the mapping to process arguments is not clear. We use
    # the shell to handle this.
    Process.exec("exec #{cmd.join(' ')}")
  end

  if opts[:wait] === false
    pid
  else
    _, status = Process.wait2(pid)
    ok(status.exitstatus)
  end
end

#with_configured_network(opts) ⇒ Object

Start container with lxc-init, configure network and yield

opts has to contain path to a script that will be executed by lxc-init. The purpose of this script is to keep the container running while the network is being configured and the user command is executed. The script has to write ‘readyn` to standard output, then block on read from standard input and exit.

Parameters:

  • opts (Hash)

Options Hash (opts):

  • :init_script (String)

    path to the script used to control the container

  • :net_config (Hash)


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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/osctld/container_control/utils/runscript.rb', line 105

def with_configured_network(opts)
  ret = nil

  # Pipes for communicating with opts[:init_script]
  in_r, in_w = IO.pipe
  out_r, out_w = IO.pipe

  # Start the container with lxc-init
  init_pid = runscript_run(
    id: ctid,
    script: opts[:init_script],
    stdin: in_r,
    stdout: out_w,
    stderr: nil,
    close_fds: [in_w, out_r],
    wait: false
  )

  in_r.close
  out_w.close

  # Wait for the container to be started
  if out_r.readline.strip == 'ready'
    # Configure network
    pid = lxc_ct.attach do
      setup_exec_env
      ENV['HOME'] = '/root'
      ENV['USER'] = 'root'
      NetConfig.import(opts[:net_config]).setup
    end

    Process.wait2(pid)

    # Execute user command
    ret = yield
  end

  # Closing in_w will bring down opts[:init_script] and stop the container
  in_w.close
  out_r.close

  _, status = Process.wait2(init_pid)
  ret || ok(status.exitstatus)
end