Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create origin ca data source #2961

Merged
merged 12 commits into from
Nov 26, 2023
3 changes: 3 additions & 0 deletions .changelog/2961.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-data-source
cloudflare_origin_ca_certificate
```
37 changes: 37 additions & 0 deletions docs/data-sources/origin_ca_certificate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
page_title: "cloudflare_origin_ca_certificate Data Source - Cloudflare"
subcategory: ""
description: |-
Use this data source to retrieve an existing origin ca certificate.
---

# cloudflare_origin_ca_certificate (Data Source)

Use this data source to retrieve an existing origin ca certificate.

## Example Usage

```terraform
data "cloudflare_origin_ca_certificate" "example" {
id = "REPLACE_ME"
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Optional

- `requested_validity` (Number) The number of days for which the certificate should be valid. [7 30 90 365 730 1095 5475]

### Read-Only

- `certificate` (String) The Origin CA certificate.
- `csr` (String) The Certificate Signing Request. Must be newline-encoded.
- `expires_on` (String) The datetime when the certificate will expire.
- `hostnames` (List of String) A list of hostnames or wildcard names bound to the certificate.
- `id` (String) The user's unique identifier.
- `request_type` (String) The signature type desired on the certificate. [origin-rsa origin-ecc keyless-certificate]
- `revoked_at` (String) The datetime when the certificate was revoked.


Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
data "cloudflare_origin_ca_certificate" "example" {
id = "REPLACE_ME"
}
2 changes: 2 additions & 0 deletions internal/framework/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/cloudflare/terraform-provider-cloudflare/internal/consts"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/api_token_permissions_groups"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/d1"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/origin_ca_certificate"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/r2_bucket"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/rulesets"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/turnstile"
Expand Down Expand Up @@ -337,6 +338,7 @@ func (p *CloudflareProvider) DataSources(ctx context.Context) []func() datasourc
return []func() datasource.DataSource{
api_token_permissions_groups.NewDataSource,
user.NewDataSource,
origin_ca_certificate.NewDataSource,
}
}

Expand Down
82 changes: 82 additions & 0 deletions internal/framework/service/origin_ca_certificate/data_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package origin_ca_certificate

import (
"context"
"fmt"

"github.com/cloudflare/cloudflare-go"

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/types"
)

var _ datasource.DataSource = &CloudflareOriginCACertificateDataSource{}

func NewDataSource() datasource.DataSource {
return &CloudflareOriginCACertificateDataSource{}
}

type CloudflareOriginCACertificateDataSource struct {
client *cloudflare.API
}

func (r *CloudflareOriginCACertificateDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_origin_ca_certificate"
}

func (r *CloudflareOriginCACertificateDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}

client, ok := req.ProviderData.(*cloudflare.API)

if !ok {
resp.Diagnostics.AddError(
"unexpected resource configure type",
fmt.Sprintf("expected *cloudflare.API, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)

return
}

r.client = client
}

func (r *CloudflareOriginCACertificateDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data CloudflareOriginCACertificateModel
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

cert, err := r.client.GetOriginCACertificate(ctx, data.ID.ValueString())
if err != nil {
resp.Diagnostics.AddError("failed to fetch Origin CA: %w", err.Error())
return
}

data = CloudflareOriginCACertificateModel{
ID: types.StringValue(cert.ID),
Certificate: types.StringValue(cert.Certificate),
CSR: types.StringValue(cert.CSR),
ExpiresOn: types.StringValue(cert.ExpiresOn.String()),
RequestType: types.StringValue(cert.RequestType),
RequestedValidity: types.Int64Value(int64(cert.RequestValidity)),
RevokedAt: types.StringValue(cert.RevokedAt.String()),
}

elements := make([]attr.Value, 0, len(cert.Hostnames))
for _, hostname := range cert.Hostnames {
elements = append(elements, types.StringValue(hostname))
}
listValue, diags := types.ListValue(types.StringType, elements)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
data.Hostnames = listValue

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package origin_ca_certificate_test

import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"os"
"testing"

"github.com/cloudflare/terraform-provider-cloudflare/internal/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)

func TestAccCloudflareOriginCACertificateDataSource(t *testing.T) {
zoneName := os.Getenv("CLOUDFLARE_DOMAIN")
csr, err := generateCSR(zoneName)
if err != nil {
t.Errorf("unable to generate CSR: %v", err)
return
}

name := fmt.Sprintf("data.cloudflare_origin_ca_certificate.%s", zoneName)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.TestAccPreCheck(t) },
ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccCheckCloudflareOriginCACertificateDataSource_Basic(zoneName, csr),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, "request_type", "origin-rsa"),
resource.TestCheckResourceAttr(name, "requested_validity", "7"),
resource.TestCheckResourceAttr(name, "hostnames.#", "1"),
resource.TestCheckResourceAttr(name, "hostnames.0", "example.com"),
),
},
},
})
}

