Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f7b9338

Browse files
committedMay 5, 2025
Merge remote-tracking branch 'origin/main' into stevenmasley/value_validate
2 parents 22f12cb + 21147bf commit f7b9338

File tree

4 files changed

+75
-15
lines changed

4 files changed

+75
-15
lines changed
 

‎go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/docker/docker v26.1.5+incompatible
77
github.com/google/uuid v1.6.0
88
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320
9+
github.com/hashicorp/terraform-plugin-log v0.9.0
910
github.com/hashicorp/terraform-plugin-sdk/v2 v2.36.1
1011
github.com/masterminds/semver v1.5.0
1112
github.com/mitchellh/mapstructure v1.5.0
@@ -50,7 +51,6 @@ require (
5051
github.com/hashicorp/terraform-exec v0.22.0 // indirect
5152
github.com/hashicorp/terraform-json v0.24.0 // indirect
5253
github.com/hashicorp/terraform-plugin-go v0.26.0 // indirect
53-
github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect
5454
github.com/hashicorp/terraform-registry-address v0.2.4 // indirect
5555
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
5656
github.com/hashicorp/yamux v0.1.1 // indirect

‎provider/agent.go

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ package provider
22

33
import (
44
"context"
5+
"crypto/sha256"
6+
"encoding/hex"
57
"fmt"
68
"path/filepath"
79
"reflect"
810
"strings"
911

1012
"github.com/google/uuid"
1113
"github.com/hashicorp/go-cty/cty"
14+
"github.com/hashicorp/terraform-plugin-log/tflog"
1215
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
1316
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1417
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
@@ -22,10 +25,12 @@ func agentResource() *schema.Resource {
2225
SchemaVersion: 1,
2326

2427
Description: "Use this resource to associate an agent.",
25-
CreateContext: func(_ context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics {
26-
// This should be a real authentication token!
27-
resourceData.SetId(uuid.NewString())
28-
err := resourceData.Set("token", uuid.NewString())
28+
CreateContext: func(ctx context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics {
29+
agentID := uuid.NewString()
30+
resourceData.SetId(agentID)
31+
32+
token := agentAuthToken(ctx, "")
33+
err := resourceData.Set("token", token)
2934
if err != nil {
3035
return diag.FromErr(err)
3136
}
@@ -48,10 +53,12 @@ func agentResource() *schema.Resource {
4853
return updateInitScript(resourceData, i)
4954
},
5055
ReadWithoutTimeout: func(ctx context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics {
51-
err := resourceData.Set("token", uuid.NewString())
56+
token := agentAuthToken(ctx, "")
57+
err := resourceData.Set("token", token)
5258
if err != nil {
5359
return diag.FromErr(err)
5460
}
61+
5562
if _, ok := resourceData.GetOk("display_apps"); !ok {
5663
err = resourceData.Set("display_apps", []interface{}{
5764
map[string]bool{
@@ -469,3 +476,37 @@ func updateInitScript(resourceData *schema.ResourceData, i interface{}) diag.Dia
469476
}
470477
return nil
471478
}
479+
480+
func agentAuthToken(ctx context.Context, agentID string) string {
481+
existingToken := helpers.OptionalEnv(RunningAgentTokenEnvironmentVariable(agentID))
482+
if existingToken == "" {
483+
// Most of the time, we will generate a new token for the agent.
484+
// In the case of a prebuilt workspace being claimed, we will override with
485+
// an existing token provided below.
486+
token := uuid.NewString()
487+
return token
488+
}
489+
490+
// An existing token was provided for this agent. That means that this
491+
// is a prebuilt workspace in the process of being claimed.
492+
// We should reuse the token.
493+
tflog.Info(ctx, "using provided agent token for prebuild", map[string]interface{}{
494+
"agent_id": agentID,
495+
})
496+
return existingToken
497+
}
498+
499+
// RunningAgentTokenEnvironmentVariable returns the name of an environment variable
500+
// that contains the token to use for the running agent. This is used for prebuilds,
501+
// where we want to reuse the same token for the next iteration of a workspace agent
502+
// before and after the workspace was claimed by a user.
503+
//
504+
// By reusing an existing token, we can avoid the need to change a value that may have been
505+
// used immutably. Thus, allowing us to avoid reprovisioning resources that may take a long time
506+
// to replace.
507+
//
508+
// agentID is unused for now, but will be used as soon as we support multiple agents.
509+
func RunningAgentTokenEnvironmentVariable(agentID string) string {
510+
sum := sha256.Sum256([]byte(agentID))
511+
return "CODER_RUNNING_WORKSPACE_AGENT_TOKEN_" + hex.EncodeToString(sum[:])
512+
}

‎provider/formtype_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"regexp"
77
"strconv"
88
"strings"
9+
"sync"
910
"testing"
1011

1112
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
@@ -53,7 +54,7 @@ func TestValidateFormType(t *testing.T) {
5354
// formTypesChecked keeps track of all checks run. It will be used to
5455
// ensure all combinations of form_type and option_type are tested.
5556
// All untested options are assumed to throw an error.
56-
formTypesChecked := make(map[string]struct{})
57+
var formTypesChecked sync.Map
5758

5859
expectType := func(expected provider.ParameterFormType, opts formTypeCheck) formTypeTestCase {
5960
ftname := opts.formType
@@ -240,12 +241,12 @@ func TestValidateFormType(t *testing.T) {
240241
for _, c := range cases {
241242
t.Run(c.name, func(t *testing.T) {
242243
t.Parallel()
243-
if _, ok := formTypesChecked[c.config.String()]; ok {
244+
if _, ok := formTypesChecked.Load(c.config.String()); ok {
244245
t.Log("Duplicated form type check, delete this extra test case")
245246
t.Fatalf("form type %q already checked", c.config.String())
246247
}
247248

248-
formTypesChecked[c.config.String()] = struct{}{}
249+
formTypesChecked.Store(c.config.String(), struct{}{})
249250
formTypeTest(t, c)
250251
})
251252
}
@@ -282,7 +283,7 @@ func TestValidateFormType(t *testing.T) {
282283
}
283284

284285
for _, check := range requiredChecks {
285-
if _, alreadyChecked := formTypesChecked[check.String()]; alreadyChecked {
286+
if _, alreadyChecked := formTypesChecked.Load(check.String()); alreadyChecked {
286287
continue
287288
}
288289

‎provider/workspace.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ func workspaceDataSource() *schema.Resource {
2727
}
2828
_ = rd.Set("start_count", count)
2929

30-
prebuild := helpers.OptionalEnv(IsPrebuildEnvironmentVariable())
31-
prebuildCount := 0
32-
if prebuild == "true" {
33-
prebuildCount = 1
30+
if isPrebuiltWorkspace() {
31+
_ = rd.Set("prebuild_count", 1)
3432
_ = rd.Set("is_prebuild", true)
33+
} else {
34+
_ = rd.Set("prebuild_count", 0)
35+
_ = rd.Set("is_prebuild", false)
3536
}
36-
_ = rd.Set("prebuild_count", prebuildCount)
3737

3838
name := helpers.OptionalEnvOrDefault("CODER_WORKSPACE_NAME", "default")
3939
rd.Set("name", name)
@@ -140,6 +140,24 @@ func workspaceDataSource() *schema.Resource {
140140
}
141141
}
142142

143+
// isPrebuiltWorkspace returns true if the workspace is an unclaimed prebuilt workspace.
144+
func isPrebuiltWorkspace() bool {
145+
return helpers.OptionalEnv(IsPrebuildEnvironmentVariable()) == "true"
146+
}
147+
148+
// IsPrebuildEnvironmentVariable returns the name of the environment variable that
149+
// indicates whether the workspace is an unclaimed prebuilt workspace.
150+
//
151+
// Knowing whether the workspace is an unclaimed prebuilt workspace allows template
152+
// authors to conditionally execute code in the template based on whether the workspace
153+
// has been assigned to a user or not. This allows identity specific configuration to
154+
// be applied only after the workspace is claimed, while the rest of the workspace can
155+
// be pre-configured.
156+
//
157+
// The value of this environment variable should be set to "true" if the workspace is prebuilt
158+
// and it has not yet been claimed by a user. Any other values, including "false"
159+
// and "" will be interpreted to mean that the workspace is not prebuilt, or was
160+
// prebuilt but has since been claimed by a user.
143161
func IsPrebuildEnvironmentVariable() string {
144162
return "CODER_WORKSPACE_IS_PREBUILD"
145163
}

0 commit comments

Comments
 (0)
Please sign in to comment.