Merge pull request #144 from seans3/inventory-cleanup

Removes inventory.go and inventory_test.go
This commit is contained in:
Kubernetes Prow Robot 2020-04-27 18:16:05 -07:00 committed by GitHub
commit f8fc51d5bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 488 deletions

View File

@ -1,121 +0,0 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//
// Inventory encapsulates a set of ObjMetadata structs,
// providing easy functionality to manipulate these sets.
package prune
import (
"fmt"
"sort"
"strings"
"sigs.k8s.io/cli-utils/pkg/object"
)
// Inventory encapsulates a grouping of unique Inventory
// structs. Organizes the Inventory structs with a map,
// which ensures there are no duplicates. Allows set
// operations such as merging sets and subtracting sets.
type Inventory struct {
set map[string]*object.ObjMetadata
}
// NewInventory returns a pointer to an Inventory
// struct grouping the passed Inventory items.
func NewInventory(items []*object.ObjMetadata) *Inventory {
inventory := Inventory{set: map[string]*object.ObjMetadata{}}
inventory.AddItems(items)
return &inventory
}
// GetItems returns the set of pointers to ObjMetadata
// structs.
func (is *Inventory) GetItems() []*object.ObjMetadata {
items := []*object.ObjMetadata{}
for _, item := range is.set {
items = append(items, item)
}
return items
}
// AddItems adds Inventory structs to the set which
// are not already in the set.
func (is *Inventory) AddItems(items []*object.ObjMetadata) {
for _, item := range items {
if item != nil {
is.set[item.String()] = item
}
}
}
// DeleteItem removes an ObjMetadata struct from the
// set if it exists in the set. Returns true if the
// ObjMetadata item was deleted, false if it did not exist
// in the set.
func (is *Inventory) DeleteItem(item *object.ObjMetadata) bool {
if item == nil {
return false
}
if _, ok := is.set[item.String()]; ok {
delete(is.set, item.String())
return true
}
return false
}
// Merge combines the unique set of ObjMetadata items from the
// current set with the passed "other" set, returning a new
// set or error. Returns an error if the passed set to merge
// is nil.
func (is *Inventory) Merge(other *Inventory) (*Inventory, error) {
if other == nil {
return nil, fmt.Errorf("inventory to merge is nil")
}
// Copy the current Inventory into result
result := NewInventory(is.GetItems())
result.AddItems(other.GetItems())
return result, nil
}
// Subtract removes the Inventory items in the "other" set from the
// current set, returning a new set. This does not modify the current
// set. Returns an error if the passed set to subtract is nil.
func (is *Inventory) Subtract(other *Inventory) (*Inventory, error) {
if other == nil {
return nil, fmt.Errorf("inventory to subtract is nil")
}
// Copy the current Inventory into result
result := NewInventory(is.GetItems())
// Remove each item in "other" which exists in "result"
for _, item := range other.GetItems() {
result.DeleteItem(item)
}
return result, nil
}
// Equals returns true if the "other" inventory set is the same
// as this current inventory set. Relies on the fact that the
// inventory items are sorted for the String() function.
func (is *Inventory) Equals(other *Inventory) bool {
if other == nil {
return false
}
return is.String() == other.String()
}
// String returns a string describing set of ObjMetadata structs.
func (is *Inventory) String() string {
strs := []string{}
for _, item := range is.GetItems() {
strs = append(strs, item.String())
}
sort.Strings(strs)
return strings.Join(strs, ", ")
}
// Size returns the number of ObjMetadata structs in the set.
func (is *Inventory) Size() int {
return len(is.set)
}

View File

