refactor: abstracting tenant control plane client generation

This commit is contained in:
Dario Tranchitella
2022-07-08 16:44:04 +02:00
parent b22e11a2a4
commit 8cac5a0c9b
13 changed files with 106 additions and 246 deletions

View File

@@ -4,6 +4,5 @@
package resources
const (
kubeconfigAdminKeyName = "admin.conf"
defaultIngressPort = 443
defaultIngressPort = 443
)

View File

@@ -20,6 +20,10 @@ import (
"github.com/clastix/kamaji/internal/utilities"
)
const (
agentNamespace = "kube-system"
)
type Agent struct {
resource *appsv1.DaemonSet
Client client.Client
@@ -53,11 +57,11 @@ func (r *Agent) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha1.T
r.resource = &appsv1.DaemonSet{
ObjectMeta: metav1.ObjectMeta{
Name: AgentName,
Namespace: kubeSystemNamespace,
Namespace: agentNamespace,
},
}
client, err := NewClient(ctx, r, tenantControlPlane)
client, err := utilities.GetTenantClient(ctx, r.Client, tenantControlPlane)
if err != nil {
return err
}
@@ -67,10 +71,6 @@ func (r *Agent) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha1.T
return nil
}
func (r *Agent) GetClient() client.Client {
return r.Client
}
func (r *Agent) CreateOrUpdate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
return controllerutil.CreateOrUpdate(ctx, r.tenantClient, r.resource, r.mutate(ctx, tenantControlPlane))
}

View File

@@ -72,10 +72,6 @@ func (r *CertificateResource) getPrefixedName(tenantControlPlane *kamajiv1alpha1
return utilities.AddTenantPrefix(r.Name, tenantControlPlane)
}
func (r *CertificateResource) GetClient() client.Client {
return r.Client
}
func (r *CertificateResource) CreateOrUpdate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
return controllerutil.CreateOrUpdate(ctx, r.Client, r.resource, r.mutate(ctx, tenantControlPlane))
}

View File

@@ -51,7 +51,7 @@ func (r *ClusterRoleBindingResource) Define(ctx context.Context, tenantControlPl
},
}
client, err := NewClient(ctx, r, tenantControlPlane)
client, err := utilities.GetTenantClient(ctx, r.Client, tenantControlPlane)
if err != nil {
return err
}
@@ -61,10 +61,6 @@ func (r *ClusterRoleBindingResource) Define(ctx context.Context, tenantControlPl
return nil
}
func (r *ClusterRoleBindingResource) GetClient() client.Client {
return r.Client
}
func (r *ClusterRoleBindingResource) CreateOrUpdate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
return controllerutil.CreateOrUpdate(ctx, r.tenantClient, r.resource, r.mutate(ctx, tenantControlPlane))
}

View File

@@ -62,10 +62,6 @@ func (r *KubeconfigResource) getPrefixedName(tenantControlPlane *kamajiv1alpha1.
return utilities.AddTenantPrefix(r.Name, tenantControlPlane)
}
func (r *KubeconfigResource) GetClient() client.Client {
return r.Client
}
func (r *KubeconfigResource) CreateOrUpdate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
return controllerutil.CreateOrUpdate(ctx, r.Client, r.resource, r.mutate(ctx, tenantControlPlane))
}

View File

@@ -49,11 +49,11 @@ func (r *ServiceAccountResource) Define(ctx context.Context, tenantControlPlane
r.resource = &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: "konnectivity-agent",
Namespace: kubeSystemNamespace,
Namespace: agentNamespace,
},
}
client, err := NewClient(ctx, r, tenantControlPlane)
client, err := utilities.GetTenantClient(ctx, r.Client, tenantControlPlane)
if err != nil {
return err
}
@@ -63,10 +63,6 @@ func (r *ServiceAccountResource) Define(ctx context.Context, tenantControlPlane
return nil
}
func (r *ServiceAccountResource) GetClient() client.Client {
return r.Client
}
func (r *ServiceAccountResource) CreateOrUpdate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
return controllerutil.CreateOrUpdate(ctx, r.tenantClient, r.resource, r.mutate(ctx, tenantControlPlane))
}

