mirror of
https://github.com/gitpod-io/gitpod.git
synced 2025-12-08 17:36:30 +00:00
1042 lines
38 KiB
Go
1042 lines
38 KiB
Go
// Copyright (c) 2020 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 wsmanager
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/rpc"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"sigs.k8s.io/e2e-framework/klient"
|
|
"sigs.k8s.io/e2e-framework/pkg/envconf"
|
|
"sigs.k8s.io/e2e-framework/pkg/features"
|
|
|
|
csapi "github.com/gitpod-io/gitpod/content-service/api"
|
|
gitpod "github.com/gitpod-io/gitpod/gitpod-protocol"
|
|
agent "github.com/gitpod-io/gitpod/test/pkg/agent/workspace/api"
|
|
"github.com/gitpod-io/gitpod/test/pkg/integration"
|
|
wsmanapi "github.com/gitpod-io/gitpod/ws-manager/api"
|
|
|
|
volumesnapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
|
|
volumesnapshotclientv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned"
|
|
)
|
|
|
|
func TestPrebuildWorkspaceTaskSuccess(t *testing.T) {
|
|
f := features.New("prebuild").
|
|
WithLabel("component", "ws-manager").
|
|
Assess("it should create a prebuild and succeed the defined tasks", func(_ context.Context, t *testing.T, cfg *envconf.Config) context.Context {
|
|
tests := []struct {
|
|
Name string
|
|
ContextURL string
|
|
WorkspaceRoot string
|
|
CheckoutLocation string
|
|
Task []gitpod.TasksItems
|
|
FF []wsmanapi.WorkspaceFeatureFlag
|
|
}{
|
|
{
|
|
Name: "classic",
|
|
ContextURL: "https://github.com/gitpod-io/empty",
|
|
CheckoutLocation: "empty",
|
|
WorkspaceRoot: "/workspace/empty",
|
|
Task: []gitpod.TasksItems{
|
|
{Init: "echo \"some output\" > someFile; sleep 10; exit 0;"},
|
|
},
|
|
},
|
|
{
|
|
Name: "pvc",
|
|
ContextURL: "https://github.com/gitpod-io/empty",
|
|
CheckoutLocation: "empty",
|
|
WorkspaceRoot: "/workspace/empty",
|
|
Task: []gitpod.TasksItems{
|
|
{Init: "echo \"some output\" > someFile; sleep 10; exit 0;"},
|
|
},
|
|
FF: []wsmanapi.WorkspaceFeatureFlag{wsmanapi.WorkspaceFeatureFlag_PERSISTENT_VOLUME_CLAIM},
|
|
},
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(5*len(tests))*time.Minute)
|
|
defer cancel()
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.Name, func(t *testing.T) {
|
|
api := integration.NewComponentAPI(ctx, cfg.Namespace(), kubeconfig, cfg.Client())
|
|
t.Cleanup(func() {
|
|
api.Done(t)
|
|
})
|
|
|
|
// TODO: change to use server API to launch the workspace, so we could run the integration test as the user code flow
|
|
// which is client -> server -> ws-manager rather than client -> ws-manager directly
|
|
ws, stopWs, err := integration.LaunchWorkspaceDirectly(t, ctx, api, integration.WithRequestModifier(func(req *wsmanapi.StartWorkspaceRequest) error {
|
|
req.Type = wsmanapi.WorkspaceType_PREBUILD
|
|
|
|
tasks, err := json.Marshal(test.Task)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Spec.Envvars = append(req.Spec.Envvars, &wsmanapi.EnvironmentVariable{
|
|
Name: "GITPOD_TASKS",
|
|
Value: string(tasks),
|
|
})
|
|
|
|
req.Spec.FeatureFlags = test.FF
|
|
req.Spec.Initializer = &csapi.WorkspaceInitializer{
|
|
Spec: &csapi.WorkspaceInitializer_Git{
|
|
Git: &csapi.GitInitializer{
|
|
RemoteUri: test.ContextURL,
|
|
CheckoutLocation: test.CheckoutLocation,
|
|
Config: &csapi.GitConfig{},
|
|
},
|
|
},
|
|
}
|
|
req.Spec.WorkspaceLocation = test.CheckoutLocation
|
|
return nil
|
|
}))
|
|
if err != nil {
|
|
t.Fatalf("cannot launch a workspace: %q", err)
|
|
}
|
|
defer func() {
|
|
// stop workspace in defer function to prevent we forget to stop the workspace
|
|
if err := stopWorkspace(t, cfg, stopWs); err != nil {
|
|
t.Errorf("cannot stop workspace: %q", err)
|
|
}
|
|
}()
|
|
_, err = integration.WaitForWorkspace(ctx, api, ws.Req.Id, func(status *wsmanapi.WorkspaceStatus) bool {
|
|
if status.Phase != wsmanapi.WorkspacePhase_STOPPED {
|
|
return false
|
|
}
|
|
if status.Conditions.HeadlessTaskFailed != "" {
|
|
t.Logf("Conditions: %v", status.Conditions)
|
|
t.Fatal("unexpected HeadlessTaskFailed condition")
|
|
}
|
|
return true
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("failed for wait workspace stop: %q", err)
|
|
}
|
|
})
|
|
}
|
|
return ctx
|
|
}).
|
|
Feature()
|
|
|
|
testEnv.Test(t, f)
|
|
}
|
|
|
|
func TestPrebuildWorkspaceTaskFail(t *testing.T) {
|
|
f := features.New("prebuild").
|
|
WithLabel("component", "server").
|
|
Assess("it should create a prebuild and fail after running the defined tasks", func(_ context.Context, t *testing.T, cfg *envconf.Config) context.Context {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
|
defer cancel()
|
|
|
|
api := integration.NewComponentAPI(ctx, cfg.Namespace(), kubeconfig, cfg.Client())
|
|
t.Cleanup(func() {
|
|
api.Done(t)
|
|
})
|
|
|
|
ws, stopWs, err := integration.LaunchWorkspaceDirectly(t, ctx, api, integration.WithRequestModifier(func(req *wsmanapi.StartWorkspaceRequest) error {
|
|
req.Type = wsmanapi.WorkspaceType_PREBUILD
|
|
req.Spec.Envvars = append(req.Spec.Envvars, &wsmanapi.EnvironmentVariable{
|
|
Name: "GITPOD_TASKS",
|
|
Value: `[{ "init": "echo \"some output\" > someFile; sleep 10; exit 1;" }]`,
|
|
})
|
|
return nil
|
|
}))
|
|
if err != nil {
|
|
t.Fatalf("cannot start workspace: %q", err)
|
|
}
|
|
|
|
defer func() {
|
|
// stop workspace in defer function to prevent we forget to stop the workspace
|
|
if err := stopWorkspace(t, cfg, stopWs); err != nil {
|
|
t.Errorf("cannot stop workspace: %q", err)
|
|
}
|
|
}()
|
|
|
|
_, err = integration.WaitForWorkspace(ctx, api, ws.Req.Id, func(status *wsmanapi.WorkspaceStatus) bool {
|
|
if status.Phase != wsmanapi.WorkspacePhase_STOPPED {
|
|
return false
|
|
}
|
|
if status.Conditions.HeadlessTaskFailed == "" {
|
|
t.Logf("Conditions: %v", status.Conditions)
|
|
t.Fatal("expected HeadlessTaskFailed condition")
|
|
}
|
|
return true
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("failed for wait workspace stop: %q", err)
|
|
}
|
|
|
|
return ctx
|
|
}).
|
|
Feature()
|
|
|
|
testEnv.Test(t, f)
|
|
}
|
|
|
|
const (
|
|
prebuildLogPath string = "/workspace/.gitpod"
|
|
prebuildLog string = "'🤙 This task ran as a workspace prebuild'"
|
|
initTask string = "echo \"some output\" > someFile; sleep 10;"
|
|
regularPrefix string = "ws-"
|
|
)
|
|
|
|
// TestOpenWorkspaceFromPrebuild
|
|
// - create a prebuild
|
|
// - stop the prebuild workspace
|
|
// - if the PVC feature flag enables, restart the control plane components (ws-manager and snapshot-controller) after the volume snapshot is finished
|
|
// - open the regular workspace from prebuild
|
|
// - make sure the regular workspace PVC object should exist or not
|
|
// - make sure either one of the condition mets
|
|
// - the prebuild log message exists
|
|
// - the init task message exists
|
|
// - the init task generated file exists
|
|
//
|
|
// - make sure the .git/ folder with correct permission
|
|
// - write a new file foobar.txt
|
|
// - stop the regular workspace
|
|
// - if the PVC feature flag enables, restart the control plane components (ws-manager and snapshot-controller) during the volume snapshot is in progress
|
|
// - relaunch the regular workspace
|
|
// - make sure the regular workspace PVC object should exist or not
|
|
// - make sure the file foobar.txt exists
|
|
func TestOpenWorkspaceFromPrebuild(t *testing.T) {
|
|
var (
|
|
snapshotcontrollerDeployment string = "snapshot-controller"
|
|
snapshotcontrollerNamespace string = "kube-system"
|
|
wsmanagerDeployment string = "ws-manager"
|
|
wsmanagerNamespace string = "default"
|
|
)
|
|
|
|
f := features.New("prebuild").
|
|
WithLabel("component", "ws-manager").
|
|
Assess("it should open workspace from prebuild successfully", func(_ context.Context, t *testing.T, cfg *envconf.Config) context.Context {
|
|
tests := []struct {
|
|
Name string
|
|
ContextURL string
|
|
WorkspaceRoot string
|
|
CheckoutLocation string
|
|
FF []wsmanapi.WorkspaceFeatureFlag
|
|
}{
|
|
{
|
|
Name: "classic",
|
|
ContextURL: "https://github.com/gitpod-io/empty",
|
|
CheckoutLocation: "empty",
|
|
WorkspaceRoot: "/workspace/empty",
|
|
},
|
|
{
|
|
Name: "pvc",
|
|
ContextURL: "https://github.com/gitpod-io/empty",
|
|
CheckoutLocation: "empty",
|
|
WorkspaceRoot: "/workspace/empty",
|
|
FF: []wsmanapi.WorkspaceFeatureFlag{wsmanapi.WorkspaceFeatureFlag_PERSISTENT_VOLUME_CLAIM},
|
|
},
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(10*len(tests))*time.Minute)
|
|
defer cancel()
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.Name, func(t *testing.T) {
|
|
isPVCEnable := reflect.DeepEqual(test.FF, []wsmanapi.WorkspaceFeatureFlag{wsmanapi.WorkspaceFeatureFlag_PERSISTENT_VOLUME_CLAIM})
|
|
|
|
api := integration.NewComponentAPI(ctx, cfg.Namespace(), kubeconfig, cfg.Client())
|
|
t.Cleanup(func() {
|
|
api.Done(t)
|
|
})
|
|
|
|
// create a prebuild and stop workspace
|
|
// TODO: change to use server API to launch the workspace, so we could run the integration test as the user code flow
|
|
// which is client -> server -> ws-manager rather than client -> ws-manager directly
|
|
ws, prebuildStopWs, err := integration.LaunchWorkspaceDirectly(t, ctx, api, integration.WithRequestModifier(func(req *wsmanapi.StartWorkspaceRequest) error {
|
|
req.Type = wsmanapi.WorkspaceType_PREBUILD
|
|
req.Spec.Envvars = append(req.Spec.Envvars, &wsmanapi.EnvironmentVariable{
|
|
Name: "GITPOD_TASKS",
|
|
Value: fmt.Sprintf(`[{ "init": %q }]`, initTask),
|
|
})
|
|
req.Spec.FeatureFlags = test.FF
|
|
req.Spec.Initializer = &csapi.WorkspaceInitializer{
|
|
Spec: &csapi.WorkspaceInitializer_Git{
|
|
Git: &csapi.GitInitializer{
|
|
RemoteUri: test.ContextURL,
|
|
CheckoutLocation: test.CheckoutLocation,
|
|
Config: &csapi.GitConfig{},
|
|
},
|
|
},
|
|
}
|
|
req.Spec.WorkspaceLocation = test.CheckoutLocation
|
|
return nil
|
|
}))
|
|
if err != nil {
|
|
t.Fatalf("cannot launch a workspace: %q", err)
|
|
}
|
|
defer func() {
|
|
// stop workspace in defer function to prevent we forget to stop the workspace
|
|
if err := stopWorkspace(t, cfg, prebuildStopWs); err != nil {
|
|
t.Errorf("cannot stop workspace: %q", err)
|
|
}
|
|
}()
|
|
prebuildSnapshot, prebuildVSInfo, err := watchStopWorkspaceAndFindSnapshot(ctx, ws.Req.Id, api)
|
|
if err != nil {
|
|
t.Fatalf("stop workspace and find snapshot error: %v", err)
|
|
}
|
|
|
|
t.Logf("prebuild snapshot: %s", prebuildSnapshot)
|
|
checkSnapshot(t, prebuildVSInfo, isPVCEnable)
|
|
|
|
// restart the ws-manager and volume snapshot after the volume snapshot is finished
|
|
if isPVCEnable {
|
|
t.Logf("rollout restart deployment %s/%s after the volume snapshot is finished", wsmanagerDeployment, wsmanagerNamespace)
|
|
if err := api.RestartDeployment(wsmanagerDeployment, wsmanagerNamespace, true); err != nil {
|
|
t.Errorf("cannot restart deployment %s/%s", wsmanagerDeployment, wsmanagerNamespace)
|
|
}
|
|
t.Logf("rollout restart deployment %s/%s is finished", wsmanagerDeployment, wsmanagerNamespace)
|
|
|
|
t.Logf("rollout restart deployment %s/%s after the volume snapshot is finished", snapshotcontrollerDeployment, snapshotcontrollerNamespace)
|
|
if err := api.RestartDeployment(snapshotcontrollerDeployment, snapshotcontrollerNamespace, true); err != nil {
|
|
t.Errorf("cannot restart deployment %s/%s", snapshotcontrollerDeployment, snapshotcontrollerNamespace)
|
|
}
|
|
t.Logf("rollout restart deployment %s/%s is finished", snapshotcontrollerDeployment, snapshotcontrollerNamespace)
|
|
|
|
// recreate a new API because the ws-manager restarted
|
|
api1 := integration.NewComponentAPI(ctx, cfg.Namespace(), kubeconfig, cfg.Client())
|
|
api = api1
|
|
t.Cleanup(func() {
|
|
api1.Done(t)
|
|
})
|
|
}
|
|
|
|
// launch the workspace from prebuild
|
|
// TODO: change to use server API to launch the workspace, so we could run the integration test as the user code flow
|
|
// which is client -> server -> ws-manager rather than client -> ws-manager directly
|
|
ws, stopWs, err := integration.LaunchWorkspaceDirectly(t, ctx, api, integration.WithRequestModifier(func(req *wsmanapi.StartWorkspaceRequest) error {
|
|
req.Spec.FeatureFlags = test.FF
|
|
req.Spec.Initializer = &csapi.WorkspaceInitializer{
|
|
Spec: &csapi.WorkspaceInitializer_Prebuild{
|
|
Prebuild: &csapi.PrebuildInitializer{
|
|
Prebuild: &csapi.SnapshotInitializer{
|
|
Snapshot: prebuildSnapshot,
|
|
FromVolumeSnapshot: isPVCEnable,
|
|
},
|
|
Git: []*csapi.GitInitializer{
|
|
{
|
|
RemoteUri: test.ContextURL,
|
|
CheckoutLocation: test.CheckoutLocation,
|
|
Config: &csapi.GitConfig{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
req.Spec.VolumeSnapshot = prebuildVSInfo
|
|
req.Spec.WorkspaceLocation = test.CheckoutLocation
|
|
return nil
|
|
}))
|
|
if err != nil {
|
|
t.Fatalf("cannot launch a workspace: %q", err)
|
|
}
|
|
|
|
// check the PVC object should exist or not
|
|
checkPVCObject(t, api, isPVCEnable, regularPrefix+ws.Req.Id)
|
|
|
|
defer func() {
|
|
// stop workspace in defer function to prevent we forget to stop the workspace
|
|
if err := stopWorkspace(t, cfg, stopWs); err != nil {
|
|
t.Errorf("cannot stop workspace: %q", err)
|
|
}
|
|
}()
|
|
|
|
rsa, closer, err := integration.Instrument(integration.ComponentWorkspace, "workspace", cfg.Namespace(), kubeconfig, cfg.Client(),
|
|
integration.WithInstanceID(ws.Req.Id),
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() {
|
|
rsa.Close()
|
|
})
|
|
integration.DeferCloser(t, closer)
|
|
|
|
// check prebuild log message exists
|
|
checkPrebuildLogExist(t, cfg, rsa, ws, test.WorkspaceRoot)
|
|
|
|
// check the folder permission is gitpod
|
|
checkFolderPermission(t, rsa, "/workspace")
|
|
|
|
// check the files/folders permission under .git/ is gitpod
|
|
checkGitFolderPermission(t, rsa, test.WorkspaceRoot)
|
|
|
|
// write file foobar.txt and stop the workspace
|
|
var writeFileResp agent.ExecResponse
|
|
err = rsa.Call("WorkspaceAgent.WriteFile", &agent.WriteFileRequest{
|
|
Path: fmt.Sprintf("%s/foobar.txt", test.WorkspaceRoot),
|
|
Content: []byte("hello world"),
|
|
Mode: 0644,
|
|
}, &writeFileResp)
|
|
if err != nil {
|
|
t.Fatalf("cannot write file %s", fmt.Sprintf("%s/foobar.txt", test.WorkspaceRoot))
|
|
}
|
|
|
|
sctx, scancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
|
defer scancel()
|
|
|
|
sapi := integration.NewComponentAPI(sctx, cfg.Namespace(), kubeconfig, cfg.Client())
|
|
defer sapi.Done(t)
|
|
|
|
// stop workspace without wait
|
|
_, err = stopWs(false, sapi)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// restart the ws-manager and volume snapshot after the volume snapshot is in progress
|
|
if isPVCEnable {
|
|
t.Logf("rollout restart deployment %s/%s after the volume snapshot is in progress", wsmanagerDeployment, wsmanagerNamespace)
|
|
if err := api.RestartDeployment(wsmanagerDeployment, wsmanagerNamespace, true); err != nil {
|
|
t.Errorf("cannot restart deployment %s/%s", wsmanagerDeployment, wsmanagerNamespace)
|
|
}
|
|
t.Logf("rollout restart deployment %s/%s is in progress", wsmanagerDeployment, wsmanagerNamespace)
|
|
|
|
t.Logf("rollout restart deployment %s/%s after the volume snapshot is in progress", snapshotcontrollerDeployment, snapshotcontrollerNamespace)
|
|
if err := api.RestartDeployment(snapshotcontrollerDeployment, snapshotcontrollerNamespace, true); err != nil {
|
|
t.Errorf("cannot restart deployment %s/%s", snapshotcontrollerDeployment, snapshotcontrollerNamespace)
|
|
}
|
|
t.Logf("rollout restart deployment %s/%s is in progress", snapshotcontrollerDeployment, snapshotcontrollerNamespace)
|
|
|
|
// recreate a new API because the ws-manager restarted
|
|
sapi1 := integration.NewComponentAPI(ctx, cfg.Namespace(), kubeconfig, cfg.Client())
|
|
sapi = sapi1
|
|
t.Cleanup(func() {
|
|
sapi.Done(t)
|
|
})
|
|
}
|
|
|
|
// find the volume snapshot by volume snapshot client
|
|
wsVSInfo, err := findVolumeSnapshot(ctx, isPVCEnable, ws.Req.Id, cfg.Namespace(), cfg.Client())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
checkSnapshot(t, wsVSInfo, isPVCEnable)
|
|
|
|
// reopen the workspace and make sure the file foobar.txt exists
|
|
ws1, stopWs1, err := integration.LaunchWorkspaceDirectly(t, ctx, sapi, integration.WithRequestModifier(func(req *wsmanapi.StartWorkspaceRequest) error {
|
|
req.ServicePrefix = ws.Req.ServicePrefix
|
|
req.Metadata.MetaId = ws.Req.Metadata.MetaId
|
|
req.Metadata.Owner = ws.Req.Metadata.Owner
|
|
req.Spec.FeatureFlags = test.FF
|
|
req.Spec.Initializer = &csapi.WorkspaceInitializer{
|
|
Spec: &csapi.WorkspaceInitializer_Backup{
|
|
Backup: &csapi.FromBackupInitializer{
|
|
CheckoutLocation: test.CheckoutLocation,
|
|
FromVolumeSnapshot: isPVCEnable,
|
|
},
|
|
},
|
|
}
|
|
req.Spec.VolumeSnapshot = wsVSInfo
|
|
req.Spec.WorkspaceLocation = test.CheckoutLocation
|
|
return nil
|
|
}))
|
|
if err != nil {
|
|
t.Fatalf("cannot launch a workspace: %q", err)
|
|
}
|
|
|
|
// check the PVC object should exist or not
|
|
checkPVCObject(t, sapi, isPVCEnable, regularPrefix+ws1.Req.Id)
|
|
|
|
defer func() {
|
|
// stop workspace in defer function to prevent we forget to stop the workspace
|
|
if err := stopWorkspace(t, cfg, stopWs1); err != nil {
|
|
t.Errorf("cannot stop workspace: %q", err)
|
|
}
|
|
}()
|
|
|
|
rsa, closer, err = integration.Instrument(integration.ComponentWorkspace, "workspace", cfg.Namespace(), kubeconfig, cfg.Client(),
|
|
integration.WithInstanceID(ws1.Req.Id),
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
integration.DeferCloser(t, closer)
|
|
|
|
var ls agent.ListDirResponse
|
|
err = rsa.Call("WorkspaceAgent.ListDir", &agent.ListDirRequest{
|
|
Dir: test.WorkspaceRoot,
|
|
}, &ls)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rsa.Close()
|
|
|
|
var found bool
|
|
for _, f := range ls.Files {
|
|
if filepath.Base(f) == "foobar.txt" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatal("did not find foobar.txt from previous workspace instance")
|
|
}
|
|
})
|
|
}
|
|
return ctx
|
|
}).
|
|
Feature()
|
|
|
|
testEnv.Test(t, f)
|
|
}
|
|
|
|
// TestOpenWorkspaceFromOutdatedPrebuild
|
|
// - create a prebuild on older commit
|
|
// - open a workspace from a later commit with a prebuild initializer
|
|
// - 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)
|
|
func TestOpenWorkspaceFromOutdatedPrebuild(t *testing.T) {
|
|
|
|
f := features.New("prebuild").
|
|
WithLabel("component", "ws-manager").
|
|
Assess("it should open a workspace from with an older prebuild initializer successfully and run the init task", func(_ context.Context, t *testing.T, cfg *envconf.Config) context.Context {
|
|
tests := []struct {
|
|
Name string
|
|
RemoteUri string
|
|
CloneTargetForPrebuild string
|
|
CloneTargetForWorkspace string
|
|
WorkspaceRoot string
|
|
CheckoutLocation string
|
|
FF []wsmanapi.WorkspaceFeatureFlag
|
|
}{
|
|
{
|
|
Name: "classic",
|
|
RemoteUri: "https://github.com/gitpod-io/test-incremental-workspace",
|
|
CloneTargetForPrebuild: "prebuild",
|
|
CloneTargetForWorkspace: "main",
|
|
CheckoutLocation: "test-incremental-workspace",
|
|
WorkspaceRoot: "/workspace/test-incremental-workspace",
|
|
},
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(10*len(tests))*time.Minute)
|
|
defer cancel()
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.Name, func(t *testing.T) {
|
|
|
|
api := integration.NewComponentAPI(ctx, cfg.Namespace(), kubeconfig, cfg.Client())
|
|
t.Cleanup(func() {
|
|
api.Done(t)
|
|
})
|
|
|
|
// create a prebuild
|
|
ws, prebuildStopWs, err := integration.LaunchWorkspaceDirectly(t, ctx, api, integration.WithRequestModifier(func(req *wsmanapi.StartWorkspaceRequest) error {
|
|
req.Type = wsmanapi.WorkspaceType_PREBUILD
|
|
req.Spec.Envvars = append(req.Spec.Envvars, &wsmanapi.EnvironmentVariable{
|
|
Name: "GITPOD_TASKS",
|
|
Value: `[{ "init": "./init.sh" }]`,
|
|
})
|
|
req.Spec.FeatureFlags = test.FF
|
|
req.Spec.Initializer = &csapi.WorkspaceInitializer{
|
|
Spec: &csapi.WorkspaceInitializer_Git{
|
|
Git: &csapi.GitInitializer{
|
|
RemoteUri: test.RemoteUri,
|
|
TargetMode: csapi.CloneTargetMode_REMOTE_BRANCH,
|
|
CloneTaget: test.CloneTargetForPrebuild,
|
|
CheckoutLocation: test.CheckoutLocation,
|
|
Config: &csapi.GitConfig{},
|
|
},
|
|
},
|
|
}
|
|
req.Spec.WorkspaceLocation = test.CheckoutLocation
|
|
return nil
|
|
}))
|
|
if err != nil {
|
|
t.Fatalf("cannot launch a workspace: %q", err)
|
|
}
|
|
defer func() {
|
|
// stop workspace in defer function to prevent we forget to stop the workspace
|
|
if err := stopWorkspace(t, cfg, prebuildStopWs); err != nil {
|
|
t.Errorf("cannot stop workspace: %q", err)
|
|
}
|
|
}()
|
|
prebuildSnapshot, prebuildVSInfo, err := watchStopWorkspaceAndFindSnapshot(ctx, ws.Req.Id, api)
|
|
if err != nil {
|
|
t.Fatalf("stop workspace and find snapshot error: %v", err)
|
|
}
|
|
|
|
t.Logf("prebuild snapshot: %s", prebuildSnapshot)
|
|
|
|
// launch the workspace on a later commit using this prebuild
|
|
ws, stopWs, err := integration.LaunchWorkspaceDirectly(t, ctx, api, integration.WithRequestModifier(func(req *wsmanapi.StartWorkspaceRequest) error {
|
|
req.Spec.Envvars = append(req.Spec.Envvars, &wsmanapi.EnvironmentVariable{
|
|
Name: "GITPOD_TASKS",
|
|
Value: `[{ "init": "./init.sh" }]`,
|
|
})
|
|
req.Spec.FeatureFlags = test.FF
|
|
req.Spec.Initializer = &csapi.WorkspaceInitializer{
|
|
Spec: &csapi.WorkspaceInitializer_Prebuild{
|
|
Prebuild: &csapi.PrebuildInitializer{
|
|
Prebuild: &csapi.SnapshotInitializer{
|
|
Snapshot: prebuildSnapshot,
|
|
},
|
|
Git: []*csapi.GitInitializer{
|
|
{
|
|
RemoteUri: test.RemoteUri,
|
|
TargetMode: csapi.CloneTargetMode_REMOTE_BRANCH,
|
|
CloneTaget: test.CloneTargetForWorkspace,
|
|
CheckoutLocation: test.CheckoutLocation,
|
|
Config: &csapi.GitConfig{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
req.Spec.VolumeSnapshot = prebuildVSInfo
|
|
req.Spec.WorkspaceLocation = test.CheckoutLocation
|
|
return nil
|
|
}))
|
|
if err != nil {
|
|
t.Fatalf("cannot launch a workspace: %q", err)
|
|
}
|
|
|
|
defer func() {
|
|
// stop workspace in defer function to prevent we forget to stop the workspace
|
|
if err := stopWorkspace(t, cfg, stopWs); err != nil {
|
|
t.Errorf("cannot stop workspace: %q", err)
|
|
}
|
|
}()
|
|
|
|
rsa, closer, err := integration.Instrument(integration.ComponentWorkspace, "workspace", cfg.Namespace(), kubeconfig, cfg.Client(),
|
|
integration.WithInstanceID(ws.Req.Id),
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() {
|
|
rsa.Close()
|
|
})
|
|
integration.DeferCloser(t, closer)
|
|
|
|
var ls agent.ListDirResponse
|
|
err = rsa.Call("WorkspaceAgent.ListDir", &agent.ListDirRequest{
|
|
Dir: test.WorkspaceRoot,
|
|
}, &ls)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rsa.Close()
|
|
|
|
var found bool
|
|
for _, f := range ls.Files {
|
|
t.Logf("file: %s", f)
|
|
if filepath.Base(f) == "incremental.txt" {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatal("did not find incremental.txt")
|
|
}
|
|
})
|
|
}
|
|
return ctx
|
|
}).
|
|
Feature()
|
|
|
|
testEnv.Test(t, f)
|
|
}
|
|
|
|
// TestPrebuildAndRegularWorkspaceDifferentWorkspaceClass
|
|
// - create a prebuild with small workspace class (20Gi disk)
|
|
// - create the workspace from prebulid with large workspace class (30Gi disk)
|
|
// - make sure either one of the condition mets
|
|
// - the prebuild log message exists
|
|
// - the init task message exists
|
|
// - the init task generated file exists
|
|
//
|
|
// - make sure the .git/ folder with correct permission
|
|
// - create a prebuild with large workspace class (30Gi disk) separately
|
|
// - create the workspace from prebuild with small workspace class (20Gi disk) separately
|
|
// - make sure the workspace can't start
|
|
func TestPrebuildAndRegularWorkspaceDifferentWorkspaceClass(t *testing.T) {
|
|
f := features.New("prebuild").
|
|
WithLabel("component", "ws-manager").
|
|
Assess("it should open workspace with different workspace class", func(_ context.Context, t *testing.T, cfg *envconf.Config) context.Context {
|
|
tests := []struct {
|
|
Name string
|
|
PrebuildWorkspaceClass string
|
|
RegularWorkspaceClass string
|
|
ContextURL string
|
|
WorkspaceRoot string
|
|
CheckoutLocation string
|
|
FF []wsmanapi.WorkspaceFeatureFlag
|
|
ShouldFailToOpenRegularWorkspace bool
|
|
}{
|
|
{
|
|
Name: "prebuild-small-regular-large-workspace-class",
|
|
// TODO: do not use hard-code workspace class name to prevent if we change to different environment to run the test
|
|
PrebuildWorkspaceClass: "small",
|
|
RegularWorkspaceClass: "default",
|
|
//
|
|
ContextURL: "https://github.com/gitpod-io/empty",
|
|
CheckoutLocation: "empty",
|
|
WorkspaceRoot: "/workspace/empty",
|
|
FF: []wsmanapi.WorkspaceFeatureFlag{wsmanapi.WorkspaceFeatureFlag_PERSISTENT_VOLUME_CLAIM},
|
|
},
|
|
{
|
|
Name: "prebuild-large-regular-small-workspace-class",
|
|
// TODO: do not use hard-code workspace class name to prevent if we change to different environment to run the test
|
|
PrebuildWorkspaceClass: "default",
|
|
RegularWorkspaceClass: "small",
|
|
//
|
|
ContextURL: "https://github.com/gitpod-io/empty",
|
|
CheckoutLocation: "empty",
|
|
WorkspaceRoot: "/workspace/empty",
|
|
FF: []wsmanapi.WorkspaceFeatureFlag{wsmanapi.WorkspaceFeatureFlag_PERSISTENT_VOLUME_CLAIM},
|
|
ShouldFailToOpenRegularWorkspace: true,
|
|
},
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(10*len(tests))*time.Minute)
|
|
defer cancel()
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.Name, func(t *testing.T) {
|
|
isPVCEnable := reflect.DeepEqual(test.FF, []wsmanapi.WorkspaceFeatureFlag{wsmanapi.WorkspaceFeatureFlag_PERSISTENT_VOLUME_CLAIM})
|
|
|
|
api := integration.NewComponentAPI(ctx, cfg.Namespace(), kubeconfig, cfg.Client())
|
|
t.Cleanup(func() {
|
|
api.Done(t)
|
|
})
|
|
|
|
// create a prebuild and stop workspace
|
|
ws, prebuildStopWs, err := integration.LaunchWorkspaceDirectly(t, ctx, api, integration.WithRequestModifier(func(req *wsmanapi.StartWorkspaceRequest) error {
|
|
req.Type = wsmanapi.WorkspaceType_PREBUILD
|
|
req.Spec.Class = test.PrebuildWorkspaceClass
|
|
req.Spec.Envvars = append(req.Spec.Envvars, &wsmanapi.EnvironmentVariable{
|
|
Name: "GITPOD_TASKS",
|
|
Value: fmt.Sprintf(`[{ "init": %q }]`, initTask),
|
|
})
|
|
req.Spec.FeatureFlags = test.FF
|
|
req.Spec.Initializer = &csapi.WorkspaceInitializer{
|
|
Spec: &csapi.WorkspaceInitializer_Git{
|
|
Git: &csapi.GitInitializer{
|
|
RemoteUri: test.ContextURL,
|
|
CheckoutLocation: test.CheckoutLocation,
|
|
Config: &csapi.GitConfig{},
|
|
},
|
|
},
|
|
}
|
|
req.Spec.WorkspaceLocation = test.CheckoutLocation
|
|
return nil
|
|
}))
|
|
if err != nil {
|
|
t.Fatalf("cannot launch a workspace: %q", err)
|
|
}
|
|
defer func() {
|
|
// stop workspace in defer function to prevent we forget to stop the workspace
|
|
if err := stopWorkspace(t, cfg, prebuildStopWs); err != nil {
|
|
t.Errorf("cannot stop workspace: %q", err)
|
|
}
|
|
}()
|
|
|
|
prebuildSnapshot, vsInfo, err := watchStopWorkspaceAndFindSnapshot(ctx, ws.Req.Id, api)
|
|
if err != nil {
|
|
t.Fatalf("stop workspace and find snapshot error: %v", err)
|
|
}
|
|
|
|
t.Logf("prebuild snapshot: %s", prebuildSnapshot)
|
|
checkSnapshot(t, vsInfo, isPVCEnable)
|
|
|
|
// launch the workspace from prebuild
|
|
ws, stopWs, err := integration.LaunchWorkspaceDirectly(t, ctx, api, integration.WithRequestModifier(func(req *wsmanapi.StartWorkspaceRequest) error {
|
|
req.Spec.Class = test.RegularWorkspaceClass
|
|
req.Spec.FeatureFlags = test.FF
|
|
req.Spec.Initializer = &csapi.WorkspaceInitializer{
|
|
Spec: &csapi.WorkspaceInitializer_Prebuild{
|
|
Prebuild: &csapi.PrebuildInitializer{
|
|
Prebuild: &csapi.SnapshotInitializer{
|
|
Snapshot: prebuildSnapshot,
|
|
FromVolumeSnapshot: true,
|
|
},
|
|
Git: []*csapi.GitInitializer{
|
|
{
|
|
RemoteUri: test.ContextURL,
|
|
CheckoutLocation: test.CheckoutLocation,
|
|
Config: &csapi.GitConfig{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
req.Spec.VolumeSnapshot = vsInfo
|
|
req.Spec.WorkspaceLocation = test.CheckoutLocation
|
|
return nil
|
|
}))
|
|
if err != nil {
|
|
if test.ShouldFailToOpenRegularWorkspace {
|
|
return
|
|
}
|
|
t.Fatalf("cannot launch a workspace: %q", err)
|
|
}
|
|
if test.ShouldFailToOpenRegularWorkspace {
|
|
t.Fatal("should failed on launch a workspace")
|
|
return
|
|
}
|
|
|
|
defer func() {
|
|
// stop workspace in defer function to prevent we forget to stop the workspace
|
|
if err := stopWorkspace(t, cfg, stopWs); err != nil {
|
|
t.Errorf("cannot stop workspace: %q", err)
|
|
}
|
|
}()
|
|
|
|
rsa, closer, err := integration.Instrument(integration.ComponentWorkspace, "workspace", cfg.Namespace(), kubeconfig, cfg.Client(),
|
|
integration.WithInstanceID(ws.Req.Id),
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() {
|
|
rsa.Close()
|
|
})
|
|
integration.DeferCloser(t, closer)
|
|
|
|
// check prebuild log message exists
|
|
checkPrebuildLogExist(t, cfg, rsa, ws, test.WorkspaceRoot)
|
|
|
|
// check the folder permission is gitpod
|
|
checkFolderPermission(t, rsa, "/workspace")
|
|
|
|
// check the files/folders permission under .git/ is gitpod
|
|
checkGitFolderPermission(t, rsa, test.WorkspaceRoot)
|
|
})
|
|
}
|
|
return ctx
|
|
}).
|
|
Feature()
|
|
|
|
testEnv.Test(t, f)
|
|
}
|
|
|
|
// checkSnapshot checks the volume snapshot information is valid or not
|
|
func checkSnapshot(t *testing.T, vsInfo *wsmanapi.VolumeSnapshotInfo, isPVCEnable bool) {
|
|
if !isPVCEnable {
|
|
if vsInfo == nil {
|
|
return
|
|
}
|
|
if vsInfo.VolumeSnapshotName == "" && vsInfo.VolumeSnapshotHandle == "" {
|
|
return
|
|
}
|
|
t.Fatalf("it should not contain volume snapshot name %s and volume snapshot handle %s without PVC", vsInfo.VolumeSnapshotName, vsInfo.VolumeSnapshotHandle)
|
|
} else {
|
|
if vsInfo == nil {
|
|
t.Fatal("it should contain volume snapshot info with PVC")
|
|
}
|
|
if vsInfo.VolumeSnapshotName != "" && vsInfo.VolumeSnapshotHandle != "" {
|
|
t.Logf("vsName: %s, vsHandle: %s", vsInfo.VolumeSnapshotName, vsInfo.VolumeSnapshotHandle)
|
|
return
|
|
}
|
|
t.Fatalf("it should contain volume snapshot name %s and volume snapshot handle %s without PVC", vsInfo.VolumeSnapshotName, vsInfo.VolumeSnapshotHandle)
|
|
}
|
|
}
|
|
|
|
// checkPVCObject checks the PVC object should exist or not
|
|
func checkPVCObject(t *testing.T, api *integration.ComponentAPI, isPVCEnable bool, pvcName string) {
|
|
if isPVCEnable {
|
|
// check the PVC object exist
|
|
if !api.IsPVCExist(pvcName) {
|
|
t.Fatal("prebuild PVC object should exist")
|
|
}
|
|
return
|
|
}
|
|
// check the PVC object not exist
|
|
if api.IsPVCExist(pvcName) {
|
|
t.Fatal("prebuild PVC object should not exist")
|
|
}
|
|
}
|
|
|
|
// checkPrebuildLogExist checks the prebuild log message exists
|
|
func checkPrebuildLogExist(t *testing.T, cfg *envconf.Config, rsa *rpc.Client, ws *integration.LaunchWorkspaceDirectlyResult, wsRoot string) {
|
|
// since the message '🤙 This task ran as a workspace prebuild' is generated by
|
|
// a prebuild workspace supervisor, so we add a retry mechanism to make sure that we
|
|
// won't check the message too earlier before the supervisor generated it.
|
|
var (
|
|
err error
|
|
grepResp agent.ExecResponse
|
|
checkPrebuildLog bool
|
|
)
|
|
err = rsa.Call("WorkspaceAgent.Exec", &agent.ExecRequest{
|
|
Dir: prebuildLogPath,
|
|
Command: "bash",
|
|
Args: []string{
|
|
"-c",
|
|
fmt.Sprintf("grep %s *", prebuildLog),
|
|
},
|
|
}, &grepResp)
|
|
if err == nil && grepResp.ExitCode == 0 && strings.Trim(grepResp.Stdout, " \t\n") != "" {
|
|
checkPrebuildLog = true
|
|
}
|
|
if checkPrebuildLog {
|
|
return
|
|
}
|
|
|
|
t.Logf("cannot found the prebuild message %s in %s, err:%v, exitCode:%d, stdout:%s", prebuildLog, prebuildLogPath, err, grepResp.ExitCode, grepResp.Stdout)
|
|
|
|
// somehow, the prebuild log message '🤙 This task ran as a workspace prebuild' does not exists
|
|
// we fall back to check the the init task message within the /workspace/.gitpod/prebuild-log-* or not
|
|
var grepResp1 agent.ExecResponse
|
|
var checkInitTaskMsg bool
|
|
err = rsa.Call("WorkspaceAgent.Exec", &agent.ExecRequest{
|
|
Dir: prebuildLogPath,
|
|
Command: "bash",
|
|
Args: []string{
|
|
"-c",
|
|
fmt.Sprintf("grep %q *", initTask),
|
|
},
|
|
}, &grepResp1)
|
|
if err == nil && grepResp1.ExitCode == 0 && strings.Trim(grepResp1.Stdout, " \t\n") != "" {
|
|
checkInitTaskMsg = true
|
|
}
|
|
if checkInitTaskMsg {
|
|
return
|
|
}
|
|
|
|
t.Logf("cannot found the init task message %s in %s, err:%v, exitCode:%d, stdout:%s", initTask, prebuildLogPath, err, grepResp.ExitCode, grepResp.Stdout)
|
|
|
|
// somehow, the init task message does not exist within the /workspace/.gitpod/prebuild-log-*
|
|
// we fall back to check the file exists or not
|
|
var ls agent.ListDirResponse
|
|
err = rsa.Call("WorkspaceAgent.ListDir", &agent.ListDirRequest{
|
|
Dir: wsRoot,
|
|
}, &ls)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var found bool
|
|
for _, f := range ls.Files {
|
|
if filepath.Base(f) == "someFile" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if found {
|
|
return
|
|
}
|
|
t.Fatal("did not find someFile from previous workspace instance")
|
|
}
|
|
|
|
// checkFolderPermission checks the folder UID and GID is gitpod
|
|
func checkFolderPermission(t *testing.T, rsa *rpc.Client, workspace string) {
|
|
var uid agent.ExecResponse
|
|
err := rsa.Call("WorkspaceAgent.Exec", &agent.ExecRequest{
|
|
Command: "stat",
|
|
Args: []string{"--format", "%U", workspace},
|
|
}, &uid)
|
|
if err != nil || uid.ExitCode != 0 || strings.Trim(uid.Stdout, " \t\n") != "gitpod" {
|
|
t.Fatalf("folder %s UID %s is incorrect, err:%v, exitCode:%d", workspace, uid.Stdout, err, uid.ExitCode)
|
|
}
|
|
|
|
var gid agent.ExecResponse
|
|
err = rsa.Call("WorkspaceAgent.Exec", &agent.ExecRequest{
|
|
Command: "stat",
|
|
Args: []string{"--format", "%G", workspace},
|
|
}, &gid)
|
|
if err != nil || uid.ExitCode != 0 || strings.Trim(gid.Stdout, " \t\n") != "gitpod" {
|
|
t.Fatalf("folder %s GID %s is incorrect, err:%v, exitCode:%d", workspace, gid.Stdout, err, uid.ExitCode)
|
|
}
|
|
}
|
|
|
|
// checkGitFolderPermission checks the files/folders UID and GID under .git/ is gitpod
|
|
func checkGitFolderPermission(t *testing.T, rsa *rpc.Client, workspaceRoot string) {
|
|
var findUserResp agent.ExecResponse
|
|
var gitDir string = fmt.Sprintf("%s/%s", workspaceRoot, ".git")
|
|
|
|
err := rsa.Call("WorkspaceAgent.Exec", &agent.ExecRequest{
|
|
Dir: gitDir,
|
|
Command: "find",
|
|
Args: []string{"!", "-user", "gitpod"},
|
|
}, &findUserResp)
|
|
if err != nil || findUserResp.ExitCode != 0 || strings.Trim(findUserResp.Stdout, " \t\n") != "" {
|
|
t.Fatalf("incorrect UID under %s folder, err:%v, exitCode:%d, stdout:%s", gitDir, err, findUserResp.ExitCode, findUserResp.Stdout)
|
|
}
|
|
|
|
var findGroupResp agent.ExecResponse
|
|
err = rsa.Call("WorkspaceAgent.Exec", &agent.ExecRequest{
|
|
Dir: gitDir,
|
|
Command: "find",
|
|
Args: []string{"!", "-group", "gitpod"},
|
|
}, &findGroupResp)
|
|
if err != nil || findGroupResp.ExitCode != 0 || strings.Trim(findGroupResp.Stdout, " \t\n") != "" {
|
|
t.Fatalf("incorrect GID under %s folder, err:%v, exitCode:%d, stdout:%s", gitDir, err, findGroupResp.ExitCode, findGroupResp.Stdout)
|
|
}
|
|
}
|
|
|
|
func findVolumeSnapshot(ctx context.Context, isPVCEnable bool, instanceID, namespace string, client klient.Client) (*wsmanapi.VolumeSnapshotInfo, error) {
|
|
vsInfo := &wsmanapi.VolumeSnapshotInfo{}
|
|
|
|
if !isPVCEnable {
|
|
return vsInfo, nil
|
|
}
|
|
|
|
vsClient, err := volumesnapshotclientv1.NewForConfig(client.RESTConfig())
|
|
if err != nil {
|
|
return vsInfo, err
|
|
}
|
|
|
|
vsInfo.VolumeSnapshotName = instanceID
|
|
|
|
watcher, err := vsClient.SnapshotV1().VolumeSnapshots(namespace).Watch(ctx, metav1.ListOptions{FieldSelector: fmt.Sprintf("metadata.name=%s", instanceID)})
|
|
if err != nil {
|
|
return vsInfo, err
|
|
}
|
|
|
|
defer func() {
|
|
// stop the volume snapshot watcher
|
|
watcher.Stop()
|
|
}()
|
|
|
|
for event := range watcher.ResultChan() {
|
|
vs, ok := event.Object.(*volumesnapshotv1.VolumeSnapshot)
|
|
if !ok {
|
|
return vsInfo, fmt.Errorf("unexpected type assertion %T", event.Object)
|
|
}
|
|
if vs != nil && vs.Status != nil && vs.Status.ReadyToUse != nil && *vs.Status.ReadyToUse && vs.Status.BoundVolumeSnapshotContentName != nil {
|
|
vsInfo.VolumeSnapshotHandle = *vs.Status.BoundVolumeSnapshotContentName
|
|
break
|
|
}
|
|
}
|
|
return vsInfo, nil
|
|
}
|
|
|
|
func watchStopWorkspaceAndFindSnapshot(ctx context.Context, instanceId string, api *integration.ComponentAPI) (string, *wsmanapi.VolumeSnapshotInfo, error) {
|
|
lastStatus, err := integration.WaitForWorkspaceStop(ctx, api, instanceId)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
if lastStatus.Conditions.HeadlessTaskFailed != "" {
|
|
return "", nil, errors.New("unexpected HeadlessTaskFailed condition")
|
|
}
|
|
if lastStatus == nil || lastStatus.Conditions == nil || lastStatus.Conditions.VolumeSnapshot == nil {
|
|
return "", nil, nil
|
|
}
|
|
return lastStatus.Conditions.Snapshot, lastStatus.Conditions.VolumeSnapshot, nil
|
|
}
|
|
|
|
func stopWorkspace(t *testing.T, cfg *envconf.Config, StopWorkspaceFunc integration.StopWorkspaceFunc) error {
|
|
sctx, scancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
|
defer scancel()
|
|
|
|
sapi := integration.NewComponentAPI(sctx, cfg.Namespace(), kubeconfig, cfg.Client())
|
|
defer sapi.Done(t)
|
|
|
|
_, err := StopWorkspaceFunc(true, sapi)
|
|
return err
|
|
}
|