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

[datadog_synthetics_test] Fix multistep client certificate #2683

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.

AntoineDona marked this conversation as resolved.
Show resolved Hide resolved
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
}
Loading