From 0d8385d2f1b05cb206040ff21864c42d6a35bd2c Mon Sep 17 00:00:00 2001 From: David Wahler Date: Mon, 25 Jul 2022 22:43:00 +0000 Subject: [PATCH 1/8] feat: Add support for "coder_metadata" resource type --- internal/provider/provider.go | 63 ++++++++++++++++++++++++++++++ internal/provider/provider_test.go | 55 ++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index ac8b67f0..4941d2f1 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -318,6 +318,69 @@ func New() *schema.Provider { }, }, }, + "coder_metadata": { + Description: "Use this resource to attach key/value pairs to a resource. They will be " + + "displayed in the Coder dashboard.", + CreateContext: func(c context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { + resourceData.SetId(uuid.NewString()) + return nil + }, + ReadContext: func(c context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { + return nil + }, + DeleteContext: func(ctx context.Context, rd *schema.ResourceData, i interface{}) diag.Diagnostics { + return nil + }, + Schema: map[string]*schema.Schema{ + "resource_id": { + Type: schema.TypeString, + Description: "The \"id\" property of another resource that metadata should be attached to.", + ForceNew: true, + Required: true, + }, + "pair": { + Type: schema.TypeList, + ForceNew: true, + Required: true, + Elem: &schema.Resource{ + CreateContext: func(c context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { + resourceData.SetId(uuid.NewString()) + return nil + }, + ReadContext: func(c context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { + return nil + }, + DeleteContext: func(ctx context.Context, rd *schema.ResourceData, i interface{}) diag.Diagnostics { + return nil + }, + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Description: "The key of this metadata item.", + ForceNew: true, + Required: true, + }, + "value": { + Type: schema.TypeString, + Description: "The value of this metadata item.", + ForceNew: true, + Required: true, + }, + "sensitive": { + Type: schema.TypeBool, + Description: "Set to \"true\" to for items such as API keys whose values should be " + + "hidden from view by default. Note that this does not prevent metadata from " + + "being retrieved using the API, so it is not suitable for secrets that should " + + "not be exposed to workspace users.", + ForceNew: true, + Optional: true, + Default: false, + }, + }, + }, + }, + }, + }, }, } } diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index 0f2d45e0..248b26b0 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -186,3 +186,58 @@ func TestApp(t *testing.T) { }}, }) } + +func TestMetadata(t *testing.T) { + t.Parallel() + prov := provider.New() + resource.Test(t, resource.TestCase{ + Providers: map[string]*schema.Provider{ + "coder": prov, + }, + IsUnitTest: true, + Steps: []resource.TestStep{{ + Config: ` + provider "coder" { + } + resource "coder_agent" "dev" { + os = "linux" + arch = "amd64" + } + resource "coder_metadata" "agent" { + resource_id = coder_agent.dev.id + pair { + key = "foo" + value = "bar" + } + pair { + key = "secret" + value = "squirrel" + sensitive = true + } + } + `, + Check: func(state *terraform.State) error { + require.Len(t, state.Modules, 1) + require.Len(t, state.Modules[0].Resources, 2) + agent := state.Modules[0].Resources["coder_agent.dev"] + require.NotNil(t, agent) + metadata := state.Modules[0].Resources["coder_metadata.agent"] + require.NotNil(t, metadata) + t.Logf("metadata attributes: %#v", metadata.Primary.Attributes) + for key, expected := range map[string]string{ + "resource_id": agent.Primary.Attributes["id"], + "pair.#": "2", + "pair.0.key": "foo", + "pair.0.value": "bar", + "pair.0.sensitive": "false", + "pair.1.key": "secret", + "pair.1.value": "squirrel", + "pair.1.sensitive": "true", + } { + require.Equal(t, expected, metadata.Primary.Attributes[key]) + } + return nil + }, + }}, + }) +} From c5bc4031c3ff189d871242c1e70b5d5c10f421f3 Mon Sep 17 00:00:00 2001 From: David Wahler Date: Wed, 27 Jul 2022 00:52:28 +0000 Subject: [PATCH 2/8] Add ugly hack to support nullable values --- internal/provider/provider.go | 88 ++++++++++++++++++++++++++---- internal/provider/provider_test.go | 23 +++++++- 2 files changed, 99 insertions(+), 12 deletions(-) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 4941d2f1..955b2c00 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -2,6 +2,7 @@ package provider import ( "context" + "errors" "fmt" "net/url" "os" @@ -9,6 +10,7 @@ import ( "strings" "github.com/google/uuid" + "github.com/hashicorp/go-cty/cty" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -323,6 +325,16 @@ func New() *schema.Provider { "displayed in the Coder dashboard.", CreateContext: func(c context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { resourceData.SetId(uuid.NewString()) + + pairs, err := populateIsNull(resourceData) + if err != nil { + return errorAsDiagnostics(err) + } + err = resourceData.Set("pair", pairs) + if err != nil { + return errorAsDiagnostics(err) + } + return nil }, ReadContext: func(c context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { @@ -343,16 +355,6 @@ func New() *schema.Provider { ForceNew: true, Required: true, Elem: &schema.Resource{ - CreateContext: func(c context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { - resourceData.SetId(uuid.NewString()) - return nil - }, - ReadContext: func(c context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { - return nil - }, - DeleteContext: func(ctx context.Context, rd *schema.ResourceData, i interface{}) diag.Diagnostics { - return nil - }, Schema: map[string]*schema.Schema{ "key": { Type: schema.TypeString, @@ -364,7 +366,7 @@ func New() *schema.Provider { Type: schema.TypeString, Description: "The value of this metadata item.", ForceNew: true, - Required: true, + Optional: true, }, "sensitive": { Type: schema.TypeBool, @@ -376,6 +378,11 @@ func New() *schema.Provider { Optional: true, Default: false, }, + "is_null": { + Type: schema.TypeBool, + ForceNew: true, + Computed: true, + }, }, }, }, @@ -419,3 +426,62 @@ func updateInitScript(resourceData *schema.ResourceData, i interface{}) diag.Dia } return nil } + +// populateIsNull reads the raw plan for a coder_metadata resource being created, +// figures out which items have null "value"s, and augments them by setting the +// "is_null" field to true. This ugly hack is necessary because terraform-plugin-sdk +// is designed around a old version of Terraform that didn't support nullable fields, +// and it doesn't correctly propagate null values for primitive types. +// Returns an interface{} representing the new value of the "pair" field, or an error. +func populateIsNull(resourceData *schema.ResourceData) (result interface{}, err error) { + // The cty package reports type mismatches by panicking + defer func() { + if r := recover(); r != nil { + err = errors.New(fmt.Sprintf("panic while handling coder_metadata: %#v", r)) + } + }() + + rawPlan := resourceData.GetRawPlan() + pairs := rawPlan.GetAttr("pair").AsValueSlice() + + var resultPairs []interface{} + for _, pair := range pairs { + resultPair := map[string]interface{}{ + "key": valueAsString(pair.GetAttr("key")), + "value": valueAsString(pair.GetAttr("value")), + "sensitive": valueAsBool(pair.GetAttr("sensitive")), + } + if pair.GetAttr("value").IsNull() { + resultPair["is_null"] = true + } + resultPairs = append(resultPairs, resultPair) + } + + return resultPairs, nil +} + +// valueAsString takes a cty.Value that may be a string or null, and converts it to either a Go string +// or a nil interface{} +func valueAsString(value cty.Value) interface{} { + if value.IsNull() { + return "" + } + return value.AsString() +} + +// valueAsString takes a cty.Value that may be a boolean or null, and converts it to either a Go bool +// or a nil interface{} +func valueAsBool(value cty.Value) interface{} { + if value.IsNull() { + return nil + } + return value.True() +} + +// errorAsDiagnostic transforms a Go error to a diag.Diagnostics object representing a fatal error. +func errorAsDiagnostics(err error) diag.Diagnostics { + return []diag.Diagnostic{{ + Severity: diag.Error, + Summary: err.Error(), + }} +} diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index 248b26b0..0e7a7513 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -214,6 +214,17 @@ func TestMetadata(t *testing.T) { value = "squirrel" sensitive = true } + pair { + key = "implicit_null" + } + pair { + key = "explicit_null" + value = null + } + pair { + key = "empty" + value = "" + } } `, Check: func(state *terraform.State) error { @@ -226,13 +237,23 @@ func TestMetadata(t *testing.T) { t.Logf("metadata attributes: %#v", metadata.Primary.Attributes) for key, expected := range map[string]string{ "resource_id": agent.Primary.Attributes["id"], - "pair.#": "2", + "pair.#": "5", "pair.0.key": "foo", "pair.0.value": "bar", "pair.0.sensitive": "false", "pair.1.key": "secret", "pair.1.value": "squirrel", "pair.1.sensitive": "true", + "pair.2.key": "implicit_null", + "pair.2.is_null": "true", + "pair.2.sensitive": "false", + "pair.3.key": "explicit_null", + "pair.3.is_null": "true", + "pair.3.sensitive": "false", + "pair.4.key": "empty", + "pair.4.value": "", + "pair.4.is_null": "false", + "pair.4.sensitive": "false", } { require.Equal(t, expected, metadata.Primary.Attributes[key]) } From 37daed4c5665ae8b4e6304567aad7f0f097f42bc Mon Sep 17 00:00:00 2001 From: David Wahler Date: Fri, 29 Jul 2022 18:52:21 +0000 Subject: [PATCH 3/8] Update docs with previous changes --- docs/resources/app.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/resources/app.md b/docs/resources/app.md index 2bcfeb70..698f52a5 100644 --- a/docs/resources/app.md +++ b/docs/resources/app.md @@ -26,11 +26,11 @@ EOF } resource "coder_app" "code-server" { - agent_id = coder_agent.dev.id - name = "VS Code" - icon = data.coder_workspace.me.access_url + "/icons/vscode.svg" - url = "http://localhost:13337" - path = true + agent_id = coder_agent.dev.id + name = "VS Code" + icon = data.coder_workspace.me.access_url + "/icons/vscode.svg" + url = "http://localhost:13337" + relative_path = true } resource "coder_app" "vim" { From d901870d37aae9a2e6e31a94a79319b10638587a Mon Sep 17 00:00:00 2001 From: David Wahler Date: Fri, 29 Jul 2022 19:11:26 +0000 Subject: [PATCH 4/8] Update docs for this PR --- docs/resources/metadata.md | 61 +++++++++++++++++++ examples/resources/coder_metadata/resource.tf | 15 +++++ internal/provider/provider.go | 7 ++- 3 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 docs/resources/metadata.md create mode 100644 examples/resources/coder_metadata/resource.tf diff --git a/docs/resources/metadata.md b/docs/resources/metadata.md new file mode 100644 index 00000000..983cbcfa --- /dev/null +++ b/docs/resources/metadata.md @@ -0,0 +1,61 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "coder_metadata Resource - terraform-provider-coder" +subcategory: "" +description: |- + Use this resource to attach key/value pairs to a resource. They will be displayed in the Coder dashboard. +--- + +# coder_metadata (Resource) + +Use this resource to attach key/value pairs to a resource. They will be displayed in the Coder dashboard. + +## Example Usage + +```terraform +data "coder_workspace" "me" { +} + +resource "kubernetes_pod" "dev" { + count = data.coder_workspace.me.start_count +} + +resource "coder_metadata" "pod_info" { + count = data.coder_workspace.me.start_count + resource_id = kubernetes_pod.dev[0].id + pair { + key = "pod_uid" + value = kubernetes_pod.dev[0].uid + } +} +``` + + +## Schema + +### Required + +- `pair` (Block List, Min: 1) Each "pair" block defines a single key/value metadata pair. (see [below for nested schema](#nestedblock--pair)) +- `resource_id` (String) The "id" property of another resource that metadata should be attached to. + +### Read-Only + +- `id` (String) The ID of this resource. + + +### Nested Schema for `pair` + +Required: + +- `key` (String) The key of this metadata item. + +Optional: + +- `sensitive` (Boolean) Set to "true" to for items such as API keys whose values should be hidden from view by default. Note that this does not prevent metadata from being retrieved using the API, so it is not suitable for secrets that should not be exposed to workspace users. +- `value` (String) The value of this metadata item. + +Read-Only: + +- `is_null` (Boolean) + + diff --git a/examples/resources/coder_metadata/resource.tf b/examples/resources/coder_metadata/resource.tf new file mode 100644 index 00000000..ef56e14d --- /dev/null +++ b/examples/resources/coder_metadata/resource.tf @@ -0,0 +1,15 @@ +data "coder_workspace" "me" { +} + +resource "kubernetes_pod" "dev" { + count = data.coder_workspace.me.start_count +} + +resource "coder_metadata" "pod_info" { + count = data.coder_workspace.me.start_count + resource_id = kubernetes_pod.dev[0].id + pair { + key = "pod_uid" + value = kubernetes_pod.dev[0].uid + } +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 955b2c00..44a0dc5a 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -351,9 +351,10 @@ func New() *schema.Provider { Required: true, }, "pair": { - Type: schema.TypeList, - ForceNew: true, - Required: true, + Type: schema.TypeList, + Description: "Each \"pair\" block defines a single key/value metadata pair.", + ForceNew: true, + Required: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "key": { From 73a59e40c3716e50444035c29488af1dd4693433 Mon Sep 17 00:00:00 2001 From: David Wahler Date: Fri, 29 Jul 2022 20:08:06 +0000 Subject: [PATCH 5/8] Add additional examples --- docs/resources/metadata.md | 13 +++++++++++++ examples/resources/coder_metadata/resource.tf | 15 +++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/docs/resources/metadata.md b/docs/resources/metadata.md index 983cbcfa..ebf82aeb 100644 --- a/docs/resources/metadata.md +++ b/docs/resources/metadata.md @@ -20,13 +20,26 @@ resource "kubernetes_pod" "dev" { count = data.coder_workspace.me.start_count } +resource "tls_private_key" "example_key_pair" { + algorithm = "ECDSA" + ecdsa_curve = "P256" +} + resource "coder_metadata" "pod_info" { count = data.coder_workspace.me.start_count resource_id = kubernetes_pod.dev[0].id + pair { + key = "description" + value = "This description will show up in the Coder dashboard." + } pair { key = "pod_uid" value = kubernetes_pod.dev[0].uid } + pair { + key = "public_key" + value = tls_private_key.example_key.public_key_openssh + } } ``` diff --git a/examples/resources/coder_metadata/resource.tf b/examples/resources/coder_metadata/resource.tf index ef56e14d..820349de 100644 --- a/examples/resources/coder_metadata/resource.tf +++ b/examples/resources/coder_metadata/resource.tf @@ -5,11 +5,26 @@ resource "kubernetes_pod" "dev" { count = data.coder_workspace.me.start_count } +resource "tls_private_key" "example_key_pair" { + algorithm = "ECDSA" + ecdsa_curve = "P256" +} + resource "coder_metadata" "pod_info" { count = data.coder_workspace.me.start_count resource_id = kubernetes_pod.dev[0].id + pair { + key = "description" + value = "This description will show up in the Coder dashboard." + } pair { key = "pod_uid" value = kubernetes_pod.dev[0].uid } + pair { + key = "public_key" + value = tls_private_key.example_key.public_key_openssh + # The value of this item will be hidden from view by default + sensitive = true + } } From 73f0c3feb979f497e70231b9401bac2edb806188 Mon Sep 17 00:00:00 2001 From: David Wahler Date: Fri, 29 Jul 2022 20:13:23 +0000 Subject: [PATCH 6/8] oops, forgot to regenerate docs --- docs/resources/metadata.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/resources/metadata.md b/docs/resources/metadata.md index ebf82aeb..3f7e154c 100644 --- a/docs/resources/metadata.md +++ b/docs/resources/metadata.md @@ -39,6 +39,8 @@ resource "coder_metadata" "pod_info" { pair { key = "public_key" value = tls_private_key.example_key.public_key_openssh + # The value of this item will be hidden from view by default + sensitive = true } } ``` From b10fb1cd943322c5a76056b27a9db227b1f67fea Mon Sep 17 00:00:00 2001 From: David Wahler Date: Fri, 29 Jul 2022 20:14:27 +0000 Subject: [PATCH 7/8] fix typo --- docs/resources/metadata.md | 2 +- examples/resources/coder_metadata/resource.tf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/resources/metadata.md b/docs/resources/metadata.md index 3f7e154c..870a3497 100644 --- a/docs/resources/metadata.md +++ b/docs/resources/metadata.md @@ -38,7 +38,7 @@ resource "coder_metadata" "pod_info" { } pair { key = "public_key" - value = tls_private_key.example_key.public_key_openssh + value = tls_private_key.example_key_pair.public_key_openssh # The value of this item will be hidden from view by default sensitive = true } diff --git a/examples/resources/coder_metadata/resource.tf b/examples/resources/coder_metadata/resource.tf index 820349de..bc5b52aa 100644 --- a/examples/resources/coder_metadata/resource.tf +++ b/examples/resources/coder_metadata/resource.tf @@ -23,7 +23,7 @@ resource "coder_metadata" "pod_info" { } pair { key = "public_key" - value = tls_private_key.example_key.public_key_openssh + value = tls_private_key.example_key_pair.public_key_openssh # The value of this item will be hidden from view by default sensitive = true } From b3755bd2211988313b5a633081d13ef436e47ae3 Mon Sep 17 00:00:00 2001 From: David Wahler Date: Mon, 1 Aug 2022 19:42:59 +0000 Subject: [PATCH 8/8] Rename "pair" block to "item" --- docs/resources/metadata.md | 12 ++--- examples/resources/coder_metadata/resource.tf | 6 +-- internal/provider/provider.go | 32 +++++++------- internal/provider/provider_test.go | 44 +++++++++---------- 4 files changed, 47 insertions(+), 47 deletions(-) diff --git a/docs/resources/metadata.md b/docs/resources/metadata.md index 870a3497..493c7323 100644 --- a/docs/resources/metadata.md +++ b/docs/resources/metadata.md @@ -28,15 +28,15 @@ resource "tls_private_key" "example_key_pair" { resource "coder_metadata" "pod_info" { count = data.coder_workspace.me.start_count resource_id = kubernetes_pod.dev[0].id - pair { + item { key = "description" value = "This description will show up in the Coder dashboard." } - pair { + item { key = "pod_uid" value = kubernetes_pod.dev[0].uid } - pair { + item { key = "public_key" value = tls_private_key.example_key_pair.public_key_openssh # The value of this item will be hidden from view by default @@ -50,15 +50,15 @@ resource "coder_metadata" "pod_info" { ### Required -- `pair` (Block List, Min: 1) Each "pair" block defines a single key/value metadata pair. (see [below for nested schema](#nestedblock--pair)) +- `item` (Block List, Min: 1) Each "item" block defines a single metadata item consisting of a key/value pair. (see [below for nested schema](#nestedblock--item)) - `resource_id` (String) The "id" property of another resource that metadata should be attached to. ### Read-Only - `id` (String) The ID of this resource. - -### Nested Schema for `pair` + +### Nested Schema for `item` Required: diff --git a/examples/resources/coder_metadata/resource.tf b/examples/resources/coder_metadata/resource.tf index bc5b52aa..73222c8b 100644 --- a/examples/resources/coder_metadata/resource.tf +++ b/examples/resources/coder_metadata/resource.tf @@ -13,15 +13,15 @@ resource "tls_private_key" "example_key_pair" { resource "coder_metadata" "pod_info" { count = data.coder_workspace.me.start_count resource_id = kubernetes_pod.dev[0].id - pair { + item { key = "description" value = "This description will show up in the Coder dashboard." } - pair { + item { key = "pod_uid" value = kubernetes_pod.dev[0].uid } - pair { + item { key = "public_key" value = tls_private_key.example_key_pair.public_key_openssh # The value of this item will be hidden from view by default diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 44a0dc5a..ef6dc1fe 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -326,11 +326,11 @@ func New() *schema.Provider { CreateContext: func(c context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { resourceData.SetId(uuid.NewString()) - pairs, err := populateIsNull(resourceData) + items, err := populateIsNull(resourceData) if err != nil { return errorAsDiagnostics(err) } - err = resourceData.Set("pair", pairs) + err = resourceData.Set("item", items) if err != nil { return errorAsDiagnostics(err) } @@ -350,9 +350,9 @@ func New() *schema.Provider { ForceNew: true, Required: true, }, - "pair": { + "item": { Type: schema.TypeList, - Description: "Each \"pair\" block defines a single key/value metadata pair.", + Description: "Each \"item\" block defines a single metadata item consisting of a key/value pair.", ForceNew: true, Required: true, Elem: &schema.Resource{ @@ -433,7 +433,7 @@ func updateInitScript(resourceData *schema.ResourceData, i interface{}) diag.Dia // "is_null" field to true. This ugly hack is necessary because terraform-plugin-sdk // is designed around a old version of Terraform that didn't support nullable fields, // and it doesn't correctly propagate null values for primitive types. -// Returns an interface{} representing the new value of the "pair" field, or an error. +// Returns an interface{} representing the new value of the "item" field, or an error. func populateIsNull(resourceData *schema.ResourceData) (result interface{}, err error) { // The cty package reports type mismatches by panicking defer func() { @@ -443,22 +443,22 @@ func populateIsNull(resourceData *schema.ResourceData) (result interface{}, err }() rawPlan := resourceData.GetRawPlan() - pairs := rawPlan.GetAttr("pair").AsValueSlice() + items := rawPlan.GetAttr("item").AsValueSlice() - var resultPairs []interface{} - for _, pair := range pairs { - resultPair := map[string]interface{}{ - "key": valueAsString(pair.GetAttr("key")), - "value": valueAsString(pair.GetAttr("value")), - "sensitive": valueAsBool(pair.GetAttr("sensitive")), + var resultItems []interface{} + for _, item := range items { + resultItem := map[string]interface{}{ + "key": valueAsString(item.GetAttr("key")), + "value": valueAsString(item.GetAttr("value")), + "sensitive": valueAsBool(item.GetAttr("sensitive")), } - if pair.GetAttr("value").IsNull() { - resultPair["is_null"] = true + if item.GetAttr("value").IsNull() { + resultItem["is_null"] = true } - resultPairs = append(resultPairs, resultPair) + resultItems = append(resultItems, resultItem) } - return resultPairs, nil + return resultItems, nil } // valueAsString takes a cty.Value that may be a string or null, and converts it to either a Go string diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index 0e7a7513..e82c5ad7 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -205,23 +205,23 @@ func TestMetadata(t *testing.T) { } resource "coder_metadata" "agent" { resource_id = coder_agent.dev.id - pair { + item { key = "foo" value = "bar" } - pair { + item { key = "secret" value = "squirrel" sensitive = true } - pair { + item { key = "implicit_null" } - pair { + item { key = "explicit_null" value = null } - pair { + item { key = "empty" value = "" } @@ -237,23 +237,23 @@ func TestMetadata(t *testing.T) { t.Logf("metadata attributes: %#v", metadata.Primary.Attributes) for key, expected := range map[string]string{ "resource_id": agent.Primary.Attributes["id"], - "pair.#": "5", - "pair.0.key": "foo", - "pair.0.value": "bar", - "pair.0.sensitive": "false", - "pair.1.key": "secret", - "pair.1.value": "squirrel", - "pair.1.sensitive": "true", - "pair.2.key": "implicit_null", - "pair.2.is_null": "true", - "pair.2.sensitive": "false", - "pair.3.key": "explicit_null", - "pair.3.is_null": "true", - "pair.3.sensitive": "false", - "pair.4.key": "empty", - "pair.4.value": "", - "pair.4.is_null": "false", - "pair.4.sensitive": "false", + "item.#": "5", + "item.0.key": "foo", + "item.0.value": "bar", + "item.0.sensitive": "false", + "item.1.key": "secret", + "item.1.value": "squirrel", + "item.1.sensitive": "true", + "item.2.key": "implicit_null", + "item.2.is_null": "true", + "item.2.sensitive": "false", + "item.3.key": "explicit_null", + "item.3.is_null": "true", + "item.3.sensitive": "false", + "item.4.key": "empty", + "item.4.value": "", + "item.4.is_null": "false", + "item.4.sensitive": "false", } { require.Equal(t, expected, metadata.Primary.Attributes[key]) }