-
Notifications
You must be signed in to change notification settings - Fork 78
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #369 from seans3/apply-prune-filter
Refactor prune/delete using ValidationFilter interface
- Loading branch information
Showing
18 changed files
with
621 additions
and
205 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// Copyright 2021 The Kubernetes Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package filter | ||
|
||
import ( | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"k8s.io/apimachinery/pkg/util/sets" | ||
) | ||
|
||
// CurrentUIDFilter implements ValidationFilter interface to determine | ||
// if an object should not be pruned (deleted) because it has recently | ||
// been applied. | ||
type CurrentUIDFilter struct { | ||
CurrentUIDs sets.String | ||
} | ||
|
||
// Name returns a filter identifier for logging. | ||
func (cuf CurrentUIDFilter) Name() string { | ||
return "CurrentUIDFilter" | ||
} | ||
|
||
// Filter returns true if the passed object should NOT be pruned (deleted) | ||
// because the it is a namespace that objects still reside in; otherwise | ||
// returns false. This filter should not be added to the list of filters | ||
// for "destroying", since every object is being deletet. Never returns an error. | ||
func (cuf CurrentUIDFilter) Filter(obj *unstructured.Unstructured) (bool, error) { | ||
uid := string(obj.GetUID()) | ||
if cuf.CurrentUIDs.Has(uid) { | ||
return true, nil | ||
} | ||
return false, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// Copyright 2019 The Kubernetes Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package filter | ||
|
||
import ( | ||
"testing" | ||
|
||
"k8s.io/apimachinery/pkg/types" | ||
"k8s.io/apimachinery/pkg/util/sets" | ||
) | ||
|
||
func TestCurrentUIDFilter(t *testing.T) { | ||
tests := map[string]struct { | ||
filterUIDs sets.String | ||
objUID string | ||
filtered bool | ||
}{ | ||
"Empty filter UIDs, object is not filtered": { | ||
filterUIDs: sets.NewString(), | ||
objUID: "bar", | ||
filtered: false, | ||
}, | ||
"Empty object UID, object is not filtered": { | ||
filterUIDs: sets.NewString("foo"), | ||
objUID: "", | ||
filtered: false, | ||
}, | ||
"Object UID not in filter UID set, object is not filtered": { | ||
filterUIDs: sets.NewString("foo", "baz"), | ||
objUID: "bar", | ||
filtered: false, | ||
}, | ||
"Object UID is in filter UID set, object is filtered": { | ||
filterUIDs: sets.NewString("foo"), | ||
objUID: "foo", | ||
filtered: true, | ||
}, | ||
"Object UID is among several filter UIDs, object is filtered": { | ||
filterUIDs: sets.NewString("foo", "bar", "baz"), | ||
objUID: "foo", | ||
filtered: true, | ||
}, | ||
} | ||
|
||
for name, tc := range tests { | ||
t.Run(name, func(t *testing.T) { | ||
filter := CurrentUIDFilter{ | ||
CurrentUIDs: tc.filterUIDs, | ||
} | ||
obj := defaultObj.DeepCopy() | ||
obj.SetUID(types.UID(tc.objUID)) | ||
actual, err := filter.Filter(obj) | ||
if err != nil { | ||
t.Fatalf("CurrentUIDFilter unexpected error (%s)", err) | ||
} | ||
if tc.filtered != actual { | ||
t.Errorf("CurrentUIDFilter expected filter (%t), got (%t)", tc.filtered, actual) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Copyright 2021 The Kubernetes Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package filter | ||
|
||
import ( | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
) | ||
|
||
// ValidationFilter interface decouples apply/prune validation | ||
// from the concrete structs used for validation. The apply/prune | ||
// functionality will run validation filters to remove objects | ||
// which should not be applied or pruned. | ||
type ValidationFilter interface { | ||
// Name returns a filter name (usually for logging). | ||
Name() string | ||
// Filter returns true if validation fails or an error. | ||
Filter(obj *unstructured.Unstructured) (bool, error) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// Copyright 2021 The Kubernetes Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package filter | ||
|
||
import ( | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"sigs.k8s.io/cli-utils/pkg/inventory" | ||
) | ||
|
||
// InventoryPolicyFilter implements ValidationFilter interface to determine | ||
// if an object should be pruned (deleted) because of the InventoryPolicy | ||
// and if the objects owning inventory identifier matchs the inventory id. | ||
type InventoryPolicyFilter struct { | ||
Inv inventory.InventoryInfo | ||
InvPolicy inventory.InventoryPolicy | ||
} | ||
|
||
// Name returns a filter identifier for logging. | ||
func (ipf InventoryPolicyFilter) Name() string { | ||
return "InventoryPolictyFilter" | ||
} | ||
|
||
// Filter returns true if the passed object should NOT be pruned (deleted) | ||
// because the "prevent remove" annotation is present; otherwise returns | ||
// false. Never returns an error. | ||
func (ipf InventoryPolicyFilter) Filter(obj *unstructured.Unstructured) (bool, error) { | ||
// Check the inventory id "match" and the adopt policy to determine | ||
// if an object should be pruned (deleted). | ||
if !inventory.CanPrune(ipf.Inv, obj, ipf.InvPolicy) { | ||
return true, nil | ||
} | ||
return false, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// Copyright 2019 The Kubernetes Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package filter | ||
|
||
import ( | ||
"testing" | ||
|
||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"sigs.k8s.io/cli-utils/pkg/common" | ||
"sigs.k8s.io/cli-utils/pkg/inventory" | ||
) | ||
|
||
var inventoryObj = &unstructured.Unstructured{ | ||
Object: map[string]interface{}{ | ||
"apiVersion": "v1", | ||
"kind": "ConfigMap", | ||
"metadata": map[string]interface{}{ | ||
"name": "inventory-name", | ||
"namespace": "inventory-namespace", | ||
}, | ||
}, | ||
} | ||
|
||
func TestInventoryPolicyFilter(t *testing.T) { | ||
tests := map[string]struct { | ||
inventoryID string | ||
objInventoryID string | ||
policy inventory.InventoryPolicy | ||
filtered bool | ||
}{ | ||
"inventory and object ids match, not filtered": { | ||
inventoryID: "foo", | ||
objInventoryID: "foo", | ||
policy: inventory.InventoryPolicyMustMatch, | ||
filtered: false, | ||
}, | ||
"inventory and object ids match and adopt, not filtered": { | ||
inventoryID: "foo", | ||
objInventoryID: "foo", | ||
policy: inventory.AdoptIfNoInventory, | ||
filtered: false, | ||
}, | ||
"inventory and object ids do no match and policy must match, filtered": { | ||
inventoryID: "foo", | ||
objInventoryID: "bar", | ||
policy: inventory.InventoryPolicyMustMatch, | ||
filtered: true, | ||
}, | ||
"inventory and object ids do no match and adopt if no inventory, filtered": { | ||
inventoryID: "foo", | ||
objInventoryID: "bar", | ||
policy: inventory.AdoptIfNoInventory, | ||
filtered: true, | ||
}, | ||
"inventory and object ids do no match and adopt all, not filtered": { | ||
inventoryID: "foo", | ||
objInventoryID: "bar", | ||
policy: inventory.AdoptAll, | ||
filtered: false, | ||
}, | ||
"object id empty and adopt all, not filtered": { | ||
inventoryID: "foo", | ||
objInventoryID: "", | ||
policy: inventory.AdoptAll, | ||
filtered: false, | ||
}, | ||
"object id empty and policy must match, filtered": { | ||
inventoryID: "foo", | ||
objInventoryID: "", | ||
policy: inventory.InventoryPolicyMustMatch, | ||
filtered: true, | ||
}, | ||
} | ||
|
||
for name, tc := range tests { | ||
t.Run(name, func(t *testing.T) { | ||
invIDLabel := map[string]string{ | ||
common.InventoryLabel: tc.inventoryID, | ||
} | ||
invObj := inventoryObj.DeepCopy() | ||
invObj.SetLabels(invIDLabel) | ||
filter := InventoryPolicyFilter{ | ||
Inv: inventory.WrapInventoryInfoObj(invObj), | ||
InvPolicy: tc.policy, | ||
} | ||
objIDAnnotation := map[string]string{ | ||
"config.k8s.io/owning-inventory": tc.objInventoryID, | ||
} | ||
obj := defaultObj.DeepCopy() | ||
obj.SetAnnotations(objIDAnnotation) | ||
actual, err := filter.Filter(obj) | ||
if err != nil { | ||
t.Fatalf("InventoryPolicyFilter unexpected error (%s)", err) | ||
} | ||
if tc.filtered != actual { | ||
t.Errorf("InventoryPolicyFilter expected filter (%t), got (%t)", tc.filtered, actual) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Copyright 2021 The Kubernetes Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package filter | ||
|
||
import ( | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"k8s.io/apimachinery/pkg/util/sets" | ||
"sigs.k8s.io/cli-utils/pkg/object" | ||
) | ||
|
||
// LocalNamespacesFilter encapsulates the set of namespaces | ||
// that are currently in use. Used to ensure we do not delete | ||
// namespaces with currently applied objects in them. | ||
type LocalNamespacesFilter struct { | ||
LocalNamespaces sets.String | ||
} | ||
|
||
// Name returns a filter identifier for logging. | ||
func (lnf LocalNamespacesFilter) Name() string { | ||
return "LocalNamespacesFilter" | ||
} | ||
|
||
// Filter returns true if the passed object should NOT be pruned (deleted) | ||
// because the it is a namespace that objects still reside in; otherwise | ||
// returns false. This filter should not be added to the list of filters | ||
// for "destroying", since every object is being delete. Never returns an error. | ||
func (lnf LocalNamespacesFilter) Filter(obj *unstructured.Unstructured) (bool, error) { | ||
id := object.UnstructuredToObjMeta(obj) | ||
if id.GroupKind == object.CoreV1Namespace.GroupKind() && | ||
lnf.LocalNamespaces.Has(id.Name) { | ||
return true, nil | ||
} | ||
return false, nil | ||
} |
Oops, something went wrong.