Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 54 additions & 27 deletions docs/resources/cluster_v2.md

Large diffs are not rendered by default.

108 changes: 105 additions & 3 deletions rancher2/resource_rancher2_cluster_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,35 @@ func resourceRancher2ClusterV2() *schema.Resource {
oldInterface, oldOk := oldObj.([]interface{})
newInterface, newOk := newObj.([]interface{})
if oldOk && newOk && len(newInterface) > 0 {
if m, ok := newInterface[0].(map[string]interface{}); ok {
if ca, ok := m["ca_certs"].(string); ok && ca != "" {
if use, ok := m["use_internal_ca_certs"].(bool); ok && use {
return fmt.Errorf("only one of \"ca_certs\" or \"use_internal_ca_certs\" can be set")
}
}
}
oldConfig := expandClusterV2LocalAuthEndpoint(oldInterface)
newConfig := expandClusterV2LocalAuthEndpoint(newInterface)
if reflect.DeepEqual(oldConfig, newConfig) {
oldUse := false
newUse := false
if len(oldInterface) > 0 {
if m, ok := oldInterface[0].(map[string]interface{}); ok {
if v, ok := m["use_internal_ca_certs"].(bool); ok {
oldUse = v
}
}
}
if len(newInterface) > 0 {
if m, ok := newInterface[0].(map[string]interface{}); ok {
if v, ok := m["use_internal_ca_certs"].(bool); ok {
newUse = v
}
}
}
if reflect.DeepEqual(oldConfig, newConfig) && oldUse == newUse {
d.Clear("local_auth_endpoint")
} else {
d.SetNew("local_auth_endpoint", flattenClusterV2LocalAuthEndpoint(newConfig))
d.SetNew("local_auth_endpoint", newObj)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe I am missing something, but why are we cutting out the flattener here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flattenClusterV2LocalAuthEndpoint serializes only API fields (ca_certs, enabled, fqdn) and does not include the Terraform-only use_internal_ca_certs flag. If we used the flattener here in CustomizeDiff, that flag would be stripped from the planned object. By passing newObj directly (which already is []interface{} from d.GetChange()), we preserve it.

}
}
}
Expand Down Expand Up @@ -110,6 +133,15 @@ func resourceRancher2ClusterV2Create(d *schema.ResourceData, meta interface{}) e
return err
}

useInternal := false
if v, ok := d.Get("local_auth_endpoint").([]interface{}); ok && len(v) > 0 {
if m, ok := v[0].(map[string]interface{}); ok {
if b, ok := m["use_internal_ca_certs"].(bool); ok {
useInternal = b
}
}
}

log.Printf("[INFO] Creating Cluster V2 %s", name)

newCluster, err := createClusterV2(meta.(*Config), cluster)
Expand All @@ -130,6 +162,18 @@ func resourceRancher2ClusterV2Create(d *schema.ResourceData, meta interface{}) e
}
}

if useInternal {
caCert, err := getClusterCACert(meta.(*Config), newCluster.Status.ClusterName)
if err != nil {
return err
}
newCluster.Spec.LocalClusterAuthEndpoint.CACerts = caCert
_, err = updateClusterV2(meta.(*Config), newCluster.ID, newCluster)
if err != nil {
return err
}
}

return resourceRancher2ClusterV2Read(d, meta)
}

Expand All @@ -150,7 +194,10 @@ func resourceRancher2ClusterV2Read(d *schema.ResourceData, meta interface{}) err
if err != nil {
return err
}
return flattenClusterV2(d, cluster)
if err := flattenClusterV2(d, cluster); err != nil {
return err
}
return setClusterV2LocalAuthEndpointInternalFlag(d, meta.(*Config), cluster)
}

func resourceRancher2ClusterV2Update(d *schema.ResourceData, meta interface{}) error {
Expand All @@ -159,6 +206,18 @@ func resourceRancher2ClusterV2Update(d *schema.ResourceData, meta interface{}) e
return err
}

if v, ok := d.Get("local_auth_endpoint").([]interface{}); ok && len(v) > 0 {
if m, ok := v[0].(map[string]interface{}); ok {
if b, ok := m["use_internal_ca_certs"].(bool); ok && b {
caCert, err := getClusterCACert(meta.(*Config), d.Get("cluster_v1_id").(string))
if err != nil {
return err
}
cluster.Spec.LocalClusterAuthEndpoint.CACerts = caCert
}
}
}

log.Printf("[INFO] Updating Cluster V2 %s", d.Id())

newCluster, err := updateClusterV2(meta.(*Config), d.Id(), cluster)
Expand Down Expand Up @@ -420,3 +479,46 @@ func setClusterV2LegacyData(d *schema.ResourceData, c *Config) error {

return nil
}

func getClusterCACert(c *Config, clusterV1ID string) (string, error) {
if c == nil {
return "", fmt.Errorf("provider config is nil")
}
if clusterV1ID == "" {
return "", fmt.Errorf("cluster_v1_id is empty")
}
client, err := c.ManagementClient()
if err != nil {
return "", err
}
cluster := &Cluster{}
err = client.APIBaseClient.ByID(managementClient.ClusterType, clusterV1ID, cluster)
if err != nil {
return "", err
}
return decodeCACertIfBase64(cluster.CACert), nil
}
Comment on lines +483 to +500

