github.com/containerd/nerdctl@v1.7.7/cmd/nerdctl/compose_start.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"os"
    23  
    24  	"github.com/containerd/containerd"
    25  	"github.com/containerd/errdefs"
    26  	"github.com/containerd/nerdctl/pkg/clientutil"
    27  	"github.com/containerd/nerdctl/pkg/cmd/compose"
    28  	"github.com/containerd/nerdctl/pkg/containerutil"
    29  	"github.com/containerd/nerdctl/pkg/labels"
    30  	"github.com/spf13/cobra"
    31  	"golang.org/x/sync/errgroup"
    32  )
    33  
    34  func newComposeStartCommand() *cobra.Command {
    35  	var composeRestartCommand = &cobra.Command{
    36  		Use:                   "start [SERVICE...]",
    37  		Short:                 "Start existing containers for service(s)",
    38  		RunE:                  composeStartAction,
    39  		SilenceUsage:          true,
    40  		SilenceErrors:         true,
    41  		DisableFlagsInUseLine: true,
    42  	}
    43  	return composeRestartCommand
    44  }
    45  
    46  func composeStartAction(cmd *cobra.Command, args []string) error {
    47  	globalOptions, err := processRootCmdFlags(cmd)
    48  	if err != nil {
    49  		return err
    50  	}
    51  
    52  	client, ctx, cancel, err := clientutil.NewClient(cmd.Context(), globalOptions.Namespace, globalOptions.Address)
    53  	if err != nil {
    54  		return err
    55  	}
    56  	defer cancel()
    57  	options, err := getComposeOptions(cmd, globalOptions.DebugFull, globalOptions.Experimental)
    58  	if err != nil {
    59  		return err
    60  	}
    61  	c, err := compose.New(client, globalOptions, options, cmd.OutOrStdout(), cmd.ErrOrStderr())
    62  	if err != nil {
    63  		return err
    64  	}
    65  
    66  	// TODO(djdongjin): move to `pkg/composer` and rewrite `c.Services + for-loop`
    67  	// with `c.project.WithServices` after refactor (#1639) is done.
    68  	services, err := c.Services(ctx, args...)
    69  	if err != nil {
    70  		return err
    71  	}
    72  	for _, svc := range services {
    73  		svcName := svc.Unparsed.Name
    74  		containers, err := c.Containers(ctx, svcName)
    75  		if err != nil {
    76  			return err
    77  		}
    78  		// return error if no containers and service replica is not zero
    79  		if len(containers) == 0 {
    80  			if len(svc.Containers) == 0 {
    81  				continue
    82  			}
    83  			return fmt.Errorf("service %q has no container to start", svcName)
    84  		}
    85  
    86  		if err := startContainers(ctx, client, containers); err != nil {
    87  			return err
    88  		}
    89  	}
    90  
    91  	return nil
    92  }
    93  
    94  func startContainers(ctx context.Context, client *containerd.Client, containers []containerd.Container) error {
    95  	eg, ctx := errgroup.WithContext(ctx)
    96  	for _, c := range containers {
    97  		c := c
    98  		eg.Go(func() error {
    99  			if cStatus, err := containerutil.ContainerStatus(ctx, c); err != nil {
   100  				// NOTE: NotFound doesn't mean that container hasn't started.
   101  				// In docker/CRI-containerd plugin, the task will be deleted
   102  				// when it exits. So, the status will be "created" for this
   103  				// case.
   104  				if !errdefs.IsNotFound(err) {
   105  					return err
   106  				}
   107  			} else if cStatus.Status == containerd.Running {
   108  				return nil
   109  			}
   110  
   111  			// in compose, always disable attach
   112  			if err := containerutil.Start(ctx, c, false, client, ""); err != nil {
   113  				return err
   114  			}
   115  			info, err := c.Info(ctx, containerd.WithoutRefreshedMetadata)
   116  			if err != nil {
   117  				return err
   118  			}
   119  
   120  			_, err = fmt.Fprintf(os.Stdout, "Container %s started\n", info.Labels[labels.Name])
   121  			return err
   122  		})
   123  	}
   124  
   125  	return eg.Wait()
   126  }