mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 17:30:00 +00:00
Add GC unit tests
Adds unit tests covering the problematic scenarios identified around conflicting data in child owner references Before After package level 51% 68% garbagecollector.go 60% 75% graph_builder.go 50% 81% graph.go 50% 68% Added/improved coverage of key functions that had lacking unit test coverage: * attemptToDeleteWorker * attemptToDeleteItem * processGraphChanges (added coverage of all added code)
This commit is contained in:
parent
603a0b016e
commit
e491c3bc70
@ -56,15 +56,18 @@ go_test(
|
|||||||
srcs = [
|
srcs = [
|
||||||
"dump_test.go",
|
"dump_test.go",
|
||||||
"garbagecollector_test.go",
|
"garbagecollector_test.go",
|
||||||
|
"graph_builder_test.go",
|
||||||
],
|
],
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/api/legacyscheme:go_default_library",
|
"//pkg/api/legacyscheme:go_default_library",
|
||||||
"//pkg/apis/core/install:go_default_library",
|
"//pkg/apis/core/install:go_default_library",
|
||||||
|
"//pkg/controller/garbagecollector/metaonly:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/meta/testrestmapper:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/meta/testrestmapper:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/json:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/json:go_default_library",
|
||||||
@ -75,12 +78,18 @@ go_test(
|
|||||||
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/metadata:go_default_library",
|
"//staging/src/k8s.io/client-go/metadata:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/metadata/fake:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/metadata/metadatainformer:go_default_library",
|
"//staging/src/k8s.io/client-go/metadata/metadatainformer:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/rest:go_default_library",
|
"//staging/src/k8s.io/client-go/rest:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/testing:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
|
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
|
||||||
"//staging/src/k8s.io/controller-manager/pkg/informerfactory:go_default_library",
|
"//staging/src/k8s.io/controller-manager/pkg/informerfactory:go_default_library",
|
||||||
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
|
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
|
||||||
|
"//vendor/github.com/golang/groupcache/lru:go_default_library",
|
||||||
|
"//vendor/github.com/google/go-cmp/cmp:go_default_library",
|
||||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||||
|
"//vendor/golang.org/x/time/rate:go_default_library",
|
||||||
"//vendor/gonum.org/v1/gonum/graph:go_default_library",
|
"//vendor/gonum.org/v1/gonum/graph:go_default_library",
|
||||||
"//vendor/gonum.org/v1/gonum/graph/simple:go_default_library",
|
"//vendor/gonum.org/v1/gonum/graph/simple:go_default_library",
|
||||||
"//vendor/k8s.io/utils/pointer:go_default_library",
|
"//vendor/k8s.io/utils/pointer:go_default_library",
|
||||||
|
File diff suppressed because it is too large
Load Diff
212
pkg/controller/garbagecollector/graph_builder_test.go
Normal file
212
pkg/controller/garbagecollector/graph_builder_test.go
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package garbagecollector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetAlternateOwnerIdentity(t *testing.T) {
|
||||||
|
ns1child1 := makeID("v1", "Child", "ns1", "child1", "childuid11")
|
||||||
|
ns1child2 := makeID("v1", "Child", "ns1", "child2", "childuid12")
|
||||||
|
|
||||||
|
ns2child1 := makeID("v1", "Child", "ns2", "child1", "childuid21")
|
||||||
|
|
||||||
|
clusterchild1 := makeID("v1", "Child", "", "child1", "childuidc1")
|
||||||
|
|
||||||
|
var (
|
||||||
|
nsabsentparentns1 = makeID("v1", "NSParent", "ns1", "parentname", "parentuid")
|
||||||
|
nsabsentparentns2 = makeID("v1", "NSParent", "ns2", "parentname", "parentuid")
|
||||||
|
|
||||||
|
nsabsentparent_version = makeID("xx", "NSParent", "ns1", "parentname", "parentuid")
|
||||||
|
nsabsentparent_kind = makeID("v1", "xxxxxxxx", "ns1", "parentname", "parentuid")
|
||||||
|
nsabsentparent_name = makeID("v1", "NSParent", "ns1", "xxxxxxxxxx", "parentuid")
|
||||||
|
|
||||||
|
clusterabsentparent = makeID("v1", "ClusterParent", "", "parentname", "parentuid")
|
||||||
|
clusterabsentparent_version = makeID("xx", "ClusterParent", "", "parentname", "parentuid")
|
||||||
|
clusterabsentparent_kind = makeID("v1", "xxxxxxxxxxxxx", "", "parentname", "parentuid")
|
||||||
|
clusterabsentparent_name = makeID("v1", "ClusterParent", "", "xxxxxxxxxx", "parentuid")
|
||||||
|
|
||||||
|
clusterabsentparent_ns1_version = makeID("xx", "ClusterParent", "ns1", "parentname", "parentuid")
|
||||||
|
clusterabsentparent_ns1_kind = makeID("v1", "xxxxxxxxxxxxx", "ns1", "parentname", "parentuid")
|
||||||
|
)
|
||||||
|
|
||||||
|
orderedNamespacedReferences := []objectReference{
|
||||||
|
makeID("v1", "kind", "ns1", "name", "uid"),
|
||||||
|
makeID("v2", "kind", "ns1", "name", "uid"),
|
||||||
|
makeID("v3", "kind", "ns1", "name", "uid"),
|
||||||
|
makeID("v4", "kind", "ns1", "name", "uid"),
|
||||||
|
makeID("v5", "kind", "ns1", "name", "uid"),
|
||||||
|
}
|
||||||
|
orderedClusterReferences := []objectReference{
|
||||||
|
makeID("v1", "kind", "", "name", "uid"),
|
||||||
|
makeID("v2", "kind", "", "name", "uid"),
|
||||||
|
makeID("v3", "kind", "", "name", "uid"),
|
||||||
|
makeID("v4", "kind", "", "name", "uid"),
|
||||||
|
makeID("v5", "kind", "", "name", "uid"),
|
||||||
|
}
|
||||||
|
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
deps []*node
|
||||||
|
verifiedAbsent objectReference
|
||||||
|
expectedAlternate *objectReference
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "namespaced alternate version",
|
||||||
|
deps: []*node{
|
||||||
|
makeNode(ns1child1, withOwners(nsabsentparentns1)),
|
||||||
|
makeNode(ns1child2, withOwners(nsabsentparent_version)),
|
||||||
|
},
|
||||||
|
verifiedAbsent: nsabsentparentns1,
|
||||||
|
expectedAlternate: &nsabsentparent_version, // switch to alternate version
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "namespaced alternate kind",
|
||||||
|
deps: []*node{
|
||||||
|
makeNode(ns1child1, withOwners(nsabsentparentns1)),
|
||||||
|
makeNode(ns1child2, withOwners(nsabsentparent_kind)),
|
||||||
|
},
|
||||||
|
verifiedAbsent: nsabsentparentns1,
|
||||||
|
expectedAlternate: &nsabsentparent_kind, // switch to alternate kind
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "namespaced alternate namespace",
|
||||||
|
deps: []*node{
|
||||||
|
makeNode(ns1child1, withOwners(nsabsentparentns1)),
|
||||||
|
makeNode(ns2child1, withOwners(nsabsentparentns2)),
|
||||||
|
},
|
||||||
|
verifiedAbsent: nsabsentparentns1,
|
||||||
|
expectedAlternate: &nsabsentparentns2, // switch to alternate namespace
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "namespaced alternate name",
|
||||||
|
deps: []*node{
|
||||||
|
makeNode(ns1child1, withOwners(nsabsentparentns1)),
|
||||||
|
makeNode(ns1child1, withOwners(nsabsentparent_name)),
|
||||||
|
},
|
||||||
|
verifiedAbsent: nsabsentparentns1,
|
||||||
|
expectedAlternate: &nsabsentparent_name, // switch to alternate name
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "cluster alternate version",
|
||||||
|
deps: []*node{
|
||||||
|
makeNode(ns1child1, withOwners(clusterabsentparent)),
|
||||||
|
makeNode(ns1child2, withOwners(clusterabsentparent_version)),
|
||||||
|
},
|
||||||
|
verifiedAbsent: clusterabsentparent,
|
||||||
|
expectedAlternate: &clusterabsentparent_ns1_version, // switch to alternate version, namespaced to new dependent since we don't know the version is cluster-scoped
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cluster alternate kind",
|
||||||
|
deps: []*node{
|
||||||
|
makeNode(ns1child1, withOwners(clusterabsentparent)),
|
||||||
|
makeNode(ns1child2, withOwners(clusterabsentparent_kind)),
|
||||||
|
},
|
||||||
|
verifiedAbsent: clusterabsentparent,
|
||||||
|
expectedAlternate: &clusterabsentparent_ns1_kind, // switch to alternate kind, namespaced to new dependent since we don't know the new kind is cluster-scoped
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cluster alternate namespace",
|
||||||
|
deps: []*node{
|
||||||
|
makeNode(ns1child1, withOwners(clusterabsentparent)),
|
||||||
|
makeNode(ns2child1, withOwners(clusterabsentparent)),
|
||||||
|
},
|
||||||
|
verifiedAbsent: clusterabsentparent,
|
||||||
|
expectedAlternate: nil, // apiVersion/kind verified cluster-scoped, namespace delta ignored, no alternates found
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cluster alternate name",
|
||||||
|
deps: []*node{
|
||||||
|
makeNode(ns1child1, withOwners(clusterabsentparent)),
|
||||||
|
makeNode(ns1child1, withOwners(clusterabsentparent_name)),
|
||||||
|
},
|
||||||
|
verifiedAbsent: clusterabsentparent,
|
||||||
|
expectedAlternate: &clusterabsentparent_name, // switch to alternate name, apiVersion/kind verified cluster-scoped, namespace dropped
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "namespaced ref from namespaced child returns first if absent is sorted last",
|
||||||
|
deps: []*node{
|
||||||
|
makeNode(ns1child1, withOwners(orderedNamespacedReferences...)),
|
||||||
|
},
|
||||||
|
verifiedAbsent: orderedNamespacedReferences[len(orderedNamespacedReferences)-1],
|
||||||
|
expectedAlternate: &orderedNamespacedReferences[0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "namespaced ref from namespaced child returns next after absent",
|
||||||
|
deps: []*node{
|
||||||
|
makeNode(ns1child1, withOwners(orderedNamespacedReferences...)),
|
||||||
|
},
|
||||||
|
verifiedAbsent: orderedNamespacedReferences[len(orderedNamespacedReferences)-2],
|
||||||
|
expectedAlternate: &orderedNamespacedReferences[len(orderedNamespacedReferences)-1],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "cluster ref from cluster child returns first if absent is sorted last",
|
||||||
|
deps: []*node{
|
||||||
|
makeNode(clusterchild1, withOwners(orderedClusterReferences...)),
|
||||||
|
},
|
||||||
|
verifiedAbsent: orderedClusterReferences[len(orderedClusterReferences)-1],
|
||||||
|
expectedAlternate: &orderedClusterReferences[0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cluster ref from cluster child returns next after absent",
|
||||||
|
deps: []*node{
|
||||||
|
makeNode(clusterchild1, withOwners(orderedClusterReferences...)),
|
||||||
|
},
|
||||||
|
verifiedAbsent: orderedClusterReferences[len(orderedClusterReferences)-2],
|
||||||
|
expectedAlternate: &orderedClusterReferences[len(orderedClusterReferences)-1],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "ignore unrelated",
|
||||||
|
deps: []*node{
|
||||||
|
makeNode(ns1child1, withOwners(clusterabsentparent, makeID("v1", "Parent", "ns1", "name", "anotheruid"))),
|
||||||
|
},
|
||||||
|
verifiedAbsent: clusterabsentparent,
|
||||||
|
expectedAlternate: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ignore matches",
|
||||||
|
deps: []*node{
|
||||||
|
makeNode(ns1child1, withOwners(clusterabsentparent, clusterabsentparent)),
|
||||||
|
},
|
||||||
|
verifiedAbsent: clusterabsentparent,
|
||||||
|
expectedAlternate: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "collapse duplicates",
|
||||||
|
deps: []*node{
|
||||||
|
makeNode(clusterchild1, withOwners(clusterabsentparent, clusterabsentparent_kind, clusterabsentparent_kind)),
|
||||||
|
},
|
||||||
|
verifiedAbsent: clusterabsentparent,
|
||||||
|
expectedAlternate: &clusterabsentparent_kind,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testcases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
alternate := getAlternateOwnerIdentity(tc.deps, tc.verifiedAbsent)
|
||||||
|
if !reflect.DeepEqual(alternate, tc.expectedAlternate) {
|
||||||
|
t.Errorf("expected\n%#v\ngot\n%#v", tc.expectedAlternate, alternate)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -27,17 +27,29 @@ import (
|
|||||||
// thrown away in this case.
|
// thrown away in this case.
|
||||||
type FakeRecorder struct {
|
type FakeRecorder struct {
|
||||||
Events chan string
|
Events chan string
|
||||||
|
|
||||||
|
IncludeObject bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func objectString(object runtime.Object, includeObject bool) string {
|
||||||
|
if !includeObject {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(" involvedObject{kind=%s,apiVersion=%s}",
|
||||||
|
object.GetObjectKind().GroupVersionKind().Kind,
|
||||||
|
object.GetObjectKind().GroupVersionKind().GroupVersion(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FakeRecorder) Event(object runtime.Object, eventtype, reason, message string) {
|
func (f *FakeRecorder) Event(object runtime.Object, eventtype, reason, message string) {
|
||||||
if f.Events != nil {
|
if f.Events != nil {
|
||||||
f.Events <- fmt.Sprintf("%s %s %s", eventtype, reason, message)
|
f.Events <- fmt.Sprintf("%s %s %s%s", eventtype, reason, message, objectString(object, f.IncludeObject))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FakeRecorder) Eventf(object runtime.Object, eventtype, reason, messageFmt string, args ...interface{}) {
|
func (f *FakeRecorder) Eventf(object runtime.Object, eventtype, reason, messageFmt string, args ...interface{}) {
|
||||||
if f.Events != nil {
|
if f.Events != nil {
|
||||||
f.Events <- fmt.Sprintf(eventtype+" "+reason+" "+messageFmt, args...)
|
f.Events <- fmt.Sprintf(eventtype+" "+reason+" "+messageFmt, args...) + objectString(object, f.IncludeObject)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user