From 801320a7d870fde2954b75702cba524acc1510ed Mon Sep 17 00:00:00 2001 From: Laitron Date: Mon, 23 Jan 2023 19:20:44 +0800 Subject: [PATCH] [Refactor] Refactor the container start command flagging process Signed-off-by: Laitron --- cmd/nerdctl/compose_start.go | 2 +- cmd/nerdctl/container_restart.go | 2 +- cmd/nerdctl/container_start.go | 143 ++++------------------------- pkg/api/types/container_types.go | 9 ++ pkg/cmd/container/start.go | 64 +++++++++++++ pkg/containerutil/containerutil.go | 89 ++++++++++++++++++ 6 files changed, 180 insertions(+), 129 deletions(-) create mode 100644 pkg/cmd/container/start.go diff --git a/cmd/nerdctl/compose_start.go b/cmd/nerdctl/compose_start.go index 07cf092d521..8a394864411 100644 --- a/cmd/nerdctl/compose_start.go +++ b/cmd/nerdctl/compose_start.go @@ -109,7 +109,7 @@ func startContainers(ctx context.Context, client *containerd.Client, containers } // in compose, always disable attach - if err := startContainer(ctx, c, false, client); err != nil { + if err := containerutil.Start(ctx, c, false, client); err != nil { return err } info, err := c.Info(ctx, containerd.WithoutRefreshedMetadata) diff --git a/cmd/nerdctl/container_restart.go b/cmd/nerdctl/container_restart.go index 7dd0e502719..f3747b9645a 100644 --- a/cmd/nerdctl/container_restart.go +++ b/cmd/nerdctl/container_restart.go @@ -71,7 +71,7 @@ func restartAction(cmd *cobra.Command, args []string) error { if err := containerutil.Stop(ctx, found.Container, timeout); err != nil { return err } - if err := startContainer(ctx, found.Container, false, client); err != nil { + if err := containerutil.Start(ctx, found.Container, false, client); err != nil { return err } _, err = fmt.Fprintf(cmd.OutOrStdout(), "%s\n", found.Req) diff --git a/cmd/nerdctl/container_start.go b/cmd/nerdctl/container_start.go index b82ca3437bd..c19121bbe4f 100644 --- a/cmd/nerdctl/container_start.go +++ b/cmd/nerdctl/container_start.go @@ -17,21 +17,10 @@ package main import ( - "context" - "fmt" - - "github.com/containerd/console" "github.com/containerd/containerd" - "github.com/containerd/containerd/cmd/ctr/commands" - "github.com/containerd/containerd/cmd/ctr/commands/tasks" + "github.com/containerd/nerdctl/pkg/api/types" "github.com/containerd/nerdctl/pkg/clientutil" - "github.com/containerd/nerdctl/pkg/containerutil" - "github.com/containerd/nerdctl/pkg/errutil" - "github.com/containerd/nerdctl/pkg/formatter" - "github.com/containerd/nerdctl/pkg/idutil/containerwalker" - "github.com/containerd/nerdctl/pkg/labels" - "github.com/containerd/nerdctl/pkg/taskutil" - "github.com/sirupsen/logrus" + "github.com/containerd/nerdctl/pkg/cmd/container" "github.com/spf13/cobra" ) @@ -52,135 +41,35 @@ func newStartCommand() *cobra.Command { return startCommand } -func startAction(cmd *cobra.Command, args []string) error { +func processContainerStartOptions(cmd *cobra.Command) (types.ContainerStartOptions, error) { globalOptions, err := processRootCmdFlags(cmd) if err != nil { - return err + return types.ContainerStartOptions{}, err } - client, ctx, cancel, err := clientutil.NewClient(cmd.Context(), globalOptions.Namespace, globalOptions.Address) + attach, err := cmd.Flags().GetBool("attach") if err != nil { - return err - } - defer cancel() - - flagA, err := cmd.Flags().GetBool("attach") - if err != nil { - return err - } - - if flagA && len(args) > 1 { - return fmt.Errorf("you cannot start and attach multiple containers at once") + return types.ContainerStartOptions{}, err } - - walker := &containerwalker.ContainerWalker{ - Client: client, - OnFound: func(ctx context.Context, found containerwalker.Found) error { - if found.MatchCount > 1 { - return fmt.Errorf("multiple IDs found with provided prefix: %s", found.Req) - } - if err := startContainer(ctx, found.Container, flagA, client); err != nil { - return err - } - if !flagA { - _, err := fmt.Fprintf(cmd.OutOrStdout(), "%s\n", found.Req) - if err != nil { - return err - } - } - return err - }, - } - for _, req := range args { - n, err := walker.Walk(ctx, req) - if err != nil { - return err - } else if n == 0 { - return fmt.Errorf("no such container %s", req) - } - } - return nil + return types.ContainerStartOptions{ + Stdout: cmd.OutOrStdout(), + GOptions: globalOptions, + Attach: attach, + }, nil } -func startContainer(ctx context.Context, container containerd.Container, flagA bool, client *containerd.Client) error { - lab, err := container.Labels(ctx) - if err != nil { - return err - } - - if err := containerutil.ReconfigNetContainer(ctx, container, client, lab); err != nil { - return err - } - - if err := containerutil.ReconfigPIDContainer(ctx, container, client, lab); err != nil { - return err - } - - process, err := container.Spec(ctx) +func startAction(cmd *cobra.Command, args []string) error { + options, err := processContainerStartOptions(cmd) if err != nil { return err } - flagT := process.Process.Terminal - var con console.Console - if flagA && flagT { - con = console.Current() - defer con.Reset() - if err := con.SetRaw(); err != nil { - return err - } - } - - logURI := lab[labels.LogURI] - cStatus := formatter.ContainerStatus(ctx, container) - if cStatus == "Up" { - logrus.Warnf("container %s is already running", container.ID()) - return nil - } - if err := containerutil.UpdateExplicitlyStoppedLabel(ctx, container, false); err != nil { - return err - } - if oldTask, err := container.Task(ctx, nil); err == nil { - if _, err := oldTask.Delete(ctx); err != nil { - logrus.WithError(err).Debug("failed to delete old task") - } - } - task, err := taskutil.NewTask(ctx, client, container, flagA, false, flagT, true, con, logURI) + client, ctx, cancel, err := clientutil.NewClient(cmd.Context(), options.GOptions.Namespace, options.GOptions.Address) if err != nil { return err } + defer cancel() - var statusC <-chan containerd.ExitStatus - if flagA { - statusC, err = task.Wait(ctx) - if err != nil { - return err - } - } - - if err := task.Start(ctx); err != nil { - return err - } - - if !flagA { - return nil - } - if flagA && flagT { - if err := tasks.HandleConsoleResize(ctx, task, con); err != nil { - logrus.WithError(err).Error("console resize") - } - } - - sigc := commands.ForwardAllSignals(ctx, task) - defer commands.StopCatch(sigc) - status := <-statusC - code, _, err := status.Result() - if err != nil { - return err - } - if code != 0 { - return errutil.NewExitCoderErr(int(code)) - } - return nil + return container.Start(ctx, client, args, options) } func startShellComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { diff --git a/pkg/api/types/container_types.go b/pkg/api/types/container_types.go index 2202db1c350..1ab73e585cd 100644 --- a/pkg/api/types/container_types.go +++ b/pkg/api/types/container_types.go @@ -21,6 +21,15 @@ import ( "time" ) +// ContainerStartOptions specifies options for the `nerdctl (container) start`. +type ContainerStartOptions struct { + Stdout io.Writer + // GOptions is the global options + GOptions GlobalCommandOptions + // Attach specifies whether to attach to the container's stdio. + Attach bool +} + // KillOptions specifies options for `nerdctl (container) kill`. type KillOptions struct { Stdout io.Writer diff --git a/pkg/cmd/container/start.go b/pkg/cmd/container/start.go new file mode 100644 index 00000000000..c38a45c0c86 --- /dev/null +++ b/pkg/cmd/container/start.go @@ -0,0 +1,64 @@ +/* + 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 container + +import ( + "context" + "fmt" + + "github.com/containerd/containerd" + "github.com/containerd/nerdctl/pkg/api/types" + "github.com/containerd/nerdctl/pkg/containerutil" + "github.com/containerd/nerdctl/pkg/idutil/containerwalker" +) + +// Start starts a list of `containers`. If attach is true, it only starts a single container. +func Start(ctx context.Context, client *containerd.Client, reqs []string, options types.ContainerStartOptions) error { + if options.Attach && len(reqs) > 1 { + return fmt.Errorf("you cannot start and attach multiple containers at once") + } + + walker := &containerwalker.ContainerWalker{ + Client: client, + OnFound: func(ctx context.Context, found containerwalker.Found) error { + var err error + if found.MatchCount > 1 { + return fmt.Errorf("multiple IDs found with provided prefix: %s", found.Req) + } + if err := containerutil.Start(ctx, found.Container, options.Attach, client); err != nil { + return err + } + if !options.Attach { + _, err := fmt.Fprintf(options.Stdout, "%s\n", found.Req) + if err != nil { + return err + } + } + return err + }, + } + + for _, req := range reqs { + n, err := walker.Walk(ctx, req) + if err != nil { + return err + } else if n == 0 { + return fmt.Errorf("no such container %s", req) + } + } + return nil +} diff --git a/pkg/containerutil/containerutil.go b/pkg/containerutil/containerutil.go index b5c3488dc40..26a408da7de 100644 --- a/pkg/containerutil/containerutil.go +++ b/pkg/containerutil/containerutil.go @@ -25,14 +25,20 @@ import ( "strings" "time" + "github.com/containerd/console" "github.com/containerd/containerd" "github.com/containerd/containerd/cio" + "github.com/containerd/containerd/cmd/ctr/commands" + "github.com/containerd/containerd/cmd/ctr/commands/tasks" "github.com/containerd/containerd/containers" "github.com/containerd/containerd/oci" "github.com/containerd/containerd/runtime/restart" + "github.com/containerd/nerdctl/pkg/errutil" + "github.com/containerd/nerdctl/pkg/formatter" "github.com/containerd/nerdctl/pkg/labels" "github.com/containerd/nerdctl/pkg/portutil" "github.com/containerd/nerdctl/pkg/rootlessutil" + "github.com/containerd/nerdctl/pkg/taskutil" "github.com/moby/sys/signal" "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" @@ -179,6 +185,89 @@ func GenerateSharingPIDOpts(ctx context.Context, targetCon containerd.Container) return opts, nil } +// Start starts `container` with `attach` flag. If `attach` is true, it will attach to the container's stdio. +func Start(ctx context.Context, container containerd.Container, flagA bool, client *containerd.Client) error { + lab, err := container.Labels(ctx) + if err != nil { + return err + } + + if err := ReconfigNetContainer(ctx, container, client, lab); err != nil { + return err + } + + if err := ReconfigPIDContainer(ctx, container, client, lab); err != nil { + return err + } + + process, err := container.Spec(ctx) + if err != nil { + return err + } + flagT := process.Process.Terminal + var con console.Console + if flagA && flagT { + con = console.Current() + defer con.Reset() + if err := con.SetRaw(); err != nil { + return err + } + } + + logURI := lab[labels.LogURI] + + cStatus := formatter.ContainerStatus(ctx, container) + if cStatus == "Up" { + logrus.Warnf("container %s is already running", container.ID()) + return nil + } + if err := UpdateExplicitlyStoppedLabel(ctx, container, false); err != nil { + return err + } + if oldTask, err := container.Task(ctx, nil); err == nil { + if _, err := oldTask.Delete(ctx); err != nil { + logrus.WithError(err).Debug("failed to delete old task") + } + } + task, err := taskutil.NewTask(ctx, client, container, flagA, false, flagT, true, con, logURI) + if err != nil { + return err + } + + var statusC <-chan containerd.ExitStatus + if flagA { + statusC, err = task.Wait(ctx) + if err != nil { + return err + } + } + + if err := task.Start(ctx); err != nil { + return err + } + + if !flagA { + return nil + } + if flagA && flagT { + if err := tasks.HandleConsoleResize(ctx, task, con); err != nil { + logrus.WithError(err).Error("console resize") + } + } + + sigc := commands.ForwardAllSignals(ctx, task) + defer commands.StopCatch(sigc) + status := <-statusC + code, _, err := status.Result() + if err != nil { + return err + } + if code != 0 { + return errutil.NewExitCoderErr(int(code)) + } + return nil +} + // Stop stops `container` by sending SIGTERM. If the container is not stopped after `timeout`, it sends a SIGKILL. func Stop(ctx context.Context, container containerd.Container, timeout *time.Duration) error { if err := UpdateExplicitlyStoppedLabel(ctx, container, true); err != nil {