Skip to content

Commit

Permalink
Merge pull request #275 from koala7659/kim-update-shoot
Browse files Browse the repository at this point in the history
feat: KIM - Implementation of update shoot functionality
  • Loading branch information
kyma-bot authored Jul 10, 2024
2 parents 5bcc401 + 6eea662 commit d357738
Show file tree
Hide file tree
Showing 21 changed files with 642 additions and 318 deletions.
16 changes: 16 additions & 0 deletions api/v1/runtime_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,19 @@ func (k *Runtime) IsConditionSet(c RuntimeConditionType, r RuntimeConditionReaso
}
return false
}

func (k *Runtime) IsStateWithConditionAndStatusSet(runtimeState State, c RuntimeConditionType, r RuntimeConditionReason, s metav1.ConditionStatus) bool {
if k.Status.State != runtimeState {
return false
}

return k.IsConditionSetWithStatus(c, r, s)
}

func (k *Runtime) IsConditionSetWithStatus(c RuntimeConditionType, r RuntimeConditionReason, s metav1.ConditionStatus) bool {
condition := meta.FindStatusCondition(k.Status.Conditions, string(c))
if condition != nil && condition.Reason == string(r) && condition.Status == s {
return true
}
return false
}
35 changes: 17 additions & 18 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,15 @@ func main() {
}

gardenerNamespace := fmt.Sprintf("garden-%s", gardenerProjectName)
shootClient, dynamicKubeconfigClient, err := initGardenerClients(gardenerKubeconfigPath, gardenerNamespace)
gardenerClient, shootClient, dynamicKubeconfigClient, err := initGardenerClients(gardenerKubeconfigPath, gardenerNamespace)

if err != nil {
setupLog.Error(err, "unable to initialize gardener clients", "controller", "GardenerCluster")
os.Exit(1)
}

kubeconfigProvider := kubeconfig.NewKubeconfigProvider(shootClient,
kubeconfigProvider := kubeconfig.NewKubeconfigProvider(
shootClient,
dynamicKubeconfigClient,
gardenerNamespace,
int64(expirationTime.Seconds()))
Expand All @@ -144,21 +145,19 @@ func main() {
os.Exit(1)
}

cfg := fsm.RCCfg{Finalizer: infrastructuremanagerv1.Finalizer}
cfg := fsm.RCCfg{
Finalizer: infrastructuremanagerv1.Finalizer,
ShootNamesapace: gardenerNamespace,
}
if persistShoot {
cfg.PVCPath = "/testdata/kim"
}

if enableRuntimeReconciler {
if err = (&runtime_controller.RuntimeReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
ShootClient: shootClient,
Log: logger,
Cfg: cfg,
EventRecorder: mgr.GetEventRecorderFor("runtime-controller"),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Runtime")
runtimeReconciler := runtime_controller.NewRuntimeReconciler(mgr, gardenerClient, logger, cfg)

if err = runtimeReconciler.SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to setup controller with Manager", "controller", "Runtime")
os.Exit(1)
}
}
Expand All @@ -182,29 +181,29 @@ func main() {
}
}

func initGardenerClients(kubeconfigPath string, namespace string) (gardener_apis.ShootInterface, client.SubResourceClient, error) {
func initGardenerClients(kubeconfigPath string, namespace string) (client.Client, gardener_apis.ShootInterface, client.SubResourceClient, error) {
restConfig, err := gardener.NewRestConfigFromFile(kubeconfigPath)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}

gardenerClientSet, err := gardener_apis.NewForConfig(restConfig)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}

gardenerClient, err := client.New(restConfig, client.Options{})
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}

shootClient := gardenerClientSet.Shoots(namespace)
dynamicKubeconfigAPI := gardenerClient.SubResource("adminkubeconfig")

err = v1beta1.AddToScheme(gardenerClient.Scheme())
if err != nil {
return nil, nil, errors.Wrap(err, "failed to register Gardener schema")
return nil, nil, nil, errors.Wrap(err, "failed to register Gardener schema")
}

