Skip to content

Commit fce8a4a

Browse files
authored
feat: preserve order of rich parameters (coder#6689)
* WIP * TDD * Implement * WIP
1 parent 2321160 commit fce8a4a

7 files changed

+187
-89
lines changed

provisioner/terraform/resources.go

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,20 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error
9999
// Indexes Terraform resources by their label.
100100
// The label is what "terraform graph" uses to reference nodes.
101101
tfResourcesByLabel := map[string]map[string]*tfjson.StateResource{}
102+
103+
// Extra array to preserve the order of rich parameters.
104+
tfResourcesRichParameters := make([]*tfjson.StateResource, 0)
105+
102106
var findTerraformResources func(mod *tfjson.StateModule)
103107
findTerraformResources = func(mod *tfjson.StateModule) {
104108
for _, module := range mod.ChildModules {
105109
findTerraformResources(module)
106110
}
107111
for _, resource := range mod.Resources {
112+
if resource.Type == "coder_parameter" {
113+
tfResourcesRichParameters = append(tfResourcesRichParameters, resource)
114+
}
115+
108116
label := convertAddressToLabel(resource.Address)
109117
if tfResourcesByLabel[label] == nil {
110118
tfResourcesByLabel[label] = map[string]*tfjson.StateResource{}
@@ -434,46 +442,44 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error
434442
}
435443

436444
parameters := make([]*proto.RichParameter, 0)
437-
for _, tfResources := range tfResourcesByLabel {
438-
for _, resource := range tfResources {
439-
if resource.Type != "coder_parameter" {
440-
continue
441-
}
442-
var param provider.Parameter
443-
err = mapstructure.Decode(resource.AttributeValues, &param)
444-
if err != nil {
445-
return nil, xerrors.Errorf("decode map values for coder_parameter.%s: %w", resource.Name, err)
446-
}
447-
protoParam := &proto.RichParameter{
448-
Name: param.Name,
449-
Description: param.Description,
450-
Type: param.Type,
451-
Mutable: param.Mutable,
452-
DefaultValue: param.Default,
453-
Icon: param.Icon,
454-
Required: !param.Optional,
455-
LegacyVariableName: param.LegacyVariableName,
456-
}
457-
if len(param.Validation) == 1 {
458-
protoParam.ValidationRegex = param.Validation[0].Regex
459-
protoParam.ValidationError = param.Validation[0].Error
460-
protoParam.ValidationMax = int32(param.Validation[0].Max)
461-
protoParam.ValidationMin = int32(param.Validation[0].Min)
462-
protoParam.ValidationMonotonic = param.Validation[0].Monotonic
463-
}
464-
if len(param.Option) > 0 {
465-
protoParam.Options = make([]*proto.RichParameterOption, 0, len(param.Option))
466-
for _, option := range param.Option {
467-
protoParam.Options = append(protoParam.Options, &proto.RichParameterOption{
468-
Name: option.Name,
469-
Description: option.Description,
470-
Value: option.Value,
471-
Icon: option.Icon,
472-
})
473-
}
445+
for _, resource := range tfResourcesRichParameters {
446+
if resource.Type != "coder_parameter" {
447+
continue
448+
}
449+
var param provider.Parameter
450+
err = mapstructure.Decode(resource.AttributeValues, &param)
451+
if err != nil {
452+
return nil, xerrors.Errorf("decode map values for coder_parameter.%s: %w", resource.Name, err)
453+
}
454+
protoParam := &proto.RichParameter{
455+
Name: param.Name,
456+
Description: param.Description,
457+
Type: param.Type,
458+
Mutable: param.Mutable,
459+
DefaultValue: param.Default,
460+
Icon: param.Icon,
461+
Required: !param.Optional,
462+
LegacyVariableName: param.LegacyVariableName,
463+
}
464+
if len(param.Validation) == 1 {
465+
protoParam.ValidationRegex = param.Validation[0].Regex
466+
protoParam.ValidationError = param.Validation[0].Error
467+
protoParam.ValidationMax = int32(param.Validation[0].Max)
468+
protoParam.ValidationMin = int32(param.Validation[0].Min)
469+
protoParam.ValidationMonotonic = param.Validation[0].Monotonic
470+
}
471+
if len(param.Option) > 0 {
472+
protoParam.Options = make([]*proto.RichParameterOption, 0, len(param.Option))
473+
for _, option := range param.Option {
474+
protoParam.Options = append(protoParam.Options, &proto.RichParameterOption{
475+
Name: option.Name,
476+
Description: option.Description,
477+
Value: option.Value,
478+
Icon: option.Icon,
479+
})
474480
}
475-
parameters = append(parameters, protoParam)
476481
}
482+
parameters = append(parameters, protoParam)
477483
}
478484

479485
// A map is used to ensure we don't have duplicates!

provisioner/terraform/resources_test.go

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -279,12 +279,14 @@ func TestConvertResources(t *testing.T) {
279279
Name: "dev",
280280
Type: "null_resource",
281281
Agents: []*proto.Agent{{
282-
Name: "dev",
283-
OperatingSystem: "windows",
284-
Architecture: "arm64",
285-
Auth: &proto.Agent_Token{},
286-
LoginBeforeReady: true,
287-
ConnectionTimeoutSeconds: 120,
282+
Name: "dev",
283+
OperatingSystem: "windows",
284+
ShutdownScriptTimeoutSeconds: 300,
285+
StartupScriptTimeoutSeconds: 300,
286+
Architecture: "arm64",
287+
Auth: &proto.Agent_Token{},
288+
LoginBeforeReady: true,
289+
ConnectionTimeoutSeconds: 120,
288290
}},
289291
}},
290292
parameters: []*proto.RichParameter{{
@@ -298,6 +300,11 @@ func TestConvertResources(t *testing.T) {
298300
Value: "second",
299301
}},
300302
Required: true,
303+
}, {
304+
Name: "Sample",
305+
Type: "string",
306+
Description: "blah blah",
307+
DefaultValue: "ok",
301308
}},
302309
},
303310
"git-auth-providers": {
@@ -345,7 +352,6 @@ func TestConvertResources(t *testing.T) {
345352
state, err := terraform.ConvertState(modules, string(tfPlanGraph))
346353
require.NoError(t, err)
347354
sortResources(state.Resources)
348-
sortParameters(state.Parameters)
349355
sort.Strings(state.GitAuthProviders)
350356

351357
expectedNoMetadata := make([]*proto.Resource, 0)
@@ -399,7 +405,6 @@ func TestConvertResources(t *testing.T) {
399405
state, err := terraform.ConvertState([]*tfjson.StateModule{tfState.Values.RootModule}, string(tfStateGraph))
400406
require.NoError(t, err)
401407
sortResources(state.Resources)
402-
sortParameters(state.Parameters)
403408
sort.Strings(state.GitAuthProviders)
404409
for _, resource := range state.Resources {
405410
for _, agent := range resource.Agents {
@@ -618,14 +623,3 @@ func sortResources(resources []*proto.Resource) {
618623
})
619624
}
620625
}
621-
622-
func sortParameters(parameters []*proto.RichParameter) {
623-
sort.Slice(parameters, func(i, j int) bool {
624-
return parameters[i].Name < parameters[j].Name
625-
})
626-
for _, parameter := range parameters {
627-
sort.Slice(parameter.Options, func(i, j int) bool {
628-
return parameter.Options[i].Name < parameter.Options[j].Name
629-
})
630-
}
631-
}

provisioner/terraform/testdata/rich-parameters/rich-parameters.tf

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,18 @@ terraform {
22
required_providers {
33
coder = {
44
source = "coder/coder"
5-
version = "0.6.6"
5+
version = "0.6.20"
66
}
77
}
88
}
99

10+
data "coder_parameter" "sample" {
11+
name = "Sample"
12+
type = "string"
13+
description = "blah blah"
14+
default = "ok"
15+
}
16+
1017
data "coder_parameter" "example" {
1118
name = "Example"
1219
type = "string"

provisioner/terraform/testdata/rich-parameters/rich-parameters.tfplan.dot

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

provisioner/terraform/testdata/rich-parameters/rich-parameters.tfplan.json

Lines changed: 59 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

provisioner/terraform/testdata/rich-parameters/rich-parameters.tfstate.dot

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)