Skip to content

Commit 7d00501

Browse files
sagor999roboquat
authored andcommitted
Add PVC support to prebuilds
1 parent 043e3c0 commit 7d00501

File tree

11 files changed

+111
-17
lines changed

11 files changed

+111
-17
lines changed

components/content-service/pkg/git/git.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,10 @@ func (c *Client) Fetch(ctx context.Context) (err error) {
341341
return c.Git(ctx, "fetch", "-p", "-P", "--tags", "-f")
342342
}
343343

344+
func (c *Client) AddSafeDirectory(ctx context.Context, dir string) (err error) {
345+
return c.Git(ctx, "config", "--global", "--add", "safe.directory", dir)
346+
}
347+
344348
// UpdateRemote performs a git fetch on the upstream remote URI
345349
func (c *Client) UpdateRemote(ctx context.Context) (err error) {
346350
//nolint:staticcheck,ineffassign

components/content-service/pkg/initializer/git.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,11 @@ func (ws *GitInitializer) Run(ctx context.Context, mappings []archive.IDMapping)
9696
b := backoff.NewExponentialBackOff()
9797
b.MaxElapsedTime = 5 * time.Minute
9898
if err = backoff.RetryNotify(gitClone, b, onGitCloneFailure); err != nil {
99-
return src, xerrors.Errorf("git initializer: %w", err)
99+
return src, xerrors.Errorf("git initializer gitClone: %w", err)
100+
}
101+
102+
if err := ws.AddSafeDirectory(ctx, ws.Location); err != nil {
103+
log.WithError(err).Warn("git initializer AddSafeDirectory")
100104
}
101105

102106
if ws.Chown {
@@ -115,10 +119,10 @@ func (ws *GitInitializer) Run(ctx context.Context, mappings []archive.IDMapping)
115119
}
116120
}
117121
if err := ws.realizeCloneTarget(ctx); err != nil {
118-
return src, xerrors.Errorf("git initializer: %w", err)
122+
return src, xerrors.Errorf("git initializer clone: %w", err)
119123
}
120124
if err := ws.UpdateRemote(ctx); err != nil {
121-
return src, xerrors.Errorf("git initializer: %w", err)
125+
return src, xerrors.Errorf("git initializer updateRemote: %w", err)
122126
}
123127
if err := ws.UpdateSubmodules(ctx); err != nil {
124128
log.WithError(err).Warn("error while updating submodules - continuing")

components/content-service/pkg/initializer/initializer.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,9 +265,10 @@ func newGitInitializer(ctx context.Context, loc string, req *csapi.GitInitialize
265265

266266
func newSnapshotInitializer(loc string, rs storage.DirectDownloader, req *csapi.SnapshotInitializer) (*SnapshotInitializer, error) {
267267
return &SnapshotInitializer{
268-
Location: loc,
269-
Snapshot: req.Snapshot,
270-
Storage: rs,
268+
Location: loc,
269+
Snapshot: req.Snapshot,
270+
Storage: rs,
271+
FromVolumeSnapshot: req.FromVolumeSnapshot,
271272
}, nil
272273
}
273274

components/content-service/pkg/initializer/snapshot.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/opentracing/opentracing-go"
1111
"golang.org/x/xerrors"
1212

13+
"github.com/gitpod-io/gitpod/common-go/log"
1314
"github.com/gitpod-io/gitpod/common-go/tracing"
1415
csapi "github.com/gitpod-io/gitpod/content-service/api"
1516
"github.com/gitpod-io/gitpod/content-service/pkg/archive"
@@ -18,9 +19,10 @@ import (
1819

1920
// SnapshotInitializer downloads a snapshot from a remote storage
2021
type SnapshotInitializer struct {
21-
Location string
22-
Snapshot string
23-
Storage storage.DirectDownloader
22+
Location string
23+
Snapshot string
24+
Storage storage.DirectDownloader
25+
FromVolumeSnapshot bool
2426
}
2527

2628
// Run downloads a snapshot from a remote storage
@@ -32,6 +34,11 @@ func (s *SnapshotInitializer) Run(ctx context.Context, mappings []archive.IDMapp
3234

3335
src = csapi.WorkspaceInitFromBackup
3436

37+
if s.FromVolumeSnapshot {
38+
log.Info("SnapshotInitializer detected volume snapshot, skipping")
39+
return src, nil
40+
}
41+
3542
ok, err := s.Storage.DownloadSnapshot(ctx, s.Location, s.Snapshot, mappings)
3643
if err != nil {
3744
return src, xerrors.Errorf("snapshot initializer: %w", err)

components/content-service/pkg/layer/provider.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,9 +282,27 @@ func (s *Provider) GetContentLayerPVC(ctx context.Context, owner, workspaceID st
282282
// At this point we've found neither a full-workspace-backup, nor a legacy backup.
283283
// It's time to use the initializer.
284284
if gis := initializer.GetSnapshot(); gis != nil {
285+
if gis.FromVolumeSnapshot {
286+
layer, err = contentDescriptorToLayerPVC([]byte{})
287+
if err != nil {
288+
return nil, nil, err
289+
}
290+
291+
l = []Layer{*layer}
292+
return l, manifest, nil
293+
}
285294
return s.getSnapshotContentLayer(ctx, gis)
286295
}
287296
if pis := initializer.GetPrebuild(); pis != nil {
297+
if pis.Prebuild.FromVolumeSnapshot {
298+
layer, err = contentDescriptorToLayerPVC([]byte{})
299+
if err != nil {
300+
return nil, nil, err
301+
}
302+
303+
l = []Layer{*layer}
304+
return l, manifest, nil
305+
}
288306
l, manifest, err = s.getPrebuildContentLayer(ctx, pis)
289307
if err != nil {
290308
log.WithError(err).WithFields(log.OWI(owner, workspaceID, "")).Warn("cannot initialize from prebuild - falling back to Git")
@@ -481,6 +499,7 @@ git config --global --add safe.directory ${GITPOD_REPO_ROOT}
481499
git status --porcelain=v2 --branch -uall > /.workspace/prestophookdata/git_status.txt
482500
git log --pretty='%h: %s' --branches --not --remotes > /.workspace/prestophookdata/git_log_1.txt
483501
git log --pretty=%H -n 1 > /.workspace/prestophookdata/git_log_2.txt
502+
cp /workspace/.gitpod/prebuild-log* /.workspace/prestophookdata/
484503
`
485504

486505
// version of this function for persistent volume claim feature

components/content-service/pkg/logs/logs.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,17 @@ func ListPrebuildLogFiles(ctx context.Context, location string) (filePaths []str
6262
}
6363
return logFiles, nil
6464
}
65-
filePaths, err = listLogFiles(strings.TrimPrefix(TerminalStoreLocation, "/workspace"), prebuildLogFilePrefix)
65+
// list log files in `location` first
66+
filePaths, err = listLogFiles("", prebuildLogFilePrefix)
6667
if err != nil {
6768
return nil, err
6869
}
70+
if len(filePaths) == 0 {
71+
filePaths, err = listLogFiles(strings.TrimPrefix(TerminalStoreLocation, "/workspace"), prebuildLogFilePrefix)
72+
if err != nil {
73+
return nil, err
74+
}
75+
}
6976
if len(filePaths) == 0 {
7077
filePaths, err = listLogFiles(strings.TrimPrefix(legacyTerminalStoreLocation, "/workspace"), legacyPrebuildLogFilePrefix)
7178
if err != nil {

components/server/ee/src/prebuilds/prebuild-manager.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,10 @@ export class PrebuildManager {
203203
} else {
204204
span.setTag("starting", true);
205205
const projectEnvVars = await projectEnvVarsPromise;
206+
const usePVC = this.shouldUsePersistentVolumeClaim(project);
206207
await this.workspaceStarter.startWorkspace({ span }, workspace, user, [], projectEnvVars, {
207208
excludeFeatureFlags: ["full_workspace_backup"],
209+
forcePVC: usePVC,
208210
});
209211
}
210212

@@ -274,6 +276,13 @@ export class PrebuildManager {
274276
return this.config.incrementalPrebuilds.repositoryPasslist.some((url) => trimRepoUrl(url) === repoUrl);
275277
}
276278

279+
protected shouldUsePersistentVolumeClaim(project?: Project): boolean {
280+
if (project?.settings?.usePersistentVolumeClaim) {
281+
return true;
282+
}
283+
return false;
284+
}
285+
277286
async fetchConfig(ctx: TraceContext, user: User, context: CommitContext): Promise<WorkspaceConfig> {
278287
const span = TraceContext.startSpan("fetchConfig", ctx);
279288
try {

components/server/ee/src/workspace/workspace-starter.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ export class WorkspaceStarterEE extends WorkspaceStarter {
3232
user: User,
3333
excludeFeatureFlags: NamedWorkspaceFeatureFlag[],
3434
ideConfig: IDEConfig,
35+
forcePVC: boolean,
3536
): Promise<WorkspaceInstance> {
36-
const instance = await super.newInstance(ctx, workspace, user, excludeFeatureFlags, ideConfig);
37+
const instance = await super.newInstance(ctx, workspace, user, excludeFeatureFlags, ideConfig, forcePVC);
3738
if (await this.eligibilityService.hasFixedWorkspaceResources(user)) {
3839
const config: WorkspaceInstanceConfiguration = instance.configuration!;
3940
const ff = config.featureFlags || [];

components/server/src/workspace/workspace-starter.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ export interface StartWorkspaceOptions {
116116
rethrow?: boolean;
117117
forceDefaultImage?: boolean;
118118
excludeFeatureFlags?: NamedWorkspaceFeatureFlag[];
119+
forcePVC?: boolean;
119120
}
120121

121122
const MAX_INSTANCE_START_RETRIES = 2;
@@ -275,7 +276,14 @@ export class WorkspaceStarter {
275276
let instance = await this.workspaceDb
276277
.trace({ span })
277278
.storeInstance(
278-
await this.newInstance(ctx, workspace, user, options.excludeFeatureFlags || [], ideConfig),
279+
await this.newInstance(
280+
ctx,
281+
workspace,
282+
user,
283+
options.excludeFeatureFlags || [],
284+
ideConfig,
285+
options.forcePVC || false,
286+
),
279287
);
280288
span.log({ newInstance: instance.id });
281289

@@ -640,6 +648,7 @@ export class WorkspaceStarter {
640648
user: User,
641649
excludeFeatureFlags: NamedWorkspaceFeatureFlag[],
642650
ideConfig: IDEConfig,
651+
forcePVC: boolean,
643652
): Promise<WorkspaceInstance> {
644653
//#endregion IDE resolution TODO(ak) move to IDE service
645654
// TODO: Compatible with ide-config not deployed, need revert after ide-config deployed
@@ -716,6 +725,10 @@ export class WorkspaceStarter {
716725

717726
featureFlags = featureFlags.filter((f) => !excludeFeatureFlags.includes(f));
718727

728+
if (forcePVC === true) {
729+
featureFlags = featureFlags.concat(["persistent_volume_claim"]);
730+
}
731+
719732
if (!!featureFlags) {
720733
// only set feature flags if there actually are any. Otherwise we waste the
721734
// few bytes of JSON in the database for no good reason.
@@ -1283,10 +1296,13 @@ export class WorkspaceStarter {
12831296
}
12841297
}
12851298

1299+
let volumeSnapshotId = lastValidWorkspaceInstanceId;
1300+
// if this is snapshot or prebuild context, then try to find volume snapshot id in it
1301+
if (SnapshotContext.is(workspace.context) || WithPrebuild.is(workspace.context)) {
1302+
volumeSnapshotId = workspace.context.snapshotBucketId;
1303+
}
12861304
let volumeSnapshotInfo = new VolumeSnapshotInfo();
1287-
const volumeSnapshots = await this.workspaceDb
1288-
.trace(traceCtx)
1289-
.findVolumeSnapshotById(lastValidWorkspaceInstanceId);
1305+
const volumeSnapshots = await this.workspaceDb.trace(traceCtx).findVolumeSnapshotById(volumeSnapshotId);
12901306
if (volumeSnapshots !== undefined) {
12911307
volumeSnapshotInfo.setVolumeSnapshotName(volumeSnapshots.id);
12921308
volumeSnapshotInfo.setVolumeSnapshotHandle(volumeSnapshots.volumeHandle);
@@ -1302,7 +1318,10 @@ export class WorkspaceStarter {
13021318
);
13031319
const userTimeoutPromise = this.userService.getDefaultWorkspaceTimeout(user);
13041320

1305-
const featureFlags = instance.configuration!.featureFlags || [];
1321+
let featureFlags = instance.configuration!.featureFlags || [];
1322+
if (volumeSnapshots !== undefined) {
1323+
featureFlags = featureFlags.concat(["persistent_volume_claim"]);
1324+
}
13061325

13071326
let ideImage: string;
13081327
if (!!instance.configuration?.ideImage) {
@@ -1470,6 +1489,7 @@ export class WorkspaceStarter {
14701489
} else if (SnapshotContext.is(context)) {
14711490
const snapshot = new SnapshotInitializer();
14721491
snapshot.setSnapshot(context.snapshotBucketId);
1492+
snapshot.setFromVolumeSnapshot(hasVolumeSnapshot);
14731493
result.setSnapshot(snapshot);
14741494
} else if (WithPrebuild.is(context)) {
14751495
if (!CommitContext.is(context)) {
@@ -1478,6 +1498,7 @@ export class WorkspaceStarter {
14781498

14791499
const snapshot = new SnapshotInitializer();
14801500
snapshot.setSnapshot(context.snapshotBucketId);
1501+
snapshot.setFromVolumeSnapshot(hasVolumeSnapshot);
14811502
const { initializer } = await this.createCommitInitializer(traceCtx, workspace, context, user);
14821503
const init = new PrebuildInitializer();
14831504
init.setPrebuild(snapshot);

components/ws-daemon/pkg/content/service.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,8 +658,12 @@ func (s *WorkspaceService) uploadWorkspaceLogs(ctx context.Context, sess *sessio
658658
return xerrors.Errorf("no remote storage configured")
659659
}
660660

661+
logLocation := sess.Location
662+
if sess.PersistentVolumeClaim {
663+
logLocation = filepath.Join(sess.ServiceLocDaemon, "prestophookdata")
664+
}
661665
// currently we're only uploading prebuild log files
662-
logFiles, err := logs.ListPrebuildLogFiles(ctx, sess.Location)
666+
logFiles, err := logs.ListPrebuildLogFiles(ctx, logLocation)
663667
if err != nil {
664668
return err
665669
}

components/ws-manager/pkg/manager/monitor.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,9 +1103,26 @@ func (m *Monitor) finalizeWorkspaceContent(ctx context.Context, wso *workspaceOb
11031103
err = m.manager.markWorkspace(context.Background(), workspaceID, addMark(pvcWorkspaceVolumeSnapshotAnnotation, string(b)))
11041104
if err != nil {
11051105
log.WithError(err).Error("cannot mark workspace with volume snapshot name - snapshot will be orphaned and backup lost")
1106+
errMark := m.manager.markWorkspace(ctx, workspaceID, addMark(workspaceExplicitFailAnnotation, xerrors.Errorf("cannot add mark to save snapshot info: %v", err).Error()))
1107+
if errMark != nil {
1108+
log.WithError(errMark).Warn("was unable to mark workspace as failed")
1109+
}
11061110
return true, nil, err
11071111
}
11081112

1113+
if tpe == api.WorkspaceType_PREBUILD {
1114+
err = m.manager.markWorkspace(context.Background(), workspaceID, addMark(workspaceSnapshotAnnotation, pvcVolumeSnapshotName))
1115+
if err != nil {
1116+
tracing.LogError(span, err)
1117+
log.WithError(err).Warn("cannot mark headless workspace with snapshot - that's one prebuild lost")
1118+
errMark := m.manager.markWorkspace(ctx, workspaceID, addMark(workspaceExplicitFailAnnotation, xerrors.Errorf("cannot add mark for prebuild snapshot info: %v", err).Error()))
1119+
if errMark != nil {
1120+
log.WithError(errMark).Warn("was unable to mark workspace as failed")
1121+
}
1122+
return true, nil, err
1123+
}
1124+
}
1125+
11091126
markVolumeSnapshotAnnotation = true
11101127
}
11111128

0 commit comments

Comments
 (0)