func generateCSR(zone string) (string, error) {
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return "", err
}

template := x509.CertificateRequest{
Subject: pkix.Name{
CommonName: zone,
},
SignatureAlgorithm: x509.SHA256WithRSA,
}

csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &template, key)
if err != nil {
return "", err
}

csrPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrBytes})
return string(csrPem), nil
}

func testAccCheckCloudflareOriginCACertificateDataSource_Basic(zoneName string, csr string) string {
return fmt.Sprintf(`
resource "cloudflare_origin_ca_certificate" "%[1]s" {
csr = <<EOT
%[2]sEOT
hostnames = [ "%[1]s", "*.%[1]s" ]
request_type = "origin-rsa"
requested_validity = 7
}

data "cloudflare_origin_ca_root_certificate" "%[1]s" {
id = cloudflare_origin_ca_certificate.%[1]s.id
}
`, zoneName, csr)
}
14 changes: 14 additions & 0 deletions internal/framework/service/origin_ca_certificate/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package origin_ca_certificate

import "github.com/hashicorp/terraform-plugin-framework/types"

type CloudflareOriginCACertificateModel struct {
ID types.String `tfsdk:"id"`
Certificate types.String `tfsdk:"certificate"`
CSR types.String `tfsdk:"csr"`
Hostnames types.List `tfsdk:"hostnames"`
ExpiresOn types.String `tfsdk:"expires_on"`
RequestType types.String `tfsdk:"request_type"`
RequestedValidity types.Int64 `tfsdk:"requested_validity"`
RevokedAt types.String `tfsdk:"revoked_at"`
}
52 changes: 52 additions & 0 deletions internal/framework/service/origin_ca_certificate/schema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package origin_ca_certificate

import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
)

func (r *CloudflareOriginCACertificateDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: "Use this data source to retrieve an existing origin ca certificate.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "The user's unique identifier.",
danquack marked this conversation as resolved.
Show resolved Hide resolved
Computed: true,
},
"certificate": schema.StringAttribute{
Computed: true,
Description: "The Origin CA certificate.",
},
"csr": schema.StringAttribute{
Computed: true,
Description: "The Certificate Signing Request. Must be newline-encoded.",
},
"expires_on": schema.StringAttribute{
Computed: true,
Description: "The datetime when the certificate will expire.",
danquack marked this conversation as resolved.
Show resolved Hide resolved
},
"hostnames": schema.ListAttribute{
Computed: true,
ElementType: types.StringType,
Description: "A list of hostnames or wildcard names bound to the certificate.",
},
"request_type": schema.StringAttribute{
Computed: true,
Description: fmt.Sprintf("The signature type desired on the certificate. %v", []string{"origin-rsa", "origin-ecc", "keyless-certificate"}),
jacobbednarz marked this conversation as resolved.
Show resolved Hide resolved
},
"requested_validity": schema.Int64Attribute{
Optional: true,
Computed: true,
Description: fmt.Sprintf("The number of days for which the certificate should be valid. %v", []int{7, 30, 90, 365, 730, 1095, 5475}),
jacobbednarz marked this conversation as resolved.
Show resolved Hide resolved
},
"revoked_at": schema.StringAttribute{
Computed: true,
Description: "The datetime when the certificate was revoked.",
danquack marked this conversation as resolved.
Show resolved Hide resolved
},
},
}
}
Loading