diff --git a/api/v1alpha1/tenantcontrolplane_funcs.go b/api/v1alpha1/tenantcontrolplane_funcs.go index 3cc6e2e6..31cc951b 100644 --- a/api/v1alpha1/tenantcontrolplane_funcs.go +++ b/api/v1alpha1/tenantcontrolplane_funcs.go @@ -5,6 +5,9 @@ package v1alpha1 import ( "context" + "fmt" + "net" + "strconv" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" @@ -14,7 +17,30 @@ import ( kamajierrors "github.com/clastix/kamaji/internal/errors" ) -func (in *TenantControlPlane) GetControlPlaneAddress(ctx context.Context, client client.Client) (string, error) { +// AssignedControlPlaneAddress returns the announced address and port of a Tenant Control Plane. +// In case of non-well formed values, or missing announcement, an error is returned. +func (in *TenantControlPlane) AssignedControlPlaneAddress() (string, int32, error) { + if len(in.Status.ControlPlaneEndpoint) == 0 { + return "", 0, fmt.Errorf("the Tenant Control Plane is not yet exposed") + } + + address, portString, err := net.SplitHostPort(in.Status.ControlPlaneEndpoint) + if err != nil { + return "", 0, errors.Wrap(err, "cannot split host port from Tenant Control Plane endpoint") + } + + port, err := strconv.Atoi(portString) + if err != nil { + return "", 0, errors.Wrap(err, "cannot convert Tenant Control Plane port from endpoint") + } + + return address, int32(port), nil +} + +// DeclaredControlPlaneAddress returns the desired Tenant Control Plane address. +// In case of dynamic allocation, e.g. using a Load Balancer, it queries the API Server looking for the allocated IP. +// When an IP has not been yet assigned, or it is expected, an error is returned. +func (in *TenantControlPlane) DeclaredControlPlaneAddress(ctx context.Context, client client.Client) (string, error) { var loadBalancerStatus corev1.LoadBalancerStatus svc := &corev1.Service{} diff --git a/controllers/resources.go b/controllers/resources.go index 0ee71e22..57fa45a6 100644 --- a/controllers/resources.go +++ b/controllers/resources.go @@ -115,7 +115,6 @@ func getKubeadmConfigResources(c client.Client, tcpReconcilerConfig TenantContro return []resources.Resource{ &resources.KubeadmConfigResource{ Name: "kubeadmconfig", - Port: tenantControlPlane.Spec.NetworkProfile.Port, KubernetesVersion: tenantControlPlane.Spec.Kubernetes.Version, PodCIDR: tenantControlPlane.Spec.NetworkProfile.PodCIDR, ServiceCIDR: tenantControlPlane.Spec.NetworkProfile.ServiceCIDR, diff --git a/e2e/utils_test.go b/e2e/utils_test.go index 7cc6b806..332682b9 100644 --- a/e2e/utils_test.go +++ b/e2e/utils_test.go @@ -7,9 +7,10 @@ import ( "bytes" "context" "fmt" + "io/ioutil" + . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "io/ioutil" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" diff --git a/internal/resources/k8s_deployment_resource.go b/internal/resources/k8s_deployment_resource.go index f0c0444b..6c642a82 100644 --- a/internal/resources/k8s_deployment_resource.go +++ b/internal/resources/k8s_deployment_resource.go @@ -58,7 +58,7 @@ func (r *KubernetesDeploymentResource) Define(_ context.Context, tenantControlPl func (r *KubernetesDeploymentResource) mutate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) controllerutil.MutateFn { return func() error { - address, err := tenantControlPlane.GetControlPlaneAddress(ctx, r.Client) + address, _, err := tenantControlPlane.AssignedControlPlaneAddress() if err != nil { return errors.Wrap(err, "cannot create TenantControlPlane Deployment") } diff --git a/internal/resources/k8s_service_resource.go b/internal/resources/k8s_service_resource.go index f50ebc55..b5983cd9 100644 --- a/internal/resources/k8s_service_resource.go +++ b/internal/resources/k8s_service_resource.go @@ -68,7 +68,7 @@ func (r *KubernetesServiceResource) CreateOrUpdate(ctx context.Context, tenantCo func (r *KubernetesServiceResource) mutate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) controllerutil.MutateFn { // We don't need to check error here: in case of dynamic external IP, the Service must be created in advance. // After that, the specific cloud controller-manager will provide an IP that will be then used. - address, _ := tenantControlPlane.GetControlPlaneAddress(ctx, r.Client) + address, _ := tenantControlPlane.DeclaredControlPlaneAddress(ctx, r.Client) return func() error { var servicePort corev1.ServicePort diff --git a/internal/resources/kubeadm_config.go b/internal/resources/kubeadm_config.go index 67e4ca22..1aefed1d 100644 --- a/internal/resources/kubeadm_config.go +++ b/internal/resources/kubeadm_config.go @@ -32,7 +32,7 @@ type KubeadmConfigResource struct { } func (r *KubeadmConfigResource) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool { - address, err := getAddress(ctx, r.Client, *tenantControlPlane) + address, _, err := tenantControlPlane.AssignedControlPlaneAddress() if err != nil { return true } @@ -66,12 +66,7 @@ func (r *KubeadmConfigResource) getPrefixedName(tenantControlPlane *kamajiv1alph } func (r *KubeadmConfigResource) CreateOrUpdate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) { - address, err := getAddress(ctx, r.Client, *tenantControlPlane) - if err != nil { - return controllerutil.OperationResultNone, err - } - - return utilities.CreateOrUpdateWithConflict(ctx, r.Client, r.resource, r.mutate(tenantControlPlane, address)) + return utilities.CreateOrUpdateWithConflict(ctx, r.Client, r.resource, r.mutate(tenantControlPlane)) } func (r *KubeadmConfigResource) GetName() string { @@ -79,7 +74,10 @@ func (r *KubeadmConfigResource) GetName() string { } func (r *KubeadmConfigResource) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error { - address, _ := getAddress(ctx, r.Client, *tenantControlPlane) + address, _, err := tenantControlPlane.AssignedControlPlaneAddress() + if err != nil { + return err + } tenantControlPlane.Status.KubeadmConfig.LastUpdate = metav1.Now() tenantControlPlane.Status.KubeadmConfig.Checksum = r.resource.GetAnnotations()["checksum"] @@ -97,11 +95,17 @@ func (r *KubeadmConfigResource) getControlPlaneEndpoint(tenantControlPlane *kama return fmt.Sprintf("%s:%d", address, tenantControlPlane.Spec.NetworkProfile.Port) } -func (r *KubeadmConfigResource) mutate(tenantControlPlane *kamajiv1alpha1.TenantControlPlane, address string) controllerutil.MutateFn { +func (r *KubeadmConfigResource) mutate(tenantControlPlane *kamajiv1alpha1.TenantControlPlane) controllerutil.MutateFn { return func() error { + address, _, err := tenantControlPlane.AssignedControlPlaneAddress() + if err != nil { + return err + } + r.resource.SetLabels(utilities.KamajiLabels()) params := kubeadm.Parameters{ + TenantControlPlaneAddress: address, TenantControlPlaneName: tenantControlPlane.GetName(), TenantControlPlaneNamespace: tenantControlPlane.GetNamespace(), TenantControlPlaneEndpoint: r.getControlPlaneEndpoint(tenantControlPlane, address), diff --git a/internal/resources/kubeadm_utils.go b/internal/resources/kubeadm_utils.go index 3ce8999f..34ab7712 100644 --- a/internal/resources/kubeadm_utils.go +++ b/internal/resources/kubeadm_utils.go @@ -24,13 +24,18 @@ func KubeadmPhaseCreate(ctx context.Context, r KubeadmPhaseResource, tenantContr return controllerutil.OperationResultNone, err } + address, _, err := tenantControlPlane.AssignedControlPlaneAddress() + if err != nil { + return controllerutil.OperationResultNone, err + } + config.Kubeconfig = *kubeconfig config.Parameters = kubeadm.Parameters{ TenantControlPlaneName: tenantControlPlane.GetName(), TenantDNSServiceIPs: tenantControlPlane.Spec.NetworkProfile.DNSServiceIPs, TenantControlPlaneVersion: tenantControlPlane.Spec.Kubernetes.Version, TenantControlPlanePodCIDR: tenantControlPlane.Spec.NetworkProfile.PodCIDR, - TenantControlPlaneAddress: tenantControlPlane.Spec.NetworkProfile.Address, + TenantControlPlaneAddress: address, TenantControlPlaneCertSANs: tenantControlPlane.Spec.NetworkProfile.CertSANs, TenantControlPlanePort: tenantControlPlane.Spec.NetworkProfile.Port, TenantControlPlaneCGroupDriver: tenantControlPlane.Spec.Kubernetes.Kubelet.CGroupFS.String(),