Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: kubernetes-sigs/controller-runtime
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.20.2
Choose a base ref
...
head repository: kubernetes-sigs/controller-runtime
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v0.20.3
Choose a head ref
  • 8 commits
  • 7 files changed
  • 4 contributors

Commits on Feb 20, 2025

  1. fix: cache should list out of global cache when present and necessary

    When the cache options are configured with DefaultNamespaces which
    include an entry with `cache.AllNamespaces`, listing from the cache
    should fallback to the global cache if there are no namespace-specific
    caches that match the namespace from the list options.
    
    Signed-off-by: Joe Lanford <[email protected]>
    joelanford authored and k8s-infra-cherrypick-robot committed Feb 20, 2025
    Copy the full SHA
    b015843 View commit details
  2. Merge pull request #3127 from k8s-infra-cherrypick-robot/cherry-pick-…

    …3126-to-release-0.20
    
    [release-0.20] 🐛 fix: cache should list out of global cache when present and necessary
    k8s-ci-robot authored Feb 20, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    22815fc View commit details

Commits on Feb 24, 2025

  1. Export envtest.ReadCRDFiles

    Signed-off-by: Stefan Büringer [email protected]
    sbueringer authored and k8s-infra-cherrypick-robot committed Feb 24, 2025
    Copy the full SHA
    772ce35 View commit details
  2. Merge pull request #3131 from k8s-infra-cherrypick-robot/cherry-pick-…

    …3129-to-release-0.20
    
    [release-0.20] 🌱 Export envtest.ReadCRDFiles
    k8s-ci-robot authored Feb 24, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    44bed88 View commit details

Commits on Mar 7, 2025

  1. bug: Fakeclient: Fix dataraces when writing to the scheme

    We have a scheme write lock but plenty of other codeptaths that read
    from the scheme and that don't do looking, resulting in dataraces if the
    two happen in parallel.
    
    This change introduces a simple RW lock and makes the fakeclient acquire
    read locking for all its operations except when needing the write lock.
    This isn't particularly smart, but given that we only have one codepath
    that writes to the scheme, it seems good enough.
    alvaroaleman authored and k8s-infra-cherrypick-robot committed Mar 7, 2025
    Copy the full SHA
    39fefb9 View commit details
  2. Merge pull request #3145 from k8s-infra-cherrypick-robot/cherry-pick-…

    …3143-to-release-0.20
    
    [release-0.20] 🐛 Fakeclient: Fix dataraces when writing to the scheme
    k8s-ci-robot authored Mar 7, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    e8b6292 View commit details
  3. Revert "[release-0.20] ✨ Expose all Go runtime metrics (#3100)"

    This reverts commit fc48583.
    
    This change breaks some users.
    alvaroaleman committed Mar 7, 2025
    Copy the full SHA
    c1de925 View commit details

Commits on Mar 8, 2025

  1. Merge pull request #3147 from alvaroaleman/rev

    [release-0.20] 🐛 Revert "✨ Expose all Go runtime metrics"
    k8s-ci-robot authored Mar 8, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    bd9c786 View commit details
