Skip to content

Commit

Permalink
[datadog_synthetics_test] Fix multistep client certificate (#2683)
Browse files Browse the repository at this point in the history
Co-authored-by: etnbrd <[email protected]>
  • Loading branch information
AntoineDona and etnbrd authored Dec 13, 2024
1 parent 6435de8 commit 146c518
Showing 1 changed file with 84 additions and 27 deletions.
111 changes: 84 additions & 27 deletions datadog/resource_datadog_synthetics_test_.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (
"strconv"
"strings"

"github.com/hashicorp/go-cty/cty"

"github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils"
"github.com/terraform-providers/terraform-provider-datadog/datadog/internal/validators"

Expand Down Expand Up @@ -2580,7 +2582,16 @@ func buildDatadogSyntheticsAPITest(d *schema.ResourceData) *datadogV1.Synthetics
request.SetCompressedProtoFile(compressAndEncodeValue(attr.(string)))
}

request = *completeSyntheticsTestRequest(request, d.Get("request_headers").(map[string]interface{}), d.Get("request_query").(map[string]interface{}), d.Get("request_basicauth").([]interface{}), d.Get("request_client_certificate").([]interface{}), d.Get("request_proxy").([]interface{}), d.Get("request_metadata").(map[string]interface{}))
if attr, ok := d.GetOk("request_client_certificate"); ok {
if requestClientCertificates, ok := attr.([]interface{}); ok && len(requestClientCertificates) > 0 {
if requestClientCertificate, ok := requestClientCertificates[0].(map[string]interface{}); ok {
clientCert, clientKey := getCertAndKeyFromMap(requestClientCertificate)
request.SetCertificate(buildDatadogRequestCertificates(clientCert["content"].(string), clientCert["filename"].(string), clientKey["content"].(string), clientKey["filename"].(string)))
}
}
}

request = *completeSyntheticsTestRequest(request, d.Get("request_headers").(map[string]interface{}), d.Get("request_query").(map[string]interface{}), d.Get("request_basicauth").([]interface{}), d.Get("request_proxy").([]interface{}), d.Get("request_metadata").(map[string]interface{}))

config := datadogV1.NewSyntheticsAPITestConfigWithDefaults()

Expand All @@ -2604,7 +2615,7 @@ func buildDatadogSyntheticsAPITest(d *schema.ResourceData) *datadogV1.Synthetics
if attr, ok := d.GetOk("api_step"); ok && syntheticsTest.GetSubtype() == "multi" {
steps := []datadogV1.SyntheticsAPIStep{}

for _, s := range attr.([]interface{}) {
for i, s := range attr.([]interface{}) {
step := datadogV1.SyntheticsAPIStep{}
stepMap := s.(map[string]interface{})

Expand Down Expand Up @@ -2670,8 +2681,19 @@ func buildDatadogSyntheticsAPITest(d *schema.ResourceData) *datadogV1.Synthetics
}
}
}
// Override the request client certificate with the one from the config
configCertContent, configKeyContent := getConfigCertAndKeyContent(d, i)

if requestClientCertificates, ok := stepMap["request_client_certificate"].([]interface{}); ok && len(requestClientCertificates) > 0 {
if requestClientCertificate, ok := requestClientCertificates[0].(map[string]interface{}); ok {
clientCert, clientKey := getCertAndKeyFromMap(requestClientCertificate)
if configCertContent != nil || configKeyContent != nil {
request.SetCertificate(buildDatadogRequestCertificates(*configCertContent, clientCert["filename"].(string), *configKeyContent, clientKey["filename"].(string)))
}
}
}

request = *completeSyntheticsTestRequest(request, stepMap["request_headers"].(map[string]interface{}), stepMap["request_query"].(map[string]interface{}), stepMap["request_basicauth"].([]interface{}), stepMap["request_client_certificate"].([]interface{}), stepMap["request_proxy"].([]interface{}), stepMap["request_metadata"].(map[string]interface{}))
request = *completeSyntheticsTestRequest(request, stepMap["request_headers"].(map[string]interface{}), stepMap["request_query"].(map[string]interface{}), stepMap["request_basicauth"].([]interface{}), stepMap["request_proxy"].([]interface{}), stepMap["request_metadata"].(map[string]interface{}))

step.SyntheticsAPITestStep.SetRequest(request)

Expand Down Expand Up @@ -2785,7 +2807,8 @@ func buildDatadogSyntheticsBrowserTest(d *schema.ResourceData) *datadogV1.Synthe

if attr, ok := d.GetOk("request_client_certificate"); ok {
requestClientCertificate := attr.(map[string]interface{})
request.SetCertificate(buildDatadogRequestCertificates(requestClientCertificate))
clientCert, clientKey := getCertAndKeyFromMap(requestClientCertificate)
request.SetCertificate(buildDatadogRequestCertificates(clientCert["content"].(string), clientCert["filename"].(string), clientKey["content"].(string), clientKey["filename"].(string)))
}

if attr, ok := d.GetOk("request_proxy"); ok {
Expand Down Expand Up @@ -3670,37 +3693,31 @@ func buildTerraformExtractedValues(extractedValues []datadogV1.SyntheticsParsing
return localExtractedValues
}

func buildDatadogRequestCertificates(requestClientCertificate map[string]interface{}) datadogV1.SyntheticsTestRequestCertificate {
func buildDatadogRequestCertificates(clientCertContent string, clientCertFilename string, clientKeyContent string, clientKeyFilename string) datadogV1.SyntheticsTestRequestCertificate {
cert := datadogV1.SyntheticsTestRequestCertificateItem{}
key := datadogV1.SyntheticsTestRequestCertificateItem{}

clientCerts := requestClientCertificate["cert"].([]interface{})
clientKeys := requestClientCertificate["key"].([]interface{})

clientCert := clientCerts[0].(map[string]interface{})
clientKey := clientKeys[0].(map[string]interface{})

if clientCert["content"] != "" {
if clientCertContent != "" {
// only set the certificate content if it is not an already hashed string
// this is needed for the update function that receives the data from the state
// and not from the config. So we get a hash of the certificate and not it's real
// value.
if isHash := isCertHash(clientCert["content"].(string)); !isHash {
cert.SetContent(clientCert["content"].(string))
if isHash := isCertHash(clientCertContent); !isHash {
cert.SetContent(clientCertContent)
}
}
if clientCert["filename"] != "" {
cert.SetFilename(clientCert["filename"].(string))
if clientCertFilename != "" {
cert.SetFilename(clientCertFilename)
}

if clientKey["content"] != "" {
if clientKeyContent != "" {
// only set the key content if it is not an already hashed string
if isHash := isCertHash(clientKey["content"].(string)); !isHash {
key.SetContent(clientKey["content"].(string))
if isHash := isCertHash(clientKeyContent); !isHash {
key.SetContent(clientKeyContent)
}
}
if clientKey["filename"] != "" {
key.SetFilename(clientKey["filename"].(string))
if clientKeyFilename != "" {
key.SetFilename(clientKeyFilename)
}

return datadogV1.SyntheticsTestRequestCertificate{
Expand Down Expand Up @@ -4474,7 +4491,7 @@ func buildTerraformMobileTestSteps(steps []datadogV1.SyntheticsMobileStep) []map
return localSteps
}

func completeSyntheticsTestRequest(request datadogV1.SyntheticsTestRequest, requestHeaders map[string]interface{}, requestQuery map[string]interface{}, requestBasicAuths []interface{}, requestClientCertificates []interface{}, requestProxies []interface{}, requestMetadata map[string]interface{}) *datadogV1.SyntheticsTestRequest {
func completeSyntheticsTestRequest(request datadogV1.SyntheticsTestRequest, requestHeaders map[string]interface{}, requestQuery map[string]interface{}, requestBasicAuths []interface{}, requestProxies []interface{}, requestMetadata map[string]interface{}) *datadogV1.SyntheticsTestRequest {
if len(requestHeaders) > 0 {
headers := make(map[string]string, len(requestHeaders))

Expand All @@ -4495,11 +4512,6 @@ func completeSyntheticsTestRequest(request datadogV1.SyntheticsTestRequest, requ
}
}

if len(requestClientCertificates) > 0 {
if requestClientCertificate, ok := requestClientCertificates[0].(map[string]interface{}); ok {
request.SetCertificate(buildDatadogRequestCertificates(requestClientCertificate))
}
}
if len(requestProxies) > 0 {
if requestProxy, ok := requestProxies[0].(map[string]interface{}); ok {
request.SetProxy(buildDatadogTestRequestProxy(requestProxy))
Expand Down Expand Up @@ -5139,3 +5151,48 @@ func validateSyntheticsAssertionOperator(val interface{}, key string) (warns []s
}
return
}

func getConfigCertAndKeyContent(d *schema.ResourceData, stepIndex int) (*string, *string) {
// For security reasons, the certificate and keys can't be stored in the terraform state. It needs to stay in clear only in the config. This function retrieve the certificate from the terraform config, rather than the state.
// To retrieve the certificate and key, we first need to build the paths to the cert and key content, and then apply these paths to the rawConfig.

rawConfig := d.GetRawConfig()
basePath := cty.GetAttrPath("api_step").
Index(cty.NumberIntVal(int64(stepIndex))).
GetAttr("request_client_certificate").
Index(cty.NumberIntVal(0))

// Get the certificate
certContentPath := basePath.
GetAttr("cert").
Index(cty.NumberIntVal(0)).
GetAttr("content")
certContent, err := certContentPath.Apply(rawConfig)
if err != nil || !certContent.IsKnown() || certContent.IsNull() {
return nil, nil
}
certContentString := certContent.AsString()

// Get the key
keyContentPath := basePath.
GetAttr("key").
Index(cty.NumberIntVal(0)).
GetAttr("content")
keyContent, err := keyContentPath.Apply(rawConfig)
if err != nil || !keyContent.IsKnown() || keyContent.IsNull() {
return nil, nil
}
keyContentString := keyContent.AsString()

return &certContentString, &keyContentString
}

func getCertAndKeyFromMap(certAndKey map[string]interface{}) (map[string]interface{}, map[string]interface{}) {

clientCerts := certAndKey["cert"].([]interface{})
clientKeys := certAndKey["key"].([]interface{})
clientCert := clientCerts[0].(map[string]interface{})
clientKey := clientKeys[0].(map[string]interface{})

return clientCert, clientKey
}

0 comments on commit 146c518

Please sign in to comment.