Skip to content

Commit

Permalink
BUGFIX: use ListType of ObjectType for Computed attributes inst…
Browse files Browse the repository at this point in the history
…ead of `Blocks` (hashicorp#246)

* Define `certificates` in `tls_certificate` data source, as a `List` of `Object`, instead of a blocks' list

This is necessary. so that we can express to Terraform that the attribute is indeed `Computed` and it can't be expected to be populated, until the data source is read.

This was creating an issue (see hashicorp#244), as Terraform protocol doesn't support expressing that a Block is Computed: only attributes can be.

This approach avoids the use of `NestedAttributes`, and as such is compatible with Protocol v5 (i.e. TF >= 0.12).

* Preparing CHANGELOG for v4.0.1

* Adding acceptance test for `tls_certificate` data source, exercising a scenario where "computed certificates are unknown until applied"

* Apply suggestions from code review

Co-authored-by: Brian Flad <[email protected]>
  • Loading branch information
Ivan De Marino and bflad authored Jul 25, 2022
1 parent 9637d90 commit 648f27e
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 75 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 4.0.1 (July 25, 2022)

BUG FIXES:

* data-source/tls_certificate: Prevented `empty list of object` error with `certificates` attribute ([#244](https://github.com/hashicorp/terraform-provider-tls/issues/244)).

## 4.0.0 (July 21, 2022)

NOTES:
Expand Down
79 changes: 4 additions & 75 deletions internal/provider/data_source_certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"strings"
"time"

"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/schemavalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
Expand Down Expand Up @@ -84,83 +83,13 @@ func (dst *certificateDataSourceType) GetSchema(_ context.Context) (tfsdk.Schema
Computed: true,
MarkdownDescription: "Unique identifier of this data source: hashing of the certificates in the chain.",
},
},
Blocks: map[string]tfsdk.Block{
"certificates": {
NestingMode: tfsdk.BlockNestingModeList,
MinItems: 0,
// TODO Remove the validators below, once a fix for https://github.com/hashicorp/terraform-plugin-framework/issues/421 ships
Validators: []tfsdk.AttributeValidator{
listvalidator.SizeAtLeast(0),
},
Attributes: map[string]tfsdk.Attribute{
"signature_algorithm": {
Type: types.StringType,
Computed: true,
MarkdownDescription: "The algorithm used to sign the certificate.",
},
"public_key_algorithm": {
Type: types.StringType,
Computed: true,
MarkdownDescription: "The key algorithm used to create the certificate.",
},
"serial_number": {
Type: types.StringType,
Computed: true,
MarkdownDescription: "Number that uniquely identifies the certificate with the CA's system. " +
"The `format` function can be used to convert this _base 10_ number " +
"into other bases, such as hex.",
},
"is_ca": {
Type: types.BoolType,
Computed: true,
MarkdownDescription: "`true` if the certificate is of a CA (Certificate Authority).",
},
"version": {
Type: types.Int64Type,
Computed: true,
MarkdownDescription: "The version the certificate is in.",
},
"issuer": {
Type: types.StringType,
Computed: true,
MarkdownDescription: "Who verified and signed the certificate, roughly following " +
"[RFC2253](https://tools.ietf.org/html/rfc2253).",
},
"subject": {
Type: types.StringType,
Computed: true,
MarkdownDescription: "The entity the certificate belongs to, roughly following " +
"[RFC2253](https://tools.ietf.org/html/rfc2253).",
},
"not_before": {
Type: types.StringType,
Computed: true,
MarkdownDescription: "The time after which the certificate is valid, as an " +
"[RFC3339](https://tools.ietf.org/html/rfc3339) timestamp.",
},
"not_after": {
Type: types.StringType,
Computed: true,
MarkdownDescription: "The time until which the certificate is invalid, as an " +
"[RFC3339](https://tools.ietf.org/html/rfc3339) timestamp.",
},
"sha1_fingerprint": {
Type: types.StringType,
Computed: true,
MarkdownDescription: "The SHA1 fingerprint of the public key of the certificate.",
},
"cert_pem": {
Type: types.StringType,
Computed: true,
MarkdownDescription: "Certificate data in [PEM (RFC 1421)](https://datatracker.ietf.org/doc/html/rfc1421) format. " +
"**NOTE**: the [underlying](https://pkg.go.dev/encoding/pem#Encode) " +
"[libraries](https://pkg.go.dev/golang.org/x/crypto/ssh#MarshalAuthorizedKey) that generate this " +
"value append a `\\n` at the end of the PEM. " +
"In case this disrupts your use case, we recommend using " +
"[`trimspace()`](https://www.terraform.io/language/functions/trimspace).",
Type: types.ListType{
ElemType: types.ObjectType{
AttrTypes: x509CertObjectAttrTypes(),
},
},
Computed: true,
MarkdownDescription: "The certificates protecting the site, with the root of the chain first.",
},
},
Expand Down
47 changes: 47 additions & 0 deletions internal/provider/data_source_certificate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -639,3 +639,50 @@ func TestDataSourceCertificate_MalformedURL(t *testing.T) {
},
})
}

func TestDataSourceCertificate_UnknownComputedCertificatesUntilApplied(t *testing.T) {
r.Test(t, r.TestCase{
ProtoV5ProviderFactories: protoV5ProviderFactories(),

Steps: []r.TestStep{
{

Config: `
resource "tls_private_key" "test" {
algorithm = "ED25519"
}
data "tls_certificate" "test" {
# This attribute value must be unknown to trigger
# the behavior, therefore this replaces the unknown
# value with a known value on apply, so the URL is valid.
url = replace(tls_private_key.test.id, "/^.*$/","https://terraform.io")
}
output "test" {
# This test must reference an underlying value of the
# certificates attribute to trigger the behavior.
value = data.tls_certificate.test.certificates[0].sha1_fingerprint
}
`,
Check: r.ComposeAggregateTestCheckFunc(
r.TestCheckResourceAttr("data.tls_certificate.test", "certificates.#", "3"),

// ISRG Root X1
r.TestCheckResourceAttr("data.tls_certificate.test", "certificates.0.issuer", "CN=DST Root CA X3,O=Digital Signature Trust Co."),
r.TestCheckResourceAttr("data.tls_certificate.test", "certificates.0.subject", "CN=ISRG Root X1,O=Internet Security Research Group,C=US"),

// R3
r.TestCheckResourceAttr("data.tls_certificate.test", "certificates.1.issuer", "CN=ISRG Root X1,O=Internet Security Research Group,C=US"),
r.TestCheckResourceAttrPair("data.tls_certificate.test", "certificates.1.issuer", "data.tls_certificate.test", "certificates.0.subject"),
r.TestCheckResourceAttr("data.tls_certificate.test", "certificates.1.subject", "CN=R3,O=Let's Encrypt,C=US"),

// terraform.io
r.TestCheckResourceAttr("data.tls_certificate.test", "certificates.2.issuer", "CN=R3,O=Let's Encrypt,C=US"),
r.TestCheckResourceAttrPair("data.tls_certificate.test", "certificates.2.issuer", "data.tls_certificate.test", "certificates.1.subject"),
r.TestCheckResourceAttr("data.tls_certificate.test", "certificates.2.subject", "CN=www.terraform.io"),
),
},
},
})
}

0 comments on commit 648f27e

Please sign in to comment.