2016-05-04 05:31:26 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2016 The Kubernetes Authors .
2016-05-04 05:31:26 +00:00
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 (
"net/http"
"net/http/httptest"
2017-02-23 19:16:13 +00:00
"reflect"
2016-05-04 05:31:26 +00:00
"strings"
"sync"
"testing"
2016-09-13 03:28:49 +00:00
"github.com/stretchr/testify/assert"
2016-05-04 05:31:26 +00:00
_ "k8s.io/kubernetes/pkg/api/install"
2017-02-23 19:16:13 +00:00
"k8s.io/apimachinery/pkg/api/meta"
2017-01-11 14:09:48 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/apimachinery/pkg/util/sets"
2017-02-23 19:16:13 +00:00
"k8s.io/apimachinery/pkg/util/strategicpatch"
2017-01-25 19:00:30 +00:00
"k8s.io/client-go/dynamic"
2017-01-19 18:27:59 +00:00
restclient "k8s.io/client-go/rest"
2017-01-27 15:20:40 +00:00
"k8s.io/client-go/util/workqueue"
2017-01-12 18:17:43 +00:00
"k8s.io/kubernetes/pkg/api"
2017-06-22 17:25:57 +00:00
"k8s.io/api/core/v1"
2017-05-04 17:55:24 +00:00
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake"
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions"
2016-09-13 03:28:49 +00:00
"k8s.io/kubernetes/pkg/controller/garbagecollector/metaonly"
2016-05-04 05:31:26 +00:00
)
func TestNewGarbageCollector ( t * testing . T ) {
2016-07-02 06:46:00 +00:00
config := & restclient . Config { }
config . ContentConfig . NegotiatedSerializer = serializer . DirectCodecFactory { CodecFactory : metaonly . NewMetadataCodecFactory ( ) }
2017-01-12 18:17:43 +00:00
metaOnlyClientPool := dynamic . NewClientPool ( config , api . Registry . RESTMapper ( ) , dynamic . LegacyAPIPathResolverFunc )
2016-07-02 06:46:00 +00:00
config . ContentConfig . NegotiatedSerializer = nil
2017-01-12 18:17:43 +00:00
clientPool := dynamic . NewClientPool ( config , api . Registry . RESTMapper ( ) , dynamic . LegacyAPIPathResolverFunc )
2017-03-16 18:41:45 +00:00
podResource := map [ schema . GroupVersionResource ] struct { } {
2017-03-29 00:12:04 +00:00
{ Version : "v1" , Resource : "pods" } : { } ,
2017-03-16 18:41:45 +00:00
// no monitor will be constructed for non-core resource, the GC construction will not fail.
2017-03-29 00:12:04 +00:00
{ Group : "tpr.io" , Version : "v1" , Resource : "unknown" } : { } ,
2017-03-16 18:41:45 +00:00
}
2017-05-04 17:55:24 +00:00
client := fake . NewSimpleClientset ( )
sharedInformers := informers . NewSharedInformerFactory ( client , 0 )
2017-05-16 17:35:45 +00:00
gc , err := NewGarbageCollector ( metaOnlyClientPool , clientPool , api . Registry . RESTMapper ( ) , podResource , ignoredResources , sharedInformers )
2016-05-04 05:31:26 +00:00
if err != nil {
t . Fatal ( err )
}
2017-02-23 19:16:13 +00:00
assert . Equal ( t , 1 , len ( gc . dependencyGraphBuilder . monitors ) )
2016-05-04 05:31:26 +00:00
}
// fakeAction records information about requests to aid in testing.
type fakeAction struct {
method string
path string
2016-05-28 22:10:25 +00:00
query string
2016-05-04 05:31:26 +00:00
}
// String returns method=path to aid in testing
func ( f * fakeAction ) String ( ) string {
return strings . Join ( [ ] string { f . method , f . path } , "=" )
}
type FakeResponse struct {
statusCode int
content [ ] byte
}
// fakeActionHandler holds a list of fakeActions received
type fakeActionHandler struct {
// statusCode and content returned by this handler for different method + path.
response map [ string ] FakeResponse
lock sync . Mutex
actions [ ] fakeAction
}
// ServeHTTP logs the action that occurred and always returns the associated status code
func ( f * fakeActionHandler ) ServeHTTP ( response http . ResponseWriter , request * http . Request ) {
f . lock . Lock ( )
defer f . lock . Unlock ( )
2016-05-28 22:10:25 +00:00
f . actions = append ( f . actions , fakeAction { method : request . Method , path : request . URL . Path , query : request . URL . RawQuery } )
2016-05-04 05:31:26 +00:00
fakeResponse , ok := f . response [ request . Method + request . URL . Path ]
if ! ok {
fakeResponse . statusCode = 200
fakeResponse . content = [ ] byte ( "{\"kind\": \"List\"}" )
}
2016-10-12 20:55:28 +00:00
response . Header ( ) . Set ( "Content-Type" , "application/json" )
2016-05-04 05:31:26 +00:00
response . WriteHeader ( fakeResponse . statusCode )
response . Write ( fakeResponse . content )
}
// testServerAndClientConfig returns a server that listens and a config that can reference it
func testServerAndClientConfig ( handler func ( http . ResponseWriter , * http . Request ) ) ( * httptest . Server , * restclient . Config ) {
srv := httptest . NewServer ( http . HandlerFunc ( handler ) )
config := & restclient . Config {
Host : srv . URL ,
}
return srv , config
}
2017-05-04 17:55:24 +00:00
type garbageCollector struct {
* GarbageCollector
stop chan struct { }
}
func setupGC ( t * testing . T , config * restclient . Config ) garbageCollector {
2016-08-22 21:36:23 +00:00
config . ContentConfig . NegotiatedSerializer = serializer . DirectCodecFactory { CodecFactory : metaonly . NewMetadataCodecFactory ( ) }
2017-01-12 18:17:43 +00:00
metaOnlyClientPool := dynamic . NewClientPool ( config , api . Registry . RESTMapper ( ) , dynamic . LegacyAPIPathResolverFunc )
2016-08-22 21:36:23 +00:00
config . ContentConfig . NegotiatedSerializer = nil
2017-01-12 18:17:43 +00:00
clientPool := dynamic . NewClientPool ( config , api . Registry . RESTMapper ( ) , dynamic . LegacyAPIPathResolverFunc )
2017-03-29 00:12:04 +00:00
podResource := map [ schema . GroupVersionResource ] struct { } { { Version : "v1" , Resource : "pods" } : { } }
2017-05-04 17:55:24 +00:00
client := fake . NewSimpleClientset ( )
sharedInformers := informers . NewSharedInformerFactory ( client , 0 )
2017-05-16 17:35:45 +00:00
gc , err := NewGarbageCollector ( metaOnlyClientPool , clientPool , api . Registry . RESTMapper ( ) , podResource , ignoredResources , sharedInformers )
2016-08-22 21:36:23 +00:00
if err != nil {
t . Fatal ( err )
}
2017-05-04 17:55:24 +00:00
stop := make ( chan struct { } )
go sharedInformers . Start ( stop )
return garbageCollector { gc , stop }
2016-08-22 21:36:23 +00:00
}
2016-12-09 18:16:33 +00:00
func getPod ( podName string , ownerReferences [ ] metav1 . OwnerReference ) * v1 . Pod {
2016-05-04 05:31:26 +00:00
return & v1 . Pod {
2016-12-03 18:57:26 +00:00
TypeMeta : metav1 . TypeMeta {
2016-05-04 05:31:26 +00:00
Kind : "Pod" ,
APIVersion : "v1" ,
} ,
2017-01-17 03:38:19 +00:00
ObjectMeta : metav1 . ObjectMeta {
2016-08-22 21:36:23 +00:00
Name : podName ,
Namespace : "ns1" ,
OwnerReferences : ownerReferences ,
2016-05-04 05:31:26 +00:00
} ,
}
}
2016-08-22 21:36:23 +00:00
func serilizeOrDie ( t * testing . T , object interface { } ) [ ] byte {
data , err := json . Marshal ( object )
2016-05-04 05:31:26 +00:00
if err != nil {
t . Fatal ( err )
}
2016-08-22 21:36:23 +00:00
return data
}
2017-02-23 19:16:13 +00:00
// test the attemptToDeleteItem function making the expected actions.
func TestAttemptToDeleteItem ( t * testing . T ) {
2016-12-09 18:16:33 +00:00
pod := getPod ( "ToBeDeletedPod" , [ ] metav1 . OwnerReference {
2016-08-22 21:36:23 +00:00
{
Kind : "ReplicationController" ,
Name : "owner1" ,
UID : "123" ,
APIVersion : "v1" ,
} ,
} )
2016-05-04 05:31:26 +00:00
testHandler := & fakeActionHandler {
response : map [ string ] FakeResponse {
"GET" + "/api/v1/namespaces/ns1/replicationcontrollers/owner1" : {
404 ,
[ ] byte { } ,
} ,
"GET" + "/api/v1/namespaces/ns1/pods/ToBeDeletedPod" : {
200 ,
2016-08-22 21:36:23 +00:00
serilizeOrDie ( t , pod ) ,
2016-05-04 05:31:26 +00:00
} ,
} ,
}
srv , clientConfig := testServerAndClientConfig ( testHandler . ServeHTTP )
defer srv . Close ( )
2017-05-04 17:55:24 +00:00
2016-08-22 21:36:23 +00:00
gc := setupGC ( t , clientConfig )
2017-05-04 17:55:24 +00:00
defer close ( gc . stop )
2016-05-04 05:31:26 +00:00
item := & node {
identity : objectReference {
2016-12-04 03:42:29 +00:00
OwnerReference : metav1 . OwnerReference {
2016-05-04 05:31:26 +00:00
Kind : pod . Kind ,
APIVersion : pod . APIVersion ,
Name : pod . Name ,
UID : pod . UID ,
} ,
Namespace : pod . Namespace ,
} ,
2017-02-23 19:16:13 +00:00
// owners are intentionally left empty. The attemptToDeleteItem routine should get the latest item from the server.
2016-05-04 05:31:26 +00:00
owners : nil ,
}
2017-02-23 19:16:13 +00:00
err := gc . attemptToDeleteItem ( item )
2016-05-04 05:31:26 +00:00
if err != nil {
t . Errorf ( "Unexpected Error: %v" , err )
}
expectedActionSet := sets . NewString ( )
expectedActionSet . Insert ( "GET=/api/v1/namespaces/ns1/replicationcontrollers/owner1" )
expectedActionSet . Insert ( "DELETE=/api/v1/namespaces/ns1/pods/ToBeDeletedPod" )
expectedActionSet . Insert ( "GET=/api/v1/namespaces/ns1/pods/ToBeDeletedPod" )
actualActionSet := sets . NewString ( )
for _ , action := range testHandler . actions {
actualActionSet . Insert ( action . String ( ) )
}
if ! expectedActionSet . Equal ( actualActionSet ) {
t . Errorf ( "expected actions:\n%v\n but got:\n%v\nDifference:\n%v" , expectedActionSet ,
actualActionSet , expectedActionSet . Difference ( actualActionSet ) )
}
}
// verifyGraphInvariants verifies that all of a node's owners list the node as a
// dependent and vice versa. uidToNode has all the nodes in the graph.
func verifyGraphInvariants ( scenario string , uidToNode map [ types . UID ] * node , t * testing . T ) {
for myUID , node := range uidToNode {
for dependentNode := range node . dependents {
found := false
for _ , owner := range dependentNode . owners {
if owner . UID == myUID {
found = true
break
}
}
if ! found {
t . Errorf ( "scenario: %s: node %s has node %s as a dependent, but it's not present in the latter node's owners list" , scenario , node . identity , dependentNode . identity )
}
}
for _ , owner := range node . owners {
ownerNode , ok := uidToNode [ owner . UID ]
if ! ok {
// It's possible that the owner node doesn't exist
continue
}
if _ , ok := ownerNode . dependents [ node ] ; ! ok {
t . Errorf ( "node %s has node %s as an owner, but it's not present in the latter node's dependents list" , node . identity , ownerNode . identity )
}
}
}
}
func createEvent ( eventType eventType , selfUID string , owners [ ] string ) event {
2016-12-09 18:16:33 +00:00
var ownerReferences [ ] metav1 . OwnerReference
2016-05-04 05:31:26 +00:00
for i := 0 ; i < len ( owners ) ; i ++ {
2016-12-09 18:16:33 +00:00
ownerReferences = append ( ownerReferences , metav1 . OwnerReference { UID : types . UID ( owners [ i ] ) } )
2016-05-04 05:31:26 +00:00
}
return event {
eventType : eventType ,
2016-11-18 20:50:17 +00:00
obj : & v1 . Pod {
2017-01-17 03:38:19 +00:00
ObjectMeta : metav1 . ObjectMeta {
2016-05-04 05:31:26 +00:00
UID : types . UID ( selfUID ) ,
OwnerReferences : ownerReferences ,
} ,
} ,
}
}
func TestProcessEvent ( t * testing . T ) {
var testScenarios = [ ] struct {
name string
// a series of events that will be supplied to the
2017-02-23 19:16:13 +00:00
// GraphBuilder.eventQueue.
2016-05-04 05:31:26 +00:00
events [ ] event
} {
{
name : "test1" ,
events : [ ] event {
createEvent ( addEvent , "1" , [ ] string { } ) ,
createEvent ( addEvent , "2" , [ ] string { "1" } ) ,
createEvent ( addEvent , "3" , [ ] string { "1" , "2" } ) ,
} ,
} ,
{
name : "test2" ,
events : [ ] event {
createEvent ( addEvent , "1" , [ ] string { } ) ,
createEvent ( addEvent , "2" , [ ] string { "1" } ) ,
createEvent ( addEvent , "3" , [ ] string { "1" , "2" } ) ,
createEvent ( addEvent , "4" , [ ] string { "2" } ) ,
createEvent ( deleteEvent , "2" , [ ] string { "doesn't matter" } ) ,
} ,
} ,
{
name : "test3" ,
events : [ ] event {
createEvent ( addEvent , "1" , [ ] string { } ) ,
createEvent ( addEvent , "2" , [ ] string { "1" } ) ,
createEvent ( addEvent , "3" , [ ] string { "1" , "2" } ) ,
createEvent ( addEvent , "4" , [ ] string { "3" } ) ,
createEvent ( updateEvent , "2" , [ ] string { "4" } ) ,
} ,
} ,
{
name : "reverse test2" ,
events : [ ] event {
createEvent ( addEvent , "4" , [ ] string { "2" } ) ,
createEvent ( addEvent , "3" , [ ] string { "1" , "2" } ) ,
createEvent ( addEvent , "2" , [ ] string { "1" } ) ,
createEvent ( addEvent , "1" , [ ] string { } ) ,
createEvent ( deleteEvent , "2" , [ ] string { "doesn't matter" } ) ,
} ,
} ,
}
for _ , scenario := range testScenarios {
2017-02-23 19:16:13 +00:00
dependencyGraphBuilder := & GraphBuilder {
graphChanges : workqueue . NewRateLimitingQueue ( workqueue . DefaultControllerRateLimiter ( ) ) ,
2016-05-18 03:24:42 +00:00
uidToNode : & concurrentUIDToNode {
2017-02-23 19:16:13 +00:00
uidToNodeLock : sync . RWMutex { } ,
uidToNode : make ( map [ types . UID ] * node ) ,
2016-05-04 05:31:26 +00:00
} ,
2017-02-23 19:16:13 +00:00
attemptToDelete : workqueue . NewRateLimitingQueue ( workqueue . DefaultControllerRateLimiter ( ) ) ,
absentOwnerCache : NewUIDCache ( 2 ) ,
2016-05-04 05:31:26 +00:00
}
for i := 0 ; i < len ( scenario . events ) ; i ++ {
2017-02-23 19:16:13 +00:00
dependencyGraphBuilder . graphChanges . Add ( & scenario . events [ i ] )
dependencyGraphBuilder . processGraphChanges ( )
verifyGraphInvariants ( scenario . name , dependencyGraphBuilder . uidToNode . uidToNode , t )
2016-05-04 05:31:26 +00:00
}
}
}
2016-05-18 03:24:42 +00:00
// TestDependentsRace relies on golang's data race detector to check if there is
// data race among in the dependents field.
func TestDependentsRace ( t * testing . T ) {
2016-08-22 21:36:23 +00:00
gc := setupGC ( t , & restclient . Config { } )
2017-05-04 17:55:24 +00:00
defer close ( gc . stop )
2016-05-18 03:24:42 +00:00
const updates = 100
2016-08-02 06:13:17 +00:00
owner := & node { dependents : make ( map [ * node ] struct { } ) }
2016-05-18 03:24:42 +00:00
ownerUID := types . UID ( "owner" )
2017-02-23 19:16:13 +00:00
gc . dependencyGraphBuilder . uidToNode . Write ( owner )
2016-05-18 03:24:42 +00:00
go func ( ) {
for i := 0 ; i < updates ; i ++ {
dependent := & node { }
2017-02-23 19:16:13 +00:00
gc . dependencyGraphBuilder . addDependentToOwners ( dependent , [ ] metav1 . OwnerReference { { UID : ownerUID } } )
gc . dependencyGraphBuilder . removeDependentFromOwners ( dependent , [ ] metav1 . OwnerReference { { UID : ownerUID } } )
2016-05-18 03:24:42 +00:00
}
} ( )
go func ( ) {
2017-02-23 19:16:13 +00:00
gc . attemptToOrphan . Add ( owner )
2016-05-18 03:24:42 +00:00
for i := 0 ; i < updates ; i ++ {
2017-02-23 19:16:13 +00:00
gc . attemptToOrphanWorker ( )
2016-05-18 03:24:42 +00:00
}
} ( )
}
2016-05-28 22:10:25 +00:00
// test the list and watch functions correctly converts the ListOptions
func TestGCListWatcher ( t * testing . T ) {
testHandler := & fakeActionHandler { }
srv , clientConfig := testServerAndClientConfig ( testHandler . ServeHTTP )
defer srv . Close ( )
2017-01-12 18:17:43 +00:00
clientPool := dynamic . NewClientPool ( clientConfig , api . Registry . RESTMapper ( ) , dynamic . LegacyAPIPathResolverFunc )
2016-11-21 02:55:31 +00:00
podResource := schema . GroupVersionResource { Version : "v1" , Resource : "pods" }
2016-09-13 03:28:49 +00:00
client , err := clientPool . ClientForGroupVersionResource ( podResource )
2016-05-28 22:10:25 +00:00
if err != nil {
t . Fatal ( err )
}
2017-02-23 19:16:13 +00:00
lw := listWatcher ( client , podResource )
if _ , err := lw . Watch ( metav1 . ListOptions { ResourceVersion : "1" } ) ; err != nil {
t . Fatal ( err )
}
if _ , err := lw . List ( metav1 . ListOptions { ResourceVersion : "1" } ) ; err != nil {
t . Fatal ( err )
}
2016-05-28 22:10:25 +00:00
if e , a := 2 , len ( testHandler . actions ) ; e != a {
t . Errorf ( "expect %d requests, got %d" , e , a )
}
2017-01-09 21:21:23 +00:00
if e , a := "resourceVersion=1&watch=true" , testHandler . actions [ 0 ] . query ; e != a {
2016-05-28 22:10:25 +00:00
t . Errorf ( "expect %s, got %s" , e , a )
}
if e , a := "resourceVersion=1" , testHandler . actions [ 1 ] . query ; e != a {
t . Errorf ( "expect %s, got %s" , e , a )
}
}
2016-08-22 21:36:23 +00:00
func podToGCNode ( pod * v1 . Pod ) * node {
return & node {
identity : objectReference {
2016-12-04 03:42:29 +00:00
OwnerReference : metav1 . OwnerReference {
2016-08-22 21:36:23 +00:00
Kind : pod . Kind ,
APIVersion : pod . APIVersion ,
Name : pod . Name ,
UID : pod . UID ,
} ,
Namespace : pod . Namespace ,
} ,
2017-02-23 19:16:13 +00:00
// owners are intentionally left empty. The attemptToDeleteItem routine should get the latest item from the server.
2016-08-22 21:36:23 +00:00
owners : nil ,
}
}
func TestAbsentUIDCache ( t * testing . T ) {
2016-12-09 18:16:33 +00:00
rc1Pod1 := getPod ( "rc1Pod1" , [ ] metav1 . OwnerReference {
2016-08-22 21:36:23 +00:00
{
Kind : "ReplicationController" ,
Name : "rc1" ,
UID : "1" ,
APIVersion : "v1" ,
} ,
} )
2016-12-09 18:16:33 +00:00
rc1Pod2 := getPod ( "rc1Pod2" , [ ] metav1 . OwnerReference {
2016-08-22 21:36:23 +00:00
{
Kind : "ReplicationController" ,
Name : "rc1" ,
UID : "1" ,
APIVersion : "v1" ,
} ,
} )
2016-12-09 18:16:33 +00:00
rc2Pod1 := getPod ( "rc2Pod1" , [ ] metav1 . OwnerReference {
2016-08-22 21:36:23 +00:00
{
Kind : "ReplicationController" ,
Name : "rc2" ,
UID : "2" ,
APIVersion : "v1" ,
} ,
} )
2016-12-09 18:16:33 +00:00
rc3Pod1 := getPod ( "rc3Pod1" , [ ] metav1 . OwnerReference {
2016-08-22 21:36:23 +00:00
{
Kind : "ReplicationController" ,
Name : "rc3" ,
UID : "3" ,
APIVersion : "v1" ,
} ,
} )
testHandler := & fakeActionHandler {
response : map [ string ] FakeResponse {
"GET" + "/api/v1/namespaces/ns1/pods/rc1Pod1" : {
200 ,
serilizeOrDie ( t , rc1Pod1 ) ,
} ,
"GET" + "/api/v1/namespaces/ns1/pods/rc1Pod2" : {
200 ,
serilizeOrDie ( t , rc1Pod2 ) ,
} ,
"GET" + "/api/v1/namespaces/ns1/pods/rc2Pod1" : {
200 ,
serilizeOrDie ( t , rc2Pod1 ) ,
} ,
"GET" + "/api/v1/namespaces/ns1/pods/rc3Pod1" : {
200 ,
serilizeOrDie ( t , rc3Pod1 ) ,
} ,
"GET" + "/api/v1/namespaces/ns1/replicationcontrollers/rc1" : {
404 ,
[ ] byte { } ,
} ,
"GET" + "/api/v1/namespaces/ns1/replicationcontrollers/rc2" : {
404 ,
[ ] byte { } ,
} ,
"GET" + "/api/v1/namespaces/ns1/replicationcontrollers/rc3" : {
404 ,
[ ] byte { } ,
} ,
} ,
}
srv , clientConfig := testServerAndClientConfig ( testHandler . ServeHTTP )
defer srv . Close ( )
gc := setupGC ( t , clientConfig )
2017-05-04 17:55:24 +00:00
defer close ( gc . stop )
2016-08-22 21:36:23 +00:00
gc . absentOwnerCache = NewUIDCache ( 2 )
2017-02-23 19:16:13 +00:00
gc . attemptToDeleteItem ( podToGCNode ( rc1Pod1 ) )
gc . attemptToDeleteItem ( podToGCNode ( rc2Pod1 ) )
2016-08-22 21:36:23 +00:00
// rc1 should already be in the cache, no request should be sent. rc1 should be promoted in the UIDCache
2017-02-23 19:16:13 +00:00
gc . attemptToDeleteItem ( podToGCNode ( rc1Pod2 ) )
2016-08-22 21:36:23 +00:00
// after this call, rc2 should be evicted from the UIDCache
2017-02-23 19:16:13 +00:00
gc . attemptToDeleteItem ( podToGCNode ( rc3Pod1 ) )
2016-08-22 21:36:23 +00:00
// check cache
if ! gc . absentOwnerCache . Has ( types . UID ( "1" ) ) {
t . Errorf ( "expected rc1 to be in the cache" )
}
if gc . absentOwnerCache . Has ( types . UID ( "2" ) ) {
t . Errorf ( "expected rc2 to not exist in the cache" )
}
if ! gc . absentOwnerCache . Has ( types . UID ( "3" ) ) {
t . Errorf ( "expected rc3 to be in the cache" )
}
// check the request sent to the server
count := 0
for _ , action := range testHandler . actions {
if action . String ( ) == "GET=/api/v1/namespaces/ns1/replicationcontrollers/rc1" {
count ++
}
}
if count != 1 {
t . Errorf ( "expected only 1 GET rc1 request, got %d" , count )
}
}
2017-02-23 19:16:13 +00:00
func TestDeleteOwnerRefPatch ( t * testing . T ) {
original := v1 . Pod {
ObjectMeta : metav1 . ObjectMeta {
UID : "100" ,
OwnerReferences : [ ] metav1 . OwnerReference {
{ UID : "1" } ,
{ UID : "2" } ,
{ UID : "3" } ,
} ,
} ,
}
originalData := serilizeOrDie ( t , original )
expected := v1 . Pod {
ObjectMeta : metav1 . ObjectMeta {
UID : "100" ,
OwnerReferences : [ ] metav1 . OwnerReference {
{ UID : "1" } ,
} ,
} ,
}
patch := deleteOwnerRefPatch ( "100" , "2" , "3" )
patched , err := strategicpatch . StrategicMergePatch ( originalData , patch , v1 . Pod { } )
if err != nil {
t . Fatal ( err )
}
var got v1 . Pod
if err := json . Unmarshal ( patched , & got ) ; err != nil {
t . Fatal ( err )
}
if ! reflect . DeepEqual ( expected , got ) {
t . Errorf ( "expected: %#v,\ngot: %#v" , expected , got )
}
}
func TestUnblockOwnerReference ( t * testing . T ) {
trueVar := true
falseVar := false
original := v1 . Pod {
ObjectMeta : metav1 . ObjectMeta {
UID : "100" ,
OwnerReferences : [ ] metav1 . OwnerReference {
{ UID : "1" , BlockOwnerDeletion : & trueVar } ,
{ UID : "2" , BlockOwnerDeletion : & falseVar } ,
{ UID : "3" } ,
} ,
} ,
}
originalData := serilizeOrDie ( t , original )
expected := v1 . Pod {
ObjectMeta : metav1 . ObjectMeta {
UID : "100" ,
OwnerReferences : [ ] metav1 . OwnerReference {
{ UID : "1" , BlockOwnerDeletion : & falseVar } ,
{ UID : "2" , BlockOwnerDeletion : & falseVar } ,
{ UID : "3" } ,
} ,
} ,
}
accessor , err := meta . Accessor ( & original )
if err != nil {
t . Fatal ( err )
}
n := node {
owners : accessor . GetOwnerReferences ( ) ,
}
patch , err := n . patchToUnblockOwnerReferences ( )
if err != nil {
t . Fatal ( err )
}
patched , err := strategicpatch . StrategicMergePatch ( originalData , patch , v1 . Pod { } )
if err != nil {
t . Fatal ( err )
}
var got v1 . Pod
if err := json . Unmarshal ( patched , & got ) ; err != nil {
t . Fatal ( err )
}
if ! reflect . DeepEqual ( expected , got ) {
t . Errorf ( "expected: %#v,\ngot: %#v" , expected , got )
t . Errorf ( "expected: %#v,\ngot: %#v" , expected . OwnerReferences , got . OwnerReferences )
for _ , ref := range got . OwnerReferences {
t . Errorf ( "ref.UID=%s, ref.BlockOwnerDeletion=%v" , ref . UID , * ref . BlockOwnerDeletion )
}
}
}
2017-06-05 23:22:46 +00:00
func TestOrphanDependentsFailure ( t * testing . T ) {
testHandler := & fakeActionHandler {
response : map [ string ] FakeResponse {
"PATCH" + "/api/v1/namespaces/ns1/pods/pod" : {
409 ,
[ ] byte { } ,
} ,
} ,
}
srv , clientConfig := testServerAndClientConfig ( testHandler . ServeHTTP )
defer srv . Close ( )
gc := setupGC ( t , clientConfig )
defer close ( gc . stop )
dependents := [ ] * node {
{
identity : objectReference {
OwnerReference : metav1 . OwnerReference {
Kind : "Pod" ,
APIVersion : "v1" ,
Name : "pod" ,
} ,
Namespace : "ns1" ,
} ,
} ,
}
err := gc . orphanDependents ( objectReference { } , dependents )
expected := ` the server reported a conflict (patch pods pod) `
if err == nil || ! strings . Contains ( err . Error ( ) , expected ) {
t . Errorf ( "expected error contains text %s, got %v" , expected , err )
}
}