Skip to content

Commit 660680b

Browse files
authoredSep 11, 2022
Merge pull request #1354 from manugupt1/3plog
Implements logURI to support 3rd party ctrd shim logger
2 parents e57225e + 0273c00 commit 660680b

File tree

3 files changed

+126
-28
lines changed

3 files changed

+126
-28
lines changed
 

‎README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,8 @@ Logging flags:
509509
- :whale: `--log-opt=fluentd-sub-second-precision=<true|false>`: Enable sub-second precision for fluentd. The default value is false.
510510
- :nerd_face: `--log-opt=fluentd-async-reconnect-interval=<1s|1ms>`: The time to wait before retrying to reconnect to fluentd. The default value is 0s.
511511
- :nerd_face: `--log-opt=fluentd-request-ack=<true|false>`: Enable request ack for fluentd. The default value is false.
512+
- :nerd_face: Accepts a LogURI which is a containerd shim logger. A scheme must be specified for the URI. Example: `nerdctl run -d --log-driver binary:///usr/bin/ctr-journald-shim docker.io/library/hello-world:latest`. An implementation of shim logger can be found at (https://github.com/containerd/containerd/tree/dbef1d56d7ebc05bc4553d72c419ed5ce025b05d/runtime/v2#logging)
513+
512514

513515
Shared memory flags:
514516
- :whale: `--ipc`: IPC namespace to use

‎cmd/nerdctl/run.go

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ func setCreateFlags(cmd *cobra.Command) {
250250

251251
// #region logging flags
252252
// log-opt needs to be StringArray, not StringSlice, to prevent "env=os,customer" from being split to {"env=os", "customer"}
253-
cmd.Flags().String("log-driver", "json-file", "Logging driver for the container")
253+
cmd.Flags().String("log-driver", "json-file", "Logging driver for the container. Default is json-file. It also supports logURI (eg: --log-driver binary://<path>)")
254254
cmd.RegisterFlagCompletionFunc("log-driver", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
255255
return logging.Drivers(), cobra.ShellCompDirectiveNoFileComp
256256
})
@@ -497,33 +497,41 @@ func createContainer(cmd *cobra.Command, ctx context.Context, client *containerd
497497
if err != nil {
498498
return nil, nil, err
499499
}
500-
logOptMap, err := parseKVStringsMapFromLogOpt(cmd, logDriver)
501-
if err != nil {
502-
return nil, nil, err
503-
}
504-
logDriverInst, err := logging.GetDriver(logDriver, logOptMap)
505-
if err != nil {
506-
return nil, nil, err
507-
}
508-
if err := logDriverInst.Init(dataStore, ns, id); err != nil {
509-
return nil, nil, err
510-
}
511-
logConfig := &logging.LogConfig{
512-
Driver: logDriver,
513-
Opts: logOptMap,
514-
}
515-
logConfigB, err := json.Marshal(logConfig)
516-
if err != nil {
517-
return nil, nil, err
518-
}
519-
logConfigFilePath := logging.LogConfigFilePath(dataStore, ns, id)
520-
if err = os.WriteFile(logConfigFilePath, logConfigB, 0600); err != nil {
521-
return nil, nil, err
522-
}
523-
if lu, err := generateLogURI(dataStore); err != nil {
524-
return nil, nil, err
525-
} else if lu != nil {
526-
logURI = lu.String()
500+
501+
// check if log driver is a valid uri. If it is a valid uri and scheme is not
502+
if u, err := url.Parse(logDriver); err == nil && u.Scheme != "" {
503+
logURI = logDriver
504+
} else {
505+
logOptMap, err := parseKVStringsMapFromLogOpt(cmd, logDriver)
506+
if err != nil {
507+
return nil, nil, err
508+
}
509+
logDriverInst, err := logging.GetDriver(logDriver, logOptMap)
510+
if err != nil {
511+
return nil, nil, err
512+
}
513+
if err := logDriverInst.Init(dataStore, ns, id); err != nil {
514+
return nil, nil, err
515+
}
516+
logConfig := &logging.LogConfig{
517+
Driver: logDriver,
518+
Opts: logOptMap,
519+
}
520+
logConfigB, err := json.Marshal(logConfig)
521+
if err != nil {
522+
return nil, nil, err
523+
}
524+
logConfigFilePath := logging.LogConfigFilePath(dataStore, ns, id)
525+
if err = os.WriteFile(logConfigFilePath, logConfigB, 0600); err != nil {
526+
return nil, nil, err
527+
}
528+
if lu, err := generateLogURI(dataStore); err != nil {
529+
return nil, nil, err
530+
} else if lu != nil {
531+
logrus.Debugf("generated log driver: %s", lu.String())
532+
533+
logURI = lu.String()
534+
}
527535
}
528536
}
529537

‎cmd/nerdctl/run_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,3 +305,91 @@ func TestRunWithJournaldLogDriverAndLogOpt(t *testing.T) {
305305
poll.WaitOn(t, check, poll.WithDelay(100*time.Microsecond), poll.WithTimeout(20*time.Second))
306306
assert.Equal(t, 1, found)
307307
}
308+
309+
func TestRunWithLogBinary(t *testing.T) {
310+
if runtime.GOOS == "windows" {
311+
t.Skip("buildkit is not enabled on windows, this feature may work on windows.")
312+
}
313+
testutil.DockerIncompatible(t)
314+
t.Parallel()
315+
base := testutil.NewBase(t)
316+
imageName := testutil.Identifier(t) + "-image"
317+
containerName := testutil.Identifier(t)
318+
319+
const dockerfile = `
320+
FROM golang:latest as builder
321+
WORKDIR /go/src/
322+
RUN mkdir -p logger
323+
WORKDIR /go/src/logger
324+
RUN echo '\
325+
package main \n\
326+
\n\
327+
import ( \n\
328+
"bufio" \n\
329+
"context" \n\
330+
"fmt" \n\
331+
"io" \n\
332+
"os" \n\
333+
"path/filepath" \n\
334+
"sync" \n\
335+
\n\
336+
"github.com/containerd/containerd/runtime/v2/logging"\n\
337+
)\n\
338+
339+
func main() {\n\
340+
logging.Run(log)\n\
341+
}\n\
342+
343+
func log(ctx context.Context, config *logging.Config, ready func() error) error {\n\
344+
var wg sync.WaitGroup \n\
345+
wg.Add(2) \n\
346+
// forward both stdout and stderr to temp files \n\
347+
go copy(&wg, config.Stdout, config.ID, "stdout") \n\
348+
go copy(&wg, config.Stderr, config.ID, "stderr") \n\
349+
350+
// signal that we are ready and setup for the container to be started \n\
351+
if err := ready(); err != nil { \n\
352+
return err \n\
353+
} \n\
354+
wg.Wait() \n\
355+
return nil \n\
356+
}\n\
357+
\n\
358+
func copy(wg *sync.WaitGroup, r io.Reader, id string, kind string) { \n\
359+
f, _ := os.Create(filepath.Join(os.TempDir(), fmt.Sprintf("%s_%s.log", id, kind))) \n\
360+
defer f.Close() \n\
361+
defer wg.Done() \n\
362+
s := bufio.NewScanner(r) \n\
363+
for s.Scan() { \n\
364+
f.WriteString(s.Text()) \n\
365+
} \n\
366+
}\n' >> main.go
367+
368+
369+
RUN go mod init
370+
RUN go mod tidy
371+
RUN go build .
372+
373+
FROM scratch
374+
COPY --from=builder /go/src/logger/logger /
375+
`
376+
377+
buildCtx, err := createBuildContext(dockerfile)
378+
assert.NilError(t, err)
379+
defer os.RemoveAll(buildCtx)
380+
tmpDir := t.TempDir()
381+
base.Cmd("build", buildCtx, "--output", fmt.Sprintf("type=local,src=/go/src/logger/logger,dest=%s", tmpDir)).AssertOK()
382+
defer base.Cmd("image", "rm", "-f", imageName).AssertOK()
383+
384+
base.Cmd("container", "rm", "-f", containerName).AssertOK()
385+
base.Cmd("run", "-d", "--log-driver", fmt.Sprintf("binary://%s/logger", tmpDir), "--name", containerName, testutil.CommonImage,
386+
"sh", "-euxc", "echo foo; echo bar").AssertOK()
387+
defer base.Cmd("container", "rm", "-f", containerName)
388+
389+
inspectedContainer := base.InspectContainer(containerName)
390+
bytes, err := os.ReadFile(filepath.Join(os.TempDir(), fmt.Sprintf("%s_%s.log", inspectedContainer.ID, "stdout")))
391+
assert.NilError(t, err)
392+
log := string(bytes)
393+
assert.Check(t, strings.Contains(log, "foo"))
394+
assert.Check(t, strings.Contains(log, "bar"))
395+
}

0 commit comments

Comments
 (0)