func setClusterV2LocalAuthEndpointInternalFlag(d *schema.ResourceData, c *Config, cluster *ClusterV2) error {
if cluster == nil || c == nil {
return fmt.Errorf("setting local auth endpoint internal flag: missing data")
}
lae := cluster.Spec.LocalClusterAuthEndpoint
useInternal := false
if cluster.Status.ClusterName != "" && lae.CACerts != "" {
caCert, err := getClusterCACert(c, cluster.Status.ClusterName)
if err != nil {
return err
}
if lae.CACerts == caCert {
useInternal = true
}
}
if v, ok := d.Get("local_auth_endpoint").([]interface{}); ok && len(v) > 0 {
if m, ok := v[0].(map[string]interface{}); ok {
m["use_internal_ca_certs"] = useInternal
d.Set("local_auth_endpoint", []interface{}{m})
}
}
return nil
}
Comment on lines +502 to +524
6 changes: 6 additions & 0 deletions rancher2/schema_cluster_v2_rke_config_local_auth_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ func clusterV2LocalAuthEndpointFields() map[string]*schema.Schema {
"ca_certs": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"enabled": {
Type: schema.TypeBool,
Expand All @@ -21,6 +22,11 @@ func clusterV2LocalAuthEndpointFields() map[string]*schema.Schema {
Type: schema.TypeString,
Optional: true,
},
"use_internal_ca_certs": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
}

return s
Expand Down
24 changes: 21 additions & 3 deletions rancher2/structure_cluster_v2_local_auth_endpoint_test.go
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really appreciate the test additions, thank you!

Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import (
)

var (
testClusterV2LocalAuthEndpointConf rkev1.LocalClusterAuthEndpoint
testClusterV2LocalAuthEndpointInterface []interface{}
testClusterV2LocalAuthEndpointConf rkev1.LocalClusterAuthEndpoint
testClusterV2LocalAuthEndpointInterface []interface{}
testClusterV2LocalAuthEndpointInterfaceUseInternal []interface{}
testClusterV2LocalAuthEndpointInterfaceWithFlag []interface{}
)

func init() {
Expand All @@ -26,6 +28,22 @@ func init() {
"fqdn": "fqdn",
},
}
testClusterV2LocalAuthEndpointInterfaceUseInternal = []interface{}{
map[string]interface{}{
"ca_certs": "ca_certs",
"enabled": true,
"fqdn": "fqdn",
"use_internal_ca_certs": true,
},
Comment on lines +31 to +37
}
testClusterV2LocalAuthEndpointInterfaceWithFlag = []interface{}{
map[string]interface{}{
"ca_certs": "ca_certs",
"enabled": true,
"fqdn": "fqdn",
"use_internal_ca_certs": false,
},
}
}

func TestFlattenClusterV2LocalAuthEndpoint(t *testing.T) {
Expand Down Expand Up @@ -53,7 +71,7 @@ func TestExpandClusterV2LocalAuthEndpoint(t *testing.T) {
ExpectedOutput rkev1.LocalClusterAuthEndpoint
}{
{
testClusterV2LocalAuthEndpointInterface,
testClusterV2LocalAuthEndpointInterfaceUseInternal,
testClusterV2LocalAuthEndpointConf,
},
Comment on lines 73 to 76
}
Expand Down
2 changes: 1 addition & 1 deletion rancher2/structure_cluster_v2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ func init() {
"name": "name",
"fleet_namespace": "fleet_namespace",
"kubernetes_version": "kubernetes_version",
"local_auth_endpoint": testClusterV2LocalAuthEndpointInterface,
"local_auth_endpoint": testClusterV2LocalAuthEndpointInterfaceWithFlag,
"rke_config": testClusterV2RKEConfigInterface,
"agent_env_vars": testClusterV2EnvVarInterface,
"cluster_agent_deployment_customization": testClusterV2ClusterAgentCustomizationInterface,
Expand Down
13 changes: 13 additions & 0 deletions rancher2/util_certs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package rancher2

import "encoding/base64"

func decodeCACertIfBase64(cert string) string {
if cert == "" {
return cert
}
if b, err := base64.StdEncoding.DecodeString(cert); err == nil {
return string(b)
}
return cert
}
17 changes: 17 additions & 0 deletions rancher2/util_certs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package rancher2

import (
"encoding/base64"
"testing"
)

func TestDecodeCACertIfBase64(t *testing.T) {
pem := "-----BEGIN CERTIFICATE-----\nFAKE\n-----END CERTIFICATE-----\n"
encoded := base64.StdEncoding.EncodeToString([]byte(pem))
if got := decodeCACertIfBase64(encoded); got != pem {
t.Fatalf("expected decoded cert, got %q", got)
}
if got := decodeCACertIfBase64(pem); got != pem {
t.Fatalf("expected unchanged cert, got %q", got)
}
}
Loading