Skip to content

Add unit tests around the new ocspServers field#3699

Merged
jetstack-bot merged 4 commits intocert-manager:masterfrom
maelvls:ocsp-unit-test
Mar 1, 2021
Merged

Add unit tests around the new ocspServers field#3699
jetstack-bot merged 4 commits intocert-manager:masterfrom
maelvls:ocsp-unit-test

Conversation

@maelvls
Copy link
Copy Markdown
Member

@maelvls maelvls commented Feb 23, 2021

In order to release cert-manager v1.2, we decided to leave some unit testing behind as detailed in #3636.

This PR adds a couple of unit tests:

  • When the Issuer has ocspServers set, it should appear on the signed certificate
  • When the Issuer has crlDistributionPoints set, it should appear on the signed certificate
  • When the CertificateRequest has the isCA field set, it should appear on the signed certificate CA
  • When the CertificateRequest has the duration field set, it should appear as notAfter on the signed ca
    → this one is failing, not sure why, I might have misunderstood the role of the cr.Spec.Duration?

Note to the reviewer:

  • I could not add these unit tests to the existing TestSign. TestSign does not allow for setting assertions on the signed certificate.
  • In these new test cases, I only test the call to Sign as opposed to the full controller flow, which means I did not have to rely on the fake clientsets (i.e., the testbuilder package)
  • There is one failing test, see comment below

Closes #3636

