Skip to content

Commit

Permalink
Make saving kubeconfig to state optional for civo_kubernetes resource (
Browse files Browse the repository at this point in the history
…#309)

* feat: make saving kubeconfig to state optional for civo_kubernetes resource
* feat: update write_kubeconfig's validator function
  • Loading branch information
Praveen005 authored Aug 13, 2024
1 parent 4ad25cd commit 74cb669
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 7 deletions.
21 changes: 20 additions & 1 deletion civo/kubernetes/resource_kubernetes_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -334,14 +341,21 @@ 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)
// d.Set("built_at", resp.BuiltAt.UTC().String())
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)
}
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
6 changes: 2 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down
68 changes: 67 additions & 1 deletion internal/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"`
Expand Down

0 comments on commit 74cb669

Please sign in to comment.