Skip to content

Commit

Permalink
tests: Stop and Wait workspace at the end of each tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
utam0k committed Sep 1, 2022
1 parent 2368146 commit 85ebebd
Show file tree
Hide file tree
Showing 11 changed files with 143 additions and 109 deletions.
9 changes: 5 additions & 4 deletions test/pkg/integration/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,20 +187,21 @@ func Instrument(component ComponentType, agentName string, namespace string, kub
}

execErrs := make(chan error, 1)
go func() {
execF := func() {
defer close(execErrs)
_, _, _, execErr := podExec.ExecCmd(cmd, podName, namespace, containerName)
if execErr != nil {
execErrs <- execErr
}
}()
}
go execF()
select {
case err := <-execErrs:
if err != nil {
return nil, closer, err
}
return nil, closer, fmt.Errorf("agent stopped unexepectedly")
case <-time.After(1 * time.Second):
case <-time.After(30 * time.Second):
}

ctx, cancel := context.WithCancel(context.Background())
Expand Down Expand Up @@ -230,7 +231,7 @@ func Instrument(component ComponentType, agentName string, namespace string, kub

var res *rpc.Client
var lastError error
waitErr := wait.PollImmediate(5*time.Second, 2*time.Minute, func() (bool, error) {
waitErr := wait.PollImmediate(5*time.Second, 3*time.Minute, func() (bool, error) {
res, lastError = rpc.DialHTTP("tcp", fmt.Sprintf("localhost:%d", localAgentPort))
if lastError != nil {
return false, nil
Expand Down
84 changes: 56 additions & 28 deletions test/pkg/integration/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,14 @@ package integration

import (
"context"
"fmt"
"io"
"strings"
"sync"
"time"

"github.com/google/uuid"
"golang.org/x/xerrors"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"

"github.com/gitpod-io/gitpod/common-go/namegen"
csapi "github.com/gitpod-io/gitpod/content-service/api"
Expand All @@ -28,7 +24,7 @@ import (

const (
gitpodBuiltinUserID = "builtin-user-workspace-probe-0000000"
perCallTimeout = 1 * time.Minute
perCallTimeout = 3 * time.Minute
)

type launchWorkspaceDirectlyOptions struct {
Expand Down Expand Up @@ -97,48 +93,53 @@ type LaunchWorkspaceDirectlyResult struct {
// LaunchWorkspaceDirectly starts a workspace pod by talking directly to ws-manager.
// Whenever possible prefer this function over LaunchWorkspaceFromContextURL, because
// it has fewer prerequisites.
func LaunchWorkspaceDirectly(ctx context.Context, api *ComponentAPI, opts ...LaunchWorkspaceDirectlyOpt) (*LaunchWorkspaceDirectlyResult, error) {
func LaunchWorkspaceDirectly(ctx context.Context, api *ComponentAPI, opts ...LaunchWorkspaceDirectlyOpt) (*LaunchWorkspaceDirectlyResult, func(waitForStop bool) error, error) {
options := launchWorkspaceDirectlyOptions{
BaseImage: "docker.io/gitpod/workspace-full:latest",
}
for _, o := range opts {
err := o(&options)
if err != nil {
return nil, err
return nil, nil, err
}
}

instanceID, err := uuid.NewRandom()
if err != nil {
return nil, err
return nil, nil, err

}
workspaceID, err := namegen.GenerateWorkspaceID()
if err != nil {
return nil, err

return nil, nil, err
}

var workspaceImage string
if options.BaseImage != "" {
workspaceImage, err = resolveOrBuildImage(ctx, api, options.BaseImage)
if err != nil {
return nil, xerrors.Errorf("cannot resolve base image: %v", err)
for {
workspaceImage, err = resolveOrBuildImage(ctx, api, options.BaseImage)
if st, ok := status.FromError(err); ok && st.Code() == codes.Unavailable {
time.Sleep(5 * time.Second)
continue
} else if err != nil {
return nil, nil, xerrors.Errorf("cannot resolve base image: %v", err)
}
break
}
}
if workspaceImage == "" {
return nil, xerrors.Errorf("cannot start workspaces without a workspace image (required by registry-facade resolver)")
return nil, nil, xerrors.Errorf("cannot start workspaces without a workspace image (required by registry-facade resolver)")
}

ideImage := options.IdeImage
if ideImage == "" {
cfg, err := GetServerIDEConfig(api.namespace, api.client)
if err != nil {
return nil, xerrors.Errorf("cannot find server IDE config: %q", err)
return nil, nil, xerrors.Errorf("cannot find server IDE config: %q", err)
}
ideImage = cfg.IDEOptions.Options.Code.Image
if ideImage == "" {
return nil, xerrors.Errorf("cannot start workspaces without an IDE image (required by registry-facade resolver)")
return nil, nil, xerrors.Errorf("cannot start workspaces without an IDE image (required by registry-facade resolver)")
}
}

Expand Down Expand Up @@ -173,7 +174,7 @@ func LaunchWorkspaceDirectly(ctx context.Context, api *ComponentAPI, opts ...Lau
for _, m := range options.Mods {
err := m(req)
if err != nil {
return nil, err
return nil, nil, err
}
}

Expand All @@ -182,26 +183,56 @@ func LaunchWorkspaceDirectly(ctx context.Context, api *ComponentAPI, opts ...Lau

wsm, err := api.WorkspaceManager()
if err != nil {
return nil, xerrors.Errorf("cannot start workspace manager: %q", err)
return nil, nil, xerrors.Errorf("cannot start workspace manager: %q", err)
}

sresp, err := wsm.StartWorkspace(sctx, req)
if err != nil {
return nil, xerrors.Errorf("cannot start workspace: %q", err)
return nil, nil, xerrors.Errorf("cannot start workspace: %q", err)
}

stopWs := func(waitForStop bool) error {
tctx, tcancel := context.WithTimeout(context.Background(), perCallTimeout)
defer tcancel()

for {
err = DeleteWorkspace(tctx, api, req.Id)
if st, ok := status.FromError(err); ok && st.Code() == codes.Unavailable {
time.Sleep(5 * time.Second)
continue
} else if err != nil {
return err
}
break
}
for {
_, err = WaitForWorkspaceStop(tctx, api, req.Id)
if st, ok := status.FromError(err); ok && st.Code() == codes.Unavailable {
time.Sleep(5 * time.Second)
continue
} else if err != nil {
return err
}
break
}
return err
}
defer func() {
if err != nil {
stopWs(false)
}
}()

lastStatus, err := WaitForWorkspaceStart(ctx, instanceID.String(), api, options.WaitForOpts...)
if err != nil {
return nil, xerrors.Errorf("cannot wait for workspace start: %q", err)
return nil, nil, xerrors.Errorf("cannot wait for workspace start: %q", err)
}

// it.t.Logf("workspace is running: instanceID=%s", instanceID.String())

return &LaunchWorkspaceDirectlyResult{
Req: req,
IdeURL: sresp.Url,
LastStatus: lastStatus,
}, nil
}, stopWs, nil
}

// LaunchWorkspaceFromContextURL force-creates a new workspace using the Gitpod server API,
Expand Down Expand Up @@ -246,9 +277,6 @@ func LaunchWorkspaceFromContextURL(ctx context.Context, contextURL string, usern
sctx, scancel := context.WithTimeout(ctx, perCallTimeout)
_ = server.StopWorkspace(sctx, resp.CreatedWorkspaceID)
scancel()
//if err != nil {
//it.t.Errorf("cannot stop workspace: %q", err)
//}

if waitForStop {
_, _ = WaitForWorkspaceStop(ctx, api, nfo.LatestInstance.ID)
Expand Down Expand Up @@ -413,7 +441,6 @@ func WaitForWorkspaceStop(ctx context.Context, api *ComponentAPI, instanceID str
_ = sub.CloseSend()
}()

var workspaceID string
done := make(chan struct{})
errCh := make(chan error)
go func() {
Expand All @@ -432,7 +459,6 @@ func WaitForWorkspaceStop(ctx context.Context, api *ComponentAPI, instanceID str
continue
}

workspaceID = status.Metadata.MetaId
if status.Conditions.Failed != "" {
errCh <- xerrors.Errorf("workspace instance %s failed: %s", instanceID, status.Conditions.Failed)
return
Expand All @@ -441,6 +467,8 @@ func WaitForWorkspaceStop(ctx context.Context, api *ComponentAPI, instanceID str
lastStatus = status
return
}

time.Sleep(10 * time.Second)
}
}()

Expand Down
66 changes: 33 additions & 33 deletions test/tests/components/ws-manager/content_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,29 @@ import (
func TestBackup(t *testing.T) {
f := features.New("backup").
Assess("it should start a workspace, create a file and successfully create a backup", func(_ context.Context, t *testing.T, cfg *envconf.Config) context.Context {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()

api := integration.NewComponentAPI(ctx, cfg.Namespace(), kubeconfig, cfg.Client())
t.Cleanup(func() {
api.Done(t)
})

wsm, err := api.WorkspaceManager()
if err != nil {
t.Fatal(err)
}

ws, err := integration.LaunchWorkspaceDirectly(ctx, api)
ws1, stopWs1, err := integration.LaunchWorkspaceDirectly(ctx, api)
if err != nil {
t.Fatal(err)
}

rsa, closer, err := integration.Instrument(integration.ComponentWorkspace, "workspace", cfg.Namespace(), kubeconfig, cfg.Client(),
integration.WithInstanceID(ws.Req.Id),
integration.WithInstanceID(ws1.Req.Id),
integration.WithContainer("workspace"),
integration.WithWorkspacekitLift(true),
)
if err != nil {
err = stopWs1(true)
if err != nil {
t.Errorf("cannot stop workspace: %q", err)
}
t.Fatal(err)
}
integration.DeferCloser(t, closer)
Expand All @@ -57,53 +56,46 @@ func TestBackup(t *testing.T) {
Content: []byte("hello world"),
Mode: 0644,
}, &resp)
if err != nil {
_, _ = wsm.StopWorkspace(ctx, &wsapi.StopWorkspaceRequest{Id: ws.Req.Id})
t.Fatal(err)
}
rsa.Close()

_, err = wsm.StopWorkspace(ctx, &wsapi.StopWorkspaceRequest{
Id: ws.Req.Id,
})
if err != nil {
err = stopWs1(true)
if err != nil {
t.Errorf("cannot stop workspace: %q", err)
}
t.Fatal(err)
}

_, err = integration.WaitForWorkspaceStop(ctx, api, ws.Req.Id)
err = stopWs1(true)
if err != nil {
t.Fatal(err)
}

ws, err = integration.LaunchWorkspaceDirectly(ctx, api,
ws2, stopWs2, err := integration.LaunchWorkspaceDirectly(ctx, api,
integration.WithRequestModifier(func(w *wsapi.StartWorkspaceRequest) error {
w.ServicePrefix = ws.Req.ServicePrefix
w.Metadata.MetaId = ws.Req.Metadata.MetaId
w.Metadata.Owner = ws.Req.Metadata.Owner
w.ServicePrefix = ws1.Req.ServicePrefix
w.Metadata.MetaId = ws1.Req.Metadata.MetaId
w.Metadata.Owner = ws1.Req.Metadata.Owner
return nil
}),
)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
err = stopWs2(true)
if 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),
integration.WithInstanceID(ws2.Req.Id),
)
if err != nil {
t.Fatal(err)
}
integration.DeferCloser(t, closer)

defer func() {
t.Log("Cleaning up on TestBackup exit")
sctx, scancel := context.WithTimeout(ctx, 5*time.Second)
defer scancel()
_, _ = wsm.StopWorkspace(sctx, &wsapi.StopWorkspaceRequest{
Id: ws.Req.Id,
})
}()

var ls agent.ListDirResponse
err = rsa.Call("WorkspaceAgent.ListDir", &agent.ListDirRequest{
Dir: "/workspace",
Expand Down Expand Up @@ -146,13 +138,17 @@ func TestMissingBackup(t *testing.T) {
api.Done(t)
})

ws, err := integration.LaunchWorkspaceDirectly(ctx, api)
ws, stopWs, err := integration.LaunchWorkspaceDirectly(ctx, api)
if err != nil {
t.Fatal(err)
}

wsm, err := api.WorkspaceManager()
if err != nil {
err = stopWs(true)
if err != nil {
t.Errorf("cannot stop workspace: %q", err)
}
t.Fatal(err)
}

Expand Down Expand Up @@ -189,7 +185,7 @@ func TestMissingBackup(t *testing.T) {
}
for _, test := range tests {
t.Run(test.Name+"_backup_init", func(t *testing.T) {
testws, err := integration.LaunchWorkspaceDirectly(ctx, api, integration.WithRequestModifier(func(w *wsapi.StartWorkspaceRequest) error {
testws, stopWs, err := integration.LaunchWorkspaceDirectly(ctx, api, integration.WithRequestModifier(func(w *wsapi.StartWorkspaceRequest) error {
w.ServicePrefix = ws.Req.ServicePrefix
w.Metadata.MetaId = ws.Req.Metadata.MetaId
w.Metadata.Owner = ws.Req.Metadata.Owner
Expand All @@ -210,6 +206,10 @@ func TestMissingBackup(t *testing.T) {
return
}
if testws.LastStatus.Conditions.Failed == "" {
err = stopWs(true)
if err != nil {
t.Errorf("cannot stop workspace: %q", err)
}
t.Errorf("restarted workspace did not fail despite missing backup, %v", testws)
}
})
Expand Down
Loading

0 comments on commit 85ebebd

Please sign in to comment.