feat: add "display_order" column to coder_parameter to keep parameters sorted in UI (#8227)

This commit is contained in:
Marcin Tojek
2023-06-30 12:41:55 +02:00
committed by GitHub
parent 72a48bee52
commit 6639c69fad
23 changed files with 969 additions and 482 deletions
+9 -17
View File
@@ -280,12 +280,7 @@ func (e *executor) planResources(ctx, killCtx context.Context, planfilePath stri
}
modules = append(modules, plan.PlannedValues.RootModule)
rawParameterNames, err := rawRichParameterNames(e.workdir)
if err != nil {
return nil, xerrors.Errorf("raw rich parameter names: %w", err)
}
state, err := ConvertState(modules, rawGraph, rawParameterNames)
state, err := ConvertState(modules, rawGraph)
if err != nil {
return nil, err
}
@@ -410,18 +405,15 @@ func (e *executor) stateResources(ctx, killCtx context.Context) (*State, error)
return nil, xerrors.Errorf("get terraform graph: %w", err)
}
converted := &State{}
if state.Values != nil {
rawParameterNames, err := rawRichParameterNames(e.workdir)
if err != nil {
return nil, xerrors.Errorf("raw rich parameter names: %w", err)
}
if state.Values == nil {
return converted, nil
}
converted, err = ConvertState([]*tfjson.StateModule{
state.Values.RootModule,
}, rawGraph, rawParameterNames)
if err != nil {
return nil, err
}
converted, err = ConvertState([]*tfjson.StateModule{
state.Values.RootModule,
}, rawGraph)
if err != nil {
return nil, err
}
return converted, nil
}
-70
View File
@@ -1,70 +0,0 @@
package terraform
import (
"fmt"
"os"
"path"
"strings"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclparse"
)
var terraformWithCoderParametersSchema = &hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{
Type: "data",
LabelNames: []string{"coder_parameter", "*"},
},
},
}
func rawRichParameterNames(workdir string) ([]string, error) {
entries, err := os.ReadDir(workdir)
if err != nil {
return nil, err
}
var coderParameterNames []string
for _, entry := range entries {
if !strings.HasSuffix(entry.Name(), ".tf") && !strings.HasSuffix(entry.Name(), ".tf.json") {
continue
}
var (
parsedTF *hcl.File
diags hcl.Diagnostics
tfFilepath = path.Join(workdir, entry.Name())
parser = hclparse.NewParser()
)
// Support .tf.json files.
// Warning: since JSON parsing in Go automatically sorts maps
// alphabetically, we can't preserve the original order of parameters
// like in HCL.
if strings.HasSuffix(entry.Name(), ".tf.json") {
parsedTF, diags = parser.ParseJSONFile(tfFilepath)
} else {
parsedTF, diags = parser.ParseHCLFile(tfFilepath)
}
if diags.HasErrors() {
return nil, hcl.Diagnostics{
{
Severity: hcl.DiagError,
Summary: "Failed to parse HCL file",
Detail: fmt.Sprintf("parser.ParseHCLFile can't parse %q file", tfFilepath),
},
}
}
content, _, _ := parsedTF.Body.PartialContent(terraformWithCoderParametersSchema)
for _, block := range content.Blocks {
if block.Type == "data" && block.Labels[0] == "coder_parameter" && len(block.Labels) == 2 {
coderParameterNames = append(coderParameterNames, block.Labels[1])
}
}
}
return coderParameterNames, nil
}
+9 -9
View File
@@ -408,30 +408,30 @@ func TestProvision(t *testing.T) {
},
Request: &proto.Provision_Plan{
RichParameterValues: []*proto.RichParameterValue{
{
Name: "Sample",
Value: "foofoo",
},
{
Name: "Example",
Value: "foobaz",
},
{
Name: "Sample",
Value: "foofoo",
},
},
},
Response: &proto.Provision_Response{
Type: &proto.Provision_Response_Complete{
Complete: &proto.Provision_Complete{
Parameters: []*proto.RichParameter{
{
Name: "Sample",
Type: "string",
DefaultValue: "foobaz",
},
{
Name: "Example",
Type: "string",
DefaultValue: "foobar",
},
{
Name: "Sample",
Type: "string",
DefaultValue: "foobaz",
},
},
Resources: []*proto.Resource{{
Name: "example",
+3 -44
View File
@@ -2,7 +2,6 @@ package terraform
import (
"fmt"
"sort"
"strings"
"github.com/awalterschulze/gographviz"
@@ -99,7 +98,7 @@ type State struct {
// ConvertState consumes Terraform state and a GraphViz representation
// produced by `terraform graph` to produce resources consumable by Coder.
// nolint:gocyclo
func ConvertState(modules []*tfjson.StateModule, rawGraph string, rawParameterNames []string) (*State, error) {
func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error) {
parsedGraph, err := gographviz.ParseString(rawGraph)
if err != nil {
return nil, xerrors.Errorf("parse graph: %w", err)
@@ -479,7 +478,7 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string, rawParameterNa
var duplicatedParamNames []string
parameters := make([]*proto.RichParameter, 0)
for _, resource := range orderedRichParametersResources(tfResourcesRichParameters, rawParameterNames) {
for _, resource := range tfResourcesRichParameters {
var param provider.Parameter
err = mapstructure.Decode(resource.AttributeValues, &param)
if err != nil {
@@ -494,6 +493,7 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string, rawParameterNa
DefaultValue: param.Default,
Icon: param.Icon,
Required: !param.Optional,
Order: int32(param.Order),
LegacyVariableName: param.LegacyVariableName,
}
if len(param.Validation) == 1 {
@@ -718,44 +718,3 @@ func findResourcesInGraph(graph *gographviz.Graph, tfResourcesByLabel map[string
return graphResources
}
func orderedRichParametersResources(tfResourcesRichParameters []*tfjson.StateResource, orderedNames []string) []*tfjson.StateResource {
if len(orderedNames) == 0 {
return tfResourcesRichParameters
}
var ordered []*tfjson.StateResource
for _, name := range orderedNames {
for _, resource := range tfResourcesRichParameters {
if resource.Name == name {
ordered = append(ordered, resource)
}
}
}
// Edge case: a parameter is present in an external module (Git repository, static files, etc.),
// which can't be easily parsed to check the parameter order.
// Those parameters will be prepended to the "ordered" list.
var external []*tfjson.StateResource
for _, resource := range tfResourcesRichParameters {
isExternal := true
for _, o := range ordered {
if resource.Name == o.Name {
isExternal = false
break
}
}
if isExternal {
external = append(external, resource)
}
}
if len(external) > 0 {
sort.Slice(external, func(i, j int) bool {
return external[i].Name < external[j].Name
})
ordered = append(external, ordered...)
}
return ordered
}
+67 -48
View File
@@ -7,7 +7,6 @@ import (
"path/filepath"
"runtime"
"sort"
"strings"
"testing"
tfjson "github.com/hashicorp/terraform-json"
@@ -353,10 +352,17 @@ func TestConvertResources(t *testing.T) {
}},
Required: true,
}, {
Name: "Sample",
Type: "string",
Description: "blah blah",
DefaultValue: "ok",
Name: "number_example",
Type: "number",
DefaultValue: "4",
ValidationMin: nil,
ValidationMax: nil,
}, {
Name: "number_example_max_zero",
Type: "number",
DefaultValue: "-2",
ValidationMin: terraform.PtrInt32(-3),
ValidationMax: terraform.PtrInt32(0),
}, {
Name: "number_example_min_max",
Type: "number",
@@ -370,17 +376,38 @@ func TestConvertResources(t *testing.T) {
ValidationMin: terraform.PtrInt32(0),
ValidationMax: terraform.PtrInt32(6),
}, {
Name: "number_example_max_zero",
Type: "number",
DefaultValue: "-2",
ValidationMin: terraform.PtrInt32(-3),
ValidationMax: terraform.PtrInt32(0),
Name: "Sample",
Type: "string",
Description: "blah blah",
DefaultValue: "ok",
}},
},
"rich-parameters-order": {
resources: []*proto.Resource{{
Name: "dev",
Type: "null_resource",
Agents: []*proto.Agent{{
Name: "dev",
OperatingSystem: "windows",
ShutdownScriptTimeoutSeconds: 300,
StartupScriptTimeoutSeconds: 300,
Architecture: "arm64",
Auth: &proto.Agent_Token{},
StartupScriptBehavior: "non-blocking",
ConnectionTimeoutSeconds: 120,
}},
}},
parameters: []*proto.RichParameter{{
Name: "Example",
Type: "string",
Required: true,
Order: 55,
}, {
Name: "number_example",
Type: "number",
DefaultValue: "4",
ValidationMin: nil,
ValidationMax: nil,
Name: "Sample",
Type: "string",
Description: "blah blah",
DefaultValue: "ok",
Order: 99,
}},
},
"rich-parameters-validation": {
@@ -399,22 +426,10 @@ func TestConvertResources(t *testing.T) {
}},
}},
parameters: []*proto.RichParameter{{
Name: "number_example_min_max",
Name: "number_example",
Type: "number",
DefaultValue: "4",
ValidationMin: terraform.PtrInt32(3),
ValidationMax: terraform.PtrInt32(6),
}, {
Name: "number_example_min",
Type: "number",
DefaultValue: "4",
ValidationMin: terraform.PtrInt32(3),
ValidationMax: nil,
}, {
Name: "number_example_min_zero",
Type: "number",
DefaultValue: "4",
ValidationMin: terraform.PtrInt32(0),
ValidationMin: nil,
ValidationMax: nil,
}, {
Name: "number_example_max",
@@ -429,10 +444,22 @@ func TestConvertResources(t *testing.T) {
ValidationMin: nil,
ValidationMax: terraform.PtrInt32(0),
}, {
Name: "number_example",
Name: "number_example_min",
Type: "number",
DefaultValue: "4",
ValidationMin: nil,
ValidationMin: terraform.PtrInt32(3),
ValidationMax: nil,
}, {
Name: "number_example_min_max",
Type: "number",
DefaultValue: "4",
ValidationMin: terraform.PtrInt32(3),
ValidationMax: terraform.PtrInt32(6),
}, {
Name: "number_example_min_zero",
Type: "number",
DefaultValue: "4",
ValidationMin: terraform.PtrInt32(0),
ValidationMax: nil,
}},
},
@@ -478,7 +505,7 @@ func TestConvertResources(t *testing.T) {
// and that no errors occur!
modules = append(modules, tfPlan.PlannedValues.RootModule)
}
state, err := terraform.ConvertState(modules, string(tfPlanGraph), richParameterResourceNames(expected.parameters))
state, err := terraform.ConvertState(modules, string(tfPlanGraph))
require.NoError(t, err)
sortResources(state.Resources)
sort.Strings(state.GitAuthProviders)
@@ -532,7 +559,7 @@ func TestConvertResources(t *testing.T) {
tfStateGraph, err := os.ReadFile(filepath.Join(dir, folderName+".tfstate.dot"))
require.NoError(t, err)
state, err := terraform.ConvertState([]*tfjson.StateModule{tfState.Values.RootModule}, string(tfStateGraph), richParameterResourceNames(expected.parameters))
state, err := terraform.ConvertState([]*tfjson.StateModule{tfState.Values.RootModule}, string(tfStateGraph))
require.NoError(t, err)
sortResources(state.Resources)
sort.Strings(state.GitAuthProviders)
@@ -591,7 +618,7 @@ func TestAppSlugValidation(t *testing.T) {
}
}
state, err := terraform.ConvertState([]*tfjson.StateModule{tfPlan.PlannedValues.RootModule}, string(tfPlanGraph), nil)
state, err := terraform.ConvertState([]*tfjson.StateModule{tfPlan.PlannedValues.RootModule}, string(tfPlanGraph))
require.Nil(t, state)
require.Error(t, err)
require.ErrorContains(t, err, "invalid app slug")
@@ -603,7 +630,7 @@ func TestAppSlugValidation(t *testing.T) {
}
}
state, err = terraform.ConvertState([]*tfjson.StateModule{tfPlan.PlannedValues.RootModule}, string(tfPlanGraph), nil)
state, err = terraform.ConvertState([]*tfjson.StateModule{tfPlan.PlannedValues.RootModule}, string(tfPlanGraph))
require.Nil(t, state)
require.Error(t, err)
require.ErrorContains(t, err, "duplicate app slug")
@@ -634,7 +661,7 @@ func TestParameterValidation(t *testing.T) {
}
}
state, err := terraform.ConvertState([]*tfjson.StateModule{tfPlan.PriorState.Values.RootModule}, string(tfPlanGraph), names)
state, err := terraform.ConvertState([]*tfjson.StateModule{tfPlan.PriorState.Values.RootModule}, string(tfPlanGraph))
require.Nil(t, state)
require.Error(t, err)
require.ErrorContains(t, err, "coder_parameter names must be unique but \"identical\" appears multiple times")
@@ -650,7 +677,7 @@ func TestParameterValidation(t *testing.T) {
}
}
state, err = terraform.ConvertState([]*tfjson.StateModule{tfPlan.PriorState.Values.RootModule}, string(tfPlanGraph), names)
state, err = terraform.ConvertState([]*tfjson.StateModule{tfPlan.PriorState.Values.RootModule}, string(tfPlanGraph))
require.Nil(t, state)
require.Error(t, err)
require.ErrorContains(t, err, "coder_parameter names must be unique but \"identical-0\" and \"identical-1\" appear multiple times")
@@ -666,7 +693,7 @@ func TestParameterValidation(t *testing.T) {
}
}
state, err = terraform.ConvertState([]*tfjson.StateModule{tfPlan.PriorState.Values.RootModule}, string(tfPlanGraph), names)
state, err = terraform.ConvertState([]*tfjson.StateModule{tfPlan.PriorState.Values.RootModule}, string(tfPlanGraph))
require.Nil(t, state)
require.Error(t, err)
require.ErrorContains(t, err, "coder_parameter names must be unique but \"identical-0\", \"identical-1\" and \"identical-2\" appear multiple times")
@@ -716,7 +743,7 @@ func TestInstanceTypeAssociation(t *testing.T) {
subgraph "root" {
"[root] `+tc.ResourceType+`.dev" [label = "`+tc.ResourceType+`.dev", shape = "box"]
}
}`, nil)
}`)
require.NoError(t, err)
require.Len(t, state.Resources, 1)
require.Equal(t, state.Resources[0].GetInstanceType(), instanceType)
@@ -787,7 +814,7 @@ func TestInstanceIDAssociation(t *testing.T) {
"[root] `+tc.ResourceType+`.dev" -> "[root] coder_agent.dev"
}
}
`, nil)
`)
require.NoError(t, err)
require.Len(t, state.Resources, 1)
require.Len(t, state.Resources[0].Agents, 1)
@@ -816,11 +843,3 @@ func sortResources(resources []*proto.Resource) {
})
}
}
func richParameterResourceNames(parameters []*proto.RichParameter) []string {
var names []string
for _, p := range parameters {
names = append(names, strings.ToLower(p.Name))
}
return names
}
@@ -0,0 +1,31 @@
terraform {
required_providers {
coder = {
source = "coder/coder"
version = "0.9.0"
}
}
}
data "coder_parameter" "sample" {
name = "Sample"
type = "string"
description = "blah blah"
default = "ok"
order = 99
}
data "coder_parameter" "example" {
name = "Example"
type = "string"
order = 55
}
resource "coder_agent" "dev" {
os = "windows"
arch = "arm64"
}
resource "null_resource" "dev" {
depends_on = [coder_agent.dev]
}
@@ -0,0 +1,24 @@
digraph {
compound = "true"
newrank = "true"
subgraph "root" {
"[root] coder_agent.dev (expand)" [label = "coder_agent.dev", shape = "box"]
"[root] data.coder_parameter.example (expand)" [label = "data.coder_parameter.example", shape = "box"]
"[root] data.coder_parameter.sample (expand)" [label = "data.coder_parameter.sample", shape = "box"]
"[root] null_resource.dev (expand)" [label = "null_resource.dev", shape = "box"]
"[root] provider[\"registry.terraform.io/coder/coder\"]" [label = "provider[\"registry.terraform.io/coder/coder\"]", shape = "diamond"]
"[root] provider[\"registry.terraform.io/hashicorp/null\"]" [label = "provider[\"registry.terraform.io/hashicorp/null\"]", shape = "diamond"]
"[root] coder_agent.dev (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]"
"[root] data.coder_parameter.example (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]"
"[root] data.coder_parameter.sample (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]"
"[root] null_resource.dev (expand)" -> "[root] coder_agent.dev (expand)"
"[root] null_resource.dev (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/null\"]"
"[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] coder_agent.dev (expand)"
"[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] data.coder_parameter.example (expand)"
"[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] data.coder_parameter.sample (expand)"
"[root] provider[\"registry.terraform.io/hashicorp/null\"] (close)" -> "[root] null_resource.dev (expand)"
"[root] root" -> "[root] provider[\"registry.terraform.io/coder/coder\"] (close)"
"[root] root" -> "[root] provider[\"registry.terraform.io/hashicorp/null\"] (close)"
}
}
@@ -0,0 +1,269 @@
{
"format_version": "1.2",
"terraform_version": "1.5.1",
"planned_values": {
"root_module": {
"resources": [
{
"address": "coder_agent.dev",
"mode": "managed",
"type": "coder_agent",
"name": "dev",
"provider_name": "registry.terraform.io/coder/coder",
"schema_version": 0,
"values": {
"arch": "arm64",
"auth": "token",
"connection_timeout": 120,
"dir": null,
"env": null,
"login_before_ready": true,
"metadata": [],
"motd_file": null,
"os": "windows",
"shutdown_script": null,
"shutdown_script_timeout": 300,
"startup_script": null,
"startup_script_behavior": null,
"startup_script_timeout": 300,
"troubleshooting_url": null
},
"sensitive_values": {
"metadata": []
}
},
{
"address": "null_resource.dev",
"mode": "managed",
"type": "null_resource",
"name": "dev",
"provider_name": "registry.terraform.io/hashicorp/null",
"schema_version": 0,
"values": {
"triggers": null
},
"sensitive_values": {}
}
]
}
},
"resource_changes": [
{
"address": "coder_agent.dev",
"mode": "managed",
"type": "coder_agent",
"name": "dev",
"provider_name": "registry.terraform.io/coder/coder",
"change": {
"actions": [
"create"
],
"before": null,
"after": {
"arch": "arm64",
"auth": "token",
"connection_timeout": 120,
"dir": null,
"env": null,
"login_before_ready": true,
"metadata": [],
"motd_file": null,
"os": "windows",
"shutdown_script": null,
"shutdown_script_timeout": 300,
"startup_script": null,
"startup_script_behavior": null,
"startup_script_timeout": 300,
"troubleshooting_url": null
},
"after_unknown": {
"id": true,
"init_script": true,
"metadata": [],
"token": true
},
"before_sensitive": false,
"after_sensitive": {
"metadata": [],
"token": true
}
}
},
{
"address": "null_resource.dev",
"mode": "managed",
"type": "null_resource",
"name": "dev",
"provider_name": "registry.terraform.io/hashicorp/null",
"change": {
"actions": [
"create"
],
"before": null,
"after": {
"triggers": null
},
"after_unknown": {
"id": true
},
"before_sensitive": false,
"after_sensitive": {}
}
}
],
"prior_state": {
"format_version": "1.0",
"terraform_version": "1.5.1",
"values": {
"root_module": {
"resources": [
{
"address": "data.coder_parameter.example",
"mode": "data",
"type": "coder_parameter",
"name": "example",
"provider_name": "registry.terraform.io/coder/coder",
"schema_version": 0,
"values": {
"default": null,
"description": null,
"display_name": null,
"icon": null,
"id": "0d872aa0-e755-48a7-986d-4d6aa8ad9a17",
"legacy_variable": null,
"legacy_variable_name": null,
"mutable": false,
"name": "Example",
"option": null,
"optional": false,
"order": 55,
"type": "string",
"validation": [],
"value": ""
},
"sensitive_values": {
"validation": []
}
},
{
"address": "data.coder_parameter.sample",
"mode": "data",
"type": "coder_parameter",
"name": "sample",
"provider_name": "registry.terraform.io/coder/coder",
"schema_version": 0,
"values": {
"default": "ok",
"description": "blah blah",
"display_name": null,
"icon": null,
"id": "76eff4ae-28f0-409f-b002-58ebced3fdcb",
"legacy_variable": null,
"legacy_variable_name": null,
"mutable": false,
"name": "Sample",
"option": null,
"optional": true,
"order": 99,
"type": "string",
"validation": [],
"value": "ok"
},
"sensitive_values": {
"validation": []
}
}
]
}
}
},
"configuration": {
"provider_config": {
"coder": {
"name": "coder",
"full_name": "registry.terraform.io/coder/coder",
"version_constraint": "0.9.0"
},
"null": {
"name": "null",
"full_name": "registry.terraform.io/hashicorp/null"
}
},
"root_module": {
"resources": [
{
"address": "coder_agent.dev",
"mode": "managed",
"type": "coder_agent",
"name": "dev",
"provider_config_key": "coder",
"expressions": {
"arch": {
"constant_value": "arm64"
},
"os": {
"constant_value": "windows"
}
},
"schema_version": 0
},
{
"address": "null_resource.dev",
"mode": "managed",
"type": "null_resource",
"name": "dev",
"provider_config_key": "null",
"schema_version": 0,
"depends_on": [
"coder_agent.dev"
]
},
{
"address": "data.coder_parameter.example",
"mode": "data",
"type": "coder_parameter",
"name": "example",
"provider_config_key": "coder",
"expressions": {
"name": {
"constant_value": "Example"
},
"order": {
"constant_value": 55
},
"type": {
"constant_value": "string"
}
},
"schema_version": 0
},
{
"address": "data.coder_parameter.sample",
"mode": "data",
"type": "coder_parameter",
"name": "sample",
"provider_config_key": "coder",
"expressions": {
"default": {
"constant_value": "ok"
},
"description": {
"constant_value": "blah blah"
},
"name": {
"constant_value": "Sample"
},
"order": {
"constant_value": 99
},
"type": {
"constant_value": "string"
}
},
"schema_version": 0
}
]
}
},
"timestamp": "2023-06-30T08:41:59Z"
}
@@ -0,0 +1,24 @@
digraph {
compound = "true"
newrank = "true"
subgraph "root" {
"[root] coder_agent.dev (expand)" [label = "coder_agent.dev", shape = "box"]
"[root] data.coder_parameter.example (expand)" [label = "data.coder_parameter.example", shape = "box"]
"[root] data.coder_parameter.sample (expand)" [label = "data.coder_parameter.sample", shape = "box"]
"[root] null_resource.dev (expand)" [label = "null_resource.dev", shape = "box"]
"[root] provider[\"registry.terraform.io/coder/coder\"]" [label = "provider[\"registry.terraform.io/coder/coder\"]", shape = "diamond"]
"[root] provider[\"registry.terraform.io/hashicorp/null\"]" [label = "provider[\"registry.terraform.io/hashicorp/null\"]", shape = "diamond"]
"[root] coder_agent.dev (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]"
"[root] data.coder_parameter.example (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]"
"[root] data.coder_parameter.sample (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]"
"[root] null_resource.dev (expand)" -> "[root] coder_agent.dev (expand)"
"[root] null_resource.dev (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/null\"]"
"[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] coder_agent.dev (expand)"
"[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] data.coder_parameter.example (expand)"
"[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] data.coder_parameter.sample (expand)"
"[root] provider[\"registry.terraform.io/hashicorp/null\"] (close)" -> "[root] null_resource.dev (expand)"
"[root] root" -> "[root] provider[\"registry.terraform.io/coder/coder\"] (close)"
"[root] root" -> "[root] provider[\"registry.terraform.io/hashicorp/null\"] (close)"
}
}
@@ -0,0 +1,114 @@
{
"format_version": "1.0",
"terraform_version": "1.5.1",
"values": {
"root_module": {
"resources": [
{
"address": "data.coder_parameter.example",
"mode": "data",
"type": "coder_parameter",
"name": "example",
"provider_name": "registry.terraform.io/coder/coder",
"schema_version": 0,
"values": {
"default": null,
"description": null,
"display_name": null,
"icon": null,
"id": "885aeb22-be2b-486a-8e0d-1883c7e27b3c",
"legacy_variable": null,
"legacy_variable_name": null,
"mutable": false,
"name": "Example",
"option": null,
"optional": false,
"order": 55,
"type": "string",
"validation": [],
"value": ""
},
"sensitive_values": {
"validation": []
}
},
{
"address": "data.coder_parameter.sample",
"mode": "data",
"type": "coder_parameter",
"name": "sample",
"provider_name": "registry.terraform.io/coder/coder",
"schema_version": 0,
"values": {
"default": "ok",
"description": "blah blah",
"display_name": null,
"icon": null,
"id": "be62f5ff-0e46-44f1-84e2-3fe0e57bb16f",
"legacy_variable": null,
"legacy_variable_name": null,
"mutable": false,
"name": "Sample",
"option": null,
"optional": true,
"order": 99,
"type": "string",
"validation": [],
"value": "ok"
},
"sensitive_values": {
"validation": []
}
},
{
"address": "coder_agent.dev",
"mode": "managed",
"type": "coder_agent",
"name": "dev",
"provider_name": "registry.terraform.io/coder/coder",
"schema_version": 0,
"values": {
"arch": "arm64",
"auth": "token",
"connection_timeout": 120,
"dir": null,
"env": null,
"id": "7fe5bde1-7063-4426-9acc-76f5a8834fa6",
"init_script": "",
"login_before_ready": true,
"metadata": [],
"motd_file": null,
"os": "windows",
"shutdown_script": null,
"shutdown_script_timeout": 300,
"startup_script": null,
"startup_script_behavior": null,
"startup_script_timeout": 300,
"token": "9b835c2c-fbb1-4824-a79f-12017badf015",
"troubleshooting_url": null
},
"sensitive_values": {
"metadata": [],
"token": true
}
},
{
"address": "null_resource.dev",
"mode": "managed",
"type": "null_resource",
"name": "dev",
"provider_name": "registry.terraform.io/hashicorp/null",
"schema_version": 0,
"values": {
"id": "2516512654554147618",
"triggers": null
},
"sensitive_values": {},
"depends_on": [
"coder_agent.dev"
]
}
]
}
}
}