Skip to content

Commit

Permalink
Updated SANs config for the HCP clusters
Browse files Browse the repository at this point in the history
Signed-off-by: Alexey Makhov <[email protected]>
  • Loading branch information
makhov committed Jun 6, 2024
1 parent 29b3bed commit 33e6b89
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 8 deletions.
14 changes: 14 additions & 0 deletions api/k0smotron.io/v1beta1/k0smotroncluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,20 @@ func (kmc *Cluster) GetConfigMapName() string {
}

func (kmc *Cluster) GetServiceName() string {
switch kmc.Spec.Service.Type {
case v1.ServiceTypeNodePort:
return kmc.GetNodePortServiceName()
case v1.ServiceTypeLoadBalancer:
return kmc.GetLoadBalancerServiceName()
case v1.ServiceTypeClusterIP:
// ClusterIP is the default
fallthrough
default:
return kmc.GetClusterIPServiceName()
}
}

func (kmc *Cluster) GetClusterIPServiceName() string {
return fmt.Sprintf("kmc-%s", kmc.Name)
}

Expand Down
2 changes: 1 addition & 1 deletion docs/contributing/dev-environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@ to a `kind.conf` file:
```

This command runs tests against the control plane of the Kubernetes cluster
deployed using Docker. It uses the Kubernetes configuration from `kind.conf`.
deployed using Docker. It uses the Kubernetes configuration from `kind.conf`.
51 changes: 46 additions & 5 deletions internal/controller/k0smotron.io/k0smotroncluster_configmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package k0smotronio
import (
"context"
"fmt"
"net"
"strings"

"github.com/imdario/mergo"
v1 "k8s.io/api/core/v1"
Expand All @@ -41,20 +43,24 @@ const kineDataSourceURLPlaceholder = "__K0SMOTRON_KINE_DATASOURCE_URL_PLACEHOLDE
// - some of the fields in the k0s config struct are not pointers, e.g. spec.api.address in string, so it will be
// marshalled as "address": "", which is not correct value for the k0s config
// - we can't use the k0s config default values, because some of them are calculated based on the cluster state (e.g. spec.api.address)
func (r *ClusterReconciler) generateConfig(kmc *km.Cluster) (v1.ConfigMap, map[string]interface{}, error) {
func (r *ClusterReconciler) generateConfig(kmc *km.Cluster, sans []string) (v1.ConfigMap, map[string]interface{}, error) {
k0smotronValues := map[string]interface{}{"spec": nil}
unstructuredConfig := k0smotronValues

if kmc.Spec.K0sConfig == nil {
k0smotronValues["apiVersion"] = "k0s.k0sproject.io/v1beta1"
k0smotronValues["kind"] = "ClusterConfig"
k0smotronValues["spec"] = getV1Beta1Spec(kmc)
k0smotronValues["spec"] = getV1Beta1Spec(kmc, sans)
} else {
unstructuredConfig = kmc.Spec.K0sConfig.UnstructuredContent()

switch kmc.Spec.K0sConfig.GetAPIVersion() {
case "k0s.k0sproject.io/v1beta1":
k0smotronValues["spec"] = getV1Beta1Spec(kmc)
existingSANs, found, err := unstructured.NestedStringSlice(unstructuredConfig, "spec", "api", "sans")
if err == nil && found {
sans = append(sans, existingSANs...)
}
k0smotronValues["spec"] = getV1Beta1Spec(kmc, sans)
default:
// TODO: should we just use the v1beta1 in case the api version is not provided?
return v1.ConfigMap{}, nil, fmt.Errorf("unsupported k0s config version: %s", kmc.Spec.K0sConfig.GetAPIVersion())
Expand Down Expand Up @@ -111,7 +117,12 @@ func (r *ClusterReconciler) reconcileK0sConfig(ctx context.Context, kmc *km.Clus
kmc.Spec.KineDataSourceURL = kineDataSourceURLPlaceholder
}

cm, unstructuredConfig, err := r.generateConfig(kmc)
sans, err := r.genSANs(kmc)
if err != nil {
return fmt.Errorf("failed to generate SANs: %w", err)
}

cm, unstructuredConfig, err := r.generateConfig(kmc, sans)
if err != nil {
return err
}
Expand Down Expand Up @@ -170,11 +181,41 @@ func (r *ClusterReconciler) detectExternalAddress(ctx context.Context) (string,
return internalAddress, nil
}

func getV1Beta1Spec(kmc *km.Cluster) map[string]interface{} {
func (r *ClusterReconciler) genSANs(kmc *km.Cluster) ([]string, error) {
var sans []string
if kmc.Spec.ExternalAddress != "" {
sans = append(sans, kmc.Spec.ExternalAddress)
}
svcName := kmc.GetServiceName()
svcNamespacedName := fmt.Sprintf("%s.%s", svcName, kmc.Namespace)

sans = append(sans, svcName)
sans = append(sans, svcNamespacedName)
sans = append(sans, fmt.Sprintf("%s.svc", svcNamespacedName))

ips, err := net.LookupHost(svcNamespacedName)
if err != nil {
return nil, fmt.Errorf("failed to resolve service IPs %s: %w", svcNamespacedName, err)
}
sans = append(sans, ips...)

cname, err := net.LookupCNAME(svcNamespacedName)
if err != nil {
return nil, fmt.Errorf("failed to resolve service CNAME %s: %w", svcNamespacedName, err)
}
// Trim the trailing dot from the CNAME
trimmedCNAME, _ := strings.CutSuffix(cname, ".")
sans = append(sans, trimmedCNAME)

return sans, nil
}

func getV1Beta1Spec(kmc *km.Cluster, sans []string) map[string]interface{} {
v1beta1Spec := map[string]interface{}{
"api": map[string]interface{}{
"externalAddress": kmc.Spec.ExternalAddress,
"port": kmc.Spec.Service.APIPort,
"sans": sans,
},
"konnectivity": map[string]interface{}{
"agentPort": kmc.Spec.Service.KonnectivityPort,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func TestGenerateCM(t *testing.T) {
},
}

cm, _, err := r.generateConfig(&kmc)
cm, _, err := r.generateConfig(&kmc, []string{})
require.NoError(t, err)

conf := cm.Data["K0SMOTRON_K0S_YAML"]
Expand All @@ -56,4 +56,32 @@ func TestGenerateCM(t *testing.T) {
assert.True(t, strings.Contains(conf, "calico"), "The provider must be calico")
assert.False(t, strings.Contains(conf, "kuberouter"), "The provider must not be kuberouter")
})

t.Run("sans merge", func(t *testing.T) {
kmc := km.Cluster{
Spec: km.ClusterSpec{
ExternalAddress: "my.external.address",
K0sConfig: &unstructured.Unstructured{Object: map[string]interface{}{
"apiVersion": "k0s.k0sproject.io/v1beta1",
"kind": "ClusterConfig",
"spec": map[string]interface{}{
"api": map[string]interface{}{
"sans": []interface{}{"my.san.address"},
},
},
}},
},
}

sans := []string{"1.2.3.4", "my.san.address2"}

cm, _, err := r.generateConfig(&kmc, sans)
require.NoError(t, err)

conf := cm.Data["K0SMOTRON_K0S_YAML"]

assert.True(t, strings.Contains(conf, "1.2.3.4"))
assert.True(t, strings.Contains(conf, "my.san.address"))
assert.True(t, strings.Contains(conf, "my.san.address2"))
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func (r *ClusterReconciler) generateService(kmc *km.Cluster) v1.Service {
default:
// Default to ClusterIP
kmc.Spec.Service.Type = v1.ServiceTypeClusterIP
name = kmc.GetServiceName()
name = kmc.GetClusterIPServiceName()

ports = append(ports,
v1.ServicePort{
Expand Down
5 changes: 5 additions & 0 deletions inttest/basic/basic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package basic

import (
"context"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -68,6 +69,10 @@ func (s *BasicSuite) TestK0sGetsUp() {
s.Require().Equal("100m", pod.Spec.Containers[0].Resources.Requests.Cpu().String())
s.Require().Equal("100Mi", pod.Spec.Containers[0].Resources.Requests.Memory().String())

configMap, err := kc.CoreV1().ConfigMaps("kmc-test").Get(s.Context(), "kmc-kmc-test-config", metav1.GetOptions{})
s.Require().NoError(err)
s.Require().True(strings.Contains(configMap.Data["K0SMOTRON_K0S_YAML"], "kmc-kmc-test-nodeport.kmc-test.svc.cluster.local"))

s.T().Log("updating k0smotron cluster")
s.updateK0smotronCluster(s.Context(), rc)

Expand Down

0 comments on commit 33e6b89

Please sign in to comment.