From ffd0d2ef58eaa625050105cfa29c01019d2c200a Mon Sep 17 00:00:00 2001 From: Jess Valarezo Date: Thu, 26 Oct 2017 14:41:04 -0400 Subject: [PATCH 1/2] ctr: move signals to commands package Signed-off-by: Jess Valarezo --- cmd/ctr/attach.go | 4 +- cmd/ctr/commands/signals.go | 59 +++++++++++++++++++++++++ cmd/ctr/{ => commands}/signals_linux.go | 2 +- cmd/ctr/{ => commands}/signals_unix.go | 2 +- cmd/ctr/commands/signals_windows.go | 23 ++++++++++ cmd/ctr/exec.go | 5 ++- cmd/ctr/kill.go | 4 +- cmd/ctr/run.go | 9 +--- cmd/ctr/start.go | 4 +- cmd/ctr/utils.go | 42 ------------------ cmd/ctr/utils_windows.go | 18 -------- 11 files changed, 96 insertions(+), 76 deletions(-) create mode 100644 cmd/ctr/commands/signals.go rename cmd/ctr/{ => commands}/signals_linux.go (98%) rename cmd/ctr/{ => commands}/signals_unix.go (98%) create mode 100644 cmd/ctr/commands/signals_windows.go diff --git a/cmd/ctr/attach.go b/cmd/ctr/attach.go index 9772141b8..f1af6ab30 100644 --- a/cmd/ctr/attach.go +++ b/cmd/ctr/attach.go @@ -55,8 +55,8 @@ var taskAttachCommand = cli.Command{ logrus.WithError(err).Error("console resize") } } else { - sigc := forwardAllSignals(ctx, task) - defer stopCatch(sigc) + sigc := commands.ForwardAllSignals(ctx, task) + defer commands.StopCatch(sigc) } ec := <-statusC diff --git a/cmd/ctr/commands/signals.go b/cmd/ctr/commands/signals.go new file mode 100644 index 000000000..f42e3b523 --- /dev/null +++ b/cmd/ctr/commands/signals.go @@ -0,0 +1,59 @@ +package commands + +import ( + gocontext "context" + "fmt" + "os" + "os/signal" + "strconv" + "strings" + "syscall" + + "github.com/containerd/containerd" + "github.com/sirupsen/logrus" +) + +type killer interface { + Kill(gocontext.Context, syscall.Signal, ...containerd.KillOpts) error +} + +// ForwardAllSignals forwards signals +func ForwardAllSignals(ctx gocontext.Context, task killer) chan os.Signal { + sigc := make(chan os.Signal, 128) + signal.Notify(sigc) + go func() { + for s := range sigc { + logrus.Debug("forwarding signal ", s) + if err := task.Kill(ctx, s.(syscall.Signal)); err != nil { + logrus.WithError(err).Errorf("forward signal %s", s) + } + } + }() + return sigc +} + +// StopCatch stops and closes a channel +func StopCatch(sigc chan os.Signal) { + signal.Stop(sigc) + close(sigc) +} + +// ParseSignal parses a given string into a syscall.Signal +// it checks that the signal exists in the platform-appropriate signalMap +func ParseSignal(rawSignal string) (syscall.Signal, error) { + s, err := strconv.Atoi(rawSignal) + if err == nil { + sig := syscall.Signal(s) + for _, msig := range signalMap { + if sig == msig { + return sig, nil + } + } + return -1, fmt.Errorf("unknown signal %q", rawSignal) + } + signal, ok := signalMap[strings.TrimPrefix(strings.ToUpper(rawSignal), "SIG")] + if !ok { + return -1, fmt.Errorf("unknown signal %q", rawSignal) + } + return signal, nil +} diff --git a/cmd/ctr/signals_linux.go b/cmd/ctr/commands/signals_linux.go similarity index 98% rename from cmd/ctr/signals_linux.go rename to cmd/ctr/commands/signals_linux.go index 0cc0a5022..97a17a314 100644 --- a/cmd/ctr/signals_linux.go +++ b/cmd/ctr/commands/signals_linux.go @@ -1,4 +1,4 @@ -package main +package commands import ( "syscall" diff --git a/cmd/ctr/signals_unix.go b/cmd/ctr/commands/signals_unix.go similarity index 98% rename from cmd/ctr/signals_unix.go rename to cmd/ctr/commands/signals_unix.go index 871a8f6bf..b940c3700 100644 --- a/cmd/ctr/signals_unix.go +++ b/cmd/ctr/commands/signals_unix.go @@ -1,6 +1,6 @@ // +build darwin freebsd solaris -package main +package commands import ( "syscall" diff --git a/cmd/ctr/commands/signals_windows.go b/cmd/ctr/commands/signals_windows.go new file mode 100644 index 000000000..695d6b1a6 --- /dev/null +++ b/cmd/ctr/commands/signals_windows.go @@ -0,0 +1,23 @@ +package commands + +import ( + "syscall" + + "golang.org/x/sys/windows" +) + +var signalMap = map[string]syscall.Signal{ + "HUP": syscall.Signal(windows.SIGHUP), + "INT": syscall.Signal(windows.SIGINT), + "QUIT": syscall.Signal(windows.SIGQUIT), + "SIGILL": syscall.Signal(windows.SIGILL), + "TRAP": syscall.Signal(windows.SIGTRAP), + "ABRT": syscall.Signal(windows.SIGABRT), + "BUS": syscall.Signal(windows.SIGBUS), + "FPE": syscall.Signal(windows.SIGFPE), + "KILL": syscall.Signal(windows.SIGKILL), + "SEGV": syscall.Signal(windows.SIGSEGV), + "PIPE": syscall.Signal(windows.SIGPIPE), + "ALRM": syscall.Signal(windows.SIGALRM), + "TERM": syscall.Signal(windows.SIGTERM), +} diff --git a/cmd/ctr/exec.go b/cmd/ctr/exec.go index ecfddf27d..65659827d 100644 --- a/cmd/ctr/exec.go +++ b/cmd/ctr/exec.go @@ -10,6 +10,7 @@ import ( "github.com/urfave/cli" ) +//TODO:(jessvalarezo) exec-id is optional here, update to required arg var taskExecCommand = cli.Command{ Name: "exec", Usage: "execute additional processes in an existing container", @@ -87,8 +88,8 @@ var taskExecCommand = cli.Command{ logrus.WithError(err).Error("console resize") } } else { - sigc := forwardAllSignals(ctx, process) - defer stopCatch(sigc) + sigc := commands.ForwardAllSignals(ctx, process) + defer commands.StopCatch(sigc) } if err := process.Start(ctx); err != nil { diff --git a/cmd/ctr/kill.go b/cmd/ctr/kill.go index 3d479ef3c..159f409ee 100644 --- a/cmd/ctr/kill.go +++ b/cmd/ctr/kill.go @@ -6,6 +6,8 @@ import ( "github.com/urfave/cli" ) +// TODO:(jessvalarezo) the pid flag is not used here +// update to be able to signal given pid var taskKillCommand = cli.Command{ Name: "kill", Usage: "signal a container (default: SIGTERM)", @@ -31,7 +33,7 @@ var taskKillCommand = cli.Command{ if id == "" { return errors.New("container id must be provided") } - signal, err := parseSignal(context.String("signal")) + signal, err := commands.ParseSignal(context.String("signal")) if err != nil { return err } diff --git a/cmd/ctr/run.go b/cmd/ctr/run.go index e0f0a82b9..88bacf2d2 100644 --- a/cmd/ctr/run.go +++ b/cmd/ctr/run.go @@ -4,7 +4,6 @@ import ( gocontext "context" "fmt" "runtime" - "syscall" "github.com/containerd/console" "github.com/containerd/containerd" @@ -20,10 +19,6 @@ type resizer interface { Resize(ctx gocontext.Context, w, h uint32) error } -type killer interface { - Kill(gocontext.Context, syscall.Signal, ...containerd.KillOpts) error -} - func withEnv(context *cli.Context) containerd.SpecOpts { return func(_ gocontext.Context, _ *containerd.Client, _ *containers.Container, s *specs.Spec) error { env := context.StringSlice("env") @@ -160,8 +155,8 @@ var runCommand = cli.Command{ logrus.WithError(err).Error("console resize") } } else { - sigc := forwardAllSignals(ctx, task) - defer stopCatch(sigc) + sigc := commands.ForwardAllSignals(ctx, task) + defer commands.StopCatch(sigc) } status := <-statusC code, _, err := status.Result() diff --git a/cmd/ctr/start.go b/cmd/ctr/start.go index 87b72ebf1..9f37af9ee 100644 --- a/cmd/ctr/start.go +++ b/cmd/ctr/start.go @@ -70,8 +70,8 @@ var taskStartCommand = cli.Command{ logrus.WithError(err).Error("console resize") } } else { - sigc := forwardAllSignals(ctx, task) - defer stopCatch(sigc) + sigc := commands.ForwardAllSignals(ctx, task) + defer commands.StopCatch(sigc) } status := <-statusC diff --git a/cmd/ctr/utils.go b/cmd/ctr/utils.go index c6fadc41a..eae6a908e 100644 --- a/cmd/ctr/utils.go +++ b/cmd/ctr/utils.go @@ -8,11 +8,7 @@ import ( "fmt" "net" "net/http" - "os" - "os/signal" - "strconv" "strings" - "syscall" "time" "github.com/containerd/console" @@ -20,7 +16,6 @@ import ( "github.com/containerd/containerd/remotes/docker" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/urfave/cli" ) @@ -95,43 +90,6 @@ func getResolver(ctx gocontext.Context, clicontext *cli.Context) (remotes.Resolv return docker.NewResolver(options), nil } -func forwardAllSignals(ctx gocontext.Context, task killer) chan os.Signal { - sigc := make(chan os.Signal, 128) - signal.Notify(sigc) - go func() { - for s := range sigc { - logrus.Debug("forwarding signal ", s) - if err := task.Kill(ctx, s.(syscall.Signal)); err != nil { - logrus.WithError(err).Errorf("forward signal %s", s) - } - } - }() - return sigc -} - -func parseSignal(rawSignal string) (syscall.Signal, error) { - s, err := strconv.Atoi(rawSignal) - if err == nil { - sig := syscall.Signal(s) - for _, msig := range signalMap { - if sig == msig { - return sig, nil - } - } - return -1, fmt.Errorf("unknown signal %q", rawSignal) - } - signal, ok := signalMap[strings.TrimPrefix(strings.ToUpper(rawSignal), "SIG")] - if !ok { - return -1, fmt.Errorf("unknown signal %q", rawSignal) - } - return signal, nil -} - -func stopCatch(sigc chan os.Signal) { - signal.Stop(sigc) - close(sigc) -} - // parseMountFlag parses a mount string in the form "type=foo,source=/path,destination=/target,options=rbind:rw" func parseMountFlag(m string) (specs.Mount, error) { mount := specs.Mount{} diff --git a/cmd/ctr/utils_windows.go b/cmd/ctr/utils_windows.go index 652478f65..ac364f792 100644 --- a/cmd/ctr/utils_windows.go +++ b/cmd/ctr/utils_windows.go @@ -5,12 +5,10 @@ import ( "net" "os" "sync" - "syscall" "github.com/Microsoft/go-winio" clog "github.com/containerd/containerd/log" "github.com/pkg/errors" - "golang.org/x/sys/windows" ) func prepareStdio(stdin, stdout, stderr string, console bool) (*sync.WaitGroup, error) { @@ -91,19 +89,3 @@ func prepareStdio(stdin, stdout, stderr string, console bool) (*sync.WaitGroup, return &wg, nil } - -var signalMap = map[string]syscall.Signal{ - "HUP": syscall.Signal(windows.SIGHUP), - "INT": syscall.Signal(windows.SIGINT), - "QUIT": syscall.Signal(windows.SIGQUIT), - "SIGILL": syscall.Signal(windows.SIGILL), - "TRAP": syscall.Signal(windows.SIGTRAP), - "ABRT": syscall.Signal(windows.SIGABRT), - "BUS": syscall.Signal(windows.SIGBUS), - "FPE": syscall.Signal(windows.SIGFPE), - "KILL": syscall.Signal(windows.SIGKILL), - "SEGV": syscall.Signal(windows.SIGSEGV), - "PIPE": syscall.Signal(windows.SIGPIPE), - "ALRM": syscall.Signal(windows.SIGALRM), - "TERM": syscall.Signal(windows.SIGTERM), -} From 16855eedf7eb2d61775262ee376585c25165f6aa Mon Sep 17 00:00:00 2001 From: Jess Valarezo Date: Thu, 26 Oct 2017 15:51:04 -0400 Subject: [PATCH 2/2] ctr: move resolver to commands package Signed-off-by: Jess Valarezo --- cmd/ctr/commands/resolver.go | 92 ++++++++++++++++++++++++++++++++++++ cmd/ctr/fetch.go | 2 +- cmd/ctr/fetchobject.go | 2 +- cmd/ctr/push.go | 8 +--- cmd/ctr/pushobject.go | 2 +- cmd/ctr/utils.go | 82 -------------------------------- 6 files changed, 97 insertions(+), 91 deletions(-) create mode 100644 cmd/ctr/commands/resolver.go diff --git a/cmd/ctr/commands/resolver.go b/cmd/ctr/commands/resolver.go new file mode 100644 index 000000000..a8cb77281 --- /dev/null +++ b/cmd/ctr/commands/resolver.go @@ -0,0 +1,92 @@ +package commands + +import ( + "bufio" + gocontext "context" + "crypto/tls" + "fmt" + "net" + "net/http" + "strings" + "time" + + "github.com/containerd/console" + "github.com/containerd/containerd/remotes" + "github.com/containerd/containerd/remotes/docker" + "github.com/pkg/errors" + "github.com/urfave/cli" +) + +// PushTracker returns a new InMemoryTracker which tracks the ref status +var PushTracker = docker.NewInMemoryTracker() + +func passwordPrompt() (string, error) { + c := console.Current() + defer c.Reset() + + if err := c.DisableEcho(); err != nil { + return "", errors.Wrap(err, "failed to disable echo") + } + + line, _, err := bufio.NewReader(c).ReadLine() + if err != nil { + return "", errors.Wrap(err, "failed to read line") + } + return string(line), nil +} + +// GetResolver prepares the resolver from the environment and options +func GetResolver(ctx gocontext.Context, clicontext *cli.Context) (remotes.Resolver, error) { + username := clicontext.String("user") + var secret string + if i := strings.IndexByte(username, ':'); i > 0 { + secret = username[i+1:] + username = username[0:i] + } + options := docker.ResolverOptions{ + PlainHTTP: clicontext.Bool("plain-http"), + Tracker: PushTracker, + } + if username != "" { + if secret == "" { + fmt.Printf("Password: ") + + var err error + secret, err = passwordPrompt() + if err != nil { + return nil, err + } + + fmt.Print("\n") + } + } else if rt := clicontext.String("refresh"); rt != "" { + secret = rt + } + + options.Credentials = func(host string) (string, string, error) { + // Only one host + return username, secret, nil + } + + tr := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + DualStack: true, + }).DialContext, + MaxIdleConns: 10, + IdleConnTimeout: 30 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: clicontext.Bool("insecure"), + }, + ExpectContinueTimeout: 5 * time.Second, + } + + options.Client = &http.Client{ + Transport: tr, + } + + return docker.NewResolver(options), nil +} diff --git a/cmd/ctr/fetch.go b/cmd/ctr/fetch.go index a06afa271..8966ff4f2 100644 --- a/cmd/ctr/fetch.go +++ b/cmd/ctr/fetch.go @@ -57,7 +57,7 @@ func fetch(ref string, cliContext *cli.Context) (containerd.Image, error) { } defer cancel() - resolver, err := getResolver(ctx, cliContext) + resolver, err := commands.GetResolver(ctx, cliContext) if err != nil { return nil, err } diff --git a/cmd/ctr/fetchobject.go b/cmd/ctr/fetchobject.go index 15cf346d9..8d6f9601e 100644 --- a/cmd/ctr/fetchobject.go +++ b/cmd/ctr/fetchobject.go @@ -26,7 +26,7 @@ var fetchObjectCommand = cli.Command{ ctx, cancel := commands.AppContext(context) defer cancel() - resolver, err := getResolver(ctx, context) + resolver, err := commands.GetResolver(ctx, context) if err != nil { return err } diff --git a/cmd/ctr/push.go b/cmd/ctr/push.go index c0c8e4420..90a5d8d39 100644 --- a/cmd/ctr/push.go +++ b/cmd/ctr/push.go @@ -21,10 +21,6 @@ import ( "golang.org/x/sync/errgroup" ) -var ( - pushTracker = docker.NewInMemoryTracker() -) - var pushCommand = cli.Command{ Name: "push", Usage: "push an image to a remote", @@ -74,11 +70,11 @@ var pushCommand = cli.Command{ desc = img.Target } - resolver, err := getResolver(ctx, context) + resolver, err := commands.GetResolver(ctx, context) if err != nil { return err } - ongoing := newPushJobs(pushTracker) + ongoing := newPushJobs(commands.PushTracker) eg, ctx := errgroup.WithContext(ctx) diff --git a/cmd/ctr/pushobject.go b/cmd/ctr/pushobject.go index 57e804825..ac3023f88 100644 --- a/cmd/ctr/pushobject.go +++ b/cmd/ctr/pushobject.go @@ -33,7 +33,7 @@ var pushObjectCommand = cli.Command{ } defer cancel() - resolver, err := getResolver(ctx, context) + resolver, err := commands.GetResolver(ctx, context) if err != nil { return err } diff --git a/cmd/ctr/utils.go b/cmd/ctr/utils.go index eae6a908e..ad00b40ef 100644 --- a/cmd/ctr/utils.go +++ b/cmd/ctr/utils.go @@ -1,95 +1,13 @@ package main import ( - "bufio" - gocontext "context" - "crypto/tls" "encoding/csv" "fmt" - "net" - "net/http" "strings" - "time" - "github.com/containerd/console" - "github.com/containerd/containerd/remotes" - "github.com/containerd/containerd/remotes/docker" specs "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" - "github.com/urfave/cli" ) -func passwordPrompt() (string, error) { - c := console.Current() - defer c.Reset() - - if err := c.DisableEcho(); err != nil { - return "", errors.Wrap(err, "failed to disable echo") - } - - line, _, err := bufio.NewReader(c).ReadLine() - if err != nil { - return "", errors.Wrap(err, "failed to read line") - } - return string(line), nil -} - -// getResolver prepares the resolver from the environment and options. -func getResolver(ctx gocontext.Context, clicontext *cli.Context) (remotes.Resolver, error) { - username := clicontext.String("user") - var secret string - if i := strings.IndexByte(username, ':'); i > 0 { - secret = username[i+1:] - username = username[0:i] - } - options := docker.ResolverOptions{ - PlainHTTP: clicontext.Bool("plain-http"), - Tracker: pushTracker, - } - if username != "" { - if secret == "" { - fmt.Printf("Password: ") - - var err error - secret, err = passwordPrompt() - if err != nil { - return nil, err - } - - fmt.Print("\n") - } - } else if rt := clicontext.String("refresh"); rt != "" { - secret = rt - } - - options.Credentials = func(host string) (string, string, error) { - // Only one host - return username, secret, nil - } - - tr := &http.Transport{ - Proxy: http.ProxyFromEnvironment, - DialContext: (&net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, - DualStack: true, - }).DialContext, - MaxIdleConns: 10, - IdleConnTimeout: 30 * time.Second, - TLSHandshakeTimeout: 10 * time.Second, - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: clicontext.Bool("insecure"), - }, - ExpectContinueTimeout: 5 * time.Second, - } - - options.Client = &http.Client{ - Transport: tr, - } - - return docker.NewResolver(options), nil -} - // parseMountFlag parses a mount string in the form "type=foo,source=/path,destination=/target,options=rbind:rw" func parseMountFlag(m string) (specs.Mount, error) { mount := specs.Mount{}