From ddb5e1651ab118534181d451ce33667bbb09b17c Mon Sep 17 00:00:00 2001 From: Maksim An Date: Fri, 20 Nov 2020 00:55:54 -0800 Subject: [PATCH] Enhance logging driver and ctr tasks to support windows Signed-off-by: Maksim An --- cmd/ctr/commands/tasks/tasks_windows.go | 7 ++ runtime/v2/logging/logging.go | 43 ----------- runtime/v2/logging/logging_unix.go | 64 ++++++++++++++++ runtime/v2/logging/logging_windows.go | 99 +++++++++++++++++++++++++ 4 files changed, 170 insertions(+), 43 deletions(-) create mode 100644 runtime/v2/logging/logging_unix.go create mode 100644 runtime/v2/logging/logging_windows.go diff --git a/cmd/ctr/commands/tasks/tasks_windows.go b/cmd/ctr/commands/tasks/tasks_windows.go index b16e8d8c8..8905c5b86 100644 --- a/cmd/ctr/commands/tasks/tasks_windows.go +++ b/cmd/ctr/commands/tasks/tasks_windows.go @@ -18,6 +18,7 @@ package tasks import ( gocontext "context" + "net/url" "time" "github.com/containerd/console" @@ -67,6 +68,12 @@ func NewTask(ctx gocontext.Context, client *containerd.Client, container contain ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStreams(con, con, nil), cio.WithTerminal}, ioOpts...)...) } else if nullIO { ioCreator = cio.NullIO + } else if logURI != "" { + u, err := url.Parse(logURI) + if err != nil { + return nil, err + } + ioCreator = cio.LogURI(u) } else { ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStdio}, ioOpts...)...) } diff --git a/runtime/v2/logging/logging.go b/runtime/v2/logging/logging.go index c394864c0..e60c4e4fe 100644 --- a/runtime/v2/logging/logging.go +++ b/runtime/v2/logging/logging.go @@ -1,5 +1,3 @@ -// +build !windows - /* Copyright The containerd Authors. @@ -20,12 +18,7 @@ package logging import ( "context" - "fmt" "io" - "os" - "os/signal" - - "golang.org/x/sys/unix" ) // Config of the container logs @@ -38,39 +31,3 @@ type Config struct { // LoggerFunc is implemented by custom v2 logging binaries type LoggerFunc func(context.Context, *Config, func() error) error - -// Run the logging driver -func Run(fn LoggerFunc) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - config := &Config{ - ID: os.Getenv("CONTAINER_ID"), - Namespace: os.Getenv("CONTAINER_NAMESPACE"), - Stdout: os.NewFile(3, "CONTAINER_STDOUT"), - Stderr: os.NewFile(4, "CONTAINER_STDERR"), - } - var ( - s = make(chan os.Signal, 32) - errCh = make(chan error, 1) - wait = os.NewFile(5, "CONTAINER_WAIT") - ) - signal.Notify(s, unix.SIGTERM) - - go func() { - errCh <- fn(ctx, config, wait.Close) - }() - - for { - select { - case <-s: - cancel() - case err := <-errCh: - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - os.Exit(0) - } - } -} diff --git a/runtime/v2/logging/logging_unix.go b/runtime/v2/logging/logging_unix.go new file mode 100644 index 000000000..cde7179ee --- /dev/null +++ b/runtime/v2/logging/logging_unix.go @@ -0,0 +1,64 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package logging + +import ( + "context" + "fmt" + "os" + "os/signal" + + "golang.org/x/sys/unix" +) + +// Run the logging driver +func Run(fn LoggerFunc) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + config := &Config{ + ID: os.Getenv("CONTAINER_ID"), + Namespace: os.Getenv("CONTAINER_NAMESPACE"), + Stdout: os.NewFile(3, "CONTAINER_STDOUT"), + Stderr: os.NewFile(4, "CONTAINER_STDERR"), + } + var ( + sigCh = make(chan os.Signal, 32) + errCh = make(chan error, 1) + wait = os.NewFile(5, "CONTAINER_WAIT") + ) + signal.Notify(sigCh, unix.SIGTERM) + + go func() { + errCh <- fn(ctx, config, wait.Close) + }() + + for { + select { + case <-sigCh: + cancel() + case err := <-errCh: + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + os.Exit(0) + } + } +} diff --git a/runtime/v2/logging/logging_windows.go b/runtime/v2/logging/logging_windows.go new file mode 100644 index 000000000..395dc82ba --- /dev/null +++ b/runtime/v2/logging/logging_windows.go @@ -0,0 +1,99 @@ +// +build windows + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package logging + +import ( + "context" + "fmt" + "net" + "os" + "os/signal" + "syscall" + + "github.com/Microsoft/go-winio" + "github.com/pkg/errors" +) + +// Run the logging driver +func Run(fn LoggerFunc) { + err := runInternal(fn) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + os.Exit(0) +} + +func runInternal(fn LoggerFunc) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + var ( + soutPipe, serrPipe, waitPipe string + sout, serr, wait net.Conn + ok bool + err error + ) + + if soutPipe, ok = os.LookupEnv("CONTAINER_STDOUT"); !ok { + return errors.New("'CONTAINER_STDOUT' environment variable missing") + } + if sout, err = winio.DialPipeContext(ctx, soutPipe); err != nil { + return errors.Wrap(err, "unable to dial stdout pipe") + } + + if serrPipe, ok = os.LookupEnv("CONTAINER_STDERR"); !ok { + return errors.New("'CONTAINER_STDERR' environment variable missing") + } + if serr, err = winio.DialPipeContext(ctx, serrPipe); err != nil { + return errors.Wrap(err, "unable to dial stderr pipe") + } + + waitPipe = os.Getenv("CONTAINER_WAIT") + if wait, err = winio.DialPipeContext(ctx, waitPipe); err != nil { + return errors.Wrap(err, "unable to dial wait pipe") + } + + config := &Config{ + ID: os.Getenv("CONTAINER_ID"), + Namespace: os.Getenv("CONTAINER_NAMESPACE"), + Stdout: sout, + Stderr: serr, + } + + var ( + sigCh = make(chan os.Signal, 2) + errCh = make(chan error, 1) + ) + + signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) + + go func() { + errCh <- fn(ctx, config, wait.Close) + }() + + for { + select { + case <-sigCh: + cancel() + case err = <-errCh: + return err + } + } +}