-
Notifications
You must be signed in to change notification settings - Fork 630
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add resource cloudflare_certificate_authorities_hostname_associations
- Loading branch information
Showing
4 changed files
with
323 additions
and
0 deletions.
There are no files selected for viewing
11 changes: 11 additions & 0 deletions
11
internal/framework/service/certificate_authorities_hostname_associations/model.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package certificate_authorities_hostname_associations | ||
|
||
import "github.com/hashicorp/terraform-plugin-framework/types" | ||
|
||
type HostnameAssociation = types.String | ||
|
||
type CertificateAuthoritiesHostnameAssociationsModel struct { | ||
ZoneID types.String `tfsdk:"zone_id"` | ||
MTLSCertificateID types.String `tfsdk:"mtls_certificate_id"` | ||
Hostnames []HostnameAssociation `tfsdk:"hostnames"` | ||
} |
191 changes: 191 additions & 0 deletions
191
internal/framework/service/certificate_authorities_hostname_associations/resource.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
package certificate_authorities_hostname_associations | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"strings" | ||
|
||
cfv1 "github.com/cloudflare/cloudflare-go" | ||
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/muxclient" | ||
"github.com/hashicorp/terraform-plugin-framework/path" | ||
"github.com/hashicorp/terraform-plugin-framework/resource" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes" | ||
"github.com/hashicorp/terraform-plugin-log/tflog" | ||
) | ||
|
||
// Ensure provider defined types fully satisfy framework interfaces. | ||
var _ resource.Resource = &CertificateAuthoritiesHostnameAssociationsResource{} | ||
var _ resource.ResourceWithImportState = &CertificateAuthoritiesHostnameAssociationsResource{} | ||
|
||
func NewResource() resource.Resource { | ||
return &CertificateAuthoritiesHostnameAssociationsResource{} | ||
} | ||
|
||
type CertificateAuthoritiesHostnameAssociationsResource struct { | ||
client *muxclient.Client | ||
} | ||
|
||
func (r *CertificateAuthoritiesHostnameAssociationsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { | ||
resp.TypeName = req.ProviderTypeName + "_certificate_authorities_hostname_associations" | ||
} | ||
|
||
func (r *CertificateAuthoritiesHostnameAssociationsResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { | ||
if req.ProviderData == nil { | ||
return | ||
} | ||
|
||
client, ok := req.ProviderData.(*muxclient.Client) | ||
|
||
if !ok { | ||
resp.Diagnostics.AddError( | ||
"unexpected resource configure type", | ||
fmt.Sprintf("Expected *muxclient.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), | ||
) | ||
|
||
return | ||
} | ||
|
||
r.client = client | ||
} | ||
|
||
func (r *CertificateAuthoritiesHostnameAssociationsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { | ||
var data *CertificateAuthoritiesHostnameAssociationsModel | ||
|
||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) | ||
|
||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
updatedHostnames, err := r.update(ctx, data) | ||
if err != nil { | ||
resp.Diagnostics.AddError("error updating Certificate Authorities Hostname Associations", err.Error()) | ||
return | ||
} | ||
|
||
data = buildCertificateAuthoritiesHostnameAssociationsModel(updatedHostnames, data.MTLSCertificateID, data.ZoneID) | ||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) | ||
} | ||
|
||
func (r *CertificateAuthoritiesHostnameAssociationsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { | ||
var data *CertificateAuthoritiesHostnameAssociationsModel | ||
|
||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...) | ||
|
||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
identifier := cfv1.ZoneIdentifier(data.ZoneID.ValueString()) | ||
params := cfv1.ListCertificateAuthoritiesHostnameAssociationsParams{ | ||
MTLSCertificateID: data.MTLSCertificateID.ValueString(), | ||
} | ||
|
||
hostnames, err := r.client.V1.ListCertificateAuthoritiesHostnameAssociations(ctx, identifier, params) | ||
if err != nil { | ||
resp.Diagnostics.AddError("error reading Access Mutual TLS Hostname Settings", err.Error()) | ||
return | ||
} | ||
data = buildCertificateAuthoritiesHostnameAssociationsModel(hostnames, data.MTLSCertificateID, data.ZoneID) | ||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) | ||
} | ||
|
||
// Helper function used by both Update, Create and Delete | ||
func (r *CertificateAuthoritiesHostnameAssociationsResource) update(ctx context.Context, data *CertificateAuthoritiesHostnameAssociationsModel) ([]cfv1.HostnameAssociation, error) { | ||
updatedHostnames := []cfv1.HostnameAssociation{} | ||
identifier := cfv1.ZoneIdentifier(data.ZoneID.ValueString()) | ||
|
||
hostnames := data.Hostnames | ||
for _, hostname := range hostnames { | ||
updatedHostnames = append(updatedHostnames, hostname.ValueString()) | ||
} | ||
|
||
updatedCertificateAuthoritiesHostnameAssociations := cfv1.UpdateCertificateAuthoritiesHostnameAssociationsParams{ | ||
MTLSCertificateID: data.MTLSCertificateID.ValueString(), | ||
Hostnames: updatedHostnames, | ||
} | ||
|
||
tflog.Debug(ctx, fmt.Sprintf("Updating Cloudflare Certificate Authorities Hostname Associations from struct: %+v", updatedCertificateAuthoritiesHostnameAssociations)) | ||
|
||
resultUpdatedHostnames, err := r.client.V1.UpdateCertificateAuthoritiesHostnameAssociations(ctx, identifier, updatedCertificateAuthoritiesHostnameAssociations) | ||
if err != nil { | ||
return nil, fmt.Errorf("error updating Certificate Authorities Hostname Associations for %s %q %s: %w", identifier.Level, identifier.Identifier, data.MTLSCertificateID, err) | ||
} | ||
return resultUpdatedHostnames, nil | ||
} | ||
|
||
func (r *CertificateAuthoritiesHostnameAssociationsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { | ||
var data *CertificateAuthoritiesHostnameAssociationsModel | ||
|
||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) | ||
|
||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
updatedHostnames, err := r.update(ctx, data) | ||
if err != nil { | ||
resp.Diagnostics.AddError("error updating Certificate Authorities Hostname Associations", err.Error()) | ||
return | ||
} | ||
|
||
data = buildCertificateAuthoritiesHostnameAssociationsModel(updatedHostnames, data.MTLSCertificateID, data.ZoneID) | ||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) | ||
} | ||
|
||
func (r *CertificateAuthoritiesHostnameAssociationsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { | ||
var data *CertificateAuthoritiesHostnameAssociationsModel | ||
|
||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...) | ||
identifier := cfv1.ZoneIdentifier(data.ZoneID.ValueString()) | ||
|
||
// To delete the associations we issue and update an empty array of hostnames | ||
deletedCertificateAuthoritiesHostnameAssociations := cfv1.UpdateCertificateAuthoritiesHostnameAssociationsParams{ | ||
MTLSCertificateID: data.MTLSCertificateID.ValueString(), | ||
Hostnames: []cfv1.HostnameAssociation{}, | ||
} | ||
|
||
_, err := r.client.V1.UpdateCertificateAuthoritiesHostnameAssociations(ctx, identifier, deletedCertificateAuthoritiesHostnameAssociations) | ||
if err != nil { | ||
resp.Diagnostics.AddError(fmt.Sprintf("error removing Certificate Authorities Hostname Associations for %s %q", identifier.Level, identifier.Identifier), err.Error()) | ||
return | ||
} | ||
} | ||
|
||
func (r *CertificateAuthoritiesHostnameAssociationsResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { | ||
attributes := strings.Split(req.ID, "/") | ||
|
||
invalidIDMessage := "invalid ID (\"%s\") specified, should be in format \"<zone_id>\" or \"<zone_id>/<mtls_certificate_id>\"" | ||
if len(attributes) != 1 || len(attributes) != 2 { | ||
resp.Diagnostics.AddError("error importing Certificate Authorities Hostname Associations", fmt.Sprintf(invalidIDMessage, req.ID)) | ||
return | ||
} | ||
|
||
zoneID := attributes[0] | ||
mtlsCertificateID := "" | ||
if len(attributes) == 2 { | ||
mtlsCertificateID = attributes[1] | ||
} | ||
|
||
tflog.Debug(ctx, fmt.Sprintf("Importing Certificate Authorities Hostname Associations: for %s %s", zoneID, mtlsCertificateID)) | ||
|
||
resp.Diagnostics.Append( | ||
resp.State.SetAttribute(ctx, path.Root("zone_id"), zoneID)..., | ||
) | ||
|
||
resp.Diagnostics.Append( | ||
resp.State.SetAttribute(ctx, path.Root("mtls_certificate_id"), mtlsCertificateID)..., | ||
) | ||
} | ||
|
||
func buildCertificateAuthoritiesHostnameAssociationsModel(hostnames []cfv1.HostnameAssociation, mtlsCertificateID basetypes.StringValue, zoneID basetypes.StringValue) *CertificateAuthoritiesHostnameAssociationsModel { | ||
model := &CertificateAuthoritiesHostnameAssociationsModel{ | ||
ZoneID: zoneID, | ||
MTLSCertificateID: mtlsCertificateID, | ||
} | ||
for _, hostname := range hostnames { | ||
model.Hostnames = append(model.Hostnames, types.StringValue(hostname)) | ||
} | ||
return model | ||
} |
90 changes: 90 additions & 0 deletions
90
internal/framework/service/certificate_authorities_hostname_associations/resource_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package certificate_authorities_hostname_associations | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
"testing" | ||
|
||
cfv1 "github.com/cloudflare/cloudflare-go" | ||
"github.com/cloudflare/terraform-provider-cloudflare/internal/acctest" | ||
"github.com/cloudflare/terraform-provider-cloudflare/internal/consts" | ||
"github.com/cloudflare/terraform-provider-cloudflare/internal/utils" | ||
"github.com/hashicorp/terraform-plugin-testing/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-testing/terraform" | ||
) | ||
|
||
func TestAccCloudflareCertificateAuthoritiesHostnameAssociations_Create(t *testing.T) { | ||
t.Parallel() | ||
rnd := utils.GenerateRandomResourceName() | ||
name := fmt.Sprintf("cloudflare_certificate_authorities_hostname_associations.%s", rnd) | ||
accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") | ||
zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") | ||
zoneName := os.Getenv("CLOUDFLARE_DOMAIN") | ||
hostname := rnd + "." + zoneName | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { | ||
acctest.TestAccPreCheck(t) | ||
acctest.TestAccPreCheck_Account(t) | ||
}, | ||
ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, | ||
CheckDestroy: testAccCheckCloudflareCertificateAuthoritiesHostnameAssociationsDestroy, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testCertificateAuthoritiesHostnameAssociationsConfig(rnd, accountID, zoneID, hostname), | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestCheckResourceAttr(name, consts.ZoneIDSchemaKey, zoneID), | ||
resource.TestCheckResourceAttrSet(name, "mtls_certificate_id"), | ||
resource.TestCheckResourceAttr(name, "hostnames.0", hostname), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckCloudflareCertificateAuthoritiesHostnameAssociationsDestroy(s *terraform.State) error { | ||
client, err := acctest.SharedV1Client() | ||
if err != nil { | ||
return fmt.Errorf("Failed to create Cloudflare client: %w", err) | ||
} | ||
|
||
for _, rs := range s.RootModule().Resources { | ||
if rs.Type != "cloudflare_zero_trust_access_mtls_hostname_settings" { | ||
continue | ||
} | ||
|
||
if rs.Primary.Attributes[consts.ZoneIDSchemaKey] != "" { | ||
settings, _ := client.GetAccessMutualTLSHostnameSettings(context.Background(), cfv1.ZoneIdentifier(rs.Primary.Attributes[consts.ZoneIDSchemaKey])) | ||
if len(settings) != 0 { | ||
return fmt.Errorf("AccessMutualTLSHostnameSettings still exists") | ||
} | ||
} | ||
|
||
if rs.Primary.Attributes[consts.AccountIDSchemaKey] != "" { | ||
settings, _ := client.GetAccessMutualTLSHostnameSettings(context.Background(), cfv1.AccountIdentifier(rs.Primary.Attributes[consts.AccountIDSchemaKey])) | ||
if len(settings) != 0 { | ||
return fmt.Errorf("AccessMutualTLSHostnameSettings still exists") | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func testCertificateAuthoritiesHostnameAssociationsConfig(rnd string, accountID string, zoneID string, hostname string) string { | ||
return fmt.Sprintf(` | ||
resource "cloudflare_mtls_certificate" "%[1]s" { | ||
account_id = "%[2]s" | ||
name = "" | ||
certificates = "-----BEGIN CERTIFICATE-----\nMIIDmDCCAoCgAwIBAgIUKTOAZNjcXVZRj4oQt0SHsl1c1vMwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVVMxFjAUBgNVBAgMDVNhbiBGcmFuY2lzY28xEzARBgNVBAcMCkNhbGlmb3JuaWExFTATBgNVBAoMDEV4YW1wbGUgSW5jLjAgFw0yMjExMjIxNjU5NDdaGA8yMTIyMTAyOTE2NTk0N1owUTELMAkGA1UEBhMCVVMxFjAUBgNVBAgMDVNhbiBGcmFuY2lzY28xEzARBgNVBAcMCkNhbGlmb3JuaWExFTATBgNVBAoMDEV4YW1wbGUgSW5jLjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMRcORwgJFTdcG/2GKI+cFYiOBNDKjCZUXEOvXWY42BkH9wxiMT869CO+enA1w5pIrXow6kCM1sQspHHaVmJUlotEMJxyoLFfA/8Kt1EKFyobOjuZs2SwyVyJ2sStvQuUQEosULZCNGZEqoH5g6zhMPxaxm7ZLrrsDZ9maNGVqo7EWLWHrZ57Q/5MtTrbxQL+eXjUmJ9K3kS+3uEwMdqR6Z3BluU1ivanpPc1CN2GNhdO0/hSY4YkGEnuLsqJyDd3cIiB1MxuCBJ4ZaqOd2viV1WcP3oU3dxVPm4MWyfYIldMWB14FahScxLhWdRnM9YZ/i9IFcLypXsuz7DjrJPtPUCAwEAAaNmMGQwHQYDVR0OBBYEFP5JzLUawNF+c3AXsYTEWHh7z2czMB8GA1UdIwQYMBaAFP5JzLUawNF+c3AXsYTEWHh7z2czMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEBMA0GCSqGSIb3DQEBCwUAA4IBAQBc+Be7NDhpE09y7hLPZGRPl1cSKBw4RI0XIv6rlbSTFs5EebpTGjhx/whNxwEZhB9HZ7111Oa1YlT8xkI9DshB78mjAHCKBAJ76moK8tkG0aqdYpJ4ZcJTVBB7l98Rvgc7zfTii7WemTy72deBbSeiEtXavm4EF0mWjHhQ5Nxpnp00Bqn5g1x8CyTDypgmugnep+xG+iFzNmTdsz7WI9T/7kDMXqB7M/FPWBORyS98OJqNDswCLF8bIZYwUBEe+bRHFomoShMzaC3tvim7WCb16noDkSTMlfKO4pnvKhpcVdSgwcruATV7y+W+Lvmz2OT/Gui4JhqeoTewsxndhDDE\n-----END CERTIFICATE-----" | ||
private_key = "" | ||
ca = true | ||
} | ||
resource "cloudflare_certificate_authorities_hostname_associations" "%[1]s" { | ||
zone_id = "%[3]s" | ||
mtls_certificate_id = cloudflare_mtls_certificate.%[1]s.id | ||
hostnames = ["%[4]s"] | ||
} | ||
`, rnd, accountID, zoneID, hostname) | ||
} |
31 changes: 31 additions & 0 deletions
31
internal/framework/service/certificate_authorities_hostname_associations/schema.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package certificate_authorities_hostname_associations | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/cloudflare/terraform-provider-cloudflare/internal/consts" | ||
"github.com/hashicorp/terraform-plugin-framework/resource" | ||
"github.com/hashicorp/terraform-plugin-framework/resource/schema" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
) | ||
|
||
func (r *CertificateAuthoritiesHostnameAssociationsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { | ||
resp.Schema = schema.Schema{ | ||
Description: "Provides a Cloudflare Certificate Authorities Hostname Associations resource.", | ||
Attributes: map[string]schema.Attribute{ | ||
consts.ZoneIDSchemaKey: schema.StringAttribute{ | ||
Description: consts.ZoneIDSchemaDescription, | ||
Required: true, | ||
}, | ||
"mtls_certificate_id": schema.StringAttribute{ | ||
Description: "TODO", | ||
Optional: true, | ||
}, | ||
"hostnames": schema.ListAttribute{ | ||
Description: "TODO", | ||
Required: true, | ||
ElementType: types.StringType, | ||
}, | ||
}, | ||
} | ||
} |