@ -1,339 +0,0 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package prune
import (
"testing"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/cli-utils/pkg/object"
)
var inventory1 = object.ObjMetadata{
Namespace: "test-namespace",
Name: "test-inv-1",
GroupKind: schema.GroupKind{
Group: "apps",
Kind: "Deployment",
},
}
var inventory2 = object.ObjMetadata{
Namespace: "test-namespace",
Name: "test-inv-2",
GroupKind: schema.GroupKind{
Group: "",
Kind: "Pod",
},
}
var inventory3 = object.ObjMetadata{
Namespace: "test-namespace",
Name: "test-inv-3",
GroupKind: schema.GroupKind{
Group: "",
Kind: "Service",
},
}
var inventory4 = object.ObjMetadata{
Namespace: "test-namespace",
Name: "test-inv-4",
GroupKind: schema.GroupKind{
Group: "apps",
Kind: "DaemonSet",
},
}
func TestNewInventory(t *testing.T) {
tests := []struct {
items []*object.ObjMetadata
expectedStr string
expectedSize int
}{
{
items: []*object.ObjMetadata{},
expectedStr: "",
expectedSize: 0,
},
{
items: []*object.ObjMetadata{&inventory1},
expectedStr: "test-namespace_test-inv-1_apps_Deployment",
expectedSize: 1,
},
{
items: []*object.ObjMetadata{&inventory1, &inventory2},
expectedStr: "test-namespace_test-inv-1_apps_Deployment, test-namespace_test-inv-2__Pod",
expectedSize: 2,
},
}
for _, test := range tests {
invSet := NewInventory(test.items)
actualStr := invSet.String()
actualSize := invSet.Size()
if test.expectedStr != actualStr {
t.Errorf("Expected Inventory (%s), got (%s)\n", test.expectedStr, actualStr)
}
if test.expectedSize != actualSize {
t.Errorf("Expected Inventory size (%d), got (%d)\n", test.expectedSize, actualSize)
}
actualItems := invSet.GetItems()
if len(test.items) != len(actualItems) {
t.Errorf("Expected num inventory items (%d), got (%d)\n", len(test.items), len(actualItems))
}
}
}
func TestInventoryAddItems(t *testing.T) {
tests := []struct {
initialItems []*object.ObjMetadata
addItems []*object.ObjMetadata
expectedItems []*object.ObjMetadata
}{
// Adding no items to empty inventory set.
{
initialItems: []*object.ObjMetadata{},
addItems: []*object.ObjMetadata{},
expectedItems: []*object.ObjMetadata{},
},
// Adding item to empty inventory set.
{
initialItems: []*object.ObjMetadata{},
addItems: []*object.ObjMetadata{&inventory1},
expectedItems: []*object.ObjMetadata{&inventory1},
},
// Adding no items does not change the inventory set
{
initialItems: []*object.ObjMetadata{&inventory1},
addItems: []*object.ObjMetadata{},
expectedItems: []*object.ObjMetadata{&inventory1},
},
// Adding an item which alread exists does not increase size.
{
initialItems: []*object.ObjMetadata{&inventory1, &inventory2},
addItems: []*object.ObjMetadata{&inventory1},
expectedItems: []*object.ObjMetadata{&inventory1, &inventory2},
},
{
initialItems: []*object.ObjMetadata{&inventory1, &inventory2},
addItems: []*object.ObjMetadata{&inventory3, &inventory4},
expectedItems: []*object.ObjMetadata{&inventory1, &inventory2, &inventory3, &inventory4},
},
}
for _, test := range tests {
invSet := NewInventory(test.initialItems)
invSet.AddItems(test.addItems)
if len(test.expectedItems) != invSet.Size() {
t.Errorf("Expected num inventory items (%d), got (%d)\n", len(test.expectedItems), invSet.Size())
}
}
}
func TestInventoryDeleteItem(t *testing.T) {
tests := []struct {
initialItems []*object.ObjMetadata
deleteItem *object.ObjMetadata
expected bool
expectedItems []*object.ObjMetadata
}{
{
initialItems: []*object.ObjMetadata{},
deleteItem: nil,
expected: false,
expectedItems: []*object.ObjMetadata{},
},
{
initialItems: []*object.ObjMetadata{},
deleteItem: &inventory1,
expected: false,
expectedItems: []*object.ObjMetadata{},
},
{
initialItems: []*object.ObjMetadata{&inventory2},
deleteItem: &inventory1,
expected: false,
expectedItems: []*object.ObjMetadata{&inventory2},
},
{
initialItems: []*object.ObjMetadata{&inventory1},
deleteItem: &inventory1,
expected: true,
expectedItems: []*object.ObjMetadata{},
},
{
initialItems: []*object.ObjMetadata{&inventory1, &inventory2},
deleteItem: &inventory1,
expected: true,
expectedItems: []*object.ObjMetadata{&inventory2},
},
}
for _, test := range tests {
invSet := NewInventory(test.initialItems)
actual := invSet.DeleteItem(test.deleteItem)
if test.expected != actual {
t.Errorf("Expected return value (%t), got (%t)\n", test.expected, actual)
}
if len(test.expectedItems) != invSet.Size() {
t.Errorf("Expected num inventory items (%d), got (%d)\n", len(test.expectedItems), invSet.Size())
}
}
}
func TestInventoryMerge(t *testing.T) {
tests := []struct {
set1 []*object.ObjMetadata
set2 []*object.ObjMetadata
merged []*object.ObjMetadata
}{
{
set1: []*object.ObjMetadata{},
set2: []*object.ObjMetadata{},
merged: []*object.ObjMetadata{},
},
{
set1: []*object.ObjMetadata{},
set2: []*object.ObjMetadata{&inventory1},
merged: []*object.ObjMetadata{&inventory1},
},
{
set1: []*object.ObjMetadata{&inventory1},
set2: []*object.ObjMetadata{},
merged: []*object.ObjMetadata{&inventory1},
},
{
set1: []*object.ObjMetadata{&inventory1, &inventory2},
set2: []*object.ObjMetadata{&inventory1},
merged: []*object.ObjMetadata{&inventory1, &inventory2},
},
{
set1: []*object.ObjMetadata{&inventory1, &inventory2},
set2: []*object.ObjMetadata{&inventory1, &inventory2},
merged: []*object.ObjMetadata{&inventory1, &inventory2},
},
{
set1: []*object.ObjMetadata{&inventory1, &inventory2},
set2: []*object.ObjMetadata{&inventory3, &inventory4},
merged: []*object.ObjMetadata{&inventory1, &inventory2, &inventory3, &inventory4},
},
}
for _, test := range tests {
invSet1 := NewInventory(test.set1)
invSet2 := NewInventory(test.set2)
expected := NewInventory(test.merged)
merged, _ := invSet1.Merge(invSet2)
if expected.Size() != merged.Size() {
t.Errorf("Expected merged inventory set size (%d), got (%d)\n", expected.Size(), merged.Size())
}
}
}
func TestInventorySubtract(t *testing.T) {
tests := []struct {
initialItems []*object.ObjMetadata
subtractItems []*object.ObjMetadata
expected []*object.ObjMetadata
}{
{
initialItems: []*object.ObjMetadata{},
subtractItems: []*object.ObjMetadata{},
expected: []*object.ObjMetadata{},
},
{
initialItems: []*object.ObjMetadata{},
subtractItems: []*object.ObjMetadata{&inventory1},
expected: []*object.ObjMetadata{},
},
{
initialItems: []*object.ObjMetadata{&inventory1},
subtractItems: []*object.ObjMetadata{},
expected: []*object.ObjMetadata{&inventory1},
},
{
initialItems: []*object.ObjMetadata{&inventory1, &inventory2},
subtractItems: []*object.ObjMetadata{&inventory1},
expected: []*object.ObjMetadata{&inventory2},
},
{
initialItems: []*object.ObjMetadata{&inventory1, &inventory2},
subtractItems: []*object.ObjMetadata{&inventory1, &inventory2},
expected: []*object.ObjMetadata{},
},
{
initialItems: []*object.ObjMetadata{&inventory1, &inventory2},
subtractItems: []*object.ObjMetadata{&inventory3, &inventory4},
expected: []*object.ObjMetadata{&inventory1, &inventory2},
},
}
for _, test := range tests {
invInitialItems := NewInventory(test.initialItems)
invSubtractItems := NewInventory(test.subtractItems)
expected := NewInventory(test.expected)
actual, _ := invInitialItems.Subtract(invSubtractItems)
if expected.Size() != actual.Size() {
t.Errorf("Expected subtracted inventory set size (%d), got (%d)\n", expected.Size(), actual.Size())
}
}
}
func TestInventoryEquals(t *testing.T) {
tests := []struct {
set1 []*object.ObjMetadata
set2 []*object.ObjMetadata
isEqual bool
}{
{
set1: []*object.ObjMetadata{},
set2: []*object.ObjMetadata{&inventory1},
isEqual: false,
},
{
set1: []*object.ObjMetadata{&inventory1},
set2: []*object.ObjMetadata{},
isEqual: false,
},
{
set1: []*object.ObjMetadata{&inventory1, &inventory2},
set2: []*object.ObjMetadata{&inventory1},
isEqual: false,
},
{
set1: []*object.ObjMetadata{&inventory1, &inventory2},
set2: []*object.ObjMetadata{&inventory3, &inventory4},
isEqual: false,
},
// Empty sets are equal.
{
set1: []*object.ObjMetadata{},
set2: []*object.ObjMetadata{},
isEqual: true,
},
{
set1: []*object.ObjMetadata{&inventory1},
set2: []*object.ObjMetadata{&inventory1},
isEqual: true,
},
// Ordering of the inventory items does not matter for equality.
{
set1: []*object.ObjMetadata{&inventory1, &inventory2},
set2: []*object.ObjMetadata{&inventory2, &inventory1},
isEqual: true,
},
}
for _, test := range tests {
invSet1 := NewInventory(test.set1)
invSet2 := NewInventory(test.set2)
if !invSet1.Equals(invSet2) && test.isEqual {
t.Errorf("Expected equal inventory sets; got unequal (%s)/(%s)\n", invSet1, invSet2)
}
if invSet1.Equals(invSet2) && !test.isEqual {
t.Errorf("Expected inequal inventory sets; got equal (%s)/(%s)\n", invSet1, invSet2)
}
}
}

