podman
224 строки · 8.5 Кб
1// Copyright 2014 go-dockerclient authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package docker
6
7import (
8"context"
9"encoding/json"
10"errors"
11"fmt"
12"io"
13"net/http"
14"net/url"
15"strconv"
16)
17
18// Exec is the type representing a `docker exec` instance and containing the
19// instance ID
20type Exec struct {
21ID string `json:"Id,omitempty" yaml:"Id,omitempty"`
22}
23
24// CreateExecOptions specify parameters to the CreateExecContainer function.
25//
26// See https://goo.gl/60TeBP for more details
27type CreateExecOptions struct {
28Env []string `json:"Env,omitempty" yaml:"Env,omitempty" toml:"Env,omitempty"`
29Cmd []string `json:"Cmd,omitempty" yaml:"Cmd,omitempty" toml:"Cmd,omitempty"`
30Container string `json:"Container,omitempty" yaml:"Container,omitempty" toml:"Container,omitempty"`
31User string `json:"User,omitempty" yaml:"User,omitempty" toml:"User,omitempty"`
32WorkingDir string `json:"WorkingDir,omitempty" yaml:"WorkingDir,omitempty" toml:"WorkingDir,omitempty"`
33DetachKeys string `json:"DetachKeys,omitempty" yaml:"DetachKeys,omitempty" toml:"DetachKeys,omitempty"`
34Context context.Context `json:"-"`
35AttachStdin bool `json:"AttachStdin,omitempty" yaml:"AttachStdin,omitempty" toml:"AttachStdin,omitempty"`
36AttachStdout bool `json:"AttachStdout,omitempty" yaml:"AttachStdout,omitempty" toml:"AttachStdout,omitempty"`
37AttachStderr bool `json:"AttachStderr,omitempty" yaml:"AttachStderr,omitempty" toml:"AttachStderr,omitempty"`
38Tty bool `json:"Tty,omitempty" yaml:"Tty,omitempty" toml:"Tty,omitempty"`
39Privileged bool `json:"Privileged,omitempty" yaml:"Privileged,omitempty" toml:"Privileged,omitempty"`
40}
41
42// CreateExec sets up an exec instance in a running container `id`, returning the exec
43// instance, or an error in case of failure.
44//
45// See https://goo.gl/60TeBP for more details
46func (c *Client) CreateExec(opts CreateExecOptions) (*Exec, error) {
47if c.serverAPIVersion == nil {
48c.checkAPIVersion()
49}
50if len(opts.Env) > 0 && c.serverAPIVersion.LessThan(apiVersion125) {
51return nil, errors.New("exec configuration Env is only supported in API#1.25 and above")
52}
53if len(opts.WorkingDir) > 0 && c.serverAPIVersion.LessThan(apiVersion135) {
54return nil, errors.New("exec configuration WorkingDir is only supported in API#1.35 and above")
55}
56path := fmt.Sprintf("/containers/%s/exec", opts.Container)
57resp, err := c.do(http.MethodPost, path, doOptions{data: opts, context: opts.Context})
58if err != nil {
59var e *Error
60if errors.As(err, &e) && e.Status == http.StatusNotFound {
61return nil, &NoSuchContainer{ID: opts.Container}
62}
63return nil, err
64}
65defer resp.Body.Close()
66var exec Exec
67if err := json.NewDecoder(resp.Body).Decode(&exec); err != nil {
68return nil, err
69}
70
71return &exec, nil
72}
73
74// StartExecOptions specify parameters to the StartExecContainer function.
75//
76// See https://goo.gl/1EeDWi for more details
77type StartExecOptions struct {
78InputStream io.Reader `qs:"-"`
79OutputStream io.Writer `qs:"-"`
80ErrorStream io.Writer `qs:"-"`
81
82Detach bool `json:"Detach,omitempty" yaml:"Detach,omitempty" toml:"Detach,omitempty"`
83Tty bool `json:"Tty,omitempty" yaml:"Tty,omitempty" toml:"Tty,omitempty"`
84
85// Use raw terminal? Usually true when the container contains a TTY.
86RawTerminal bool `qs:"-"`
87
88// If set, after a successful connect, a sentinel will be sent and then the
89// client will block on receive before continuing.
90//
91// It must be an unbuffered channel. Using a buffered channel can lead
92// to unexpected behavior.
93Success chan struct{} `json:"-"`
94
95Context context.Context `json:"-"`
96}
97
98// StartExec starts a previously set up exec instance id. If opts.Detach is
99// true, it returns after starting the exec command. Otherwise, it sets up an
100// interactive session with the exec command.
101//
102// See https://goo.gl/1EeDWi for more details
103func (c *Client) StartExec(id string, opts StartExecOptions) error {
104cw, err := c.StartExecNonBlocking(id, opts)
105if err != nil {
106return err
107}
108if cw != nil {
109return cw.Wait()
110}
111return nil
112}
113
114// StartExecNonBlocking starts a previously set up exec instance id. If opts.Detach is
115// true, it returns after starting the exec command. Otherwise, it sets up an
116// interactive session with the exec command.
117//
118// See https://goo.gl/1EeDWi for more details
119func (c *Client) StartExecNonBlocking(id string, opts StartExecOptions) (CloseWaiter, error) {
120if id == "" {
121return nil, &NoSuchExec{ID: id}
122}
123
124path := fmt.Sprintf("/exec/%s/start", id)
125
126if opts.Detach {
127resp, err := c.do(http.MethodPost, path, doOptions{data: opts, context: opts.Context})
128if err != nil {
129var e *Error
130if errors.As(err, &e) && e.Status == http.StatusNotFound {
131return nil, &NoSuchExec{ID: id}
132}
133return nil, err
134}
135defer resp.Body.Close()
136return nil, nil
137}
138
139return c.hijack(http.MethodPost, path, hijackOptions{
140success: opts.Success,
141setRawTerminal: opts.RawTerminal,
142in: opts.InputStream,
143stdout: opts.OutputStream,
144stderr: opts.ErrorStream,
145data: opts,
146})
147}
148
149// ResizeExecTTY resizes the tty session used by the exec command id. This API
150// is valid only if Tty was specified as part of creating and starting the exec
151// command.
152//
153// See https://goo.gl/Mo5bxx for more details
154func (c *Client) ResizeExecTTY(id string, height, width int) error {
155params := make(url.Values)
156params.Set("h", strconv.Itoa(height))
157params.Set("w", strconv.Itoa(width))
158
159path := fmt.Sprintf("/exec/%s/resize?%s", id, params.Encode())
160resp, err := c.do(http.MethodPost, path, doOptions{})
161if err != nil {
162return err
163}
164resp.Body.Close()
165return nil
166}
167
168// ExecProcessConfig is a type describing the command associated to a Exec
169// instance. It's used in the ExecInspect type.
170type ExecProcessConfig struct {
171User string `json:"user,omitempty" yaml:"user,omitempty" toml:"user,omitempty"`
172Privileged bool `json:"privileged,omitempty" yaml:"privileged,omitempty" toml:"privileged,omitempty"`
173Tty bool `json:"tty,omitempty" yaml:"tty,omitempty" toml:"tty,omitempty"`
174EntryPoint string `json:"entrypoint,omitempty" yaml:"entrypoint,omitempty" toml:"entrypoint,omitempty"`
175Arguments []string `json:"arguments,omitempty" yaml:"arguments,omitempty" toml:"arguments,omitempty"`
176}
177
178// ExecInspect is a type with details about a exec instance, including the
179// exit code if the command has finished running. It's returned by a api
180// call to /exec/(id)/json
181//
182// See https://goo.gl/ctMUiW for more details
183type ExecInspect struct {
184ID string `json:"ID,omitempty" yaml:"ID,omitempty" toml:"ID,omitempty"`
185ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty" toml:"ExitCode,omitempty"`
186ProcessConfig ExecProcessConfig `json:"ProcessConfig,omitempty" yaml:"ProcessConfig,omitempty" toml:"ProcessConfig,omitempty"`
187ContainerID string `json:"ContainerID,omitempty" yaml:"ContainerID,omitempty" toml:"ContainerID,omitempty"`
188DetachKeys string `json:"DetachKeys,omitempty" yaml:"DetachKeys,omitempty" toml:"DetachKeys,omitempty"`
189Running bool `json:"Running,omitempty" yaml:"Running,omitempty" toml:"Running,omitempty"`
190OpenStdin bool `json:"OpenStdin,omitempty" yaml:"OpenStdin,omitempty" toml:"OpenStdin,omitempty"`
191OpenStderr bool `json:"OpenStderr,omitempty" yaml:"OpenStderr,omitempty" toml:"OpenStderr,omitempty"`
192OpenStdout bool `json:"OpenStdout,omitempty" yaml:"OpenStdout,omitempty" toml:"OpenStdout,omitempty"`
193CanRemove bool `json:"CanRemove,omitempty" yaml:"CanRemove,omitempty" toml:"CanRemove,omitempty"`
194}
195
196// InspectExec returns low-level information about the exec command id.
197//
198// See https://goo.gl/ctMUiW for more details
199func (c *Client) InspectExec(id string) (*ExecInspect, error) {
200path := fmt.Sprintf("/exec/%s/json", id)
201resp, err := c.do(http.MethodGet, path, doOptions{})
202if err != nil {
203var e *Error
204if errors.As(err, &e) && e.Status == http.StatusNotFound {
205return nil, &NoSuchExec{ID: id}
206}
207return nil, err
208}
209defer resp.Body.Close()
210var exec ExecInspect
211if err := json.NewDecoder(resp.Body).Decode(&exec); err != nil {
212return nil, err
213}
214return &exec, nil
215}
216
217// NoSuchExec is the error returned when a given exec instance does not exist.
218type NoSuchExec struct {
219ID string
220}
221
222func (err *NoSuchExec) Error() string {
223return "No such exec instance: " + err.ID
224}
225