@@ -24,11 +24,11 @@ import (
2424 "strconv"
2525 "time"
2626
27- "github.com/go-logr/logr"
2827 . "github.com/onsi/ginkgo/v2"
2928 . "github.com/onsi/gomega"
3029 . "github.com/onsi/gomega/gstruct"
31- sdkhandler "github.com/operator-framework/operator-lib/handler"
30+
31+ "github.com/go-logr/logr"
3232 "helm.sh/helm/v3/pkg/action"
3333 "helm.sh/helm/v3/pkg/chart"
3434 "helm.sh/helm/v3/pkg/chartutil"
@@ -46,6 +46,7 @@ import (
4646 "k8s.io/client-go/tools/record"
4747 "sigs.k8s.io/controller-runtime/pkg/client"
4848 "sigs.k8s.io/controller-runtime/pkg/client/fake"
49+ "sigs.k8s.io/controller-runtime/pkg/client/interceptor"
4950 "sigs.k8s.io/controller-runtime/pkg/event"
5051 "sigs.k8s.io/controller-runtime/pkg/log/zap"
5152 "sigs.k8s.io/controller-runtime/pkg/manager"
@@ -54,6 +55,8 @@ import (
5455 "sigs.k8s.io/controller-runtime/pkg/source"
5556 "sigs.k8s.io/yaml"
5657
58+ sdkhandler "github.com/operator-framework/operator-lib/handler"
59+
5760 "github.com/operator-framework/helm-operator-plugins/internal/sdk/controllerutil"
5861 "github.com/operator-framework/helm-operator-plugins/pkg/annotation"
5962 helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client"
@@ -205,6 +208,18 @@ var _ = Describe("Reconciler", func() {
205208 Expect (WithMaxConcurrentReconciles (- 1 )(r )).NotTo (Succeed ())
206209 })
207210 })
211+ _ = Describe ("WithWaitForDeletionTimeout" , func () {
212+ It ("should set the reconciler wait for deletion timeout" , func () {
213+ Expect (WithWaitForDeletionTimeout (time .Second )(r )).To (Succeed ())
214+ Expect (r .waitForDeletionTimeout ).To (Equal (time .Second ))
215+ })
216+ It ("should fail if value is zero" , func () {
217+ Expect (WithWaitForDeletionTimeout (0 )(r )).NotTo (Succeed ())
218+ })
219+ It ("should fail if value is negative" , func () {
220+ Expect (WithWaitForDeletionTimeout (- time .Second )(r )).NotTo (Succeed ())
221+ })
222+ })
208223 _ = Describe ("WithReconcilePeriod" , func () {
209224 It ("should set the reconciler reconcile period" , func () {
210225 Expect (WithReconcilePeriod (0 )(r )).To (Succeed ())
@@ -686,6 +701,57 @@ var _ = Describe("Reconciler", func() {
686701 })
687702 })
688703 })
704+ When ("cache contains stale CR that has actually been deleted" , func () {
705+ // This test simulates what we expect to happen when we timeout waiting for a CR that we
706+ // deleted to be removed from the cache.
707+ It ("ignores not found errors and returns successfully" , func () {
708+ By ("deleting the CR and then setting a finalizer on the stale CR" , func () {
709+ // We _actually_ remove the CR from the API server, but we'll make a fake client
710+ // that returns the stale CR. The stale CR will have a finalizer set because that
711+ Expect (mgr .GetClient ().Delete (ctx , obj )).To (Succeed ())
712+ Eventually (func () error {
713+ return mgr .GetAPIReader ().Get (ctx , objKey , obj )
714+ }).Should (WithTransform (apierrors .IsNotFound , BeTrue ()))
715+ obj .SetFinalizers ([]string {uninstallFinalizer })
716+ })
717+
718+ By ("configuring a client that returns the stale CR" , func () {
719+ // Make a client that returns the stale CR, but sends writes to the real client.
720+ cl := fake .NewClientBuilder ().WithObjects (obj ).WithInterceptorFuncs (interceptor.Funcs {
721+ Create : func (ctx context.Context , _ client.WithWatch , fakeObj client.Object , opts ... client.CreateOption ) error {
722+ return mgr .GetClient ().Create (ctx , fakeObj , opts ... )
723+ },
724+ Delete : func (ctx context.Context , _ client.WithWatch , fakeObj client.Object , opts ... client.DeleteOption ) error {
725+ return mgr .GetClient ().Delete (ctx , fakeObj , opts ... )
726+ },
727+ DeleteAllOf : func (ctx context.Context , _ client.WithWatch , fakeObj client.Object , opts ... client.DeleteAllOfOption ) error {
728+ return mgr .GetClient ().DeleteAllOf (ctx , fakeObj , opts ... )
729+ },
730+ Update : func (ctx context.Context , _ client.WithWatch , fakeObj client.Object , opts ... client.UpdateOption ) error {
731+ return mgr .GetClient ().Update (ctx , fakeObj , opts ... )
732+ },
733+ Patch : func (ctx context.Context , _ client.WithWatch , fakeObj client.Object , patch client.Patch , opts ... client.PatchOption ) error {
734+ return mgr .GetClient ().Patch (ctx , fakeObj , patch , opts ... )
735+ },
736+ SubResource : func (_ client.WithWatch , subresource string ) client.SubResourceClient {
737+ return mgr .GetClient ().SubResource (subresource )
738+ },
739+ }).WithStatusSubresource (obj ).Build ()
740+ r .client = cl
741+ })
742+
743+ By ("successfully ignoring not found errors and returning a nil error" , func () {
744+ res , err := r .Reconcile (ctx , req )
745+ Expect (res ).To (Equal (reconcile.Result {}))
746+ Expect (err ).ToNot (HaveOccurred ())
747+ })
748+
749+ By ("ensuring the finalizer is removed and the CR is deleted" , func () {
750+ err := mgr .GetAPIReader ().Get (ctx , objKey , obj )
751+ Expect (apierrors .IsNotFound (err )).To (BeTrue ())
752+ })
753+ })
754+ })
689755 When ("CR is deleted, release is not present, but uninstall finalizer exists" , func () {
690756 It ("removes the finalizer" , func () {
691757 By ("adding the uninstall finalizer and deleting the CR" , func () {
0 commit comments