Path: blob/main/test/tests/components/ws-manager/prebuild_test.go
2500 views
// Copyright (c) 2020 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package wsmanager56import (7"context"8"encoding/json"9"errors"10"fmt"11"path/filepath"12"strings"13"testing"14"time"1516"sigs.k8s.io/e2e-framework/pkg/envconf"17"sigs.k8s.io/e2e-framework/pkg/features"1819csapi "github.com/gitpod-io/gitpod/content-service/api"20gitpod "github.com/gitpod-io/gitpod/gitpod-protocol"21agent "github.com/gitpod-io/gitpod/test/pkg/agent/workspace/api"22"github.com/gitpod-io/gitpod/test/pkg/integration"23wsmanapi "github.com/gitpod-io/gitpod/ws-manager/api"24)2526func TestPrebuildWorkspaceTaskSuccess(t *testing.T) {27f := features.New("prebuild").28WithLabel("component", "ws-manager").29Assess("it should create a prebuild and succeed the defined tasks", func(testCtx context.Context, t *testing.T, cfg *envconf.Config) context.Context {30tests := []struct {31Name string32ContextURL string33WorkspaceRoot string34CheckoutLocation string35Task []gitpod.TasksItems36FF []wsmanapi.WorkspaceFeatureFlag37}{38{39Name: "classic",40ContextURL: "https://github.com/gitpod-io/empty",41CheckoutLocation: "empty",42WorkspaceRoot: "/workspace/empty",43Task: []gitpod.TasksItems{44{Init: "echo \"some output\" > someFile; exit 0;"},45},46},47}48for _, test := range tests {49test := test50t.Run(test.Name, func(t *testing.T) {51t.Parallel()5253ctx, cancel := context.WithTimeout(testCtx, time.Duration(5*len(tests))*time.Minute)54defer cancel()5556api := integration.NewComponentAPI(ctx, cfg.Namespace(), kubeconfig, cfg.Client())57t.Cleanup(func() {58api.Done(t)59})6061// TODO: change to use server API to launch the workspace, so we could run the integration test as the user code flow62// which is client -> server -> ws-manager rather than client -> ws-manager directly63ws, stopWs, err := integration.LaunchWorkspaceDirectly(t, ctx, api, integration.WithRequestModifier(func(req *wsmanapi.StartWorkspaceRequest) error {64req.Type = wsmanapi.WorkspaceType_PREBUILD6566tasks, err := json.Marshal(test.Task)67if err != nil {68return err69}70req.Spec.Envvars = append(req.Spec.Envvars, &wsmanapi.EnvironmentVariable{71Name: "GITPOD_TASKS",72Value: string(tasks),73})7475req.Spec.FeatureFlags = test.FF76req.Spec.Initializer = &csapi.WorkspaceInitializer{77Spec: &csapi.WorkspaceInitializer_Git{78Git: &csapi.GitInitializer{79RemoteUri: test.ContextURL,80CheckoutLocation: test.CheckoutLocation,81Config: &csapi.GitConfig{},82},83},84}85req.Spec.WorkspaceLocation = test.CheckoutLocation86return nil87}), integration.WithWaitWorkspaceForOpts(integration.WaitForStopped))88if err != nil {89t.Fatalf("cannot launch a workspace: %q", err)90}91t.Cleanup(func() {92// stop workspace in defer function to prevent we forget to stop the workspace93if err := stopWorkspace(t, cfg, stopWs); err != nil {94t.Errorf("cannot stop workspace: %q", err)95}96})9798if ws.LastStatus == nil {99t.Fatal("workspace status is nil")100}101if ws.LastStatus.Phase != wsmanapi.WorkspacePhase_STOPPED {102t.Fatalf("unexpected workspace phase: %v", ws.LastStatus.Phase)103}104if ws.LastStatus.Conditions != nil && ws.LastStatus.Conditions.HeadlessTaskFailed != "" {105t.Logf("Conditions: %v", ws.LastStatus.Conditions)106t.Fatalf("unexpected HeadlessTaskFailed condition: %v", ws.LastStatus.Conditions.HeadlessTaskFailed)107}108})109}110return testCtx111}).112Feature()113114testEnv.Test(t, f)115}116117func TestPrebuildWorkspaceTaskFail(t *testing.T) {118f := features.New("prebuild").119WithLabel("component", "server").120Assess("it should create a prebuild and fail after running the defined tasks", func(testCtx context.Context, t *testing.T, cfg *envconf.Config) context.Context {121t.Parallel()122123ctx, cancel := context.WithTimeout(testCtx, 5*time.Minute)124defer cancel()125126api := integration.NewComponentAPI(ctx, cfg.Namespace(), kubeconfig, cfg.Client())127t.Cleanup(func() {128api.Done(t)129})130131ws, stopWs, err := integration.LaunchWorkspaceDirectly(t, ctx, api, integration.WithRequestModifier(func(req *wsmanapi.StartWorkspaceRequest) error {132req.Type = wsmanapi.WorkspaceType_PREBUILD133req.Spec.Envvars = append(req.Spec.Envvars, &wsmanapi.EnvironmentVariable{134Name: "GITPOD_TASKS",135Value: `[{ "init": "echo \"some output\" > someFile; exit 1;" }]`,136})137return nil138}), integration.WithWaitWorkspaceForOpts(integration.WaitForStopped))139if err != nil {140t.Fatalf("cannot start workspace: %q", err)141}142143t.Cleanup(func() {144// stop workspace in defer function to prevent we forget to stop the workspace145if err := stopWorkspace(t, cfg, stopWs); err != nil {146t.Errorf("cannot stop workspace: %q", err)147}148})149150if ws.LastStatus == nil {151t.Fatal("workspace status is nil")152}153if ws.LastStatus.Phase != wsmanapi.WorkspacePhase_STOPPED {154t.Fatalf("unexpected workspace phase: %v", ws.LastStatus.Phase)155}156if ws.LastStatus.Conditions == nil || ws.LastStatus.Conditions.HeadlessTaskFailed == "" {157t.Logf("Status: %v", ws.LastStatus)158t.Fatal("expected HeadlessTaskFailed condition")159}160161return testCtx162}).163Feature()164165testEnv.Test(t, f)166}167168const (169prebuildLogPath string = "/workspace/.gitpod"170prebuildLog string = "'🍊 This task ran as a workspace prebuild'"171initTask string = "echo \"some output\" > someFile;"172regularPrefix string = "ws-"173)174175// TestOpenWorkspaceFromPrebuild176// - create a prebuild177// - stop the prebuild workspace178// - open the regular workspace from prebuild179// - make sure either one of the condition mets180// - the prebuild log message exists181// - the init task message exists182// - the init task generated file exists183//184// - make sure the .git/ folder with correct permission185// - write a new file foobar.txt186// - stop the regular workspace187// - relaunch the regular workspace188// - make sure the file foobar.txt exists189func TestOpenWorkspaceFromPrebuild(t *testing.T) {190f := features.New("prebuild").191WithLabel("component", "ws-manager").192Assess("it should open workspace from prebuild successfully", func(testCtx context.Context, t *testing.T, cfg *envconf.Config) context.Context {193tests := []struct {194Name string195ContextURL string196WorkspaceRoot string197CheckoutLocation string198FF []wsmanapi.WorkspaceFeatureFlag199}{200{201Name: "classic",202ContextURL: "https://github.com/gitpod-io/empty",203CheckoutLocation: "empty",204WorkspaceRoot: "/workspace/empty",205},206}207208for _, test := range tests {209test := test210t.Run(test.Name, func(t *testing.T) {211t.Parallel()212213ctx, cancel := context.WithTimeout(testCtx, time.Duration(10*len(tests))*time.Minute)214defer cancel()215216api := integration.NewComponentAPI(ctx, cfg.Namespace(), kubeconfig, cfg.Client())217t.Cleanup(func() {218api.Done(t)219})220221var prebuildSnapshot string222func() {223// create a prebuild and stop workspace224// TODO: change to use server API to launch the workspace, so we could run the integration test as the user code flow225// which is client -> server -> ws-manager rather than client -> ws-manager directly226ws, prebuildStopWs, err := integration.LaunchWorkspaceDirectly(t, ctx, api,227integration.WithRequestModifier(func(req *wsmanapi.StartWorkspaceRequest) error {228req.Type = wsmanapi.WorkspaceType_PREBUILD229req.Spec.Envvars = append(req.Spec.Envvars, &wsmanapi.EnvironmentVariable{230Name: "GITPOD_TASKS",231Value: fmt.Sprintf(`[{ "init": %q }]`, initTask),232})233req.Spec.FeatureFlags = test.FF234req.Spec.Initializer = &csapi.WorkspaceInitializer{235Spec: &csapi.WorkspaceInitializer_Git{236Git: &csapi.GitInitializer{237RemoteUri: test.ContextURL,238CheckoutLocation: test.CheckoutLocation,239Config: &csapi.GitConfig{},240},241},242}243req.Spec.WorkspaceLocation = test.CheckoutLocation244return nil245}),246// Wait for the prebuild to finish, as this can happen quickly before we247// would have time to observe the workspace to stop and get its status.248integration.WithWaitWorkspaceForOpts(integration.WaitForStopped),249)250if err != nil {251t.Fatalf("cannot launch a workspace: %q", err)252}253defer func() {254if err := stopWorkspace(t, cfg, prebuildStopWs); err != nil {255t.Errorf("cannot stop workspace: %q", err)256}257}()258259prebuildSnapshot, _, err = findSnapshotFromStoppedWs(t, ctx, ws.LastStatus)260if err != nil {261_ = stopWorkspace(t, cfg, prebuildStopWs)262t.Fatalf("stop workspace and find snapshot error: %v", err)263}264265t.Logf("prebuild snapshot: %s", prebuildSnapshot)266267// check the prebuild logs have been uploaded268checkPrebuildLogUploaded(t, ctx, api, ws)269}()270271// launch the workspace from prebuild272// TODO: change to use server API to launch the workspace, so we could run the integration test as the user code flow273// which is client -> server -> ws-manager rather than client -> ws-manager directly274ws, stopWsFunc, err := integration.LaunchWorkspaceDirectly(t, ctx, api, integration.WithRequestModifier(func(req *wsmanapi.StartWorkspaceRequest) error {275req.Spec.FeatureFlags = test.FF276req.Spec.Initializer = &csapi.WorkspaceInitializer{277Spec: &csapi.WorkspaceInitializer_Prebuild{278Prebuild: &csapi.PrebuildInitializer{279Prebuild: &csapi.SnapshotInitializer{280Snapshot: prebuildSnapshot,281FromVolumeSnapshot: false,282},283Git: []*csapi.GitInitializer{284{285RemoteUri: test.ContextURL,286CheckoutLocation: test.CheckoutLocation,287Config: &csapi.GitConfig{},288},289},290},291},292}293294req.Spec.WorkspaceLocation = test.CheckoutLocation295return nil296}))297if err != nil {298t.Fatalf("cannot launch a workspace: %q", err)299}300301t.Cleanup(func() {302// stop workspace in defer function to prevent we forget to stop the workspace303if err := stopWorkspace(t, cfg, stopWsFunc); err != nil {304t.Errorf("cannot stop workspace: %q", err)305}306})307308rsa, closer, err := integration.Instrument(integration.ComponentWorkspace, "workspace", cfg.Namespace(), kubeconfig, cfg.Client(),309integration.WithInstanceID(ws.Req.Id),310)311if err != nil {312t.Fatal(err)313}314t.Cleanup(func() {315rsa.Close()316})317integration.DeferCloser(t, closer)318319// check prebuild log message exists320checkPrebuildLogExist(t, cfg, rsa, ws, test.WorkspaceRoot)321322// check the folder permission is gitpod323checkFolderPermission(t, rsa, "/workspace")324325// check the files/folders permission under .git/ is gitpod326checkGitFolderPermission(t, rsa, test.WorkspaceRoot)327328// write file foobar.txt and stop the workspace329var writeFileResp agent.ExecResponse330err = rsa.Call("WorkspaceAgent.WriteFile", &agent.WriteFileRequest{331Path: fmt.Sprintf("%s/foobar.txt", test.WorkspaceRoot),332Content: []byte("hello world"),333Mode: 0644,334}, &writeFileResp)335if err != nil {336t.Fatalf("cannot write file %s", fmt.Sprintf("%s/foobar.txt", test.WorkspaceRoot))337}338339sctx, scancel := context.WithTimeout(context.Background(), 5*time.Minute)340defer scancel()341342sapi := integration.NewComponentAPI(sctx, cfg.Namespace(), kubeconfig, cfg.Client())343defer sapi.Done(t)344345// stop workspace and wait, we're about to restart it346_, err = stopWsFunc(true, sapi)347if err != nil {348t.Fatal(err)349}350351// reopen the workspace and make sure the file foobar.txt exists352ws1, stopWs1Func, err := integration.LaunchWorkspaceDirectly(t, ctx, sapi, integration.WithRequestModifier(func(req *wsmanapi.StartWorkspaceRequest) error {353req.ServicePrefix = ws.Req.ServicePrefix354req.Metadata.MetaId = ws.Req.Metadata.MetaId355req.Metadata.Owner = ws.Req.Metadata.Owner356req.Spec.FeatureFlags = test.FF357req.Spec.Initializer = &csapi.WorkspaceInitializer{358Spec: &csapi.WorkspaceInitializer_Backup{359Backup: &csapi.FromBackupInitializer{360CheckoutLocation: test.CheckoutLocation,361FromVolumeSnapshot: false,362},363},364}365req.Spec.WorkspaceLocation = test.CheckoutLocation366return nil367}))368if err != nil {369t.Fatalf("cannot launch a workspace: %q", err)370}371372t.Cleanup(func() {373// stop workspace in defer function to prevent we forget to stop the workspace374if err := stopWorkspace(t, cfg, stopWs1Func); err != nil {375t.Errorf("cannot stop workspace: %q", err)376}377})378379rsa, closer, err = integration.Instrument(integration.ComponentWorkspace, "workspace", cfg.Namespace(), kubeconfig, cfg.Client(),380integration.WithInstanceID(ws1.Req.Id),381)382if err != nil {383t.Fatal(err)384}385integration.DeferCloser(t, closer)386387var ls agent.ListDirResponse388err = rsa.Call("WorkspaceAgent.ListDir", &agent.ListDirRequest{389Dir: test.WorkspaceRoot,390}, &ls)391if err != nil {392t.Fatal(err)393}394rsa.Close()395396var found bool397for _, f := range ls.Files {398if filepath.Base(f) == "foobar.txt" {399found = true400break401}402}403if !found {404t.Fatal("did not find foobar.txt from previous workspace instance")405}406})407}408return testCtx409}).410Feature()411412testEnv.Test(t, f)413}414415// TestOpenWorkspaceFromOutdatedPrebuild416// - create a prebuild on older commit417// - open a workspace from a later commit with a prebuild initializer418// - make sure the workspace's init task ran (the init task will create a file `incremental.txt` see https://github.com/gitpod-io/test-incremental-workspace/blob/main/.gitpod.yml)419func TestOpenWorkspaceFromOutdatedPrebuild(t *testing.T) {420421f := features.New("prebuild").422WithLabel("component", "ws-manager").423Assess("it should open a workspace from with an older prebuild initializer successfully and run the init task", func(testCtx context.Context, t *testing.T, cfg *envconf.Config) context.Context {424tests := []struct {425Name string426RemoteUri string427CloneTargetForPrebuild string428CloneTargetForWorkspace string429WorkspaceRoot string430CheckoutLocation string431FF []wsmanapi.WorkspaceFeatureFlag432}{433{434Name: "classic",435RemoteUri: "https://github.com/gitpod-io/test-incremental-workspace",436CloneTargetForPrebuild: "prebuild",437CloneTargetForWorkspace: "main",438CheckoutLocation: "test-incremental-workspace",439WorkspaceRoot: "/workspace/test-incremental-workspace",440},441}442443for _, test := range tests {444test := test445t.Run(test.Name, func(t *testing.T) {446t.Parallel()447448ctx, cancel := context.WithTimeout(testCtx, time.Duration(10*len(tests))*time.Minute)449defer cancel()450451api := integration.NewComponentAPI(ctx, cfg.Namespace(), kubeconfig, cfg.Client())452t.Cleanup(func() {453api.Done(t)454})455456// create a prebuild457ws, prebuildStopWs, err := integration.LaunchWorkspaceDirectly(t, ctx, api,458integration.WithRequestModifier(func(req *wsmanapi.StartWorkspaceRequest) error {459req.Type = wsmanapi.WorkspaceType_PREBUILD460req.Spec.Envvars = append(req.Spec.Envvars, &wsmanapi.EnvironmentVariable{461Name: "GITPOD_TASKS",462Value: `[{ "init": "./init.sh" }]`,463})464req.Spec.FeatureFlags = test.FF465req.Spec.Initializer = &csapi.WorkspaceInitializer{466Spec: &csapi.WorkspaceInitializer_Git{467Git: &csapi.GitInitializer{468RemoteUri: test.RemoteUri,469TargetMode: csapi.CloneTargetMode_REMOTE_BRANCH,470CloneTaget: test.CloneTargetForPrebuild,471CheckoutLocation: test.CheckoutLocation,472Config: &csapi.GitConfig{},473},474},475}476req.Spec.WorkspaceLocation = test.CheckoutLocation477return nil478}),479// The init task only runs for a short duration, so it's possible we miss the Running state, as it quickly transitions to Stopping/Stopped.480// Therefore wait for the workspace to stop to get its last status.481integration.WithWaitWorkspaceForOpts(integration.WaitForStopped))482if err != nil {483t.Fatalf("cannot launch a workspace: %q", err)484}485defer func() {486// stop workspace in defer function to prevent we forget to stop the workspace487if err := stopWorkspace(t, cfg, prebuildStopWs); err != nil {488t.Errorf("cannot stop workspace: %q", err)489}490}()491prebuildSnapshot, _, err := findSnapshotFromStoppedWs(t, ctx, ws.LastStatus)492if err != nil {493t.Fatalf("stop workspace and find snapshot error: %v", err)494}495if prebuildSnapshot == "" {496t.Fatalf("prebuild snapshot is empty")497}498499t.Logf("prebuild snapshot: %s", prebuildSnapshot)500501// launch the workspace on a later commit using this prebuild502ws, stopWs, err := integration.LaunchWorkspaceDirectly(t, ctx, api, integration.WithRequestModifier(func(req *wsmanapi.StartWorkspaceRequest) error {503req.Spec.Envvars = append(req.Spec.Envvars, &wsmanapi.EnvironmentVariable{504Name: "GITPOD_TASKS",505Value: `[{ "init": "./init.sh" }]`,506})507req.Spec.FeatureFlags = test.FF508req.Spec.Initializer = &csapi.WorkspaceInitializer{509Spec: &csapi.WorkspaceInitializer_Prebuild{510Prebuild: &csapi.PrebuildInitializer{511Prebuild: &csapi.SnapshotInitializer{512Snapshot: prebuildSnapshot,513},514Git: []*csapi.GitInitializer{515{516RemoteUri: test.RemoteUri,517TargetMode: csapi.CloneTargetMode_REMOTE_BRANCH,518CloneTaget: test.CloneTargetForWorkspace,519CheckoutLocation: test.CheckoutLocation,520Config: &csapi.GitConfig{},521},522},523},524},525}526527req.Spec.WorkspaceLocation = test.CheckoutLocation528return nil529}))530if err != nil {531t.Fatalf("cannot launch a workspace: %q", err)532}533534defer func() {535// stop workspace in defer function to prevent we forget to stop the workspace536if err := stopWorkspace(t, cfg, stopWs); err != nil {537t.Errorf("cannot stop workspace: %q", err)538}539}()540541rsa, closer, err := integration.Instrument(integration.ComponentWorkspace, "workspace", cfg.Namespace(), kubeconfig, cfg.Client(),542integration.WithInstanceID(ws.Req.Id),543)544if err != nil {545t.Fatal(err)546}547t.Cleanup(func() {548rsa.Close()549})550integration.DeferCloser(t, closer)551552var ls agent.ListDirResponse553err = rsa.Call("WorkspaceAgent.ListDir", &agent.ListDirRequest{554Dir: test.WorkspaceRoot,555}, &ls)556if err != nil {557t.Fatal(err)558}559rsa.Close()560561var found bool562for _, f := range ls.Files {563t.Logf("file: %s", f)564if filepath.Base(f) == "incremental.txt" {565found = true566}567}568if !found {569t.Fatal("did not find incremental.txt")570}571})572}573return testCtx574}).575Feature()576577testEnv.Test(t, f)578}579580// checkPrebuildLogExist checks the prebuild log message exists581func checkPrebuildLogExist(t *testing.T, cfg *envconf.Config, rsa *integration.RpcClient, ws *integration.LaunchWorkspaceDirectlyResult, wsRoot string) {582// since the message '🍊 This task ran as a workspace prebuild' is generated by583// a prebuild workspace supervisor, so we add a retry mechanism to make sure that we584// won't check the message too earlier before the supervisor generated it.585var (586err error587grepResp agent.ExecResponse588checkPrebuildLog bool589)590err = rsa.Call("WorkspaceAgent.Exec", &agent.ExecRequest{591Dir: prebuildLogPath,592Command: "bash",593Args: []string{594"-c",595fmt.Sprintf("grep -r %s *", prebuildLog),596},597}, &grepResp)598if err == nil && grepResp.ExitCode == 0 && strings.Trim(grepResp.Stdout, " \t\n") != "" {599checkPrebuildLog = true600}601if checkPrebuildLog {602return603}604605t.Logf("cannot found the prebuild message %s in %s, err:%v, exitCode:%d, stdout:%s, stderr:%s", prebuildLog, prebuildLogPath, err, grepResp.ExitCode, grepResp.Stdout, grepResp.Stderr)606607// somehow, the prebuild log message '🍊 This task ran as a workspace prebuild' does not exists608// we fall back to check the init task message within the /workspace/.gitpod/prebuild-log-* or not609var grepResp1 agent.ExecResponse610var checkInitTaskMsg bool611err = rsa.Call("WorkspaceAgent.Exec", &agent.ExecRequest{612Dir: prebuildLogPath,613Command: "bash",614Args: []string{615"-c",616fmt.Sprintf("grep %q *", initTask),617},618}, &grepResp1)619if err == nil && grepResp1.ExitCode == 0 && strings.Trim(grepResp1.Stdout, " \t\n") != "" {620checkInitTaskMsg = true621}622if checkInitTaskMsg {623return624}625626t.Logf("cannot found the init task message %s in %s, err:%v, exitCode:%d, stdout:%s", initTask, prebuildLogPath, err, grepResp.ExitCode, grepResp.Stdout)627628// somehow, the init task message does not exist within the /workspace/.gitpod/prebuild-log-*629// we fall back to check the file exists or not630var ls agent.ListDirResponse631err = rsa.Call("WorkspaceAgent.ListDir", &agent.ListDirRequest{632Dir: wsRoot,633}, &ls)634if err != nil {635t.Fatal(err)636}637638var found bool639for _, f := range ls.Files {640if filepath.Base(f) == "someFile" {641found = true642break643}644}645if found {646return647}648t.Fatal("did not find someFile from previous workspace instance")649}650651// checkFolderPermission checks the folder UID and GID is gitpod652func checkFolderPermission(t *testing.T, rsa *integration.RpcClient, workspace string) {653var uid agent.ExecResponse654err := rsa.Call("WorkspaceAgent.Exec", &agent.ExecRequest{655Command: "stat",656Args: []string{"--format", "%U", workspace},657}, &uid)658if err != nil || uid.ExitCode != 0 || strings.Trim(uid.Stdout, " \t\n") != "gitpod" {659t.Fatalf("folder %s UID %s is incorrect, err:%v, exitCode:%d", workspace, uid.Stdout, err, uid.ExitCode)660}661662var gid agent.ExecResponse663err = rsa.Call("WorkspaceAgent.Exec", &agent.ExecRequest{664Command: "stat",665Args: []string{"--format", "%G", workspace},666}, &gid)667if err != nil || uid.ExitCode != 0 || strings.Trim(gid.Stdout, " \t\n") != "gitpod" {668t.Fatalf("folder %s GID %s is incorrect, err:%v, exitCode:%d", workspace, gid.Stdout, err, uid.ExitCode)669}670}671672// checkGitFolderPermission checks the files/folders UID and GID under .git/ is gitpod673func checkGitFolderPermission(t *testing.T, rsa *integration.RpcClient, workspaceRoot string) {674var findUserResp agent.ExecResponse675var gitDir string = fmt.Sprintf("%s/%s", workspaceRoot, ".git")676677err := rsa.Call("WorkspaceAgent.Exec", &agent.ExecRequest{678Dir: gitDir,679Command: "find",680Args: []string{"!", "-user", "gitpod"},681}, &findUserResp)682if err != nil || findUserResp.ExitCode != 0 || strings.Trim(findUserResp.Stdout, " \t\n") != "" {683t.Fatalf("incorrect UID under %s folder, err:%v, exitCode:%d, stdout:%s", gitDir, err, findUserResp.ExitCode, findUserResp.Stdout)684}685686var findGroupResp agent.ExecResponse687err = rsa.Call("WorkspaceAgent.Exec", &agent.ExecRequest{688Dir: gitDir,689Command: "find",690Args: []string{"!", "-group", "gitpod"},691}, &findGroupResp)692if err != nil || findGroupResp.ExitCode != 0 || strings.Trim(findGroupResp.Stdout, " \t\n") != "" {693t.Fatalf("incorrect GID under %s folder, err:%v, exitCode:%d, stdout:%s", gitDir, err, findGroupResp.ExitCode, findGroupResp.Stdout)694}695}696697func checkPrebuildLogUploaded(t *testing.T, ctx context.Context, api *integration.ComponentAPI, ws *integration.LaunchWorkspaceDirectlyResult) {698cs, err := api.ContentService()699if err != nil {700t.Fatal(err)701}702resp, err := cs.ListLogs(ctx, &csapi.ListLogsRequest{703WorkspaceId: ws.LastStatus.Metadata.MetaId,704InstanceId: ws.Req.Id,705OwnerId: ws.LastStatus.Metadata.Owner,706})707if err != nil {708t.Fatal(err)709}710if len(resp.TaskId) == 0 {711t.Fatal("no logs found")712}713t.Logf("found logs (task ids: %v)", resp.TaskId)714}715716func findSnapshotFromStoppedWs(t *testing.T, ctx context.Context, lastStatus *wsmanapi.WorkspaceStatus) (string, *wsmanapi.VolumeSnapshotInfo, error) {717if lastStatus == nil {718return "", nil, fmt.Errorf("did not get last workspace status")719}720if lastStatus.Phase != wsmanapi.WorkspacePhase_STOPPED {721return "", nil, fmt.Errorf("workspace is not stopped: %s", lastStatus.Phase)722}723if lastStatus.Conditions == nil {724return "", nil, nil725}726if lastStatus.Conditions.HeadlessTaskFailed != "" {727return "", nil, errors.New("unexpected HeadlessTaskFailed condition")728}729return lastStatus.Conditions.Snapshot, lastStatus.Conditions.VolumeSnapshot, nil730}731732func stopWorkspace(t *testing.T, cfg *envconf.Config, StopWorkspaceFunc integration.StopWorkspaceFunc) error {733sctx, scancel := context.WithTimeout(context.Background(), 5*time.Minute)734defer scancel()735736sapi := integration.NewComponentAPI(sctx, cfg.Namespace(), kubeconfig, cfg.Client())737defer sapi.Done(t)738739_, err := StopWorkspaceFunc(true, sapi)740return err741}742743744