From fce24d588f8fbd54cad5da88545c67934f6223a0 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 6 Mar 2024 15:09:49 -0800 Subject: [PATCH 1/2] backend/kubernetes: Remove legacy helper/schema dependency As with most of our remote state backends, this one was depending on just a tiny slice of the (enormous and now-poorly-understood) legacy SDK. In an effort to eliminate the legacy SDK snapshot from this codebase, this replaces it with functionality from our new "backendbase" package, which aims to provide just a narrow set of utilities to minimize the churn caused by removing the legacy SDK and thus reduce the risk of this change. This is currently using the "SDK-like" utilities, which emulate some of the questionable-but-convenient assumptions the legacy SDK makes, such as the idea that empty string and null are equivalent. Hopefully in future we can wean this backend even further off of these older assumptions, but the priority for now is to eliminate the legacy SDK without significantly disturbing the shape of the existing working code. --- .../remote-state/kubernetes/backend.go | 470 ++++++++++-------- .../backend/remote-state/kubernetes/go.mod | 6 +- .../backend/remote-state/kubernetes/go.sum | 6 - 3 files changed, 260 insertions(+), 222 deletions(-) diff --git a/internal/backend/remote-state/kubernetes/backend.go b/internal/backend/remote-state/kubernetes/backend.go index 85d3037c67..8ba390dfb1 100644 --- a/internal/backend/remote-state/kubernetes/backend.go +++ b/internal/backend/remote-state/kubernetes/backend.go @@ -5,16 +5,13 @@ package kubernetes import ( "bytes" - "context" "fmt" "log" "os" "path/filepath" - "github.com/hashicorp/terraform/internal/backend" - "github.com/hashicorp/terraform/internal/legacy/helper/schema" - "github.com/hashicorp/terraform/version" "github.com/mitchellh/go-homedir" + "github.com/zclconf/go-cty/cty" k8sSchema "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" @@ -22,6 +19,12 @@ import ( restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + + "github.com/hashicorp/terraform/internal/backend" + "github.com/hashicorp/terraform/internal/backend/backendbase" + "github.com/hashicorp/terraform/internal/configs/configschema" + "github.com/hashicorp/terraform/internal/tfdiags" + "github.com/hashicorp/terraform/version" ) // Modified from github.com/terraform-providers/terraform-provider-kubernetes @@ -44,158 +47,191 @@ var ( // New creates a new backend for kubernetes remote state. func New() backend.Backend { - s := &schema.Backend{ - Schema: map[string]*schema.Schema{ - "secret_suffix": { - Type: schema.TypeString, - Required: true, - Description: "Suffix used when creating the secret. The secret will be named in the format: `tfstate-{workspace}-{secret_suffix}`.", - }, - "labels": { - Type: schema.TypeMap, - Optional: true, - Description: "Map of additional labels to be applied to the secret.", - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "namespace": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("KUBE_NAMESPACE", "default"), - Description: "Namespace to store the secret in.", - }, - "in_cluster_config": { - Type: schema.TypeBool, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("KUBE_IN_CLUSTER_CONFIG", false), - Description: "Used to authenticate to the cluster from inside a pod.", - }, - "load_config_file": { - Type: schema.TypeBool, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("KUBE_LOAD_CONFIG_FILE", true), - Description: "Load local kubeconfig.", - }, - "host": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("KUBE_HOST", ""), - Description: "The hostname (in form of URI) of Kubernetes master.", - }, - "username": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("KUBE_USER", ""), - Description: "The username to use for HTTP basic authentication when accessing the Kubernetes master endpoint.", - }, - "password": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("KUBE_PASSWORD", ""), - Description: "The password to use for HTTP basic authentication when accessing the Kubernetes master endpoint.", - }, - "insecure": { - Type: schema.TypeBool, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("KUBE_INSECURE", false), - Description: "Whether server should be accessed without verifying the TLS certificate.", - }, - "client_certificate": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("KUBE_CLIENT_CERT_DATA", ""), - Description: "PEM-encoded client certificate for TLS authentication.", - }, - "client_key": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("KUBE_CLIENT_KEY_DATA", ""), - Description: "PEM-encoded client certificate key for TLS authentication.", - }, - "cluster_ca_certificate": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("KUBE_CLUSTER_CA_CERT_DATA", ""), - Description: "PEM-encoded root certificates bundle for TLS authentication.", - }, - "config_paths": { - Type: schema.TypeList, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - Description: "A list of paths to kube config files. Can be set with KUBE_CONFIG_PATHS environment variable.", - }, - "config_path": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("KUBE_CONFIG_PATH", ""), - Description: "Path to the kube config file. Can be set with KUBE_CONFIG_PATH environment variable.", - }, - "config_context": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("KUBE_CTX", ""), - }, - "config_context_auth_info": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("KUBE_CTX_AUTH_INFO", ""), - Description: "", - }, - "config_context_cluster": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("KUBE_CTX_CLUSTER", ""), - Description: "", - }, - "token": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.EnvDefaultFunc("KUBE_TOKEN", ""), - Description: "Token to authentifcate a service account.", - }, - "proxy_url": { - Type: schema.TypeString, - Optional: true, - Description: "URL to the proxy to be used for all API requests", - DefaultFunc: schema.EnvDefaultFunc("KUBE_PROXY_URL", ""), - }, - "exec": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "api_version": { - Type: schema.TypeString, - Required: true, - }, - "command": { - Type: schema.TypeString, - Required: true, - }, - "env": { - Type: schema.TypeMap, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "args": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, + return &Backend{ + Base: backendbase.Base{ + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "secret_suffix": { + Type: cty.String, + Required: true, + Description: "Suffix used when creating the secret. The secret will be named in the format: `tfstate-{workspace}-{secret_suffix}`.", + }, + "labels": { + Type: cty.Map(cty.String), + Optional: true, + Description: "Map of additional labels to be applied to the secret.", + }, + "namespace": { + Type: cty.String, + Optional: true, + Description: "Namespace to store the secret in.", + }, + "in_cluster_config": { + Type: cty.Bool, + Optional: true, + Description: "Used to authenticate to the cluster from inside a pod.", + }, + "load_config_file": { + Type: cty.Bool, + Optional: true, + Description: "Load local kubeconfig.", + }, + "host": { + Type: cty.String, + Optional: true, + Description: "The hostname (in form of URI) of Kubernetes master.", + }, + "username": { + Type: cty.String, + Optional: true, + Description: "The username to use for HTTP basic authentication when accessing the Kubernetes master endpoint.", + }, + "password": { + Type: cty.String, + Optional: true, + Description: "The password to use for HTTP basic authentication when accessing the Kubernetes master endpoint.", + }, + "insecure": { + Type: cty.Bool, + Optional: true, + Description: "Whether server should be accessed without verifying the TLS certificate.", + }, + "client_certificate": { + Type: cty.String, + Optional: true, + Description: "PEM-encoded client certificate for TLS authentication.", + }, + "client_key": { + Type: cty.String, + Optional: true, + Description: "PEM-encoded client certificate key for TLS authentication.", + }, + "cluster_ca_certificate": { + Type: cty.String, + Optional: true, + Description: "PEM-encoded root certificates bundle for TLS authentication.", + }, + "config_paths": { + Type: cty.List(cty.String), + Optional: true, + Description: "A list of paths to kube config files. Can be set with KUBE_CONFIG_PATHS environment variable.", + }, + "config_path": { + Type: cty.String, + Optional: true, + Description: "Path to the kube config file. Can be set with KUBE_CONFIG_PATH environment variable.", + }, + "config_context": { + Type: cty.String, + Optional: true, + }, + "config_context_auth_info": { + Type: cty.String, + Optional: true, + Description: "", + }, + "config_context_cluster": { + Type: cty.String, + Optional: true, + Description: "", + }, + "token": { + Type: cty.String, + Optional: true, + Description: "Token to authentifcate a service account.", + }, + "proxy_url": { + Type: cty.String, + Optional: true, + Description: "URL to the proxy to be used for all API requests", + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "exec": { + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "api_version": { + Type: cty.String, + Required: true, + }, + "command": { + Type: cty.String, + Required: true, + }, + "env": { + Type: cty.Map(cty.String), + Optional: true, + }, + "args": { + Type: cty.List(cty.String), + Optional: true, + }, + }, }, }, }, - Description: "Use a credential plugin to authenticate.", + }, + SDKLikeDefaults: backendbase.SDKLikeDefaults{ + "namespace": { + EnvVars: []string{"KUBE_NAMESPACE"}, + Fallback: "default", + }, + "in_cluster_config": { + EnvVars: []string{"KUBE_IN_CLUSTER_CONFIG"}, + Fallback: "false", + }, + "load_config_file": { + EnvVars: []string{"KUBE_LOAD_CONFIG_FILE"}, + Fallback: "true", + }, + "host": { + EnvVars: []string{"KUBE_HOST"}, + }, + "username": { + EnvVars: []string{"KUBE_USER"}, + }, + "password": { + EnvVars: []string{"KUBE_PASSWORD"}, + }, + "insecure": { + EnvVars: []string{"KUBE_INSECURE"}, + Fallback: "false", + }, + "client_certificate": { + EnvVars: []string{"KUBE_CLIENT_CERT_DATA"}, + }, + "client_key": { + EnvVars: []string{"KUBE_CLIENT_KEY_DATA"}, + }, + "cluster_ca_certificate": { + EnvVars: []string{"KUBE_CLUSTER_CA_CERT_DATA"}, + }, + "config_path": { + EnvVars: []string{"KUBE_CONFIG_PATH"}, + }, + "config_context": { + EnvVars: []string{"KUBE_CTX"}, + }, + "config_context_auth_info": { + EnvVars: []string{"KUBE_CTX_AUTH_INFO"}, + }, + "config_context_cluster": { + EnvVars: []string{"KUBE_CTX_CLUSTER"}, + }, + "token": { + EnvVars: []string{"KUBE_TOKEN"}, + }, + "proxy_url": { + EnvVars: []string{"KUBE_PROXY_URL"}, + }, }, }, } - - result := &Backend{Backend: s} - result.Backend.ConfigureFunc = result.configure - return result } type Backend struct { - *schema.Backend + backendbase.Base // The fields below are set from configure kubernetesSecretClient dynamic.ResourceInterface @@ -234,68 +270,69 @@ func (b Backend) KubernetesLeaseClient() (coordinationv1.LeaseInterface, error) return b.kubernetesLeaseClient, nil } -func (b *Backend) configure(ctx context.Context) error { +func (b *Backend) Configure(configVal cty.Value) tfdiags.Diagnostics { if b.config != nil { return nil } - // Grab the resource data - data := schema.FromContextBackendConfig(ctx) + data := backendbase.NewSDKLikeData(configVal) cfg, err := getInitialConfig(data) if err != nil { - return err + return backendbase.ErrorAsDiagnostics(err) } // Overriding with static configuration cfg.UserAgent = fmt.Sprintf("HashiCorp/1.0 Terraform/%s", version.String()) - if v, ok := data.GetOk("host"); ok { - cfg.Host = v.(string) - } - if v, ok := data.GetOk("username"); ok { - cfg.Username = v.(string) + if v := data.String("host"); v != "" { + cfg.Host = v } - if v, ok := data.GetOk("password"); ok { - cfg.Password = v.(string) + if v := data.String("username"); v != "" { + cfg.Username = v } - if v, ok := data.GetOk("insecure"); ok { - cfg.Insecure = v.(bool) + if v := data.String("password"); v != "" { + cfg.Password = v } - if v, ok := data.GetOk("cluster_ca_certificate"); ok { - cfg.CAData = bytes.NewBufferString(v.(string)).Bytes() + cfg.Insecure = data.Bool("insecure") + if v := data.String("cluster_ca_certificate"); v != "" { + cfg.CAData = bytes.NewBufferString(v).Bytes() } - if v, ok := data.GetOk("client_certificate"); ok { - cfg.CertData = bytes.NewBufferString(v.(string)).Bytes() + if v := data.String("client_certificate"); v != "" { + cfg.CertData = bytes.NewBufferString(v).Bytes() } - if v, ok := data.GetOk("client_key"); ok { - cfg.KeyData = bytes.NewBufferString(v.(string)).Bytes() + if v := data.String("client_key"); v != "" { + cfg.KeyData = bytes.NewBufferString(v).Bytes() } - if v, ok := data.GetOk("token"); ok { - cfg.BearerToken = v.(string) + if v := data.String("token"); v != "" { + cfg.BearerToken = v } - if v, ok := data.GetOk("labels"); ok { + if v := data.GetAttr("labels", cty.Map(cty.String)); !v.IsNull() { labels := map[string]string{} - for k, vv := range v.(map[string]interface{}) { - labels[k] = vv.(string) + for it := v.ElementIterator(); it.Next(); { + kV, vV := it.Element() + if vV.IsNull() { + vV = cty.StringVal("") + } + labels[kV.AsString()] = vV.AsString() } b.labels = labels } - ns := data.Get("namespace").(string) + ns := data.String("namespace") b.namespace = ns - b.nameSuffix = data.Get("secret_suffix").(string) + b.nameSuffix = data.String("secret_suffix") b.config = cfg return nil } -func getInitialConfig(data *schema.ResourceData) (*restclient.Config, error) { +func getInitialConfig(data backendbase.SDKLikeData) (*restclient.Config, error) { var cfg *restclient.Config var err error - inCluster := data.Get("in_cluster_config").(bool) + inCluster := data.Bool("in_cluster_config") if inCluster { cfg, err = restclient.InClusterConfig() if err != nil { @@ -314,16 +351,14 @@ func getInitialConfig(data *schema.ResourceData) (*restclient.Config, error) { return cfg, err } -func tryLoadingConfigFile(d *schema.ResourceData) (*restclient.Config, error) { +func tryLoadingConfigFile(d backendbase.SDKLikeData) (*restclient.Config, error) { loader := &clientcmd.ClientConfigLoadingRules{} configPaths := []string{} - if v, ok := d.Get("config_path").(string); ok && v != "" { + if v := d.String("config_path"); v != "" { configPaths = []string{v} - } else if v, ok := d.Get("config_paths").([]interface{}); ok && len(v) > 0 { - for _, p := range v { - configPaths = append(configPaths, p.(string)) - } + } else if v := d.GetAttr("config_paths", cty.List(cty.String)); !v.IsNull() { + configPaths = append(configPaths, decodeListOfString(v)...) } else if v := os.Getenv("KUBE_CONFIG_PATHS"); v != "" { configPaths = filepath.SplitList(v) } @@ -348,46 +383,56 @@ func tryLoadingConfigFile(d *schema.ResourceData) (*restclient.Config, error) { overrides := &clientcmd.ConfigOverrides{} ctxSuffix := "; default context" - ctx, ctxOk := d.GetOk("config_context") - authInfo, authInfoOk := d.GetOk("config_context_auth_info") - cluster, clusterOk := d.GetOk("config_context_cluster") - if ctxOk || authInfoOk || clusterOk { + configCtx := d.String("config_context") + authInfo := d.String("config_context_auth_info") + cluster := d.String("config_context_cluster") + if configCtx != "" || authInfo != "" || cluster != "" { ctxSuffix = "; overriden context" - if ctxOk { - overrides.CurrentContext = ctx.(string) + if configCtx != "" { + overrides.CurrentContext = configCtx ctxSuffix += fmt.Sprintf("; config ctx: %s", overrides.CurrentContext) log.Printf("[DEBUG] Using custom current context: %q", overrides.CurrentContext) } overrides.Context = clientcmdapi.Context{} - if authInfoOk { - overrides.Context.AuthInfo = authInfo.(string) + if authInfo != "" { + overrides.Context.AuthInfo = authInfo ctxSuffix += fmt.Sprintf("; auth_info: %s", overrides.Context.AuthInfo) } - if clusterOk { - overrides.Context.Cluster = cluster.(string) + if cluster != "" { + overrides.Context.Cluster = cluster ctxSuffix += fmt.Sprintf("; cluster: %s", overrides.Context.Cluster) } log.Printf("[DEBUG] Using overidden context: %#v", overrides.Context) } - if v, ok := d.GetOk("exec"); ok { - exec := &clientcmdapi.ExecConfig{} - if spec, ok := v.([]interface{})[0].(map[string]interface{}); ok { - exec.APIVersion = spec["api_version"].(string) - exec.Command = spec["command"].(string) - exec.Args = expandStringSlice(spec["args"].([]interface{})) - for kk, vv := range spec["env"].(map[string]interface{}) { - exec.Env = append(exec.Env, clientcmdapi.ExecEnvVar{Name: kk, Value: vv.(string)}) + // exec is a nested block with nesting mode NestingSingle, so GetAttr + // will return a value of an object type that will be null if the block + // isn't present at all. + if v := d.GetAttr("exec", cty.DynamicPseudoType); !v.IsNull() { + spec := backendbase.NewSDKLikeData(v) + exec := &clientcmdapi.ExecConfig{ + APIVersion: spec.String("api_version"), + Command: spec.String("command"), + Args: decodeListOfString(spec.GetAttr("args", cty.List(cty.String))), + } + if envMap := spec.GetAttr("env", cty.Map(cty.String)); !envMap.IsNull() { + for it := envMap.ElementIterator(); it.Next(); { + kV, vV := it.Element() + if vV.IsNull() { + vV = cty.StringVal("") + } + exec.Env = append(exec.Env, clientcmdapi.ExecEnvVar{ + Name: kV.AsString(), + Value: vV.AsString(), + }) } - } else { - return nil, fmt.Errorf("Failed to parse exec") } overrides.AuthInfo.Exec = exec } - if v, ok := d.GetOk("proxy_url"); ok { - overrides.ClusterDefaults.ProxyURL = v.(string) + if v := d.String("proxy_url"); v != "" { + overrides.ClusterDefaults.ProxyURL = v } cc := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loader, overrides) @@ -404,15 +449,18 @@ func tryLoadingConfigFile(d *schema.ResourceData) (*restclient.Config, error) { return cfg, nil } -func expandStringSlice(s []interface{}) []string { - result := make([]string, len(s), len(s)) - for k, v := range s { - // Handle the Terraform parser bug which turns empty strings in lists to nil. - if v == nil { - result[k] = "" +func decodeListOfString(v cty.Value) []string { + if v.IsNull() { + return nil + } + ret := make([]string, 0, v.LengthInt()) + for it := v.ElementIterator(); it.Next(); { + _, vV := it.Element() + if vV.IsNull() { + ret = append(ret, "") } else { - result[k] = v.(string) + ret = append(ret, vV.AsString()) } } - return result + return ret } diff --git a/internal/backend/remote-state/kubernetes/go.mod b/internal/backend/remote-state/kubernetes/go.mod index f75f81932b..c7a0cc2bf2 100644 --- a/internal/backend/remote-state/kubernetes/go.mod +++ b/internal/backend/remote-state/kubernetes/go.mod @@ -4,8 +4,8 @@ go 1.22.5 require ( github.com/hashicorp/terraform v0.0.0-00010101000000-000000000000 - github.com/hashicorp/terraform/internal/legacy v0.0.0-00010101000000-000000000000 github.com/mitchellh/go-homedir v1.1.0 + github.com/zclconf/go-cty v1.15.0 k8s.io/api v0.25.5 k8s.io/apimachinery v0.25.5 k8s.io/client-go v0.25.5 @@ -49,10 +49,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/mailru/easyjson v0.7.6 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect @@ -60,7 +57,6 @@ require ( github.com/spf13/afero v1.9.3 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/testify v1.8.4 // indirect - github.com/zclconf/go-cty v1.15.0 // indirect golang.org/x/crypto v0.21.0 // indirect golang.org/x/mod v0.16.0 // indirect golang.org/x/net v0.23.0 // indirect diff --git a/internal/backend/remote-state/kubernetes/go.sum b/internal/backend/remote-state/kubernetes/go.sum index 47c5b4b80e..80d0a20d08 100644 --- a/internal/backend/remote-state/kubernetes/go.sum +++ b/internal/backend/remote-state/kubernetes/go.sum @@ -261,18 +261,12 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= From 5001b937fe262bf96fd4118846e4ba8de49cb730 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Mon, 9 Sep 2024 16:23:08 -0400 Subject: [PATCH 2/2] remove unused replace directive --- internal/backend/remote-state/kubernetes/go.mod | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/backend/remote-state/kubernetes/go.mod b/internal/backend/remote-state/kubernetes/go.mod index c7a0cc2bf2..bb51e40246 100644 --- a/internal/backend/remote-state/kubernetes/go.mod +++ b/internal/backend/remote-state/kubernetes/go.mod @@ -95,6 +95,4 @@ replace github.com/hashicorp/terraform/internal/backend/remote-state/pg => ../pg replace github.com/hashicorp/terraform/internal/backend/remote-state/s3 => ../s3 -replace github.com/hashicorp/terraform/internal/legacy => ../../../legacy - replace github.com/hashicorp/terraform => ../../../..