mirror of
https://github.com/ysoftdevs/gardener-extension-shoot-fleet-agent.git
synced 2026-01-15 16:23:41 +01:00
164 lines
5.4 KiB
Go
164 lines
5.4 KiB
Go
package token_requestor_handler
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
extensionscontroller "github.com/gardener/gardener/extensions/pkg/controller"
|
|
constsv1alpha1 "github.com/gardener/gardener/pkg/apis/resources/v1alpha1"
|
|
"github.com/gardener/gardener/pkg/extensions"
|
|
kutil "github.com/gardener/gardener/pkg/utils/kubernetes"
|
|
"github.com/gardener/gardener/pkg/utils/retry"
|
|
"github.com/go-logr/logr"
|
|
v1 "k8s.io/api/core/v1"
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
clientapiv1 "k8s.io/client-go/tools/clientcmd/api/v1"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/log"
|
|
"sigs.k8s.io/yaml"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
TokenRequestorSecretName = "shoot-access-extension-shoot-fleet-agent"
|
|
TokenRequestorSecretKey = constsv1alpha1.DataKeyKubeconfig
|
|
TargetServiceAccountName = "extension-shoot-fleet-agent"
|
|
TargetServiceAccountNamespace = "kube-system"
|
|
)
|
|
|
|
type TokenRequestorHandler struct {
|
|
ctx context.Context
|
|
client client.Client
|
|
cluster *extensions.Cluster
|
|
logger logr.Logger
|
|
}
|
|
|
|
func NewTokenRequestorHandler(ctx context.Context, client client.Client, cluster *extensions.Cluster) *TokenRequestorHandler {
|
|
return &TokenRequestorHandler{
|
|
ctx: ctx,
|
|
client: client,
|
|
cluster: cluster,
|
|
logger: log.Log.WithValues("logger", "token-requestor-handler", "cluster", cluster.ObjectMeta.Name),
|
|
}
|
|
}
|
|
|
|
func (t *TokenRequestorHandler) getGenericKubeconfigName() string {
|
|
return extensionscontroller.GenericTokenKubeconfigSecretNameFromCluster(t.cluster)
|
|
}
|
|
|
|
func (t *TokenRequestorHandler) getGenericKubeconfig() (*clientapiv1.Config, error) {
|
|
kubeconfigSecret := &v1.Secret{}
|
|
kubeconfigSecretName := t.getGenericKubeconfigName()
|
|
if err := t.client.Get(t.ctx, kutil.Key(t.cluster.ObjectMeta.Name, kubeconfigSecretName), kubeconfigSecret); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
kubeconfigData, ok := kubeconfigSecret.Data[TokenRequestorSecretKey]
|
|
if !ok {
|
|
return nil, fmt.Errorf("secret %s doesn't have data key %s", kubeconfigSecretName, TokenRequestorSecretKey)
|
|
}
|
|
|
|
kubeconfig := &clientapiv1.Config{}
|
|
if err := yaml.Unmarshal(kubeconfigData, kubeconfig); err != nil {
|
|
return nil, err
|
|
}
|
|
return kubeconfig, nil
|
|
}
|
|
|
|
func (t *TokenRequestorHandler) isTokenInserted() bool {
|
|
kubeconfigSecret := &v1.Secret{}
|
|
if err := t.client.Get(t.ctx, kutil.Key(t.cluster.ObjectMeta.Name, TokenRequestorSecretName), kubeconfigSecret); err != nil {
|
|
t.logger.Info(fmt.Sprintf("Couldn't fet the kubeconfig secret %s, %+v", TokenRequestorSecretName, err))
|
|
return false
|
|
}
|
|
|
|
kubeconfigData, ok := kubeconfigSecret.Data[TokenRequestorSecretKey]
|
|
if !ok {
|
|
t.logger.Info(fmt.Sprintf("Kubeconfig secret %s doesn't contain data item %s", TokenRequestorSecretName, TokenRequestorSecretKey))
|
|
return false
|
|
}
|
|
|
|
kubeconfig := &clientapiv1.Config{}
|
|
if err := yaml.Unmarshal(kubeconfigData, kubeconfig); err != nil {
|
|
t.logger.Info(fmt.Sprintf("Data item %s in kubeconfig secret %s couldn't be parsed: %+v", TokenRequestorSecretKey, TokenRequestorSecretName, err))
|
|
return false
|
|
}
|
|
|
|
if len(kubeconfig.AuthInfos) == 0 || kubeconfig.AuthInfos[0].AuthInfo.Token == "" {
|
|
t.logger.Info(fmt.Sprintf("Kubeconfig in secret %s doesn't contain token (yet?)", TokenRequestorSecretName))
|
|
return false
|
|
}
|
|
|
|
t.logger.Info(fmt.Sprintf("Kubeconfig in secret %s contains a proper token", TokenRequestorSecretName))
|
|
return true
|
|
}
|
|
|
|
func (t *TokenRequestorHandler) createKubeconfigSecretFromGeneric() error {
|
|
kubeconfig, err := t.getGenericKubeconfig()
|
|
s := false
|
|
for _, value := range t.cluster.Shoot.Status.AdvertisedAddresses {
|
|
// check for external URL server
|
|
if value.Name == "external" {
|
|
kubeconfig.Clusters[0].Cluster.Server = value.URL
|
|
s = true
|
|
break
|
|
}
|
|
}
|
|
if s != true {
|
|
t.logger.Info(fmt.Sprintf("Shoot status doesn't contain external URL address."))
|
|
return err
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
kubeconfigYaml, err := yaml.Marshal(kubeconfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
existingSecret := &v1.Secret{}
|
|
err = t.client.Get(t.ctx, kutil.Key(t.cluster.ObjectMeta.Name, TokenRequestorSecretName), existingSecret)
|
|
if err == nil || !apierrors.IsNotFound(err) {
|
|
return err
|
|
}
|
|
|
|
secretObject := &v1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: t.cluster.ObjectMeta.Name,
|
|
Name: TokenRequestorSecretName,
|
|
Labels: map[string]string{
|
|
constsv1alpha1.ResourceManagerPurpose: constsv1alpha1.LabelPurposeTokenRequest,
|
|
},
|
|
Annotations: map[string]string{
|
|
constsv1alpha1.ServiceAccountName: TargetServiceAccountName,
|
|
constsv1alpha1.ServiceAccountNamespace: TargetServiceAccountNamespace,
|
|
},
|
|
},
|
|
StringData: map[string]string{
|
|
TokenRequestorSecretKey: string(kubeconfigYaml),
|
|
},
|
|
}
|
|
return t.client.Create(t.ctx, secretObject)
|
|
}
|
|
|
|
// EnsureKubeconfig creates the secret with a token-requestor label and waits until gardener fills in
|
|
// the token into the kubeconfig
|
|
func (t *TokenRequestorHandler) EnsureKubeconfig() error {
|
|
t.logger.Info(fmt.Sprintf("Trying to create the token requestor secret"))
|
|
if err := t.createKubeconfigSecretFromGeneric(); err != nil {
|
|
t.logger.Error(err, "Generic kubeconfig could not be copied")
|
|
return err
|
|
}
|
|
|
|
t.logger.Info("Waiting until the token is propagated into the token requestor secret")
|
|
if err := retry.UntilTimeout(t.ctx, 5*time.Second, 60*time.Second, func(ctx context.Context) (bool, error) {
|
|
return t.isTokenInserted(), nil
|
|
}); err != nil {
|
|
t.logger.Error(err, "Kubeconfig could not be created")
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|