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 }