1
16
17 package garbagecollector
18
19 import (
20 "context"
21 "io"
22 "net/http"
23 "strings"
24 "testing"
25 "time"
26
27 "k8s.io/api/core/v1"
28 apierrors "k8s.io/apimachinery/pkg/api/errors"
29 "k8s.io/apimachinery/pkg/api/resource"
30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31 "k8s.io/apimachinery/pkg/types"
32 "k8s.io/apimachinery/pkg/util/wait"
33 kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
34 "k8s.io/kubernetes/test/integration/framework"
35 )
36
37 type roundTripFunc func(req *http.Request) (*http.Response, error)
38
39 func (w roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
40 return w(req)
41 }
42
43 type readDelayer struct {
44 delay time.Duration
45 io.ReadCloser
46 }
47
48 func (b *readDelayer) Read(p []byte) (n int, err error) {
49 defer time.Sleep(b.delay)
50 return b.ReadCloser.Read(p)
51 }
52
53 func TestClusterScopedOwners(t *testing.T) {
54
55 server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
56 server.ClientConfig.WrapTransport = func(rt http.RoundTripper) http.RoundTripper {
57 return roundTripFunc(func(req *http.Request) (*http.Response, error) {
58 if req.URL.Query().Get("watch") != "true" || !strings.Contains(req.URL.String(), "persistentvolumes") {
59 return rt.RoundTrip(req)
60 }
61 resp, err := rt.RoundTrip(req)
62 if err != nil {
63 return resp, err
64 }
65 resp.Body = &readDelayer{30 * time.Second, resp.Body}
66 return resp, err
67 })
68 }
69 ctx := setupWithServer(t, server, 5)
70 defer ctx.tearDown()
71
72 _, clientSet := ctx.gc, ctx.clientSet
73
74 ns := createNamespaceOrDie("gc-cluster-scope-deletion", clientSet, t)
75 defer deleteNamespaceOrDie(ns.Name, clientSet, t)
76
77 t.Log("Create a pair of objects")
78 pv, err := clientSet.CoreV1().PersistentVolumes().Create(context.TODO(), &v1.PersistentVolume{
79 ObjectMeta: metav1.ObjectMeta{Name: "pv-valid"},
80 Spec: v1.PersistentVolumeSpec{
81 PersistentVolumeSource: v1.PersistentVolumeSource{HostPath: &v1.HostPathVolumeSource{Path: "/foo"}},
82 Capacity: v1.ResourceList{v1.ResourceStorage: resource.MustParse("1Gi")},
83 AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteMany},
84 },
85 }, metav1.CreateOptions{})
86 if err != nil {
87 t.Fatal(err)
88 }
89 if _, err := clientSet.CoreV1().ConfigMaps(ns.Name).Create(context.TODO(), &v1.ConfigMap{
90 ObjectMeta: metav1.ObjectMeta{
91 Name: "cm-valid",
92 OwnerReferences: []metav1.OwnerReference{{Kind: "PersistentVolume", APIVersion: "v1", Name: pv.Name, UID: pv.UID}},
93 },
94 }, metav1.CreateOptions{}); err != nil {
95 t.Fatal(err)
96 }
97
98 t.Log("Create a namespaced object with a missing parent")
99 if _, err := clientSet.CoreV1().ConfigMaps(ns.Name).Create(context.TODO(), &v1.ConfigMap{
100 ObjectMeta: metav1.ObjectMeta{
101 Name: "cm-missing",
102 Labels: map[string]string{"missing": "true"},
103 OwnerReferences: []metav1.OwnerReference{{Kind: "PersistentVolume", APIVersion: "v1", Name: "missing-name", UID: types.UID("missing-uid")}},
104 },
105 }, metav1.CreateOptions{}); err != nil {
106 t.Fatal(err)
107 }
108
109 t.Log("Create a namespaced object with a missing type parent")
110 if _, err := clientSet.CoreV1().ConfigMaps(ns.Name).Create(context.TODO(), &v1.ConfigMap{
111 ObjectMeta: metav1.ObjectMeta{
112 Name: "cm-invalid",
113 OwnerReferences: []metav1.OwnerReference{{Kind: "UnknownType", APIVersion: "unknown.group/v1", Name: "invalid-name", UID: types.UID("invalid-uid")}},
114 },
115 }, metav1.CreateOptions{}); err != nil {
116 t.Fatal(err)
117 }
118
119
120 if err := wait.Poll(5*time.Second, 300*time.Second, func() (bool, error) {
121 _, err := clientSet.CoreV1().ConfigMaps(ns.Name).Get(context.TODO(), "cm-missing", metav1.GetOptions{})
122 switch {
123 case apierrors.IsNotFound(err):
124 return true, nil
125 case err != nil:
126 return false, err
127 default:
128 t.Logf("cm with missing parent still exists, retrying")
129 return false, nil
130 }
131 }); err != nil {
132 t.Fatal(err)
133 }
134 t.Logf("deletable children removed")
135
136
137 time.Sleep(5 * time.Second)
138
139
140 if _, err := clientSet.CoreV1().ConfigMaps(ns.Name).Get(context.TODO(), "cm-invalid", metav1.GetOptions{}); err != nil {
141 t.Fatalf("child with invalid ownerRef is unexpectedly missing: %v", err)
142 }
143
144
145 if _, err := clientSet.CoreV1().ConfigMaps(ns.Name).Get(context.TODO(), "cm-valid", metav1.GetOptions{}); err != nil {
146 t.Fatalf("child with valid ownerRef is unexpectedly missing: %v", err)
147 }
148 }
149
View as plain text