github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/tests/rkt_attach_test.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 // +build !fly,!kvm 16 17 package main 18 19 import ( 20 "fmt" 21 "os" 22 "path/filepath" 23 "strings" 24 "testing" 25 "time" 26 27 "github.com/coreos/gexpect" 28 "github.com/rkt/rkt/tests/testutils" 29 ) 30 31 // TestAttachSmoke is a smoke test for rkt-attach. It exercises several 32 // features: tty/streams mux, auto-attach, interactive I/O, sandbox support. 33 func TestAttachSmoke(t *testing.T) { 34 actionTimeout := 30 * time.Second 35 ctx := testutils.NewRktRunCtx() 36 defer ctx.Cleanup() 37 38 imageName := "coreos.com/rkt-inspect/test-attach" 39 appName := "test-attach" 40 aciPatchArgs := []string{"--name=" + imageName, "--exec=/inspect --read-stdin --check-tty"} 41 aciFileName := patchTestACI("rkt-inspect-attach-tty.aci", aciPatchArgs...) 42 combinedOutput(t, ctx.ExecCmd("fetch", "--insecure-options=image", aciFileName)) 43 defer os.Remove(aciFileName) 44 45 var tests = []struct { 46 testName string 47 rktRunArgs []string 48 expect string 49 sandbox bool 50 }{ 51 { 52 `Check tty with terminal (positive test) - immutable pod`, 53 []string{`--stdin=tty`, `--stdout=tty`, `--stderr=tty`}, 54 `stdin is a terminal`, 55 false, 56 }, 57 { 58 `Check tty without terminal (negative test) - immutable pod`, 59 []string{`--stdin=stream`, `--stdout=stream`, `--stderr=stream`}, 60 `stdin is not a terminal`, 61 false, 62 }, 63 { 64 `Check tty with terminal (positive test) - sandbox`, 65 []string{`--stdin=tty`, `--stdout=tty`, `--stderr=tty`}, 66 `stdin is a terminal`, 67 true, 68 }, 69 { 70 `Check tty without terminal (negative test) - sandbox`, 71 []string{`--stdin=stream`, `--stdout=stream`, `--stderr=stream`}, 72 `stdin is not a terminal`, 73 true, 74 }, 75 } 76 77 for i, tt := range tests { 78 t.Logf("Running test #%v: %v", i, tt.testName) 79 80 tmpDir := mustTempDir("rkt-test-attach-") 81 uuidFile := filepath.Join(tmpDir, "uuid") 82 defer os.RemoveAll(tmpDir) 83 84 err := os.Setenv("RKT_EXPERIMENT_ATTACH", "true") 85 if err != nil { 86 panic(err) 87 } 88 defer os.Unsetenv("RKT_EXPERIMENT_ATTACH") 89 90 var uuid string 91 var podProc *gexpect.ExpectSubprocess 92 if tt.sandbox { 93 err := os.Setenv("RKT_EXPERIMENT_APP", "true") 94 if err != nil { 95 panic(err) 96 } 97 defer os.Unsetenv("RKT_EXPERIMENT_APP") 98 99 rkt := ctx.Cmd() + " app sandbox --uuid-file-save=" + uuidFile 100 podProc = spawnOrFail(t, rkt) 101 102 // wait for the sandbox to start 103 uuid, err = waitPodReady(ctx, t, uuidFile, 30*time.Second) 104 if err != nil { 105 t.Fatal(err) 106 } 107 addArgs := []string{"app", "add", "--debug", uuid, imageName, "--name=" + appName} 108 combinedOutput(t, ctx.ExecCmd(append(addArgs, tt.rktRunArgs...)...)) 109 combinedOutput(t, ctx.ExecCmd("app", "start", "--debug", uuid, "--app="+appName)) 110 } else { 111 // app starts and blocks, waiting for input 112 rktRunCmd := fmt.Sprintf("%s --insecure-options=image run --uuid-file-save=%s %s %s", ctx.Cmd(), uuidFile, aciFileName, strings.Join(tt.rktRunArgs, " ")) 113 podProc = spawnOrFail(t, rktRunCmd) 114 uuid, err = waitPodReady(ctx, t, uuidFile, actionTimeout) 115 if err != nil { 116 t.Fatal(err) 117 } 118 } 119 120 // wait for the app to become attachable 121 if err := waitAppAttachable(ctx, t, uuid, appName, 30*time.Second); err != nil { 122 t.Fatalf("Failed to wait for attachable app #%v: %v", i, err) 123 } 124 125 // attach and unblock app by sending some input 126 rktAttachCmd := fmt.Sprintf("%s attach %s", ctx.Cmd(), uuid) 127 attachProc := spawnOrFail(t, rktAttachCmd) 128 input := "some_input" 129 if err := attachProc.SendLine(input); err != nil { 130 t.Fatalf("Failed to send %q on the prompt #%v: %v", input, i, err) 131 } 132 feedback := fmt.Sprintf("Received text: %s", input) 133 if err := expectTimeoutWithOutput(attachProc, feedback, actionTimeout); err != nil { 134 t.Fatalf("Waited for the prompt but not found #%v: %v", i, err) 135 } 136 137 // check test result 138 if err := expectTimeoutWithOutput(attachProc, tt.expect, actionTimeout); err != nil { 139 t.Fatalf("Expected %q but not found #%v: %v", tt.expect, i, err) 140 } 141 if err := attachProc.Close(); err != nil { 142 t.Fatalf("Detach #%v failed: %v", i, err) 143 } 144 145 combinedOutput(t, ctx.ExecCmd("stop", uuid)) 146 147 waitOrFail(t, podProc, 0) 148 } 149 } 150 151 func TestAttachStartStop(t *testing.T) { 152 testSandbox(t, func(ctx *testutils.RktRunCtx, child *gexpect.ExpectSubprocess, podUUID string) { 153 appName := "attach-start-stop" 154 // total retry timeout: 10s 155 r := retry{ 156 n: 20, 157 t: 500 * time.Millisecond, 158 } 159 160 assertStatus := func(name, status string) error { 161 return r.Retry(func() error { 162 got := combinedOutput(t, ctx.ExecCmd("app", "status", podUUID, "--app="+name)) 163 164 if !strings.Contains(got, status) { 165 return fmt.Errorf("unexpected result, got %q", got) 166 } 167 168 return nil 169 }) 170 } 171 172 aci := patchTestACI( 173 "rkt-inspect-attach-start-stop.aci", 174 "--name=coreos.com/rkt-inspect/attach-start-stop", 175 "--exec=/inspect -read-stdin -sleep 30", 176 ) 177 defer os.Remove(aci) 178 179 // fetch app 180 combinedOutput(t, ctx.ExecCmd("fetch", "--insecure-options=image", aci)) 181 182 // add app 183 combinedOutput(t, ctx.ExecCmd( 184 "app", "add", podUUID, 185 "coreos.com/rkt-inspect/attach-start-stop", 186 "--name="+appName, 187 "--stdin=stream", "--stdout=stream", "--stderr=stream", 188 )) 189 190 // start app 191 combinedOutput(t, ctx.ExecCmd("app", "start", "--debug", podUUID, "--app="+appName)) 192 if err := assertStatus("attach-start-stop", "running"); err != nil { 193 t.Error(err) 194 return 195 } 196 197 // wait for the app to become attachable 198 if err := waitAppAttachable(ctx, t, podUUID, appName, 30*time.Second); err != nil { 199 t.Fatalf("Failed to wait for attachable app: %v", err) 200 } 201 202 // attach and unblock app by sending some input 203 rktAttachCmd := fmt.Sprintf("%s attach %s", ctx.Cmd(), podUUID) 204 attachProc := spawnOrFail(t, rktAttachCmd) 205 input := "some_input" 206 if err := attachProc.SendLine(input); err != nil { 207 t.Errorf("Failed to send %q on the prompt: %v", input, err) 208 return 209 } 210 211 if err := expectTimeoutWithOutput(attachProc, input, 30*time.Second); err != nil { 212 t.Errorf("Waited for feedback %q but not found: %v", input, err) 213 return 214 } 215 216 // stop after entering input 217 combinedOutput(t, ctx.ExecCmd("app", "stop", "--debug", podUUID, "--app="+appName)) 218 if err := assertStatus("attach-start-stop", "exited"); err != nil { 219 t.Error(err) 220 return 221 } 222 223 // restart app 224 combinedOutput(t, ctx.ExecCmd("app", "start", "--debug", podUUID, "--app="+appName)) 225 if err := assertStatus("attach-start-stop", "running"); err != nil { 226 t.Error(err) 227 return 228 } 229 230 // stop without entering input 231 combinedOutput(t, ctx.ExecCmd("app", "stop", "--debug", podUUID, "--app="+appName)) 232 if err := assertStatus("attach-start-stop", "exited"); err != nil { 233 t.Error(err) 234 return 235 } 236 }) 237 }