cert-manager/pkg/controller/certificates/sync_test.go
James Munnelly 70d7d222e8 Centralise fake structures into 'fake' subpackages
Signed-off-by: James Munnelly <james@munnelly.eu>
2019-07-26 12:30:23 +01:00

971 lines
32 KiB
Go

/*
Copyright 2019 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 (
"bytes"
"context"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"math/big"
"testing"
"time"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
coretesting "k8s.io/client-go/testing"
clock "k8s.io/utils/clock/testing"
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
testpkg "github.com/jetstack/cert-manager/pkg/controller/test"
"github.com/jetstack/cert-manager/pkg/issuer"
"github.com/jetstack/cert-manager/pkg/issuer/fake"
_ "github.com/jetstack/cert-manager/pkg/issuer/selfsigned"
"github.com/jetstack/cert-manager/pkg/util/pki"
"github.com/jetstack/cert-manager/test/unit/gen"
)
func generatePrivateKey(t *testing.T) *rsa.PrivateKey {
pk, err := pki.GenerateRSAPrivateKey(2048)
if err != nil {
t.Errorf("failed to generate private key: %v", err)
t.FailNow()
}
return pk
}
var serialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128)
func generateSelfSignedCert(t *testing.T, crt *cmapi.Certificate, sn *big.Int, key crypto.Signer, notBefore, notAfter time.Time) []byte {
commonName := pki.CommonNameForCertificate(crt)
dnsNames := pki.DNSNamesForCertificate(crt)
if sn == nil {
var err error
sn, err = rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
t.Errorf("failed to generate serial number: %v", err)
t.FailNow()
}
}
template := &x509.Certificate{
Version: 3,
BasicConstraintsValid: true,
SerialNumber: sn,
Subject: pkix.Name{
CommonName: commonName,
},
NotBefore: notBefore,
NotAfter: notAfter,
// see http://golang.org/pkg/crypto/x509/#KeyUsage
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
DNSNames: dnsNames,
}
derBytes, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
if err != nil {
t.Errorf("error signing cert: %v", err)
t.FailNow()
}
pemByteBuffer := bytes.NewBuffer([]byte{})
err = pem.Encode(pemByteBuffer, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
if err != nil {
t.Errorf("failed to encode cert: %v", err)
t.FailNow()
}
return pemByteBuffer.Bytes()
}
func TestSync(t *testing.T) {
nowTime := time.Now()
nowMetaTime := metav1.NewTime(nowTime)
fixedClock := clock.NewFakeClock(nowTime)
exampleCert := gen.Certificate("test",
gen.SetCertificateDNSNames("example.com"),
gen.SetCertificateIssuer(cmapi.ObjectReference{Name: "test"}),
gen.SetCertificateSecretName("output"),
)
exampleCertNotFoundCondition := gen.CertificateFrom(exampleCert,
gen.SetCertificateStatusCondition(cmapi.CertificateCondition{
Type: cmapi.CertificateConditionReady,
Status: cmapi.ConditionFalse,
Reason: "NotFound",
Message: "Certificate does not exist",
LastTransitionTime: &nowMetaTime,
}),
)
exampleCertTemporaryCondition := gen.CertificateFrom(exampleCert,
gen.SetCertificateStatusCondition(cmapi.CertificateCondition{
Type: cmapi.CertificateConditionReady,
Status: cmapi.ConditionFalse,
Reason: "TemporaryCertificate",
Message: "Certificate issuance in progress. Temporary certificate issued.",
LastTransitionTime: &nowMetaTime,
}),
)
pk1 := generatePrivateKey(t)
pk1PEM := pki.EncodePKCS1PrivateKey(pk1)
cert1PEM := generateSelfSignedCert(t, exampleCert, nil, pk1, nowTime, nowTime.Add(time.Hour*12))
cert1, err := pki.DecodeX509CertificateBytes(cert1PEM)
if err != nil {
t.Errorf("Error decoding test cert1 bytes: %v", err)
t.FailNow()
}
pk2 := generatePrivateKey(t)
// pk2PEM := pki.EncodePKCS1PrivateKey(pk2)
cert2PEM := generateSelfSignedCert(t, exampleCert, nil, pk2, nowTime, nowTime.Add(time.Hour*24))
cert2, err := pki.DecodeX509CertificateBytes(cert2PEM)
if err != nil {
t.Errorf("Error decoding test cert2 bytes: %v", err)
t.FailNow()
}
localTempCert := generateSelfSignedCert(t, exampleCert, big.NewInt(staticTemporarySerialNumber), pk1, nowTime, nowTime)
exampleCertWrongGroup := exampleCert.DeepCopy()
exampleCertWrongGroup.Spec.IssuerRef.Group = "wrong.group.io"
tests := map[string]controllerFixture{
"should update certificate with NotExists if issuer does not return a keypair": {
Issuer: gen.Issuer("test",
gen.AddIssuerCondition(cmapi.IssuerCondition{
Type: cmapi.IssuerConditionReady,
Status: cmapi.ConditionTrue,
}),
gen.SetIssuerSelfSigned(cmapi.SelfSignedIssuer{}),
),
Certificate: *exampleCert,
IssuerImpl: &fake.Issuer{
IssueFunc: func(context.Context, *cmapi.Certificate) (*issuer.IssueResponse, error) {
// By not returning a response, we trigger a 'no-op' action
// which causes the certificate controller to only update
// the status of the Certificate and not create a Secret.
return nil, nil
},
},
Builder: &testpkg.Builder{
CertManagerObjects: []runtime.Object{gen.Certificate("test")},
ExpectedActions: []testpkg.Action{
testpkg.NewAction(coretesting.NewUpdateAction(
cmapi.SchemeGroupVersion.WithResource("certificates"),
gen.DefaultTestNamespace,
exampleCertNotFoundCondition,
)),
},
},
CheckFn: func(t *testing.T, s *controllerFixture, args ...interface{}) {
},
Err: false,
},
"should create a secret containing the private key only when one doesn't exist": {
Issuer: gen.Issuer("test",
gen.AddIssuerCondition(cmapi.IssuerCondition{
Type: cmapi.IssuerConditionReady,
Status: cmapi.ConditionTrue,
}),
gen.SetIssuerSelfSigned(cmapi.SelfSignedIssuer{}),
),
Certificate: *exampleCert,
IssuerImpl: &fake.Issuer{
IssueFunc: func(context.Context, *cmapi.Certificate) (*issuer.IssueResponse, error) {
return &issuer.IssueResponse{
PrivateKey: pk1PEM,
}, nil
},
},
StaticTemporaryCert: localTempCert,
Builder: &testpkg.Builder{
CertManagerObjects: []runtime.Object{gen.Certificate("test")},
ExpectedActions: []testpkg.Action{
testpkg.NewAction(coretesting.NewUpdateAction(
cmapi.SchemeGroupVersion.WithResource("certificates"),
gen.DefaultTestNamespace,
exampleCertNotFoundCondition,
)),
testpkg.NewAction(coretesting.NewCreateAction(
corev1.SchemeGroupVersion.WithResource("secrets"),
gen.DefaultTestNamespace,
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: gen.DefaultTestNamespace,
Name: "output",
Labels: map[string]string{
cmapi.CertificateNameKey: "test",
},
Annotations: map[string]string{
"certmanager.k8s.io/alt-names": "example.com",
"certmanager.k8s.io/common-name": "example.com",
"certmanager.k8s.io/ip-sans": "",
"certmanager.k8s.io/issuer-kind": "Issuer",
"certmanager.k8s.io/issuer-name": "test",
},
},
Type: corev1.SecretTypeTLS,
Data: map[string][]byte{
corev1.TLSCertKey: localTempCert,
corev1.TLSPrivateKeyKey: pk1PEM,
TLSCAKey: nil,
},
},
)),
},
},
},
"should update an existing empty secret with the private key": {
Issuer: gen.Issuer("test",
gen.AddIssuerCondition(cmapi.IssuerCondition{
Type: cmapi.IssuerConditionReady,
Status: cmapi.ConditionTrue,
}),
gen.SetIssuerSelfSigned(cmapi.SelfSignedIssuer{}),
),
Certificate: *exampleCert,
IssuerImpl: &fake.Issuer{
IssueFunc: func(context.Context, *cmapi.Certificate) (*issuer.IssueResponse, error) {
return &issuer.IssueResponse{
PrivateKey: pk1PEM,
}, nil
},
},
StaticTemporaryCert: localTempCert,
Builder: &testpkg.Builder{
KubeObjects: []runtime.Object{
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: gen.DefaultTestNamespace,
Name: "output",
SelfLink: "abc",
Labels: map[string]string{
cmapi.CertificateNameKey: "nottest",
},
Annotations: map[string]string{
"testannotation": "true",
},
},
},
},
CertManagerObjects: []runtime.Object{gen.Certificate("test")},
ExpectedActions: []testpkg.Action{
testpkg.NewAction(coretesting.NewUpdateAction(
cmapi.SchemeGroupVersion.WithResource("certificates"),
gen.DefaultTestNamespace,
exampleCertNotFoundCondition,
)),
testpkg.NewAction(coretesting.NewUpdateAction(
corev1.SchemeGroupVersion.WithResource("secrets"),
gen.DefaultTestNamespace,
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: gen.DefaultTestNamespace,
Name: "output",
SelfLink: "abc",
Labels: map[string]string{
cmapi.CertificateNameKey: "test",
},
Annotations: map[string]string{
"testannotation": "true",
"certmanager.k8s.io/alt-names": "example.com",
"certmanager.k8s.io/common-name": "example.com",
"certmanager.k8s.io/ip-sans": "",
"certmanager.k8s.io/issuer-kind": "Issuer",
"certmanager.k8s.io/issuer-name": "test",
},
},
Data: map[string][]byte{
corev1.TLSCertKey: localTempCert,
corev1.TLSPrivateKeyKey: pk1PEM,
TLSCAKey: nil,
},
},
)),
},
},
},
"should create a new secret containing private key and cert": {
Issuer: gen.Issuer("test",
gen.AddIssuerCondition(cmapi.IssuerCondition{
Type: cmapi.IssuerConditionReady,
Status: cmapi.ConditionTrue,
}),
gen.SetIssuerSelfSigned(cmapi.SelfSignedIssuer{}),
),
Certificate: *exampleCert,
IssuerImpl: &fake.Issuer{
IssueFunc: func(context.Context, *cmapi.Certificate) (*issuer.IssueResponse, error) {
return &issuer.IssueResponse{
PrivateKey: pk1PEM,
Certificate: cert1PEM,
}, nil
},
},
Builder: &testpkg.Builder{
CertManagerObjects: []runtime.Object{gen.Certificate("test")},
ExpectedActions: []testpkg.Action{
testpkg.NewAction(coretesting.NewUpdateAction(
cmapi.SchemeGroupVersion.WithResource("certificates"),
gen.DefaultTestNamespace,
exampleCertNotFoundCondition,
)),
testpkg.NewAction(coretesting.NewCreateAction(
corev1.SchemeGroupVersion.WithResource("secrets"),
gen.DefaultTestNamespace,
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: gen.DefaultTestNamespace,
Name: "output",
Labels: map[string]string{
cmapi.CertificateNameKey: "test",
},
Annotations: map[string]string{
"certmanager.k8s.io/alt-names": "example.com",
"certmanager.k8s.io/common-name": "example.com",
"certmanager.k8s.io/ip-sans": "",
"certmanager.k8s.io/issuer-kind": "Issuer",
"certmanager.k8s.io/issuer-name": "test",
},
},
Data: map[string][]byte{
corev1.TLSCertKey: cert1PEM,
corev1.TLSPrivateKeyKey: pk1PEM,
TLSCAKey: nil,
},
Type: corev1.SecretTypeTLS,
},
)),
},
},
},
"should update an existing secret with private key and cert": {
Issuer: gen.Issuer("test",
gen.AddIssuerCondition(cmapi.IssuerCondition{
Type: cmapi.IssuerConditionReady,
Status: cmapi.ConditionTrue,
}),
gen.SetIssuerSelfSigned(cmapi.SelfSignedIssuer{}),
),
Certificate: *exampleCert,
IssuerImpl: &fake.Issuer{
IssueFunc: func(context.Context, *cmapi.Certificate) (*issuer.IssueResponse, error) {
return &issuer.IssueResponse{
PrivateKey: pk1PEM,
Certificate: cert1PEM,
}, nil
},
},
Builder: &testpkg.Builder{
KubeObjects: []runtime.Object{
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: gen.DefaultTestNamespace,
Name: "output",
SelfLink: "abc",
Labels: map[string]string{
cmapi.CertificateNameKey: "nottest",
},
Annotations: map[string]string{
"testannotation": "true",
},
},
},
},
CertManagerObjects: []runtime.Object{gen.Certificate("test")},
ExpectedActions: []testpkg.Action{
testpkg.NewAction(coretesting.NewUpdateAction(
cmapi.SchemeGroupVersion.WithResource("certificates"),
gen.DefaultTestNamespace,
exampleCertNotFoundCondition,
)),
testpkg.NewAction(coretesting.NewUpdateAction(
corev1.SchemeGroupVersion.WithResource("secrets"),
gen.DefaultTestNamespace,
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: gen.DefaultTestNamespace,
Name: "output",
SelfLink: "abc",
Labels: map[string]string{
cmapi.CertificateNameKey: "test",
},
Annotations: map[string]string{
"testannotation": "true",
"certmanager.k8s.io/alt-names": "example.com",
"certmanager.k8s.io/common-name": "example.com",
"certmanager.k8s.io/ip-sans": "",
"certmanager.k8s.io/issuer-kind": "Issuer",
"certmanager.k8s.io/issuer-name": "test",
},
},
Data: map[string][]byte{
corev1.TLSCertKey: cert1PEM,
corev1.TLSPrivateKeyKey: pk1PEM,
TLSCAKey: nil,
},
},
)),
},
},
},
"should mark certificate with invalid private key as DoesNotMatch": {
Issuer: gen.Issuer("test",
gen.AddIssuerCondition(cmapi.IssuerCondition{
Type: cmapi.IssuerConditionReady,
Status: cmapi.ConditionTrue,
}),
gen.SetIssuerSelfSigned(cmapi.SelfSignedIssuer{}),
),
Certificate: *exampleCert,
IssuerImpl: &fake.Issuer{
IssueFunc: func(context.Context, *cmapi.Certificate) (*issuer.IssueResponse, error) {
return &issuer.IssueResponse{
PrivateKey: pk1PEM,
Certificate: cert1PEM,
}, nil
},
},
Builder: &testpkg.Builder{
KubeObjects: []runtime.Object{
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: gen.DefaultTestNamespace,
Name: "output",
SelfLink: "abc",
Labels: map[string]string{
cmapi.CertificateNameKey: "nottest",
},
Annotations: map[string]string{
"testannotation": "true",
// We want ONLY invalid key, issuer annotations should be correct
"certmanager.k8s.io/issuer-kind": "Issuer",
"certmanager.k8s.io/issuer-name": "test",
},
},
Data: map[string][]byte{
corev1.TLSCertKey: cert2PEM,
corev1.TLSPrivateKeyKey: pk1PEM,
TLSCAKey: nil,
},
},
},
CertManagerObjects: []runtime.Object{gen.Certificate("test")},
ExpectedActions: []testpkg.Action{
testpkg.NewAction(coretesting.NewUpdateAction(
cmapi.SchemeGroupVersion.WithResource("certificates"),
gen.DefaultTestNamespace,
gen.CertificateFrom(exampleCert,
gen.SetCertificateStatusCondition(cmapi.CertificateCondition{
Type: cmapi.CertificateConditionReady,
Status: cmapi.ConditionFalse,
Reason: "DoesNotMatch",
Message: "Certificate private key does not match certificate",
LastTransitionTime: &nowMetaTime,
}),
gen.SetCertificateNotAfter(metav1.NewTime(cert2.NotAfter)),
),
)),
testpkg.NewAction(coretesting.NewUpdateAction(
corev1.SchemeGroupVersion.WithResource("secrets"),
gen.DefaultTestNamespace,
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: gen.DefaultTestNamespace,
Name: "output",
SelfLink: "abc",
Labels: map[string]string{
cmapi.CertificateNameKey: "test",
},
Annotations: map[string]string{
"testannotation": "true",
"certmanager.k8s.io/alt-names": "example.com",
"certmanager.k8s.io/common-name": "example.com",
"certmanager.k8s.io/ip-sans": "",
"certmanager.k8s.io/issuer-kind": "Issuer",
"certmanager.k8s.io/issuer-name": "test",
},
},
Data: map[string][]byte{
corev1.TLSCertKey: cert1PEM,
corev1.TLSPrivateKeyKey: pk1PEM,
TLSCAKey: nil,
},
},
)),
},
},
},
"should update status of up to date certificate": {
Issuer: gen.Issuer("test",
gen.AddIssuerCondition(cmapi.IssuerCondition{
Type: cmapi.IssuerConditionReady,
Status: cmapi.ConditionTrue,
}),
gen.SetIssuerSelfSigned(cmapi.SelfSignedIssuer{}),
),
Certificate: *exampleCert,
IssuerImpl: &fake.Issuer{
IssueFunc: func(context.Context, *cmapi.Certificate) (*issuer.IssueResponse, error) {
return &issuer.IssueResponse{
PrivateKey: pk1PEM,
Certificate: cert1PEM,
}, nil
},
},
Builder: &testpkg.Builder{
KubeObjects: []runtime.Object{
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: gen.DefaultTestNamespace,
Name: "output",
SelfLink: "abc",
Labels: map[string]string{
cmapi.CertificateNameKey: "test",
},
Annotations: map[string]string{
"testannotation": "true",
"certmanager.k8s.io/alt-names": "example.com",
"certmanager.k8s.io/common-name": "example.com",
"certmanager.k8s.io/ip-sans": "",
"certmanager.k8s.io/issuer-kind": "Issuer",
"certmanager.k8s.io/issuer-name": "test",
},
},
Data: map[string][]byte{
corev1.TLSCertKey: cert1PEM,
corev1.TLSPrivateKeyKey: pk1PEM,
TLSCAKey: nil,
},
},
},
CertManagerObjects: []runtime.Object{gen.Certificate("test")},
ExpectedActions: []testpkg.Action{
testpkg.NewAction(coretesting.NewUpdateAction(
cmapi.SchemeGroupVersion.WithResource("certificates"),
gen.DefaultTestNamespace,
gen.CertificateFrom(exampleCert,
gen.SetCertificateStatusCondition(cmapi.CertificateCondition{
Type: cmapi.CertificateConditionReady,
Status: cmapi.ConditionTrue,
Reason: "Ready",
Message: "Certificate is up to date and has not expired",
LastTransitionTime: &nowMetaTime,
}),
gen.SetCertificateNotAfter(metav1.NewTime(cert1.NotAfter)),
),
)),
},
},
},
"should update the reason field with temporary self signed cert text": {
Issuer: gen.Issuer("test",
gen.AddIssuerCondition(cmapi.IssuerCondition{
Type: cmapi.IssuerConditionReady,
Status: cmapi.ConditionTrue,
}),
gen.SetIssuerSelfSigned(cmapi.SelfSignedIssuer{}),
),
Certificate: *exampleCert,
IssuerImpl: &fake.Issuer{
IssueFunc: func(context.Context, *cmapi.Certificate) (*issuer.IssueResponse, error) {
return &issuer.IssueResponse{
PrivateKey: pk1PEM,
}, nil
},
},
// set this to something other than localTempCert, so that we can
// assert that the controller doesn't enter in a loop updating the
// Secret resource with a newly generated certificate
StaticTemporaryCert: cert1PEM,
Builder: &testpkg.Builder{
KubeObjects: []runtime.Object{
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: gen.DefaultTestNamespace,
Name: "output",
SelfLink: "abc",
Labels: map[string]string{
cmapi.CertificateNameKey: "nottest",
},
Annotations: map[string]string{
"testannotation": "true",
},
},
Data: map[string][]byte{
corev1.TLSCertKey: localTempCert,
corev1.TLSPrivateKeyKey: pk1PEM,
TLSCAKey: nil,
},
},
},
CertManagerObjects: []runtime.Object{gen.Certificate("test")},
ExpectedActions: []testpkg.Action{
testpkg.NewAction(coretesting.NewUpdateAction(
corev1.SchemeGroupVersion.WithResource("secrets"),
gen.DefaultTestNamespace,
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: gen.DefaultTestNamespace,
Name: "output",
SelfLink: "abc",
Labels: map[string]string{
cmapi.CertificateNameKey: "test",
},
Annotations: map[string]string{
"testannotation": "true",
"certmanager.k8s.io/alt-names": "example.com",
"certmanager.k8s.io/common-name": "example.com",
"certmanager.k8s.io/ip-sans": "",
"certmanager.k8s.io/issuer-kind": "Issuer",
"certmanager.k8s.io/issuer-name": "test",
},
},
Data: map[string][]byte{
corev1.TLSCertKey: localTempCert,
corev1.TLSPrivateKeyKey: pk1PEM,
TLSCAKey: nil,
},
},
)),
testpkg.NewAction(coretesting.NewUpdateAction(
cmapi.SchemeGroupVersion.WithResource("certificates"),
gen.DefaultTestNamespace,
exampleCertTemporaryCondition,
)),
},
},
},
"should mark certificate with wrong issuer name as DoesNotMatch": {
Issuer: gen.Issuer("test",
gen.AddIssuerCondition(cmapi.IssuerCondition{
Type: cmapi.IssuerConditionReady,
Status: cmapi.ConditionTrue,
}),
gen.SetIssuerSelfSigned(cmapi.SelfSignedIssuer{}),
),
Certificate: *exampleCert,
IssuerImpl: &fake.Issuer{
IssueFunc: func(context.Context, *cmapi.Certificate) (*issuer.IssueResponse, error) {
return &issuer.IssueResponse{
PrivateKey: pk1PEM,
Certificate: cert1PEM,
}, nil
},
},
Builder: &testpkg.Builder{
KubeObjects: []runtime.Object{
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: gen.DefaultTestNamespace,
Name: "output",
SelfLink: "abc",
Labels: map[string]string{
cmapi.CertificateNameKey: "test",
},
Annotations: map[string]string{
"testannotation": "true",
"certmanager.k8s.io/issuer-kind": "Issuer",
"certmanager.k8s.io/issuer-name": "not-test",
},
},
Data: map[string][]byte{
corev1.TLSCertKey: cert1PEM,
corev1.TLSPrivateKeyKey: pk1PEM,
TLSCAKey: nil,
},
},
},
CertManagerObjects: []runtime.Object{gen.Certificate("test")},
ExpectedActions: []testpkg.Action{
testpkg.NewAction(coretesting.NewUpdateAction(
cmapi.SchemeGroupVersion.WithResource("certificates"),
gen.DefaultTestNamespace,
gen.CertificateFrom(exampleCert,
gen.SetCertificateStatusCondition(cmapi.CertificateCondition{
Type: cmapi.CertificateConditionReady,
Status: cmapi.ConditionFalse,
Reason: "DoesNotMatch",
Message: "Issuer of the certificate is not up to date: \"not-test\"",
LastTransitionTime: &nowMetaTime,
}),
gen.SetCertificateNotAfter(metav1.NewTime(cert1.NotAfter)),
),
)),
testpkg.NewAction(coretesting.NewUpdateAction(
corev1.SchemeGroupVersion.WithResource("secrets"),
gen.DefaultTestNamespace,
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: gen.DefaultTestNamespace,
Name: "output",
SelfLink: "abc",
Labels: map[string]string{
cmapi.CertificateNameKey: "test",
},
Annotations: map[string]string{
"testannotation": "true",
"certmanager.k8s.io/alt-names": "example.com",
"certmanager.k8s.io/common-name": "example.com",
"certmanager.k8s.io/ip-sans": "",
"certmanager.k8s.io/issuer-kind": "Issuer",
"certmanager.k8s.io/issuer-name": "test",
},
},
Data: map[string][]byte{
corev1.TLSCertKey: cert1PEM,
corev1.TLSPrivateKeyKey: pk1PEM,
TLSCAKey: nil,
},
},
)),
},
},
},
"should mark certificate with duplicate secretName as DuplicateSecretName": {
Issuer: gen.Issuer("test",
gen.AddIssuerCondition(cmapi.IssuerCondition{
Type: cmapi.IssuerConditionReady,
Status: cmapi.ConditionTrue,
}),
gen.SetIssuerSelfSigned(cmapi.SelfSignedIssuer{}),
),
Certificate: *exampleCert,
IssuerImpl: &fake.Issuer{
IssueFunc: func(context.Context, *cmapi.Certificate) (*issuer.IssueResponse, error) {
return &issuer.IssueResponse{
PrivateKey: pk1PEM,
Certificate: cert1PEM,
}, nil
},
},
Builder: &testpkg.Builder{
CertManagerObjects: []runtime.Object{
gen.Certificate("test"),
gen.Certificate("dup-test",
gen.SetCertificateDNSNames("example.com"),
gen.SetCertificateIssuer(cmapi.ObjectReference{Name: "test"}),
gen.SetCertificateSecretName("output"),
),
},
ExpectedActions: []testpkg.Action{
testpkg.NewAction(coretesting.NewUpdateAction(
cmapi.SchemeGroupVersion.WithResource("certificates"),
gen.DefaultTestNamespace,
gen.CertificateFrom(exampleCert,
gen.SetCertificateStatusCondition(cmapi.CertificateCondition{
Type: cmapi.CertificateConditionReady,
Status: cmapi.ConditionFalse,
Reason: "DuplicateSecretName",
Message: "Another Certificate is using the same secretName",
LastTransitionTime: &nowMetaTime,
}),
),
)),
},
},
},
"should allow duplicate secretName in different namespaces": {
Issuer: gen.Issuer("test",
gen.AddIssuerCondition(cmapi.IssuerCondition{
Type: cmapi.IssuerConditionReady,
Status: cmapi.ConditionTrue,
}),
gen.SetIssuerSelfSigned(cmapi.SelfSignedIssuer{}),
),
Certificate: *exampleCert,
IssuerImpl: &fake.Issuer{
IssueFunc: func(context.Context, *cmapi.Certificate) (*issuer.IssueResponse, error) {
return &issuer.IssueResponse{
PrivateKey: pk1PEM,
Certificate: cert1PEM,
}, nil
},
},
Builder: &testpkg.Builder{
CertManagerObjects: []runtime.Object{
gen.Certificate("test"),
gen.CertificateFrom(exampleCert,
gen.SetCertificateNamespace("other-unit-test-ns")),
},
ExpectedActions: []testpkg.Action{
// specifically tests that a secret is created - behaves as usual
testpkg.NewAction(coretesting.NewUpdateAction(
cmapi.SchemeGroupVersion.WithResource("certificates"),
gen.DefaultTestNamespace,
exampleCertNotFoundCondition,
)),
testpkg.NewAction(coretesting.NewCreateAction(
corev1.SchemeGroupVersion.WithResource("secrets"),
gen.DefaultTestNamespace,
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: gen.DefaultTestNamespace,
Name: "output",
Labels: map[string]string{
cmapi.CertificateNameKey: "test",
},
Annotations: map[string]string{
"certmanager.k8s.io/alt-names": "example.com",
"certmanager.k8s.io/common-name": "example.com",
"certmanager.k8s.io/ip-sans": "",
"certmanager.k8s.io/issuer-kind": "Issuer",
"certmanager.k8s.io/issuer-name": "test",
},
},
Data: map[string][]byte{
corev1.TLSCertKey: cert1PEM,
corev1.TLSPrivateKeyKey: pk1PEM,
TLSCAKey: nil,
},
Type: corev1.SecretTypeTLS,
},
)),
},
},
},
"should exit sync nil if group is not certmanager.k8s.io": {
Issuer: gen.Issuer("test",
gen.AddIssuerCondition(cmapi.IssuerCondition{
Type: cmapi.IssuerConditionReady,
Status: cmapi.ConditionTrue,
}),
gen.SetIssuerSelfSigned(cmapi.SelfSignedIssuer{}),
),
Certificate: *exampleCertWrongGroup,
IssuerImpl: &fake.Issuer{
IssueFunc: func(context.Context, *cmapi.Certificate) (*issuer.IssueResponse, error) {
return nil, errors.New("unexpected issue call")
},
},
Builder: &testpkg.Builder{
CertManagerObjects: []runtime.Object{gen.Certificate("test")},
ExpectedActions: []testpkg.Action{},
},
},
//"should add annotations to already existing secret resource": {
// Issuer: gen.Issuer("test",
// gen.AddIssuerCondition(cmapi.IssuerCondition{
// Type: cmapi.IssuerConditionReady,
// Status: cmapi.ConditionTrue,
// }),
// gen.SetIssuerSelfSigned(cmapi.SelfSignedIssuer{}),
// ),
// Certificate: *gen.CertificateFrom(exampleCert,
// gen.SetCertificateStatusCondition(cmapi.CertificateCondition{
// Type: cmapi.CertificateConditionReady,
// Status: cmapi.ConditionTrue,
// Reason: "Ready",
// Message: "Certificate is up to date and has not expired",
// LastTransitionTime: nowMetaTime,
// }),
// gen.SetCertificateNotAfter(metav1.NewTime(cert1.NotAfter)),
// ),
// IssuerImpl: &fake.Issuer{
// IssueFunc: func(context.Context, *cmapi.Certificate) (*issuer.IssueResponse, error) {
// return &issuer.IssueResponse{
// PrivateKey: pk1PEM,
// Certificate: cert1PEM,
// }, nil
// },
// },
// Builder: &testpkg.Builder{
// KubeObjects: []runtime.Object{
// &corev1.Secret{
// ObjectMeta: metav1.ObjectMeta{
// Namespace: gen.DefaultTestNamespace,
// Name: "output",
// SelfLink: "abc",
// Labels: map[string]string{
// cmapi.CertificateNameKey: "nottest",
// },
// Annotations: map[string]string{
// "testannotation": "true",
// },
// },
// Data: map[string][]byte{
// corev1.TLSCertKey: cert1PEM,
// corev1.TLSPrivateKeyKey: pk1PEM,
// TLSCAKey: nil,
// },
// },
// },
// CertManagerObjects: []runtime.Object{gen.Certificate("test")},
// ExpectedActions: []testpkg.Action{
// testpkg.NewAction(coretesting.NewGetAction(
// corev1.SchemeGroupVersion.WithResource("secrets"),
// gen.DefaultTestNamespace,
// "output",
// )),
// testpkg.NewAction(coretesting.NewUpdateAction(
// corev1.SchemeGroupVersion.WithResource("secrets"),
// gen.DefaultTestNamespace,
// &corev1.Secret{
// ObjectMeta: metav1.ObjectMeta{
// Namespace: gen.DefaultTestNamespace,
// Name: "output",
// SelfLink: "abc",
// Labels: map[string]string{
// cmapi.CertificateNameKey: "test",
// },
// Annotations: map[string]string{
// "testannotation": "true",
// "certmanager.k8s.io/alt-names": "example.com",
// "certmanager.k8s.io/common-name": "example.com",
// "certmanager.k8s.io/issuer-kind": "Issuer",
// "certmanager.k8s.io/issuer-name": "test",
// },
// },
// Data: map[string][]byte{
// corev1.TLSCertKey: cert1PEM,
// corev1.TLSPrivateKeyKey: pk1PEM,
// TLSCAKey: nil,
// },
// },
// )),
// },
// },
//},
}
for n, test := range tests {
t.Run(n, func(t *testing.T) {
if test.Builder == nil {
test.Builder = &testpkg.Builder{}
}
test.Clock = fixedClock
test.Setup(t)
crtCopy := test.Certificate.DeepCopy()
err := test.Controller.Sync(test.Ctx, crtCopy)
if err != nil && !test.Err {
t.Errorf("Expected function to not error, but got: %v", err)
}
if err == nil && test.Err {
t.Errorf("Expected function to get an error, but got: %v", err)
}
test.Finish(t, crtCopy, err)
})
}
}