Skip to content

Commit

Permalink
Merge pull request #196 from vmware/bugfix/vcf-csr
Browse files Browse the repository at this point in the history
feat: add `fqdn` parameter for `r/csr`
  • Loading branch information
spacegospod committed Jul 8, 2024
2 parents 4ad8d1f + 431c5fa commit 7638290
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 99 deletions.
3 changes: 2 additions & 1 deletion docs/resources/csr.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ external or internal CA.
- `country` (String) ISO 3166 country code where company is legally registered
- `domain_id` (String) Domain Id or Name for which the CSRs should be generated
- `email` (String) Contact email address
- `fqdn` (String) FQDN of the resource
- `key_size` (Number) Certificate public key size. One among: 2048, 3072, 4096
- `locality` (String) The city or locality where company is legally registered
- `organization` (String) The name under which your company is known. The listed organization must be the legal registrant of the domain name in the certificate request.
- `organization_unit` (String) Organization with which the certificate is associated
- `resource` (String) Resources for which the CSRs are to be generated. One among: SDDC_MANAGER, VCENTER, NSX_MANAGER, NSXT_MANAGER, VROPS, VRSLCM, VXRAIL_MANAGER
- `resource` (String) Resources for which the CSRs are to be generated. One among: SDDC_MANAGER, PSC, VCENTER, NSX_MANAGER, NSXT_MANAGER, VROPS, VRSLCM, VXRAIL_MANAGER
- `state` (String) Full name (do not abbreviate) of the state, province, region, or territory where your company is legally registered.

### Optional
Expand Down
45 changes: 4 additions & 41 deletions internal/certificates/certificate_operations.go
Original file line number Diff line number Diff line change
@@ -1,49 +1,20 @@
// Copyright 2023 Broadcom. All Rights Reserved.
// Copyright 2023-2024 Broadcom. All Rights Reserved.
// SPDX-License-Identifier: MPL-2.0

package certificates

import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/vmware/terraform-provider-vcf/internal/api_client"
"github.com/vmware/terraform-provider-vcf/internal/constants"
validationutils "github.com/vmware/terraform-provider-vcf/internal/validation"
"github.com/vmware/vcf-sdk-go/client"
vcfclient "github.com/vmware/vcf-sdk-go/client"
"github.com/vmware/vcf-sdk-go/client/certificates"
"github.com/vmware/vcf-sdk-go/client/domains"
"github.com/vmware/vcf-sdk-go/models"
"strings"
"time"
)

func GetFqdnOfResourceTypeInDomain(ctx context.Context, domainId, resourceType string, apiClient *client.VcfClient) (*string, error) {
if apiClient == nil || len(resourceType) < 1 || len(domainId) < 1 {
return nil, nil
}

endpointsParams := domains.NewGetDomainEndpointsParamsWithContext(ctx).
WithTimeout(constants.DefaultVcfApiCallTimeout).WithID(domainId)

endpointsResult, err := apiClient.Domains.GetDomainEndpoints(endpointsParams)
if err != nil {
return nil, err
}

for _, endpoint := range endpointsResult.Payload.Elements {
if resourceType == *endpoint.Type {
result := *endpoint.URL
if strings.Contains(result, "https://") {
result = strings.ReplaceAll(result, "https://", "")
}
return &result, nil
}
}
return nil, nil
}

