From c4914f71033d08f696336c2bc2b5312781397bb2 Mon Sep 17 00:00:00 2001 From: joshvanl Date: Wed, 16 Jun 2021 14:17:39 +0100 Subject: [PATCH] Adds venafi CertificateSigningRequest controller Signed-off-by: joshvanl --- .../certificatesigningrequests/BUILD.bazel | 1 + .../venafi/BUILD.bazel | 73 ++ .../venafi/venafi.go | 217 +++++ .../venafi/venafi_test.go | 921 ++++++++++++++++++ 4 files changed, 1212 insertions(+) create mode 100644 pkg/controller/certificatesigningrequests/venafi/BUILD.bazel create mode 100644 pkg/controller/certificatesigningrequests/venafi/venafi.go create mode 100644 pkg/controller/certificatesigningrequests/venafi/venafi_test.go diff --git a/pkg/controller/certificatesigningrequests/BUILD.bazel b/pkg/controller/certificatesigningrequests/BUILD.bazel index 203d6519f..2b1eec407 100644 --- a/pkg/controller/certificatesigningrequests/BUILD.bazel +++ b/pkg/controller/certificatesigningrequests/BUILD.bazel @@ -74,6 +74,7 @@ filegroup( "//pkg/controller/certificatesigningrequests/selfsigned:all-srcs", "//pkg/controller/certificatesigningrequests/util:all-srcs", "//pkg/controller/certificatesigningrequests/vault:all-srcs", + "//pkg/controller/certificatesigningrequests/venafi:all-srcs", ], tags = ["automanaged"], visibility = ["//visibility:public"], diff --git a/pkg/controller/certificatesigningrequests/venafi/BUILD.bazel b/pkg/controller/certificatesigningrequests/venafi/BUILD.bazel new file mode 100644 index 000000000..b034ebd94 --- /dev/null +++ b/pkg/controller/certificatesigningrequests/venafi/BUILD.bazel @@ -0,0 +1,73 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["venafi.go"], + importpath = "github.com/jetstack/cert-manager/pkg/controller/certificatesigningrequests/venafi", + visibility = ["//visibility:public"], + deps = [ + "//pkg/api/util:go_default_library", + "//pkg/apis/certmanager/v1:go_default_library", + "//pkg/apis/experimental/v1alpha1:go_default_library", + "//pkg/controller:go_default_library", + "//pkg/controller/certificatesigningrequests:go_default_library", + "//pkg/controller/certificatesigningrequests/util:go_default_library", + "//pkg/issuer/venafi/client:go_default_library", + "//pkg/issuer/venafi/client/api:go_default_library", + "//pkg/logs:go_default_library", + "//pkg/util/pki:go_default_library", + "@com_github_venafi_vcert_v4//pkg/endpoint:go_default_library", + "@io_k8s_api//certificates/v1:go_default_library", + "@io_k8s_api//core/v1:go_default_library", + "@io_k8s_apimachinery//pkg/api/errors:go_default_library", + "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", + "@io_k8s_client_go//kubernetes/typed/certificates/v1:go_default_library", + "@io_k8s_client_go//listers/core/v1:go_default_library", + "@io_k8s_client_go//tools/record:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["venafi_test.go"], + embed = [":go_default_library"], + deps = [ + "//pkg/api/util:go_default_library", + "//pkg/apis/certmanager:go_default_library", + "//pkg/apis/certmanager/v1:go_default_library", + "//pkg/apis/meta/v1:go_default_library", + "//pkg/controller/certificatesigningrequests:go_default_library", + "//pkg/controller/certificatesigningrequests/util:go_default_library", + "//pkg/controller/test:go_default_library", + "//pkg/issuer/venafi/client:go_default_library", + "//pkg/issuer/venafi/client/api:go_default_library", + "//pkg/issuer/venafi/client/fake:go_default_library", + "//pkg/util/pki:go_default_library", + "//test/unit/gen:go_default_library", + "@com_github_venafi_vcert_v4//pkg/endpoint:go_default_library", + "@io_k8s_api//authorization/v1:go_default_library", + "@io_k8s_api//certificates/v1:go_default_library", + "@io_k8s_api//core/v1:go_default_library", + "@io_k8s_apimachinery//pkg/api/errors:go_default_library", + "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", + "@io_k8s_apimachinery//pkg/runtime:go_default_library", + "@io_k8s_apimachinery//pkg/runtime/schema:go_default_library", + "@io_k8s_client_go//listers/core/v1:go_default_library", + "@io_k8s_client_go//testing:go_default_library", + "@io_k8s_utils//clock/testing:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/pkg/controller/certificatesigningrequests/venafi/venafi.go b/pkg/controller/certificatesigningrequests/venafi/venafi.go new file mode 100644 index 000000000..7e56f841c --- /dev/null +++ b/pkg/controller/certificatesigningrequests/venafi/venafi.go @@ -0,0 +1,217 @@ +/* +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 venafi + +import ( + "context" + "encoding/base64" + "encoding/json" + "fmt" + + "github.com/Venafi/vcert/v4/pkg/endpoint" + certificatesv1 "k8s.io/api/certificates/v1" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + certificatesclient "k8s.io/client-go/kubernetes/typed/certificates/v1" + corelisters "k8s.io/client-go/listers/core/v1" + "k8s.io/client-go/tools/record" + + apiutil "github.com/jetstack/cert-manager/pkg/api/util" + cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1" + experimentalapi "github.com/jetstack/cert-manager/pkg/apis/experimental/v1alpha1" + controllerpkg "github.com/jetstack/cert-manager/pkg/controller" + "github.com/jetstack/cert-manager/pkg/controller/certificatesigningrequests" + "github.com/jetstack/cert-manager/pkg/controller/certificatesigningrequests/util" + venaficlient "github.com/jetstack/cert-manager/pkg/issuer/venafi/client" + venafiapi "github.com/jetstack/cert-manager/pkg/issuer/venafi/client/api" + logf "github.com/jetstack/cert-manager/pkg/logs" + "github.com/jetstack/cert-manager/pkg/util/pki" + utilpki "github.com/jetstack/cert-manager/pkg/util/pki" +) + +const ( + CSRControllerName = "certificatesigningrequests-issuer-venafi" +) + +// Venafi is a Kubernetes CertificateSigningRequest controller, responsible for +// signing CertificateSigningRequests that reference a cert-manager Venafi +// Issuer or ClusterIssuer +type Venafi struct { + issuerOptions controllerpkg.IssuerOptions + secretsLister corelisters.SecretLister + certClient certificatesclient.CertificateSigningRequestInterface + recorder record.EventRecorder + + clientBuilder venaficlient.VenafiClientBuilder +} + +func init() { + controllerpkg.Register(CSRControllerName, func(ctx *controllerpkg.Context) (controllerpkg.Interface, error) { + return controllerpkg.NewBuilder(ctx, CSRControllerName). + For(certificatesigningrequests.New(apiutil.IssuerVenafi, NewVenafi(ctx))). + Complete() + }) +} + +func NewVenafi(ctx *controllerpkg.Context) *Venafi { + return &Venafi{ + issuerOptions: ctx.IssuerOptions, + secretsLister: ctx.KubeSharedInformerFactory.Core().V1().Secrets().Lister(), + certClient: ctx.Client.CertificatesV1().CertificateSigningRequests(), + recorder: ctx.Recorder, + clientBuilder: venaficlient.New, + } +} + +// Sign attempts to sign the given CertificateSigningRequest based on the +// provided Venafi Issuer or ClusterIssuer. This function will update the resource +// if signing was successful. Returns an error which, if not nil, should +// trigger a retry. +// Since this signer takes some time to sign the request, this controller will +// set a "pick ID" annotation value that is used to fetch the latest state of +// the request in subsequent re-syncs. +func (v *Venafi) Sign(ctx context.Context, csr *certificatesv1.CertificateSigningRequest, issuerObj cmapi.GenericIssuer) error { + log := logf.FromContext(ctx, "sign") + log = logf.WithRelatedResource(log, issuerObj) + + resourceNamespace := v.issuerOptions.ResourceNamespace(issuerObj) + + client, err := v.clientBuilder(resourceNamespace, v.secretsLister, issuerObj) + if apierrors.IsNotFound(err) { + message := "Required secret resource not found" + v.recorder.Event(csr, corev1.EventTypeWarning, "SecretNotFound", message) + log.Error(err, message) + return nil + } + + if err != nil { + message := fmt.Sprintf("Failed to initialise venafi client for signing: %s", err) + v.recorder.Event(csr, corev1.EventTypeWarning, "ErrorVenafiInit", message) + log.Error(err, message) + return err + } + + var customFields []venafiapi.CustomField + if annotation, exists := csr.GetAnnotations()[experimentalapi.CertificateSigningRequestVenafiCustomFieldsAnnotationKey]; exists && annotation != "" { + err := json.Unmarshal([]byte(annotation), &customFields) + if err != nil { + message := fmt.Sprintf("Failed to parse %q annotation: %s", experimentalapi.CertificateSigningRequestVenafiCustomFieldsAnnotationKey, err) + v.recorder.Event(csr, corev1.EventTypeWarning, "ErrorCustomFields", message) + util.CertificateSigningRequestSetFailed(csr, "ErrorCustomFields", message) + _, err = v.certClient.UpdateStatus(ctx, csr, metav1.UpdateOptions{}) + return err + } + } + + duration, err := pki.DurationFromCertificateSigningRequest(csr) + if err != nil { + message := fmt.Sprintf("Failed to parse requested duration: %s", err) + log.Error(err, message) + v.recorder.Event(csr, corev1.EventTypeWarning, "ErrorParseDuration", message) + util.CertificateSigningRequestSetFailed(csr, "ErrorParseDuration", message) + _, err := v.certClient.UpdateStatus(ctx, csr, metav1.UpdateOptions{}) + return err + } + + pickupID := csr.GetAnnotations()[experimentalapi.CertificateSigningRequestVenafiPickupIDAnnotationKey] + + // check if the pickup ID annotation is there, if not set it up. + if len(pickupID) == 0 { + pickupID, err := client.RequestCertificate(csr.Spec.Request, duration, customFields) + // Check some known error types + if err != nil { + switch err.(type) { + + case venaficlient.ErrCustomFieldsType: + log.Error(err, "") + v.recorder.Event(csr, corev1.EventTypeWarning, "ErrorCustomFields", err.Error()) + util.CertificateSigningRequestSetFailed(csr, "ErrorCustomFields", err.Error()) + _, err := v.certClient.UpdateStatus(ctx, csr, metav1.UpdateOptions{}) + return err + + default: + message := fmt.Sprintf("Failed to request venafi certificate: %s", err) + log.Error(err, message) + v.recorder.Event(csr, corev1.EventTypeWarning, "ErrorRequest", message) + util.CertificateSigningRequestSetFailed(csr, "ErrorRequest", message) + _, err := v.certClient.UpdateStatus(ctx, csr, metav1.UpdateOptions{}) + return err + } + } + + if csr.Annotations == nil { + csr.Annotations = make(map[string]string) + } + csr.Annotations[experimentalapi.CertificateSigningRequestVenafiPickupIDAnnotationKey] = pickupID + _, err = v.certClient.Update(ctx, csr, metav1.UpdateOptions{}) + return err + } + + certPem, err := client.RetrieveCertificate(pickupID, csr.Spec.Request, duration, customFields) + if err != nil { + switch err.(type) { + case endpoint.ErrCertificatePending, endpoint.ErrRetrieveCertificateTimeout: + message := "Venafi certificate still in a pending state, the request will be retried" + log.Error(err, message) + v.recorder.Event(csr, corev1.EventTypeNormal, "IssuancePending", message) + return err + + default: + message := fmt.Sprintf("Failed to obtain venafi certificate: %s", err) + log.Error(err, message) + v.recorder.Event(csr, corev1.EventTypeWarning, "ErrorRetrieve", message) + return err + } + } + + bundle, err := utilpki.ParseSingleCertificateChainPEM(certPem) + if err != nil { + message := fmt.Sprintf("Failed to parse returned certificate bundle: %s", err) + log.Error(err, message) + v.recorder.Event(csr, corev1.EventTypeWarning, "ErrorParse", message) + util.CertificateSigningRequestSetFailed(csr, "ErrorParse", message) + _, err := v.certClient.UpdateStatus(ctx, csr, metav1.UpdateOptions{}) + return err + } + + // Update the status.certificate first so that the sync from updating will + // not cause another issuance before setting the CA. + csr.Status.Certificate = bundle.ChainPEM + csr, err = v.certClient.UpdateStatus(ctx, csr, metav1.UpdateOptions{}) + if err != nil { + message := "Error updating certificate" + v.recorder.Eventf(csr, corev1.EventTypeWarning, "SigningError", "%s: %s", message, err) + return err + } + + if csr.Annotations == nil { + csr.Annotations = make(map[string]string) + } + csr.Annotations[experimentalapi.CertificateSigningRequestCAAnnotationKey] = base64.StdEncoding.EncodeToString(bundle.CAPEM) + _, err = v.certClient.Update(ctx, csr, metav1.UpdateOptions{}) + if err != nil { + message := fmt.Sprintf("Error setting %q", experimentalapi.CertificateSigningRequestCAAnnotationKey) + v.recorder.Eventf(csr, corev1.EventTypeWarning, "SigningError", "%s: %s", message, err) + return err + } + + log.V(logf.DebugLevel).Info("certificate issued") + v.recorder.Event(csr, corev1.EventTypeNormal, "CertificateIssued", "Certificate fetched from venafi issuer successfully") + + return nil +} diff --git a/pkg/controller/certificatesigningrequests/venafi/venafi_test.go b/pkg/controller/certificatesigningrequests/venafi/venafi_test.go new file mode 100644 index 000000000..e287457a8 --- /dev/null +++ b/pkg/controller/certificatesigningrequests/venafi/venafi_test.go @@ -0,0 +1,921 @@ +/* +Copyright 2021 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 venafi + +import ( + "context" + "crypto/x509" + "errors" + "fmt" + "testing" + "time" + + "github.com/Venafi/vcert/v4/pkg/endpoint" + authzv1 "k8s.io/api/authorization/v1" + certificatesv1 "k8s.io/api/certificates/v1" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + corelisters "k8s.io/client-go/listers/core/v1" + coretesting "k8s.io/client-go/testing" + fakeclock "k8s.io/utils/clock/testing" + + apiutil "github.com/jetstack/cert-manager/pkg/api/util" + "github.com/jetstack/cert-manager/pkg/apis/certmanager" + cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1" + cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1" + "github.com/jetstack/cert-manager/pkg/controller/certificatesigningrequests" + "github.com/jetstack/cert-manager/pkg/controller/certificatesigningrequests/util" + testpkg "github.com/jetstack/cert-manager/pkg/controller/test" + venaficlient "github.com/jetstack/cert-manager/pkg/issuer/venafi/client" + venafiapi "github.com/jetstack/cert-manager/pkg/issuer/venafi/client/api" + fakevenaficlient "github.com/jetstack/cert-manager/pkg/issuer/venafi/client/fake" + "github.com/jetstack/cert-manager/pkg/util/pki" + "github.com/jetstack/cert-manager/test/unit/gen" +) + +var ( + fixedClockStart = time.Now() + fixedClock = fakeclock.NewFakeClock(fixedClockStart) +) + +func TestProcessItem(t *testing.T) { + metaFixedClockStart := metav1.NewTime(fixedClockStart) + util.Clock = fixedClock + + rootCSRPEM, rootPK, err := gen.CSR(x509.RSA, + gen.SetCSRCommonName("root"), + ) + if err != nil { + t.Fatal(err) + } + + rootTmpl, err := pki.GenerateTemplateFromCSRPEM(rootCSRPEM, time.Hour, true) + if err != nil { + t.Fatal(err) + } + _, rootCert, err := pki.SignCertificate(rootTmpl, rootTmpl, rootPK.Public(), rootPK) + if err != nil { + t.Fatal(err) + } + + leafCSRPEM, leafPK, err := gen.CSR(x509.RSA, + gen.SetCSRCommonName("leaf"), + ) + if err != nil { + t.Fatal(err) + } + leafTmpl, err := pki.GenerateTemplateFromCSRPEM(leafCSRPEM, time.Hour, false) + if err != nil { + t.Fatal(err) + } + _, leafCert, err := pki.SignCertificate(leafTmpl, rootCert, leafPK.Public(), rootPK) + if err != nil { + t.Fatal(err) + } + certBundle, err := pki.ParseSingleCertificateChain([]*x509.Certificate{leafCert, rootCert}) + if err != nil { + t.Fatal(err) + } + + baseIssuer := gen.Issuer("test-issuer", + gen.SetIssuerVenafi(cmapi.VenafiIssuer{ + Cloud: &cmapi.VenafiCloud{}, + }), + gen.AddIssuerCondition(cmapi.IssuerCondition{ + Type: cmapi.IssuerConditionReady, + Status: cmmeta.ConditionTrue, + }), + ) + + baseCSR := gen.CertificateSigningRequest("test-cr", + gen.SetCertificateSigningRequestRequest(leafCSRPEM), + gen.SetCertificateSigningRequestSignerName("issuers.cert-manager.io/default-unit-test-ns.test-issuer"), + gen.SetCertificateSigningRequestDuration("1440h"), + gen.SetCertificateSigningRequestUsername("user-1"), + gen.SetCertificateSigningRequestGroups([]string{"group-1", "group-2"}), + gen.SetCertificateSigningRequestUID("uid-1"), + gen.SetCertificateSigningRequestExtra(map[string]certificatesv1.ExtraValue{ + "extra": []string{"1", "2"}, + }), + ) + + tests := map[string]struct { + builder *testpkg.Builder + csr *certificatesv1.CertificateSigningRequest + clientBuilder venaficlient.VenafiClientBuilder + expectedErr bool + }{ + "a CertificateSigningRequest without an approved condition should do nothing": { + csr: gen.CertificateSigningRequestFrom(baseCSR), + builder: &testpkg.Builder{ + CertManagerObjects: []runtime.Object{baseIssuer.DeepCopy()}, + }, + }, + "a CertificateSigningRequest with a denied condition should do nothing": { + csr: gen.CertificateSigningRequestFrom(baseCSR, + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateDenied, + Status: corev1.ConditionTrue, + }), + ), + builder: &testpkg.Builder{ + CertManagerObjects: []runtime.Object{baseIssuer.DeepCopy()}, + ExpectedEvents: []string{}, + ExpectedActions: nil, + }, + }, + "an approved CSR where the venafi client builder returns a not found error should fire event and do nothing": { + csr: gen.CertificateSigningRequestFrom(baseCSR, + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + ), + clientBuilder: func(_ string, _ corelisters.SecretLister, _ cmapi.GenericIssuer) (venaficlient.Interface, error) { + return nil, apierrors.NewNotFound(schema.GroupResource{}, "test-secret") + }, + builder: &testpkg.Builder{ + CertManagerObjects: []runtime.Object{baseIssuer.DeepCopy()}, + ExpectedEvents: []string{ + "Warning SecretNotFound Required secret resource not found", + }, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewCreateAction( + authzv1.SchemeGroupVersion.WithResource("subjectaccessreviews"), + "", + &authzv1.SubjectAccessReview{ + Spec: authzv1.SubjectAccessReviewSpec{ + User: "user-1", + Groups: []string{"group-1", "group-2"}, + Extra: map[string]authzv1.ExtraValue{ + "extra": []string{"1", "2"}, + }, + UID: "uid-1", + + ResourceAttributes: &authzv1.ResourceAttributes{ + Group: certmanager.GroupName, + Resource: "signers", + Verb: "reference", + Namespace: baseIssuer.Namespace, + Name: baseIssuer.Name, + Version: "*", + }, + }, + }, + )), + }, + }, + }, + "an approved CSR where the venafi client builder returns a generic error should mark as Failed": { + csr: gen.CertificateSigningRequestFrom(baseCSR, + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + ), + clientBuilder: func(_ string, _ corelisters.SecretLister, _ cmapi.GenericIssuer) (venaficlient.Interface, error) { + return nil, errors.New("generic error") + }, + expectedErr: true, + builder: &testpkg.Builder{ + CertManagerObjects: []runtime.Object{baseIssuer.DeepCopy()}, + ExpectedEvents: []string{ + "Warning ErrorVenafiInit Failed to initialise venafi client for signing: generic error", + }, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewCreateAction( + authzv1.SchemeGroupVersion.WithResource("subjectaccessreviews"), + "", + &authzv1.SubjectAccessReview{ + Spec: authzv1.SubjectAccessReviewSpec{ + User: "user-1", + Groups: []string{"group-1", "group-2"}, + Extra: map[string]authzv1.ExtraValue{ + "extra": []string{"1", "2"}, + }, + UID: "uid-1", + + ResourceAttributes: &authzv1.ResourceAttributes{ + Group: certmanager.GroupName, + Resource: "signers", + Verb: "reference", + Namespace: baseIssuer.Namespace, + Name: baseIssuer.Name, + Version: "*", + }, + }, + }, + )), + }, + }, + }, + "an approved CSR where the custom fields annotations contain garbage data should mark as Failed": { + csr: gen.CertificateSigningRequestFrom(baseCSR, + gen.AddCertificateSigningRequestAnnotations(map[string]string{ + "venafi.experimental.cert-manager.io/custom-fields": "garbage-data", + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + ), + clientBuilder: func(_ string, _ corelisters.SecretLister, _ cmapi.GenericIssuer) (venaficlient.Interface, error) { + return &fakevenaficlient.Venafi{}, nil + }, + builder: &testpkg.Builder{ + CertManagerObjects: []runtime.Object{baseIssuer.DeepCopy()}, + ExpectedEvents: []string{ + `Warning ErrorCustomFields Failed to parse "venafi.experimental.cert-manager.io/custom-fields" annotation: invalid character 'g' looking for beginning of value`, + }, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewCreateAction( + authzv1.SchemeGroupVersion.WithResource("subjectaccessreviews"), + "", + &authzv1.SubjectAccessReview{ + Spec: authzv1.SubjectAccessReviewSpec{ + User: "user-1", + Groups: []string{"group-1", "group-2"}, + Extra: map[string]authzv1.ExtraValue{ + "extra": []string{"1", "2"}, + }, + UID: "uid-1", + + ResourceAttributes: &authzv1.ResourceAttributes{ + Group: certmanager.GroupName, + Resource: "signers", + Verb: "reference", + Namespace: baseIssuer.Namespace, + Name: baseIssuer.Name, + Version: "*", + }, + }, + }, + )), + testpkg.NewAction(coretesting.NewUpdateSubresourceAction( + certificatesv1.SchemeGroupVersion.WithResource("certificatesigningrequests"), + "status", + "", + gen.CertificateSigningRequestFrom(baseCSR.DeepCopy(), + gen.AddCertificateSigningRequestAnnotations(map[string]string{ + "venafi.experimental.cert-manager.io/custom-fields": "garbage-data", + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateFailed, + Status: corev1.ConditionTrue, + Reason: "ErrorCustomFields", + Message: `Failed to parse "venafi.experimental.cert-manager.io/custom-fields" annotation: invalid character 'g' looking for beginning of value`, + LastTransitionTime: metaFixedClockStart, + LastUpdateTime: metaFixedClockStart, + }), + ), + )), + }, + }, + }, + "an approved CSR where the requested duration annotations contains garbage data should mark as Failed": { + csr: gen.CertificateSigningRequestFrom(baseCSR, + gen.AddCertificateSigningRequestAnnotations(map[string]string{ + "venafi.experimental.cert-manager.io/custom-fields": `[ {"name": "field-name", "value": "vield value"}]`, + }), + gen.SetCertificateSigningRequestDuration("garbage-duration"), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + ), + clientBuilder: func(_ string, _ corelisters.SecretLister, _ cmapi.GenericIssuer) (venaficlient.Interface, error) { + return &fakevenaficlient.Venafi{}, nil + }, + builder: &testpkg.Builder{ + CertManagerObjects: []runtime.Object{baseIssuer.DeepCopy()}, + ExpectedEvents: []string{ + `Warning ErrorParseDuration Failed to parse requested duration: failed to parse requested duration on annotation "experimental.cert-manager.io/request-duration": time: invalid duration "garbage-duration"`, + }, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewCreateAction( + authzv1.SchemeGroupVersion.WithResource("subjectaccessreviews"), + "", + &authzv1.SubjectAccessReview{ + Spec: authzv1.SubjectAccessReviewSpec{ + User: "user-1", + Groups: []string{"group-1", "group-2"}, + Extra: map[string]authzv1.ExtraValue{ + "extra": []string{"1", "2"}, + }, + UID: "uid-1", + + ResourceAttributes: &authzv1.ResourceAttributes{ + Group: certmanager.GroupName, + Resource: "signers", + Verb: "reference", + Namespace: baseIssuer.Namespace, + Name: baseIssuer.Name, + Version: "*", + }, + }, + }, + )), + testpkg.NewAction(coretesting.NewUpdateSubresourceAction( + certificatesv1.SchemeGroupVersion.WithResource("certificatesigningrequests"), + "status", + "", + gen.CertificateSigningRequestFrom(baseCSR.DeepCopy(), + gen.SetCertificateSigningRequestDuration("garbage-duration"), + gen.AddCertificateSigningRequestAnnotations(map[string]string{ + "venafi.experimental.cert-manager.io/custom-fields": `[ {"name": "field-name", "value": "vield value"}]`, + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateFailed, + Status: corev1.ConditionTrue, + Reason: "ErrorParseDuration", + Message: `Failed to parse requested duration: failed to parse requested duration on annotation "experimental.cert-manager.io/request-duration": time: invalid duration "garbage-duration"`, + LastTransitionTime: metaFixedClockStart, + LastUpdateTime: metaFixedClockStart, + }), + ), + )), + }, + }, + }, + "an approved CSR which does not yet have a pickup ID, but the client responds with fields type error, should mark as Failed": { + csr: gen.CertificateSigningRequestFrom(baseCSR, + gen.AddCertificateSigningRequestAnnotations(map[string]string{ + "venafi.experimental.cert-manager.io/custom-fields": `[ {"name": "field-name", "value": "vield value"}]`, + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + ), + clientBuilder: func(_ string, _ corelisters.SecretLister, _ cmapi.GenericIssuer) (venaficlient.Interface, error) { + return &fakevenaficlient.Venafi{ + RequestCertificateFn: func(_ []byte, _ time.Duration, _ []venafiapi.CustomField) (string, error) { + return "", venaficlient.ErrCustomFieldsType{Type: "test-type"} + }, + }, nil + }, + builder: &testpkg.Builder{ + CertManagerObjects: []runtime.Object{baseIssuer.DeepCopy()}, + ExpectedEvents: []string{ + `Warning ErrorCustomFields certificate request contains an invalid Venafi custom fields type: "test-type"`, + }, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewCreateAction( + authzv1.SchemeGroupVersion.WithResource("subjectaccessreviews"), + "", + &authzv1.SubjectAccessReview{ + Spec: authzv1.SubjectAccessReviewSpec{ + User: "user-1", + Groups: []string{"group-1", "group-2"}, + Extra: map[string]authzv1.ExtraValue{ + "extra": []string{"1", "2"}, + }, + UID: "uid-1", + + ResourceAttributes: &authzv1.ResourceAttributes{ + Group: certmanager.GroupName, + Resource: "signers", + Verb: "reference", + Namespace: baseIssuer.Namespace, + Name: baseIssuer.Name, + Version: "*", + }, + }, + }, + )), + testpkg.NewAction(coretesting.NewUpdateSubresourceAction( + certificatesv1.SchemeGroupVersion.WithResource("certificatesigningrequests"), + "status", + "", + gen.CertificateSigningRequestFrom(baseCSR.DeepCopy(), + gen.AddCertificateSigningRequestAnnotations(map[string]string{ + "venafi.experimental.cert-manager.io/custom-fields": `[ {"name": "field-name", "value": "vield value"}]`, + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateFailed, + Status: corev1.ConditionTrue, + Reason: "ErrorCustomFields", + Message: `certificate request contains an invalid Venafi custom fields type: "test-type"`, + LastTransitionTime: metaFixedClockStart, + LastUpdateTime: metaFixedClockStart, + }), + ), + )), + }, + }, + }, + "an approved CSR which does not yet have a pickup ID, but the client responds a generic error, should mark as Failed": { + csr: gen.CertificateSigningRequestFrom(baseCSR, + gen.AddCertificateSigningRequestAnnotations(map[string]string{ + "venafi.experimental.cert-manager.io/custom-fields": `[ {"name": "field-name", "value": "vield value"}]`, + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + ), + clientBuilder: func(_ string, _ corelisters.SecretLister, _ cmapi.GenericIssuer) (venaficlient.Interface, error) { + return &fakevenaficlient.Venafi{ + RequestCertificateFn: func(_ []byte, _ time.Duration, _ []venafiapi.CustomField) (string, error) { + return "", errors.New("generic error") + }, + }, nil + }, + builder: &testpkg.Builder{ + CertManagerObjects: []runtime.Object{baseIssuer.DeepCopy()}, + ExpectedEvents: []string{ + "Warning ErrorRequest Failed to request venafi certificate: generic error", + }, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewCreateAction( + authzv1.SchemeGroupVersion.WithResource("subjectaccessreviews"), + "", + &authzv1.SubjectAccessReview{ + Spec: authzv1.SubjectAccessReviewSpec{ + User: "user-1", + Groups: []string{"group-1", "group-2"}, + Extra: map[string]authzv1.ExtraValue{ + "extra": []string{"1", "2"}, + }, + UID: "uid-1", + + ResourceAttributes: &authzv1.ResourceAttributes{ + Group: certmanager.GroupName, + Resource: "signers", + Verb: "reference", + Namespace: baseIssuer.Namespace, + Name: baseIssuer.Name, + Version: "*", + }, + }, + }, + )), + testpkg.NewAction(coretesting.NewUpdateSubresourceAction( + certificatesv1.SchemeGroupVersion.WithResource("certificatesigningrequests"), + "status", + "", + gen.CertificateSigningRequestFrom(baseCSR.DeepCopy(), + gen.AddCertificateSigningRequestAnnotations(map[string]string{ + "venafi.experimental.cert-manager.io/custom-fields": `[ {"name": "field-name", "value": "vield value"}]`, + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateFailed, + Status: corev1.ConditionTrue, + Reason: "ErrorRequest", + Message: "Failed to request venafi certificate: generic error", + LastTransitionTime: metaFixedClockStart, + LastUpdateTime: metaFixedClockStart, + }), + ), + )), + }, + }, + }, + "an approved CSR which does not yet have a pickup ID, should update the annotation with one and return": { + csr: gen.CertificateSigningRequestFrom(baseCSR, + gen.AddCertificateSigningRequestAnnotations(map[string]string{ + "venafi.experimental.cert-manager.io/custom-fields": `[ {"name": "field-name", "value": "vield value"}]`, + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + ), + clientBuilder: func(_ string, _ corelisters.SecretLister, _ cmapi.GenericIssuer) (venaficlient.Interface, error) { + return &fakevenaficlient.Venafi{ + RequestCertificateFn: func(_ []byte, _ time.Duration, _ []venafiapi.CustomField) (string, error) { + return "test-pickup-id", nil + }, + }, nil + }, + builder: &testpkg.Builder{ + CertManagerObjects: []runtime.Object{baseIssuer.DeepCopy()}, + ExpectedEvents: []string{}, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewCreateAction( + authzv1.SchemeGroupVersion.WithResource("subjectaccessreviews"), + "", + &authzv1.SubjectAccessReview{ + Spec: authzv1.SubjectAccessReviewSpec{ + User: "user-1", + Groups: []string{"group-1", "group-2"}, + Extra: map[string]authzv1.ExtraValue{ + "extra": []string{"1", "2"}, + }, + UID: "uid-1", + + ResourceAttributes: &authzv1.ResourceAttributes{ + Group: certmanager.GroupName, + Resource: "signers", + Verb: "reference", + Namespace: baseIssuer.Namespace, + Name: baseIssuer.Name, + Version: "*", + }, + }, + }, + )), + testpkg.NewAction(coretesting.NewUpdateAction( + certificatesv1.SchemeGroupVersion.WithResource("certificatesigningrequests"), + "", + gen.CertificateSigningRequestFrom(baseCSR.DeepCopy(), + gen.AddCertificateSigningRequestAnnotations(map[string]string{ + "venafi.experimental.cert-manager.io/custom-fields": `[ {"name": "field-name", "value": "vield value"}]`, + "venafi.experimental.cert-manager.io/pickup-id": "test-pickup-id", + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + ), + )), + }, + }, + }, + "an approved CSR which has a pickup ID, retrieve certificate returns a pending error, fire event and return error": { + csr: gen.CertificateSigningRequestFrom(baseCSR, + gen.AddCertificateSigningRequestAnnotations(map[string]string{ + "venafi.experimental.cert-manager.io/custom-fields": `[ {"name": "field-name", "value": "vield value"}]`, + "venafi.experimental.cert-manager.io/pickup-id": "test-pickup-id", + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + ), + clientBuilder: func(_ string, _ corelisters.SecretLister, _ cmapi.GenericIssuer) (venaficlient.Interface, error) { + return &fakevenaficlient.Venafi{ + RetrieveCertificateFn: func(_ string, _ []byte, _ time.Duration, _ []venafiapi.CustomField) ([]byte, error) { + return nil, endpoint.ErrCertificatePending{} + }, + }, nil + }, + expectedErr: true, + builder: &testpkg.Builder{ + CertManagerObjects: []runtime.Object{baseIssuer.DeepCopy()}, + ExpectedEvents: []string{ + "Normal IssuancePending Venafi certificate still in a pending state, the request will be retried", + }, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewCreateAction( + authzv1.SchemeGroupVersion.WithResource("subjectaccessreviews"), + "", + &authzv1.SubjectAccessReview{ + Spec: authzv1.SubjectAccessReviewSpec{ + User: "user-1", + Groups: []string{"group-1", "group-2"}, + Extra: map[string]authzv1.ExtraValue{ + "extra": []string{"1", "2"}, + }, + UID: "uid-1", + + ResourceAttributes: &authzv1.ResourceAttributes{ + Group: certmanager.GroupName, + Resource: "signers", + Verb: "reference", + Namespace: baseIssuer.Namespace, + Name: baseIssuer.Name, + Version: "*", + }, + }, + }, + )), + }, + }, + }, + "an approved CSR which has a pickup ID, retrieve certificate returns a timeout error, fire event and return error": { + csr: gen.CertificateSigningRequestFrom(baseCSR, + gen.AddCertificateSigningRequestAnnotations(map[string]string{ + "venafi.experimental.cert-manager.io/custom-fields": `[ {"name": "field-name", "value": "vield value"}]`, + "venafi.experimental.cert-manager.io/pickup-id": "test-pickup-id", + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + ), + clientBuilder: func(_ string, _ corelisters.SecretLister, _ cmapi.GenericIssuer) (venaficlient.Interface, error) { + return &fakevenaficlient.Venafi{ + RetrieveCertificateFn: func(_ string, _ []byte, _ time.Duration, _ []venafiapi.CustomField) ([]byte, error) { + return nil, endpoint.ErrRetrieveCertificateTimeout{} + }, + }, nil + }, + expectedErr: true, + builder: &testpkg.Builder{ + CertManagerObjects: []runtime.Object{baseIssuer.DeepCopy()}, + ExpectedEvents: []string{ + "Normal IssuancePending Venafi certificate still in a pending state, the request will be retried", + }, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewCreateAction( + authzv1.SchemeGroupVersion.WithResource("subjectaccessreviews"), + "", + &authzv1.SubjectAccessReview{ + Spec: authzv1.SubjectAccessReviewSpec{ + User: "user-1", + Groups: []string{"group-1", "group-2"}, + Extra: map[string]authzv1.ExtraValue{ + "extra": []string{"1", "2"}, + }, + UID: "uid-1", + + ResourceAttributes: &authzv1.ResourceAttributes{ + Group: certmanager.GroupName, + Resource: "signers", + Verb: "reference", + Namespace: baseIssuer.Namespace, + Name: baseIssuer.Name, + Version: "*", + }, + }, + }, + )), + }, + }, + }, + "an approved CSR which has a pickup ID, retrieve certificate returns a generic error, fire event and return error": { + csr: gen.CertificateSigningRequestFrom(baseCSR, + gen.AddCertificateSigningRequestAnnotations(map[string]string{ + "venafi.experimental.cert-manager.io/custom-fields": `[ {"name": "field-name", "value": "vield value"}]`, + "venafi.experimental.cert-manager.io/pickup-id": "test-pickup-id", + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + ), + clientBuilder: func(_ string, _ corelisters.SecretLister, _ cmapi.GenericIssuer) (venaficlient.Interface, error) { + return &fakevenaficlient.Venafi{ + RetrieveCertificateFn: func(_ string, _ []byte, _ time.Duration, _ []venafiapi.CustomField) ([]byte, error) { + return nil, errors.New("generic error") + }, + }, nil + }, + expectedErr: true, + builder: &testpkg.Builder{ + CertManagerObjects: []runtime.Object{baseIssuer.DeepCopy()}, + ExpectedEvents: []string{ + "Warning ErrorRetrieve Failed to obtain venafi certificate: generic error", + }, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewCreateAction( + authzv1.SchemeGroupVersion.WithResource("subjectaccessreviews"), + "", + &authzv1.SubjectAccessReview{ + Spec: authzv1.SubjectAccessReviewSpec{ + User: "user-1", + Groups: []string{"group-1", "group-2"}, + Extra: map[string]authzv1.ExtraValue{ + "extra": []string{"1", "2"}, + }, + UID: "uid-1", + + ResourceAttributes: &authzv1.ResourceAttributes{ + Group: certmanager.GroupName, + Resource: "signers", + Verb: "reference", + Namespace: baseIssuer.Namespace, + Name: baseIssuer.Name, + Version: "*", + }, + }, + }, + )), + }, + }, + }, + "an approved CSR which has a pickup ID, retrieve certificate returns garbage certificates, should mark as Failed": { + csr: gen.CertificateSigningRequestFrom(baseCSR, + gen.AddCertificateSigningRequestAnnotations(map[string]string{ + "venafi.experimental.cert-manager.io/custom-fields": `[ {"name": "field-name", "value": "vield value"}]`, + "venafi.experimental.cert-manager.io/pickup-id": "test-pickup-id", + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + ), + clientBuilder: func(_ string, _ corelisters.SecretLister, _ cmapi.GenericIssuer) (venaficlient.Interface, error) { + return &fakevenaficlient.Venafi{ + RetrieveCertificateFn: func(_ string, _ []byte, _ time.Duration, _ []venafiapi.CustomField) ([]byte, error) { + return []byte("garbage"), nil + }, + }, nil + }, + builder: &testpkg.Builder{ + CertManagerObjects: []runtime.Object{baseIssuer.DeepCopy()}, + ExpectedEvents: []string{ + "Warning ErrorParse Failed to parse returned certificate bundle: error decoding certificate PEM block", + }, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewCreateAction( + authzv1.SchemeGroupVersion.WithResource("subjectaccessreviews"), + "", + &authzv1.SubjectAccessReview{ + Spec: authzv1.SubjectAccessReviewSpec{ + User: "user-1", + Groups: []string{"group-1", "group-2"}, + Extra: map[string]authzv1.ExtraValue{ + "extra": []string{"1", "2"}, + }, + UID: "uid-1", + + ResourceAttributes: &authzv1.ResourceAttributes{ + Group: certmanager.GroupName, + Resource: "signers", + Verb: "reference", + Namespace: baseIssuer.Namespace, + Name: baseIssuer.Name, + Version: "*", + }, + }, + }, + )), + testpkg.NewAction(coretesting.NewUpdateSubresourceAction( + certificatesv1.SchemeGroupVersion.WithResource("certificatesigningrequests"), + "status", + "", + gen.CertificateSigningRequestFrom(baseCSR.DeepCopy(), + gen.AddCertificateSigningRequestAnnotations(map[string]string{ + "venafi.experimental.cert-manager.io/custom-fields": `[ {"name": "field-name", "value": "vield value"}]`, + "venafi.experimental.cert-manager.io/pickup-id": "test-pickup-id", + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateFailed, + Status: corev1.ConditionTrue, + Reason: "ErrorParse", + Message: "Failed to parse returned certificate bundle: error decoding certificate PEM block", + LastTransitionTime: metaFixedClockStart, + LastUpdateTime: metaFixedClockStart, + }), + ), + )), + }, + }, + }, + "an approved CSR which has a pickup ID, retrieve certificate returns a CA and certificate should update with CA and certificate": { + csr: gen.CertificateSigningRequestFrom(baseCSR, + gen.AddCertificateSigningRequestAnnotations(map[string]string{ + "venafi.experimental.cert-manager.io/custom-fields": `[ {"name": "field-name", "value": "vield value"}]`, + "venafi.experimental.cert-manager.io/pickup-id": "test-pickup-id", + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + ), + clientBuilder: func(_ string, _ corelisters.SecretLister, _ cmapi.GenericIssuer) (venaficlient.Interface, error) { + return &fakevenaficlient.Venafi{ + RetrieveCertificateFn: func(_ string, _ []byte, _ time.Duration, _ []venafiapi.CustomField) ([]byte, error) { + return []byte(fmt.Sprintf("%s%s", certBundle.ChainPEM, certBundle.CAPEM)), nil + }, + }, nil + }, + builder: &testpkg.Builder{ + CertManagerObjects: []runtime.Object{baseIssuer.DeepCopy()}, + ExpectedEvents: []string{ + "Normal CertificateIssued Certificate fetched from venafi issuer successfully", + }, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewCreateAction( + authzv1.SchemeGroupVersion.WithResource("subjectaccessreviews"), + "", + &authzv1.SubjectAccessReview{ + Spec: authzv1.SubjectAccessReviewSpec{ + User: "user-1", + Groups: []string{"group-1", "group-2"}, + Extra: map[string]authzv1.ExtraValue{ + "extra": []string{"1", "2"}, + }, + UID: "uid-1", + + ResourceAttributes: &authzv1.ResourceAttributes{ + Group: certmanager.GroupName, + Resource: "signers", + Verb: "reference", + Namespace: baseIssuer.Namespace, + Name: baseIssuer.Name, + Version: "*", + }, + }, + }, + )), + testpkg.NewAction(coretesting.NewUpdateSubresourceAction( + certificatesv1.SchemeGroupVersion.WithResource("certificatesigningrequests"), + "status", + "", + gen.CertificateSigningRequestFrom(baseCSR.DeepCopy(), + gen.AddCertificateSigningRequestAnnotations(map[string]string{ + "venafi.experimental.cert-manager.io/custom-fields": `[ {"name": "field-name", "value": "vield value"}]`, + "venafi.experimental.cert-manager.io/pickup-id": "test-pickup-id", + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + gen.SetCertificateSigningRequestCertificate(certBundle.ChainPEM), + ), + )), + testpkg.NewAction(coretesting.NewUpdateAction( + certificatesv1.SchemeGroupVersion.WithResource("certificatesigningrequests"), + "", + gen.CertificateSigningRequestFrom(baseCSR.DeepCopy(), + gen.AddCertificateSigningRequestAnnotations(map[string]string{ + "venafi.experimental.cert-manager.io/custom-fields": `[ {"name": "field-name", "value": "vield value"}]`, + "venafi.experimental.cert-manager.io/pickup-id": "test-pickup-id", + }), + gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{ + Type: certificatesv1.CertificateApproved, + Status: corev1.ConditionTrue, + }), + gen.SetCertificateSigningRequestCertificate(certBundle.ChainPEM), + gen.SetCertificateSigningRequestCA(certBundle.CAPEM), + ), + )), + }, + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + if test.csr != nil { + test.builder.KubeObjects = append(test.builder.KubeObjects, test.csr) + } + + fixedClock.SetTime(fixedClockStart) + test.builder.Clock = fixedClock + test.builder.T = t + test.builder.Init() + + // Always return true for SubjectAccessReviews in tests + test.builder.FakeKubeClient().PrependReactor("create", "*", func(action coretesting.Action) (bool, runtime.Object, error) { + if action.GetResource() != authzv1.SchemeGroupVersion.WithResource("subjectaccessreviews") { + return false, nil, nil + } + return true, &authzv1.SubjectAccessReview{ + Status: authzv1.SubjectAccessReviewStatus{ + Allowed: true, + }, + }, nil + }) + + defer test.builder.Stop() + + venafi := NewVenafi(test.builder.Context) + venafi.clientBuilder = test.clientBuilder + + controller := certificatesigningrequests.New(apiutil.IssuerVenafi, venafi) + controller.Register(test.builder.Context) + test.builder.Start() + + err := controller.ProcessItem(context.Background(), test.csr.Name) + if err != nil && !test.expectedErr { + t.Errorf("expected to not get an error, but got: %v", err) + } + if err == nil && test.expectedErr { + t.Errorf("expected to get an error but did not get one") + } + + test.builder.CheckAndFinish(err) + }) + } +}