From 0bc040189626971f71510eb596abad1abdf5a70e Mon Sep 17 00:00:00 2001 From: m00g3n Date: Tue, 16 Jul 2024 16:26:31 +0200 Subject: [PATCH] add provision killswitch --- api/v1/runtime_types.go | 6 ++ api/v1/runtime_types_test.go | 76 +++++++++++++++++++ cmd/main.go | 2 +- .../fsm/runtime_fsm_create_shoot_dry_run.go | 40 ++++++++++ .../runtime/fsm/runtime_fsm_initialise.go | 5 +- .../fsm/runtime_fsm_initialise_test.go | 3 + .../controller/runtime/runtime_controller.go | 6 +- .../runtime/runtime_controller_test.go | 17 +++-- 8 files changed, 144 insertions(+), 11 deletions(-) create mode 100644 api/v1/runtime_types_test.go create mode 100644 internal/controller/runtime/fsm/runtime_fsm_create_shoot_dry_run.go diff --git a/api/v1/runtime_types.go b/api/v1/runtime_types.go index b29406ef..0e9603f5 100644 --- a/api/v1/runtime_types.go +++ b/api/v1/runtime_types.go @@ -33,6 +33,7 @@ import ( const ( Finalizer = "runtime-controller.infrastructure-manager.kyma-project.io/deletion-hook" AnnotationGardenerCloudDelConfirmation = "confirmation.gardener.cloud/deletion" + LabelControlledByProvisioner = "kyma-project.io/controlled-by-provisioner" ) const ( @@ -294,3 +295,8 @@ func (k *Runtime) ValidateRequiredLabels() error { } return nil } + +func (k *Runtime) IsControlledByProvisioner() bool { + value, found := k.Labels[LabelControlledByProvisioner] + return !found || value != "false" +} diff --git a/api/v1/runtime_types_test.go b/api/v1/runtime_types_test.go new file mode 100644 index 00000000..fab62595 --- /dev/null +++ b/api/v1/runtime_types_test.go @@ -0,0 +1,76 @@ +package v1_test + +import ( + "fmt" + "testing" + + imv1 "github.com/kyma-project/infrastructure-manager/api/v1" + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func Test_Runtime_IsControlledByProvisioner(t *testing.T) { + var tests = []struct { + desc string + rt imv1.Runtime + expected bool + }{ + { + desc: "rt without labels (zero value) should be controlled by the provisioner", + rt: imv1.Runtime{}, + expected: true, + }, + { + desc: "rt without labels (empty map) should be controlled by the provisioner", + rt: imv1.Runtime{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{}, + }, + }, + expected: true, + }, + { + desc: fmt.Sprintf(`rt without label: "%s" should be controlled by the provisioner`, imv1.LabelControlledByProvisioner), + rt: imv1.Runtime{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"test": "me"}, + }, + }, + expected: true, + }, + { + desc: fmt.Sprintf(`rt with label: "%s=true" should be controlled by the provisioner`, imv1.LabelControlledByProvisioner), + rt: imv1.Runtime{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{imv1.LabelControlledByProvisioner: "true"}, + }, + }, + expected: true, + }, + { + desc: fmt.Sprintf(`rt with label: "%s=sth" should be controlled by the provisioner`, imv1.LabelControlledByProvisioner), + rt: imv1.Runtime{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{imv1.LabelControlledByProvisioner: "sth"}, + }, + }, + expected: true, + }, + { + desc: fmt.Sprintf(`rt with label: "%s=false" should NOT be controlled by the provisioner`, imv1.LabelControlledByProvisioner), + rt: imv1.Runtime{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{imv1.LabelControlledByProvisioner: "false"}, + }, + }, + expected: false, + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) { + actual := tt.rt.IsControlledByProvisioner() + assert.Equal(t, tt.expected, actual, tt.desc) + }) + } +} diff --git a/cmd/main.go b/cmd/main.go index 5c400e73..9c7e7d86 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -59,7 +59,7 @@ func init() { const defaultMinimalRotationTimeRatio = 0.6 const defaultExpirationTime = 24 * time.Hour -const defaultRuntimeReconcilerEnabled = false +const defaultRuntimeReconcilerEnabled = true const defaultGardenerRequestTimeout = 60 * time.Second func main() { diff --git a/internal/controller/runtime/fsm/runtime_fsm_create_shoot_dry_run.go b/internal/controller/runtime/fsm/runtime_fsm_create_shoot_dry_run.go new file mode 100644 index 00000000..bd94c3c1 --- /dev/null +++ b/internal/controller/runtime/fsm/runtime_fsm_create_shoot_dry_run.go @@ -0,0 +1,40 @@ +package fsm + +import ( + "context" + "fmt" + + imv1 "github.com/kyma-project/infrastructure-manager/api/v1" + ctrl "sigs.k8s.io/controller-runtime" +) + +func sFnCreateShootDryRun(_ context.Context, m *fsm, s *systemState) (stateFn, *ctrl.Result, error) { + m.log.Info("Create shoot [dry-run]") + + newShoot, err := convertShoot(&s.instance, m.ConverterConfig) + if err != nil { + m.log.Error(err, "Failed to convert Runtime instance to shoot object [dry-run]") + return updateStatePendingWithErrorAndStop( + &s.instance, + imv1.ConditionTypeRuntimeProvisioned, + imv1.ConditionReasonConversionError, + "Runtime conversion error") + } + + s.shoot = &newShoot + s.instance.UpdateStateReady( + imv1.ConditionTypeRuntimeProvisioned, + imv1.ConditionReasonConfigurationCompleted, + "Runtime processing completed successfully [dry-run]") + + // stop machine if persistence not enabled + if m.PVCPath == "" { + return updateStatusAndStop() + } + + path := fmt.Sprintf("%s/%s-%s.yaml", m.PVCPath, s.shoot.Namespace, s.shoot.Name) + if err := persist(path, s.shoot, m.writerProvider); err != nil { + return updateStatusAndStopWithError(err) + } + return updateStatusAndStop() +} diff --git a/internal/controller/runtime/fsm/runtime_fsm_initialise.go b/internal/controller/runtime/fsm/runtime_fsm_initialise.go index 1f5ff4cd..23b5d837 100644 --- a/internal/controller/runtime/fsm/runtime_fsm_initialise.go +++ b/internal/controller/runtime/fsm/runtime_fsm_initialise.go @@ -34,10 +34,13 @@ func sFnInitialize(ctx context.Context, m *fsm, s *systemState) (stateFn, *ctrl. if instanceIsNotBeingDeleted && s.shoot == nil { m.log.Info("Gardener shoot does not exist, creating new one") + if s.instance.IsControlledByProvisioner() { + return switchState(sFnCreateShootDryRun) + } return switchState(sFnCreateShoot) } - if instanceIsNotBeingDeleted { + if instanceIsNotBeingDeleted && !s.instance.IsControlledByProvisioner() { m.log.Info("Gardener shoot exists, processing") return switchState(sFnSelectShootProcessing) } diff --git a/internal/controller/runtime/fsm/runtime_fsm_initialise_test.go b/internal/controller/runtime/fsm/runtime_fsm_initialise_test.go index a9ac5362..fcdceea8 100644 --- a/internal/controller/runtime/fsm/runtime_fsm_initialise_test.go +++ b/internal/controller/runtime/fsm/runtime_fsm_initialise_test.go @@ -55,6 +55,9 @@ var _ = Describe("KIM sFnInitialise", func() { Name: "test-instance", Namespace: "default", Finalizers: []string{"test-me-plz"}, + Labels: map[string]string{ + imv1.LabelControlledByProvisioner: "false", + }, }, } diff --git a/internal/controller/runtime/runtime_controller.go b/internal/controller/runtime/runtime_controller.go index e935430f..82161dee 100644 --- a/internal/controller/runtime/runtime_controller.go +++ b/internal/controller/runtime/runtime_controller.go @@ -91,6 +91,10 @@ func (r *RuntimeReconciler) UpdateShootClient(client client.Client) { func (r *RuntimeReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&imv1.Runtime{}). - WithEventFilter(predicate.GenerationChangedPredicate{}). + WithEventFilter(predicate.Or( + predicate.GenerationChangedPredicate{}, + predicate.LabelChangedPredicate{}, + predicate.AnnotationChangedPredicate{}, + )). Complete(r) } diff --git a/internal/controller/runtime/runtime_controller_test.go b/internal/controller/runtime/runtime_controller_test.go index 47d63335..8bcee559 100644 --- a/internal/controller/runtime/runtime_controller_test.go +++ b/internal/controller/runtime/runtime_controller_test.go @@ -186,14 +186,15 @@ func CreateRuntimeStub(resourceName string) *imv1.Runtime { Name: resourceName, Namespace: "default", Labels: map[string]string{ - imv1.LabelKymaRuntimeID: "059dbc39-fd2b-4186-b0e5-8a1bc8ede5b8", - imv1.LabelKymaInstanceID: "test-instance", - imv1.LabelKymaRegion: "region", - imv1.LabelKymaName: "caadafae-1234-1234-1234-123456789abc", - imv1.LabelKymaBrokerPlanID: "broker-plan-id", - imv1.LabelKymaBrokerPlanName: "aws", - imv1.LabelKymaGlobalAccountID: "461f6292-8085-41c8-af0c-e185f39b5e18", - imv1.LabelKymaSubaccountID: "c5ad84ae-3d1b-4592-bee1-f022661f7b30", + imv1.LabelKymaRuntimeID: "059dbc39-fd2b-4186-b0e5-8a1bc8ede5b8", + imv1.LabelKymaInstanceID: "test-instance", + imv1.LabelKymaRegion: "region", + imv1.LabelKymaName: "caadafae-1234-1234-1234-123456789abc", + imv1.LabelKymaBrokerPlanID: "broker-plan-id", + imv1.LabelKymaBrokerPlanName: "aws", + imv1.LabelKymaGlobalAccountID: "461f6292-8085-41c8-af0c-e185f39b5e18", + imv1.LabelKymaSubaccountID: "c5ad84ae-3d1b-4592-bee1-f022661f7b30", + imv1.LabelControlledByProvisioner: "false", }, }, Spec: imv1.RuntimeSpec{