github.com/coreos/rocket@v1.30.1-0.20200224141603-171c416fac02/rkt/attach.go (about) 1 // Copyright 2016 The rkt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package main 16 17 import ( 18 "fmt" 19 "strings" 20 21 "github.com/rkt/rkt/common" 22 pkgPod "github.com/rkt/rkt/pkg/pod" 23 "github.com/rkt/rkt/stage0" 24 "github.com/rkt/rkt/store/imagestore" 25 "github.com/rkt/rkt/store/treestore" 26 "github.com/spf13/cobra" 27 ) 28 29 var ( 30 cmdAttach = &cobra.Command{ 31 Use: "attach [--app=APPNAME] [--mode=MODE] UUID", 32 Short: "Attach to an app running within a rkt pod", 33 34 Long: `UUID should be the UUID of a running pod.`, 35 Run: ensureSuperuser(runWrapper(runAttach)), 36 } 37 flagAttachMode string 38 ) 39 40 func init() { 41 if common.IsExperimentEnabled("attach") { 42 cmdRkt.AddCommand(cmdAttach) 43 cmdAttach.Flags().StringVar(&flagAppName, "app", "", "name of the app to enter within the specified pod") 44 cmdAttach.Flags().StringVar(&flagAttachMode, "mode", "auto", "attach mode") 45 } 46 } 47 48 func runAttach(cmd *cobra.Command, args []string) (exit int) { 49 if len(args) < 1 { 50 cmd.Usage() 51 return 254 52 } 53 54 uuid := args[0] 55 p, err := pkgPod.PodFromUUIDString(getDataDir(), uuid) 56 if err != nil { 57 stderr.PrintE("problem retrieving pod", err) 58 return 254 59 } 60 defer p.Close() 61 62 if p.State() != pkgPod.Running { 63 stderr.Printf("pod %q isn't currently running", p.UUID) 64 return 254 65 } 66 67 podPID, err := p.ContainerPid1() 68 if err != nil { 69 stderr.PrintE(fmt.Sprintf("unable to determine the pid for pod %q", p.UUID), err) 70 return 254 71 } 72 73 appName, err := getAppName(p) 74 if err != nil { 75 stderr.PrintE("unable to determine app name", err) 76 return 254 77 } 78 79 s, err := imagestore.NewStore(storeDir()) 80 if err != nil { 81 stderr.PrintE("cannot open store", err) 82 return 254 83 } 84 85 ts, err := treestore.NewStore(treeStoreDir(), s) 86 if err != nil { 87 stderr.PrintE("cannot open store", err) 88 return 254 89 } 90 91 stage1TreeStoreID, err := p.GetStage1TreeStoreID() 92 if err != nil { 93 stderr.PrintE("error getting stage1 treeStoreID", err) 94 return 254 95 } 96 97 // prepare stage1/attach flags 98 stage1RootFS := ts.GetRootFS(stage1TreeStoreID) 99 attachArgs, err := createStage1AttachFlags(flagAttachMode) 100 if err != nil { 101 stderr.PrintE("invalid attach mode", err) 102 return 254 103 } 104 attachArgs = append(attachArgs, 105 fmt.Sprintf("--app=%s", appName), 106 fmt.Sprintf("--debug=%t", globalFlags.Debug), 107 ) 108 109 if err = stage0.Attach(p.Path(), podPID, *appName, stage1RootFS, uuid, attachArgs); err != nil { 110 stderr.PrintE("attach failed", err) 111 return 254 112 } 113 // not reached when stage0.Attach execs /enter 114 return 0 115 } 116 117 // createStage1AttachFlags parses an attach stage0 CLI "--mode" flag and 118 // returns options suited for stage1/attach entrypoint invocation 119 func createStage1AttachFlags(attachMode string) ([]string, error) { 120 attachArgs := []string{} 121 122 // list mode: just print endpoints 123 if attachMode == "list" { 124 attachArgs = append(attachArgs, "--action=list") 125 return attachArgs, nil 126 } 127 128 // auto-attach mode: stage1-attach will figure out endpoints 129 if attachMode == "auto" || attachMode == "" { 130 attachArgs = append(attachArgs, "--action=auto-attach") 131 return attachArgs, nil 132 } 133 134 // custom-attach mode: user specified endpoints 135 var customEndpoints struct { 136 TTYIn bool 137 TTYOut bool 138 Stdin bool 139 Stdout bool 140 Stderr bool 141 } 142 attachArgs = append(attachArgs, "--action=custom-attach") 143 144 // parse comma-separated endpoints for custom attach 145 eps := strings.Split(attachMode, ",") 146 for _, e := range eps { 147 switch e { 148 case "stdin": 149 customEndpoints.Stdin = true 150 case "stdout": 151 customEndpoints.Stdout = true 152 case "stderr": 153 customEndpoints.Stderr = true 154 case "tty": 155 customEndpoints.TTYIn = true 156 customEndpoints.TTYOut = true 157 case "tty-in": 158 customEndpoints.TTYIn = true 159 case "tty-out": 160 customEndpoints.TTYOut = true 161 default: 162 return nil, fmt.Errorf("unknown endpoint %q", e) 163 } 164 } 165 166 // check that the resulting attach mode is sane 167 if !(customEndpoints.TTYIn || customEndpoints.TTYOut || customEndpoints.Stdin || customEndpoints.Stdout || customEndpoints.Stderr) { 168 return nil, fmt.Errorf("mode must specify at least one endpoint to attach") 169 } 170 if (customEndpoints.TTYIn || customEndpoints.TTYOut) && (customEndpoints.Stdin || customEndpoints.Stdout || customEndpoints.Stderr) { 171 return nil, fmt.Errorf("incompatible endpoints %q, cannot simultaneously attach TTY and streams", attachMode) 172 } 173 174 attachArgs = append(attachArgs, 175 fmt.Sprintf("--tty-in=%t", customEndpoints.TTYIn), 176 fmt.Sprintf("--tty-out=%t", customEndpoints.TTYOut), 177 fmt.Sprintf("--stdin=%t", customEndpoints.Stdin), 178 fmt.Sprintf("--stdout=%t", customEndpoints.Stdout), 179 fmt.Sprintf("--stderr=%t", customEndpoints.Stderr), 180 ) 181 return attachArgs, nil 182 183 }