@jetstack-bot jetstack-bot added dco-signoff: yes Indicates that all commits in the pull request have the valid DCO sign-off message. do-not-merge/release-note-label-needed Indicates that a PR should not merge because it's missing one of the release note labels. needs-kind Indicates a PR lacks a `kind/foo` label and requires one. labels Feb 23, 2021
@maelvls maelvls marked this pull request as draft February 23, 2021 15:54
@jetstack-bot jetstack-bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Feb 23, 2021
@jetstack-bot jetstack-bot requested a review from jakexks February 23, 2021 15:54
@jetstack-bot jetstack-bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Feb 23, 2021
@maelvls maelvls force-pushed the ocsp-unit-test branch 3 times, most recently from 93eff53 to 3e37bd1 Compare February 23, 2021 19:31
))),
givenNamespace: "default",
assertSignedCert: func(t *testing.T, got *x509.Certificate) {
assert.Equal(t, time.Now().UTC().Add(30*time.Minute), got.NotAfter)
Copy link
Copy Markdown
Member Author

@maelvls maelvls Feb 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get a mismatch here, am I misunderstanding the role of cr.Spec.Duration? Why does the returned signed certificate have a 0 time on its NotAfter field?

% go test ./pkg/controller/certificaterequests/ca/ -run TestCA_Sign
I0223 20:32:06.072826   16843 ca.go:128] cert-manager "msg"="certificate issued"
I0223 20:32:06.075193   16843 ca.go:128] cert-manager "msg"="certificate issued"
I0223 20:32:06.077904   16843 ca.go:128] cert-manager "msg"="certificate issued"
I0223 20:32:06.080078   16843 ca.go:128] cert-manager "msg"="certificate issued"
--- FAIL: TestCA_Sign (0.38s)
    --- FAIL: TestCA_Sign/when_the_CertificateRequest_has_the_duration_field_set,_it_should_appear_as_notAfter_on_the_signed_ca (0.00s)
        ca_test.go:440:
            	Error Trace:	ca_test.go:440
            	            				ca_test.go:545
            	Error:      	Not equal:
            	            	expected: time.Time{wall:0x459e058, ext:63749707326, loc:(*time.Location)(nil)}
            	            	actual  : time.Time{wall:0x0, ext:63749707326, loc:(*time.Location)(nil)}

            	            	Diff:
            	            	--- Expected
            	            	+++ Actual
            	            	@@ -1,3 +1,3 @@
            	            	 (time.Time) {
            	            	- wall: (uint64) 72999000,
            	            	+ wall: (uint64) 0,
            	            	  ext: (int64) 63749707326,
            	Test:       	TestCA_Sign/when_the_CertificateRequest_has_the_duration_field_set,_it_should_appear_as_notAfter_on_the_signed_ca
FAIL
FAIL	github.com/jetstack/cert-manager/pkg/controller/certificaterequests/ca	0.411s
FAIL

Copy link
Copy Markdown
Contributor

@irbekrm irbekrm Feb 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is happening because time.Now() gives you a value to a higher precision than what ends up on the cert.

If I modify time.Now().UTC().Add(30*time.Minute) -> time.Now().UTC().Add(30*time.Minute).Round(time.Second *2) this test passes.

Not sure why the assertion shows it as 0.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oohh thank you! I added String() in order to see a human-readable time, we can see the difference:

expected: 2021-02-24 14:26:38.587263 +0000 UTC
actual:   2021-02-24 14:26:38 +0000 UTC

I'll use your tip to fix the test case!

Copy link
Copy Markdown
Member Author

@maelvls maelvls Feb 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used Truncate instead of Round, here is how I went:

// The notAfter field uses a precision of 1 second; the current time is
// truncated before adding the certificate request's duration value.
assert.Equal(t, time.Now().UTC().Truncate(1*time.Second).Add(30*time.Minute).String(), got.NotAfter.String())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes truncating is obviously a better approach than rounding 😄

String() makes it more readable, but maybe asserting the equality of the actual structs (rather than strings) is more important?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I'll do that then, and will show the .String() values in the error message instead

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense, I think

@maelvls maelvls marked this pull request as ready for review February 23, 2021 19:35
@jetstack-bot jetstack-bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Feb 23, 2021
Signed-off-by: Maël Valais <mael@vls.dev>
maelvls and others added 2 commits February 24, 2021 15:07
Signed-off-by: Maël Valais <mael@vls.dev>
Co-authored-by: Irbe Krumina <irbekrm@gmail.com>
Also removed the unused "givenNamespace"

Signed-off-by: Maël Valais <mael@vls.dev>
Co-authored-by: Irbe Krumina <irbekrm@gmail.com>
Copy link
Copy Markdown
Contributor

@irbekrm irbekrm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, I added a few comments. Happy to lgtm, if the 'return' is to stay 😄

gotIssueResp, gotErr := c.Sign(context.Background(), test.givenCR, test.givenCAIssuer)
if test.wantErr != "" {
assert.EqualError(t, gotErr, test.wantErr)
return
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: is there maybe a way how we can not return here and still run the other checks even when we expect an error (i.e by testing that gotCert is nil etc)?
I feel like it is a little confusing that we return in a test- but happy to be convinced otherwise.

Copy link
Copy Markdown
Member Author

@maelvls maelvls Mar 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also realize that returning here potentially "skips" below assertions that might have to be checked even if an error is returned...

Would it make sense to do the following instead:

if test.wantErr != "" {
	assert.EqualError(t, gotErr, test.wantErr)
} else {
	require.NoError(t, gotErr)

	require.NotNil(t, gotIssueResp)
	gotCert, err := pki.DecodeX509CertificateBytes(gotIssueResp.Certificate)
	require.NoError(t, err)
	test.assertSignedCert(t, gotCert)                
}

Copy link
Copy Markdown
Contributor

@irbekrm irbekrm Mar 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is clearer! I am always torn between whether I actually want to check error strings and whether I want to verify for example that gotIssuerResp is nil in case of an error.
I personally have a slight preference for not having else blocks and not comparing error strings only types if possible:

// given that `wantsErr` is boolean and  `gotErr` is error
if test.wantsErr != (test.gotErr != nil) {
	// unexpected presence error or the lack of it, fail
}
require.Equal(test.IssuerResp, gotIssuerResp) // check both that it's correct when there's no error and nil when there is error 
if gotIssuerResp != nil {
	gotCert, err := pki.DecodeX509CertificateBytes(gotIssueResp.Certificate)
	require.NoError(t, err)
	test.assertSignedCert(t, gotCert)
}          

But I don't think this ^ is better, just different, so your fix looks good to me.

Signed-off-by: Maël Valais <mael@vls.dev>
Co-authored-by: Irbe Krumina <irbekrm@gmail.com>
@irbekrm
Copy link
Copy Markdown
Contributor

irbekrm commented Mar 1, 2021

/lgtm

@jetstack-bot jetstack-bot added the lgtm Indicates that a PR is ready to be merged. label Mar 1, 2021
@irbekrm
Copy link
Copy Markdown
Contributor

irbekrm commented Mar 1, 2021

I think it still wants a release note.

@maelvls
Copy link
Copy Markdown
Member Author

maelvls commented Mar 1, 2021

tide Pending — Not mergeable. Needs approved label.

Not sure but I think that Tide will also expect a "Github PR approval"; by default it does not require one, and as soon as one person gives a review then Tide waits until that person gives a Github approval:

a-github-review-was-given

Try giving a Github PR approval just to see?

@irbekrm
Copy link
Copy Markdown
Contributor

irbekrm commented Mar 1, 2021

/approve

@irbekrm irbekrm self-requested a review March 1, 2021 16:51
@irbekrm
Copy link
Copy Markdown
Contributor

irbekrm commented Mar 1, 2021

please approve 😆

@irbekrm
Copy link
Copy Markdown
Contributor

irbekrm commented Mar 1, 2021

Perhaps I need to assign myself before approving

/assign @irbekrm

@irbekrm
Copy link
Copy Markdown
Contributor

irbekrm commented Mar 1, 2021

/approve

@irbekrm
Copy link
Copy Markdown
Contributor

irbekrm commented Mar 1, 2021

Ohh, I am not in the OWNERS file https://github.com/jetstack/cert-manager/blob/master/OWNERS

@irbekrm
Copy link
Copy Markdown
Contributor

irbekrm commented Mar 1, 2021

soo.. 🥁 ...

/approve

@jetstack-bot
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: irbekrm, maelvls

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@jetstack-bot jetstack-bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Mar 1, 2021
@irbekrm irbekrm added the kind/cleanup Categorizes issue or PR as related to cleaning up code, process, or technical debt. label Mar 1, 2021
@jetstack-bot jetstack-bot removed the needs-kind Indicates a PR lacks a `kind/foo` label and requires one. label Mar 1, 2021
@irbekrm irbekrm added the release-note-none Denotes a PR that doesn't merit a release note. label Mar 1, 2021
@jetstack-bot jetstack-bot removed the do-not-merge/release-note-label-needed Indicates that a PR should not merge because it's missing one of the release note labels. label Mar 1, 2021
@jetstack-bot jetstack-bot merged commit a9c672e into cert-manager:master Mar 1, 2021
@jetstack-bot jetstack-bot added this to the v1.3 milestone Mar 1, 2021
Comment on lines +439 to +440
expectNotAfter := time.Now().UTC().Truncate(1 * time.Second).Add(30 * time.Minute)
assert.Equalf(t, expectNotAfter, got.NotAfter, "time mismatch, expect='%s', got='%s'", expectNotAfter.String(), got.NotAfter.String())
Copy link
Copy Markdown
Contributor

@jsoref jsoref Mar 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently this requires some additional tolerance as it was reported off by 1:
#3475 (comment)

Copy link
Copy Markdown
Member Author

@maelvls maelvls Mar 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I might want to check that got.NotAfter is within the range

[expectNotAfter - 2 time.Second, expectNotAfter + 2 * time.Second]

or something like that 😅

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the long term, if the API is malleable, maybe you change:
https://github.com/jetstack/cert-manager/blob/923eec2d1bce1e2bc865fb7c5557c38ad0bd045f/pkg/util/pki/csr.go#L320-L321 so that you can provide a time provider instead of being forced to live in real time.

n.b. even if you don't do that, someone should fix it so that time.Now() is only called once. It's introducing drift between the two function calls which is arguably wrong.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. dco-signoff: yes Indicates that all commits in the pull request have the valid DCO sign-off message. kind/cleanup Categorizes issue or PR as related to cleaning up code, process, or technical debt. lgtm Indicates that a PR is ready to be merged. release-note-none Denotes a PR that doesn't merit a release note. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a unit test around the new ocspServers and crlDistributionPoints

4 participants