diff --git a/.github/workflows/run-vuln-check.yaml b/.github/workflows/run-vuln-check.yaml index eb9e0c14..6994722a 100644 --- a/.github/workflows/run-vuln-check.yaml +++ b/.github/workflows/run-vuln-check.yaml @@ -15,5 +15,5 @@ jobs: - name: vulncheck uses: golang/govulncheck-action@v1 with: - go-version-input: 1.22.4 + go-version-input: 1.22.5 go-package: ./... diff --git a/Dockerfile b/Dockerfile index 5f8ca5fa..134c249c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.22.4 as builder +FROM golang:1.22.5 as builder ARG TARGETOS ARG TARGETARCH diff --git a/api/v1/runtime_types.go b/api/v1/runtime_types.go index 72e53f70..2b4ca2f7 100644 --- a/api/v1/runtime_types.go +++ b/api/v1/runtime_types.go @@ -28,7 +28,10 @@ import ( //+kubebuilder:printcolumn:name="SHOOT-NAME",type=string,JSONPath=`.metadata.labels.kyma-project\.io/shoot-name` //+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" -const Finalizer = "runtime-controller.infrastructure-manager.kyma-project.io/deletion-hook" +const ( + Finalizer = "runtime-controller.infrastructure-manager.kyma-project.io/deletion-hook" + AnnotationGardenerCloudDelConfirmation = "confirmation.gardener.cloud/deletion" +) const ( RuntimeStateReady = "Ready" @@ -60,12 +63,13 @@ const ( ConditionReasonConfigurationCompleted = RuntimeConditionReason("ConfigurationCompleted") ConditionReasonConfigurationErr = RuntimeConditionReason("ConfigurationError") - ConditionReasonDeletion = RuntimeConditionReason("Deletion") - ConditionReasonDeletionErr = RuntimeConditionReason("DeletionErr") - ConditionReasonConversionError = RuntimeConditionReason("ConversionErr") - ConditionReasonCreationError = RuntimeConditionReason("CreationErr") - ConditionReasonGardenerError = RuntimeConditionReason("GardenerErr") - ConditionReasonDeleted = RuntimeConditionReason("Deleted") + ConditionReasonDeletion = RuntimeConditionReason("Deletion") + ConditionReasonDeletionErr = RuntimeConditionReason("DeletionErr") + ConditionReasonConversionError = RuntimeConditionReason("ConversionErr") + ConditionReasonCreationError = RuntimeConditionReason("CreationErr") + ConditionReasonGardenerError = RuntimeConditionReason("GardenerErr") + ConditionReasonSerializationError = RuntimeConditionReason("SerializationErr") + ConditionReasonDeleted = RuntimeConditionReason("Deleted") ) //+kubebuilder:object:root=true diff --git a/config/crd/bases/infrastructuremanager.kyma-project.io_runtimes.yaml b/config/crd/bases/infrastructuremanager.kyma-project.io_runtimes.yaml index 8bd0bb71..29e2da1f 100644 --- a/config/crd/bases/infrastructuremanager.kyma-project.io_runtimes.yaml +++ b/config/crd/bases/infrastructuremanager.kyma-project.io_runtimes.yaml @@ -758,14 +758,19 @@ spec: >= v1.26.' type: string systemReserved: - description: SystemReserved is the configuration + description: 'SystemReserved is the configuration for resources reserved for system processes not managed by kubernetes (e.g. journald). When updating these values, be aware that cgroup resizes may not succeed on active worker nodes. Look for the NodeAllocatableEnforced event to determine if the configuration was - applied. + applied. Deprecated: Separately configuring + resource reservations for system processes + is deprecated in Gardener and will be removed + soon. Please merge existing resource reservations + into the kubeReserved field. TODO(MichaelEischer): + Drop this field after v1.113 has been released.' properties: cpu: anyOf: diff --git a/go.mod b/go.mod index d32a019d..2fd813b7 100644 --- a/go.mod +++ b/go.mod @@ -1,21 +1,22 @@ module github.com/kyma-project/infrastructure-manager -go 1.22.4 +go 1.22.5 require ( - github.com/gardener/gardener v1.97.2 - github.com/gardener/gardener-extension-provider-aws v1.55.1 - github.com/gardener/gardener-extension-provider-gcp v1.36.2 - github.com/gardener/gardener-extension-provider-openstack v1.40.1 + github.com/gardener/gardener v1.98.2 + github.com/gardener/gardener-extension-provider-aws v1.56.0 + github.com/gardener/gardener-extension-provider-gcp v1.37.0 + github.com/gardener/gardener-extension-provider-openstack v1.41.0 github.com/go-logr/logr v1.4.2 github.com/onsi/ginkgo/v2 v2.19.0 github.com/onsi/gomega v1.33.1 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.19.1 github.com/stretchr/testify v1.9.0 - k8s.io/api v0.30.1 - k8s.io/apimachinery v0.30.1 - k8s.io/client-go v0.30.1 + 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 ) @@ -59,23 +60,22 @@ require ( github.com/stretchr/objx v0.5.2 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect + golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect golang.org/x/net v0.26.0 // indirect - golang.org/x/oauth2 v0.20.0 // indirect + golang.org/x/oauth2 v0.21.0 // indirect golang.org/x/sys v0.21.0 // indirect golang.org/x/term v0.21.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.22.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/protobuf v1.34.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect 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 ) diff --git a/go.sum b/go.sum index 8e07c776..64326e82 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= @@ -17,14 +19,14 @@ github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0 github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/gardener/gardener v1.97.2 h1:B1DlUvDdNSugcGJw+bavTNmuxcYdm5W7Dw4oB9eHijs= -github.com/gardener/gardener v1.97.2/go.mod h1:Y9HPpdO039YcdrPE/o8+NixlEvrLv6d34ojIhBfy2Uw= -github.com/gardener/gardener-extension-provider-aws v1.55.1 h1:bZs91iAn1sQvquAYcY1DsLJ+jJZDzwKfCEGy7lTLfNU= -github.com/gardener/gardener-extension-provider-aws v1.55.1/go.mod h1:wP5sVDIaW/isHJ6AuHYAiQrEq8ENw6nSSjg1BHAs528= -github.com/gardener/gardener-extension-provider-gcp v1.36.2 h1:FplEkJfypLBbsBQq+Fa88g61IsXEBbw+HYS6tAM4efU= -github.com/gardener/gardener-extension-provider-gcp v1.36.2/go.mod h1:7ck2wAfLFS8TRZ6lrAou2OIvZh/0ML0KB2kFGaZa8ZY= -github.com/gardener/gardener-extension-provider-openstack v1.40.1 h1:2/VJDkvZSMYZJNbPKflhgStZyfxU8YK6oJQIzi/gMUg= -github.com/gardener/gardener-extension-provider-openstack v1.40.1/go.mod h1:lx+Fg58zSsBXW6ci/+Bft2rS9gEwKFG8tVVpxbewFDw= +github.com/gardener/gardener v1.98.2 h1:gD7ZqUZet/OP8x6uiwjBzFOCvsgWDewF5IwWtaE6EfM= +github.com/gardener/gardener v1.98.2/go.mod h1:EfvKbBF53a52Wz16/Qe8hhpTZTaJ/P+CRWRr35BVVq0= +github.com/gardener/gardener-extension-provider-aws v1.56.0 h1:dESZ7IKBH8klWGYitg5hsX1aN1Cb+Oz3qLBxJ71Ltek= +github.com/gardener/gardener-extension-provider-aws v1.56.0/go.mod h1:kyUkLDvIEbxg5Kv4XsOa0CBFxbwmf5KFeKsJ/oVcijw= +github.com/gardener/gardener-extension-provider-gcp v1.37.0 h1:lRvh4syK7h8JzG9tVCI+O7Q1vEvXuK7DGs6vAaoQ1Pw= +github.com/gardener/gardener-extension-provider-gcp v1.37.0/go.mod h1:/bDiE8xZz2vfP8u3A5Pr2IqVo0VX9keL5ntjExqhB5A= +github.com/gardener/gardener-extension-provider-openstack v1.41.0 h1:AlzWuvw3DhkeXsQ6AnSHqp7tkWjtBZB+e7oCFpnFVBY= +github.com/gardener/gardener-extension-provider-openstack v1.41.0/go.mod h1:DqpbvoN61U7qSzRFXv3UyRTLkopd2yIXhtPRLzfE2Ro= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= @@ -126,15 +128,16 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM= -golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= -golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -149,8 +152,8 @@ golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4= -google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -162,14 +165,14 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.30.1 h1:kCm/6mADMdbAxmIh0LBjS54nQBE+U4KmbCfIkF5CpJY= -k8s.io/api v0.30.1/go.mod h1:ddbN2C0+0DIiPntan/bye3SW3PdwLa11/0yqwvuRrJM= +k8s.io/api v0.30.2 h1:+ZhRj+28QT4UOH+BKznu4CBgPWgkXO7XAvMcMl0qKvI= +k8s.io/api v0.30.2/go.mod h1:ULg5g9JvOev2dG0u2hig4Z7tQ2hHIuS+m8MNZ+X6EmI= k8s.io/apiextensions-apiserver v0.30.1 h1:4fAJZ9985BmpJG6PkoxVRpXv9vmPUOVzl614xarePws= k8s.io/apiextensions-apiserver v0.30.1/go.mod h1:R4GuSrlhgq43oRY9sF2IToFh7PVlF1JjfWdoG3pixk4= -k8s.io/apimachinery v0.30.1 h1:ZQStsEfo4n65yAdlGTfP/uSHMQSoYzU/oeEbkmF7P2U= -k8s.io/apimachinery v0.30.1/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= -k8s.io/client-go v0.30.1 h1:uC/Ir6A3R46wdkgCV3vbLyNOYyCJ8oZnjtJGKfytl/Q= -k8s.io/client-go v0.30.1/go.mod h1:wrAqLNs2trwiCH/wxxmT/x3hKVH9PuV0GGW0oDoHVqc= +k8s.io/apimachinery v0.30.2 h1:fEMcnBj6qkzzPGSVsAZtQThU62SmQ4ZymlXRC5yFSCg= +k8s.io/apimachinery v0.30.2/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/client-go v0.30.2 h1:sBIVJdojUNPDU/jObC+18tXWcTJVcwyqS9diGdWHk50= +k8s.io/client-go v0.30.2/go.mod h1:JglKSWULm9xlJLx4KCkfLLQ7XwtlbflV6uFFSHTMgVs= k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= diff --git a/internal/controller/runtime/fsm/runtime_fsm_common.go b/internal/controller/runtime/fsm/runtime_fsm_common.go index b28a109e..5ba47706 100644 --- a/internal/controller/runtime/fsm/runtime_fsm_common.go +++ b/internal/controller/runtime/fsm/runtime_fsm_common.go @@ -28,6 +28,10 @@ func requeue() (stateFn, *ctrl.Result, error) { return nil, &ctrl.Result{Requeue: true}, nil } +func requeueAfter(d time.Duration) (stateFn, *ctrl.Result, error) { + return nil, &ctrl.Result{RequeueAfter: d}, nil +} + func stop() (stateFn, *ctrl.Result, error) { return nil, nil, nil } diff --git a/internal/controller/runtime/fsm/runtime_fsm_delete_shoot.go b/internal/controller/runtime/fsm/runtime_fsm_delete_shoot.go index 7e6a8f79..8fa7d86a 100644 --- a/internal/controller/runtime/fsm/runtime_fsm_delete_shoot.go +++ b/internal/controller/runtime/fsm/runtime_fsm_delete_shoot.go @@ -2,14 +2,48 @@ package fsm import ( "context" + "encoding/json" imv1 "github.com/kyma-project/infrastructure-manager/api/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" ) 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" + s.shoot.Annotations = addGardenerCloudDelConfirmation(s.shoot.Annotations) + s.shoot.ManagedFields = nil + // attempt to marshall patched instance + shootData, err := json.Marshal(&s.shoot) + 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() + } + } + if !s.instance.IsStateWithConditionSet(imv1.RuntimeStateTerminating, imv1.ConditionTypeRuntimeProvisioned, imv1.ConditionReasonDeletion) { + m.log.Info("setting state to in deletion") s.instance.UpdateStateDeletion( imv1.ConditionTypeRuntimeProvisioned, imv1.ConditionReasonDeletion, @@ -19,8 +53,8 @@ func sFnDeleteShoot(ctx context.Context, m *fsm, s *systemState) (stateFn, *ctrl return updateStatusAndRequeue() } - err := m.ShootClient.Delete(ctx, s.instance.Name, v1.DeleteOptions{}) - + m.log.Info("deleting shoot") + err := m.ShootClient.Delete(ctx, s.instance.Name, metav1.DeleteOptions{}) if err != nil { m.log.Error(err, "Failed to delete gardener Shoot") @@ -30,7 +64,24 @@ func sFnDeleteShoot(ctx context.Context, m *fsm, s *systemState) (stateFn, *ctrl "False", "Gardener API delete error", ) - return updateStatusAndRequeue() + return updateStatusAndRequeueAfter(gardenerRequeueDuration) + } + + return updateStatusAndRequeueAfter(gardenerRequeueDuration) +} + +func isGardenerCloudDelConfirmationSet(a map[string]string) bool { + if len(a) == 0 { + return false + } + val, found := a[imv1.AnnotationGardenerCloudDelConfirmation] + return found && (val == "true") +} + +func addGardenerCloudDelConfirmation(a map[string]string) map[string]string { + if len(a) == 0 { + a = map[string]string{} } - return updateStatusAndStop() + a[imv1.AnnotationGardenerCloudDelConfirmation] = "true" + return a } diff --git a/internal/controller/runtime/fsm/runtime_fsm_delete_shoot_test.go b/internal/controller/runtime/fsm/runtime_fsm_delete_shoot_test.go new file mode 100644 index 00000000..267eea9e --- /dev/null +++ b/internal/controller/runtime/fsm/runtime_fsm_delete_shoot_test.go @@ -0,0 +1,64 @@ +package fsm + +import ( + "fmt" + "testing" + + v1 "github.com/kyma-project/infrastructure-manager/api/v1" +) + +func Test_addGardenerCloudDelConfirmation(t *testing.T) { + instance := v1.Runtime{} + actual := addGardenerCloudDelConfirmation(instance.Annotations) + if _, found := actual[v1.AnnotationGardenerCloudDelConfirmation]; !found { + t.Errorf("actual map should contain '%s' annotation", v1.AnnotationGardenerCloudDelConfirmation) + } +} + +func Test_IsGardenerCloudDelConfirmation(t *testing.T) { + var cases = []struct { + annotations map[string]string + expected bool + }{ + { + expected: false, + }, + { + annotations: (map[string]string{}), + expected: false, + }, + { + annotations: map[string]string{"test": "me"}, + expected: false, + }, + { + annotations: map[string]string{ + v1.AnnotationGardenerCloudDelConfirmation: "true", + "test": "me", + }, + expected: true, + }, + { + annotations: map[string]string{ + v1.AnnotationGardenerCloudDelConfirmation: "anything", + "test": "me", + }, + expected: false, + }, + { + annotations: map[string]string{ + v1.AnnotationGardenerCloudDelConfirmation: "", + }, + expected: false, + }, + } + + for i, tt := range cases { + t.Run(fmt.Sprintf("case-%d", i), func(t *testing.T) { + if actual := isGardenerCloudDelConfirmationSet(tt.annotations); actual != tt.expected { + t.Errorf("expected IsGardenerCloudDelConfirmation == %t", + tt.expected) + } + }) + } +} diff --git a/internal/controller/runtime/fsm/runtime_fsm_initialise.go b/internal/controller/runtime/fsm/runtime_fsm_initialise.go index c0bd9c5d..ea1330de 100644 --- a/internal/controller/runtime/fsm/runtime_fsm_initialise.go +++ b/internal/controller/runtime/fsm/runtime_fsm_initialise.go @@ -42,15 +42,27 @@ func sFnInitialize(ctx context.Context, m *fsm, s *systemState) (stateFn, *ctrl. return switchState(sFnPrepareCluster) // wait for pending shoot operation to complete } - if !instanceIsNotBeingDeleted && instanceHasFinalizer && s.shoot != nil { - m.log.Info("Instance is being deleted") + // resource cleanup is done; + // instance is being deleted and shoot was already deleted + if !instanceIsNotBeingDeleted && instanceHasFinalizer && s.shoot == nil { + return removeFinalizerAndStop(ctx, m, s) + } + + // resource cleanup did not start; + // instance is being deleted and shoot is not being deleted + if !instanceIsNotBeingDeleted && instanceHasFinalizer && s.shoot.DeletionTimestamp.IsZero() { + m.log.Info("Delete instance resources") return switchState(sFnDeleteShoot) } - if !instanceIsNotBeingDeleted && instanceHasFinalizer && s.shoot == nil { - return removeFinalizerAndStop(ctx, m, s) + // resource cleanup in progress; + // instance is being deleted and shoot is being deleted + if !instanceIsNotBeingDeleted && instanceHasFinalizer { + m.log.Info("Waiting on instance resources being deleted") + return requeueAfter(gardenerRequeueDuration) } + m.log.Info("noting to reconcile, stopping sfm") return stop() } diff --git a/internal/controller/runtime/runtime_controller.go b/internal/controller/runtime/runtime_controller.go index 5415cd53..769a0764 100644 --- a/internal/controller/runtime/runtime_controller.go +++ b/internal/controller/runtime/runtime_controller.go @@ -55,7 +55,7 @@ func (r *RuntimeReconciler) Reconcile(ctx context.Context, request ctrl.Request) if err := r.Get(ctx, request.NamespacedName, &runtime); err != nil { return ctrl.Result{ - Requeue: true, + Requeue: false, }, client.IgnoreNotFound(err) } @@ -90,6 +90,6 @@ func NewRuntimeReconciler(mgr ctrl.Manager, shootClient gardener.ShootClient, lo func (r *RuntimeReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&imv1.Runtime{}). - WithEventFilter(predicate.And(predicate.GenerationChangedPredicate{})). + WithEventFilter(predicate.GenerationChangedPredicate{}). Complete(r) } diff --git a/internal/gardener/client.go b/internal/gardener/client.go index 4a543318..9f5e075f 100644 --- a/internal/gardener/client.go +++ b/internal/gardener/client.go @@ -7,6 +7,7 @@ import ( gardener_api "github.com/gardener/gardener/pkg/apis/core/v1beta1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" ) @@ -16,6 +17,7 @@ type ShootClient interface { Create(ctx context.Context, shoot *gardener_api.Shoot, opts v1.CreateOptions) (*gardener_api.Shoot, error) Get(ctx context.Context, name string, opts v1.GetOptions) (*gardener_api.Shoot, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *gardener_api.Shoot, err error) // List(ctx context.Context, opts v1.ListOptions) (*gardener.ShootList, error) } diff --git a/internal/gardener/mocks/ShootClient.go b/internal/gardener/mocks/ShootClient.go index cd0cc706..ac582f90 100644 --- a/internal/gardener/mocks/ShootClient.go +++ b/internal/gardener/mocks/ShootClient.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.36.1. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks @@ -7,6 +7,8 @@ import ( mock "github.com/stretchr/testify/mock" + types "k8s.io/apimachinery/pkg/types" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" @@ -21,6 +23,10 @@ type ShootClient struct { func (_m *ShootClient) Create(ctx context.Context, shoot *v1beta1.Shoot, opts v1.CreateOptions) (*v1beta1.Shoot, error) { ret := _m.Called(ctx, shoot, opts) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 *v1beta1.Shoot var r1 error if rf, ok := ret.Get(0).(func(context.Context, *v1beta1.Shoot, v1.CreateOptions) (*v1beta1.Shoot, error)); ok { @@ -47,6 +53,10 @@ func (_m *ShootClient) Create(ctx context.Context, shoot *v1beta1.Shoot, opts v1 func (_m *ShootClient) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { ret := _m.Called(ctx, name, opts) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, v1.DeleteOptions) error); ok { r0 = rf(ctx, name, opts) @@ -61,6 +71,10 @@ func (_m *ShootClient) Delete(ctx context.Context, name string, opts v1.DeleteOp func (_m *ShootClient) Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta1.Shoot, error) { ret := _m.Called(ctx, name, opts) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *v1beta1.Shoot var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, v1.GetOptions) (*v1beta1.Shoot, error)); ok { @@ -83,6 +97,43 @@ func (_m *ShootClient) Get(ctx context.Context, name string, opts v1.GetOptions) return r0, r1 } +// Patch provides a mock function with given fields: ctx, name, pt, data, opts, subresources +func (_m *ShootClient) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (*v1beta1.Shoot, error) { + _va := make([]interface{}, len(subresources)) + for _i := range subresources { + _va[_i] = subresources[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, name, pt, data, opts) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for Patch") + } + + var r0 *v1beta1.Shoot + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, types.PatchType, []byte, v1.PatchOptions, ...string) (*v1beta1.Shoot, error)); ok { + return rf(ctx, name, pt, data, opts, subresources...) + } + if rf, ok := ret.Get(0).(func(context.Context, string, types.PatchType, []byte, v1.PatchOptions, ...string) *v1beta1.Shoot); ok { + r0 = rf(ctx, name, pt, data, opts, subresources...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*v1beta1.Shoot) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, types.PatchType, []byte, v1.PatchOptions, ...string) error); ok { + r1 = rf(ctx, name, pt, data, opts, subresources...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // NewShootClient creates a new instance of ShootClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewShootClient(t interface {