View File

@ -161,21 +161,27 @@ func infoToObjMetadata(info *resource.Info) (*object.ObjMetadata, error) {
return object.CreateObjMetadata(info.Namespace, info.Name, gk)
}
// unionPastInventory takes a set of grouping objects (infos), returning the
// union of the objects referenced by these grouping objects as an
// Inventory. Returns an error if any of the passed objects are not
// grouping objects, or if unable to retrieve the inventory from any
// grouping object.
func unionPastInventory(infos []*resource.Info) (*Inventory, error) {
inventorySet := NewInventory([]*object.ObjMetadata{})
// unionPastObjs takes a set of inventory objects (infos), returning the
// union of the objects referenced by these inventory objects.
// Returns an error if any of the passed objects are not inventory
// objects, or if unable to retrieve the referenced objects from any
// inventory object.
func unionPastObjs(infos []*resource.Info) ([]object.ObjMetadata, error) {
objSet := map[string]object.ObjMetadata{}
for _, info := range infos {
inv, err := RetrieveInventoryFromGroupingObj([]*resource.Info{info})
objs, err := RetrieveInventoryFromGroupingObj([]*resource.Info{info})
if err != nil {
return nil, err
}
inventorySet.AddItems(inv)
for _, obj := range objs {
objSet[(*obj).String()] = *obj // De-duping
}
}
return inventorySet, nil
pastObjs := make([]object.ObjMetadata, 0, len(objSet))
for _, obj := range objSet {
pastObjs = append(pastObjs, obj)
}
return pastObjs, nil
}
// Prune deletes the set of resources which were previously applied
@ -195,18 +201,18 @@ func (po *PruneOptions) Prune(currentObjects []*resource.Info, eventChannel chan
if err != nil {
return err
}
prevObjs, err := unionPastInventory(pastGroupingInfos)
pastObjs, err := unionPastObjs(pastGroupingInfos)
if err != nil {
return err
}
// Iterate through set of all previously applied objects.
for _, inv := range prevObjs.GetItems() {
mapping, err := po.mapper.RESTMapping(inv.GroupKind)
for _, past := range pastObjs {
mapping, err := po.mapper.RESTMapping(past.GroupKind)
if err != nil {
return err
}
namespacedClient := po.client.Resource(mapping.Resource).Namespace(inv.Namespace)
obj, err := namespacedClient.Get(inv.Name, metav1.GetOptions{})
namespacedClient := po.client.Resource(mapping.Resource).Namespace(past.Namespace)
obj, err := namespacedClient.Get(past.Name, metav1.GetOptions{})
if err != nil {
// Object not found -- skip it and move to the next object.
if apierrors.IsNotFound(err) {
@ -226,7 +232,7 @@ func (po *PruneOptions) Prune(currentObjects []*resource.Info, eventChannel chan
continue
}
if !po.DryRun {
err = namespacedClient.Delete(inv.Name, &metav1.DeleteOptions{})
err = namespacedClient.Delete(past.Name, &metav1.DeleteOptions{})
if err != nil {
return err
}

View File

@ -118,59 +118,74 @@ func createGroupingInfo(name string, children ...*resource.Info) *resource.Info
return groupingInfo
}
func TestUnionPastInventory(t *testing.T) {
func TestUnionPastObjs(t *testing.T) {
tests := map[string]struct {
groupingInfos []*resource.Info
expected []*object.ObjMetadata
expected []object.ObjMetadata
}{
"Empty grouping objects = empty inventory": {
groupingInfos: []*resource.Info{},
expected: []*object.ObjMetadata{},
expected: []object.ObjMetadata{},
},
"No children in grouping object, equals no inventory": {
groupingInfos: []*resource.Info{createGroupingInfo("test-1")},
expected: []*object.ObjMetadata{},
expected: []object.ObjMetadata{},
},
"Grouping object with Pod1 returns inventory with Pod1": {
groupingInfos: []*resource.Info{createGroupingInfo("test-1", pod1Info)},
expected: []*object.ObjMetadata{pod1Inv},
expected: []object.ObjMetadata{*pod1Inv},
},
"Grouping object with three pods returns inventory with three pods": {
groupingInfos: []*resource.Info{
createGroupingInfo("test-1", pod1Info, pod2Info, pod3Info),
},
expected: []*object.ObjMetadata{pod1Inv, pod2Inv, pod3Inv},
expected: []object.ObjMetadata{*pod1Inv, *pod2Inv, *pod3Inv},
},
"Two grouping objects with different pods returns inventory with both pods": {
groupingInfos: []*resource.Info{
createGroupingInfo("test-1", pod1Info),
createGroupingInfo("test-2", pod2Info),
},
expected: []*object.ObjMetadata{pod1Inv, pod2Inv},
expected: []object.ObjMetadata{*pod1Inv, *pod2Inv},
},
"Two grouping objects with overlapping pods returns set of pods": {
groupingInfos: []*resource.Info{
createGroupingInfo("test-1", pod1Info, pod2Info),
createGroupingInfo("test-2", pod2Info, pod3Info),
},
expected: []*object.ObjMetadata{pod1Inv, pod2Inv, pod3Inv},
expected: []object.ObjMetadata{*pod1Inv, *pod2Inv, *pod3Inv},
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
actual, err := unionPastInventory(tc.groupingInfos)
expected := NewInventory(tc.expected)
actual, err := unionPastObjs(tc.groupingInfos)
if err != nil {
t.Errorf("Unexpected error received: %s\n", err)
}
if !expected.Equals(actual) {
t.Errorf("Expected inventory (%s), got (%s)\n", expected, actual)
if len(tc.expected) != len(actual) {
t.Fatalf("Expected (%d) objects, got (%d)\n", len(tc.expected), len(actual))
}
for _, expectedObj := range tc.expected {
if !objInArray(expectedObj, actual) {
t.Fatalf("Expected object (%s), but not found\n", expectedObj)
}
}
})
}
}
// objInArray is a helper function that returns true if passed obj
// is in array of objects; false otherwise.
func objInArray(obj object.ObjMetadata, arr []object.ObjMetadata) bool {
for _, a := range arr {
if a == obj {
return true
}
}
return false
}
func TestPrune(t *testing.T) {
tests := map[string]struct {
// pastInfos/currentInfos do NOT contain the grouping object.