Skip to content

Commit 129c295

Browse files
committed
Prebuilds
Signed-off-by: Danny Kopping <danny@coder.com>
1 parent aef6220 commit 129c295

File tree

5 files changed

+144
-6
lines changed

5 files changed

+144
-6
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ to setup your local Terraform to use your local version rather than the registry
4747
}
4848
```
4949
2. Run `terraform init` and observe a warning like `Warning: Provider development overrides are in effect`
50-
4. Run `go build -o terraform-provider-coder` to build the provider binary, which Terraform will try locate and execute
50+
4. Run `make build` to build the provider binary, which Terraform will try locate and execute
5151
5. All local Terraform runs will now use your local provider!
5252
6. _**NOTE**: we vendor in this provider into `github.com/coder/coder`, so if you're testing with a local clone then you should also run `go mod edit -replace github.com/coder/terraform-provider-coder=/path/to/terraform-provider-coder` in your clone._
5353

provider/agent.go

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ package provider
33
import (
44
"context"
55
"fmt"
6+
"os"
67
"path/filepath"
78
"reflect"
89
"strings"
910

1011
"github.com/google/uuid"
1112
"github.com/hashicorp/go-cty/cty"
13+
"github.com/hashicorp/terraform-plugin-log/tflog"
1214
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
1315
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1416
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
@@ -22,10 +24,54 @@ func agentResource() *schema.Resource {
2224
SchemaVersion: 1,
2325

2426
Description: "Use this resource to associate an agent.",
25-
CreateContext: func(_ context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics {
27+
CreateContext: func(ctx context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics {
2628
// This should be a real authentication token!
2729
resourceData.SetId(uuid.NewString())
28-
err := resourceData.Set("token", uuid.NewString())
30+
31+
// CODER_RUNNING_WORKSPACE_AGENT_TOKEN is *only* used for prebuilds. We pass it down when we want to rebuild a prebuilt workspace
32+
// but not generate a new agent token. The provisionerdserver will retrieve this token and push it down to
33+
// here where it will be reused.
34+
// Context: the agent token is often used in immutable attributes of workspace resource (e.g. VM/container)
35+
// to initialize the agent, so if that value changes it will necessitate a replacement of that resource, thus
36+
// obviating the whole point of the prebuild.
37+
//
38+
// The default path is for a new token to be generated on each new resource creation.
39+
// TODO: add logging when the running token is actually used.
40+
var token string
41+
42+
isPrebuild := helpers.OptionalEnv(IsPrebuildEnvironmentVariable()) == "true"
43+
if !isPrebuild {
44+
token = os.Getenv(RunningAgentTokenEnvironmentVariable())
45+
}
46+
47+
allEnv := make(map[string]interface{})
48+
for _, v := range os.Environ() {
49+
split := strings.Split(v, "=")
50+
var key, val string
51+
if len(split) > 0 {
52+
key = split[0]
53+
}
54+
if len(split) > 1 {
55+
val = split[1]
56+
}
57+
58+
allEnv[key] = val
59+
}
60+
61+
allEnv["is_prebuild"] = fmt.Sprintf("%v", isPrebuild)
62+
63+
if token == "" {
64+
token = uuid.NewString()
65+
if !isPrebuild {
66+
tflog.Warn(ctx, "NOT USING EXISTING AGENT TOKEN", allEnv)
67+
}
68+
} else {
69+
if !isPrebuild {
70+
tflog.Info(ctx, "IS USING EXISTING AGENT TOKEN", allEnv)
71+
}
72+
}
73+
74+
err := resourceData.Set("token", token)
2975
if err != nil {
3076
return diag.FromErr(err)
3177
}
@@ -469,3 +515,7 @@ func updateInitScript(resourceData *schema.ResourceData, i interface{}) diag.Dia
469515
}
470516
return nil
471517
}
518+
519+
func RunningAgentTokenEnvironmentVariable() string {
520+
return "CODER_RUNNING_WORKSPACE_AGENT_TOKEN"
521+
}

provider/workspace.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ 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
34+
_ = rd.Set("is_prebuild", true)
35+
}
36+
_ = rd.Set("prebuild_count", prebuildCount)
37+
3038
name := helpers.OptionalEnvOrDefault("CODER_WORKSPACE_NAME", "default")
3139
rd.Set("name", name)
3240

@@ -88,6 +96,16 @@ func workspaceDataSource() *schema.Resource {
8896
Computed: true,
8997
Description: "A computed count based on `transition` state. If `start`, count will equal 1.",
9098
},
99+
"prebuild_count": {
100+
Type: schema.TypeInt,
101+
Computed: true,
102+
Description: "TODO",
103+
},
104+
"is_prebuild": {
105+
Type: schema.TypeBool,
106+
Computed: true,
107+
Description: "TODO",
108+
},
91109
"transition": {
92110
Type: schema.TypeString,
93111
Computed: true,
@@ -121,3 +139,7 @@ func workspaceDataSource() *schema.Resource {
121139
},
122140
}
123141
}
142+
143+
func IsPrebuildEnvironmentVariable() string {
144+
return "CODER_WORKSPACE_IS_PREBUILD"
145+
}

provider/workspace_preset.go

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@ package provider
22

33
import (
44
"context"
5-
65
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
76
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
87
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
98
"github.com/mitchellh/mapstructure"
109
)
1110

1211
type WorkspacePreset struct {
13-
Name string `mapstructure:"name"`
14-
Parameters map[string]string `mapstructure:"parameters"`
12+
Name string `mapstructure:"name"`
13+
Parameters map[string]string `mapstructure:"parameters"`
14+
Prebuild []WorkspacePrebuild `mapstructure:"prebuilds"`
15+
}
16+
17+
type WorkspacePrebuild struct {
18+
Instances int `mapstructure:"instances"`
1519
}
1620

1721
func workspacePresetDataSource() *schema.Resource {
@@ -24,9 +28,19 @@ func workspacePresetDataSource() *schema.Resource {
2428
err := mapstructure.Decode(struct {
2529
Name interface{}
2630
Parameters interface{}
31+
Prebuilds []struct {
32+
Instances interface{}
33+
}
2734
}{
2835
Name: rd.Get("name"),
2936
Parameters: rd.Get("parameters"),
37+
Prebuilds: []struct {
38+
Instances interface{}
39+
}{
40+
{
41+
Instances: rd.Get("prebuilds.0.instances"),
42+
},
43+
},
3044
}, &preset)
3145
if err != nil {
3246
return diag.Errorf("decode workspace preset: %s", err)
@@ -65,6 +79,22 @@ func workspacePresetDataSource() *schema.Resource {
6579
ValidateFunc: validation.StringIsNotEmpty,
6680
},
6781
},
82+
"prebuilds": {
83+
Type: schema.TypeSet,
84+
Description: "Prebuilds of the workspace preset.",
85+
Optional: true,
86+
MaxItems: 1, // TODO: is this always true? More than 1 prebuilds config per preset?
87+
Elem: &schema.Resource{
88+
Schema: map[string]*schema.Schema{
89+
"instances": {
90+
Type: schema.TypeInt,
91+
Required: true,
92+
ForceNew: true,
93+
ValidateFunc: validation.IntAtLeast(1),
94+
},
95+
},
96+
},
97+
},
6898
},
6999
}
70100
}

provider/workspace_preset_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,42 @@ func TestWorkspacePreset(t *testing.T) {
108108
// So we test it here to make sure we don't regress.
109109
ExpectError: regexp.MustCompile("Inappropriate value for attribute \"parameters\": map of string required"),
110110
},
111+
{
112+
Name: "Prebuilds is set, but not its required fields",
113+
Config: `
114+
data "coder_workspace_preset" "preset_1" {
115+
name = "preset_1"
116+
parameters = {
117+
"region" = "us-east1-a"
118+
}
119+
prebuilds {}
120+
}`,
121+
ExpectError: regexp.MustCompile("The argument \"instances\" is required, but no definition was found."),
122+
},
123+
{
124+
Name: "Prebuilds is set, and so are its required fields",
125+
Config: `
126+
data "coder_workspace_preset" "preset_1" {
127+
name = "preset_1"
128+
parameters = {
129+
"region" = "us-east1-a"
130+
}
131+
prebuilds {
132+
instances = 1
133+
}
134+
}`,
135+
ExpectError: nil,
136+
Check: func(state *terraform.State) error {
137+
require.Len(t, state.Modules, 1)
138+
require.Len(t, state.Modules[0].Resources, 1)
139+
resource := state.Modules[0].Resources["data.coder_workspace_preset.preset_1"]
140+
require.NotNil(t, resource)
141+
attrs := resource.Primary.Attributes
142+
require.Equal(t, attrs["name"], "preset_1")
143+
require.Equal(t, attrs["prebuilds.0.instances"], "1")
144+
return nil
145+
},
146+
},
111147
}
112148

113149
for _, testcase := range testcases {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy