cert-manager/pkg/controller/cainjector/indexers.go
jetstack-bot 0b9366c0fb
Merge pull request #6232 from inteon/fix_log_reassignment
[BUGFIX] Incorrect re-assignment of cross-invocation variable
2023-07-26 13:35:07 +02:00

225 lines
7.6 KiB
Go

/*
Copyright 2020 The cert-manager Authors.
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 cainjector
import (
"context"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/handler"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
)
const (
// injectFromPath is the index key used to look up the value of inject-ca-from on targeted objects
injectFromPath = ".metadata.annotations.inject-ca-from"
// injectFromSecretPath is the index key used to look up the value of
// inject-ca-from-secret on targeted objects
injectFromSecretPath = ".metadata.annotations.inject-ca-from-secret"
)
// certFromSecretToInjectableMapFuncBuilder returns a handler.MapFunc that, for
// a Secret change, ensures that if this Secret is a Certificate Secret of
// Certificate that is configured as a CA source for an injectable via
// inject-ca-from annotation, a reconcile loop will be triggered for this
// injectable
func certFromSecretToInjectableMapFuncBuilder(cl client.Reader, log logr.Logger, config setup) handler.MapFunc {
return func(ctx context.Context, obj client.Object) []ctrl.Request {
secretName := types.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()}
certName := owningCertForSecret(obj.(*corev1.Secret))
if certName == nil {
return nil
}
log := log.WithValues("type", config.resourceName, "secret", secretName, "certificate", *certName)
// confirm that a service owns this cert
var cert cmapi.Certificate
if err := cl.Get(ctx, *certName, &cert); err != nil {
// TODO(directxman12): check for not found error?
log.Error(err, "unable to fetch certificate that owns the secret")
return nil
}
objs := config.listType.DeepCopyObject().(client.ObjectList)
if err := cl.List(ctx, objs, client.MatchingFields{injectFromPath: certName.String()}); err != nil {
log.Error(err, "unable to fetch injectables associated with certificate")
return nil
}
var reqs []ctrl.Request
if err := meta.EachListItem(objs, func(obj runtime.Object) error {
metaInfo, err := meta.Accessor(obj)
if err != nil {
log.Error(err, "unable to get metadata from list item")
// continue on error
return nil
}
reqs = append(reqs, ctrl.Request{NamespacedName: types.NamespacedName{
Name: metaInfo.GetName(),
Namespace: metaInfo.GetNamespace(),
}})
return nil
}); err != nil {
log.Error(err, "unable get items from list")
return nil
}
return reqs
}
}
// certToInjectableMapFuncBuilder returns a handler.MapFunc that, for
// a Certificate change, ensures that if this Certificate that is configured as
// a CA source for an injectable via inject-ca-from annotation, a reconcile loop
// will be triggered for this injectable
func certToInjectableMapFuncBuilder(cl client.Reader, log logr.Logger, config setup) handler.MapFunc {
return func(ctx context.Context, obj client.Object) []ctrl.Request {
certName := types.NamespacedName{Namespace: obj.GetNamespace(), Name: obj.GetName()}
log := log.WithValues("type", config.resourceName, "certificate", certName)
objs := config.listType.DeepCopyObject().(client.ObjectList)
if err := cl.List(ctx, objs, client.MatchingFields{injectFromPath: certName.String()}); err != nil {
log.Error(err, "unable to fetch injectables associated with certificate")
return nil
}
var reqs []ctrl.Request
if err := meta.EachListItem(objs, func(obj runtime.Object) error {
metaInfo, err := meta.Accessor(obj)
if err != nil {
log.Error(err, "unable to get metadata from list item")
// continue on error
return nil
}
reqs = append(reqs, ctrl.Request{NamespacedName: types.NamespacedName{
Name: metaInfo.GetName(),
Namespace: metaInfo.GetNamespace(),
}})
return nil
}); err != nil {
log.Error(err, "unable get items from list")
return nil
}
return reqs
}
}
// secretForInjectableMapFuncBuilder returns a handler.MapFunc that, for a
// config for particular injectable type (i.e CRD, APIService) and a Secret,
// returns all injectables that have the inject-ca-from-secret annotion with the
// given secret name. This will be used in an event handler to ensure that
// changes to a Secret triggers a reconcile loop for the relevant injectable.
func secretForInjectableMapFuncBuilder(cl client.Reader, log logr.Logger, config setup) handler.MapFunc {
return func(ctx context.Context, obj client.Object) []ctrl.Request {
secretName := types.NamespacedName{Namespace: obj.GetNamespace(), Name: obj.GetName()}
log := log.WithValues("type", config.resourceName, "secret", secretName)
objs := config.listType.DeepCopyObject().(client.ObjectList)
// TODO: ensure that this is cache lister, not a direct client
if err := cl.List(ctx, objs, client.MatchingFields{injectFromSecretPath: secretName.String()}); err != nil {
log.Error(err, "unable to fetch injectables associated with secret")
return nil
}
var reqs []ctrl.Request
if err := meta.EachListItem(objs, func(obj runtime.Object) error {
metaInfo, err := meta.Accessor(obj)
if err != nil {
log.Error(err, "unable to get metadata from list item")
// continue on error
return nil
}
reqs = append(reqs, ctrl.Request{NamespacedName: types.NamespacedName{
Name: metaInfo.GetName(),
Namespace: metaInfo.GetNamespace(),
}})
return nil
}); err != nil {
log.Error(err, "unable get items from list")
return nil
}
return reqs
}
}
// injectableCAFromIndexer is an IndexerFunc indexing on certificates
// referenced by injectables.
func injectableCAFromIndexer(rawObj client.Object) []string {
metaInfo, err := meta.Accessor(rawObj)
if err != nil {
return nil
}
// skip invalid certificate names
certNameRaw := metaInfo.GetAnnotations()[cmapi.WantInjectAnnotation]
if certNameRaw == "" {
return nil
}
certName := splitNamespacedName(certNameRaw)
if certName.Namespace == "" {
return nil
}
return []string{certNameRaw}
}
// injectableCAFromSecretIndexer is an IndexerFunc indexing on secrets
// referenced by injectables.
func injectableCAFromSecretIndexer(rawObj client.Object) []string {
metaInfo, err := meta.Accessor(rawObj)
if err != nil {
return nil
}
// skip invalid secret names
secretNameRaw := metaInfo.GetAnnotations()[cmapi.WantInjectFromSecretAnnotation]
if secretNameRaw == "" {
return nil
}
secretName := splitNamespacedName(secretNameRaw)
if secretName.Namespace == "" {
return nil
}
return []string{secretNameRaw}
}
// hasInjectableAnnotation returns predicates that determine whether an object is a
// cainjector injectable by looking at whether it has one of the three
// annotations used to mark injectables.
func hasInjectableAnnotation(o client.Object) bool {
annots := o.GetAnnotations()
if _, ok := annots[cmapi.WantInjectAPIServerCAAnnotation]; ok {
return true
}
if _, ok := annots[cmapi.WantInjectAnnotation]; ok {
return true
}
if _, ok := annots[cmapi.WantInjectFromSecretAnnotation]; ok {
return true
}
return false
}