github.com/crossplane/upjet@v1.3.0/pkg/terraform/timeouts.go (about)

     1  // SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package terraform
     6  
     7  import (
     8  	"github.com/crossplane/crossplane-runtime/pkg/errors"
     9  
    10  	"github.com/crossplane/upjet/pkg/config"
    11  	"github.com/crossplane/upjet/pkg/resource/json"
    12  )
    13  
    14  // "e2bfb730-ecaa-11e6-8f88-34363bc7c4c0" is a hardcoded string for Terraform
    15  // timeout key in private raw, i.e. provider specific metadata:
    16  // https://github.com/hashicorp/terraform-plugin-sdk/blob/112e2164c381d80e8ada3170dac9a8a5db01079a/helper/schema/resource_timeout.go#L14
    17  const tfMetaTimeoutKey = "e2bfb730-ecaa-11e6-8f88-34363bc7c4c0"
    18  
    19  type timeouts config.OperationTimeouts
    20  
    21  func (ts timeouts) asParameter() map[string]string {
    22  	param := make(map[string]string)
    23  	if t := ts.Read.String(); t != "0s" {
    24  		param["read"] = t
    25  	}
    26  	if t := ts.Create.String(); t != "0s" {
    27  		param["create"] = t
    28  	}
    29  	if t := ts.Update.String(); t != "0s" {
    30  		param["update"] = t
    31  	}
    32  	if t := ts.Delete.String(); t != "0s" {
    33  		param["delete"] = t
    34  	}
    35  	return param
    36  }
    37  
    38  func (ts timeouts) asMetadata() map[string]any {
    39  	// See how timeouts encoded as metadata on Terraform side:
    40  	// https://github.com/hashicorp/terraform-plugin-sdk/blob/112e2164c381d80e8ada3170dac9a8a5db01079a/helper/schema/resource_timeout.go#L170
    41  	meta := make(map[string]any)
    42  	if t := ts.Read.String(); t != "0s" {
    43  		meta["read"] = ts.Read.Nanoseconds()
    44  	}
    45  	if t := ts.Create.String(); t != "0s" {
    46  		meta["create"] = ts.Create.Nanoseconds()
    47  	}
    48  	if t := ts.Update.String(); t != "0s" {
    49  		meta["update"] = ts.Update.Nanoseconds()
    50  	}
    51  	if t := ts.Delete.String(); t != "0s" {
    52  		meta["delete"] = ts.Delete.Nanoseconds()
    53  	}
    54  	return meta
    55  }
    56  
    57  func insertTimeoutsMeta(existingMeta []byte, to timeouts) ([]byte, error) {
    58  	customTimeouts := to.asMetadata()
    59  	if len(customTimeouts) == 0 {
    60  		// No custom timeout configured, nothing to do.
    61  		return existingMeta, nil
    62  	}
    63  	meta := make(map[string]any)
    64  	if len(existingMeta) == 0 {
    65  		// No existing data, just initialize a new meta with custom timeouts.
    66  		meta[tfMetaTimeoutKey] = customTimeouts
    67  		return json.JSParser.Marshal(meta)
    68  	}
    69  	// There are some existing metadata, let's parse it to insert custom
    70  	// timeouts properly.
    71  	if err := json.JSParser.Unmarshal(existingMeta, &meta); err != nil {
    72  		return nil, errors.Wrap(err, "cannot parse existing metadata")
    73  	}
    74  	if existingTimeouts, ok := meta[tfMetaTimeoutKey].(map[string]any); ok {
    75  		// There are some timeout configuration exists in existing metadata.
    76  		// Only override custom timeouts.
    77  		for k, v := range customTimeouts {
    78  			existingTimeouts[k] = v
    79  		}
    80  		return json.JSParser.Marshal(meta)
    81  	}
    82  	// No existing timeout configuration, initialize it with custom timeouts.
    83  	meta[tfMetaTimeoutKey] = customTimeouts
    84  	return json.JSParser.Marshal(meta)
    85  }