...

Source file src/github.com/cert-manager/issuer-lib/controllers/certificaterequest_controller_test.go

Documentation: github.com/cert-manager/issuer-lib/controllers

     1  /*
     2  Copyright 2023 The cert-manager Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package controllers
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"testing"
    24  	"time"
    25  
    26  	cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
    27  	cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
    28  	cmgen "github.com/cert-manager/cert-manager/test/unit/gen"
    29  	logrtesting "github.com/go-logr/logr/testing"
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	"k8s.io/apimachinery/pkg/runtime"
    34  	"k8s.io/apimachinery/pkg/types"
    35  	"k8s.io/client-go/tools/record"
    36  	clocktesting "k8s.io/utils/clock/testing"
    37  	"k8s.io/utils/ptr"
    38  	"sigs.k8s.io/controller-runtime/pkg/client"
    39  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    40  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    41  
    42  	"github.com/cert-manager/issuer-lib/api/v1alpha1"
    43  	"github.com/cert-manager/issuer-lib/conditions"
    44  	"github.com/cert-manager/issuer-lib/controllers/signer"
    45  	"github.com/cert-manager/issuer-lib/internal/kubeutil"
    46  	"github.com/cert-manager/issuer-lib/internal/testapi/api"
    47  	"github.com/cert-manager/issuer-lib/internal/testapi/testutil"
    48  	"github.com/cert-manager/issuer-lib/internal/tests/errormatch"
    49  )
    50  
    51  func TestCertificateRequestReconcilerReconcile(t *testing.T) {
    52  	t.Parallel()
    53  
    54  	fieldOwner := "test-certificate-request-reconciler-reconcile"
    55  
    56  	type testCase struct {
    57  		name                string
    58  		sign                signer.Sign
    59  		objects             []client.Object
    60  		validateError       *errormatch.Matcher
    61  		expectedResult      reconcile.Result
    62  		expectedStatusPatch *cmapi.CertificateRequestStatus
    63  		expectedEvents      []string
    64  	}
    65  
    66  	randTime := randomTime()
    67  
    68  	fakeTime1 := randTime.Truncate(time.Second)
    69  	fakeTimeObj1 := metav1.NewTime(fakeTime1)
    70  	fakeClock1 := clocktesting.NewFakeClock(fakeTime1)
    71  
    72  	fakeTime2 := randTime.Add(4 * time.Hour).Truncate(time.Second)
    73  	fakeTimeObj2 := metav1.NewTime(fakeTime2)
    74  	fakeClock2 := clocktesting.NewFakeClock(fakeTime2)
    75  
    76  	issuer1 := testutil.TestIssuer(
    77  		"issuer-1",
    78  		testutil.SetTestIssuerNamespace("ns1"),
    79  		testutil.SetTestIssuerGeneration(70),
    80  		testutil.SetTestIssuerStatusCondition(
    81  			fakeClock1,
    82  			cmapi.IssuerConditionReady,
    83  			cmmeta.ConditionTrue,
    84  			v1alpha1.IssuerConditionReasonChecked,
    85  			"Succeeded checking the issuer",
    86  		),
    87  	)
    88  
    89  	clusterIssuer1 := testutil.TestClusterIssuer(
    90  		"cluster-issuer-1",
    91  		testutil.SetTestClusterIssuerGeneration(70),
    92  		testutil.SetTestClusterIssuerStatusCondition(
    93  			fakeClock1,
    94  			cmapi.IssuerConditionReady,
    95  			cmmeta.ConditionTrue,
    96  			v1alpha1.IssuerConditionReasonChecked,
    97  			"Succeeded checking the issuer",
    98  		),
    99  	)
   100  
   101  	cr1 := cmgen.CertificateRequest(
   102  		"cr1",
   103  		cmgen.SetCertificateRequestNamespace("ns1"),
   104  		cmgen.SetCertificateRequestIssuer(cmmeta.ObjectReference{
   105  			Group: api.SchemeGroupVersion.Group,
   106  		}),
   107  		func(cr *cmapi.CertificateRequest) {
   108  			conditions.SetCertificateRequestStatusCondition(
   109  				fakeClock1,
   110  				cr.Status.Conditions,
   111  				&cr.Status.Conditions,
   112  				cmapi.CertificateRequestConditionReady,
   113  				cmmeta.ConditionUnknown,
   114  				v1alpha1.CertificateRequestConditionReasonInitializing,
   115  				fieldOwner+" has begun reconciling this CertificateRequest",
   116  			)
   117  			conditions.SetCertificateRequestStatusCondition(
   118  				fakeClock1,
   119  				cr.Status.Conditions,
   120  				&cr.Status.Conditions,
   121  				cmapi.CertificateRequestConditionApproved,
   122  				cmmeta.ConditionTrue,
   123  				"ApprovedReason",
   124  				"ApprovedMessage",
   125  			)
   126  		},
   127  	)
   128  
   129  	successSigner := func(cert string) signer.Sign {
   130  		return func(_ context.Context, _ signer.CertificateRequestObject, _ v1alpha1.Issuer) (signer.PEMBundle, error) {
   131  			return signer.PEMBundle{
   132  				ChainPEM: []byte(cert),
   133  			}, nil
   134  		}
   135  	}
   136  
   137  	tests := []testCase{
   138  		// NOTE: The IssuerError error cannot be tested in this unit test. It is tested in the
   139  		// integration test instead.
   140  
   141  		// Ignore the request if the CertificateRequest is not found.
   142  		{
   143  			name:    "ignore-certificaterequest-not-found",
   144  			objects: []client.Object{},
   145  		},
   146  
   147  		// Ignore unless approved or denied.
   148  		{
   149  			name: "ignore-unless-approved-or-denied",
   150  			objects: []client.Object{
   151  				cmgen.CertificateRequestFrom(cr1, func(cr *cmapi.CertificateRequest) {
   152  					cr.Status.Conditions = nil
   153  				}),
   154  			},
   155  		},
   156  
   157  		// Ignore CertificateRequest with an unknown issuerRef group.
   158  		{
   159  			name: "issuer-ref-unknown-group",
   160  			objects: []client.Object{
   161  				cmgen.CertificateRequestFrom(cr1, func(cr *cmapi.CertificateRequest) {
   162  					cr.Spec.IssuerRef.Group = "unknown-group"
   163  				}),
   164  			},
   165  		},
   166  
   167  		// Ignore CertificateRequest with an unknown issuerRef kind.
   168  		{
   169  			name: "issuer-ref-unknown-kind",
   170  			objects: []client.Object{
   171  				cmgen.CertificateRequestFrom(cr1, func(cr *cmapi.CertificateRequest) {
   172  					cr.Spec.IssuerRef.Kind = "unknown-kind"
   173  				}),
   174  			},
   175  		},
   176  
   177  		// Ignore CertificateRequest which is already Ready.
   178  		{
   179  			name: "already-ready",
   180  			objects: []client.Object{
   181  				cmgen.CertificateRequestFrom(cr1,
   182  					cmgen.SetCertificateRequestStatusCondition(cmapi.CertificateRequestCondition{
   183  						Type:   cmapi.CertificateRequestConditionReady,
   184  						Reason: cmapi.CertificateRequestReasonIssued,
   185  						Status: cmmeta.ConditionTrue,
   186  					}),
   187  				),
   188  			},
   189  		},
   190  
   191  		// Ignore CertificateRequest which is already Failed.
   192  		{
   193  			name: "already-failed",
   194  			objects: []client.Object{
   195  				cmgen.CertificateRequestFrom(cr1,
   196  					cmgen.SetCertificateRequestStatusCondition(cmapi.CertificateRequestCondition{
   197  						Type:   cmapi.CertificateRequestConditionReady,
   198  						Status: cmmeta.ConditionFalse,
   199  						Reason: cmapi.CertificateRequestReasonFailed,
   200  					}),
   201  				),
   202  			},
   203  		},
   204  
   205  		// Ignore CertificateRequest which is already Denied.
   206  		{
   207  			name: "already-denied",
   208  			objects: []client.Object{
   209  				cmgen.CertificateRequestFrom(cr1,
   210  					cmgen.SetCertificateRequestStatusCondition(cmapi.CertificateRequestCondition{
   211  						Type:   cmapi.CertificateRequestConditionReady,
   212  						Status: cmmeta.ConditionFalse,
   213  						Reason: cmapi.CertificateRequestReasonDenied,
   214  					}),
   215  				),
   216  			},
   217  		},
   218  
   219  		// Initialize the CertificateRequest Ready condition if it is missing.
   220  		{
   221  			name: "initialize-ready-condition",
   222  			objects: []client.Object{
   223  				cmgen.CertificateRequestFrom(cr1, func(cr *cmapi.CertificateRequest) {
   224  					removeCertificateRequestCondition(cr, cmapi.CertificateRequestConditionReady)
   225  				}),
   226  			},
   227  			expectedStatusPatch: &cmapi.CertificateRequestStatus{
   228  				Conditions: []cmapi.CertificateRequestCondition{
   229  					{
   230  						Type:               cmapi.CertificateRequestConditionReady,
   231  						Status:             cmmeta.ConditionUnknown,
   232  						Reason:             v1alpha1.CertificateRequestConditionReasonInitializing,
   233  						Message:            fieldOwner + " has started reconciling this CertificateRequest",
   234  						LastTransitionTime: &fakeTimeObj2,
   235  					},
   236  				},
   237  			},
   238  		},
   239  
   240  		// If denied, set Ready condition status to false and reason to denied.
   241  		{
   242  			name: "set-ready-denied",
   243  			objects: []client.Object{
   244  				cmgen.CertificateRequestFrom(cr1, cmgen.SetCertificateRequestStatusCondition(cmapi.CertificateRequestCondition{
   245  					Type:   cmapi.CertificateRequestConditionDenied,
   246  					Status: cmmeta.ConditionTrue,
   247  					Reason: "",
   248  				})),
   249  			},
   250  			expectedStatusPatch: &cmapi.CertificateRequestStatus{
   251  				Conditions: []cmapi.CertificateRequestCondition{
   252  					{
   253  						Type:               cmapi.CertificateRequestConditionReady,
   254  						Status:             cmmeta.ConditionFalse,
   255  						Reason:             cmapi.CertificateRequestReasonDenied,
   256  						Message:            "Detected that the CertificateRequest is denied, so it will never be Ready.",
   257  						LastTransitionTime: &fakeTimeObj2,
   258  					},
   259  				},
   260  				FailureTime: &fakeTimeObj2,
   261  			},
   262  			expectedEvents: []string{
   263  				"Warning PermanentError Detected that the CertificateRequest is denied, so it will never be Ready.",
   264  			},
   265  		},
   266  
   267  		// If issuer is missing, set Ready condition status to false and reason to pending.
   268  		{
   269  			name: "set-ready-pending-missing-issuer",
   270  			objects: []client.Object{
   271  				cmgen.CertificateRequestFrom(cr1, func(cr *cmapi.CertificateRequest) {
   272  					cr.Spec.IssuerRef.Name = issuer1.Name
   273  					cr.Spec.IssuerRef.Kind = issuer1.Kind
   274  				}),
   275  			},
   276  			expectedStatusPatch: &cmapi.CertificateRequestStatus{
   277  				Conditions: []cmapi.CertificateRequestCondition{
   278  					{
   279  						Type:               cmapi.CertificateRequestConditionReady,
   280  						Status:             cmmeta.ConditionFalse,
   281  						Reason:             cmapi.CertificateRequestReasonPending,
   282  						Message:            "testissuers.testing.cert-manager.io \"issuer-1\" not found. Waiting for it to be created.",
   283  						LastTransitionTime: &fakeTimeObj2,
   284  					},
   285  				},
   286  			},
   287  			expectedEvents: []string{
   288  				"Normal WaitingForIssuerExist testissuers.testing.cert-manager.io \"issuer-1\" not found. Waiting for it to be created.",
   289  			},
   290  		},
   291  
   292  		// If issuer has no ready condition, set Ready condition status to false and reason to
   293  		// pending.
   294  		{
   295  			name: "set-ready-pending-issuer-has-no-ready-condition",
   296  			objects: []client.Object{
   297  				cmgen.CertificateRequestFrom(cr1,
   298  					cmgen.SetCertificateRequestIssuer(cmmeta.ObjectReference{
   299  						Name:  issuer1.Name,
   300  						Group: api.SchemeGroupVersion.Group,
   301  					}),
   302  				),
   303  				testutil.TestIssuerFrom(issuer1,
   304  					func(si *api.TestIssuer) {
   305  						si.Status.Conditions = nil
   306  					},
   307  				),
   308  			},
   309  			expectedStatusPatch: &cmapi.CertificateRequestStatus{
   310  				Conditions: []cmapi.CertificateRequestCondition{
   311  					{
   312  						Type:               cmapi.CertificateRequestConditionReady,
   313  						Status:             cmmeta.ConditionFalse,
   314  						Reason:             cmapi.CertificateRequestReasonPending,
   315  						Message:            "Waiting for issuer to become ready. Current issuer ready condition: <none>.",
   316  						LastTransitionTime: &fakeTimeObj2,
   317  					},
   318  				},
   319  			},
   320  			expectedEvents: []string{
   321  				"Normal WaitingForIssuerReady Waiting for issuer to become ready. Current issuer ready condition: <none>.",
   322  			},
   323  		},
   324  
   325  		// If issuer is not ready, set Ready condition status to false and reason to pending.
   326  		{
   327  			name: "set-ready-pending-issuer-is-not-ready",
   328  			objects: []client.Object{
   329  				cmgen.CertificateRequestFrom(cr1,
   330  					cmgen.SetCertificateRequestIssuer(cmmeta.ObjectReference{
   331  						Name:  issuer1.Name,
   332  						Group: api.SchemeGroupVersion.Group,
   333  					}),
   334  				),
   335  				testutil.TestIssuerFrom(issuer1,
   336  					testutil.SetTestIssuerStatusCondition(
   337  						fakeClock1,
   338  						cmapi.IssuerConditionReady,
   339  						cmmeta.ConditionFalse,
   340  						"[REASON]",
   341  						"[MESSAGE]",
   342  					),
   343  				),
   344  			},
   345  			expectedStatusPatch: &cmapi.CertificateRequestStatus{
   346  				Conditions: []cmapi.CertificateRequestCondition{
   347  					{
   348  						Type:               cmapi.CertificateRequestConditionReady,
   349  						Status:             cmmeta.ConditionFalse,
   350  						Reason:             cmapi.CertificateRequestReasonPending,
   351  						Message:            "Waiting for issuer to become ready. Current issuer ready condition is \"[REASON]\": [MESSAGE].",
   352  						LastTransitionTime: &fakeTimeObj2,
   353  					},
   354  				},
   355  			},
   356  			expectedEvents: []string{
   357  				"Normal WaitingForIssuerReady Waiting for issuer to become ready. Current issuer ready condition is \"[REASON]\": [MESSAGE].",
   358  			},
   359  		},
   360  
   361  		// If issuer's ready condition is outdated, set Ready condition status to false and reason
   362  		// to pending.
   363  		{
   364  			name: "set-ready-pending-issuer-ready-outdated",
   365  			objects: []client.Object{
   366  				cmgen.CertificateRequestFrom(cr1,
   367  					cmgen.SetCertificateRequestIssuer(cmmeta.ObjectReference{
   368  						Name:  issuer1.Name,
   369  						Group: api.SchemeGroupVersion.Group,
   370  					}),
   371  				),
   372  				testutil.TestIssuerFrom(issuer1,
   373  					testutil.SetTestIssuerGeneration(issuer1.Generation+1),
   374  				),
   375  			},
   376  			expectedStatusPatch: &cmapi.CertificateRequestStatus{
   377  				Conditions: []cmapi.CertificateRequestCondition{
   378  					{
   379  						Type:               cmapi.CertificateRequestConditionReady,
   380  						Status:             cmmeta.ConditionFalse,
   381  						Reason:             cmapi.CertificateRequestReasonPending,
   382  						Message:            "Waiting for issuer to become ready. Current issuer ready condition is outdated.",
   383  						LastTransitionTime: &fakeTimeObj2,
   384  					},
   385  				},
   386  			},
   387  			expectedEvents: []string{
   388  				"Normal WaitingForIssuerReady Waiting for issuer to become ready. Current issuer ready condition is outdated.",
   389  			},
   390  		},
   391  
   392  		// If the sign function returns an error & it's too late for a retry, set the Ready
   393  		// condition to Failed.
   394  		{
   395  			name: "timeout-permanent-error",
   396  			sign: func(_ context.Context, cr signer.CertificateRequestObject, _ v1alpha1.Issuer) (signer.PEMBundle, error) {
   397  				return signer.PEMBundle{}, fmt.Errorf("a specific error")
   398  			},
   399  			objects: []client.Object{
   400  				cmgen.CertificateRequestFrom(cr1,
   401  					cmgen.SetCertificateRequestIssuer(cmmeta.ObjectReference{
   402  						Name:  issuer1.Name,
   403  						Group: api.SchemeGroupVersion.Group,
   404  					}),
   405  					func(cr *cmapi.CertificateRequest) {
   406  						cr.CreationTimestamp = metav1.NewTime(fakeTimeObj2.Add(-2 * time.Minute))
   407  					},
   408  				),
   409  				testutil.TestIssuerFrom(issuer1),
   410  			},
   411  			expectedStatusPatch: &cmapi.CertificateRequestStatus{
   412  				Conditions: []cmapi.CertificateRequestCondition{
   413  					{
   414  						Type:               cmapi.CertificateRequestConditionReady,
   415  						Status:             cmmeta.ConditionFalse,
   416  						Reason:             cmapi.CertificateRequestReasonFailed,
   417  						Message:            "Failed permanently to sign CertificateRequest: a specific error",
   418  						LastTransitionTime: &fakeTimeObj2,
   419  					},
   420  				},
   421  				FailureTime: &fakeTimeObj2,
   422  			},
   423  			validateError: errormatch.ErrorContains("terminal error: a specific error"),
   424  			expectedEvents: []string{
   425  				"Warning PermanentError Failed permanently to sign CertificateRequest: a specific error",
   426  			},
   427  		},
   428  
   429  		// If the sign function returns a reason for being pending, set the Ready condition to Pending (even if
   430  		// the MaxRetryDuration has been exceeded).
   431  		{
   432  			name: "retry-on-pending-error",
   433  			sign: func(_ context.Context, cr signer.CertificateRequestObject, _ v1alpha1.Issuer) (signer.PEMBundle, error) {
   434  				return signer.PEMBundle{}, signer.PendingError{Err: fmt.Errorf("reason for being pending")}
   435  			},
   436  			objects: []client.Object{
   437  				cmgen.CertificateRequestFrom(cr1,
   438  					cmgen.SetCertificateRequestIssuer(cmmeta.ObjectReference{
   439  						Name:  issuer1.Name,
   440  						Group: api.SchemeGroupVersion.Group,
   441  					}),
   442  					func(cr *cmapi.CertificateRequest) {
   443  						cr.CreationTimestamp = metav1.NewTime(fakeTimeObj2.Add(-2 * time.Minute))
   444  					},
   445  				),
   446  				testutil.TestIssuerFrom(issuer1),
   447  			},
   448  			expectedStatusPatch: &cmapi.CertificateRequestStatus{
   449  				Conditions: []cmapi.CertificateRequestCondition{
   450  					{
   451  						Type:               cmapi.CertificateRequestConditionReady,
   452  						Status:             cmmeta.ConditionFalse,
   453  						Reason:             cmapi.CertificateRequestReasonPending,
   454  						Message:            "Signing still in progress. Reason: Signing still in progress. Reason: reason for being pending",
   455  						LastTransitionTime: &fakeTimeObj2,
   456  					},
   457  				},
   458  			},
   459  			expectedResult: reconcile.Result{
   460  				Requeue: true,
   461  			},
   462  			expectedEvents: []string{
   463  				"Warning RetryableError Signing still in progress. Reason: Signing still in progress. Reason: reason for being pending",
   464  			},
   465  		},
   466  
   467  		// If the sign function returns an SetCertificateRequestConditionError error with a condition
   468  		// type that is *not present* in the status, the new condition is *added* to the
   469  		// CertificateRequest.
   470  		// Additionally, if the error wrapped by SetCertificateRequestConditionError is not one of the
   471  		// supported 'signer API' errors an we still *have time left* to retry, set the Ready
   472  		// condition to *Pending*.
   473  		{
   474  			name: "error-set-certificate-request-condition-should-add-new-condition-and-retry",
   475  			sign: func(_ context.Context, cr signer.CertificateRequestObject, _ v1alpha1.Issuer) (signer.PEMBundle, error) {
   476  				return signer.PEMBundle{}, signer.SetCertificateRequestConditionError{
   477  					Err:           fmt.Errorf("test error"),
   478  					ConditionType: "[condition type]",
   479  					Status:        cmmeta.ConditionTrue,
   480  					Reason:        "[reason]",
   481  				}
   482  			},
   483  			objects: []client.Object{
   484  				cmgen.CertificateRequestFrom(cr1,
   485  					func(cr *cmapi.CertificateRequest) {
   486  						cr.CreationTimestamp = fakeTimeObj2
   487  					},
   488  					cmgen.SetCertificateRequestIssuer(cmmeta.ObjectReference{
   489  						Name:  issuer1.Name,
   490  						Group: api.SchemeGroupVersion.Group,
   491  					}),
   492  				),
   493  				testutil.TestIssuerFrom(issuer1),
   494  			},
   495  			expectedStatusPatch: &cmapi.CertificateRequestStatus{
   496  				Conditions: []cmapi.CertificateRequestCondition{
   497  					{
   498  						Type:               "[condition type]",
   499  						Status:             cmmeta.ConditionTrue,
   500  						Reason:             "[reason]",
   501  						Message:            "test error",
   502  						LastTransitionTime: &fakeTimeObj2,
   503  					},
   504  					{
   505  						Type:               cmapi.CertificateRequestConditionReady,
   506  						Status:             cmmeta.ConditionFalse,
   507  						Reason:             cmapi.CertificateRequestReasonPending,
   508  						Message:            "Failed to sign CertificateRequest, will retry: test error",
   509  						LastTransitionTime: &fakeTimeObj2,
   510  					},
   511  				},
   512  			},
   513  			validateError: errormatch.ErrorContains("terminal error: test error"),
   514  			expectedEvents: []string{
   515  				"Warning RetryableError Failed to sign CertificateRequest, will retry: test error",
   516  			},
   517  		},
   518  
   519  		// If the sign function returns an SetCertificateRequestConditionError error with a condition
   520  		// type that is *already present* in the status, the existing condition is *updated* with
   521  		// the values specified in the error.
   522  		// Additionally, if the error wrapped by SetCertificateRequestConditionError is not one of the
   523  		// supported 'signer API' errors an we still *have time left* to retry, set the Ready
   524  		// condition to *Pending*.
   525  		{
   526  			name: "error-set-certificate-request-condition-should-update-existing-condition-and-retry",
   527  			sign: func(_ context.Context, cr signer.CertificateRequestObject, _ v1alpha1.Issuer) (signer.PEMBundle, error) {
   528  				return signer.PEMBundle{}, signer.SetCertificateRequestConditionError{
   529  					Err:           fmt.Errorf("test error2"),
   530  					ConditionType: "[condition type]",
   531  					Status:        cmmeta.ConditionTrue,
   532  					Reason:        "[reason]",
   533  				}
   534  			},
   535  			objects: []client.Object{
   536  				cmgen.CertificateRequestFrom(cr1,
   537  					func(cr *cmapi.CertificateRequest) {
   538  						cr.CreationTimestamp = fakeTimeObj2
   539  					},
   540  					cmgen.SetCertificateRequestIssuer(cmmeta.ObjectReference{
   541  						Name:  issuer1.Name,
   542  						Group: api.SchemeGroupVersion.Group,
   543  					}),
   544  					cmgen.AddCertificateRequestStatusCondition(cmapi.CertificateRequestCondition{
   545  						Type:               "[condition type]",
   546  						Status:             cmmeta.ConditionTrue,
   547  						Reason:             "[reason]",
   548  						Message:            "test error",
   549  						LastTransitionTime: &fakeTimeObj2,
   550  					}),
   551  				),
   552  				testutil.TestIssuerFrom(issuer1),
   553  			},
   554  			expectedStatusPatch: &cmapi.CertificateRequestStatus{
   555  				Conditions: []cmapi.CertificateRequestCondition{
   556  					{
   557  						Type:               "[condition type]",
   558  						Status:             cmmeta.ConditionTrue,
   559  						Reason:             "[reason]",
   560  						Message:            "test error2",
   561  						LastTransitionTime: &fakeTimeObj2,
   562  					},
   563  					{
   564  						Type:               cmapi.CertificateRequestConditionReady,
   565  						Status:             cmmeta.ConditionFalse,
   566  						Reason:             cmapi.CertificateRequestReasonPending,
   567  						Message:            "Failed to sign CertificateRequest, will retry: test error2",
   568  						LastTransitionTime: &fakeTimeObj2,
   569  					},
   570  				},
   571  			},
   572  			validateError: errormatch.ErrorContains("test error2"),
   573  			expectedEvents: []string{
   574  				"Warning RetryableError Failed to sign CertificateRequest, will retry: test error2",
   575  			},
   576  		},
   577  
   578  		// If the sign function returns an SetCertificateRequestConditionError error with a condition
   579  		// type that is *not present* in the status, the new condition is *added* to the
   580  		// CertificateRequest.
   581  		// Additionally, if the error wrapped by SetCertificateRequestConditionError is not one of the
   582  		// supported 'signer API' errors an we have *no time left* to retry, set the Ready condition
   583  		// to *Failed*.
   584  		{
   585  			name: "error-set-certificate-request-condition-should-add-new-condition-and-timeout",
   586  			sign: func(_ context.Context, cr signer.CertificateRequestObject, _ v1alpha1.Issuer) (signer.PEMBundle, error) {
   587  				return signer.PEMBundle{}, signer.SetCertificateRequestConditionError{
   588  					Err:           fmt.Errorf("test error"),
   589  					ConditionType: "[condition type]",
   590  					Status:        cmmeta.ConditionTrue,
   591  					Reason:        "[reason]",
   592  				}
   593  			},
   594  			objects: []client.Object{
   595  				cmgen.CertificateRequestFrom(cr1,
   596  					cmgen.SetCertificateRequestIssuer(cmmeta.ObjectReference{
   597  						Name:  issuer1.Name,
   598  						Group: api.SchemeGroupVersion.Group,
   599  					}),
   600  					func(cr *cmapi.CertificateRequest) {
   601  						cr.CreationTimestamp = metav1.NewTime(fakeTimeObj2.Add(-2 * time.Minute))
   602  					},
   603  				),
   604  				testutil.TestIssuerFrom(issuer1),
   605  			},
   606  			expectedStatusPatch: &cmapi.CertificateRequestStatus{
   607  				Conditions: []cmapi.CertificateRequestCondition{
   608  					{
   609  						Type:               "[condition type]",
   610  						Status:             cmmeta.ConditionTrue,
   611  						Reason:             "[reason]",
   612  						Message:            "test error",
   613  						LastTransitionTime: &fakeTimeObj2,
   614  					},
   615  					{
   616  						Type:               cmapi.CertificateRequestConditionReady,
   617  						Status:             cmmeta.ConditionFalse,
   618  						Reason:             cmapi.CertificateRequestReasonFailed,
   619  						Message:            "Failed permanently to sign CertificateRequest: test error",
   620  						LastTransitionTime: &fakeTimeObj2,
   621  					},
   622  				},
   623  				FailureTime: &fakeTimeObj2,
   624  			},
   625  			validateError: errormatch.ErrorContains("terminal error: test error"),
   626  			expectedEvents: []string{
   627  				"Warning PermanentError Failed permanently to sign CertificateRequest: test error",
   628  			},
   629  		},
   630  
   631  		// If the sign function returns an SetCertificateRequestConditionError error with a condition
   632  		// type that is *already present* in the status, the existing condition is *updated* with
   633  		// the values specified in the error.
   634  		// Additionally, if the error wrapped by SetCertificateRequestConditionError is not one of the
   635  		// supported 'signer API' errors an we have *no time left* to retry, set the Ready condition
   636  		// to *Failed*.
   637  		{
   638  			name: "error-set-certificate-request-condition-should-update-existing-condition-and-timeout",
   639  			sign: func(_ context.Context, cr signer.CertificateRequestObject, _ v1alpha1.Issuer) (signer.PEMBundle, error) {
   640  				return signer.PEMBundle{}, signer.SetCertificateRequestConditionError{
   641  					Err:           fmt.Errorf("test error2"),
   642  					ConditionType: "[condition type]",
   643  					Status:        cmmeta.ConditionTrue,
   644  					Reason:        "[reason]",
   645  				}
   646  			},
   647  			objects: []client.Object{
   648  				cmgen.CertificateRequestFrom(cr1,
   649  					cmgen.SetCertificateRequestIssuer(cmmeta.ObjectReference{
   650  						Name:  issuer1.Name,
   651  						Group: api.SchemeGroupVersion.Group,
   652  					}),
   653  					func(cr *cmapi.CertificateRequest) {
   654  						cr.CreationTimestamp = metav1.NewTime(fakeTimeObj2.Add(-2 * time.Minute))
   655  					},
   656  					cmgen.AddCertificateRequestStatusCondition(cmapi.CertificateRequestCondition{
   657  						Type:               "[condition type]",
   658  						Status:             cmmeta.ConditionTrue,
   659  						Reason:             "[reason]",
   660  						Message:            "test error",
   661  						LastTransitionTime: &fakeTimeObj1,
   662  					}),
   663  				),
   664  				testutil.TestIssuerFrom(issuer1),
   665  			},
   666  			expectedStatusPatch: &cmapi.CertificateRequestStatus{
   667  				Conditions: []cmapi.CertificateRequestCondition{
   668  					{
   669  						Type:               "[condition type]",
   670  						Status:             cmmeta.ConditionTrue,
   671  						Reason:             "[reason]",
   672  						Message:            "test error2",
   673  						LastTransitionTime: &fakeTimeObj1, // since the status is not updated, the LastTransitionTime is not updated either
   674  					},
   675  					{
   676  						Type:               cmapi.CertificateRequestConditionReady,
   677  						Status:             cmmeta.ConditionFalse,
   678  						Reason:             cmapi.CertificateRequestReasonFailed,
   679  						Message:            "Failed permanently to sign CertificateRequest: test error2",
   680  						LastTransitionTime: &fakeTimeObj2,
   681  					},
   682  				},
   683  				FailureTime: &fakeTimeObj2,
   684  			},
   685  			validateError: errormatch.ErrorContains("terminal error: test error2"),
   686  			expectedEvents: []string{
   687  				"Warning PermanentError Failed permanently to sign CertificateRequest: test error2",
   688  			},
   689  		},
   690  
   691  		// If the sign function returns an SetCertificateRequestConditionError, the specified
   692  		// conditions value is updated/ added to the CertificateRequest status.
   693  		// Additionally, if the error wrapped by SetCertificateRequestConditionError is a PendingError
   694  		// error, the Ready condition is set to Pending (even if the MaxRetryDuration has been
   695  		// exceeded).
   696  		{
   697  			name: "error-set-certificate-request-condition-should-not-timeout-if-pending",
   698  			sign: func(_ context.Context, cr signer.CertificateRequestObject, _ v1alpha1.Issuer) (signer.PEMBundle, error) {
   699  				return signer.PEMBundle{}, signer.SetCertificateRequestConditionError{
   700  					Err:           signer.PendingError{Err: fmt.Errorf("test error")},
   701  					ConditionType: "[condition type]",
   702  					Status:        cmmeta.ConditionTrue,
   703  					Reason:        "[reason]",
   704  				}
   705  			},
   706  			objects: []client.Object{
   707  				cmgen.CertificateRequestFrom(cr1,
   708  					cmgen.SetCertificateRequestIssuer(cmmeta.ObjectReference{
   709  						Name:  issuer1.Name,
   710  						Group: api.SchemeGroupVersion.Group,
   711  					}),
   712  					func(cr *cmapi.CertificateRequest) {
   713  						cr.CreationTimestamp = metav1.NewTime(fakeTimeObj2.Add(-2 * time.Minute))
   714  					},
   715  				),
   716  				testutil.TestIssuerFrom(issuer1),
   717  			},
   718  			expectedStatusPatch: &cmapi.CertificateRequestStatus{
   719  				Conditions: []cmapi.CertificateRequestCondition{
   720  					{
   721  						Type:               "[condition type]",
   722  						Status:             cmmeta.ConditionTrue,
   723  						Reason:             "[reason]",
   724  						Message:            "test error",
   725  						LastTransitionTime: &fakeTimeObj2,
   726  					},
   727  					{
   728  						Type:               cmapi.CertificateRequestConditionReady,
   729  						Status:             cmmeta.ConditionFalse,
   730  						Reason:             cmapi.CertificateRequestReasonPending,
   731  						Message:            "Signing still in progress. Reason: Signing still in progress. Reason: test error",
   732  						LastTransitionTime: &fakeTimeObj2,
   733  					},
   734  				},
   735  			},
   736  			expectedResult: reconcile.Result{
   737  				Requeue: false,
   738  			},
   739  			expectedEvents: []string{
   740  				"Warning RetryableError Signing still in progress. Reason: Signing still in progress. Reason: test error",
   741  			},
   742  		},
   743  
   744  		// If the sign function returns an SetCertificateRequestConditionError, the specified
   745  		// conditions value is updated/ added to the CertificateRequest status.
   746  		// Additionally, if the error wrapped by SetCertificateRequestConditionError is a PendingError
   747  		// error, the Ready condition is set to Failed (even if the MaxRetryDuration has NOT been
   748  		// exceeded).
   749  		{
   750  			name: "error-set-certificate-request-condition-should-not-retry-on-permanent-error",
   751  			sign: func(_ context.Context, cr signer.CertificateRequestObject, _ v1alpha1.Issuer) (signer.PEMBundle, error) {
   752  				return signer.PEMBundle{}, signer.SetCertificateRequestConditionError{
   753  					Err:           signer.PermanentError{Err: fmt.Errorf("test error")},
   754  					ConditionType: "[condition type]",
   755  					Status:        cmmeta.ConditionTrue,
   756  					Reason:        "[reason]",
   757  				}
   758  			},
   759  			objects: []client.Object{
   760  				cmgen.CertificateRequestFrom(cr1,
   761  					cmgen.SetCertificateRequestIssuer(cmmeta.ObjectReference{
   762  						Name:  issuer1.Name,
   763  						Group: api.SchemeGroupVersion.Group,
   764  					}),
   765  				),
   766  				testutil.TestIssuerFrom(issuer1),
   767  			},
   768  			expectedStatusPatch: &cmapi.CertificateRequestStatus{
   769  				Conditions: []cmapi.CertificateRequestCondition{
   770  					{
   771  						Type:               "[condition type]",
   772  						Status:             cmmeta.ConditionTrue,
   773  						Reason:             "[reason]",
   774  						Message:            "test error",
   775  						LastTransitionTime: &fakeTimeObj2,
   776  					},
   777  					{
   778  						Type:               cmapi.CertificateRequestConditionReady,
   779  						Status:             cmmeta.ConditionFalse,
   780  						Reason:             cmapi.CertificateRequestReasonFailed,
   781  						Message:            "Failed permanently to sign CertificateRequest: test error",
   782  						LastTransitionTime: &fakeTimeObj2,
   783  					},
   784  				},
   785  				FailureTime: &fakeTimeObj2,
   786  			},
   787  			validateError: errormatch.ErrorContains("terminal error: test error"),
   788  			expectedEvents: []string{
   789  				"Warning PermanentError Failed permanently to sign CertificateRequest: test error",
   790  			},
   791  		},
   792  
   793  		// Set the Ready condition to Failed if the sign function returns a permanent error.
   794  		{
   795  			name: "fail-on-permanent-error",
   796  			sign: func(_ context.Context, cr signer.CertificateRequestObject, _ v1alpha1.Issuer) (signer.PEMBundle, error) {
   797  				return signer.PEMBundle{}, signer.PermanentError{Err: fmt.Errorf("a specific error")}
   798  			},
   799  			objects: []client.Object{
   800  				cmgen.CertificateRequestFrom(cr1,
   801  					cmgen.SetCertificateRequestIssuer(cmmeta.ObjectReference{
   802  						Name:  issuer1.Name,
   803  						Group: api.SchemeGroupVersion.Group,
   804  					}),
   805  				),
   806  				testutil.TestIssuerFrom(issuer1),
   807  			},
   808  			expectedStatusPatch: &cmapi.CertificateRequestStatus{
   809  				Conditions: []cmapi.CertificateRequestCondition{
   810  					{
   811  						Type:               cmapi.CertificateRequestConditionReady,
   812  						Status:             cmmeta.ConditionFalse,
   813  						Reason:             cmapi.CertificateRequestReasonFailed,
   814  						Message:            "Failed permanently to sign CertificateRequest: a specific error",
   815  						LastTransitionTime: &fakeTimeObj2,
   816  					},
   817  				},
   818  				FailureTime: &fakeTimeObj2,
   819  			},
   820  			validateError: errormatch.ErrorContains("terminal error: a specific error"),
   821  			expectedEvents: []string{
   822  				"Warning PermanentError Failed permanently to sign CertificateRequest: a specific error",
   823  			},
   824  		},
   825  
   826  		// Set the Ready condition to Pending if sign returns an error and we still have time left
   827  		// to retry.
   828  		{
   829  			name: "retry-on-error",
   830  			sign: func(_ context.Context, cr signer.CertificateRequestObject, _ v1alpha1.Issuer) (signer.PEMBundle, error) {
   831  				return signer.PEMBundle{}, errors.New("waiting for approval")
   832  			},
   833  			objects: []client.Object{
   834  				cmgen.CertificateRequestFrom(cr1,
   835  					func(cr *cmapi.CertificateRequest) {
   836  						cr.CreationTimestamp = fakeTimeObj2
   837  					},
   838  					func(cr *cmapi.CertificateRequest) {
   839  						cr.Spec.IssuerRef.Name = issuer1.Name
   840  						cr.Spec.IssuerRef.Kind = issuer1.Kind
   841  					},
   842  				),
   843  				testutil.TestIssuerFrom(issuer1),
   844  			},
   845  			expectedStatusPatch: &cmapi.CertificateRequestStatus{
   846  				Conditions: []cmapi.CertificateRequestCondition{
   847  					{
   848  						Type:               cmapi.CertificateRequestConditionReady,
   849  						Status:             cmmeta.ConditionFalse,
   850  						Reason:             cmapi.CertificateRequestReasonPending,
   851  						Message:            "Failed to sign CertificateRequest, will retry: waiting for approval",
   852  						LastTransitionTime: &fakeTimeObj2,
   853  					},
   854  				},
   855  			},
   856  			validateError: errormatch.ErrorContains("waiting for approval"),
   857  			expectedEvents: []string{
   858  				"Warning RetryableError Failed to sign CertificateRequest, will retry: waiting for approval",
   859  			},
   860  		},
   861  
   862  		{
   863  			name: "success-issuer",
   864  			sign: successSigner("a-signed-certificate"),
   865  			objects: []client.Object{
   866  				cmgen.CertificateRequestFrom(cr1, func(cr *cmapi.CertificateRequest) {
   867  					cr.Spec.IssuerRef.Name = issuer1.Name
   868  					cr.Spec.IssuerRef.Kind = issuer1.Kind
   869  				}),
   870  				testutil.TestIssuerFrom(issuer1),
   871  			},
   872  			expectedStatusPatch: &cmapi.CertificateRequestStatus{
   873  				Certificate: []byte("a-signed-certificate"),
   874  				Conditions: []cmapi.CertificateRequestCondition{
   875  					{
   876  						Type:               cmapi.CertificateRequestConditionReady,
   877  						Status:             cmmeta.ConditionTrue,
   878  						Reason:             cmapi.CertificateRequestReasonIssued,
   879  						Message:            "Succeeded signing the CertificateRequest",
   880  						LastTransitionTime: &fakeTimeObj2,
   881  					},
   882  				},
   883  			},
   884  			expectedEvents: []string{
   885  				"Normal Issued Succeeded signing the CertificateRequest",
   886  			},
   887  		},
   888  
   889  		{
   890  			name: "success-clusterissuer",
   891  			sign: successSigner("a-signed-certificate"),
   892  			objects: []client.Object{
   893  				cmgen.CertificateRequestFrom(cr1, func(cr *cmapi.CertificateRequest) {
   894  					cr.Spec.IssuerRef.Name = clusterIssuer1.Name
   895  					cr.Spec.IssuerRef.Kind = clusterIssuer1.Kind
   896  				}),
   897  				testutil.TestClusterIssuerFrom(clusterIssuer1),
   898  			},
   899  			expectedStatusPatch: &cmapi.CertificateRequestStatus{
   900  				Certificate: []byte("a-signed-certificate"),
   901  				Conditions: []cmapi.CertificateRequestCondition{
   902  					{
   903  						Type:               cmapi.CertificateRequestConditionReady,
   904  						Status:             cmmeta.ConditionTrue,
   905  						Reason:             cmapi.CertificateRequestReasonIssued,
   906  						Message:            "Succeeded signing the CertificateRequest",
   907  						LastTransitionTime: &fakeTimeObj2,
   908  					},
   909  				},
   910  			},
   911  			expectedEvents: []string{
   912  				"Normal Issued Succeeded signing the CertificateRequest",
   913  			},
   914  		},
   915  	}
   916  
   917  	for _, tc := range tests {
   918  		tc := tc
   919  		t.Run(tc.name, func(t *testing.T) {
   920  			t.Parallel()
   921  
   922  			scheme := runtime.NewScheme()
   923  			require.NoError(t, setupCertificateRequestReconcilerScheme(scheme))
   924  			require.NoError(t, api.AddToScheme(scheme))
   925  			fakeClient := fake.NewClientBuilder().
   926  				WithScheme(scheme).
   927  				WithObjects(tc.objects...).
   928  				Build()
   929  
   930  			req := reconcile.Request{
   931  				NamespacedName: types.NamespacedName{
   932  					Name:      cr1.Name,
   933  					Namespace: cr1.Namespace,
   934  				},
   935  			}
   936  
   937  			var crBefore cmapi.CertificateRequest
   938  			err := fakeClient.Get(context.TODO(), req.NamespacedName, &crBefore)
   939  			require.NoError(t, client.IgnoreNotFound(err), "unexpected error from fake client")
   940  
   941  			logger := logrtesting.NewTestLoggerWithOptions(t, logrtesting.Options{LogTimestamp: true, Verbosity: 10})
   942  			fakeRecorder := record.NewFakeRecorder(100)
   943  
   944  			controller := (&CertificateRequestReconciler{
   945  				RequestController: RequestController{
   946  					IssuerTypes:        []v1alpha1.Issuer{&api.TestIssuer{}},
   947  					ClusterIssuerTypes: []v1alpha1.Issuer{&api.TestClusterIssuer{}},
   948  					FieldOwner:         fieldOwner,
   949  					MaxRetryDuration:   time.Minute,
   950  					EventSource:        kubeutil.NewEventStore(),
   951  					Client:             fakeClient,
   952  					Sign:               tc.sign,
   953  					EventRecorder:      fakeRecorder,
   954  					Clock:              fakeClock2,
   955  				},
   956  			}).Init()
   957  
   958  			err = controller.setAllIssuerTypesWithGroupVersionKind(scheme)
   959  			require.NoError(t, err)
   960  
   961  			res, statusPatch, reconcileErr := controller.reconcileStatusPatch(logger, context.TODO(), req)
   962  			var crStatusPatch *cmapi.CertificateRequestStatus
   963  			if statusPatch != nil {
   964  				crStatusPatch = statusPatch.(CertificateRequestPatch).CertificateRequestPatch()
   965  			}
   966  
   967  			assert.Equal(t, tc.expectedResult, res)
   968  			assert.Equal(t, tc.expectedStatusPatch, crStatusPatch)
   969  			ptr.Deref(tc.validateError, *errormatch.NoError())(t, reconcileErr)
   970  
   971  			allEvents := chanToSlice(fakeRecorder.Events)
   972  			if len(tc.expectedEvents) == 0 {
   973  				assert.Emptyf(t, allEvents, "expected no events to be recorded, but got: %#v", allEvents)
   974  			} else {
   975  				assert.Equal(t, tc.expectedEvents, allEvents)
   976  			}
   977  		})
   978  	}
   979  }
   980  
   981  func chanToSlice(ch <-chan string) []string {
   982  	out := make([]string, 0, len(ch))
   983  	for i := 0; i < len(ch); i++ {
   984  		out = append(out, <-ch)
   985  	}
   986  	return out
   987  }
   988  
   989  func removeCertificateRequestCondition(cr *cmapi.CertificateRequest, conditionType cmapi.CertificateRequestConditionType) {
   990  	for i, cond := range cr.Status.Conditions {
   991  		if cond.Type == conditionType {
   992  			cr.Status.Conditions = append(cr.Status.Conditions[:i], cr.Status.Conditions[i+1:]...)
   993  			return
   994  		}
   995  	}
   996  }
   997  
   998  func TestCertificateRequestMatchIssuerType(t *testing.T) {
   999  	t.Parallel()
  1000  
  1001  	type testcase struct {
  1002  		name string
  1003  
  1004  		issuerTypes        []v1alpha1.Issuer
  1005  		clusterIssuerTypes []v1alpha1.Issuer
  1006  		cr                 *cmapi.CertificateRequest
  1007  
  1008  		expectedIssuerType v1alpha1.Issuer
  1009  		expectedIssuerName types.NamespacedName
  1010  		expectedError      *errormatch.Matcher
  1011  	}
  1012  
  1013  	createCr := func(name string, namespace string, kind string, group string) *cmapi.CertificateRequest {
  1014  		return &cmapi.CertificateRequest{
  1015  			ObjectMeta: metav1.ObjectMeta{
  1016  				Namespace: namespace,
  1017  			},
  1018  			Spec: cmapi.CertificateRequestSpec{
  1019  				IssuerRef: cmmeta.ObjectReference{
  1020  					Name:  name,
  1021  					Kind:  kind,
  1022  					Group: group,
  1023  				},
  1024  			},
  1025  		}
  1026  	}
  1027  
  1028  	testcases := []testcase{
  1029  		{
  1030  			name:               "empty",
  1031  			issuerTypes:        nil,
  1032  			clusterIssuerTypes: nil,
  1033  			cr:                 nil,
  1034  
  1035  			expectedIssuerType: nil,
  1036  			expectedIssuerName: types.NamespacedName{},
  1037  			expectedError:      errormatch.ErrorContains("invalid reference, CertificateRequest is nil"),
  1038  		},
  1039  		{
  1040  			name:               "no issuers",
  1041  			issuerTypes:        nil,
  1042  			clusterIssuerTypes: nil,
  1043  			cr:                 createCr("name", "namespace", "", "test"),
  1044  
  1045  			expectedIssuerType: nil,
  1046  			expectedIssuerName: types.NamespacedName{},
  1047  			expectedError:      errormatch.ErrorContains("no issuer found for reference: [Group=\"test\", Kind=\"\", Name=\"name\"]"),
  1048  		},
  1049  		{
  1050  			name:               "match issuer",
  1051  			issuerTypes:        []v1alpha1.Issuer{&api.TestIssuer{}},
  1052  			clusterIssuerTypes: []v1alpha1.Issuer{&api.TestClusterIssuer{}},
  1053  			cr:                 createCr("name", "namespace", "TestIssuer", "testing.cert-manager.io"),
  1054  
  1055  			expectedIssuerType: &api.TestIssuer{},
  1056  			expectedIssuerName: types.NamespacedName{Name: "name", Namespace: "namespace"},
  1057  		},
  1058  		{
  1059  			name:               "match cluster issuer",
  1060  			issuerTypes:        []v1alpha1.Issuer{&api.TestIssuer{}},
  1061  			clusterIssuerTypes: []v1alpha1.Issuer{&api.TestClusterIssuer{}},
  1062  			cr:                 createCr("name", "namespace", "TestClusterIssuer", "testing.cert-manager.io"),
  1063  
  1064  			expectedIssuerType: &api.TestClusterIssuer{},
  1065  			expectedIssuerName: types.NamespacedName{Name: "name"},
  1066  		},
  1067  		{
  1068  			name:               "select kind if empty",
  1069  			issuerTypes:        []v1alpha1.Issuer{},
  1070  			clusterIssuerTypes: []v1alpha1.Issuer{&api.TestClusterIssuer{}},
  1071  			cr:                 createCr("name", "namespace", "", "testing.cert-manager.io"),
  1072  
  1073  			expectedIssuerType: &api.TestClusterIssuer{},
  1074  			expectedIssuerName: types.NamespacedName{Name: "name"},
  1075  		},
  1076  		{
  1077  			name:               "prefer issuer over cluster issuer (v1)",
  1078  			issuerTypes:        []v1alpha1.Issuer{&api.TestIssuer{}},
  1079  			clusterIssuerTypes: []v1alpha1.Issuer{&api.TestClusterIssuer{}},
  1080  			cr:                 createCr("name", "namespace", "", "testing.cert-manager.io"),
  1081  
  1082  			expectedIssuerType: &api.TestIssuer{},
  1083  			expectedIssuerName: types.NamespacedName{Name: "name", Namespace: "namespace"},
  1084  		},
  1085  		{
  1086  			name:               "prefer issuer over cluster issuer (v2)",
  1087  			issuerTypes:        []v1alpha1.Issuer{&api.TestIssuer{}},
  1088  			clusterIssuerTypes: []v1alpha1.Issuer{&api.TestIssuer{}},
  1089  			cr:                 createCr("name", "namespace", "", "testing.cert-manager.io"),
  1090  
  1091  			expectedIssuerType: &api.TestIssuer{},
  1092  			expectedIssuerName: types.NamespacedName{Name: "name", Namespace: "namespace"},
  1093  		},
  1094  	}
  1095  
  1096  	scheme := runtime.NewScheme()
  1097  	require.NoError(t, api.AddToScheme(scheme))
  1098  
  1099  	for _, tc := range testcases {
  1100  		tc := tc
  1101  		t.Run(tc.name, func(t *testing.T) {
  1102  			t.Parallel()
  1103  
  1104  			crr := &CertificateRequestReconciler{
  1105  				RequestController: RequestController{
  1106  					IssuerTypes:        tc.issuerTypes,
  1107  					ClusterIssuerTypes: tc.clusterIssuerTypes,
  1108  				},
  1109  			}
  1110  
  1111  			require.NoError(t, crr.setAllIssuerTypesWithGroupVersionKind(scheme))
  1112  
  1113  			issuerType, issuerName, err := crr.matchIssuerType(tc.cr)
  1114  
  1115  			if tc.expectedIssuerType != nil {
  1116  				require.NoError(t, kubeutil.SetGroupVersionKind(scheme, tc.expectedIssuerType))
  1117  			}
  1118  
  1119  			assert.Equal(t, tc.expectedIssuerType, issuerType)
  1120  			assert.Equal(t, tc.expectedIssuerName, issuerName)
  1121  			if !ptr.Deref(tc.expectedError, *errormatch.NoError())(t, err) {
  1122  				t.Fail()
  1123  			}
  1124  		})
  1125  	}
  1126  }
  1127  

View as plain text