From 4ccf5e88a44d8d75334e1be4667ae543dd988f71 Mon Sep 17 00:00:00 2001 From: Marques Johansson Date: Mon, 9 Sep 2024 16:51:53 -0400 Subject: [PATCH] distinguish frameworkResources from sdkResources Signed-off-by: Marques Johansson --- config/overrides.go | 52 +++++++++++++++++++++++++++++++++++++- config/provider.go | 61 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 106 insertions(+), 7 deletions(-) diff --git a/config/overrides.go b/config/overrides.go index 54e716c..b6446e8 100644 --- a/config/overrides.go +++ b/config/overrides.go @@ -18,19 +18,69 @@ package config import ( // Note(turkenh): we are importing this to embed provider schema document + "context" _ "embed" + "errors" "strings" upconfig "github.com/crossplane/upjet/pkg/config" ) +type nonemptyIDConversion struct{} + +var invalidNonEmptyString = "" // "badc0de1-a15d-401b-94e4-deadbeefc0de" // "invalidnonemptystring" + +// NewTFNonEmptyIDConversion initializes a new TerraformConversion to remove +// empty ID values from the exchanged data at runtime between the Crossplane +// & Terraform layers. +func NewTFNonEmptyIDConversion() upconfig.TerraformConversion { + return nonemptyIDConversion{} +} + +// Convert will remove empty ID values from the exchanged data at runtime +func (s nonemptyIDConversion) Convert(params map[string]any, r *upconfig.Resource, mode upconfig.Mode) (map[string]any, error) { + var m map[string]any + for k, v := range params { + m[k] = v + if mode == upconfig.FromTerraform && k == "id" && (v.(string) == "" || v.(string) == invalidNonEmptyString) { + m[k] = nil + } + } + return m, nil +} + // IdentifierAssignedByEquinix will work for all Equinix types because even if // the ID is assigned by user, we'll see it in the TF State ID. The // resource-specific configurations should override this whenever possible. // See https://github.com/crossplane/upjet/blob/main/docs/configuring-a-resource.md#case-2-identifier-from-provider for more details. func IdentifierAssignedByEquinix() upconfig.ResourceOption { return func(r *upconfig.Resource) { - r.ExternalName = upconfig.IdentifierFromProvider + e := upconfig.IdentifierFromProvider + e.GetIDFn = func(ctx context.Context, externalName string, parameters map[string]interface{}, cfg map[string]interface{}) (string, error) { + if externalName == "" { + return invalidNonEmptyString, nil + } + return externalName, nil + } + /* + r.TerraformConversions = []upconfig.TerraformConversion{ + NewTFNonEmptyIDConversion(), + } + */ + e.GetExternalNameFn = func(tfstate map[string]any) (string, error) { + id, ok := tfstate["id"] + if !ok { + return "", errors.New("id attribute missing from state file") + } + + idStr, ok := id.(string) + if !ok { + return "", errors.New("value of id needs to be string") + } + + return idStr, nil + } + r.ExternalName = e } } diff --git a/config/provider.go b/config/provider.go index df5338d..75ff1cc 100644 --- a/config/provider.go +++ b/config/provider.go @@ -20,6 +20,7 @@ import ( // Note(turkenh): we are importing this to embed provider schema document "context" _ "embed" + "fmt" "github.com/crossplane-contrib/provider-jet-equinix/config/metal" upconfig "github.com/crossplane/upjet/pkg/config" @@ -44,6 +45,44 @@ var providerSchema string //go:embed provider-metadata.yaml var providerMetadata string +var frameworkResources = []string{ + "equinix_metal_connection", + "equinix_metal_gateway", + "equinix_metal_organization", + "equinix_metal_organization_member", + "equinix_metal_project", + "equinix_metal_project_ssh_key", + "equinix_metal_ssh_key", + "equinix_metal_vlan", +} + +var sdkResources = []string{ + "equinix_fabric_cloud_router", + "equinix_fabric_connection", + "equinix_fabric_network", + "equinix_fabric_routing_protocol", + "equinix_fabric_service_profile", + "equinix_metal_bgp_session", + "equinix_metal_device", + "equinix_metal_device_network_type", + "equinix_metal_ip_attachment", + "equinix_metal_port", + "equinix_metal_port_vlan_attachment", + "equinix_metal_project_api_key", + "equinix_metal_reserved_ip_block", + "equinix_metal_spot_market_request", + "equinix_metal_user_api_key", + "equinix_metal_virtual_circuit", + "equinix_metal_vrf", + "equinix_network_acl_template", + "equinix_network_bgp", + "equinix_network_device", + "equinix_network_device_link", + "equinix_network_file", + "equinix_network_ssh_key", + "equinix_network_ssh_user", +} + func getProviderSchema(s string) (*schema.Provider, error) { ps := tfjson.ProviderSchemas{} if err := ps.UnmarshalJSON([]byte(s)); err != nil { @@ -87,18 +126,18 @@ func GetProvider(_ context.Context, generationProvider bool) (*upconfig.Provider ), upconfig.WithShortName(resourcePrefix), upconfig.WithRootGroup("equinix.jet.crossplane.io"), - upconfig.WithIncludeList([]string{ - ".*", - }), - // + // WithIncludeList implies CLI based Terraform reconciliation + upconfig.WithIncludeList(nil), + upconfig.WithTerraformPluginFrameworkIncludeList(resourceList(frameworkResources)), + upconfig.WithTerraformPluginSDKIncludeList(sdkResources), upconfig.WithReferenceInjectors([]upconfig.ReferenceInjector{reference.NewInjector(modulePath)}), upconfig.WithFeaturesPackage("internal/features"), upconfig.WithTerraformProvider(p), upconfig.WithTerraformPluginFrameworkProvider(fwProvider), upconfig.WithSchemaTraversers(&upconfig.SingletonListEmbedder{}), // upconfig.WithSkipList([]string{".*"}), // helpful when debugging to minimize the number of resources - // config.WithTerraformPluginSDKIncludeList(resourceList(terraformSDKIncludeList)), - // config.WithTerraformPluginFrameworkIncludeList(resourceList(terraformPluginFrameworkExternalNameConfigs)), + // upconfig.WithTerraformPluginSDKIncludeList(resourceList(terraformSDKIncludeList)), + // upconfig.WithTerraformPluginFrameworkIncludeList(resourceList(terraformPluginFrameworkExternalNameConfigs)), ) for _, configure := range []func(provider *upconfig.Provider){ @@ -111,3 +150,13 @@ func GetProvider(_ context.Context, generationProvider bool) (*upconfig.Provider pc.ConfigureResources() return pc, err } + +// resourceList returns a list of resources regex patterns that are expected to be included in the provider +func resourceList(t []string) []string { + l := make([]string, len(t)) + for i, n := range t { + // Expected format is regex and we'd like to have exact matches. + l[i] = fmt.Sprintf("^%s$", n) + } + return l +}