Skip to content
This repository was archived by the owner on Jun 26, 2023. It is now read-only.

Commit d6d4a2d

Browse files
committed
Don't delete subns if anchor has conflict
See issue #1149. This affects the ability to turn a subnamespace into a full namespace. If you remove the subnamespaceOf annotation, move the namespace to a new parent, and *then* clean up the anchor, everything's ok. But if you delete the anchor _before_ moving the namespace, the namespace gets deleted, which is clearly wrong. This change is the minimal safe change needed to fix this in v0.5. In v0.6, we should restructure these functions to be more heavily based on the explicit state of the anchor, not the implied state. Tested: new e2e test fails (hangs, actually) without this change and passes with it. All e2e tests pass on GKE 1.17.
1 parent 6f44c1c commit d6d4a2d

File tree

2 files changed

+55
-5
lines changed

2 files changed

+55
-5
lines changed

incubator/hnc/internal/reconcilers/anchor.go

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ package reconcilers
1717

1818
import (
1919
"context"
20+
"errors"
2021

2122
"github.com/go-logr/logr"
2223
corev1 "k8s.io/api/core/v1"
23-
"k8s.io/apimachinery/pkg/api/errors"
24+
apierrors "k8s.io/apimachinery/pkg/api/errors"
2425
"k8s.io/apimachinery/pkg/types"
2526
ctrl "sigs.k8s.io/controller-runtime"
2627
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -65,7 +66,7 @@ func (r *AnchorReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
6566
// purged.
6667
inst, err := r.getInstance(ctx, pnm, nm)
6768
if err != nil {
68-
if errors.IsNotFound(err) {
69+
if apierrors.IsNotFound(err) {
6970
return ctrl.Result{}, nil
7071
}
7172
return ctrl.Result{}, err
@@ -118,13 +119,19 @@ func (r *AnchorReconciler) onDeleting(ctx context.Context, log logr.Logger, inst
118119
return false, err
119120
}
120121

122+
// Update the state of the anchor
123+
//
124+
// TODO: call this in the parent function after v0.5 so there's no special code path for the
125+
// onDeleting case.
126+
r.updateState(log, inst, snsInst)
127+
121128
log.Info("The anchor is being deleted", "deletingCRD", deletingCRD)
122129
switch {
123130
case len(inst.ObjectMeta.Finalizers) == 0:
124131
// We've finished processing this, nothing to do.
125132
log.Info("Do nothing since the finalizers are already gone.")
126133
return true, nil
127-
case r.shouldDeleteSubns(inst, snsInst, deletingCRD):
134+
case r.shouldDeleteSubns(log, inst, snsInst, deletingCRD):
128135
// The subnamespace is not already being deleted but it allows cascadingDelete or it's a leaf.
129136
// Delete the subnamespace, unless the CRD is being deleted, in which case, we want to leave the
130137
// namespaces alone.
@@ -143,7 +150,9 @@ func (r *AnchorReconciler) onDeleting(ctx context.Context, log logr.Logger, inst
143150

144151
// shouldDeleteSubns returns true if the namespace still exists and it is a leaf
145152
// subnamespace or it allows cascading delete unless the CRD is being deleted.
146-
func (r *AnchorReconciler) shouldDeleteSubns(inst *api.SubnamespaceAnchor, nsInst *corev1.Namespace, deletingCRD bool) bool {
153+
//
154+
// TODO: fix comment post-v0.5
155+
func (r *AnchorReconciler) shouldDeleteSubns(log logr.Logger, inst *api.SubnamespaceAnchor, nsInst *corev1.Namespace, deletingCRD bool) bool {
147156
r.forest.Lock()
148157
defer r.forest.Unlock()
149158

@@ -152,6 +161,27 @@ func (r *AnchorReconciler) shouldDeleteSubns(inst *api.SubnamespaceAnchor, nsIns
152161
return false
153162
}
154163

164+
// If the anchor isn't in the "ok" state, don't delete the namespace. If it's "conflict," we
165+
// *definitely* don't want to delete it; if it's missing (and possibly being created), then there
166+
// isn't really a good option but we'll eventually put a condition on the namespace so it's not a
167+
// big deal.
168+
//
169+
// TODO: much of the remaining portion of this function can probably be replaced by this switch
170+
// statement. In v0.5, we're playing it safe so I won't modify the rest of the function, but in
171+
// v0.6 we should restructure.
172+
switch inst.Status.State {
173+
case api.Missing:
174+
return false
175+
case api.Conflict:
176+
return false
177+
case api.Ok:
178+
// keep going...
179+
default:
180+
log.Error(errors.New("Illegal state"), "Unknown state", "state", inst.Status.State)
181+
// Stay on the safe side - don't delete
182+
return false
183+
}
184+
155185
cnm := inst.Name
156186
pnm := inst.Namespace
157187
cns := r.forest.Get(cnm)
@@ -259,7 +289,7 @@ func (r *AnchorReconciler) getNamespace(ctx context.Context, nm string) (*corev1
259289
ns := &corev1.Namespace{}
260290
nnm := types.NamespacedName{Name: nm}
261291
if err := r.Get(ctx, nnm, ns); err != nil {
262-
if !errors.IsNotFound(err) {
292+
if !apierrors.IsNotFound(err) {
263293
return nil, err
264294
}
265295
return &corev1.Namespace{}, nil

incubator/hnc/test/e2e/issues_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,26 @@ var _ = Describe("Issues", func() {
3131
nsSubSub2, nsSubChild, nsSubSubChild)
3232
})
3333

34+
It("Should not delete full namespace when a faulty anchor is deleted - issue #1149", func() {
35+
// Setup
36+
MustRun("kubectl create ns", nsParent)
37+
MustRun("kubectl hns create", nsChild, "-n", nsParent)
38+
39+
// Wait for subns
40+
MustRun("kubectl describe ns", nsChild)
41+
42+
// Remove annotation
43+
MustRun("kubectl annotate ns", nsChild, "hnc.x-k8s.io/subnamespaceOf-")
44+
RunShouldNotContain("subnamespaceOf", 1, "kubectl get -oyaml ns", nsChild)
45+
46+
// Delete anchor
47+
MustRun("kubectl delete subns", nsChild, "-n", nsParent)
48+
MustNotRun("kubectl get subns", nsChild, "-n", nsParent)
49+
50+
// Verify that namespace still exists
51+
MustRun("kubectl describe ns", nsChild)
52+
})
53+
3454
// Note that this was never actually a problem (only subnamespaces were affected) but it seems
3555
// like a good thing to test anyway.
3656
It("Should delete full namespaces with propagated objects - issue #1130", func() {

0 commit comments

Comments
 (0)