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), ¶ms); 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 }