Skip to content

Commit 8a087d9

Browse files
authored
Merge pull request #39 from DirectXMan12/bug/cache-list-reflection
Make sure cache Reader still works
2 parents e06131a + 9db07f6 commit 8a087d9

File tree

4 files changed

+98
-13
lines changed

4 files changed

+98
-13
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,8 @@
1111
# Output of the go coverage tool, specifically when used with LiteIDE
1212
*.out
1313

14+
# editor and IDE paraphernalia
1415
.idea
16+
*.swp
17+
*.swo
18+
*~

pkg/cache/cache.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ type Informers interface {
5555
GetInformerForKind(gvk schema.GroupVersionKind) (toolscache.SharedIndexInformer, error)
5656

5757
// Start runs all the informers known to this cache until the given channel is closed.
58-
// It does not block.
58+
// It blocks.
5959
Start(stopCh <-chan struct{}) error
6060

6161
// WaitForCacheSync waits for all the caches to sync. Returns false if it could not sync a cache.

pkg/cache/cache_test.go

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,83 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package cache
17+
package cache_test
1818

1919
import (
20+
"context"
21+
2022
. "github.com/onsi/ginkgo"
23+
. "github.com/onsi/gomega"
24+
kcorev1 "k8s.io/api/core/v1"
25+
26+
"sigs.k8s.io/controller-runtime/pkg/cache"
27+
"sigs.k8s.io/controller-runtime/pkg/client"
2128
)
2229

30+
var _ = Describe("Informer Cache", func() {
31+
var stop chan struct{}
32+
33+
BeforeEach(func() {
34+
stop = make(chan struct{})
35+
Expect(cfg).NotTo(BeNil())
36+
})
37+
AfterEach(func() {
38+
close(stop)
39+
})
40+
41+
Describe("as a Reader", func() {
42+
It("should be able to list objects that haven't been watched previously", func() {
43+
By("Creating the cache")
44+
reader, err := cache.New(cfg, cache.Options{})
45+
Expect(err).NotTo(HaveOccurred())
46+
47+
By("running the cache and waiting for it to sync")
48+
go func() {
49+
defer GinkgoRecover()
50+
Expect(reader.Start(stop)).ToNot(HaveOccurred())
51+
}()
52+
Expect(reader.WaitForCacheSync(stop)).NotTo(BeFalse())
53+
54+
By("Listing all services in the cluster")
55+
listObj := &kcorev1.ServiceList{}
56+
Expect(reader.List(context.Background(), nil, listObj)).NotTo(HaveOccurred())
57+
58+
By("Verifying that the returned list contains the Kubernetes service")
59+
// NB: there has to be at least the kubernetes service in the cluster
60+
Expect(listObj.Items).NotTo(BeEmpty())
61+
hasKubeService := false
62+
for _, svc := range listObj.Items {
63+
if svc.Namespace == "default" && svc.Name == "kubernetes" {
64+
hasKubeService = true
65+
break
66+
}
67+
}
68+
Expect(hasKubeService).To(BeTrue())
69+
})
70+
71+
It("should be able to get objects that haven't been watched previously", func() {
72+
By("Creating the cache")
73+
reader, err := cache.New(cfg, cache.Options{})
74+
Expect(err).NotTo(HaveOccurred())
75+
76+
By("running the cache and waiting for it to sync")
77+
go func() {
78+
defer GinkgoRecover()
79+
Expect(reader.Start(stop)).ToNot(HaveOccurred())
80+
}()
81+
Expect(reader.WaitForCacheSync(stop)).NotTo(BeFalse())
82+
83+
By("Getting the Kubernetes service")
84+
svc := &kcorev1.Service{}
85+
Expect(reader.Get(context.Background(), client.ObjectKey{Namespace: "default", Name: "kubernetes"}, svc)).NotTo(HaveOccurred())
86+
87+
By("Verifying that the returned service looks reasonable")
88+
Expect(svc.Name).To(Equal("kubernetes"))
89+
Expect(svc.Namespace).To(Equal("default"))
90+
})
91+
})
92+
})
93+
2394
var _ = Describe("Indexers", func() {
2495
//three := int64(3)
2596
//knownPodKey := client.ObjectKey{Name: "some-pod", Namespace: "some-ns"}

pkg/cache/informer_cache.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"fmt"
2222
"reflect"
23+
"strings"
2324

2425
apimeta "k8s.io/apimachinery/pkg/api/meta"
2526
"k8s.io/apimachinery/pkg/runtime"
@@ -30,9 +31,11 @@ import (
3031
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
3132
)
3233

33-
var _ Informers = &informerCache{}
34-
var _ client.Reader = &informerCache{}
35-
var _ Cache = &informerCache{}
34+
var (
35+
_ Informers = &informerCache{}
36+
_ client.Reader = &informerCache{}
37+
_ Cache = &informerCache{}
38+
)
3639

3740
// informerCache is a Kubernetes Object cache populated from InformersMap. cache wraps an InformersMap.
3841
type informerCache struct {
@@ -60,19 +63,26 @@ func (ip *informerCache) List(ctx context.Context, opts *client.ListOptions, out
6063
return nil
6164
}
6265

63-
// http://knowyourmeme.com/memes/this-is-fine
64-
outType := reflect.Indirect(reflect.ValueOf(itemsPtr)).Type().Elem()
65-
cacheType, ok := outType.(runtime.Object)
66-
if !ok {
67-
return fmt.Errorf("cannot get cache for %T, its element is not a runtime.Object", out)
68-
}
69-
7066
gvk, err := apiutil.GVKForObject(out, ip.Scheme)
7167
if err != nil {
7268
return err
7369
}
7470

75-
cache, err := ip.InformersMap.Get(gvk, cacheType)
71+
if !strings.HasSuffix(gvk.Kind, "List") {
72+
return fmt.Errorf("non-list type %T (kind %q) passed as output", out, gvk)
73+
}
74+
// we need the non-list GVK, so chop off the "List" from the end of the kind
75+
gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
76+
77+
// http://knowyourmeme.com/memes/this-is-fine
78+
elemType := reflect.Indirect(reflect.ValueOf(itemsPtr)).Type().Elem()
79+
cacheTypeValue := reflect.Zero(reflect.PtrTo(elemType))
80+
cacheTypeObj, ok := cacheTypeValue.Interface().(runtime.Object)
81+
if !ok {
82+
return fmt.Errorf("cannot get cache for %T, its element %T is not a runtime.Object", out, cacheTypeValue.Interface())
83+
}
84+
85+
cache, err := ip.InformersMap.Get(gvk, cacheTypeObj)
7686
if err != nil {
7787
return err
7888
}

0 commit comments

Comments
 (0)