@@ -7,6 +7,10 @@ package wsmanager
7
7
import (
8
8
"context"
9
9
"encoding/json"
10
+ "fmt"
11
+ "path/filepath"
12
+ "reflect"
13
+ "strings"
10
14
"testing"
11
15
"time"
12
16
@@ -15,6 +19,7 @@ import (
15
19
16
20
csapi "github.com/gitpod-io/gitpod/content-service/api"
17
21
gitpod "github.com/gitpod-io/gitpod/gitpod-protocol"
22
+ agent "github.com/gitpod-io/gitpod/test/pkg/agent/workspace/api"
18
23
"github.com/gitpod-io/gitpod/test/pkg/integration"
19
24
wsmanapi "github.com/gitpod-io/gitpod/ws-manager/api"
20
25
)
@@ -171,3 +176,257 @@ func TestPrebuildWorkspaceTaskFail(t *testing.T) {
171
176
172
177
testEnv .Test (t , f )
173
178
}
179
+
180
+ const (
181
+ prebuildLogPath string = "/workspace/.gitpod"
182
+ prebuildLog string = "'🤙 This task ran as a workspace prebuild'"
183
+ )
184
+
185
+ func TestOpenWorkspaceFromPrebuild (t * testing.T ) {
186
+ f := features .New ("prebuild" ).
187
+ WithLabel ("component" , "ws-manager" ).
188
+ Assess ("it should open workspace from prebuild successfully" , func (_ context.Context , t * testing.T , cfg * envconf.Config ) context.Context {
189
+ tests := []struct {
190
+ Name string
191
+ ContextURL string
192
+ WorkspaceRoot string
193
+ CheckoutLocation string
194
+ FF []wsmanapi.WorkspaceFeatureFlag
195
+ }{
196
+ {
197
+ Name : "classic" ,
198
+ ContextURL : "https://github.com/gitpod-io/empty" ,
199
+ CheckoutLocation : "empty" ,
200
+ WorkspaceRoot : "/workspace/empty" ,
201
+ },
202
+ {
203
+ Name : "pvc" ,
204
+ ContextURL : "https://github.com/gitpod-io/empty" ,
205
+ CheckoutLocation : "empty" ,
206
+ WorkspaceRoot : "/workspace/empty" ,
207
+ FF : []wsmanapi.WorkspaceFeatureFlag {wsmanapi .WorkspaceFeatureFlag_PERSISTENT_VOLUME_CLAIM },
208
+ },
209
+ }
210
+
211
+ ctx , cancel := context .WithTimeout (context .Background (), time .Duration (10 * len (tests ))* time .Minute )
212
+ defer cancel ()
213
+
214
+ for _ , test := range tests {
215
+ t .Run (test .Name , func (t * testing.T ) {
216
+ api := integration .NewComponentAPI (ctx , cfg .Namespace (), kubeconfig , cfg .Client ())
217
+ t .Cleanup (func () {
218
+ api .Done (t )
219
+ })
220
+
221
+ // create a prebuild and stop workspace
222
+ _ , prebuildStopWs , err := integration .LaunchWorkspaceDirectly (t , ctx , api , integration .WithRequestModifier (func (req * wsmanapi.StartWorkspaceRequest ) error {
223
+ req .Type = wsmanapi .WorkspaceType_PREBUILD
224
+ req .Spec .Envvars = append (req .Spec .Envvars , & wsmanapi.EnvironmentVariable {
225
+ Name : "GITPOD_TASKS" ,
226
+ Value : `[{ "init": "echo \"some output\" > someFile; sleep 60; exit 0;" }]` ,
227
+ })
228
+ req .Spec .FeatureFlags = test .FF
229
+ req .Spec .Initializer = & csapi.WorkspaceInitializer {
230
+ Spec : & csapi.WorkspaceInitializer_Git {
231
+ Git : & csapi.GitInitializer {
232
+ RemoteUri : test .ContextURL ,
233
+ TargetMode : csapi .CloneTargetMode_REMOTE_BRANCH ,
234
+ CloneTaget : "main" ,
235
+ CheckoutLocation : test .CheckoutLocation ,
236
+ Config : & csapi.GitConfig {},
237
+ },
238
+ },
239
+ }
240
+ req .Spec .WorkspaceLocation = test .CheckoutLocation
241
+ return nil
242
+ }))
243
+ if err != nil {
244
+ t .Fatalf ("cannot launch a workspace: %q" , err )
245
+ }
246
+
247
+ prebuildSnapshot , vsInfo , err := stopWorkspaceAndFindSnapshot (prebuildStopWs , api )
248
+ if err != nil {
249
+ t .Fatalf ("stop workspace and find snapshot error: %v" , err )
250
+ }
251
+
252
+ // launch the workspace from prebuild
253
+ ws , stopWs , err := integration .LaunchWorkspaceDirectly (t , ctx , api , integration .WithRequestModifier (func (req * wsmanapi.StartWorkspaceRequest ) error {
254
+ req .Spec .FeatureFlags = test .FF
255
+ req .Spec .Initializer = & csapi.WorkspaceInitializer {
256
+ Spec : & csapi.WorkspaceInitializer_Prebuild {
257
+ Prebuild : & csapi.PrebuildInitializer {
258
+ Prebuild : & csapi.SnapshotInitializer {
259
+ Snapshot : prebuildSnapshot ,
260
+ FromVolumeSnapshot : reflect .DeepEqual (test .FF , []wsmanapi.WorkspaceFeatureFlag {wsmanapi .WorkspaceFeatureFlag_PERSISTENT_VOLUME_CLAIM }),
261
+ },
262
+ Git : []* csapi.GitInitializer {
263
+ {
264
+ RemoteUri : test .ContextURL ,
265
+ TargetMode : csapi .CloneTargetMode_REMOTE_BRANCH ,
266
+ CloneTaget : "main" ,
267
+ CheckoutLocation : test .CheckoutLocation ,
268
+ Config : & csapi.GitConfig {},
269
+ },
270
+ },
271
+ },
272
+ },
273
+ }
274
+ req .Spec .VolumeSnapshot = vsInfo
275
+ req .Spec .WorkspaceLocation = test .CheckoutLocation
276
+ return nil
277
+ }))
278
+ if err != nil {
279
+ t .Fatalf ("cannot launch a workspace: %q" , err )
280
+ }
281
+
282
+ defer func () {
283
+ // stop workspace in defer function to prevent we forget to stop the workspace
284
+ if err := stopWorkspace (t , cfg , stopWs ); err != nil {
285
+ t .Errorf ("cannot stop workspace: %q" , err )
286
+ }
287
+ }()
288
+
289
+ rsa , closer , err := integration .Instrument (integration .ComponentWorkspace , "workspace" , cfg .Namespace (), kubeconfig , cfg .Client (),
290
+ integration .WithInstanceID (ws .Req .Id ),
291
+ )
292
+ if err != nil {
293
+ t .Fatal (err )
294
+ }
295
+ t .Cleanup (func () {
296
+ rsa .Close ()
297
+ })
298
+ integration .DeferCloser (t , closer )
299
+
300
+ // check prebuild log message exists
301
+ var resp agent.ExecResponse
302
+ var checkPrebuildSuccess bool
303
+ for i := 0 ; i < 10 ; i ++ {
304
+ err = rsa .Call ("WorkspaceAgent.Exec" , & agent.ExecRequest {
305
+ Dir : prebuildLogPath ,
306
+ Command : "bash" ,
307
+ Args : []string {
308
+ "-c" ,
309
+ fmt .Sprintf ("grep %s *" , prebuildLog ),
310
+ },
311
+ }, & resp )
312
+ if err == nil && resp .ExitCode == 0 && strings .Trim (resp .Stdout , " \t \n " ) != "" {
313
+ checkPrebuildSuccess = true
314
+ break
315
+ }
316
+
317
+ // wait 3 seconds and check
318
+ time .Sleep (3 * time .Second )
319
+ }
320
+
321
+ if ! checkPrebuildSuccess {
322
+ t .Fatalf ("cannot found the prebuild message %s in %s, err:%v, exitCode:%d, stdout:%s" , prebuildLog , prebuildLogPath , err , resp .ExitCode , resp .Stdout )
323
+ }
324
+
325
+ // write file foobar.txt and stop the workspace
326
+ err = rsa .Call ("WorkspaceAgent.WriteFile" , & agent.WriteFileRequest {
327
+ Path : fmt .Sprintf ("%s/foobar.txt" , test .WorkspaceRoot ),
328
+ Content : []byte ("hello world" ),
329
+ Mode : 0644 ,
330
+ }, & resp )
331
+ if err != nil {
332
+ t .Fatalf ("cannot write file %s" , fmt .Sprintf ("%s/foobar.txt" , test .WorkspaceRoot ))
333
+ }
334
+
335
+ sctx , scancel := context .WithTimeout (context .Background (), 5 * time .Minute )
336
+ defer scancel ()
337
+
338
+ sapi := integration .NewComponentAPI (sctx , cfg .Namespace (), kubeconfig , cfg .Client ())
339
+ defer sapi .Done (t )
340
+
341
+ // stop workspace to get the volume snapshot information
342
+ _ , vsInfo , err = stopWorkspaceAndFindSnapshot (stopWs , sapi )
343
+ if err != nil {
344
+ t .Fatal (err )
345
+ }
346
+
347
+ // reopen the workspace and make sure the file foobar.txt exists
348
+ ws1 , stopWs1 , err := integration .LaunchWorkspaceDirectly (t , ctx , api , integration .WithRequestModifier (func (req * wsmanapi.StartWorkspaceRequest ) error {
349
+ req .ServicePrefix = ws .Req .ServicePrefix
350
+ req .Metadata .MetaId = ws .Req .Metadata .MetaId
351
+ req .Metadata .Owner = ws .Req .Metadata .Owner
352
+ req .Spec .FeatureFlags = test .FF
353
+ req .Spec .Initializer = & csapi.WorkspaceInitializer {
354
+ Spec : & csapi.WorkspaceInitializer_Backup {
355
+ Backup : & csapi.FromBackupInitializer {
356
+ CheckoutLocation : test .CheckoutLocation ,
357
+ FromVolumeSnapshot : reflect .DeepEqual (test .FF , []wsmanapi.WorkspaceFeatureFlag {wsmanapi .WorkspaceFeatureFlag_PERSISTENT_VOLUME_CLAIM }),
358
+ },
359
+ },
360
+ }
361
+ req .Spec .VolumeSnapshot = vsInfo
362
+ req .Spec .WorkspaceLocation = test .CheckoutLocation
363
+ return nil
364
+ }))
365
+ if err != nil {
366
+ t .Fatalf ("cannot launch a workspace: %q" , err )
367
+ }
368
+
369
+ defer func () {
370
+ // stop workspace in defer function to prevent we forget to stop the workspace
371
+ if err := stopWorkspace (t , cfg , stopWs1 ); err != nil {
372
+ t .Errorf ("cannot stop workspace: %q" , err )
373
+ }
374
+ }()
375
+
376
+ rsa , closer , err = integration .Instrument (integration .ComponentWorkspace , "workspace" , cfg .Namespace (), kubeconfig , cfg .Client (),
377
+ integration .WithInstanceID (ws1 .Req .Id ),
378
+ )
379
+ if err != nil {
380
+ t .Fatal (err )
381
+ }
382
+ integration .DeferCloser (t , closer )
383
+
384
+ var ls agent.ListDirResponse
385
+ err = rsa .Call ("WorkspaceAgent.ListDir" , & agent.ListDirRequest {
386
+ Dir : test .WorkspaceRoot ,
387
+ }, & ls )
388
+ if err != nil {
389
+ t .Fatal (err )
390
+ }
391
+ rsa .Close ()
392
+
393
+ var found bool
394
+ for _ , f := range ls .Files {
395
+ if filepath .Base (f ) == "foobar.txt" {
396
+ found = true
397
+ break
398
+ }
399
+ }
400
+ if ! found {
401
+ t .Fatal ("did not find foobar.txt from previous workspace instance" )
402
+ }
403
+ })
404
+ }
405
+ return ctx
406
+ }).
407
+ Feature ()
408
+
409
+ testEnv .Test (t , f )
410
+ }
411
+
412
+ func stopWorkspaceAndFindSnapshot (StopWorkspaceFunc integration.StopWorkspaceFunc , api * integration.ComponentAPI ) (string , * wsmanapi.VolumeSnapshotInfo , error ) {
413
+ lastStatus , err := StopWorkspaceFunc (true , api )
414
+ if err != nil {
415
+ return "" , nil , err
416
+ }
417
+ if lastStatus == nil && lastStatus .Conditions == nil {
418
+ return "" , nil , nil
419
+ }
420
+ return lastStatus .Conditions .Snapshot , lastStatus .Conditions .VolumeSnapshot , nil
421
+ }
422
+
423
+ func stopWorkspace (t * testing.T , cfg * envconf.Config , StopWorkspaceFunc integration.StopWorkspaceFunc ) error {
424
+ sctx , scancel := context .WithTimeout (context .Background (), 5 * time .Minute )
425
+ defer scancel ()
426
+
427
+ sapi := integration .NewComponentAPI (sctx , cfg .Namespace (), kubeconfig , cfg .Client ())
428
+ defer sapi .Done (t )
429
+
430
+ _ , err := StopWorkspaceFunc (true , sapi )
431
+ return err
432
+ }
0 commit comments