@@ -3,6 +3,7 @@ package cli_test
3
3
import (
4
4
"bytes"
5
5
"context"
6
+ "database/sql"
6
7
"os"
7
8
"path/filepath"
8
9
"runtime"
@@ -18,6 +19,7 @@ import (
18
19
"github.com/coder/coder/v2/coderd/coderdtest"
19
20
"github.com/coder/coder/v2/coderd/database"
20
21
"github.com/coder/coder/v2/coderd/database/dbtestutil"
22
+ "github.com/coder/coder/v2/coderd/database/dbtime"
21
23
"github.com/coder/coder/v2/coderd/rbac"
22
24
"github.com/coder/coder/v2/codersdk"
23
25
"github.com/coder/coder/v2/provisioner/echo"
@@ -412,84 +414,162 @@ func TestTemplatePush(t *testing.T) {
412
414
413
415
t .Run ("WorkspaceTagsTerraform" ,func (t * testing.T ) {
414
416
t .Parallel ()
415
- ctx := testutil .Context (t ,testutil .WaitShort )
416
417
417
- // Start an instance **without** a built-in provisioner.
418
- // We're not actually testing that the Terraform applies.
419
- // What we test is that a provisioner job is created with the expected
420
- // tags based on the __content__ of the Terraform.
421
- store ,ps := dbtestutil .NewDB (t )
422
- client := coderdtest .New (t ,& coderdtest.Options {
423
- Database :store ,
424
- Pubsub :ps ,
425
- })
426
-
427
- owner := coderdtest .CreateFirstUser (t ,client )
428
- templateAdmin ,_ := coderdtest .CreateAnotherUser (t ,client ,owner .OrganizationID ,rbac .RoleTemplateAdmin ())
429
-
430
- // Create a tar file with some pre-defined content
431
- tarFile := testutil .CreateTar (t ,map [string ]string {
432
- "main.tf" :`
433
- variable "a" {
434
- type = string
435
- default = "1"
436
- }
437
- data "coder_parameter" "b" {
438
- type = string
439
- default = "2"
440
- }
441
- resource "null_resource" "test" {}
442
- data "coder_workspace_tags" "tags" {
443
- tags = {
444
- "foo": "bar",
445
- "a": var.a,
446
- "b": data.coder_parameter.b.value,
447
- }
448
- }` ,
449
- })
450
-
451
- // Write the tar file to disk.
452
- tempDir := t .TempDir ()
453
- err := tfparse .WriteArchive (tarFile ,"application/x-tar" ,tempDir )
454
- require .NoError (t ,err )
455
-
456
- // Run `coder templates push`
457
- templateName := strings .ReplaceAll (testutil .GetRandomName (t ),"_" ,"-" )
458
- var stdout ,stderr strings.Builder
459
- inv ,root := clitest .New (t ,"templates" ,"push" ,templateName ,"-d" ,tempDir ,"--yes" )
460
- inv .Stdout = & stdout
461
- inv .Stderr = & stderr
462
- clitest .SetupConfig (t ,templateAdmin ,root )
463
-
464
- // Don't forget to clean up!
465
- cancelCtx ,cancel := context .WithCancel (ctx )
466
- t .Cleanup (cancel )
467
- done := make (chan error )
468
- go func () {
469
- done <- inv .WithContext (cancelCtx ).Run ()
470
- }()
471
-
472
- // Assert that a provisioner job was created with the desired tags.
473
- wantTags := database .StringMap (provisionersdk .MutateTags (uuid .Nil ,map [string ]string {
474
- "foo" :"bar" ,
475
- "a" :"1" ,
476
- "b" :"2" ,
477
- }))
478
- require .Eventually (t ,func ()bool {
479
- jobs ,err := store .GetProvisionerJobsCreatedAfter (ctx , time.Time {})
480
- if ! assert .NoError (t ,err ) {
481
- return false
482
- }
483
- if len (jobs )== 0 {
484
- return false
485
- }
486
- return assert .EqualValues (t ,wantTags ,jobs [0 ].Tags )
487
- },testutil .WaitShort ,testutil .IntervalSlow )
488
-
489
- cancel ()
490
- <- done
418
+ tests := []struct {
419
+ name string
420
+ setupDaemon func (ctx context.Context ,store database.Store ,owner codersdk.CreateFirstUserResponse ,tags database.StringMap ,now time.Time )error
421
+ expectOutput string
422
+ }{
423
+ {
424
+ name :"no provisioners available" ,
425
+ setupDaemon :func (_ context.Context ,_ database.Store ,_ codersdk.CreateFirstUserResponse ,_ database.StringMap ,_ time.Time )error {
426
+ return nil
427
+ },
428
+ expectOutput :"there are no provisioners that accept the required tags" ,
429
+ },
430
+ {
431
+ name :"provisioner stale" ,
432
+ setupDaemon :func (ctx context.Context ,store database.Store ,owner codersdk.CreateFirstUserResponse ,tags database.StringMap ,now time.Time )error {
433
+ pk ,err := store .InsertProvisionerKey (ctx , database.InsertProvisionerKeyParams {
434
+ ID :uuid .New (),
435
+ CreatedAt :now ,
436
+ OrganizationID :owner .OrganizationID ,
437
+ Name :"test" ,
438
+ Tags :tags ,
439
+ HashedSecret : []byte ("secret" ),
440
+ })
441
+ if err != nil {
442
+ return err
443
+ }
444
+ oneHourAgo := now .Add (- time .Hour )
445
+ _ ,err = store .UpsertProvisionerDaemon (ctx , database.UpsertProvisionerDaemonParams {
446
+ Provisioners : []database.ProvisionerType {database .ProvisionerTypeTerraform },
447
+ LastSeenAt : sql.NullTime {Time :oneHourAgo ,Valid :true },
448
+ CreatedAt :oneHourAgo ,
449
+ Name :"test" ,
450
+ Tags :tags ,
451
+ OrganizationID :owner .OrganizationID ,
452
+ KeyID :pk .ID ,
453
+ })
454
+ return err
455
+ },
456
+ expectOutput :"Provisioners that accept the required tags have not responded for longer than expected" ,
457
+ },
458
+ {
459
+ name :"active provisioner" ,
460
+ setupDaemon :func (ctx context.Context ,store database.Store ,owner codersdk.CreateFirstUserResponse ,tags database.StringMap ,now time.Time )error {
461
+ pk ,err := store .InsertProvisionerKey (ctx , database.InsertProvisionerKeyParams {
462
+ ID :uuid .New (),
463
+ CreatedAt :now ,
464
+ OrganizationID :owner .OrganizationID ,
465
+ Name :"test" ,
466
+ Tags :tags ,
467
+ HashedSecret : []byte ("secret" ),
468
+ })
469
+ if err != nil {
470
+ return err
471
+ }
472
+ _ ,err = store .UpsertProvisionerDaemon (ctx , database.UpsertProvisionerDaemonParams {
473
+ Provisioners : []database.ProvisionerType {database .ProvisionerTypeTerraform },
474
+ LastSeenAt : sql.NullTime {Time :now ,Valid :true },
475
+ CreatedAt :now ,
476
+ Name :"test-active" ,
477
+ Tags :tags ,
478
+ OrganizationID :owner .OrganizationID ,
479
+ KeyID :pk .ID ,
480
+ })
481
+ return err
482
+ },
483
+ expectOutput :"" ,
484
+ },
485
+ }
491
486
492
- require .Contains (t ,stderr .String (),"No provisioners are available to handle the job!" )
487
+ for _ ,tt := range tests {
488
+ tt := tt
489
+ t .Run (tt .name ,func (t * testing.T ) {
490
+ t .Parallel ()
491
+
492
+ // Start an instance **without** a built-in provisioner.
493
+ // We're not actually testing that the Terraform applies.
494
+ // What we test is that a provisioner job is created with the expected
495
+ // tags based on the __content__ of the Terraform.
496
+ store ,ps := dbtestutil .NewDB (t )
497
+ client := coderdtest .New (t ,& coderdtest.Options {
498
+ Database :store ,
499
+ Pubsub :ps ,
500
+ })
501
+
502
+ owner := coderdtest .CreateFirstUser (t ,client )
503
+ templateAdmin ,_ := coderdtest .CreateAnotherUser (t ,client ,owner .OrganizationID ,rbac .RoleTemplateAdmin ())
504
+
505
+ // Create a tar file with some pre-defined content
506
+ tarFile := testutil .CreateTar (t ,map [string ]string {
507
+ "main.tf" :`
508
+ variable "a" {
509
+ type = string
510
+ default = "1"
511
+ }
512
+ data "coder_parameter" "b" {
513
+ type = string
514
+ default = "2"
515
+ }
516
+ resource "null_resource" "test" {}
517
+ data "coder_workspace_tags" "tags" {
518
+ tags = {
519
+ "a": var.a,
520
+ "b": data.coder_parameter.b.value,
521
+ "test_name": "` + tt .name + `"
522
+ }
523
+ }` ,
524
+ })
525
+
526
+ // Write the tar file to disk.
527
+ tempDir := t .TempDir ()
528
+ err := tfparse .WriteArchive (tarFile ,"application/x-tar" ,tempDir )
529
+ require .NoError (t ,err )
530
+
531
+ wantTags := database .StringMap (provisionersdk .MutateTags (uuid .Nil ,map [string ]string {
532
+ "a" :"1" ,
533
+ "b" :"2" ,
534
+ "test_name" :tt .name ,
535
+ }))
536
+
537
+ templateName := strings .ReplaceAll (testutil .GetRandomName (t ),"_" ,"-" )
538
+
539
+ inv ,root := clitest .New (t ,"templates" ,"push" ,templateName ,"-d" ,tempDir ,"--yes" )
540
+ clitest .SetupConfig (t ,templateAdmin ,root )
541
+ pty := ptytest .New (t ).Attach (inv )
542
+
543
+ ctx := testutil .Context (t ,testutil .WaitShort )
544
+ now := dbtime .Now ()
545
+ require .NoError (t ,tt .setupDaemon (ctx ,store ,owner ,wantTags ,now ))
546
+
547
+ cancelCtx ,cancel := context .WithCancel (ctx )
548
+ t .Cleanup (cancel )
549
+ done := make (chan error )
550
+ go func () {
551
+ done <- inv .WithContext (cancelCtx ).Run ()
552
+ }()
553
+
554
+ require .Eventually (t ,func ()bool {
555
+ jobs ,err := store .GetProvisionerJobsCreatedAfter (ctx , time.Time {})
556
+ if ! assert .NoError (t ,err ) {
557
+ return false
558
+ }
559
+ if len (jobs )== 0 {
560
+ return false
561
+ }
562
+ return assert .EqualValues (t ,wantTags ,jobs [0 ].Tags )
563
+ },testutil .WaitShort ,testutil .IntervalFast )
564
+
565
+ if tt .expectOutput != "" {
566
+ pty .ExpectMatch (tt .expectOutput )
567
+ }
568
+
569
+ cancel ()
570
+ <- done
571
+ })
572
+ }
493
573
})
494
574
495
575
t .Run ("ChangeTags" ,func (t * testing.T ) {