12 changes: 6 additions & 6 deletions pkg/cache/cache_test.go
Original file line number Diff line number Diff line change
@@ -130,16 +130,16 @@ var _ = Describe("Informer Cache with ReaderFailOnMissingInformer", func() {
var _ = Describe("Multi-Namespace Informer Cache", func() {
CacheTest(cache.New, cache.Options{
DefaultNamespaces: map[string]cache.Config{
testNamespaceOne: {},
testNamespaceTwo: {},
"default": {},
cache.AllNamespaces: {FieldSelector: fields.OneTermEqualSelector("metadata.namespace", testNamespaceOne)},
testNamespaceTwo: {},
"default": {},
},
})
NonBlockingGetTest(cache.New, cache.Options{
DefaultNamespaces: map[string]cache.Config{
testNamespaceOne: {},
testNamespaceTwo: {},
"default": {},
cache.AllNamespaces: {FieldSelector: fields.OneTermEqualSelector("metadata.namespace", testNamespaceOne)},
testNamespaceTwo: {},
"default": {},
},
})
})
3 changes: 3 additions & 0 deletions pkg/cache/multi_namespace_cache.go
Original file line number Diff line number Diff line change
@@ -262,6 +262,9 @@ func (c *multiNamespaceCache) List(ctx context.Context, list client.ObjectList,
if listOpts.Namespace != corev1.NamespaceAll {
cache, ok := c.namespaceToCache[listOpts.Namespace]
if !ok {
if global, hasGlobal := c.namespaceToCache[AllNamespaces]; hasGlobal {
return global.List(ctx, list, opts...)
}
return fmt.Errorf("unable to list: %v because of unknown namespace for the cache", listOpts.Namespace)
}
return cache.List(ctx, list, opts...)
24 changes: 20 additions & 4 deletions pkg/client/fake/client.go
Original file line number Diff line number Diff line change
@@ -75,8 +75,8 @@ type fakeClient struct {
trackerWriteLock sync.Mutex
tracker versionedTracker

schemeWriteLock sync.Mutex
scheme *runtime.Scheme
schemeLock sync.RWMutex
scheme *runtime.Scheme

restMapper meta.RESTMapper
withStatusSubresource sets.Set[schema.GroupVersionKind]
@@ -512,6 +512,8 @@ func (t versionedTracker) updateObject(gvr schema.GroupVersionResource, obj runt
}

func (c *fakeClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
c.schemeLock.RLock()
defer c.schemeLock.RUnlock()
gvr, err := getGVRFromObject(obj, c.scheme)
if err != nil {
return err
@@ -561,6 +563,8 @@ func (c *fakeClient) Watch(ctx context.Context, list client.ObjectList, opts ...
}

func (c *fakeClient) List(ctx context.Context, obj client.ObjectList, opts ...client.ListOption) error {
c.schemeLock.RLock()
defer c.schemeLock.RUnlock()
gvk, err := apiutil.GVKForObject(obj, c.scheme)
if err != nil {
return err
@@ -573,9 +577,11 @@ func (c *fakeClient) List(ctx context.Context, obj client.ObjectList, opts ...cl
if _, isUnstructuredList := obj.(runtime.Unstructured); isUnstructuredList && !c.scheme.Recognizes(gvk) {
// We need to register the ListKind with UnstructuredList:
// https://github.com/kubernetes/kubernetes/blob/7b2776b89fb1be28d4e9203bdeec079be903c103/staging/src/k8s.io/client-go/dynamic/fake/simple.go#L44-L51
c.schemeWriteLock.Lock()
c.schemeLock.RUnlock()
c.schemeLock.Lock()
c.scheme.AddKnownTypeWithName(gvk.GroupVersion().WithKind(gvk.Kind+"List"), &unstructured.UnstructuredList{})
c.schemeWriteLock.Unlock()
c.schemeLock.Unlock()
c.schemeLock.RLock()
}

listOpts := client.ListOptions{}
@@ -726,6 +732,8 @@ func (c *fakeClient) IsObjectNamespaced(obj runtime.Object) (bool, error) {
}

func (c *fakeClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {
c.schemeLock.RLock()
defer c.schemeLock.RUnlock()
createOptions := &client.CreateOptions{}
createOptions.ApplyOptions(opts)

@@ -762,6 +770,8 @@ func (c *fakeClient) Create(ctx context.Context, obj client.Object, opts ...clie
}

func (c *fakeClient) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error {
c.schemeLock.RLock()
defer c.schemeLock.RUnlock()
gvr, err := getGVRFromObject(obj, c.scheme)
if err != nil {
return err
@@ -807,6 +817,8 @@ func (c *fakeClient) Delete(ctx context.Context, obj client.Object, opts ...clie
}

func (c *fakeClient) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error {
c.schemeLock.RLock()
defer c.schemeLock.RUnlock()
gvk, err := apiutil.GVKForObject(obj, c.scheme)
if err != nil {
return err
@@ -856,6 +868,8 @@ func (c *fakeClient) Update(ctx context.Context, obj client.Object, opts ...clie
}

func (c *fakeClient) update(obj client.Object, isStatus bool, opts ...client.UpdateOption) error {
c.schemeLock.RLock()
defer c.schemeLock.RUnlock()
updateOptions := &client.UpdateOptions{}
updateOptions.ApplyOptions(opts)

@@ -884,6 +898,8 @@ func (c *fakeClient) Patch(ctx context.Context, obj client.Object, patch client.
}

func (c *fakeClient) patch(obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
c.schemeLock.RLock()
defer c.schemeLock.RUnlock()
patchOptions := &client.PatchOptions{}
patchOptions.ApplyOptions(opts)

88 changes: 88 additions & 0 deletions pkg/client/fake/client_test.go
Original file line number Diff line number Diff line change
@@ -2516,6 +2516,93 @@ var _ = Describe("Fake client", func() {
Expect(cl.SubResource(subResourceScale).Update(context.Background(), obj, client.WithSubResourceBody(scale)).Error()).To(Equal(expectedErr))
})

It("is threadsafe", func() {
cl := NewClientBuilder().Build()

u := func() *unstructured.Unstructured {
u := &unstructured.Unstructured{}
u.SetAPIVersion("custom/v1")
u.SetKind("Version")
u.SetName("foo")
return u
}

uList := func() *unstructured.UnstructuredList {
u := &unstructured.UnstructuredList{}
u.SetAPIVersion("custom/v1")
u.SetKind("Version")

return u
}

meta := func() *metav1.PartialObjectMetadata {
return &metav1.PartialObjectMetadata{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "default",
},
TypeMeta: metav1.TypeMeta{
APIVersion: "custom/v1",
Kind: "Version",
},
}
}
metaList := func() *metav1.PartialObjectMetadataList {
return &metav1.PartialObjectMetadataList{
TypeMeta: metav1.TypeMeta{

APIVersion: "custom/v1",
Kind: "Version",
},
}
}

pod := func() *corev1.Pod {
return &corev1.Pod{ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "default",
}}
}

ctx := context.Background()
ops := []func(){
func() { _ = cl.Create(ctx, u()) },
func() { _ = cl.Get(ctx, client.ObjectKeyFromObject(u()), u()) },
func() { _ = cl.Update(ctx, u()) },
func() { _ = cl.Patch(ctx, u(), client.RawPatch(types.StrategicMergePatchType, []byte("foo"))) },
func() { _ = cl.Delete(ctx, u()) },
func() { _ = cl.DeleteAllOf(ctx, u(), client.HasLabels{"foo"}) },
func() { _ = cl.List(ctx, uList()) },

func() { _ = cl.Create(ctx, meta()) },
func() { _ = cl.Get(ctx, client.ObjectKeyFromObject(meta()), meta()) },
func() { _ = cl.Update(ctx, meta()) },
func() { _ = cl.Patch(ctx, meta(), client.RawPatch(types.StrategicMergePatchType, []byte("foo"))) },
func() { _ = cl.Delete(ctx, meta()) },
func() { _ = cl.DeleteAllOf(ctx, meta(), client.HasLabels{"foo"}) },
func() { _ = cl.List(ctx, metaList()) },

func() { _ = cl.Create(ctx, pod()) },
func() { _ = cl.Get(ctx, client.ObjectKeyFromObject(pod()), pod()) },
func() { _ = cl.Update(ctx, pod()) },
func() { _ = cl.Patch(ctx, pod(), client.RawPatch(types.StrategicMergePatchType, []byte("foo"))) },
func() { _ = cl.Delete(ctx, pod()) },
func() { _ = cl.DeleteAllOf(ctx, pod(), client.HasLabels{"foo"}) },
func() { _ = cl.List(ctx, &corev1.PodList{}) },
}

wg := sync.WaitGroup{}
wg.Add(len(ops))
for _, op := range ops {
go func() {
defer wg.Done()
op()
}()
}

wg.Wait()
})

scalableObjs := []client.Object{
&appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
@@ -2604,6 +2691,7 @@ var _ = Describe("Fake client", func() {
scaleExpected.ResourceVersion = scaleActual.ResourceVersion
Expect(cmp.Diff(scaleExpected, scaleActual)).To(BeEmpty())
})

}
})

8 changes: 4 additions & 4 deletions pkg/envtest/crd.go
Original file line number Diff line number Diff line change
@@ -94,7 +94,7 @@ func InstallCRDs(config *rest.Config, options CRDInstallOptions) ([]*apiextensio
defaultCRDOptions(&options)

// Read the CRD yamls into options.CRDs
if err := readCRDFiles(&options); err != nil {
if err := ReadCRDFiles(&options); err != nil {
return nil, fmt.Errorf("unable to read CRD files: %w", err)
}

@@ -115,8 +115,8 @@ func InstallCRDs(config *rest.Config, options CRDInstallOptions) ([]*apiextensio
return options.CRDs, nil
}

// readCRDFiles reads the directories of CRDs in options.Paths and adds the CRD structs to options.CRDs.
func readCRDFiles(options *CRDInstallOptions) error {
// ReadCRDFiles reads the directories of CRDs in options.Paths and adds the CRD structs to options.CRDs.
func ReadCRDFiles(options *CRDInstallOptions) error {
if len(options.Paths) > 0 {
crdList, err := renderCRDs(options)
if err != nil {
@@ -217,7 +217,7 @@ func (p *poller) poll(ctx context.Context) (done bool, err error) {
// UninstallCRDs uninstalls a collection of CRDs by reading the crd yaml files from a directory.
func UninstallCRDs(config *rest.Config, options CRDInstallOptions) error {
// Read the CRD yamls into options.CRDs
if err := readCRDFiles(&options); err != nil {
if err := ReadCRDFiles(&options); err != nil {
return err
}

2 changes: 1 addition & 1 deletion pkg/envtest/crd_test.go
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@ var _ = Describe("Test", func() {
"testdata/crdv1_original",
},
}
err := readCRDFiles(&opt)
err := ReadCRDFiles(&opt)
Expect(err).NotTo(HaveOccurred())

expectedCRDs := sets.NewString(
4 changes: 2 additions & 2 deletions pkg/internal/controller/metrics/metrics.go
Original file line number Diff line number Diff line change
@@ -88,7 +88,7 @@ func init() {
ActiveWorkers,
// expose process metrics like CPU, Memory, file descriptor usage etc.
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
// expose all Go runtime metrics like GC stats, memory stats etc.
collectors.NewGoCollector(collectors.WithGoCollectorRuntimeMetrics(collectors.MetricsAll)),
// expose Go runtime metrics like GC stats, memory stats etc.
collectors.NewGoCollector(),
)
}