From c01e08044248a394366b18ee6904f1f31ade74ca Mon Sep 17 00:00:00 2001 From: Christian Weichel Date: Fri, 27 Aug 2021 09:36:17 +0000 Subject: [PATCH] [tests] Add integration tests for docker --- test/go.mod | 2 + test/go.sum | 2 + test/pkg/integration/agent.go | 4 +- test/tests/workspace/docker_test.go | 48 +++++++++++++ test/tests/workspace/workspace_agent/main.go | 71 ++++++++++++++++++++ 5 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 test/tests/workspace/docker_test.go diff --git a/test/go.mod b/test/go.mod index d13ab5879c2f27..b5675893bf1ea8 100644 --- a/test/go.mod +++ b/test/go.mod @@ -12,7 +12,9 @@ require ( github.com/gitpod-io/gitpod/ws-manager/api v0.0.0-00010101000000-000000000000 github.com/go-sql-driver/mysql v1.5.0 github.com/google/uuid v1.2.0 + github.com/prometheus/procfs v0.7.3 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c + golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 google.golang.org/grpc v1.39.1 k8s.io/api v0.22.0 diff --git a/test/go.sum b/test/go.sum index 2965c71eac2dd6..6c174329fc2d5b 100644 --- a/test/go.sum +++ b/test/go.sum @@ -650,6 +650,8 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= diff --git a/test/pkg/integration/agent.go b/test/pkg/integration/agent.go index b4243120c056ac..0a3cb4ded1966c 100644 --- a/test/pkg/integration/agent.go +++ b/test/pkg/integration/agent.go @@ -13,13 +13,15 @@ import ( "net/rpc" "os" "os/signal" + "strconv" "syscall" ) // ServeAgent is the main entrypoint for agents. It establishes flags and starts an RPC server // on a port passed as flag. func ServeAgent(rcvr interface{}) { - port := flag.Int("rpc-port", 0, "the port on wich to run the RPC server on") + defaultPort, _ := strconv.Atoi(os.Getenv("AGENT_RPC_PORT")) + port := flag.Int("rpc-port", defaultPort, "the port on wich to run the RPC server on") flag.Parse() ta := &testAgent{ diff --git a/test/tests/workspace/docker_test.go b/test/tests/workspace/docker_test.go new file mode 100644 index 00000000000000..c6d70870cec29d --- /dev/null +++ b/test/tests/workspace/docker_test.go @@ -0,0 +1,48 @@ +// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Licensed under the GNU Affero General Public License (AGPL). +// See License-AGPL.txt in the project root for license information. + +package workspace_test + +import ( + "testing" + "time" + + "github.com/gitpod-io/gitpod/test/pkg/integration" + agent "github.com/gitpod-io/gitpod/test/tests/workspace/workspace_agent/api" +) + +func TestRunDocker(t *testing.T) { + it, _ := integration.NewTest(t, 5*time.Minute) + defer it.Done() + + ws := integration.LaunchWorkspaceDirectly(it) + instanceID := ws.Req.Id + defer integration.DeleteWorkspace(it, ws.Req.Id) + + rsa, err := it.Instrument(integration.ComponentWorkspace, "workspace", integration.WithInstanceID(instanceID), integration.WithWorkspacekitLift(true)) + if err != nil { + t.Error(err) + return + } + defer rsa.Close() + + var resp agent.ExecResponse + err = rsa.Call("WorkspaceAgent.Exec", &agent.ExecRequest{ + Dir: "/", + Command: "bash", + Args: []string{ + "-c", + "docker run --rm alpine:latest", + }, + }, &resp) + if err != nil { + t.Errorf("docker run failed: %v\n%s\n%s", err, resp.Stdout, resp.Stderr) + return + } + + if resp.ExitCode != 0 { + t.Errorf("docker run failed: %s\n%s", resp.Stdout, resp.Stderr) + return + } +} diff --git a/test/tests/workspace/workspace_agent/main.go b/test/tests/workspace/workspace_agent/main.go index 9d30361aa65de7..19fe891a31b335 100644 --- a/test/tests/workspace/workspace_agent/main.go +++ b/test/tests/workspace/workspace_agent/main.go @@ -7,18 +7,88 @@ package main import ( "bytes" "fmt" + "io" "os" "os/exec" + "strconv" "strings" "github.com/gitpod-io/gitpod/test/pkg/integration" "github.com/gitpod-io/gitpod/test/tests/workspace/workspace_agent/api" + "github.com/prometheus/procfs" + "golang.org/x/sys/unix" ) func main() { + err := enterSupervisorNamespaces() + if err != nil { + panic(fmt.Sprintf("enterSupervisorNamespaces: %v", err)) + } + integration.ServeAgent(new(WorkspaceAgent)) } +func enterSupervisorNamespaces() error { + if os.Getenv("AGENT_IN_RING2") != "" { + return nil + } + + nsenter, err := exec.LookPath("nsenter") + if err != nil { + return fmt.Errorf("cannot find nsenter") + } + + // This agent expectes to be called using the workspacekit lift (i.e. in ring1). + // We then enter the PID and mount namespace of supervisor. + // First, we need to find the supervisor process + proc, err := procfs.NewFS("/proc") + if err != nil { + return err + } + procs, err := proc.AllProcs() + if err != nil { + return err + } + var supervisorPID int + for _, p := range procs { + cmd, _ := p.CmdLine() + for _, c := range cmd { + if strings.HasSuffix(c, "supervisor") { + supervisorPID = p.PID + break + } + } + if supervisorPID != 0 { + break + } + } + if supervisorPID == 0 { + return fmt.Errorf("no supervisor process found") + } + + // Then we copy ourselves to a location that we can access from the supervisor NS + self, err := os.Executable() + if err != nil { + return err + } + fn := fmt.Sprintf("/proc/%d/root/agent", supervisorPID) + dst, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0755) + if err != nil { + return err + } + selfFD, err := os.Open(self) + if err != nil { + return err + } + defer selfFD.Close() + _, err = io.Copy(dst, selfFD) + if err != nil { + return fmt.Errorf("error copying agent: %w", err) + } + + return unix.Exec(nsenter, append([]string{nsenter, "-t", strconv.Itoa(supervisorPID), "-m", "-p", "/agent"}, os.Args[1:]...), append(os.Environ(), "AGENT_IN_RING2=true")) +} + // WorkspaceAgent provides ingteration test services from within a workspace type WorkspaceAgent struct { } @@ -70,6 +140,7 @@ func (*WorkspaceAgent) Exec(req *api.ExecRequest, resp *api.ExecResponse) (err e return fmt.Errorf("%s: %w", fullCommand, err) } rc = exitError.ExitCode() + err = nil } *resp = api.ExecResponse{