github.com/containerd/nerdctl@v1.7.7/pkg/composer/exec.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 composer
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"os"
    23  	"sort"
    24  	"strconv"
    25  	"strings"
    26  
    27  	"github.com/containerd/containerd"
    28  	"github.com/containerd/log"
    29  	"github.com/containerd/nerdctl/pkg/composer/serviceparser"
    30  	"github.com/containerd/nerdctl/pkg/labels"
    31  )
    32  
    33  // ExecOptions stores options passed from users as flags and args.
    34  type ExecOptions struct {
    35  	ServiceName string
    36  	Index       int
    37  	// params to be passed to `nerdctl exec`
    38  	Detach      bool
    39  	Interactive bool
    40  	Tty         bool
    41  	Privileged  bool
    42  	User        string
    43  	WorkDir     string
    44  	Env         []string
    45  	Args        []string
    46  }
    47  
    48  // Exec executes a given command on a running container specified by
    49  // `ServiceName` (and `Index` if it has multiple instances).
    50  func (c *Composer) Exec(ctx context.Context, eo ExecOptions) error {
    51  	containers, err := c.Containers(ctx, eo.ServiceName)
    52  	if err != nil {
    53  		return fmt.Errorf("fail to get containers for service %s: %s", eo.ServiceName, err)
    54  	}
    55  	if len(containers) == 0 {
    56  		return fmt.Errorf("no running containers from service %s", eo.ServiceName)
    57  	}
    58  	if eo.Index > len(containers) {
    59  		return fmt.Errorf("index (%d) out of range: only %d running instances from service %s",
    60  			eo.Index, len(containers), eo.ServiceName)
    61  	}
    62  	if len(containers) == 1 {
    63  		return c.exec(ctx, containers[0], eo)
    64  	}
    65  	// The order of the containers is not consistently ascending
    66  	// we need to re-sort them.
    67  	sort.SliceStable(containers, func(i, j int) bool {
    68  		infoI, _ := containers[i].Info(ctx, containerd.WithoutRefreshedMetadata)
    69  		infoJ, _ := containers[j].Info(ctx, containerd.WithoutRefreshedMetadata)
    70  		segsI := strings.Split(infoI.Labels[labels.Name], serviceparser.Separator)
    71  		segsJ := strings.Split(infoJ.Labels[labels.Name], serviceparser.Separator)
    72  		indexI, _ := strconv.Atoi(segsI[len(segsI)-1])
    73  		indexJ, _ := strconv.Atoi(segsJ[len(segsJ)-1])
    74  		return indexI < indexJ
    75  	})
    76  	return c.exec(ctx, containers[eo.Index-1], eo)
    77  }
    78  
    79  // exec constructs/executes the `nerdctl exec` command to be executed on the given container.
    80  func (c *Composer) exec(ctx context.Context, container containerd.Container, eo ExecOptions) error {
    81  	args := []string{
    82  		"exec",
    83  		fmt.Sprintf("--detach=%t", eo.Detach),
    84  		fmt.Sprintf("--interactive=%t", eo.Interactive),
    85  		fmt.Sprintf("--tty=%t", eo.Tty),
    86  		fmt.Sprintf("--privileged=%t", eo.Privileged),
    87  	}
    88  	if eo.User != "" {
    89  		args = append(args, "--user", eo.User)
    90  	}
    91  	if eo.WorkDir != "" {
    92  		args = append(args, "--workdir", eo.WorkDir)
    93  	}
    94  	for _, e := range eo.Env {
    95  		args = append(args, "--env", e)
    96  	}
    97  	args = append(args, container.ID())
    98  	args = append(args, eo.Args...)
    99  	cmd := c.createNerdctlCmd(ctx, args...)
   100  
   101  	if eo.Interactive {
   102  		cmd.Stdin = os.Stdin
   103  	}
   104  	if !eo.Detach {
   105  		cmd.Stdout = os.Stdout
   106  		cmd.Stderr = os.Stderr
   107  	}
   108  
   109  	if c.DebugPrintFull {
   110  		log.G(ctx).Debugf("Executing %v", cmd.Args)
   111  	}
   112  	return cmd.Run()
   113  }