Files

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
}