@@ -12,9 +12,9 @@ import (
12
12
13
13
"github.com/hashicorp/hcl/v2"
14
14
"github.com/hashicorp/hcl/v2/hclsyntax"
15
- "github.com/zclconf/go-cty/cty"
16
15
17
16
"github.com/coder/coder/v2/coderd/rbac/policy"
17
+ "github.com/coder/coder/v2/provisioner/terraform/tfparse"
18
18
"github.com/coder/coder/v2/provisionersdk"
19
19
20
20
"github.com/google/uuid"
@@ -64,6 +64,7 @@ type Builder struct {
64
64
templateVersion * database.TemplateVersion
65
65
templateVersionJob * database.ProvisionerJob
66
66
templateVersionParameters * []database.TemplateVersionParameter
67
+ templateVersionVariables * []database.TemplateVersionVariable
67
68
templateVersionWorkspaceTags * []database.TemplateVersionWorkspaceTag
68
69
lastBuild * database.WorkspaceBuild
69
70
lastBuildErr * error
@@ -617,6 +618,22 @@ func (b *Builder) getTemplateVersionParameters() ([]database.TemplateVersionPara
617
618
return tvp , nil
618
619
}
619
620
621
+ func (b * Builder ) getTemplateVersionVariables () ([]database.TemplateVersionVariable , error ) {
622
+ if b .templateVersionVariables != nil {
623
+ return * b .templateVersionVariables , nil
624
+ }
625
+ tvID , err := b .getTemplateVersionID ()
626
+ if err != nil {
627
+ return nil , xerrors .Errorf ("get template version ID to get variables: %w" , err )
628
+ }
629
+ tvs , err := b .store .GetTemplateVersionVariables (b .ctx , tvID )
630
+ if err != nil && ! xerrors .Is (err , sql .ErrNoRows ) {
631
+ return nil , xerrors .Errorf ("get template version %s variables: %w" , tvID , err )
632
+ }
633
+ b .templateVersionVariables = & tvs
634
+ return tvs , nil
635
+ }
636
+
620
637
// verifyNoLegacyParameters verifies that initiator can't start the workspace build
621
638
// if it uses legacy parameters (database.ParameterSchemas).
622
639
func (b * Builder ) verifyNoLegacyParameters () error {
@@ -678,17 +695,35 @@ func (b *Builder) getProvisionerTags() (map[string]string, error) {
678
695
tags [name ] = value
679
696
}
680
697
681
- // Step 2: Mutate workspace tags
698
+ // Step 2: Mutate workspace tags:
699
+ // - Get workspace tags from the template version job
700
+ // - Get template version variables from the template version as they can be
701
+ // referenced in workspace tags
702
+ // - Get parameters from the workspace build as they can also be referenced
703
+ // in workspace tags
704
+ // - Evaluate workspace tags given the above inputs
682
705
workspaceTags , err := b .getTemplateVersionWorkspaceTags ()
683
706
if err != nil {
684
707
return nil , BuildError {http .StatusInternalServerError , "failed to fetch template version workspace tags" , err }
685
708
}
709
+ tvs , err := b .getTemplateVersionVariables ()
710
+ if err != nil {
711
+ return nil , BuildError {http .StatusInternalServerError , "failed to fetch template version variables" , err }
712
+ }
713
+ varsM := make (map [string ]string )
714
+ for _ , tv := range tvs {
715
+ varsM [tv .Name ] = tv .Value
716
+ }
686
717
parameterNames , parameterValues , err := b .getParameters ()
687
718
if err != nil {
688
719
return nil , err // already wrapped BuildError
689
720
}
721
+ paramsM := make (map [string ]string )
722
+ for i , name := range parameterNames {
723
+ paramsM [name ] = parameterValues [i ]
724
+ }
690
725
691
- evalCtx := buildParametersEvalContext ( parameterNames , parameterValues )
726
+ evalCtx := tfparse . BuildEvalContext ( varsM , paramsM )
692
727
for _ , workspaceTag := range workspaceTags {
693
728
expr , diags := hclsyntax .ParseExpression ([]byte (workspaceTag .Value ), "expression.hcl" , hcl .InitialPos )
694
729
if diags .HasErrors () {
@@ -701,7 +736,7 @@ func (b *Builder) getProvisionerTags() (map[string]string, error) {
701
736
}
702
737
703
738
// Do not use "val.AsString()" as it can panic
704
- str , err := ctyValueString (val )
739
+ str , err := tfparse . CtyValueString (val )
705
740
if err != nil {
706
741
return nil , BuildError {http .StatusBadRequest , "failed to marshal cty.Value as string" , err }
707
742
}
@@ -710,44 +745,6 @@ func (b *Builder) getProvisionerTags() (map[string]string, error) {
710
745
return tags , nil
711
746
}
712
747
713
- func buildParametersEvalContext (names , values []string ) * hcl.EvalContext {
714
- m := map [string ]cty.Value {}
715
- for i , name := range names {
716
- m [name ] = cty .MapVal (map [string ]cty.Value {
717
- "value" : cty .StringVal (values [i ]),
718
- })
719
- }
720
-
721
- if len (m ) == 0 {
722
- return nil // otherwise, panic: must not call MapVal with empty map
723
- }
724
-
725
- return & hcl.EvalContext {
726
- Variables : map [string ]cty.Value {
727
- "data" : cty .MapVal (map [string ]cty.Value {
728
- "coder_parameter" : cty .MapVal (m ),
729
- }),
730
- },
731
- }
732
- }
733
-
734
- func ctyValueString (val cty.Value ) (string , error ) {
735
- switch val .Type () {
736
- case cty .Bool :
737
- if val .True () {
738
- return "true" , nil
739
- } else {
740
- return "false" , nil
741
- }
742
- case cty .Number :
743
- return val .AsBigFloat ().String (), nil
744
- case cty .String :
745
- return val .AsString (), nil
746
- default :
747
- return "" , xerrors .Errorf ("only primitive types are supported - bool, number, and string" )
748
- }
749
- }
750
-
751
748
func (b * Builder ) getTemplateVersionWorkspaceTags () ([]database.TemplateVersionWorkspaceTag , error ) {
752
749
if b .templateVersionWorkspaceTags != nil {
753
750
return * b .templateVersionWorkspaceTags , nil
0 commit comments