* Configurable issuer duration and renewBefore [1/3] This is part one of (probably) three parts manually moving the changes from commit 723015174a167d746323f506ab3575cfb243d8bd to the new master. This commit moves the basic functionality of configurable duration while skipping e2e tests and docs. It does not include new work. Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Configurable issuer duration and renewBefore [2/3] This commit moves over most of the e2e testing updates, some things are intentionally left out as they may be obsolete Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Configurable issuer duration and renewBefore [3/3] This commit moves the documentation changes, completely the migration of the original code to the latest master Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Rerunning all hack scripts with since the massive bazel update Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Add missing boilerplate headers Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Rerun codegen hack Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Rerunning update-docs hack Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fix failing unit tests Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fix build errors in e2e tests Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Rerun update-deps Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Don't recreate the CA issuer, it already exists Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Need to create new issuers for the duration and renew time tests because those fields are set in the issuer, so make sure they are named uniquely Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Add duration e2e tests for self-signed issuer Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Add duration e2e tests for vault w/ custom mount path Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Add validation to disallow acme certificates with duration and renewBefore set and update unit tests to verify Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Update docs to mention duration/renew for self-signed issuer and fix potential parsing errors with rst formatting Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Self-signed issuer was missing duration validation Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fix a bug causing certificates with a short enough renew-before w.r.t their duration to be renewed instantly and forever Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Print the exact time until renewal Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Move duration and renwal validation to the issuer validation Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Update e2e tests to work with new validation Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Add e2e test for the self-signed issuer Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Redo cert duration and renew before to appear as part of the CSR and not the issuer Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Updating tests to match new duration/renewbefore format Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Update e2e tests to match new format Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Update docs to reflect changing the field from issuers to certificates Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Remove event firing and replace with a TODO as of discussion on PR Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Run hack scripts Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Remove the sync unit test since without events there is no way to catch the warnings that it was testing Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Use IssuerOptions RenewBeforeExpiryDuration if certificates dont set a renewBefore value for immediate renewal checks Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Delete check on certificate data length in e2e test for certificate duration as there is no reason it should be there Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Update e2e tests since certificate creation will never generate an event Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Rerunning hack scripts after big rebase Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fix a few problems that slipped through during the rebase Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fix an e2e error that resulted from the rebase Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Add unit test for the calculateTimeBeforeExpiry function Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Adding back in a bunch of missing error checks Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Remove unused function Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Add missing boilerplate Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Remove unused constant Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Move log constants to function body Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Rerun hack scripts Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Remove mistakenly commited file Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Remove double-import of util package Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fix bad function call in e2e vault issuer Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Change duration and renewBefore to be pointer fields as they are optional Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Remove wrong vault issuer test that got passed the rebase somehow Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Change e2e to use pointer format Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Move e2e cert tests out of issuer test file Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Move e2e self-signed issuer test to new location Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Make sure to check for nil in GenerateTemplate Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Add more empty checks to be safe Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Rerunning hacks after rebase Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fix bad function call in new e2e test Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Try not setting duration and renewbefore on acme e2e tests Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Zero checks should really just be replaced by nil tests, zero should be caught as any other too-small value Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fixed a missing nil check that got away Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Change e2e duration test format to use pointer times to better simulate API calls Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fix sync unit test to match e2e test format Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fix vault e2e test Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Revert changes to Certificate sync function Signed-off-by: James Munnelly <james@munnelly.eu> * Remove selfsigned e2e issuer.go Signed-off-by: James Munnelly <james@munnelly.eu> * Don't use ACME issuer in duration example and tidy up line endings Signed-off-by: James Munnelly <james@munnelly.eu> * Allow renewBefore to be set on ACME certificates Signed-off-by: James Munnelly <james@munnelly.eu> * Update renewBefore ACME docs. Remove unused fields. Signed-off-by: James Munnelly <james@munnelly.eu> * Rename calculateTimeBeforeExpiry to calculateDurationUntilRenew Signed-off-by: James Munnelly <james@munnelly.eu>
387 lines
13 KiB
Go
387 lines
13 KiB
Go
/*
|
|
Copyright 2018 The Jetstack cert-manager contributors.
|
|
|
|
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 certificates
|
|
|
|
import (
|
|
"context"
|
|
"crypto/x509"
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
"time"
|
|
|
|
api "k8s.io/api/core/v1"
|
|
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
|
"k8s.io/apimachinery/pkg/util/runtime"
|
|
|
|
"github.com/golang/glog"
|
|
"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
|
|
"github.com/jetstack/cert-manager/pkg/apis/certmanager/validation"
|
|
"github.com/jetstack/cert-manager/pkg/issuer"
|
|
"github.com/jetstack/cert-manager/pkg/util"
|
|
"github.com/jetstack/cert-manager/pkg/util/errors"
|
|
"github.com/jetstack/cert-manager/pkg/util/kube"
|
|
"github.com/jetstack/cert-manager/pkg/util/pki"
|
|
)
|
|
|
|
const (
|
|
errorIssuerNotFound = "IssuerNotFound"
|
|
errorIssuerNotReady = "IssuerNotReady"
|
|
errorIssuerInit = "IssuerInitError"
|
|
errorSavingCertificate = "SaveCertError"
|
|
errorConfig = "ConfigError"
|
|
|
|
reasonIssuingCertificate = "IssueCert"
|
|
reasonRenewingCertificate = "RenewCert"
|
|
|
|
successCertificateIssued = "CertIssued"
|
|
successCertificateRenewed = "CertRenewed"
|
|
|
|
messageErrorSavingCertificate = "Error saving TLS certificate: "
|
|
|
|
messageIssuingCertificate = "Issuing certificate..."
|
|
messageRenewingCertificate = "Renewing certificate..."
|
|
|
|
messageCertificateIssued = "Certificate issued successfully"
|
|
messageCertificateRenewed = "Certificate renewed successfully"
|
|
)
|
|
|
|
const (
|
|
TLSCAKey = "ca.crt"
|
|
)
|
|
|
|
var (
|
|
certificateGvk = v1alpha1.SchemeGroupVersion.WithKind("Certificate")
|
|
)
|
|
|
|
// to help testing
|
|
var now = time.Now
|
|
|
|
func (c *Controller) Sync(ctx context.Context, crt *v1alpha1.Certificate) (requeue bool, err error) {
|
|
crtCopy := crt.DeepCopy()
|
|
defer func() {
|
|
if _, saveErr := c.updateCertificateStatus(crt, crtCopy); saveErr != nil {
|
|
err = utilerrors.NewAggregate([]error{saveErr, err})
|
|
}
|
|
}()
|
|
|
|
el := validation.ValidateCertificate(crtCopy)
|
|
if len(el) > 0 {
|
|
msg := fmt.Sprintf("Resource validation failed: %v", el.ToAggregate())
|
|
crtCopy.UpdateStatusCondition(v1alpha1.CertificateConditionReady, v1alpha1.ConditionFalse, errorConfig, msg, false)
|
|
return
|
|
}
|
|
|
|
for i, c := range crtCopy.Status.Conditions {
|
|
if c.Type == v1alpha1.CertificateConditionReady {
|
|
if c.Reason == errorConfig && c.Status == v1alpha1.ConditionFalse {
|
|
crtCopy.Status.Conditions = append(crtCopy.Status.Conditions[:i], crtCopy.Status.Conditions[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// step zero: check if the referenced issuer exists and is ready
|
|
issuerObj, err := c.getGenericIssuer(crtCopy)
|
|
if err != nil {
|
|
s := fmt.Sprintf("Issuer %s does not exist", err.Error())
|
|
glog.Info(s)
|
|
c.Recorder.Event(crtCopy, api.EventTypeWarning, errorIssuerNotFound, s)
|
|
return false, err
|
|
}
|
|
|
|
el = validation.ValidateCertificateForIssuer(crtCopy, issuerObj)
|
|
if len(el) > 0 {
|
|
msg := fmt.Sprintf("Resource validation failed: %v", el.ToAggregate())
|
|
crtCopy.UpdateStatusCondition(v1alpha1.CertificateConditionReady, v1alpha1.ConditionFalse, errorConfig, msg, false)
|
|
return
|
|
}
|
|
|
|
for i, c := range crtCopy.Status.Conditions {
|
|
if c.Type == v1alpha1.CertificateConditionReady {
|
|
if c.Reason == errorConfig && c.Status == v1alpha1.ConditionFalse {
|
|
crtCopy.Status.Conditions = append(crtCopy.Status.Conditions[:i], crtCopy.Status.Conditions[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
issuerReady := issuerObj.HasCondition(v1alpha1.IssuerCondition{
|
|
Type: v1alpha1.IssuerConditionReady,
|
|
Status: v1alpha1.ConditionTrue,
|
|
})
|
|
if !issuerReady {
|
|
s := fmt.Sprintf("Issuer %s not ready", issuerObj.GetObjectMeta().Name)
|
|
glog.Info(s)
|
|
c.Recorder.Event(crtCopy, api.EventTypeWarning, errorIssuerNotReady, s)
|
|
return false, fmt.Errorf(s)
|
|
}
|
|
|
|
i, err := c.IssuerFactory().IssuerFor(issuerObj)
|
|
if err != nil {
|
|
s := "Error initializing issuer: " + err.Error()
|
|
glog.Info(s)
|
|
c.Recorder.Event(crtCopy, api.EventTypeWarning, errorIssuerInit, s)
|
|
return false, err
|
|
}
|
|
|
|
key, err := kube.SecretTLSKey(c.secretLister, crtCopy.Namespace, crtCopy.Spec.SecretName)
|
|
// if we don't have a private key, we need to trigger a re-issue immediately
|
|
if k8sErrors.IsNotFound(err) || errors.IsInvalidData(err) {
|
|
return c.issue(ctx, i, crtCopy)
|
|
}
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
// grab existing certificate and validate private key
|
|
cert, err := kube.SecretTLSCert(c.secretLister, crtCopy.Namespace, crtCopy.Spec.SecretName)
|
|
// if we don't have a certificate, we need to trigger a re-issue immediately
|
|
if k8sErrors.IsNotFound(err) || errors.IsInvalidData(err) {
|
|
return c.issue(ctx, i, crtCopy)
|
|
}
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
// begin checking if the TLS certificate is valid/needs a re-issue or renew
|
|
|
|
// check if the private key is the corresponding pair to the certificate
|
|
matches, err := pki.PublicKeyMatchesCertificate(key.Public(), cert)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if !matches {
|
|
return c.issue(ctx, i, crtCopy)
|
|
}
|
|
|
|
// validate the common name is correct
|
|
expectedCN := pki.CommonNameForCertificate(crtCopy)
|
|
if expectedCN != cert.Subject.CommonName {
|
|
return c.issue(ctx, i, crtCopy)
|
|
}
|
|
|
|
// validate the dns names are correct
|
|
expectedDNSNames := pki.DNSNamesForCertificate(crtCopy)
|
|
if !util.EqualUnsorted(cert.DNSNames, expectedDNSNames) {
|
|
return c.issue(ctx, i, crtCopy)
|
|
}
|
|
|
|
// check if the certificate needs renewal
|
|
needsRenew := c.Context.IssuerOptions.CertificateNeedsRenew(cert, crt.Spec.RenewBefore)
|
|
if needsRenew {
|
|
return c.issue(ctx, i, crtCopy)
|
|
}
|
|
|
|
// TODO: add checks for KeySize, KeyAlgorithm fields
|
|
// TODO: add checks for Organization field
|
|
// TODO: add checks for IsCA field
|
|
|
|
// end checking if the TLS certificate is valid/needs a re-issue or renew
|
|
|
|
return false, nil
|
|
}
|
|
|
|
// TODO: replace with a call to controllerpkg.Helper.GetGenericIssuer
|
|
func (c *Controller) getGenericIssuer(crt *v1alpha1.Certificate) (v1alpha1.GenericIssuer, error) {
|
|
switch crt.Spec.IssuerRef.Kind {
|
|
case "", v1alpha1.IssuerKind:
|
|
return c.issuerLister.Issuers(crt.Namespace).Get(crt.Spec.IssuerRef.Name)
|
|
case v1alpha1.ClusterIssuerKind:
|
|
if c.clusterIssuerLister == nil {
|
|
return nil, fmt.Errorf("cannot get ClusterIssuer for %q as cert-manager is scoped to a single namespace", crt.Name)
|
|
}
|
|
return c.clusterIssuerLister.Get(crt.Spec.IssuerRef.Name)
|
|
default:
|
|
return nil, fmt.Errorf(`invalid value %q for certificate issuer kind. Must be empty, %q or %q`, crt.Spec.IssuerRef.Kind, v1alpha1.IssuerKind, v1alpha1.ClusterIssuerKind)
|
|
}
|
|
}
|
|
|
|
func (c *Controller) scheduleRenewal(crt *v1alpha1.Certificate) {
|
|
key, err := keyFunc(crt)
|
|
|
|
if err != nil {
|
|
runtime.HandleError(fmt.Errorf("error getting key for certificate resource: %s", err.Error()))
|
|
return
|
|
}
|
|
|
|
cert, err := kube.SecretTLSCert(c.secretLister, crt.Namespace, crt.Spec.SecretName)
|
|
|
|
if err != nil {
|
|
if !errors.IsInvalidData(err) {
|
|
runtime.HandleError(fmt.Errorf("[%s/%s] Error getting certificate '%s': %s", crt.Namespace, crt.Name, crt.Spec.SecretName, err.Error()))
|
|
}
|
|
return
|
|
}
|
|
|
|
renewIn := c.calculateDurationUntilRenew(cert, crt)
|
|
|
|
c.scheduledWorkQueue.Add(key, renewIn)
|
|
|
|
glog.Infof("Certificate %s/%s scheduled for renewal in %s", crt.Namespace, crt.Name, renewIn.String())
|
|
}
|
|
|
|
// issuerKind returns the kind of issuer for a certificate
|
|
func issuerKind(crt *v1alpha1.Certificate) string {
|
|
if crt.Spec.IssuerRef.Kind == "" {
|
|
return v1alpha1.IssuerKind
|
|
} else {
|
|
return crt.Spec.IssuerRef.Kind
|
|
}
|
|
}
|
|
|
|
func (c *Controller) updateSecret(crt *v1alpha1.Certificate, namespace string, cert, key, ca []byte) (*api.Secret, error) {
|
|
secret, err := c.Client.CoreV1().Secrets(namespace).Get(crt.Spec.SecretName, metav1.GetOptions{})
|
|
if err != nil && !k8sErrors.IsNotFound(err) {
|
|
return nil, err
|
|
}
|
|
if k8sErrors.IsNotFound(err) {
|
|
secret = &api.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: crt.Spec.SecretName,
|
|
Namespace: namespace,
|
|
},
|
|
Type: api.SecretTypeTLS,
|
|
Data: map[string][]byte{},
|
|
}
|
|
}
|
|
secret.Data[api.TLSCertKey] = cert
|
|
secret.Data[api.TLSPrivateKeyKey] = key
|
|
|
|
if ca != nil {
|
|
secret.Data[TLSCAKey] = ca
|
|
}
|
|
|
|
if secret.Annotations == nil {
|
|
secret.Annotations = make(map[string]string)
|
|
}
|
|
|
|
// Note: since this sets annotations based on certificate resource, incorrect
|
|
// annotations will be set if resource and actual certificate somehow get out
|
|
// of sync
|
|
dnsNames := pki.DNSNamesForCertificate(crt)
|
|
cn := pki.CommonNameForCertificate(crt)
|
|
|
|
secret.Annotations[v1alpha1.AltNamesAnnotationKey] = strings.Join(dnsNames, ",")
|
|
secret.Annotations[v1alpha1.CommonNameAnnotationKey] = cn
|
|
|
|
secret.Annotations[v1alpha1.IssuerNameAnnotationKey] = crt.Spec.IssuerRef.Name
|
|
secret.Annotations[v1alpha1.IssuerKindAnnotationKey] = issuerKind(crt)
|
|
|
|
if secret.Labels == nil {
|
|
secret.Labels = make(map[string]string)
|
|
}
|
|
|
|
secret.Labels[v1alpha1.CertificateNameKey] = crt.Name
|
|
|
|
// if it is a new resource
|
|
if secret.SelfLink == "" {
|
|
secret, err = c.Client.CoreV1().Secrets(namespace).Create(secret)
|
|
} else {
|
|
secret, err = c.Client.CoreV1().Secrets(namespace).Update(secret)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return secret, nil
|
|
}
|
|
|
|
// return an error on failure. If retrieval is succesful, the certificate data
|
|
// and private key will be stored in the named secret
|
|
func (c *Controller) issue(ctx context.Context, issuer issuer.Interface, crt *v1alpha1.Certificate) (bool, error) {
|
|
resp, err := issuer.Issue(ctx, crt)
|
|
if err != nil {
|
|
glog.Infof("Error issuing certificate for %s/%s: %v", crt.Namespace, crt.Name, err)
|
|
return false, err
|
|
}
|
|
|
|
if resp.PrivateKey == nil {
|
|
return resp.Requeue, nil
|
|
}
|
|
|
|
if _, err := c.updateSecret(crt, crt.Namespace, resp.Certificate, resp.PrivateKey, resp.CA); err != nil {
|
|
s := messageErrorSavingCertificate + err.Error()
|
|
glog.Info(s)
|
|
c.Recorder.Event(crt, api.EventTypeWarning, errorSavingCertificate, s)
|
|
return false, err
|
|
}
|
|
|
|
if len(resp.Certificate) > 0 {
|
|
s := messageCertificateIssued
|
|
glog.Info(s)
|
|
c.Recorder.Event(crt, api.EventTypeNormal, successCertificateIssued, s)
|
|
crt.UpdateStatusCondition(v1alpha1.CertificateConditionReady, v1alpha1.ConditionTrue, successCertificateIssued, s, true)
|
|
|
|
// as we have just written a certificate, we should schedule it for renewal
|
|
c.scheduleRenewal(crt)
|
|
}
|
|
|
|
return resp.Requeue, nil
|
|
}
|
|
|
|
func (c *Controller) updateCertificateStatus(old, new *v1alpha1.Certificate) (*v1alpha1.Certificate, error) {
|
|
if reflect.DeepEqual(old.Status, new.Status) {
|
|
return nil, nil
|
|
}
|
|
// TODO: replace Update call with UpdateStatus. This requires a custom API
|
|
// server with the /status subresource enabled and/or subresource support
|
|
// for CRDs (https://github.com/kubernetes/kubernetes/issues/38113)
|
|
return c.CMClient.CertmanagerV1alpha1().Certificates(new.Namespace).Update(new)
|
|
}
|
|
|
|
// calculateDurationUntilRenew calculates how long cert-manager should wait to
|
|
// until attempting to renew this certificate resource.
|
|
func (c *Controller) calculateDurationUntilRenew(cert *x509.Certificate, crt *v1alpha1.Certificate) time.Duration {
|
|
messageCertificateDuration := "Certificate received from server has a validity duration of %s. The requested certificate validity duration was %s"
|
|
messageScheduleModified := "Certificate renewal duration was changed to fit inside the received certificate validity duration from issuer."
|
|
|
|
// validate if the certificate received was with the issuer configured
|
|
// duration. If not we generate an event to warn the user of that fact.
|
|
certDuration := cert.NotAfter.Sub(cert.NotBefore)
|
|
if crt.Spec.Duration != nil && certDuration < crt.Spec.Duration.Duration {
|
|
s := fmt.Sprintf(messageCertificateDuration, certDuration, crt.Spec.Duration.Duration)
|
|
glog.Info(s)
|
|
// TODO Use the message as the reason in a 'renewal status' condition
|
|
}
|
|
|
|
// renew is the duration before the certificate expiration that cert-manager
|
|
// will start to try renewing the certificate.
|
|
renewBefore := v1alpha1.DefaultRenewBefore
|
|
if crt.Spec.RenewBefore != nil {
|
|
renewBefore = crt.Spec.RenewBefore.Duration
|
|
}
|
|
|
|
// Verify that the renewBefore duration is inside the certificate validity duration.
|
|
// If not we notify with an event that we will renew the certificate
|
|
// before (certificate duration / 3) of its expiration duration.
|
|
if renewBefore > certDuration {
|
|
glog.Info(messageScheduleModified)
|
|
// TODO Use the message as the reason in a 'renewal status' condition
|
|
// We will renew 1/3 before the expiration date.
|
|
renewBefore = certDuration / 3
|
|
}
|
|
|
|
// calculate the amount of time until expiry
|
|
durationUntilExpiry := cert.NotAfter.Sub(now())
|
|
// calculate how long until we should start attempting to renew the certificate
|
|
renewIn := durationUntilExpiry - renewBefore
|
|
|
|
return renewIn
|
|
}
|