From 7e799591ccc1f58a24d983988f4f49d6bfa731c4 Mon Sep 17 00:00:00 2001 From: Denis Banshchikov Date: Sat, 16 Aug 2025 23:38:41 +0300 Subject: [PATCH 1/5] feat(cluster_v2): add use_internal_ca_certs option for local_auth_endpoint --- docs/resources/cluster_v2.md | 67 ++++++------ rancher2/resource_rancher2_cluster_v2.go | 101 +++++++++++++++++- ...uster_v2_rke_config_local_auth_endpoint.go | 13 ++- ...ure_cluster_v2_local_auth_endpoint_test.go | 17 ++- 4 files changed, 155 insertions(+), 43 deletions(-) diff --git a/docs/resources/cluster_v2.md b/docs/resources/cluster_v2.md index 307db9fd5..358844b7d 100644 --- a/docs/resources/cluster_v2.md +++ b/docs/resources/cluster_v2.md @@ -4,7 +4,7 @@ page_title: "rancher2_cluster_v2 Resource" # rancher2\_cluster\_v2 Resource -Provides a Rancher v2 Cluster v2 resource. This can be used to create RKE2 and K3s Clusters for Rancher v2 environments and retrieve their information. +Provides a Rancher v2 Cluster v2 resource. This can be used to create RKE2 and K3s Clusters for Rancher v2 environments and retrieve their information. This resource is available in Rancher v2.6.0 and above. @@ -117,7 +117,7 @@ resource "rancher2_cluster_v2" "foo-k3s" { kubernetes_version = "rke2/k3s-version" enable_network_policy = false rke_config { - # nodes in this pool have all roles + # nodes in this pool have all roles machine_pools { name = "pool" cloud_credential_secret_name = rancher2_cloud_credential.foo.id @@ -190,7 +190,7 @@ resource "rancher2_machine_config_v2" "foo-harvester-v2" { } } -# Create a new cluster +# Create a new cluster resource "rancher2_cluster_v2" "foo-harvester-v2" { name = "foo-harvester-v2" kubernetes_version = "" @@ -229,7 +229,7 @@ EOF } ``` -### Create a node-driver cluster with Harvester as both the infrastructure provider and cloud provider +### Create a node-driver cluster with Harvester as both the infrastructure provider and cloud provider The example below utilizes the arguments such as `machine_selector_config`, `machine_global_config`, and `chart_values`. More explanations and examples for those arguments can be found on this page. @@ -428,7 +428,7 @@ EOF memory_request = "500Mi" } } - + fleet_agent_deployment_customization { # The same format and requirement for values as the cluster_agent_deployment_customization argument # ... @@ -438,7 +438,7 @@ EOF machine_pools { # ... } - } + } } ``` @@ -446,12 +446,12 @@ EOF This argument is available in Rancher 2.11.0 and above. -You can configure a Priority Class and or Pod Disruption Budget to be automatically deployed for the cattle cluster agent when provisioning or updating downstream clusters. +You can configure a Priority Class and or Pod Disruption Budget to be automatically deployed for the cattle cluster agent when provisioning or updating downstream clusters. -In order to use this field, you must ensure that the `cluster-agent-scheduling-customization` feature is enabled in the Rancher server. +In order to use this field, you must ensure that the `cluster-agent-scheduling-customization` feature is enabled in the Rancher server. -The example below demonstrates how to set the `scheduling_customization` field to deploy a Priority Class and Pod Disruption Budget. Currently, this field is only supported for the cluster agent. +The example below demonstrates how to set the `scheduling_customization` field to deploy a Priority Class and Pod Disruption Budget. Currently, this field is only supported for the cluster agent. ```hcl resource "rancher2_cluster_v2" "foo" { @@ -460,22 +460,22 @@ resource "rancher2_cluster_v2" "foo" { cluster_agent_deployment_customization { scheduling_customization { priority_class { - # The preemption_policy must be set to 'Never', 'PreemptLowerPriority', or omitted. + # The preemption_policy must be set to 'Never', 'PreemptLowerPriority', or omitted. # If omitted, the default of 'PreemptLowerPriority' is used. preemption_policy = "PreemptLowerPriority" # The value cannot be less than negative 1 billion, or greater than 1 billion value = 1000000000 } pod_disruption_budget { - # min_available and max_unavailable must either be non-negative whole integers, + # min_available and max_unavailable must either be non-negative whole integers, # or whole number percentages greater than 0 and less than or equal to 100 (e.g. "50%"). # You cannot set both min_available and max_unavailable at the same time. min_available = "1" - + # max_unavailable = "1" } } - + } rke_config { @@ -483,7 +483,7 @@ resource "rancher2_cluster_v2" "foo" { machine_pools { # ... } - } + } } ``` @@ -554,7 +554,7 @@ resource "rancher2_cluster_v2" "foo_cluster_v2" { } ``` -### Creating Rancher V2 Cluster with Machine Selector Files +### Creating Rancher V2 Cluster with Machine Selector Files This argument is available in Rancher v2.7.2 and above. @@ -567,7 +567,7 @@ resource "rancher2_cluster_v2" "foo" { machine_selector_files { machine_label_selector { match_labels = { - # You can specify multiple labels + # You can specify multiple labels "rke.cattle.io/control-plane-role" = "true", "rke.cattle.io/etcd-role" = "true", } @@ -616,7 +616,7 @@ resource "rancher2_cluster_v2" "foo" { machine_selector_config { machine_label_selector { match_labels = { - # You can specify multiple labels + # You can specify multiple labels "rke.cattle.io/control-plane-role" = "true", "rke.cattle.io/etcd-role" = "true", } @@ -692,7 +692,7 @@ EOF } ``` -### Customize the ETCD snapshot feature on the cluster +### Customize the ETCD snapshot feature on the cluster ```hcl resource "rancher2_cloud_credential" "credentials" { @@ -726,7 +726,7 @@ resource "rancher2_cluster_v2" "foo" { ### Customize distribution-specified server configurations in a cluster -You can customize all server configurations on the cluster by utilizing the `machine_global_config` argument. +You can customize all server configurations on the cluster by utilizing the `machine_global_config` argument. For the full list of server configurations, please refer to [RKE2 server configuration](https://docs.rke2.io/reference/server_config) and [K3s server configuration](https://docs.k3s.io/cli/server). @@ -814,7 +814,7 @@ EOF ### Customize chart values in a cluster -You can specify the values for the system charts installed by RKE2 or K3s. +You can specify the values for the system charts installed by RKE2 or K3s. For more information about how RKE2 or K3s manage packaged components, please refer to [RKE2 documentation](https://docs.rke2.io/helm) or [K3s documentation](https://docs.k3s.io/installation/packaged-components). @@ -886,14 +886,14 @@ EOF The following arguments are supported: * `name` - (Required, forceNew, string) The name of the cluster. -* `fleet_namespace` - (Optional, ForceNew, string, default: fleet-default) Fleet namespace is the namespace where the cluster is to create in the local cluster. It is recommended to leave it as the default value. +* `fleet_namespace` - (Optional, ForceNew, string, default: fleet-default) Fleet namespace is the namespace where the cluster is to create in the local cluster. It is recommended to leave it as the default value. * `kubernetes_version` - (Required, string) The RKE2 or K3s version for the cluster. * `agent_env_vars` - (Optional, list) Agent env vars is a list of additional environment variables to be appended to the `cattle-cluster-agent` and `fleet-agent` deployment, and the plan for the [system upgrade controller](https://github.com/rancher/system-upgrade-controller) to upgrade nodes. * `cluster_agent_deployment_customization` - (Optional, list) Cluster agent deployment customization specifies the additional tolerations, new affinity rules, and new resource requirements on the `cattle-cluster-agent` deployment. This argument is available in Rancher v2.7.5 and above. * `fleet_agent_deployment_customization` - (Optional, list) Fleet agent deployment customization specifies the additional tolerations, new affinity rules, and new resource requirements on the `fleet-agent` deployment. The argument is available in Rancher v2.7.5 and above. * `rke_config` - (Optional/computed, list, max length: 1) The RKE configuration for the cluster. * `local_auth_endpoint` - (Optional, list, max length: 1) Local auth endpoint configures the Authorized Cluster Endpoint (ACE) which can be used to directly access the Kubernetes API server, without requiring communication through Rancher. For more information, please refer to [Rancher Documentation](https://ranchermanager.docs.rancher.com/how-to-guides/new-user-guides/kubernetes-clusters-in-rancher-setup/register-existing-clusters#authorized-cluster-endpoint-support-for-rke2-and-k3s-clusters). -* `cloud_credential_secret_name` - (Optional, string) Cloud credential secret name is the secret to be used when a cloud credential secret name is not specified at the machine pool level. +* `cloud_credential_secret_name` - (Optional, string) Cloud credential secret name is the secret to be used when a cloud credential secret name is not specified at the machine pool level. * `default_pod_security_admission_configuration_template_name` - (Optional, string) The name of the pre-defined pod security admission configuration template to be applied to the cluster. Rancher admins (or those with the right permissions) can create, manage, and edit those templates. For more information, please refer to [Rancher Documentation](https://ranchermanager.docs.rancher.com/how-to-guides/new-user-guides/authentication-permissions-and-global-configuration/psa-config-templates). The argument is available in Rancher v2.7.2 and above. * `default_cluster_role_for_project_members` - (Optional, string) Default cluster role for project members. * `enable_network_policy` - (Optional, bool, default: false) Enable k8s network policy on the cluster. @@ -929,7 +929,7 @@ These arguments are available in Rancher v2.7.5 and above. The `scheduling_custo * `append_tolerations` - (Optional, list) A list of tolerations to be appended to the default tolerations. * `override_affinity` - (Optional, string, JSON format) Override affinity overrides the global default affinity setting. -* `override_resource_requirements` - (Optional, list) Override resource requirements overrides the default value for requests and/or limits. +* `override_resource_requirements` - (Optional, list) Override resource requirements overrides the default value for requests and/or limits. + `scheduling_customization` - (Optional, list) Supported in Rancher 2.11.0 and above. Defines the configuration of a Priority Class and or Pod Disruption Budget. Currently only supported in the `cluster_agent_deployment_customization` field, and requires the `cattle_cluster_agent_scheduling_customization` feature to be enabled. ### `append_tolerations` @@ -946,10 +946,10 @@ These arguments are available in Rancher v2.7.5 and above. The `scheduling_custo #### Arguments -The resource units follow Kubernetes' standard, +The resource units follow Kubernetes' standard, see more information on [Resource Management for Pods and Containers](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#resource-units-in-kubernetes). -* `cpu_limit` - (Optional, string) The maximum CPU limit for agent. +* `cpu_limit` - (Optional, string) The maximum CPU limit for agent. * `cpu_request` - (Optional, string) The minimum CPU required for agent. * `memory_limit` - (Optional, string) The maximum memory limit for agent. * `memory_request` - (Optional, string) The minimum memory required for agent. @@ -966,13 +966,13 @@ see more information on [Resource Management for Pods and Containers](https://ku #### Arguments * `min_available` - (Optional, string) The minimum number of agent replicas that must be running at a given time. This can be a non-negative whole number or a whole number percentage (e.g. "1", "50%"). This field cannot be used at the same time as `max_unavailable`. -* `max_unavailable` - (Optional, string) The maximum number of agent replicas that can be unavailable at a given time. This can be a non-negative whole number or a whole number percentage (e.g. "1", "50%"). This field cannot be used at the same time as `min_available`. +* `max_unavailable` - (Optional, string) The maximum number of agent replicas that can be unavailable at a given time. This can be a non-negative whole number or a whole number percentage (e.g. "1", "50%"). This field cannot be used at the same time as `min_available`. ### `priority_class` #### Arguments -* `value` - (Optional, int) The priority value set for the Priority Class. Must be greater than or equal to negative 1 billion, and less than or equal to 1 billion. +* `value` - (Optional, int) The priority value set for the Priority Class. Must be greater than or equal to negative 1 billion, and less than or equal to 1 billion. * `preemption_policy` (Optional, string) The preemption policy set for the Priority Class. Must be set to either 'Never', 'PreemptLowerPriority', or omitted. ### `rke_config` @@ -987,7 +987,7 @@ see more information on [Resource Management for Pods and Containers](https://ku * `machine_pools` - (Optional/computed, list) Cluster V2 machine pools. * `machine_selector_config` - (Optional/computed, list) Machine selector config is the same as machine_global_config except that a label selector can be specified with the configuration. The configuration will only be applied to nodes that match the provided label selector. The configuration from machine_selector_config takes precedence over the one from machine_global_config. This argument is available in Rancher v2.7.2 and later. * `machine_selector_files` - (Optional/computed, list) Machine selector files provide a means to deliver files to nodes so that the files can be in place before initiating RKE2/K3s server or agent processes. Please refer to Rancher documentation for [RKE2 Cluster Configuration Reference](https://ranchermanager.docs.rancher.com/reference-guides/cluster-configuration/rancher-server-configuration/rke2-cluster-configuration#machineselectorfiles) and [K3s Cluster Configuration Reference](https://ranchermanager.docs.rancher.com/reference-guides/cluster-configuration/rancher-server-configuration/k3s-cluster-configuration#machineselectorfiles). This argument is available in Rancher v2.7.2 and later. -* `registries` - (Optional, list, max length: 1) Docker registries from which the cluster pulls images. +* `registries` - (Optional, list, max length: 1) Docker registries from which the cluster pulls images. * `etcd` - (Optional/computed, list, max length: 1) Etcd configures the behavior of the automatic etcd snapshot feature. * `rotate_certificates` (Optional, list, max length: 1) Cluster V2 certificate rotation. * `etcd_snapshot_create` (Optional, list, max length: 1) Cluster V2 etcd snapshot create. @@ -1000,6 +1000,7 @@ see more information on [Resource Management for Pods and Containers](https://ku * `enabled` - (Optional, bool, default: false) Enable the authorized cluster endpoint. * `fqdn` - (Optional, string) FQDN for the authorized cluster endpoint. If one is entered, it should point to the downstream cluster. * `ca_certs` - (Optional, string) CA certs for the authorized cluster endpoint. It is only needed if there is a load balancer in front of the downstream cluster that is using an untrusted certificate. If you have a valid certificate, then nothing needs to be added to the CA Certificates field. +* `use_internal_ca_certs` - (Optional, bool, default: false) Use the cluster's internally generated CA certificates for the authorized cluster endpoint. Conflicts with `ca_certs`. When enabled, the provider automatically retrieves the certificates from the cluster. #### `upgrade_strategy` @@ -1022,7 +1023,7 @@ see more information on [Resource Management for Pods and Containers](https://ku * `disable_eviction` - (Optional, bool, default: false) If `disable_eviction` is set to true, force drain to use delete rather than evict. * `grace_period` - (Optional/computed, int) Time in seconds given to each pod to terminate gracefully. If negative, the default value specified in the pod will be used. * `timeout` - (Optional/computed, int) Time to wait (in seconds) before giving up for one try. -* `skip_wait_for_delete_timeout_seconds` - (Optional/computed, int) Skip waiting for the pods that have a DeletionTimeStamp > N seconds to be deleted. Seconds must be greater than 0 to skip. Such pods will be force deleted. +* `skip_wait_for_delete_timeout_seconds` - (Optional/computed, int) Skip waiting for the pods that have a DeletionTimeStamp > N seconds to be deleted. Seconds must be greater than 0 to skip. Such pods will be force deleted. #### `machine_pools` @@ -1047,7 +1048,7 @@ see more information on [Resource Management for Pods and Containers](https://ku * `machine_labels` - (Optional, map) Labels for Machine pool nodes. * `machine_os` - (Optional) OS Type in machine pool. Default `linux`(string) * `labels` - (Optional, map) Labels for Machine Deployment Resource. -* `annotations` - (Optional, map) Annotations for Machine Deployment Resource. +* `annotations` - (Optional, map) Annotations for Machine Deployment Resource. ##### `machine_config` @@ -1116,9 +1117,9 @@ This argument is available in Rancher v2.7.2 and later. ##### Arguments -* `name` - (Required, string) The name of the secret. -* `default_permissions` - (Optional, string) The numeric representation of the default file permissions for all files defined under the items. -* `items` - (Optional, list) Items is a list of configurations for files, such as where to retrieve the content from the source, where to put the file on nodes, and etc. +* `name` - (Required, string) The name of the secret. +* `default_permissions` - (Optional, string) The numeric representation of the default file permissions for all files defined under the items. +* `items` - (Optional, list) Items is a list of configurations for files, such as where to retrieve the content from the source, where to put the file on nodes, and etc. #### `configmap` diff --git a/rancher2/resource_rancher2_cluster_v2.go b/rancher2/resource_rancher2_cluster_v2.go index e1eec5aab..c9c67b6e3 100644 --- a/rancher2/resource_rancher2_cluster_v2.go +++ b/rancher2/resource_rancher2_cluster_v2.go @@ -53,10 +53,26 @@ func resourceRancher2ClusterV2() *schema.Resource { if oldOk && newOk && len(newInterface) > 0 { 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) } } } @@ -110,6 +126,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) @@ -130,6 +155,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) } @@ -150,7 +187,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 { @@ -159,6 +199,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) @@ -420,3 +472,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 cluster.CACert, nil +} + +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 +} diff --git a/rancher2/schema_cluster_v2_rke_config_local_auth_endpoint.go b/rancher2/schema_cluster_v2_rke_config_local_auth_endpoint.go index 64477bdd2..8dc7b3411 100644 --- a/rancher2/schema_cluster_v2_rke_config_local_auth_endpoint.go +++ b/rancher2/schema_cluster_v2_rke_config_local_auth_endpoint.go @@ -9,8 +9,9 @@ import ( func clusterV2LocalAuthEndpointFields() map[string]*schema.Schema { s := map[string]*schema.Schema{ "ca_certs": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"use_internal_ca_certs"}, }, "enabled": { Type: schema.TypeBool, @@ -21,7 +22,13 @@ func clusterV2LocalAuthEndpointFields() map[string]*schema.Schema { Type: schema.TypeString, Optional: true, }, + "use_internal_ca_certs": { + Type: schema.TypeBool, + Optional: true, + Default: false, + ConflictsWith: []string{"ca_certs"}, + }, } return s -} +} \ No newline at end of file diff --git a/rancher2/structure_cluster_v2_local_auth_endpoint_test.go b/rancher2/structure_cluster_v2_local_auth_endpoint_test.go index f8a145e4b..574215b4c 100644 --- a/rancher2/structure_cluster_v2_local_auth_endpoint_test.go +++ b/rancher2/structure_cluster_v2_local_auth_endpoint_test.go @@ -8,8 +8,9 @@ import ( ) var ( - testClusterV2LocalAuthEndpointConf rkev1.LocalClusterAuthEndpoint - testClusterV2LocalAuthEndpointInterface []interface{} + testClusterV2LocalAuthEndpointConf rkev1.LocalClusterAuthEndpoint + testClusterV2LocalAuthEndpointInterface []interface{} + testClusterV2LocalAuthEndpointInterfaceUseInternal []interface{} ) func init() { @@ -26,6 +27,14 @@ func init() { "fqdn": "fqdn", }, } + testClusterV2LocalAuthEndpointInterfaceUseInternal = []interface{}{ + map[string]interface{}{ + "ca_certs": "ca_certs", + "enabled": true, + "fqdn": "fqdn", + "use_internal_ca_certs": true, + }, + } } func TestFlattenClusterV2LocalAuthEndpoint(t *testing.T) { @@ -53,7 +62,7 @@ func TestExpandClusterV2LocalAuthEndpoint(t *testing.T) { ExpectedOutput rkev1.LocalClusterAuthEndpoint }{ { - testClusterV2LocalAuthEndpointInterface, + testClusterV2LocalAuthEndpointInterfaceUseInternal, testClusterV2LocalAuthEndpointConf, }, } @@ -62,4 +71,4 @@ func TestExpandClusterV2LocalAuthEndpoint(t *testing.T) { output := expandClusterV2LocalAuthEndpoint(tc.Input) assert.Equal(t, tc.ExpectedOutput, output, "Unexpected output from expander.") } -} +} \ No newline at end of file From e9a4c69f48396ea1a11dcf384c4f2b160d61c191 Mon Sep 17 00:00:00 2001 From: Denis Banshchikov Date: Sat, 16 Aug 2025 23:38:42 +0300 Subject: [PATCH 2/5] chore(cluster_v2): run gofmt on local_auth_endpoint files --- rancher2/schema_cluster_v2_rke_config_local_auth_endpoint.go | 2 +- rancher2/structure_cluster_v2_local_auth_endpoint_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rancher2/schema_cluster_v2_rke_config_local_auth_endpoint.go b/rancher2/schema_cluster_v2_rke_config_local_auth_endpoint.go index 8dc7b3411..7b3f64b72 100644 --- a/rancher2/schema_cluster_v2_rke_config_local_auth_endpoint.go +++ b/rancher2/schema_cluster_v2_rke_config_local_auth_endpoint.go @@ -31,4 +31,4 @@ func clusterV2LocalAuthEndpointFields() map[string]*schema.Schema { } return s -} \ No newline at end of file +} diff --git a/rancher2/structure_cluster_v2_local_auth_endpoint_test.go b/rancher2/structure_cluster_v2_local_auth_endpoint_test.go index 574215b4c..4d024cd97 100644 --- a/rancher2/structure_cluster_v2_local_auth_endpoint_test.go +++ b/rancher2/structure_cluster_v2_local_auth_endpoint_test.go @@ -71,4 +71,4 @@ func TestExpandClusterV2LocalAuthEndpoint(t *testing.T) { output := expandClusterV2LocalAuthEndpoint(tc.Input) assert.Equal(t, tc.ExpectedOutput, output, "Unexpected output from expander.") } -} \ No newline at end of file +} From 5ab05c73c484bf04e0b8d721c7d0be1bc9d0ff64 Mon Sep 17 00:00:00 2001 From: Denis Banshchikov Date: Sat, 16 Aug 2025 23:38:42 +0300 Subject: [PATCH 3/5] fix(cluster_v2): correct use_internal_ca_certs schema and update tests --- rancher2/resource_rancher2_cluster_v2.go | 7 +++++++ ...hema_cluster_v2_rke_config_local_auth_endpoint.go | 12 +++++------- .../structure_cluster_v2_local_auth_endpoint_test.go | 9 +++++++++ rancher2/structure_cluster_v2_test.go | 2 +- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/rancher2/resource_rancher2_cluster_v2.go b/rancher2/resource_rancher2_cluster_v2.go index c9c67b6e3..59c742d3d 100644 --- a/rancher2/resource_rancher2_cluster_v2.go +++ b/rancher2/resource_rancher2_cluster_v2.go @@ -51,6 +51,13 @@ 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) oldUse := false diff --git a/rancher2/schema_cluster_v2_rke_config_local_auth_endpoint.go b/rancher2/schema_cluster_v2_rke_config_local_auth_endpoint.go index 7b3f64b72..622fe1f91 100644 --- a/rancher2/schema_cluster_v2_rke_config_local_auth_endpoint.go +++ b/rancher2/schema_cluster_v2_rke_config_local_auth_endpoint.go @@ -9,9 +9,8 @@ import ( func clusterV2LocalAuthEndpointFields() map[string]*schema.Schema { s := map[string]*schema.Schema{ "ca_certs": { - Type: schema.TypeString, - Optional: true, - ConflictsWith: []string{"use_internal_ca_certs"}, + Type: schema.TypeString, + Optional: true, }, "enabled": { Type: schema.TypeBool, @@ -23,10 +22,9 @@ func clusterV2LocalAuthEndpointFields() map[string]*schema.Schema { Optional: true, }, "use_internal_ca_certs": { - Type: schema.TypeBool, - Optional: true, - Default: false, - ConflictsWith: []string{"ca_certs"}, + Type: schema.TypeBool, + Optional: true, + Default: false, }, } diff --git a/rancher2/structure_cluster_v2_local_auth_endpoint_test.go b/rancher2/structure_cluster_v2_local_auth_endpoint_test.go index 4d024cd97..f40038b71 100644 --- a/rancher2/structure_cluster_v2_local_auth_endpoint_test.go +++ b/rancher2/structure_cluster_v2_local_auth_endpoint_test.go @@ -11,6 +11,7 @@ var ( testClusterV2LocalAuthEndpointConf rkev1.LocalClusterAuthEndpoint testClusterV2LocalAuthEndpointInterface []interface{} testClusterV2LocalAuthEndpointInterfaceUseInternal []interface{} + testClusterV2LocalAuthEndpointInterfaceWithFlag []interface{} ) func init() { @@ -35,6 +36,14 @@ func init() { "use_internal_ca_certs": true, }, } + testClusterV2LocalAuthEndpointInterfaceWithFlag = []interface{}{ + map[string]interface{}{ + "ca_certs": "ca_certs", + "enabled": true, + "fqdn": "fqdn", + "use_internal_ca_certs": false, + }, + } } func TestFlattenClusterV2LocalAuthEndpoint(t *testing.T) { diff --git a/rancher2/structure_cluster_v2_test.go b/rancher2/structure_cluster_v2_test.go index 5177cbd33..b7dd10be5 100644 --- a/rancher2/structure_cluster_v2_test.go +++ b/rancher2/structure_cluster_v2_test.go @@ -232,7 +232,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, From fff159971febef42c298cf4564854153ec47e203 Mon Sep 17 00:00:00 2001 From: Denis Banshchikov Date: Sat, 16 Aug 2025 23:38:42 +0300 Subject: [PATCH 4/5] chore(cluster_v2): decode internal CA certs for local auth endpoint --- rancher2/resource_rancher2_cluster_v2.go | 2 +- rancher2/util_certs.go | 13 +++++++++++++ rancher2/util_certs_test.go | 17 +++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 rancher2/util_certs.go create mode 100644 rancher2/util_certs_test.go diff --git a/rancher2/resource_rancher2_cluster_v2.go b/rancher2/resource_rancher2_cluster_v2.go index 59c742d3d..3b9e6df94 100644 --- a/rancher2/resource_rancher2_cluster_v2.go +++ b/rancher2/resource_rancher2_cluster_v2.go @@ -496,7 +496,7 @@ func getClusterCACert(c *Config, clusterV1ID string) (string, error) { if err != nil { return "", err } - return cluster.CACert, nil + return decodeCACertIfBase64(cluster.CACert), nil } func setClusterV2LocalAuthEndpointInternalFlag(d *schema.ResourceData, c *Config, cluster *ClusterV2) error { diff --git a/rancher2/util_certs.go b/rancher2/util_certs.go new file mode 100644 index 000000000..5bf2a8140 --- /dev/null +++ b/rancher2/util_certs.go @@ -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 +} diff --git a/rancher2/util_certs_test.go b/rancher2/util_certs_test.go new file mode 100644 index 000000000..98a1df06f --- /dev/null +++ b/rancher2/util_certs_test.go @@ -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) + } +} From 908cb37da91f57f965d9a9f155facb1efd5ae319 Mon Sep 17 00:00:00 2001 From: Denis Banshchikov Date: Sat, 16 Aug 2025 23:38:43 +0300 Subject: [PATCH 5/5] fix(cluster_v2): mark ca_certs field as computed --- rancher2/schema_cluster_v2_rke_config_local_auth_endpoint.go | 1 + 1 file changed, 1 insertion(+) diff --git a/rancher2/schema_cluster_v2_rke_config_local_auth_endpoint.go b/rancher2/schema_cluster_v2_rke_config_local_auth_endpoint.go index 622fe1f91..3ab274943 100644 --- a/rancher2/schema_cluster_v2_rke_config_local_auth_endpoint.go +++ b/rancher2/schema_cluster_v2_rke_config_local_auth_endpoint.go @@ -11,6 +11,7 @@ func clusterV2LocalAuthEndpointFields() map[string]*schema.Schema { "ca_certs": { Type: schema.TypeString, Optional: true, + Computed: true, }, "enabled": { Type: schema.TypeBool,