func ValidateResourceCertificates(ctx context.Context, client *vcfclient.VcfClient,
domainId string, resourceCertificateSpecs []*models.ResourceCertificateSpec) diag.Diagnostics {
validateResourceCertificatesParams := certificates.NewValidateResourceCertificatesParams().
Expand All @@ -66,7 +37,7 @@ func ValidateResourceCertificates(ctx context.Context, client *vcfclient.VcfClie
return validationutils.ConvertCertificateValidationsResultToDiag(validationResponse)
}
validationId := validationResponse.ValidationID
// Wait for certificate validation to fisnish
// Wait for certificate validation to finish
if !validationutils.HasCertificateValidationFinished(validationResponse) {
for {
getResourceCertificatesValidationResultParams := certificates.NewGetResourceCertificatesValidationByIDParams().
Expand Down Expand Up @@ -95,15 +66,7 @@ func ValidateResourceCertificates(ctx context.Context, client *vcfclient.VcfClie
}

func GetCertificateForResourceInDomain(ctx context.Context, client *vcfclient.VcfClient,
domainId, resourceType string) (*models.Certificate, error) {
resourceFqdn, err := GetFqdnOfResourceTypeInDomain(ctx, domainId, resourceType, client)
if err != nil {
return nil, err
}
if resourceFqdn == nil {
return nil, fmt.Errorf("could not determine FQDN for resourceType %s in domain %s", resourceType, domainId)
}

domainId, resourceFqdn string) (*models.Certificate, error) {
viewCertificatesParams := certificates.NewGetCertificatesByDomainParamsWithContext(ctx).
WithTimeout(constants.DefaultVcfApiCallTimeout)
viewCertificatesParams.ID = domainId
Expand All @@ -115,7 +78,7 @@ func GetCertificateForResourceInDomain(ctx context.Context, client *vcfclient.Vc

allCertsForDomain := certificatesResponse.Payload.Elements
for _, cert := range allCertsForDomain {
if cert.IssuedTo != nil && *cert.IssuedTo == *resourceFqdn {
if cert.IssuedTo != nil && *cert.IssuedTo == resourceFqdn {
return cert, nil
}
}
Expand Down
9 changes: 9 additions & 0 deletions internal/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,15 @@ const (

// VcfTestComputeClusterName the display name of the compute cluster that will contain the edge nodes.
VcfTestComputeClusterName = "VCF_TEST_COMPUTE_CLUSTER_NAME"

// VcfTestVcenterFqdn the FQDN of the vcenter server.
VcfTestVcenterFqdn = "VCF_TEST_VCENTER_FQDN"

// VcfTestSddcManagerFqdn the FQDN of the SDDC manager.
VcfTestSddcManagerFqdn = "VCF_TEST_SDDC_MANAGER_FQDN"

// VcfTestNsxManagerFqdn the FQDN of the NSX manager.
VcfTestNsxManagerFqdn = "VCF_TEST_NSX_MANAGER_FQDN"
)

func GetIso3166CountryCodes() []string {
Expand Down
23 changes: 8 additions & 15 deletions internal/provider/resource_certificate.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Broadcom. All Rights Reserved.
// Copyright 2023-2024 Broadcom. All Rights Reserved.
// SPDX-License-Identifier: MPL-2.0

package provider
Expand Down Expand Up @@ -60,31 +60,24 @@ func resourceResourceCertificateCreate(ctx context.Context, data *schema.Resourc

csrID := data.Get("csr_id").(string)
csrIdComponents := strings.Split(csrID, ":")
if len(csrIdComponents) != 4 {
if len(csrIdComponents) != 5 {
return diag.FromErr(fmt.Errorf("CSR ID invalid"))
}

domainID := csrIdComponents[1]
resourceType := csrIdComponents[2]
resourceFqdn := csrIdComponents[3]
caType := data.Get("ca_id").(string)

resourceFqdn, err := certificates.GetFqdnOfResourceTypeInDomain(ctx, domainID, resourceType, apiClient)
if err != nil {
return diag.FromErr(err)
}
if resourceFqdn == nil {
return diag.FromErr(fmt.Errorf("could not determine FQDN for resourceType %s in domain %s", resourceType, domainID))
}

err = certificates.GenerateCertificateForResource(ctx, vcfClient, &domainID, &resourceType, resourceFqdn, &caType)
err := certificates.GenerateCertificateForResource(ctx, vcfClient, &domainID, &resourceType, &resourceFqdn, &caType)
if err != nil {
return diag.FromErr(err)
}

certificateOperationSpec := &models.CertificateOperationSpec{
OperationType: resource_utils.ToStringPointer("INSTALL"),
Resources: []*models.Resource{{
Fqdn: *resourceFqdn,
Fqdn: resourceFqdn,
Type: &resourceType,
}},
}
Expand Down Expand Up @@ -119,14 +112,14 @@ func resourceResourceCertificateRead(ctx context.Context, data *schema.ResourceD

csrID := data.Get("csr_id").(string)
csrIdComponents := strings.Split(csrID, ":")
if len(csrIdComponents) != 4 {
if len(csrIdComponents) != 5 {
return diag.FromErr(fmt.Errorf("CSR ID invalid"))
}

domainID := csrIdComponents[1]
resourceType := csrIdComponents[2]
resourceFqdn := csrIdComponents[3]

cert, err := certificates.GetCertificateForResourceInDomain(ctx, apiClient, domainID, resourceType)
cert, err := certificates.GetCertificateForResourceInDomain(ctx, apiClient, domainID, resourceFqdn)
if err != nil {
return diag.FromErr(err)
}
Expand Down
97 changes: 91 additions & 6 deletions internal/provider/resource_certificate_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Broadcom. All Rights Reserved.
// Copyright 2023-2024 Broadcom. All Rights Reserved.
// SPDX-License-Identifier: MPL-2.0

package provider
Expand All @@ -11,8 +11,8 @@ import (
"testing"
)

func TestAccResourceVcfResourceCertificate(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
func TestAccResourceVcfResourceCertificate_vCenter(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
testAccVcfCertificateAuthorityPreCheck(t)
Expand All @@ -24,7 +24,9 @@ func TestAccResourceVcfResourceCertificate(t *testing.T) {
os.Getenv(constants.VcfTestDomainDataSourceId),
os.Getenv(constants.VcfTestMsftCaServerUrl),
os.Getenv(constants.VcfTestMsftCaUser),
os.Getenv(constants.VcfTestMsftCaSecret)),
os.Getenv(constants.VcfTestMsftCaSecret),
"VCENTER",
os.Getenv(constants.VcfTestVcenterFqdn)),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.issued_by"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.issued_to"),
Expand All @@ -49,7 +51,87 @@ func TestAccResourceVcfResourceCertificate(t *testing.T) {
})
}

func testAccVcfResourceCertificate(domainID, msftCaServerUrl, msftCaUser, msftCaSecret string) string {
func TestAccResourceVcfResourceCertificate_sddcManager(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
testAccVcfCertificateAuthorityPreCheck(t)
},
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: testAccVcfResourceCertificate(
os.Getenv(constants.VcfTestDomainDataSourceId),
os.Getenv(constants.VcfTestMsftCaServerUrl),
os.Getenv(constants.VcfTestMsftCaUser),
os.Getenv(constants.VcfTestMsftCaSecret),
"SDDC_MANAGER",
os.Getenv(constants.VcfTestSddcManagerFqdn)),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.issued_by"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.issued_to"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.expiration_status"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.certificate_error"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.key_size"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.not_after"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.not_before"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.pem_encoded"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.public_key"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.public_key_algorithm"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.serial_number"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.signature_algorithm"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.subject"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.subject_alternative_name.#"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.thumbprint"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.thumbprint_algorithm"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.version"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.number_of_days_to_expire")),
},
},
})
}

func TestAccResourceVcfResourceCertificate_nsx(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
testAccVcfCertificateAuthorityPreCheck(t)
},
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: testAccVcfResourceCertificate(
os.Getenv(constants.VcfTestDomainDataSourceId),
os.Getenv(constants.VcfTestMsftCaServerUrl),
os.Getenv(constants.VcfTestMsftCaUser),
os.Getenv(constants.VcfTestMsftCaSecret),
"NSXT_MANAGER",
os.Getenv(constants.VcfTestNsxManagerFqdn)),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.issued_by"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.issued_to"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.expiration_status"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.certificate_error"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.key_size"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.not_after"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.not_before"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.pem_encoded"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.public_key"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.public_key_algorithm"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.serial_number"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.signature_algorithm"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.subject"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.subject_alternative_name.#"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.thumbprint"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.thumbprint_algorithm"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.version"),
resource.TestCheckResourceAttrSet("vcf_certificate.vcenter_cert", "certificate.0.number_of_days_to_expire")),
},
},
})
}

