Example #1
0
func TestReattach(t *testing.T) {
	_, mocker := testSetup(t)
	defer testTeardown(t, mocker)

	testServer, _ := server.(*testAttachServer)

	cfg := executor.ExecutorConfig{
		Common: executor.Common{
			ID:   "attach",
			Name: "tether_test_executor",
		},

		Sessions: map[string]*executor.SessionConfig{
			"attach": &executor.SessionConfig{
				Common: executor.Common{
					ID:   "attach",
					Name: "tether_test_session",
				},
				Tty:      false,
				Attach:   true,
				RunBlock: true,
				Cmd: executor.Cmd{
					Path: "/usr/bin/tee",
					// grep, matching everything, reading from stdin
					Args: []string{"/usr/bin/tee", pathPrefix + "/tee.out"},
					Env:  []string{},
					Dir:  "/",
				},
			},
		},
		Key: genKey(),
	}

	_, _, conn := StartAttachTether(t, &cfg, mocker)
	defer conn.Close()

	// wait for updates to occur
	<-testServer.updated

	if !testServer.enabled {
		t.Errorf("attach server was not enabled")
	}

	containerConfig := &ssh.ClientConfig{
		User: "******",
		HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
			return nil
		},
	}

	// create the SSH client from the mocked connection
	sshConn, chans, reqs, err := ssh.NewClientConn(conn, "notappliable", containerConfig)
	assert.NoError(t, err)
	defer sshConn.Close()

	var sshSession attach.SessionInteraction
	done := make(chan bool)
	buf := &bytes.Buffer{}
	testBytes := []byte("\x1b[32mhello world\x1b[39m!\n")

	attachFunc := func() {
		attachClient := ssh.NewClient(sshConn, chans, reqs)
		if attachClient == nil {
			t.Errorf("Failed to get ssh.NewClient")
		}

		_, err = attach.SSHls(attachClient)
		assert.NoError(t, err)

		sshSession, err = attach.SSHAttach(attachClient, cfg.ID)
		assert.NoError(t, err)

		stdout := sshSession.Stdout()

		// read from session into buffer
		go func() {
			io.CopyN(buf, stdout, int64(len(testBytes)))
			done <- true
		}()

		// write something to echo
		log.Debug("sending test data")
		sshSession.Stdin().Write(testBytes)
		log.Debug("sent test data")
	}

	limit := 100
	for i := 0; i <= limit; i++ {
		if i > 0 {
			// truncate the buffer for the retach
			buf.Reset()
			testBytes = []byte(fmt.Sprintf("\x1b[32mhello world - again %dth time \x1b[39m!\n", i))
		}

		// attach
		attachFunc()

		// wait for the close to propagate
		<-done

		// send close-stdin if this is the last iteration
		if i == limit {
			// exit
			sshSession.CloseStdin()
		} else {
			// detach
			sshSession.Stdin().Close()
		}
		assert.Equal(t, buf.Bytes(), testBytes)
	}
}
Example #2
0
/////////////////////////////////////////////////////////////////////////////////////
// TestAttachInvalid sets up the config for attach testing - launches a process but
// tries to attach to an invalid session id
//
func TestAttachInvalid(t *testing.T) {
	_, mocker := testSetup(t)
	defer testTeardown(t, mocker)

	testServer, _ := server.(*testAttachServer)

	cfg := executor.ExecutorConfig{
		Common: executor.Common{
			ID:   "attachinvalid",
			Name: "tether_test_executor",
		},

		Sessions: map[string]*executor.SessionConfig{
			"valid": &executor.SessionConfig{
				Common: executor.Common{
					ID:   "valid",
					Name: "tether_test_session",
				},
				Tty:    false,
				Attach: true,
				Cmd: executor.Cmd{
					Path: "/usr/bin/tee",
					// grep, matching everything, reading from stdin
					Args: []string{"/usr/bin/tee", pathPrefix + "/tee.out"},
					Env:  []string{},
					Dir:  "/",
				},
			},
		},
		Key: genKey(),
	}

	tthr, _, conn := StartAttachTether(t, &cfg, mocker)
	defer conn.Close()

	// wait for updates to occur
	<-testServer.updated

	if !testServer.enabled {
		t.Errorf("attach server was not enabled")
	}

	cconfig := &ssh.ClientConfig{
		User: "******",
		HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
			return nil
		},
	}

	// create the SSH client
	sConn, chans, reqs, err := ssh.NewClientConn(conn, "notappliable", cconfig)
	assert.NoError(t, err)
	defer sConn.Close()

	client := ssh.NewClient(sConn, chans, reqs)

	_, err = attach.SSHAttach(client, "invalid")
	tthr.Stop()
	if err == nil {
		t.Errorf("Expected to fail on attempt to attach to invalid session")
	}
}
Example #3
0
/////////////////////////////////////////////////////////////////////////////////////
// TestAttachTTYConfig sets up the config for attach testing
//
func TestAttachTTY(t *testing.T) {
	t.Skip("TTY test skipped - not sure how to test this correctly")

	_, mocker := testSetup(t)
	defer testTeardown(t, mocker)

	testServer, _ := server.(*testAttachServer)

	cfg := executor.ExecutorConfig{
		Common: executor.Common{
			ID:   "attach",
			Name: "tether_test_executor",
		},

		Sessions: map[string]*executor.SessionConfig{
			"attach": &executor.SessionConfig{
				Common: executor.Common{
					ID:   "attach",
					Name: "tether_test_session",
				},
				Tty:    true,
				Attach: true,
				Cmd: executor.Cmd{
					Path: "/usr/bin/tee",
					// grep, matching everything, reading from stdin
					Args: []string{"/usr/bin/tee", pathPrefix + "/tee.out"},
					Env:  []string{},
					Dir:  "/",
				},
			},
		},
		Key: genKey(),
	}

	_, _, conn := StartAttachTether(t, &cfg, mocker)
	defer conn.Close()

	// wait for updates to occur
	<-testServer.updated

	if !testServer.enabled {
		t.Errorf("attach server was not enabled")
	}

	cconfig := &ssh.ClientConfig{
		User: "******",
		HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
			return nil
		},
	}

	// create the SSH client
	sConn, chans, reqs, err := ssh.NewClientConn(conn, "notappliable", cconfig)
	assert.NoError(t, err)

	defer sConn.Close()
	client := ssh.NewClient(sConn, chans, reqs)

	session, err := attach.SSHAttach(client, cfg.ID)
	assert.NoError(t, err)

	stdout := session.Stdout()

	// FIXME: this is line buffered - how do I disable that so we don't have odd hangs to diagnose
	// when the trailing \n is missed
	testBytes := []byte("\x1b[32mhello world\x1b[39m!\n")
	// after tty translation the above string should result in the following
	refBytes := []byte("\x5e[[32mhello world\x5e[[39m!\n")

	// read from session into buffer
	buf := &bytes.Buffer{}

	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		io.CopyN(buf, stdout, int64(len(refBytes)))
		wg.Done()
	}()

	// write something to echo
	log.Debug("sending test data")
	session.Stdin().Write(testBytes)
	log.Debug("sent test data")

	// wait for the close to propagate
	wg.Wait()
	session.CloseStdin()

	assert.Equal(t, refBytes, buf.Bytes())
}
Example #4
0
/////////////////////////////////////////////////////////////////////////////////////
// TestAttachMultiple sets up the config for attach testing - tests launching and
// attaching to multiple processes simultaneously
//
func TestAttachMultiple(t *testing.T) {
	_, mocker := testSetup(t)
	defer testTeardown(t, mocker)

	testServer, _ := server.(*testAttachServer)

	cfg := executor.ExecutorConfig{
		Common: executor.Common{
			ID:   "tee1",
			Name: "tether_test_executor",
		},

		Sessions: map[string]*executor.SessionConfig{
			"tee1": &executor.SessionConfig{
				Common: executor.Common{
					ID:   "tee1",
					Name: "tether_test_session",
				},
				Tty:    false,
				Attach: true,
				Cmd: executor.Cmd{
					Path: "/usr/bin/tee",
					// grep, matching everything, reading from stdin
					Args: []string{"/usr/bin/tee", pathPrefix + "/tee1.out"},
					Env:  []string{},
					Dir:  "/",
				},
			},
			"tee2": &executor.SessionConfig{
				Common: executor.Common{
					ID:   "tee2",
					Name: "tether_test_session2",
				},
				Tty:    false,
				Attach: true,
				Cmd: executor.Cmd{
					Path: "/usr/bin/tee",
					// grep, matching everything, reading from stdin
					Args: []string{"/usr/bin/tee", pathPrefix + "/tee2.out"},
					Env:  []string{},
					Dir:  "/",
				},
			},
			"tee3": &executor.SessionConfig{
				Common: executor.Common{
					ID:   "tee3",
					Name: "tether_test_session2",
				},
				Tty:    false,
				Attach: false,
				Cmd: executor.Cmd{
					Path: "/usr/bin/tee",
					// grep, matching everything, reading from stdin
					Args: []string{"/usr/bin/tee", pathPrefix + "/tee3.out"},
					Env:  []string{},
					Dir:  "/",
				},
			},
		},
		Key: genKey(),
		Diagnostics: executor.Diagnostics{
			DebugLevel: 2,
		},
	}

	_, _, conn := StartAttachTether(t, &cfg, mocker)
	defer conn.Close()

	// wait for updates to occur
	<-mocker.Started
	<-testServer.updated

	if !testServer.enabled {
		t.Errorf("attach server was not enabled")
	}

	cconfig := &ssh.ClientConfig{
		User: "******",
		HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
			return nil
		},
	}

	// create the SSH client
	sConn, chans, reqs, err := ssh.NewClientConn(conn, "notappliable", cconfig)
	assert.NoError(t, err)

	defer sConn.Close()
	client := ssh.NewClient(sConn, chans, reqs)

	ids, err := attach.SSHls(client)
	assert.NoError(t, err)

	// there's no ordering guarantee in the returned ids
	if len(ids) != len(cfg.Sessions) {
		t.Errorf("ID list - expected %d, got %d", len(cfg.Sessions), len(ids))
	}

	// check the ids we got correspond to those in the config
	for _, id := range ids {
		if _, ok := cfg.Sessions[id]; !ok {
			t.Errorf("Expected sessions to have an entry for %s", id)
		}
	}

	sessionA, err := attach.SSHAttach(client, "tee1")
	assert.NoError(t, err)

	sessionB, err := attach.SSHAttach(client, "tee2")
	assert.NoError(t, err)

	stdoutA := sessionA.Stdout()
	stdoutB := sessionB.Stdout()

	// FIXME: this is line buffered - how do I disable that so we don't have odd hangs to diagnose
	// when the trailing \n is missed
	testBytesA := []byte("hello world!\n")
	testBytesB := []byte("goodbye world!\n")
	// read from session into buffer
	bufA := &bytes.Buffer{}
	bufB := &bytes.Buffer{}

	var wg sync.WaitGroup
	// wg.Add cannot go inside the go routines as the Add may not have happened by the time we call Wait
	wg.Add(2)
	go func() {
		io.CopyN(bufA, stdoutA, int64(len(testBytesA)))
		wg.Done()
	}()
	go func() {
		io.CopyN(bufB, stdoutB, int64(len(testBytesB)))
		wg.Done()
	}()

	// write something to echo
	log.Debug("sending test data")
	sessionA.Stdin().Write(testBytesA)
	sessionB.Stdin().Write(testBytesB)
	log.Debug("sent test data")

	// wait for the close to propagate
	wg.Wait()

	sessionA.CloseStdin()
	sessionB.CloseStdin()

	<-mocker.Cleaned

	assert.Equal(t, bufA.Bytes(), testBytesA)
	assert.Equal(t, bufB.Bytes(), testBytesB)
}
Example #5
0
func attachCase(t *testing.T, runblock bool) {
	_, mocker := testSetup(t)
	defer testTeardown(t, mocker)

	testServer, _ := server.(*testAttachServer)

	cfg := executor.ExecutorConfig{
		Common: executor.Common{
			ID:   "attach",
			Name: "tether_test_executor",
		},

		Sessions: map[string]*executor.SessionConfig{
			"attach": {
				Common: executor.Common{
					ID:   "attach",
					Name: "tether_test_session",
				},
				Tty:      false,
				Attach:   true,
				RunBlock: runblock,
				Cmd: executor.Cmd{
					Path: "/usr/bin/tee",
					// grep, matching everything, reading from stdin
					Args: []string{"/usr/bin/tee", pathPrefix + "/tee.out"},
					Env:  []string{},
					Dir:  "/",
				},
			},
		},
		Key: genKey(),
	}

	_, _, conn := StartAttachTether(t, &cfg, mocker)
	defer conn.Close()

	// wait for updates to occur
	<-testServer.updated

	if !testServer.enabled {
		t.Errorf("attach server was not enabled")
	}

	containerConfig := &ssh.ClientConfig{
		User: "******",
		HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
			return nil
		},
	}

	// create the SSH client from the mocked connection
	sshConn, chans, reqs, err := ssh.NewClientConn(conn, "notappliable", containerConfig)
	assert.NoError(t, err)
	defer sshConn.Close()

	attachClient := ssh.NewClient(sshConn, chans, reqs)

	if runblock {
		_, err = attach.SSHls(attachClient)
		assert.NoError(t, err)
	}
	sshSession, err := attach.SSHAttach(attachClient, cfg.ID)
	assert.NoError(t, err)

	stdout := sshSession.Stdout()

	// FIXME: the pipe pair are line buffered - how do I disable that so we
	// don't have odd hangs to diagnose when the trailing \n is missed

	testBytes := []byte("\x1b[32mhello world\x1b[39m!\n")
	// read from session into buffer
	buf := &bytes.Buffer{}
	done := make(chan bool)
	go func() { io.CopyN(buf, stdout, int64(len(testBytes))); done <- true }()

	// write something to echo
	log.Debug("sending test data")
	sshSession.Stdin().Write(testBytes)
	log.Debug("sent test data")

	// wait for the close to propagate
	<-done
	sshSession.CloseStdin()
}