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  }