Newer
Older
AnthosCertManager / pkg / controller / certificaterequests / controller.go
package certificaterequests

import (
	"context"
	"fmt"

	k8sErrors "k8s.io/apimachinery/pkg/api/errors"

	v1 "gitbucket.jerxie.com/yangyangxie/AnthosCertManager/pkg/apis/anthoscertmanager/v1"
	acmclient "gitbucket.jerxie.com/yangyangxie/AnthosCertManager/pkg/client/clientset/versioned"
	acmlisters "gitbucket.jerxie.com/yangyangxie/AnthosCertManager/pkg/client/listers/anthoscertmanager/v1"
	controllerpkg "gitbucket.jerxie.com/yangyangxie/AnthosCertManager/pkg/controller"
	"gitbucket.jerxie.com/yangyangxie/AnthosCertManager/pkg/issuer"
	logf "gitbucket.jerxie.com/yangyangxie/AnthosCertManager/pkg/logs"

	"github.com/go-logr/logr"
	"k8s.io/apimachinery/pkg/runtime/schema"
	corelisters "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"
	"k8s.io/client-go/util/workqueue"
	"k8s.io/utils/clock"
)

var keyFunc = controllerpkg.KeyFunc

// Issuer implements the funcationalitiy to sign a certificate request for a particular issue type.
type Issuer interface {
	Sign(context.Context, *v1.CertificateRequest, v1.GenericIssuer) (*issuer.IssueResponse, error)
}

// Issuer Contractor builds a Issuer instance using the given controller
// context.
type IssuerConstructor func(*controllerpkg.Context) Issuer

type controller struct {
	//helper issuer.Helper

	// clientset used to update cert-manager API resources
	acmClient acmclient.Interface

	// fieldManager is the manager name used for the Apply operations.
	fieldManager string

	certificateRequestLister acmlisters.CertificateRequestLister

	// we need to wait for Secrets to be synced to avoid a situation where CA issuer's Secret
	// is not yet in cached at a time when issuance is attempted,
	// more details at https://github.com/cert-manager/cert-manager/issues/5216
	secretLister corelisters.SecretLister

	queue workqueue.RateLimitingInterface

	// logger to be used by this controller
	log logr.Logger

	// used to record Events about resources to the API
	recorder record.EventRecorder

	// the issuer kind to react to when a certificate request is synced
	issuerType string

	issuerLister        acmlisters.IssuerLister
	clusterIssuerLister acmlisters.ClusterIssuerLister

	// extraInformerResources are the set of resources which should cause
	// reconciles if owned by a CertifcateRequest.
	extraInformerResources []schema.GroupVersionResource

	// Issuer to call sign function
	issuerConstructor IssuerConstructor
	issuer            Issuer

	// used for testing
	clock clock.Clock

	// reporter *util.Reporter
}

// NewController will construct a new certificaterequest controller using the given
// Issuer implementation.
func NewController(issuerType string, issuerConstructor IssuerConstructor, extraInformerResources ...schema.GroupVersionResource) *controller {
	return &controller{
		issuerType:             issuerType,
		issuerConstructor:      issuerConstructor,
		extraInformerResources: extraInformerResources,
	}
}

func (c *controller) Register(ctx *controllerpkg.Context) (workqueue.RateLimitingInterface, []cache.InformerSynced, error) {
	componentName := "certificaterequests-issuer-" + c.issuerType

	c.log = logf.FromContext(ctx.RootContext, componentName)

	// create a working queue
	c.queue = workqueue.NewNamedRateLimitingQueue(controllerpkg.DefaultItemBasedRateLimiter(), componentName)

	secretsInformer := ctx.KubeSharedInformerFactory.Core().V1().Secrets()
	issuerInformer := ctx.SharedInformerFactory.AnthosCertmanager().V1().Issuers()
	c.issuerLister = issuerInformer.Lister()
	c.secretLister = secretsInformer.Lister()

	// obtain references to all the informers used by this controller
	certificateRequestInformer := ctx.SharedInformerFactory.AnthosCertmanager().V1().CertificateRequests()

	mustSync := []cache.InformerSynced{
		certificateRequestInformer.Informer().HasSynced,
		issuerInformer.Informer().HasSynced,
		secretsInformer.Informer().HasSynced,
	}

	// If the manger is scoped to all namespaces, we should also obtain a lister for clusterissuers.
	if ctx.Namespace == "" {
		clusterIssuerInformer := ctx.SharedInformerFactory.AnthosCertmanager().V1().ClusterIssuers()
		c.clusterIssuerLister = clusterIssuerInformer.Lister()

		// register handler function for cluster issuers resources
		clusterIssuerInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{WorkFunc: c.handleGenericIssuer})
	}

	c.certificateRequestLister = certificateRequestInformer.Lister()

	// register handler functions
	certificateRequestInformer.Informer().AddEventHandler(&controllerpkg.QueuingEventHandler{Queue: c.queue})
	issuerInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{WorkFunc: c.handleGenericIssuer})

	// create an issuer helper for reading generic issuers
	// c.helper = issuer.NewHelper(c.issuerLister, c.clusterIssuerLister)

	// clock is used to set the FailureTime of failed CertificateRequests
	c.clock = ctx.Clock
	// recorder records events about resources to the Kubernetes api
	c.recorder = ctx.Recorder
	// c.reporter = util.NewReporter(c.clock, c.recorder)
	c.acmClient = ctx.ACMClient
	c.fieldManager = ctx.FieldManager

	// Construct the issuer implementation with the built component context.
	c.issuer = c.issuerConstructor(ctx)

	c.log.V(logf.DebugLevel).Info("new certificate request controller registered",
		"type", c.issuerType)

	return c.queue, mustSync, nil

}

// ProcessItem is the worker function that will be called with a new key from
// the workqueue. A key corresponds to a certificate request object.
func (c *controller) ProcessItem(ctx context.Context, key string) error {
	log := logf.FromContext(ctx)
	dbg := log.V(logf.DebugLevel)

	namespace, name, err := cache.SplitMetaNamespaceKey(key)
	if err != nil {
		log.Error(err, "invalid resource key")
		return nil
	}

	cr, err := c.certificateRequestLister.CertificateRequests(namespace).Get(name)
	if err != nil {
		if k8sErrors.IsNotFound(err) {
			dbg.Info(fmt.Sprintf("certificate request in work queue no longer exists: %s", err))
			return nil
		}

		return err
	}

	ctx = logf.NewContext(ctx, logf.WithResource(log, cr))
	return c.Sync(ctx, cr)
}

func certificateRequestGetter(lister acmlisters.CertificateRequestLister) func(namespace, name string) (interface{}, error) {
	return func(namespace, name string) (interface{}, error) {
		return lister.CertificateRequests(namespace).Get(name)
	}
}