return shootClient, dynamicKubeconfigAPI, nil
return gardenerClient, shootClient, dynamicKubeconfigAPI, nil
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ require (
k8s.io/api v0.30.2
k8s.io/apimachinery v0.30.2
k8s.io/client-go v0.30.2
k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0
sigs.k8s.io/controller-runtime v0.18.4
sigs.k8s.io/yaml v1.4.0
)
Expand Down Expand Up @@ -76,6 +75,7 @@ require (
k8s.io/apiextensions-apiserver v0.30.1 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
)
Expand Down
8 changes: 4 additions & 4 deletions internal/controller/runtime/fsm/runtime_fsm.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (

"github.com/go-logr/logr"
imv1 "github.com/kyma-project/infrastructure-manager/api/v1"
"github.com/kyma-project/infrastructure-manager/internal/gardener"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand All @@ -28,8 +27,9 @@ type writerGetter = func(filePath string) (io.Writer, error)

// runtime reconciler specific configuration
type RCCfg struct {
Finalizer string
PVCPath string
Finalizer string
PVCPath string
ShootNamesapace string
}

func (f stateFn) String() string {
Expand All @@ -46,7 +46,7 @@ type Watch = func(src source.Source, eventhandler handler.EventHandler, predicat
type K8s struct {
client.Client
record.EventRecorder
ShootClient gardener.ShootClient
ShootClient client.Client
}

type Fsm interface {
Expand Down
51 changes: 7 additions & 44 deletions internal/controller/runtime/fsm/runtime_fsm_create_shoot.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,18 @@ import (
"context"

imv1 "github.com/kyma-project/infrastructure-manager/api/v1"
gardener_shoot "github.com/kyma-project/infrastructure-manager/internal/gardener/shoot"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
)

func sFnCreateShoot(ctx context.Context, m *fsm, s *systemState) (stateFn, *ctrl.Result, error) {
converterConfig := FixConverterConfig()
converter := gardener_shoot.NewConverter(converterConfig)
shoot, err := converter.ToShoot(s.instance)

m.log.Info("Create shoot")
newShoot, err := convertShoot(&s.instance)
if err != nil {
m.log.Error(err, "unable to convert Runtime CR to a shoot object")

s.instance.UpdateStatePending(
imv1.ConditionTypeRuntimeProvisioned,
imv1.ConditionReasonConversionError,
"False",
"Runtime conversion error",
)

return updateStatusAndStop()
m.log.Error(err, "Failed to convert Runtime instance to shoot object")
return updateStatePendingWithErrorAndStop(&s.instance, imv1.ConditionTypeRuntimeProvisioned, imv1.ConditionReasonConversionError, "Runtime conversion error")
}

m.log.Info("Shoot converted successfully", "Name", shoot.Name, "Namespace", shoot.Namespace, "Shoot", shoot)

s.shoot, err = m.ShootClient.Create(ctx, &shoot, v1.CreateOptions{})
err = m.ShootClient.Create(ctx, &newShoot)

if err != nil {
m.log.Error(err, "Failed to create new gardener Shoot")
Expand All @@ -40,11 +26,9 @@ func sFnCreateShoot(ctx context.Context, m *fsm, s *systemState) (stateFn, *ctrl
"False",
"Gardener API create error",
)

return updateStatusAndRequeueAfter(gardenerRequeueDuration)
}

m.log.Info("Gardener shoot for runtime initialised successfully", "Name", s.shoot.Name, "Namespace", s.shoot.Namespace)
m.log.Info("Gardener shoot for runtime initialised successfully", "Name", newShoot.Name, "Namespace", newShoot.Namespace)

s.instance.UpdateStatePending(
imv1.ConditionTypeRuntimeProvisioned,
Expand All @@ -55,30 +39,9 @@ func sFnCreateShoot(ctx context.Context, m *fsm, s *systemState) (stateFn, *ctrl

shouldPersistShoot := m.PVCPath != ""
if shouldPersistShoot {
s.shoot = newShoot.DeepCopy()
return switchState(sFnPersistShoot)
}

return updateStatusAndRequeueAfter(gardenerRequeueDuration)
}

func FixConverterConfig() gardener_shoot.ConverterConfig {
return gardener_shoot.ConverterConfig{
Kubernetes: gardener_shoot.KubernetesConfig{
DefaultVersion: "1.29", //nolint:godox TODO: Should be parametrised
},

DNS: gardener_shoot.DNSConfig{
SecretName: "aws-route53-secret-dev",
DomainPrefix: "dev.kyma.ondemand.com",
ProviderType: "aws-route53",
},
Provider: gardener_shoot.ProviderConfig{
AWS: gardener_shoot.AWSConfig{
EnableIMDSv2: true, //nolint:godox TODO: Should be parametrised
},
},
Gardener: gardener_shoot.GardenerConfig{
ProjectName: "kyma-dev", //nolint:godox TODO: should be parametrised
},
}
}
33 changes: 9 additions & 24 deletions internal/controller/runtime/fsm/runtime_fsm_delete_shoot.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,26 @@ package fsm

import (
"context"
"encoding/json"

imv1 "github.com/kyma-project/infrastructure-manager/api/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/ptr"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
)

func sFnDeleteShoot(ctx context.Context, m *fsm, s *systemState) (stateFn, *ctrl.Result, error) {
m.log.Info("delete shoot state")
if !isGardenerCloudDelConfirmationSet(s.shoot.Annotations) {
m.log.Info("patching shoot with del-confirmation")
// workaround for Gardener client
s.shoot.Kind = "Shoot"
s.shoot.APIVersion = "core.gardener.cloud/v1beta1"
setObjectFields(s.shoot)
s.shoot.Annotations = addGardenerCloudDelConfirmation(s.shoot.Annotations)
s.shoot.ManagedFields = nil
// attempt to marshall patched instance
shootData, err := json.Marshal(&s.shoot)

err := m.ShootClient.Patch(ctx, s.shoot, client.Apply, &client.PatchOptions{
FieldManager: "kim",
Force: ptrTo(true),
})

if err != nil {
// unrecoverable error
s.instance.UpdateStateDeletion(
imv1.ConditionTypeRuntimeProvisioned,
imv1.ConditionReasonSerializationError,
"False",
err.Error())
return updateStatusAndStop()
}
// see: https://gardener.cloud/docs/gardener/projects/#four-eyes-principle-for-resource-deletion
if s.shoot, err = m.ShootClient.Patch(ctx, s.shoot.Name, types.ApplyPatchType, shootData,
metav1.PatchOptions{
FieldManager: "kim",
Force: ptr.To(true),
}); err != nil {
m.log.Error(err, "unable to patch shoot:", s.shoot.Name)
return requeue()
}
Expand All @@ -54,7 +39,7 @@ func sFnDeleteShoot(ctx context.Context, m *fsm, s *systemState) (stateFn, *ctrl
}

m.log.Info("deleting shoot")
err := m.ShootClient.Delete(ctx, s.instance.Name, metav1.DeleteOptions{})
err := m.ShootClient.Delete(ctx, s.shoot)
if err != nil {
m.log.Error(err, "Failed to delete gardener Shoot")

Expand Down
4 changes: 2 additions & 2 deletions internal/controller/runtime/fsm/runtime_fsm_initialise.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ func sFnInitialize(ctx context.Context, m *fsm, s *systemState) (stateFn, *ctrl.
return switchState(sFnCreateShoot)
}

if instanceIsNotBeingDeleted && s.shoot != nil {
if instanceIsNotBeingDeleted {
m.log.Info("Gardener shoot exists, processing")
return switchState(sFnPrepareCluster) // wait for pending shoot operation to complete
return switchState(sFnSelectShootProcessing)
}

// resource cleanup is done;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,13 @@ var _ = Describe("KIM sFnInitialise", func() {
},
),
Entry(
"should return sFnPrepareCluster and no error when exists Provisioning Condition and shoot exists",
"should return sFnSelectShootProcessing and no error when exists Provisioning Condition and shoot exists",
testCtx,
must(newFakeFSM, withTestFinalizer),
&systemState{instance: testRtWithFinalizerAndProvisioningCondition, shoot: &testShoot},
testOpts{
MatchExpectedErr: BeNil(),
MatchNextFnState: haveName("sFnPrepareCluster"),
MatchNextFnState: haveName("sFnSelectShootProcessing"),
},
),
)
Expand Down
Loading

0 comments on commit d357738

Please sign in to comment.