Files
gardener-extension-shoot-fl…/pkg/controller/actuator.go
2021-01-28 17:37:47 +01:00

204 lines
7.4 KiB
Go

// Copyright (c) 2019 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package controller
import (
"context"
b64 "encoding/base64"
"fmt"
"github.com/gardener/gardener/pkg/extensions"
"github.com/go-logr/logr"
fleetv1alpha1 "github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
"github.com/gardener/gardener/extensions/pkg/controller"
"github.com/gardener/gardener/extensions/pkg/controller/extension"
"github.com/javamachr/gardener-extension-shoot-fleet-agent/pkg/controller/config"
extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1"
kutil "github.com/gardener/gardener/pkg/utils/kubernetes"
)
// ActuatorName is the name of the Fleet agent actuator.
const ActuatorName = "shoot-fleet-agent-actuator"
const KubeconfigSecretName = "kubecfg"
const KubeconfigKey = "kubeconfig"
// NewActuator returns an actuator responsible for Extension resources.
func NewActuator(config config.Config) extension.Actuator {
fleetKubeConfig, _ := b64.StdEncoding.DecodeString(config.FleetAgentConfig.ClientConnection.Kubeconfig)
var kubeconfigPath string
var err error
if kubeconfigPath, err = writeKubeconfigToTempFile(fleetKubeConfig); err != nil {
panic(err)
}
fleetClientConfig, _ := clientcmd.BuildConfigFromFlags("", kubeconfigPath)
fleetManager, err := NewManagerForConfig(fleetClientConfig, "clusters")//TODO get from config
if err != nil {
panic(err)
}
return &actuator{
logger: log.Log.WithName(ActuatorName),
serviceConfig: config,
fleetManager: fleetManager,
}
}
type actuator struct {
client client.Client
config *rest.Config
decoder runtime.Decoder
fleetManager *FleetManager
serviceConfig config.Config
logger logr.Logger
}
// Reconcile the Extension resource.
func (a *actuator) Reconcile(ctx context.Context, ex *extensionsv1alpha1.Extension) error {
namespace := ex.GetNamespace()
a.logger.Info("Component is being reconciled", "component", "fleet-agent-management", "namespace", namespace)
cluster, err := controller.GetCluster(ctx, a.client, namespace)
if err != nil {
return err
}
cfg := &config.Config{}
if ex.Spec.ProviderConfig != nil {//here we parse providerconfig
if _, _, err := a.decoder.Decode(ex.Spec.ProviderConfig.Raw, nil, cfg); err != nil {
return fmt.Errorf("failed to decode provider config: %+v", err)
}
}
a.registerClusterInFleetManager(ctx, namespace, cluster)
return a.updateStatus(ctx, ex)
}
// Delete the Extension resource.
func (a *actuator) Delete(ctx context.Context, ex *extensionsv1alpha1.Extension) error {
namespace := ex.GetNamespace()
a.logger.Info("Component is being deleted", "component", "fleet-agent-management", "namespace", namespace)
return nil
}
// Restore the Extension resource.
func (a *actuator) Restore(ctx context.Context, ex *extensionsv1alpha1.Extension) error {
a.logger.Info("Component is being restored", "component", "fleet-agent-management")
return a.Reconcile(ctx, ex)
}
// Migrate the Extension resource.
func (a *actuator) Migrate(ctx context.Context, ex *extensionsv1alpha1.Extension) error {
a.logger.Info("Component is being migrated", "component", "fleet-agent-management")
return a.Delete(ctx, ex)
}
// InjectConfig injects the rest config to this actuator.
func (a *actuator) InjectConfig(config *rest.Config) error {
a.config = config
return nil
}
// InjectClient injects the controller runtime client into the reconciler.
func (a *actuator) InjectClient(client client.Client) error {
a.client = client
return nil
}
// InjectScheme injects the given scheme into the reconciler.
func (a *actuator) InjectScheme(scheme *runtime.Scheme) error {
a.decoder = serializer.NewCodecFactory(scheme).UniversalDecoder()
return nil
}
func (a *actuator) registerClusterInFleetManager(ctx context.Context, namespace string, cluster *extensions.Cluster) {
a.logger.Info("Starting with already registered check")
registered, err := a.fleetManager.GetCluster(ctx, cluster.Shoot.Name)
if !errors.IsNotFound(err) {
a.logger.Info("Cluster already registered - skipping registration", "clientId", registered.Spec.ClientID)
return
} else {
a.logger.Info("Cluster registration not found.")
}
a.logger.Info("Starting cluster registration process")
secret := &corev1.Secret{}
labels := make(map[string]string)
labels["corebundle"] = "true"
labels["region"] = cluster.Shoot.Spec.Region
labels["cluster"] = cluster.Shoot.Name
if a.serviceConfig.FleetAgentConfig.Labels != nil && len(a.serviceConfig.FleetAgentConfig.Labels) > 0 {//adds labels from configuration
for key, value := range a.serviceConfig.Labels{
labels[key] = value
}
}
a.logger.Info("Looking up Secret with KubeConfig for given Shoot.", "namespace", namespace, "secretName", KubeconfigSecretName)
if err := a.client.Get(ctx, kutil.Key(namespace, KubeconfigSecretName), secret); err == nil {
secretData := make(map[string][]byte)
secretData["value"] = secret.Data[KubeconfigKey]
a.logger.Info("Loaded kubeconfig from secret", "kubeconfig", secret, "namespace", namespace)
const fleetRegisterNamespace = "clusters"
kubeconfigSecret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "kubecfg-" + cluster.Shoot.Name,
Namespace: fleetRegisterNamespace,
},
Data: secretData,
}
clusterRegistration := fleetv1alpha1.Cluster{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{
Name: cluster.Shoot.Name,
Namespace: fleetRegisterNamespace,
Labels: labels,
},
Spec: fleetv1alpha1.ClusterSpec{
KubeConfigSecret: "kubecfg-" + cluster.Shoot.Name,
},
}
a.logger.Info("Creating kubeconfig secret for Fleet registration.")
if _, err = a.fleetManager.CreateKubeconfigSecret(ctx, &kubeconfigSecret); err != nil {
a.logger.Error(err, "Failed to create secret with kubeconfig for Fleet registration")
}
a.logger.Info("Creating Cluster registration for Fleet registration.")
if _, err = a.fleetManager.CreateCluster(ctx, &clusterRegistration); err != nil {
a.logger.Error(err, "Failed to create Cluster for Fleet registration")
}
a.logger.Info("Registered shoot cluster in Fleet Manager ", "registration", clusterRegistration)
} else {
a.logger.Error(err, "Failed to find Secret with kubeconfig for Fleet registration.")
}
}
func (a *actuator) updateStatus(ctx context.Context, ex *extensionsv1alpha1.Extension) error {
return controller.TryUpdateStatus(ctx, retry.DefaultBackoff, a.client, ex, func() error {
return nil
})
}