Skip to content

Commit 9ed2c0a

Browse files
authored
Merge pull request #3085 from crosbymichael/shim-logs
Shim pluggable logging
2 parents 8f63d2a + e6ae9cc commit 9ed2c0a

16 files changed

Lines changed: 538 additions & 105 deletions

File tree

cio/io.go

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import (
2020
"context"
2121
"fmt"
2222
"io"
23+
"net/url"
2324
"os"
24-
"path/filepath"
2525
"sync"
2626

2727
"github.com/containerd/containerd/defaults"
@@ -222,46 +222,76 @@ type DirectIO struct {
222222
cio
223223
}
224224

225-
var _ IO = &DirectIO{}
225+
var (
226+
_ IO = &DirectIO{}
227+
_ IO = &logURI{}
228+
)
229+
230+
// LogURI provides the raw logging URI
231+
func LogURI(uri *url.URL) Creator {
232+
return func(_ string) (IO, error) {
233+
return &logURI{
234+
config: Config{
235+
Stdout: uri.String(),
236+
Stderr: uri.String(),
237+
},
238+
}, nil
239+
}
240+
}
241+
242+
// BinaryIO forwards container STDOUT|STDERR directly to a logging binary
243+
func BinaryIO(binary string, args map[string]string) Creator {
244+
return func(_ string) (IO, error) {
245+
uri := &url.URL{
246+
Scheme: "binary",
247+
Host: binary,
248+
}
249+
for k, v := range args {
250+
uri.Query().Set(k, v)
251+
}
252+
return &logURI{
253+
config: Config{
254+
Stdout: uri.String(),
255+
Stderr: uri.String(),
256+
},
257+
}, nil
258+
}
259+
}
226260

227261
// LogFile creates a file on disk that logs the task's STDOUT,STDERR.
228262
// If the log file already exists, the logs will be appended to the file.
229263
func LogFile(path string) Creator {
230264
return func(_ string) (IO, error) {
231-
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
232-
return nil, err
233-
}
234-
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
235-
if err != nil {
236-
return nil, err
265+
uri := &url.URL{
266+
Scheme: "file",
267+
Host: path,
237268
}
238-
f.Close()
239-
return &logIO{
269+
return &logURI{
240270
config: Config{
241-
Stdout: path,
242-
Stderr: path,
271+
Stdout: uri.String(),
272+
Stderr: uri.String(),
243273
},
244274
}, nil
245275
}
246276
}
247277

248-
type logIO struct {
278+
type logURI struct {
249279
config Config
250280
}
251281

252-
func (l *logIO) Config() Config {
282+
func (l *logURI) Config() Config {
253283
return l.config
254284
}
255285

256-
func (l *logIO) Cancel() {
286+
func (l *logURI) Cancel() {
257287

258288
}
259289

260-
func (l *logIO) Wait() {
290+
func (l *logURI) Wait() {
261291

262292
}
263293

264-
func (l *logIO) Close() error {
294+
func (l *logURI) Close() error {
265295
return nil
266296
}
267297

cmd/ctr/commands/run/run.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ var Command = cli.Command{
9999
Name: "null-io",
100100
Usage: "send all IO to /dev/null",
101101
},
102+
cli.StringFlag{
103+
Name: "log-uri",
104+
Usage: "log uri",
105+
},
102106
cli.BoolFlag{
103107
Name: "detach,d",
104108
Usage: "detach from the task after it has started execution",
@@ -161,7 +165,7 @@ var Command = cli.Command{
161165
}
162166
opts := getNewTaskOpts(context)
163167
ioOpts := []cio.Opt{cio.WithFIFODir(context.String("fifo-dir"))}
164-
task, err := tasks.NewTask(ctx, client, container, context.String("checkpoint"), con, context.Bool("null-io"), ioOpts, opts...)
168+
task, err := tasks.NewTask(ctx, client, container, context.String("checkpoint"), con, context.Bool("null-io"), context.String("log-uri"), ioOpts, opts...)
165169
if err != nil {
166170
return err
167171
}

cmd/ctr/commands/tasks/start.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ var startCommand = cli.Command{
3535
Name: "null-io",
3636
Usage: "send all IO to /dev/null",
3737
},
38+
cli.StringFlag{
39+
Name: "log-uri",
40+
Usage: "log uri",
41+
},
3842
cli.StringFlag{
3943
Name: "fifo-dir",
4044
Usage: "directory used for storing IO FIFOs",
@@ -85,7 +89,7 @@ var startCommand = cli.Command{
8589
}
8690
}
8791

88-
task, err := NewTask(ctx, client, container, "", con, context.Bool("null-io"), ioOpts, opts...)
92+
task, err := NewTask(ctx, client, container, "", con, context.Bool("null-io"), context.String("log-uri"), ioOpts, opts...)
8993
if err != nil {
9094
return err
9195
}

cmd/ctr/commands/tasks/tasks_unix.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package tasks
2020

2121
import (
2222
gocontext "context"
23+
"net/url"
2324
"os"
2425
"os/signal"
2526

@@ -67,7 +68,7 @@ func HandleConsoleResize(ctx gocontext.Context, task resizer, con console.Consol
6768
}
6869

6970
// NewTask creates a new task
70-
func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, checkpoint string, con console.Console, nullIO bool, ioOpts []cio.Opt, opts ...containerd.NewTaskOpts) (containerd.Task, error) {
71+
func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, checkpoint string, con console.Console, nullIO bool, logURI string, ioOpts []cio.Opt, opts ...containerd.NewTaskOpts) (containerd.Task, error) {
7172
stdio := cio.NewCreator(append([]cio.Opt{cio.WithStdio}, ioOpts...)...)
7273
if checkpoint != "" {
7374
im, err := client.GetImage(ctx, checkpoint)
@@ -86,6 +87,13 @@ func NewTask(ctx gocontext.Context, client *containerd.Client, container contain
8687
}
8788
ioCreator = cio.NullIO
8889
}
90+
if logURI != "" {
91+
u, err := url.Parse(logURI)
92+
if err != nil {
93+
return nil, err
94+
}
95+
ioCreator = cio.LogURI(u)
96+
}
8997
return container.NewTask(ctx, ioCreator, opts...)
9098
}
9199

cmd/ctr/commands/tasks/tasks_windows.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func HandleConsoleResize(ctx gocontext.Context, task resizer, con console.Consol
5858
}
5959

6060
// NewTask creates a new task
61-
func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, _ string, con console.Console, nullIO bool, ioOpts []cio.Opt, opts ...containerd.NewTaskOpts) (containerd.Task, error) {
61+
func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, _ string, con console.Console, nullIO bool, logURI string, ioOpts []cio.Opt, opts ...containerd.NewTaskOpts) (containerd.Task, error) {
6262
var ioCreator cio.Creator
6363
if con != nil {
6464
if nullIO {

runtime/proc/proc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ type Process interface {
7272
// platform implementations
7373
type Platform interface {
7474
CopyConsole(ctx context.Context, console console.Console, stdin, stdout, stderr string,
75-
wg, cwg *sync.WaitGroup) (console.Console, error)
75+
wg *sync.WaitGroup) (console.Console, error)
7676
ShutdownConsole(ctx context.Context, console console.Console) error
7777
Close() error
7878
}

runtime/v1/linux/proc/exec.go

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ type execProcess struct {
4646
mu sync.Mutex
4747
id string
4848
console console.Console
49-
io runc.IO
49+
io *processIO
5050
status int
5151
exited time.Time
5252
pid *safePid
@@ -172,29 +172,30 @@ func (e *execProcess) start(ctx context.Context) (err error) {
172172
// access e.pid until it is updated.
173173
e.pid.Lock()
174174
defer e.pid.Unlock()
175+
175176
var (
176177
socket *runc.Socket
177-
pidfile = filepath.Join(e.path, fmt.Sprintf("%s.pid", e.id))
178+
pio *processIO
179+
pidFile = newExecPidFile(e.path, e.id)
178180
)
179181
if e.stdio.Terminal {
180182
if socket, err = runc.NewTempConsoleSocket(); err != nil {
181183
return errors.Wrap(err, "failed to create runc console socket")
182184
}
183185
defer socket.Close()
184-
} else if e.stdio.IsNull() {
185-
if e.io, err = runc.NewNullIO(); err != nil {
186-
return errors.Wrap(err, "creating new NULL IO")
187-
}
188186
} else {
189-
if e.io, err = runc.NewPipeIO(e.parent.IoUID, e.parent.IoGID, withConditionalIO(e.stdio)); err != nil {
190-
return errors.Wrap(err, "failed to create runc io pipes")
187+
if pio, err = createIO(ctx, e.id, e.parent.IoUID, e.parent.IoGID, e.stdio); err != nil {
188+
return errors.Wrap(err, "failed to create init process I/O")
191189
}
190+
e.io = pio
192191
}
193192
opts := &runc.ExecOpts{
194-
PidFile: pidfile,
195-
IO: e.io,
193+
PidFile: pidFile.Path(),
196194
Detach: true,
197195
}
196+
if pio != nil {
197+
opts.IO = pio.IO()
198+
}
198199
if socket != nil {
199200
opts.ConsoleSocket = socket
200201
}
@@ -203,38 +204,43 @@ func (e *execProcess) start(ctx context.Context) (err error) {
203204
return e.parent.runtimeError(err, "OCI runtime exec failed")
204205
}
205206
if e.stdio.Stdin != "" {
206-
sc, err := fifo.OpenFifo(context.Background(), e.stdio.Stdin, syscall.O_WRONLY|syscall.O_NONBLOCK, 0)
207-
if err != nil {
208-
return errors.Wrapf(err, "failed to open stdin fifo %s", e.stdio.Stdin)
207+
if err := e.openStdin(e.stdio.Stdin); err != nil {
208+
return err
209209
}
210-
e.closers = append(e.closers, sc)
211-
e.stdin = sc
212210
}
213-
var copyWaitGroup sync.WaitGroup
214211
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
215212
defer cancel()
216213
if socket != nil {
217214
console, err := socket.ReceiveMaster()
218215
if err != nil {
219216
return errors.Wrap(err, "failed to retrieve console master")
220217
}
221-
if e.console, err = e.parent.Platform.CopyConsole(ctx, console, e.stdio.Stdin, e.stdio.Stdout, e.stdio.Stderr, &e.wg, &copyWaitGroup); err != nil {
218+
if e.console, err = e.parent.Platform.CopyConsole(ctx, console, e.stdio.Stdin, e.stdio.Stdout, e.stdio.Stderr, &e.wg); err != nil {
222219
return errors.Wrap(err, "failed to start console copy")
223220
}
224-
} else if !e.stdio.IsNull() {
225-
if err := copyPipes(ctx, e.io, e.stdio.Stdin, e.stdio.Stdout, e.stdio.Stderr, &e.wg, &copyWaitGroup); err != nil {
221+
} else {
222+
if err := pio.Copy(ctx, &e.wg); err != nil {
226223
return errors.Wrap(err, "failed to start io pipe copy")
227224
}
228225
}
229-
copyWaitGroup.Wait()
230-
pid, err := runc.ReadPidFile(opts.PidFile)
226+
pid, err := pidFile.Read()
231227
if err != nil {
232228
return errors.Wrap(err, "failed to retrieve OCI runtime exec pid")
233229
}
234230
e.pid.pid = pid
235231
return nil
236232
}
237233

234+
func (e *execProcess) openStdin(path string) error {
235+
sc, err := fifo.OpenFifo(context.Background(), path, syscall.O_WRONLY|syscall.O_NONBLOCK, 0)
236+
if err != nil {
237+
return errors.Wrapf(err, "failed to open stdin fifo %s", path)
238+
}
239+
e.stdin = sc
240+
e.closers = append(e.closers, sc)
241+
return nil
242+
}
243+
238244
func (e *execProcess) Status(ctx context.Context) (string, error) {
239245
s, err := e.parent.Status(ctx)
240246
if err != nil {

0 commit comments

Comments
 (0)