View File

@@ -1,90 +0,0 @@
// Copyright 2022 Clastix Labs
// SPDX-License-Identifier: Apache-2.0
package konnectivity
import (
"context"
"fmt"
"time"
corev1 "k8s.io/api/core/v1"
k8stypes "k8s.io/apimachinery/pkg/types"
restclient "k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
kubeconfigutil "github.com/clastix/kamaji/internal/kubeconfig"
)
// TODO: refactor and merge with /internal/resources/kubeadm_utils.go
// Logic is pretty close
// https://github.com/clastix/kamaji/issues/63
const (
kubeconfigAdminKeyName = "admin.conf"
timeout = 10 // seconds
kubeSystemNamespace = "kube-system"
)
type ExternalKubernetesResource interface {
GetClient() client.Client
}
func NewClient(ctx context.Context, r ExternalKubernetesResource, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (client.Client, error) {
options := client.Options{}
config, err := getRESTClientConfig(ctx, r, tenantControlPlane)
if err != nil {
return nil, err
}
return client.New(config, options)
}
func getKubeconfigSecret(ctx context.Context, r ExternalKubernetesResource, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (*corev1.Secret, error) {
kubeconfigSecretName := tenantControlPlane.Status.KubeConfig.Admin.SecretName
namespacedName := k8stypes.NamespacedName{Namespace: tenantControlPlane.GetNamespace(), Name: kubeconfigSecretName}
secret := &corev1.Secret{}
if err := r.GetClient().Get(ctx, namespacedName, secret); err != nil {
return nil, err
}
return secret, nil
}
func getKubeconfig(ctx context.Context, r ExternalKubernetesResource, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (*kubeconfigutil.Kubeconfig, error) {
secretKubeconfig, err := getKubeconfigSecret(ctx, r, tenantControlPlane)
if err != nil {
return nil, err
}
bytes, ok := secretKubeconfig.Data[kubeconfigAdminKeyName]
if !ok {
return nil, fmt.Errorf("%s is not into kubeconfig secret", kubeconfigAdminKeyName)
}
return kubeconfigutil.GetKubeconfigFromBytes(bytes)
}
func getRESTClientConfig(ctx context.Context, r ExternalKubernetesResource, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (*restclient.Config, error) {
kubeconfig, err := getKubeconfig(ctx, r, tenantControlPlane)
if err != nil {
return nil, err
}
config := &restclient.Config{
Host: fmt.Sprintf("https://%s:%d", getTenantControllerInternalFQDN(*tenantControlPlane), tenantControlPlane.Spec.NetworkProfile.Port),
TLSClientConfig: restclient.TLSClientConfig{
CAData: kubeconfig.Clusters[0].Cluster.CertificateAuthorityData,
CertData: kubeconfig.AuthInfos[0].AuthInfo.ClientCertificateData,
KeyData: kubeconfig.AuthInfos[0].AuthInfo.ClientKeyData,
},
Timeout: time.Second * timeout,
}
return config, nil
}
func getTenantControllerInternalFQDN(tenantControlPlane kamajiv1alpha1.TenantControlPlane) string {
return fmt.Sprintf("%s.%s.svc.cluster.local", tenantControlPlane.GetName(), tenantControlPlane.GetNamespace())
}

View File

@@ -17,6 +17,7 @@ import (
kamajiapi "github.com/clastix/kamaji/api"
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
"github.com/clastix/kamaji/internal/kubeadm"
"github.com/clastix/kamaji/internal/utilities"
)
type KubeadmAddon int
@@ -70,7 +71,7 @@ func (r *KubeadmAddonResource) ShouldCleanup(tenantControlPlane *kamajiv1alpha1.
}
func (r *KubeadmAddonResource) CleanUp(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (bool, error) {
client, err := GetRESTClient(ctx, r, tenantControlPlane)
client, err := utilities.GetTenantRESTClient(ctx, r.Client, tenantControlPlane)
if err != nil {
return false, err
}

View File

@@ -21,8 +21,6 @@ import (
type kubeadmPhase int
const kubeadmPhaseTimeout = 10 // seconds
const (
PhaseUploadConfigKubeadm kubeadmPhase = iota
PhaseUploadConfigKubelet

View File

@@ -6,21 +6,16 @@ package resources
import (
"context"
"fmt"
"time"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
k8stypes "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/version"
clientset "k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
kubeconfigutil "github.com/clastix/kamaji/internal/kubeconfig"
kamajiupgrade "github.com/clastix/kamaji/internal/upgrade"
"github.com/clastix/kamaji/internal/utilities"
)
type KubernetesUpgrade struct {
@@ -70,7 +65,7 @@ func (k *KubernetesUpgrade) CreateOrUpdate(ctx context.Context, plane *kamajiv1a
return controllerutil.OperationResultNone, nil
}
// Checking if the upgrade is allowed, or not
restClient, err := k.getRESTClient(ctx, plane)
restClient, err := utilities.GetTenantRESTClient(ctx, k.Client, plane)
if err != nil {
return controllerutil.OperationResultNone, errors.Wrap(err, "cannot create REST client required for Kubernetes upgrade plan")
}
@@ -110,59 +105,6 @@ func (k *KubernetesUpgrade) UpdateTenantControlPlaneStatus(ctx context.Context,
return nil
}
func (k *KubernetesUpgrade) getKubeconfigSecret(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (*corev1.Secret, error) {
kubeconfigSecretName := tenantControlPlane.Status.KubeConfig.Admin.SecretName
namespacedName := k8stypes.NamespacedName{Namespace: tenantControlPlane.GetNamespace(), Name: kubeconfigSecretName}
secret := &corev1.Secret{}
if err := k.Client.Get(ctx, namespacedName, secret); err != nil {
return nil, err
}
return secret, nil
}
func (k *KubernetesUpgrade) getKubeconfig(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (*kubeconfigutil.Kubeconfig, error) {
secretKubeconfig, err := k.getKubeconfigSecret(ctx, tenantControlPlane)
if err != nil {
return nil, err
}
bytes, ok := secretKubeconfig.Data[kubeconfigAdminKeyName]
if !ok {
return nil, fmt.Errorf("%s is not into kubeconfig secret", kubeconfigAdminKeyName)
}
return kubeconfigutil.GetKubeconfigFromBytes(bytes)
}
func (k *KubernetesUpgrade) getRESTClient(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (*clientset.Clientset, error) {
config, err := k.getRESTClientConfig(ctx, tenantControlPlane)
if err != nil {
return nil, err
}
return clientset.NewForConfig(config)
}
func (k *KubernetesUpgrade) getRESTClientConfig(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (*restclient.Config, error) {
kubeconfig, err := k.getKubeconfig(ctx, tenantControlPlane)
if err != nil {
return nil, err
}
config := &restclient.Config{
Host: fmt.Sprintf("https://%s:%d", getTenantControllerInternalFQDN(*tenantControlPlane), tenantControlPlane.Spec.NetworkProfile.Port),
TLSClientConfig: restclient.TLSClientConfig{
CAData: kubeconfig.Clusters[0].Cluster.CertificateAuthorityData,
CertData: kubeconfig.AuthInfos[0].AuthInfo.ClientCertificateData,
KeyData: kubeconfig.AuthInfos[0].AuthInfo.ClientKeyData,
},
Timeout: time.Second * kubeadmPhaseTimeout,
}
return config, nil
}
func (k *KubernetesUpgrade) isUpgradable() error {
newK8sVersion, err := version.ParseSemantic(k.upgrade.After.KubeVersion)
if err != nil {

View File

@@ -5,18 +5,12 @@ package resources
import (
"context"
"fmt"
"time"
corev1 "k8s.io/api/core/v1"
k8stypes "k8s.io/apimachinery/pkg/types"
clientset "k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
"github.com/clastix/kamaji/internal/kubeadm"
kubeconfigutil "github.com/clastix/kamaji/internal/kubeconfig"
"github.com/clastix/kamaji/internal/utilities"
)
func KubeadmPhaseCreate(ctx context.Context, r KubeadmPhaseResource, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
@@ -25,7 +19,7 @@ func KubeadmPhaseCreate(ctx context.Context, r KubeadmPhaseResource, tenantContr
return controllerutil.OperationResultNone, err
}
kubeconfig, err := getKubeconfig(ctx, r, tenantControlPlane)
kubeconfig, err := utilities.GetKubeconfig(ctx, r.GetClient(), tenantControlPlane)
if err != nil {
return controllerutil.OperationResultNone, err
}
@@ -53,7 +47,7 @@ func KubeadmPhaseCreate(ctx context.Context, r KubeadmPhaseResource, tenantContr
return controllerutil.OperationResultNone, nil
}
client, err := GetRESTClient(ctx, r, tenantControlPlane)
client, err := utilities.GetTenantRESTClient(ctx, r.GetClient(), tenantControlPlane)
if err != nil {
return controllerutil.OperationResultNone, err
}
@@ -74,56 +68,3 @@ func KubeadmPhaseCreate(ctx context.Context, r KubeadmPhaseResource, tenantContr
return controllerutil.OperationResultUpdated, nil
}
func getKubeconfigSecret(ctx context.Context, r KubeadmPhaseResource, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (*corev1.Secret, error) {
kubeconfigSecretName := tenantControlPlane.Status.KubeConfig.Admin.SecretName
namespacedName := k8stypes.NamespacedName{Namespace: tenantControlPlane.GetNamespace(), Name: kubeconfigSecretName}
secret := &corev1.Secret{}
if err := r.GetClient().Get(ctx, namespacedName, secret); err != nil {
return nil, err
}
return secret, nil
}
func getKubeconfig(ctx context.Context, r KubeadmPhaseResource, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (*kubeconfigutil.Kubeconfig, error) {
secretKubeconfig, err := getKubeconfigSecret(ctx, r, tenantControlPlane)
if err != nil {
return nil, err
}
bytes, ok := secretKubeconfig.Data[kubeconfigAdminKeyName]
if !ok {
return nil, fmt.Errorf("%s is not into kubeconfig secret", kubeconfigAdminKeyName)
}
return kubeconfigutil.GetKubeconfigFromBytes(bytes)
}
func GetRESTClient(ctx context.Context, r KubeadmPhaseResource, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (*clientset.Clientset, error) {
config, err := getRESTClientConfig(ctx, r, tenantControlPlane)
if err != nil {
return nil, err
}
return clientset.NewForConfig(config)
}
func getRESTClientConfig(ctx context.Context, r KubeadmPhaseResource, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (*restclient.Config, error) {
kubeconfig, err := getKubeconfig(ctx, r, tenantControlPlane)
if err != nil {
return nil, err
}
config := &restclient.Config{
Host: fmt.Sprintf("https://%s:%d", getTenantControllerInternalFQDN(*tenantControlPlane), tenantControlPlane.Spec.NetworkProfile.Port),
TLSClientConfig: restclient.TLSClientConfig{
CAData: kubeconfig.Clusters[0].Cluster.CertificateAuthorityData,
CertData: kubeconfig.AuthInfos[0].AuthInfo.ClientCertificateData,
KeyData: kubeconfig.AuthInfos[0].AuthInfo.ClientKeyData,
},
Timeout: time.Second * kubeadmPhaseTimeout,
}
return config, nil
}

View File

@@ -68,10 +68,6 @@ func getTenantControllerExternalFQDN(tenantControlPlane kamajiv1alpha1.TenantCon
return fmt.Sprintf("%s.%s.%s", tenantControlPlane.GetName(), tenantControlPlane.GetNamespace(), tenantControlPlane.Spec.NetworkProfile.Domain)
}
func getTenantControllerInternalFQDN(tenantControlPlane kamajiv1alpha1.TenantControlPlane) string {
return fmt.Sprintf("%s.%s.svc.cluster.local", tenantControlPlane.GetName(), tenantControlPlane.GetNamespace())
}
func getLatestConfigRV(tenantControlPlane kamajiv1alpha1.TenantControlPlane) string {
return tenantControlPlane.Status.KubeadmConfig.ResourceVersion
}

View File

@@ -0,0 +1,89 @@
// Copyright 2022 Clastix Labs
// SPDX-License-Identifier: Apache-2.0
package utilities
import (
"context"
"fmt"
"time"
corev1 "k8s.io/api/core/v1"
k8stypes "k8s.io/apimachinery/pkg/types"
clientset "k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
kubeconfigutil "github.com/clastix/kamaji/internal/kubeconfig"
)
const (
kubeadmConfigSecretKeyName = "admin.conf"
)
func GetTenantClient(ctx context.Context, c client.Client, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (client.Client, error) {
options := client.Options{}
config, err := getRESTClientConfig(ctx, c, tenantControlPlane)
if err != nil {
return nil, err
}
return client.New(config, options)
}
func GetTenantRESTClient(ctx context.Context, client client.Client, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (*clientset.Clientset, error) {
config, err := getRESTClientConfig(ctx, client, tenantControlPlane)
if err != nil {
return nil, err
}
return clientset.NewForConfig(config)
}
func GetKubeconfigSecret(ctx context.Context, client client.Client, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (*corev1.Secret, error) {
secret := &corev1.Secret{}
if err := client.Get(ctx, k8stypes.NamespacedName{Namespace: tenantControlPlane.GetNamespace(), Name: tenantControlPlane.Status.KubeConfig.Admin.SecretName}, secret); err != nil {
return nil, err
}
return secret, nil
}
func getRESTClientConfig(ctx context.Context, client client.Client, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (*restclient.Config, error) {
kubeconfig, err := GetKubeconfig(ctx, client, tenantControlPlane)
if err != nil {
return nil, err
}
config := &restclient.Config{
Host: fmt.Sprintf("https://%s:%d", getTenantControllerInternalFQDN(*tenantControlPlane), tenantControlPlane.Spec.NetworkProfile.Port),
TLSClientConfig: restclient.TLSClientConfig{
CAData: kubeconfig.Clusters[0].Cluster.CertificateAuthorityData,
CertData: kubeconfig.AuthInfos[0].AuthInfo.ClientCertificateData,
KeyData: kubeconfig.AuthInfos[0].AuthInfo.ClientKeyData,
},
Timeout: 10 * time.Second,
}
return config, nil
}
func GetKubeconfig(ctx context.Context, client client.Client, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (*kubeconfigutil.Kubeconfig, error) {
secretKubeconfig, err := GetKubeconfigSecret(ctx, client, tenantControlPlane)
if err != nil {
return nil, err
}
bytes, ok := secretKubeconfig.Data[kubeadmConfigSecretKeyName]
if !ok {
return nil, fmt.Errorf("%s is not into kubeconfig secret", kubeadmConfigSecretKeyName)
}
return kubeconfigutil.GetKubeconfigFromBytes(bytes)
}
func getTenantControllerInternalFQDN(tenantControlPlane kamajiv1alpha1.TenantControlPlane) string {
return fmt.Sprintf("%s.%s.svc.cluster.local", tenantControlPlane.GetName(), tenantControlPlane.GetNamespace())
}