From 74cb669887a316ff0443f25a429f3834b09f5e90 Mon Sep 17 00:00:00 2001 From: Praveen Kumar <42026111+Praveen005@users.noreply.github.com> Date: Tue, 13 Aug 2024 14:43:04 +0530 Subject: [PATCH] Make saving kubeconfig to state optional for civo_kubernetes resource (#309) * feat: make saving kubeconfig to state optional for civo_kubernetes resource * feat: update write_kubeconfig's validator function --- .../kubernetes/resource_kubernetes_cluster.go | 21 +++++- go.mod | 2 +- go.sum | 6 +- internal/utils/utils.go | 68 ++++++++++++++++++- 4 files changed, 90 insertions(+), 7 deletions(-) diff --git a/civo/kubernetes/resource_kubernetes_cluster.go b/civo/kubernetes/resource_kubernetes_cluster.go index decb9de1..a23fb66d 100644 --- a/civo/kubernetes/resource_kubernetes_cluster.go +++ b/civo/kubernetes/resource_kubernetes_cluster.go @@ -126,6 +126,13 @@ func ResourceKubernetesCluster() *schema.Resource { Sensitive: true, Description: "The kubeconfig of the cluster", }, + "write_kubeconfig": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Whether to write the kubeconfig to state", + ValidateDiagFunc: utils.ValidateProviderVersion, + }, "api_endpoint": { Type: schema.TypeString, Computed: true, @@ -334,7 +341,7 @@ func resourceKubernetesClusterRead(_ context.Context, d *schema.ResourceData, m d.Set("tags", strings.Join(resp.Tags, " ")) // space separated tags d.Set("status", resp.Status) d.Set("ready", resp.Ready) - d.Set("kubeconfig", resp.KubeConfig) + // d.Set("kubeconfig", resp.KubeConfig) d.Set("api_endpoint", resp.APIEndPoint) d.Set("master_ip", resp.MasterIP) d.Set("dns_entry", resp.DNSEntry) @@ -342,6 +349,13 @@ func resourceKubernetesClusterRead(_ context.Context, d *schema.ResourceData, m d.Set("created_at", resp.CreatedAt.UTC().String()) d.Set("firewall_id", resp.FirewallID) + writeKubeconfig := d.Get("write_kubeconfig").(bool) + if writeKubeconfig { + d.Set("kubeconfig", resp.KubeConfig) + } else { + d.Set("kubeconfig", "") + } + if err := d.Set("pools", flattenNodePool(resp)); err != nil { return diag.Errorf("[ERR] error retrieving the pool for kubernetes cluster error: %#v", err) } @@ -426,6 +440,11 @@ func resourceKubernetesClusterUpdate(ctx context.Context, d *schema.ResourceData config.Tags = d.Get("tags").(string) } + if d.HasChange("write_kubeconfig") { + // setting atleast one field inside the KubernetesClusterConfig, just to ensure we are not sending an empty config + config.FirewallID = d.Get("firewall_id").(string) + } + log.Printf("[INFO] updating the kubernetes cluster %s", d.Id()) _, err := apiClient.UpdateKubernetesCluster(d.Id(), config) if err != nil { diff --git a/go.mod b/go.mod index a8413293..647d2227 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ require ( github.com/civo/civogo v0.3.73 github.com/google/uuid v1.3.1 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 + github.com/hashicorp/go-version v1.7.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0 github.com/stretchr/testify v1.8.4 golang.org/x/crypto v0.25.0 @@ -30,7 +31,6 @@ require ( github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.6.0 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/hc-install v0.6.2 // indirect github.com/hashicorp/hcl/v2 v2.19.1 // indirect github.com/hashicorp/logutils v1.0.0 // indirect diff --git a/go.sum b/go.sum index f5b5424c..e9c90279 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,6 @@ github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmms github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/civo/civogo v0.3.70 h1:QPuFm5EmpkScbdFo5/6grcG2xcvd/lgdolOtENT04Ac= -github.com/civo/civogo v0.3.70/go.mod h1:7UCYX+qeeJbrG55E1huv+0ySxcHTqq/26FcHLVelQJM= github.com/civo/civogo v0.3.73 h1:thkNnkziU+xh+MEOChIUwRZI1forN20+SSAPe/VFDME= github.com/civo/civogo v0.3.73/go.mod h1:7UCYX+qeeJbrG55E1huv+0ySxcHTqq/26FcHLVelQJM= github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= @@ -78,8 +76,8 @@ github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/hc-install v0.6.2 h1:V1k+Vraqz4olgZ9UzKiAcbman9i9scg9GgSt/U3mw/M= github.com/hashicorp/hc-install v0.6.2/go.mod h1:2JBpd+NCFKiHiu/yYCGaPyPHhZLxXTpz8oreHa/a3Ps= github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI= diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 5fa2ce74..20518b18 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -6,18 +6,27 @@ package utils import ( "encoding/json" "fmt" - "github.com/google/uuid" + "log" + "os/exec" "regexp" "sort" "strconv" "strings" "sync" + "github.com/google/uuid" + "github.com/civo/civogo" "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/go-version" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" ) +// VersionInfo stores Provider's version Info +type VersionInfo struct { + ProviderSelections map[string]string `json:"provider_selections"` +} + // ValidateName is a function to check if the name is valid func ValidateName(v interface{}, _ string) (ws []string, es []error) { var errs []error @@ -190,6 +199,63 @@ func ValidateClusterType(v interface{}, path cty.Path) diag.Diagnostics { return diags } +// ValidateProviderVersion function compares the current provider verson of the user with the threshold version and shows warning accordingly +func ValidateProviderVersion(v interface{}, path cty.Path) diag.Diagnostics { + var versionInfo VersionInfo + diags := diag.Diagnostics{} + + cmd := exec.Command("terraform", "version", "-json") + output, err := cmd.Output() + if err != nil { + log.Printf("[ERROR] error running terraform show: %v\n", err) + return diags + } + + err = json.Unmarshal(output, &versionInfo) + if err != nil { + log.Printf("[ERROR] error parsing JSON: %v\n", err) + return diags + } + versionField := "registry.terraform.io/civo/civo" + currentProviderVersion := versionInfo.ProviderSelections[versionField] + thresholdProviderVersion := "1.0.49" + + v1, err := version.NewSemver(currentProviderVersion) + if err != nil { + log.Println("[ERROR] error parsing the given version") + return diags + } + v2, err := version.NewVersion(thresholdProviderVersion) + if err != nil { + log.Println("[ERROR] error parsing the given version") + return diags + } + + lastStep := path[len(path)-1] + var field string + if step, ok := lastStep.(cty.GetAttrStep); ok { + field = step.Name + } + + if v1.LessThanOrEqual(v2) { + if field == "write_password" { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Default initial_password behavior changed", + Detail: "Starting from version 1.0.50 the initial password is not written to state by default, if you wish to keep the initial password configuration in state, please add the input write_password and set it to true. Example configuration: `write_password = true`.", + }) + + } else if field == "write_kubeconfig" { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Default kubeconfig behavior changed", + Detail: "Starting from version 1.0.50, kubeconfig will no longer be written to the Terraform state by default for the civo_kubernetes resource. This change is made to enhance security by preventing sensitive information from being stored in state files. If you want to retain kubeconfig in your state file, please update your configuration by adding the `write_kubeconfig` parameter and setting it to `true`. Example configuration: `write_kubeconfig = true`.", + }) + } + } + return diags +} + // CustomError captures a specific portion of the full API error type CustomError struct { Code string `json:"code"`