Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/nerdctl/compose_start.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion cmd/nerdctl/container_restart.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
143 changes: 16 additions & 127 deletions cmd/nerdctl/container_start.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -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) {
Expand Down
9 changes: 9 additions & 0 deletions pkg/api/types/container_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
64 changes: 64 additions & 0 deletions pkg/cmd/container/start.go
Original file line number Diff line number Diff line change
@@ -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
}
89 changes: 89 additions & 0 deletions pkg/containerutil/containerutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down