forked from containerd/containerd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
io.go
192 lines (169 loc) · 3.86 KB
/
io.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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
92
93
94
95
96
97
98
99
100
101
102
103
104
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
package containerd
import (
"context"
"fmt"
"io"
"os"
"sync"
)
// IOConfig holds the io configurations.
type IOConfig struct {
// Terminal is true if one has been allocated
Terminal bool
// Stdin path
Stdin string
// Stdout path
Stdout string
// Stderr path
Stderr string
}
// IO holds the io information for a task or process
type IO interface {
// Config returns the IO configuration.
Config() IOConfig
// Cancel aborts all current io operations
Cancel()
// Wait blocks until all io copy operations have completed
Wait()
// Close cleans up all open io resources
Close() error
}
// cio is a basic container IO implementation.
type cio struct {
config IOConfig
closer *wgCloser
}
func (c *cio) Config() IOConfig {
return c.config
}
func (c *cio) Cancel() {
if c.closer == nil {
return
}
c.closer.Cancel()
}
func (c *cio) Wait() {
if c.closer == nil {
return
}
c.closer.Wait()
}
func (c *cio) Close() error {
if c.closer == nil {
return nil
}
return c.closer.Close()
}
// IOCreation creates new IO sets for a task
type IOCreation func(id string) (IO, error)
// IOAttach allows callers to reattach to running tasks
type IOAttach func(*FIFOSet) (IO, error)
// NewIO returns an IOCreation that will provide IO sets without a terminal
func NewIO(stdin io.Reader, stdout, stderr io.Writer) IOCreation {
return NewIOWithTerminal(stdin, stdout, stderr, false)
}
// NewIOWithTerminal creates a new io set with the provied io.Reader/Writers for use with a terminal
func NewIOWithTerminal(stdin io.Reader, stdout, stderr io.Writer, terminal bool) IOCreation {
return func(id string) (_ IO, err error) {
paths, err := NewFifos(id)
if err != nil {
return nil, err
}
defer func() {
if err != nil && paths.Dir != "" {
os.RemoveAll(paths.Dir)
}
}()
cfg := IOConfig{
Terminal: terminal,
Stdout: paths.Out,
Stderr: paths.Err,
Stdin: paths.In,
}
i := &cio{config: cfg}
set := &ioSet{
in: stdin,
out: stdout,
err: stderr,
}
closer, err := copyIO(paths, set, cfg.Terminal)
if err != nil {
return nil, err
}
i.closer = closer
return i, nil
}
}
// WithAttach attaches the existing io for a task to the provided io.Reader/Writers
func WithAttach(stdin io.Reader, stdout, stderr io.Writer) IOAttach {
return func(paths *FIFOSet) (IO, error) {
if paths == nil {
return nil, fmt.Errorf("cannot attach to existing fifos")
}
cfg := IOConfig{
Terminal: paths.Terminal,
Stdout: paths.Out,
Stderr: paths.Err,
Stdin: paths.In,
}
i := &cio{config: cfg}
set := &ioSet{
in: stdin,
out: stdout,
err: stderr,
}
closer, err := copyIO(paths, set, cfg.Terminal)
if err != nil {
return nil, err
}
i.closer = closer
return i, nil
}
}
// Stdio returns an IO set to be used for a task
// that outputs the container's IO as the current processes Stdio
func Stdio(id string) (IO, error) {
return NewIO(os.Stdin, os.Stdout, os.Stderr)(id)
}
// StdioTerminal will setup the IO for the task to use a terminal
func StdioTerminal(id string) (IO, error) {
return NewIOWithTerminal(os.Stdin, os.Stdout, os.Stderr, true)(id)
}
// NullIO redirects the container's IO into /dev/null
func NullIO(id string) (IO, error) {
return &cio{}, nil
}
// FIFOSet is a set of fifos for use with tasks
type FIFOSet struct {
// Dir is the directory holding the task fifos
Dir string
// In, Out, and Err fifo paths
In, Out, Err string
// Terminal returns true if a terminal is being used for the task
Terminal bool
}
type ioSet struct {
in io.Reader
out, err io.Writer
}
type wgCloser struct {
wg *sync.WaitGroup
dir string
set []io.Closer
cancel context.CancelFunc
}
func (g *wgCloser) Wait() {
g.wg.Wait()
}
func (g *wgCloser) Close() error {
for _, f := range g.set {
f.Close()
}
if g.dir != "" {
return os.RemoveAll(g.dir)
}
return nil
}
func (g *wgCloser) Cancel() {
g.cancel()
}