mirror of
https://github.com/ysoftdevs/gardener-extension-shoot-fleet-agent.git
synced 2026-04-11 03:07:10 +02:00
Initial v1.0.0 commit
This commit is contained in:
204
pkg/controller/actuator.go
Normal file
204
pkg/controller/actuator.go
Normal file
@@ -0,0 +1,204 @@
|
||||
// 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
|
||||
})
|
||||
}
|
||||
66
pkg/controller/add.go
Normal file
66
pkg/controller/add.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// 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 (
|
||||
controllerconfig "github.com/javamachr/gardener-extension-shoot-fleet-agent/pkg/controller/config"
|
||||
|
||||
"github.com/gardener/gardener/extensions/pkg/controller/extension"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
)
|
||||
|
||||
const (
|
||||
// Type is the type of Extension resource.
|
||||
Type = "shoot-fleet-agent"
|
||||
// ControllerName is the name of the shoot fleet agent service controller.
|
||||
ControllerName = "shoot_fleet_agent"
|
||||
// FinalizerSuffix is the finalizer suffix for the shoot fleet agent service controller.
|
||||
FinalizerSuffix = "shoot-fleet-agent"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultAddOptions are the default AddOptions for AddToManager.
|
||||
DefaultAddOptions = AddOptions{}
|
||||
)
|
||||
|
||||
// AddOptions are options to apply when adding the shoot fleet agent service controller to the manager.
|
||||
type AddOptions struct {
|
||||
// ControllerOptions contains options for the controller.
|
||||
ControllerOptions controller.Options
|
||||
// ServiceConfig contains configuration for the shoot fleet agent service.
|
||||
ServiceConfig controllerconfig.Config
|
||||
// IgnoreOperationAnnotation specifies whether to ignore the operation annotation or not.
|
||||
IgnoreOperationAnnotation bool
|
||||
}
|
||||
|
||||
// AddToManager adds a controller with the default Options to the given Controller Manager.
|
||||
func AddToManager(mgr manager.Manager) error {
|
||||
return AddToManagerWithOptions(mgr, DefaultAddOptions.ControllerOptions, DefaultAddOptions.ServiceConfig)
|
||||
}
|
||||
|
||||
// AddToManagerWithOptions adds a controller with the given Options to the given manager.
|
||||
// The opts.Reconciler is being set with a newly instantiated actuator.
|
||||
func AddToManagerWithOptions(mgr manager.Manager, opts controller.Options, config controllerconfig.Config) error {
|
||||
return extension.Add(mgr, extension.AddArgs{
|
||||
Actuator: NewActuator(config),
|
||||
ControllerOptions: opts,
|
||||
Name: ControllerName,
|
||||
FinalizerSuffix: FinalizerSuffix,
|
||||
Resync: 0,
|
||||
Predicates: extension.DefaultPredicates(DefaultAddOptions.IgnoreOperationAnnotation),
|
||||
Type: Type,
|
||||
})
|
||||
}
|
||||
7
pkg/controller/config/config.go
Normal file
7
pkg/controller/config/config.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package config
|
||||
|
||||
import "github.com/javamachr/gardener-extension-shoot-fleet-agent/pkg/apis/config"
|
||||
|
||||
type Config struct {
|
||||
config.FleetAgentConfig
|
||||
}
|
||||
51
pkg/controller/fleetmanager.go
Normal file
51
pkg/controller/fleetmanager.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
clientset "github.com/javamachr/gardener-extension-shoot-fleet-agent/pkg/client/fleet/clientset/versioned"
|
||||
"github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
type FleetManager struct {
|
||||
secretClient kubernetes.Clientset
|
||||
fleetClient clientset.Interface
|
||||
namespace string
|
||||
}
|
||||
|
||||
func NewManagerForConfig(c *rest.Config, namespace string) (*FleetManager, error) {
|
||||
secretClient, err := kubernetes.NewForConfig(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fleetClient, err := clientset.NewForConfig(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &FleetManager{
|
||||
secretClient: *secretClient,
|
||||
fleetClient: fleetClient,
|
||||
namespace: namespace,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *FleetManager) CreateCluster(ctx context.Context, cluster *v1alpha1.Cluster) (*v1alpha1.Cluster, error) {
|
||||
return f.fleetClient.FleetV1alpha1().Clusters(f.namespace).Create(ctx, cluster, metav1.CreateOptions{})
|
||||
}
|
||||
|
||||
func (f *FleetManager) UpdateCluster(ctx context.Context, cluster *v1alpha1.Cluster) (*v1alpha1.Cluster, error) {
|
||||
return f.fleetClient.FleetV1alpha1().Clusters(f.namespace).Update(ctx, cluster, metav1.UpdateOptions{})
|
||||
}
|
||||
|
||||
func (f *FleetManager) GetCluster(ctx context.Context, clusterName string) (*v1alpha1.Cluster, error) {
|
||||
return f.fleetClient.FleetV1alpha1().Clusters(f.namespace).Get(ctx, clusterName, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
func (f *FleetManager) CreateKubeconfigSecret(ctx context.Context, secret *corev1.Secret) (*corev1.Secret, error) {
|
||||
return f.secretClient.CoreV1().Secrets(f.namespace).Create(ctx, secret, metav1.CreateOptions{})
|
||||
}
|
||||
57
pkg/controller/healthcheck/add.go
Normal file
57
pkg/controller/healthcheck/add.go
Normal file
@@ -0,0 +1,57 @@
|
||||
// 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 healthcheck
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
fleetcontroller "github.com/javamachr/gardener-extension-shoot-fleet-agent/pkg/controller"
|
||||
|
||||
"github.com/gardener/gardener/extensions/pkg/controller/healthcheck"
|
||||
healthcheckconfig "github.com/gardener/gardener/extensions/pkg/controller/healthcheck/config"
|
||||
extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultSyncPeriod = time.Second * 30
|
||||
// DefaultAddOptions are the default DefaultAddArgs for AddToManager.
|
||||
DefaultAddOptions = healthcheck.DefaultAddArgs{
|
||||
HealthCheckConfig: healthcheckconfig.HealthCheckConfig{SyncPeriod: metav1.Duration{Duration: defaultSyncPeriod}},
|
||||
}
|
||||
)
|
||||
|
||||
// RegisterHealthChecks registers health checks for each extension resource
|
||||
// HealthChecks are grouped by extension (e.g worker), extension.type (e.g aws) and Health Check Type (e.g SystemComponentsHealthy)
|
||||
func RegisterHealthChecks(mgr manager.Manager, opts healthcheck.DefaultAddArgs) error {
|
||||
return healthcheck.DefaultRegistration(
|
||||
fleetcontroller.Type,
|
||||
extensionsv1alpha1.SchemeGroupVersion.WithKind(extensionsv1alpha1.ExtensionResource),
|
||||
func() client.ObjectList { return &extensionsv1alpha1.ExtensionList{} },
|
||||
func() extensionsv1alpha1.Object { return &extensionsv1alpha1.Extension{} },
|
||||
mgr,
|
||||
opts,
|
||||
nil,
|
||||
[]healthcheck.ConditionTypeToHealthCheck{},
|
||||
)
|
||||
}
|
||||
|
||||
// AddToManager adds a controller with the default Options.
|
||||
func AddToManager(mgr manager.Manager) error {
|
||||
return RegisterHealthChecks(mgr, DefaultAddOptions)
|
||||
}
|
||||
24
pkg/controller/utils.go
Normal file
24
pkg/controller/utils.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
func writeKubeconfigToTempFile(kubeconfig []byte) (path string, error error) {
|
||||
tmpFile, err := ioutil.TempFile(os.TempDir(), "kubeconfig-")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if _, err = tmpFile.Write(kubeconfig); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Close the file
|
||||
if err := tmpFile.Close(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return tmpFile.Name(), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user