diff --git a/cmd/nerdctl/stop.go b/cmd/nerdctl/container_stop.go similarity index 58% rename from cmd/nerdctl/stop.go rename to cmd/nerdctl/container_stop.go index d4fd4ffbd3a..effec209924 100644 --- a/cmd/nerdctl/stop.go +++ b/cmd/nerdctl/container_stop.go @@ -17,17 +17,12 @@ package main import ( - "context" - "fmt" "time" - "github.com/containerd/nerdctl/pkg/clientutil" - "github.com/containerd/nerdctl/pkg/containerutil" - "github.com/spf13/cobra" - "github.com/containerd/containerd" - "github.com/containerd/containerd/errdefs" - "github.com/containerd/nerdctl/pkg/idutil/containerwalker" + "github.com/containerd/nerdctl/pkg/api/types" + "github.com/containerd/nerdctl/pkg/cmd/container" + "github.com/spf13/cobra" ) func newStopCommand() *cobra.Command { @@ -40,57 +35,38 @@ func newStopCommand() *cobra.Command { SilenceUsage: true, SilenceErrors: true, } - stopCommand.Flags().IntP("time", "t", 10, "Seconds to wait for stop before killing it") + stopCommand.Flags().IntP("time", "t", 10, "Seconds to wait before sending a SIGKILL") return stopCommand } -func stopAction(cmd *cobra.Command, args []string) error { - // Time to wait after sending a SIGTERM and before sending a SIGKILL. +func processContainerStopOptions(cmd *cobra.Command) (types.ContainerStopOptions, error) { globalOptions, err := processRootCmdFlags(cmd) if err != nil { - return err + return types.ContainerStopOptions{}, err } var timeout *time.Duration if cmd.Flags().Changed("time") { timeValue, err := cmd.Flags().GetInt("time") if err != nil { - return err + return types.ContainerStopOptions{}, err } t := time.Duration(timeValue) * time.Second timeout = &t } - client, ctx, cancel, err := clientutil.NewClient(cmd.Context(), globalOptions.Namespace, globalOptions.Address) + return types.ContainerStopOptions{ + Stdout: cmd.OutOrStdout(), + Stderr: cmd.ErrOrStderr(), + GOptions: globalOptions, + Timeout: timeout, + }, nil +} + +func stopAction(cmd *cobra.Command, args []string) error { + options, err := processContainerStopOptions(cmd) if err != nil { return err } - defer cancel() - - 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 := containerutil.Stop(ctx, found.Container, timeout); err != nil { - if errdefs.IsNotFound(err) { - fmt.Fprintf(cmd.ErrOrStderr(), "No such container: %s\n", found.Req) - return nil - } - return err - } - _, err := fmt.Fprintf(cmd.OutOrStdout(), "%s\n", found.Req) - 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 container.Stop(cmd.Context(), args, options) } func stopShellComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { diff --git a/cmd/nerdctl/stop_linux_test.go b/cmd/nerdctl/container_stop_linux_test.go similarity index 100% rename from cmd/nerdctl/stop_linux_test.go rename to cmd/nerdctl/container_stop_linux_test.go diff --git a/pkg/api/types/container_types.go b/pkg/api/types/container_types.go index 96951c5198a..caf3e4c4210 100644 --- a/pkg/api/types/container_types.go +++ b/pkg/api/types/container_types.go @@ -16,7 +16,10 @@ package types -import "io" +import ( + "io" + "time" +) // KillOptions specifies options for `nerdctl (container) kill`. type KillOptions struct { @@ -28,6 +31,17 @@ type KillOptions struct { KillSignal string } +// ContainerStopOptions specifies options for `nerdctl (container) stop`. +type ContainerStopOptions struct { + Stdout io.Writer + Stderr io.Writer + // GOptions is the global options + GOptions GlobalCommandOptions + // Timeout specifies how long to wait after sending a SIGTERM and before sending a SIGKILL. + // If it's nil, the default is 10 seconds. + Timeout *time.Duration +} + // ContainerRemoveOptions specifies options for `nerdctl (container) rm`. type ContainerRemoveOptions struct { Stdout io.Writer diff --git a/pkg/cmd/container/stop.go b/pkg/cmd/container/stop.go new file mode 100644 index 00000000000..b4ef6ced1b7 --- /dev/null +++ b/pkg/cmd/container/stop.go @@ -0,0 +1,63 @@ +/* + 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/errdefs" + "github.com/containerd/nerdctl/pkg/api/types" + "github.com/containerd/nerdctl/pkg/clientutil" + "github.com/containerd/nerdctl/pkg/containerutil" + "github.com/containerd/nerdctl/pkg/idutil/containerwalker" +) + +func Stop(ctx context.Context, reqs []string, opt types.ContainerStopOptions) error { + client, ctx, cancel, err := clientutil.NewClient(ctx, opt.GOptions.Namespace, opt.GOptions.Address) + if err != nil { + return err + } + defer cancel() + + 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 := containerutil.Stop(ctx, found.Container, opt.Timeout); err != nil { + if errdefs.IsNotFound(err) { + fmt.Fprintf(opt.Stderr, "No such container: %s\n", found.Req) + return nil + } + return err + } + _, err := fmt.Fprintf(opt.Stdout, "%s\n", found.Req) + 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 +}