Skip to content

Commit b879279

Browse files
svenefftingeroboquat
authored andcommitted
[content-service] incremental workspace init
if we initialize the workspace from an outdated prebuild, we need the regular workspace tasks (including init) should run.
1 parent 1956748 commit b879279

File tree

2 files changed

+178
-5
lines changed

2 files changed

+178
-5
lines changed

Diff for: components/content-service/pkg/initializer/prebuild.go

+22-5
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,20 @@ func (p *PrebuildInitializer) Run(ctx context.Context, mappings []archive.IDMapp
8181

8282
// at this point we're actually a prebuild initialiser because we've been able to restore
8383
// the prebuild.
84+
8485
src = csapi.WorkspaceInitFromPrebuild
8586

8687
// make sure we're on the correct branch
8788
for _, gi := range p.Git {
88-
err = runGitInit(ctx, gi)
89+
90+
commitChanged, err := runGitInit(ctx, gi)
8991
if err != nil {
9092
return src, err
9193
}
94+
if commitChanged {
95+
// head commit has changed, so it's an outdated prebuild, which we treat as other
96+
src = csapi.WorkspaceInitFromOther
97+
}
9298
}
9399
log.Debug("Initialized workspace with prebuilt snapshot")
94100
return
@@ -108,7 +114,7 @@ func clearWorkspace(location string) error {
108114
return nil
109115
}
110116

111-
func runGitInit(ctx context.Context, gInit *GitInitializer) (err error) {
117+
func runGitInit(ctx context.Context, gInit *GitInitializer) (commitChanged bool, err error) {
112118
span, ctx := opentracing.StartSpanFromContext(ctx, "runGitInit")
113119
span.LogFields(
114120
tracelog.String("IsWorkingCopy", fmt.Sprintf("%v", git.IsWorkingCopy(gInit.Location))),
@@ -125,14 +131,25 @@ func runGitInit(ctx context.Context, gInit *GitInitializer) (err error) {
125131
} else {
126132
// git returned a non-zero exit code because of some reason we did not anticipate or an actual failure.
127133
log.WithError(err).WithField("output", string(out)).Error("unexpected git stash error")
128-
return xerrors.Errorf("prebuild initializer: %w", err)
134+
return commitChanged, xerrors.Errorf("prebuild initializer: %w", err)
129135
}
130136
}
131137
didStash := !strings.Contains(string(out), "No local changes to save")
132138

139+
statusBefore, err := gInit.Status(ctx)
140+
if err != nil {
141+
log.WithError(err).Warn("couldn't run git status - continuing")
142+
}
133143
err = checkGitStatus(gInit.realizeCloneTarget(ctx))
134144
if err != nil {
135-
return xerrors.Errorf("prebuild initializer: %w", err)
145+
return commitChanged, xerrors.Errorf("prebuild initializer: %w", err)
146+
}
147+
statusAfter, err := gInit.Status(ctx)
148+
if err != nil {
149+
log.WithError(err).Warn("couldn't run git status - continuing")
150+
}
151+
if statusBefore != nil && statusAfter != nil {
152+
commitChanged = statusBefore.LatestCommit != statusAfter.LatestCommit
136153
}
137154

138155
err = gInit.UpdateSubmodules(ctx)
@@ -173,5 +190,5 @@ func runGitInit(ctx context.Context, gInit *GitInitializer) (err error) {
173190
return
174191
}
175192
}()
176-
return nil
193+
return commitChanged, nil
177194
}

Diff for: test/tests/components/ws-manager/prebuild_test.go

+156
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,162 @@ func TestOpenWorkspaceFromPrebuild(t *testing.T) {
492492
testEnv.Test(t, f)
493493
}
494494

495+
// TestOpenWorkspaceFromOutdatedPrebuild
496+
// - create a prebuild on older commit
497+
// - open a workspace from a later commit with a prebuild initializer
498+
// - 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)
499+
func TestOpenWorkspaceFromOutdatedPrebuild(t *testing.T) {
500+
501+
f := features.New("prebuild").
502+
WithLabel("component", "ws-manager").
503+
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 {
504+
tests := []struct {
505+
Name string
506+
RemoteUri string
507+
CloneTargetForPrebuild string
508+
CloneTargetForWorkspace string
509+
WorkspaceRoot string
510+
CheckoutLocation string
511+
FF []wsmanapi.WorkspaceFeatureFlag
512+
}{
513+
{
514+
Name: "classic",
515+
RemoteUri: "https://github.com/gitpod-io/test-incremental-workspace",
516+
CloneTargetForPrebuild: "prebuild",
517+
CloneTargetForWorkspace: "main",
518+
CheckoutLocation: "test-incremental-workspace",
519+
WorkspaceRoot: "/workspace/test-incremental-workspace",
520+
},
521+
}
522+
523+
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(10*len(tests))*time.Minute)
524+
defer cancel()
525+
526+
for _, test := range tests {
527+
t.Run(test.Name, func(t *testing.T) {
528+
529+
api := integration.NewComponentAPI(ctx, cfg.Namespace(), kubeconfig, cfg.Client())
530+
t.Cleanup(func() {
531+
api.Done(t)
532+
})
533+
534+
// create a prebuild
535+
ws, prebuildStopWs, err := integration.LaunchWorkspaceDirectly(t, ctx, api, integration.WithRequestModifier(func(req *wsmanapi.StartWorkspaceRequest) error {
536+
req.Type = wsmanapi.WorkspaceType_PREBUILD
537+
req.Spec.Envvars = append(req.Spec.Envvars, &wsmanapi.EnvironmentVariable{
538+
Name: "GITPOD_TASKS",
539+
Value: `[{ "init": "./init.sh" }]`,
540+
})
541+
req.Spec.FeatureFlags = test.FF
542+
req.Spec.Initializer = &csapi.WorkspaceInitializer{
543+
Spec: &csapi.WorkspaceInitializer_Git{
544+
Git: &csapi.GitInitializer{
545+
RemoteUri: test.RemoteUri,
546+
TargetMode: csapi.CloneTargetMode_REMOTE_BRANCH,
547+
CloneTaget: test.CloneTargetForPrebuild,
548+
CheckoutLocation: test.CheckoutLocation,
549+
Config: &csapi.GitConfig{},
550+
},
551+
},
552+
}
553+
req.Spec.WorkspaceLocation = test.CheckoutLocation
554+
return nil
555+
}))
556+
if err != nil {
557+
t.Fatalf("cannot launch a workspace: %q", err)
558+
}
559+
defer func() {
560+
// stop workspace in defer function to prevent we forget to stop the workspace
561+
if err := stopWorkspace(t, cfg, prebuildStopWs); err != nil {
562+
t.Errorf("cannot stop workspace: %q", err)
563+
}
564+
}()
565+
prebuildSnapshot, prebuildVSInfo, err := watchStopWorkspaceAndFindSnapshot(ctx, ws.Req.Id, api)
566+
if err != nil {
567+
t.Fatalf("stop workspace and find snapshot error: %v", err)
568+
}
569+
570+
t.Logf("prebuild snapshot: %s", prebuildSnapshot)
571+
572+
// launch the workspace on a later commit using this prebuild
573+
ws, stopWs, err := integration.LaunchWorkspaceDirectly(t, ctx, api, integration.WithRequestModifier(func(req *wsmanapi.StartWorkspaceRequest) error {
574+
req.Spec.Envvars = append(req.Spec.Envvars, &wsmanapi.EnvironmentVariable{
575+
Name: "GITPOD_TASKS",
576+
Value: `[{ "init": "./init.sh" }]`,
577+
})
578+
req.Spec.FeatureFlags = test.FF
579+
req.Spec.Initializer = &csapi.WorkspaceInitializer{
580+
Spec: &csapi.WorkspaceInitializer_Prebuild{
581+
Prebuild: &csapi.PrebuildInitializer{
582+
Prebuild: &csapi.SnapshotInitializer{
583+
Snapshot: prebuildSnapshot,
584+
},
585+
Git: []*csapi.GitInitializer{
586+
{
587+
RemoteUri: test.RemoteUri,
588+
TargetMode: csapi.CloneTargetMode_REMOTE_BRANCH,
589+
CloneTaget: test.CloneTargetForWorkspace,
590+
CheckoutLocation: test.CheckoutLocation,
591+
Config: &csapi.GitConfig{},
592+
},
593+
},
594+
},
595+
},
596+
}
597+
req.Spec.VolumeSnapshot = prebuildVSInfo
598+
req.Spec.WorkspaceLocation = test.CheckoutLocation
599+
return nil
600+
}))
601+
if err != nil {
602+
t.Fatalf("cannot launch a workspace: %q", err)
603+
}
604+
605+
defer func() {
606+
// stop workspace in defer function to prevent we forget to stop the workspace
607+
if err := stopWorkspace(t, cfg, stopWs); err != nil {
608+
t.Errorf("cannot stop workspace: %q", err)
609+
}
610+
}()
611+
612+
rsa, closer, err := integration.Instrument(integration.ComponentWorkspace, "workspace", cfg.Namespace(), kubeconfig, cfg.Client(),
613+
integration.WithInstanceID(ws.Req.Id),
614+
)
615+
if err != nil {
616+
t.Fatal(err)
617+
}
618+
t.Cleanup(func() {
619+
rsa.Close()
620+
})
621+
integration.DeferCloser(t, closer)
622+
623+
var ls agent.ListDirResponse
624+
err = rsa.Call("WorkspaceAgent.ListDir", &agent.ListDirRequest{
625+
Dir: test.WorkspaceRoot,
626+
}, &ls)
627+
if err != nil {
628+
t.Fatal(err)
629+
}
630+
rsa.Close()
631+
632+
var found bool
633+
for _, f := range ls.Files {
634+
t.Logf("file: %s", f)
635+
if filepath.Base(f) == "incremental.txt" {
636+
found = true
637+
}
638+
}
639+
if !found {
640+
t.Fatal("did not find incremental.txt")
641+
}
642+
})
643+
}
644+
return ctx
645+
}).
646+
Feature()
647+
648+
testEnv.Test(t, f)
649+
}
650+
495651
// TestPrebuildAndRegularWorkspaceDifferentWorkspaceClass
496652
// - create a prebuild with small workspace class (20Gi disk)
497653
// - create the workspace from prebulid with large workspace class (30Gi disk)

0 commit comments

Comments
 (0)