github.com/crossplane/upjet@v1.3.0/pkg/registry/resource.go (about)

     1  // SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package registry
     6  
     7  import (
     8  	"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
     9  	"github.com/pkg/errors"
    10  	"gopkg.in/yaml.v2"
    11  
    12  	"github.com/crossplane/upjet/pkg/resource/json"
    13  )
    14  
    15  const (
    16  	// RandRFC1123Subdomain represents a template variable to be substituted
    17  	// by the test runner at runtime with a random RFC1123 subdomain string.
    18  	RandRFC1123Subdomain = "${Rand.RFC1123Subdomain}"
    19  )
    20  
    21  // Dependencies are the example manifests for the dependency resources.
    22  // Key is formatted as <Terraform resource>.<example name>
    23  type Dependencies map[string]string
    24  
    25  // ResourceExample represents the scraped example HCL configuration
    26  // for a Terraform resource
    27  type ResourceExample struct {
    28  	Name         string            `yaml:"name"`
    29  	Manifest     string            `yaml:"manifest"`
    30  	References   map[string]string `yaml:"references,omitempty"`
    31  	Dependencies Dependencies      `yaml:"dependencies,omitempty"`
    32  	Paved        fieldpath.Paved   `yaml:"-"`
    33  }
    34  
    35  // Resource represents the scraped metadata for a Terraform resource
    36  type Resource struct {
    37  	// SubCategory is the category name under which this Resource resides in
    38  	// Terraform registry docs. Example:"Key Vault" for Azure Vault resources.
    39  	// In Terraform docs, resources are grouped (categorized) using this field.
    40  	SubCategory string `yaml:"subCategory"`
    41  	// Description is a short description for the resource as it appears in
    42  	// Terraform registry. Example: "Manages a Key Vault Key." for the
    43  	// azurerm_key_vault_key resource.
    44  	// This field is suitable for use in generating CRD Kind documentation.
    45  	Description string `yaml:"description,omitempty"`
    46  	// Name is the Terraform name of the resource. Example: azurerm_key_vault_key
    47  	Name string `yaml:"name"`
    48  	// Title is the title name of the resource that appears in
    49  	// the Terraform registry doc page for a Terraform resource.
    50  	Title string `yaml:"title"`
    51  	// Examples are the example HCL configuration blocks for the resource
    52  	// that appear in the resource's registry page. They are in the same
    53  	// order as they appear on the registry page.
    54  	Examples []ResourceExample `yaml:"examples,omitempty"`
    55  	// ArgumentDocs maps resource attributes to their documentation in the
    56  	// resource's registry page.
    57  	ArgumentDocs map[string]string `yaml:"argumentDocs"`
    58  	// ImportStatements are the example Terraform import statements as they
    59  	// appear in the resource's registry page.
    60  	// Example: terraform import azurerm_key_vault.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.KeyVault/vaults/vault1
    61  	ImportStatements []string `yaml:"importStatements"`
    62  	// ExternalName configured for this resource. This allows the
    63  	// external name used in the generated example manifests to be
    64  	// overridden for a specific resource via configuration.
    65  	ExternalName string `yaml:"-"`
    66  }
    67  
    68  // ProviderMetadata metadata for a Terraform native provider
    69  type ProviderMetadata struct {
    70  	Name      string               `yaml:"name"`
    71  	Resources map[string]*Resource `yaml:"resources"`
    72  }
    73  
    74  // NewProviderMetadataFromFile loads metadata from the specified YAML-formatted document
    75  func NewProviderMetadataFromFile(providerMetadata []byte) (*ProviderMetadata, error) {
    76  	metadata := &ProviderMetadata{}
    77  	if err := yaml.Unmarshal(providerMetadata, metadata); err != nil {
    78  		return nil, errors.Wrap(err, "failed to unmarshal provider metadata")
    79  	}
    80  	for name, rm := range metadata.Resources {
    81  		for j, re := range rm.Examples {
    82  			if err := re.Paved.UnmarshalJSON([]byte(re.Manifest)); err != nil {
    83  				return nil, errors.Wrapf(err, "cannot pave example manifest JSON: %s", re.Manifest)
    84  			}
    85  			rm.Examples[j] = re
    86  			if rm.Examples[j].Dependencies == nil {
    87  				rm.Examples[j].Dependencies = make(map[string]string)
    88  			}
    89  		}
    90  		metadata.Resources[name] = rm
    91  	}
    92  	return metadata, nil
    93  }
    94  
    95  // SetPathValue sets the field at the specified path to the given value
    96  // in the example manifest.
    97  func (re *ResourceExample) SetPathValue(fieldPath string, val any) error {
    98  	return errors.Wrapf(re.Paved.SetValue(fieldPath, val), "cannot set example manifest path %q to value: %#v", fieldPath, val)
    99  }
   100  
   101  // SetPathValue sets the field at the specified path to the given value
   102  // in the example manifest of the specified dependency. Key format is:
   103  // <Terraform resource type>.<configuration block name>, e.g.,
   104  // aws_subnet.subnet1
   105  func (d Dependencies) SetPathValue(dependencyKey string, fieldPath string, val any) error {
   106  	m, ok := d[dependencyKey]
   107  	if !ok {
   108  		return nil
   109  	}
   110  	var params map[string]any
   111  	if err := json.TFParser.Unmarshal([]byte(m), &params); err != nil {
   112  		return errors.Wrapf(err, "cannot unmarshal dependency %q as JSON", dependencyKey)
   113  	}
   114  	p := fieldpath.Pave(params)
   115  	if err := p.SetValue(fieldPath, val); err != nil {
   116  		return errors.Wrapf(err, "cannot set example dependency %q path %q to value: %#v", dependencyKey, fieldPath, val)
   117  	}
   118  	buff, err := p.MarshalJSON()
   119  	if err != nil {
   120  		return errors.Wrapf(err, "cannot marshal dependency %q as JSON", dependencyKey)
   121  	}
   122  	d[dependencyKey] = string(buff)
   123  	return nil
   124  }