Skip to content

Commit 3d82e94

Browse files
committed
Ensures metadata only is cached for pods and services
Signed-off-by: irbekrm <irbekrm@gmail.com>
1 parent 659530c commit 3d82e94

File tree

9 files changed

+77
-55
lines changed

9 files changed

+77
-55
lines changed

cmd/controller/app/controller.go

+1
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ func Run(opts *options.ControllerOptions, stopCh <-chan struct{}) error {
211211
log.V(logf.DebugLevel).Info("starting shared informer factories")
212212
ctx.SharedInformerFactory.Start(rootCtx.Done())
213213
ctx.KubeSharedInformerFactory.Start(rootCtx.Done())
214+
ctx.MetadataInformerFactory.Start(rootCtx.Done())
214215

215216
if utilfeature.DefaultFeatureGate.Enabled(feature.ExperimentalGatewayAPISupport) {
216217
ctx.GWShared.Start(rootCtx.Done())

internal/informers/core.go

-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package informers
1919
import (
2020
corev1 "k8s.io/api/core/v1"
2121
certificatesv1 "k8s.io/client-go/informers/certificates/v1"
22-
corev1informers "k8s.io/client-go/informers/core/v1"
2322
networkingv1informers "k8s.io/client-go/informers/networking/v1"
2423
corev1listers "k8s.io/client-go/listers/core/v1"
2524
"k8s.io/client-go/tools/cache"
@@ -45,8 +44,6 @@ const pleaseOpenIssue = "Please report this by opening an issue with this error
4544
type KubeInformerFactory interface {
4645
Start(<-chan struct{})
4746
WaitForCacheSync(<-chan struct{}) map[string]bool
48-
Pods() corev1informers.PodInformer
49-
Services() corev1informers.ServiceInformer
5047
Ingresses() networkingv1informers.IngressInformer
5148
Secrets() SecretInformer
5249
CertificateSigningRequests() certificatesv1.CertificateSigningRequestInformer

internal/informers/core_basic.go

-8
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,6 @@ func (bf *baseFactory) WaitForCacheSync(stopCh <-chan struct{}) map[string]bool
6464
return ret
6565
}
6666

67-
func (bf *baseFactory) Pods() corev1informers.PodInformer {
68-
return bf.f.Core().V1().Pods()
69-
}
70-
71-
func (bf *baseFactory) Services() corev1informers.ServiceInformer {
72-
return bf.f.Core().V1().Services()
73-
}
74-
7567
func (bf *baseFactory) Ingresses() networkingv1informers.IngressInformer {
7668
return bf.f.Networking().V1().Ingresses()
7769
}

internal/informers/core_filteredsecrets.go

-8
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,6 @@ func (bf *filteredSecretsFactory) WaitForCacheSync(stopCh <-chan struct{}) map[s
115115
return caches
116116
}
117117

118-
func (bf *filteredSecretsFactory) Pods() corev1informers.PodInformer {
119-
return bf.typedInformerFactory.Core().V1().Pods()
120-
}
121-
122-
func (bf *filteredSecretsFactory) Services() corev1informers.ServiceInformer {
123-
return bf.typedInformerFactory.Core().V1().Services()
124-
}
125-
126118
func (bf *filteredSecretsFactory) Ingresses() networkingv1informers.IngressInformer {
127119
return bf.typedInformerFactory.Networking().V1().Ingresses()
128120
}

pkg/controller/acmechallenges/controller.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ func (c *controller) Register(ctx *controllerpkg.Context) (workqueue.RateLimitin
9494
secretInformer := ctx.KubeSharedInformerFactory.Secrets()
9595
// we register these informers here so the HTTP01 solver has a synced
9696
// cache when managing pod/service/ingress resources
97-
podInformer := ctx.KubeSharedInformerFactory.Pods()
98-
serviceInformer := ctx.KubeSharedInformerFactory.Services()
97+
podInformer := ctx.MetadataInformerFactory.ForResource(corev1.SchemeGroupVersion.WithResource("pods"))
98+
serviceInformer := ctx.MetadataInformerFactory.ForResource(corev1.SchemeGroupVersion.WithResource("services"))
9999
ingressInformer := ctx.KubeSharedInformerFactory.Ingresses()
100100

101101
// build a list of InformerSynced functions that will be returned by the Register method.

pkg/controller/context.go

+26
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,15 @@ import (
2626
corev1 "k8s.io/api/core/v1"
2727
apierrors "k8s.io/apimachinery/pkg/api/errors"
2828
"k8s.io/apimachinery/pkg/api/resource"
29+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30+
"k8s.io/apimachinery/pkg/labels"
31+
"k8s.io/apimachinery/pkg/selection"
2932
"k8s.io/client-go/discovery"
3033
"k8s.io/client-go/kubernetes"
3134
"k8s.io/client-go/kubernetes/scheme"
3235
clientv1 "k8s.io/client-go/kubernetes/typed/core/v1"
3336
"k8s.io/client-go/metadata"
37+
"k8s.io/client-go/metadata/metadatainformer"
3438
"k8s.io/client-go/rest"
3539
"k8s.io/client-go/tools/clientcmd"
3640
"k8s.io/client-go/tools/record"
@@ -44,6 +48,7 @@ import (
4448
"github.com/cert-manager/cert-manager/internal/controller/feature"
4549
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
4650
"github.com/cert-manager/cert-manager/pkg/acme/accounts"
51+
cmacme "github.com/cert-manager/cert-manager/pkg/apis/acme/v1"
4752
clientset "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned"
4853
cmscheme "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned/scheme"
4954
informers "github.com/cert-manager/cert-manager/pkg/client/informers/externalversions"
@@ -84,6 +89,8 @@ type Context struct {
8489
CMClient clientset.Interface
8590
// GWClient is a GatewayAPI clientset.
8691
GWClient gwclient.Interface
92+
// MetadataClient is a PartialObjectMetadata client
93+
MetadataClient metadata.Interface
8794
// DiscoveryClient is a discovery interface. Usually set to Client.Discovery unless a fake client is in use.
8895
DiscoveryClient discovery.DiscoveryInterface
8996

@@ -98,6 +105,10 @@ type Context struct {
98105
// instances for cert-manager.io types
99106
SharedInformerFactory informers.SharedInformerFactory
100107

108+
// MetadataInformerFactory can be used to start partial metadata
109+
// informers
110+
MetadataInformerFactory metadatainformer.SharedInformerFactory
111+
101112
// GWShared can be used to obtain SharedIndexInformer instances for
102113
// gateway.networking.k8s.io types
103114
GWShared gwinformers.SharedInformerFactory
@@ -273,6 +284,20 @@ func NewContextFactory(ctx context.Context, opts ContextOptions) (*ContextFactor
273284
} else {
274285
kubeSharedInformerFactory = internalinformers.NewBaseKubeInformerFactory(clients.kubeClient, resyncPeriod, opts.Namespace)
275286
}
287+
r, err := labels.NewRequirement(cmacme.DomainLabelKey, selection.Exists, nil)
288+
if err != nil {
289+
panic(fmt.Errorf("internal error: failed to build label selector to filter HTTP-01 challenge resources: %w", err))
290+
}
291+
isHTTP01ChallengeResourceLabelSelector := labels.NewSelector().Add(*r)
292+
metadataInformerFactory := metadatainformer.NewFilteredSharedInformerFactory(clients.metadataOnlyClient, resyncPeriod, opts.Namespace, func(listOptions *metav1.ListOptions) {
293+
// metadataInformersFactory is at the moment only used for pods
294+
// and services for http-01 challenge which can be identified by
295+
// the same label keys, so it is okay to set the label selector
296+
// here. If we start using it for other resources then we'll
297+
// have to set the selectors on individual informers instead.
298+
listOptions.LabelSelector = isHTTP01ChallengeResourceLabelSelector.String()
299+
300+
})
276301

277302
gwSharedInformerFactory := gwinformers.NewSharedInformerFactoryWithOptions(clients.gwClient, resyncPeriod, gwinformers.WithNamespace(opts.Namespace))
278303

@@ -286,6 +311,7 @@ func NewContextFactory(ctx context.Context, opts ContextOptions) (*ContextFactor
286311
SharedInformerFactory: sharedInformerFactory,
287312
GWShared: gwSharedInformerFactory,
288313
GatewaySolverEnabled: clients.gatewayAvailable,
314+
MetadataInformerFactory: metadataInformerFactory,
289315
ContextOptions: opts,
290316
},
291317
}, nil

pkg/issuer/acme/http/http.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ import (
2929

3030
corev1 "k8s.io/api/core/v1"
3131
utilerrors "k8s.io/apimachinery/pkg/util/errors"
32-
corev1listers "k8s.io/client-go/listers/core/v1"
3332
networkingv1listers "k8s.io/client-go/listers/networking/v1"
33+
"k8s.io/client-go/tools/cache"
3434
k8snet "k8s.io/utils/net"
3535
gwapilisters "sigs.k8s.io/gateway-api/pkg/client/listers/apis/v1beta1"
3636

@@ -59,8 +59,8 @@ var (
5959
type Solver struct {
6060
*controller.Context
6161

62-
podLister corev1listers.PodLister
63-
serviceLister corev1listers.ServiceLister
62+
podLister cache.GenericLister
63+
serviceLister cache.GenericLister
6464
ingressLister networkingv1listers.IngressLister
6565
httpRouteLister gwapilisters.HTTPRouteLister
6666

@@ -74,8 +74,8 @@ type reachabilityTest func(ctx context.Context, url *url.URL, key string, dnsSer
7474
func NewSolver(ctx *controller.Context) (*Solver, error) {
7575
return &Solver{
7676
Context: ctx,
77-
podLister: ctx.KubeSharedInformerFactory.Pods().Lister(),
78-
serviceLister: ctx.KubeSharedInformerFactory.Services().Lister(),
77+
podLister: ctx.MetadataInformerFactory.ForResource(corev1.SchemeGroupVersion.WithResource("pods")).Lister(),
78+
serviceLister: ctx.MetadataInformerFactory.ForResource(corev1.SchemeGroupVersion.WithResource("services")).Lister(),
7979
ingressLister: ctx.KubeSharedInformerFactory.Ingresses().Lister(),
8080
httpRouteLister: ctx.GWShared.Gateway().V1beta1().HTTPRoutes().Lister(),
8181
testReachability: testReachability,
@@ -108,19 +108,19 @@ func (s *Solver) Present(ctx context.Context, issuer v1.GenericIssuer, ch *cmacm
108108
log := logf.FromContext(ctx).WithName(loggerName)
109109
ctx = logf.NewContext(ctx, log)
110110

111-
_, podErr := s.ensurePod(ctx, ch)
112-
svc, svcErr := s.ensureService(ctx, ch)
111+
podErr := s.ensurePod(ctx, ch)
112+
svcName, svcErr := s.ensureService(ctx, ch)
113113
if svcErr != nil {
114114
return utilerrors.NewAggregate([]error{podErr, svcErr})
115115
}
116116
var ingressErr, gatewayErr error
117117
if ch.Spec.Solver.HTTP01 != nil {
118118
if ch.Spec.Solver.HTTP01.Ingress != nil {
119-
_, ingressErr = s.ensureIngress(ctx, ch, svc.Name)
119+
_, ingressErr = s.ensureIngress(ctx, ch, svcName)
120120
return utilerrors.NewAggregate([]error{podErr, svcErr, ingressErr})
121121
}
122122
if ch.Spec.Solver.HTTP01.GatewayHTTPRoute != nil {
123-
_, gatewayErr = s.ensureGatewayHTTPRoute(ctx, ch, svc.Name)
123+
_, gatewayErr = s.ensureGatewayHTTPRoute(ctx, ch, svcName)
124124
return utilerrors.NewAggregate([]error{podErr, svcErr, gatewayErr})
125125
}
126126
}

pkg/issuer/acme/http/pod.go

+19-13
Original file line numberDiff line numberDiff line change
@@ -47,35 +47,36 @@ func podLabels(ch *cmacme.Challenge) map[string]string {
4747
}
4848
}
4949

50-
func (s *Solver) ensurePod(ctx context.Context, ch *cmacme.Challenge) (*corev1.Pod, error) {
50+
func (s *Solver) ensurePod(ctx context.Context, ch *cmacme.Challenge) error {
5151
log := logf.FromContext(ctx).WithName("ensurePod")
5252

5353
log.V(logf.DebugLevel).Info("checking for existing HTTP01 solver pods")
5454
existingPods, err := s.getPodsForChallenge(ctx, ch)
5555
if err != nil {
56-
return nil, err
56+
return err
5757
}
5858
if len(existingPods) == 1 {
5959
logf.WithRelatedResource(log, existingPods[0]).Info("found one existing HTTP01 solver pod")
60-
return existingPods[0], nil
60+
return nil
6161
}
6262
if len(existingPods) > 1 {
6363
log.V(logf.InfoLevel).Info("multiple challenge solver pods found for challenge. cleaning up all existing pods.")
6464
err := s.cleanupPods(ctx, ch)
6565
if err != nil {
66-
return nil, err
66+
return err
6767
}
68-
return nil, fmt.Errorf("multiple existing challenge solver pods found and cleaned up. retrying challenge sync")
68+
return fmt.Errorf("multiple existing challenge solver pods found and cleaned up. retrying challenge sync")
6969
}
7070

7171
log.V(logf.InfoLevel).Info("creating HTTP01 challenge solver pod")
7272

73-
return s.createPod(ctx, ch)
73+
_, err = s.createPod(ctx, ch)
74+
return err
7475
}
7576

7677
// getPodsForChallenge returns a list of pods that were created to solve
7778
// the given challenge
78-
func (s *Solver) getPodsForChallenge(ctx context.Context, ch *cmacme.Challenge) ([]*corev1.Pod, error) {
79+
func (s *Solver) getPodsForChallenge(ctx context.Context, ch *cmacme.Challenge) ([]*metav1.PartialObjectMetadata, error) {
7980
log := logf.FromContext(ctx)
8081

8182
podLabels := podLabels(ch)
@@ -88,19 +89,24 @@ func (s *Solver) getPodsForChallenge(ctx context.Context, ch *cmacme.Challenge)
8889
orderSelector = orderSelector.Add(*req)
8990
}
9091

91-
podList, err := s.podLister.Pods(ch.Namespace).List(orderSelector)
92+
podMetadataList, err := s.podLister.ByNamespace(ch.Namespace).List(orderSelector)
9293
if err != nil {
9394
return nil, err
9495
}
9596

96-
var relevantPods []*corev1.Pod
97-
for _, pod := range podList {
98-
if !metav1.IsControlledBy(pod, ch) {
99-
logf.WithRelatedResource(log, pod).Info("found existing solver pod for this challenge resource, however " +
97+
var relevantPods []*metav1.PartialObjectMetadata
98+
for _, pod := range podMetadataList {
99+
// TODO: can we use a metadata lister instead?
100+
p, ok := pod.(*metav1.PartialObjectMetadata)
101+
if !ok {
102+
return nil, fmt.Errorf("internal error: cannot cast PartialMetadata: %+#v", pod)
103+
}
104+
if !metav1.IsControlledBy(p, ch) {
105+
logf.WithRelatedResource(log, p).Info("found existing solver pod for this challenge resource, however " +
100106
"it does not have an appropriate OwnerReference referencing this challenge. Skipping it altogether.")
101107
continue
102108
}
103-
relevantPods = append(relevantPods, pod)
109+
relevantPods = append(relevantPods, p)
104110
}
105111

106112
return relevantPods, nil

pkg/issuer/acme/http/service.go

+20-12
Original file line numberDiff line numberDiff line change
@@ -31,34 +31,37 @@ import (
3131
logf "github.com/cert-manager/cert-manager/pkg/logs"
3232
)
3333

34-
func (s *Solver) ensureService(ctx context.Context, ch *cmacme.Challenge) (*corev1.Service, error) {
34+
// ensureService ensures that a Service exists for the given Challenge. It
35+
// returns the name of the Service and error if any.
36+
func (s *Solver) ensureService(ctx context.Context, ch *cmacme.Challenge) (string, error) {
3537
log := logf.FromContext(ctx).WithName("ensureService")
3638

3739
log.V(logf.DebugLevel).Info("checking for existing HTTP01 solver services for challenge")
3840
existingServices, err := s.getServicesForChallenge(ctx, ch)
3941
if err != nil {
40-
return nil, err
42+
return "", err
4143
}
4244
if len(existingServices) == 1 {
4345
logf.WithRelatedResource(log, existingServices[0]).Info("found one existing HTTP01 solver Service for challenge resource")
44-
return existingServices[0], nil
46+
return existingServices[0].Name, nil
4547
}
4648
if len(existingServices) > 1 {
4749
log.V(logf.DebugLevel).Info("multiple challenge solver services found for challenge. cleaning up all existing services.")
4850
err := s.cleanupServices(ctx, ch)
4951
if err != nil {
50-
return nil, err
52+
return "", err
5153
}
52-
return nil, fmt.Errorf("multiple existing challenge solver services found and cleaned up. retrying challenge sync")
54+
return "", fmt.Errorf("multiple existing challenge solver services found and cleaned up. retrying challenge sync")
5355
}
5456

5557
log.V(logf.DebugLevel).Info("creating HTTP01 challenge solver service")
56-
return s.createService(ctx, ch)
58+
svc, err := s.createService(ctx, ch)
59+
return svc.Name, err
5760
}
5861

5962
// getServicesForChallenge returns a list of services that were created to solve
6063
// http challenges for the given domain
61-
func (s *Solver) getServicesForChallenge(ctx context.Context, ch *cmacme.Challenge) ([]*corev1.Service, error) {
64+
func (s *Solver) getServicesForChallenge(ctx context.Context, ch *cmacme.Challenge) ([]*metav1.PartialObjectMetadata, error) {
6265
log := logf.FromContext(ctx).WithName("getServicesForChallenge")
6366

6467
podLabels := podLabels(ch)
@@ -71,19 +74,24 @@ func (s *Solver) getServicesForChallenge(ctx context.Context, ch *cmacme.Challen
7174
selector = selector.Add(*req)
7275
}
7376

74-
serviceList, err := s.serviceLister.Services(ch.Namespace).List(selector)
77+
serviceList, err := s.serviceLister.ByNamespace(ch.Namespace).List(selector)
7578
if err != nil {
7679
return nil, err
7780
}
7881

79-
var relevantServices []*corev1.Service
82+
var relevantServices []*metav1.PartialObjectMetadata
8083
for _, service := range serviceList {
81-
if !metav1.IsControlledBy(service, ch) {
82-
logf.WithRelatedResource(log, service).Info("found existing solver pod for this challenge resource, however " +
84+
// TODO: can we use a metadata specific lister instead?
85+
s, ok := service.(*metav1.PartialObjectMetadata)
86+
if !ok {
87+
return nil, fmt.Errorf("internal error: cannot cast Service PartialObjectMetadata")
88+
}
89+
if !metav1.IsControlledBy(s, ch) {
90+
logf.WithRelatedResource(log, s).Info("found existing solver pod for this challenge resource, however " +
8391
"it does not have an appropriate OwnerReference referencing this challenge. Skipping it altogether.")
8492
continue
8593
}
86-
relevantServices = append(relevantServices, service)
94+
relevantServices = append(relevantServices, s)
8795
}
8896

8997
return relevantServices, nil

0 commit comments

Comments
 (0)