func testAccVcfResourceCertificate(domainID, msftCaServerUrl, msftCaUser, msftCaSecret, resource, fqdn string) string {
return fmt.Sprintf(`
resource "vcf_certificate_authority" "ca" {
microsoft {
Expand All @@ -69,7 +151,8 @@ func testAccVcfResourceCertificate(domainID, msftCaServerUrl, msftCaUser, msftCa
state = "Sofia-grad"
organization = "VMware Inc."
organization_unit = "VCF"
resource = "VCENTER"
resource = %q
fqdn = %q
}
Expand All @@ -82,5 +165,7 @@ func testAccVcfResourceCertificate(domainID, msftCaServerUrl, msftCaUser, msftCa
msftCaSecret,
msftCaServerUrl,
domainID,
resource,
fqdn,
)
}
32 changes: 15 additions & 17 deletions internal/provider/resource_csr.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Broadcom. All Rights Reserved.
// Copyright 2023-2024 Broadcom. All Rights Reserved.
// SPDX-License-Identifier: MPL-2.0

package provider
Expand Down Expand Up @@ -81,11 +81,16 @@ func ResourceCsr() *schema.Resource {
ValidateFunc: validation.StringIsNotEmpty,
},
"resource": {
Type: schema.TypeString,
Required: true,
// TODO when migrating to 5.x.x support check if these are still accurate
Description: "Resources for which the CSRs are to be generated. One among: SDDC_MANAGER, VCENTER, NSX_MANAGER, NSXT_MANAGER, VROPS, VRSLCM, VXRAIL_MANAGER",
ValidateFunc: validation.StringInSlice([]string{"SDDC_MANAGER", "VCENTER", "NSX_MANAGER", "NSXT_MANAGER", "VROPS", "VRSLCM", "VXRAIL_MANAGER"}, false),
Type: schema.TypeString,
Required: true,
Description: "Resources for which the CSRs are to be generated. One among: SDDC_MANAGER, PSC, VCENTER, NSX_MANAGER, NSXT_MANAGER, VROPS, VRSLCM, VXRAIL_MANAGER",
ValidateFunc: validation.StringInSlice([]string{"SDDC_MANAGER", "PSC", "VCENTER", "NSX_MANAGER", "NSXT_MANAGER", "VROPS", "VRSLCM", "VXRAIL_MANAGER"}, false),
},
"fqdn": {
Type: schema.TypeString,
Required: true,
Description: "FQDN of the resource",
ValidateFunc: validation.NoZeroValues,
},
"csr": {
Type: schema.TypeList,
Expand All @@ -103,14 +108,7 @@ func resourceCsrCreate(ctx context.Context, data *schema.ResourceData, meta inte

domainId := data.Get("domain_id").(string)
resourceType := data.Get("resource").(string)

resourceFqdn, err := certificates.GetFqdnOfResourceTypeInDomain(ctx, domainId, resourceType, apiClient)
if err != nil {
return diag.FromErr(err)
}
if resourceFqdn == nil {
return diag.FromErr(fmt.Errorf("could not determine FQDN for resourceType %s in domain %s", resourceType, domainId))
}
resourceFqdn := data.Get("fqdn").(string)

country := data.Get("country").(string)
email := data.Get("email").(string)
Expand All @@ -135,7 +133,7 @@ func resourceCsrCreate(ctx context.Context, data *schema.ResourceData, meta inte
CSRGenerationSpec: csrGenerationSpec,
Resources: []*models.Resource{
{
Fqdn: *resourceFqdn,
Fqdn: resourceFqdn,
Type: &resourceType,
},
},
Expand All @@ -158,7 +156,7 @@ func resourceCsrCreate(ctx context.Context, data *schema.ResourceData, meta inte
if err != nil {
return diag.FromErr(err)
}
data.SetId("csr:" + domainId + ":" + resourceType + ":" + taskId)
data.SetId(fmt.Sprintf("csr:%s:%s:%s:%s", domainId, resourceType, resourceFqdn, taskId))

getCsrsParams := certificatesSdk.NewGetCSRsParamsWithContext(ctx).
WithTimeout(constants.DefaultVcfApiCallTimeout).
Expand All @@ -168,7 +166,7 @@ func resourceCsrCreate(ctx context.Context, data *schema.ResourceData, meta inte
return diag.FromErr(err)
}

csr := getCsrByResourceFqdn(*resourceFqdn, getCsrResponse.Payload.Elements)
csr := getCsrByResourceFqdn(resourceFqdn, getCsrResponse.Payload.Elements)
flattenedCsr := certificates.FlattenCsr(csr)
_ = data.Set("csr", []interface{}{flattenedCsr})

Expand Down
Loading

0 comments on commit 7638290

Please sign in to comment.