Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: Use Pebble as the ACME server in integration tests #339

Merged
merged 17 commits into from
Nov 8, 2024

Conversation

marc1404
Copy link
Member

@marc1404 marc1404 commented Nov 7, 2024

How to categorize this PR?

/kind test
/kind enhancement

What this PR does / why we need it:

The integration test skeleton added with #315 currently uses the Let's Encrypt ACME staging service.
While it can be safely assumed that this service should be relatively stable it means that the integration tests are making calls to the internet and effectively depend on an external service. Furthermore, we cannot skip the DNS-01 challenge in integration tests as the staging service would enforce passed challenges.

Pebble is a small ACME server intended to be run for testing purposes. It's already used in the functional E2E tests. This PR invokes the code to start the Pebble ACME server locally during the integration test suite startup.

Which issue(s) this PR fixes:

n.a.

Special notes for your reviewer:

/cc @MartinWeindel

Thanks to @MartinWeindel for the great idea! 💡
Additional thanks to @LucaBernstein for debugging and fixing the TLS setup. 🔐

Release note:

Use Pebble as an ACME server in the integration tests.

@gardener-prow gardener-prow bot added kind/test Test kind/enhancement Enhancement, improvement, extension cla: yes Indicates the PR's author has signed the cla-assistant.io CLA. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Nov 7, 2024
@gardener-robot-ci-1 gardener-robot-ci-1 added the reviewed/ok-to-test Has approval for testing (check PR in detail before setting this label because PR is run on CI/CD) label Nov 7, 2024
@gardener-robot-ci-3 gardener-robot-ci-3 added needs/ok-to-test Needs approval for testing (check PR in detail before setting this label because PR is run on CI/CD) and removed reviewed/ok-to-test Has approval for testing (check PR in detail before setting this label because PR is run on CI/CD) labels Nov 7, 2024
restConfig *rest.Config
testEnv *envtest.Environment
testClient client.Client
acmeDirectoryAddress string
Copy link
Member Author

Choose a reason for hiding this comment

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

acmeDirectoryAddress has been added. This variable holds the directory address of the local Pebble ACME server that will be used to create Issuer resources in tests.
The test asserts that this variable has been set before the test starts.

By("Start Pebble ACME server")
certificatePath, directoryAddress, err := testutils.RunPebble(log.WithName("pebble"))
Expect(err).NotTo(HaveOccurred())
acmeDirectoryAddress = directoryAddress
Copy link
Member Author

Choose a reason for hiding this comment

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

Sets the package scoped variable that tests will use to create Issuer resources.

@@ -86,6 +100,7 @@ var _ = BeforeSuite(func() {
DeferCleanup(func() {
By("Stop test environment")
Expect(testEnv.Stop()).To(Succeed())
_ = os.RemoveAll(filepath.Dir(certificatePath))
Copy link
Member Author

@marc1404 marc1404 Nov 7, 2024

Choose a reason for hiding this comment

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

The code responsible for starting the Pebble server generates a certificate and private key on the fly in a temporary OS directory.
Instead of deleting the two files and their directory individually, we can clear them in one sweep.

@@ -28,6 +28,8 @@ var _ = Describe("Issuer controller tests", func() {
)

BeforeEach(func() {
Expect(acmeDirectoryAddress).NotTo(BeEmpty())
Copy link
Member Author

Choose a reason for hiding this comment

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

During development, I messed up initializing the package level variable in the test suite. Tests should assert early on that the variable has been set.

Comment on lines 1 to 26
package testutils

import (
"github.com/go-logr/logr"
"log"
"strings"
)

type logBridge struct {
logr logr.Logger
}

func (logBridge *logBridge) Write(p []byte) (n int, err error) {
message := strings.TrimSpace(string(p))

logBridge.logr.Info(message)

return len(p), nil
}

// NewLogBridge creates a new log.Logger that forwards all log messages to the given logr.Logger.
func NewLogBridge(logr logr.Logger) *log.Logger {
writer := &logBridge{logr}

return log.New(writer, "", 0)
}
Copy link
Member Author

@marc1404 marc1404 Nov 7, 2024

Choose a reason for hiding this comment

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

Pebble's code expects a logger from the log package. (ref)
This utility creates a bridge that returns a log.Logger but redirects messages to the logr.Logger used by the test suite.

Comment on lines 39 to 46
// We don't want to go through DNS-01 challenges in the integration tests as we would have to spin up a local, authoritative DNS server.
// Setting the environment variable PEBBLE_VA_ALWAYS_VALID to 1 makes the Pebble server always return a valid response for the validation authority.
// Testing the DNS-01 challenge is covered by the functional E2E tests.
// See the Pebble documentation: https://github.com/letsencrypt/pebble#skipping-validation
err = os.Setenv("PEBBLE_VA_ALWAYS_VALID", "1")
if err != nil {
return "", "", fmt.Errorf("failed to set environment variable: %v", err)
}
Copy link
Member Author

Choose a reason for hiding this comment

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

This could also be done in the test suite but as we never want to pass DNS-01 challenges in the integration tests I figured the responsibility could be kept in the Pebble utility code.

Comment on lines 69 to 74
err := http.ListenAndServeTLS(
listenAddress,
certificatePath,
privateKeyPath,
muxHandler)
cmd.FailOnError(err, "Calling ListenAndServeTLS()")
Copy link
Member Author

Choose a reason for hiding this comment

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

http.ListenAndServeTLS is a blocking function that only returns in case of an error (e.g. address already in use).

cmd.FailOnError is implemented by Pebble and effectively exits the process with the error.
It's useful as it will exit the integration test suite early with a descriptive error message in case the Pebble server can't be started.

Comment on lines 82 to 90
rootCAs, err := loadCertPool(certificatePath)
if err != nil {
return err
}

customTransport := http.DefaultTransport.(*http.Transport).Clone()
customTransport.TLSClientConfig = &tls.Config{RootCAs: rootCAs}
http.DefaultTransport = customTransport
client := &http.Client{Transport: customTransport}
Copy link
Member Author

Choose a reason for hiding this comment

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

The ACME protocol enforces TLS and the utility code generates a temporary certificate on the fly.
We need to trust the certificate when performing the availability check. Otherwise, we'd have to skip the insecure validation.

Copy link
Member

@MartinWeindel MartinWeindel left a comment

Choose a reason for hiding this comment

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

I took a quick look to the gosec issues (run make sast to list them) and provide easy solutions for them.

test/utils/pebble.go Outdated Show resolved Hide resolved
test/utils/pebble.go Outdated Show resolved Hide resolved
test/utils/pebble.go Outdated Show resolved Hide resolved
test/utils/pebble.go Outdated Show resolved Hide resolved
@gardener-robot-ci-3 gardener-robot-ci-3 added the reviewed/ok-to-test Has approval for testing (check PR in detail before setting this label because PR is run on CI/CD) label Nov 8, 2024
@gardener-robot-ci-1 gardener-robot-ci-1 removed the reviewed/ok-to-test Has approval for testing (check PR in detail before setting this label because PR is run on CI/CD) label Nov 8, 2024
@gardener gardener deleted a comment from gardener-prow bot Nov 8, 2024
@gardener-robot-ci-1 gardener-robot-ci-1 added reviewed/ok-to-test Has approval for testing (check PR in detail before setting this label because PR is run on CI/CD) and removed reviewed/ok-to-test Has approval for testing (check PR in detail before setting this label because PR is run on CI/CD) labels Nov 8, 2024
Copy link
Member

@MartinWeindel MartinWeindel left a comment

Choose a reason for hiding this comment

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

/lgtm

I've fixed a minor issuer on the fly. The pebble HTTP server was not closed in the cleanup.

Great to have an ACME server in the integration tests. Thanks!

@gardener-prow gardener-prow bot added the lgtm Indicates that a PR is ready to be merged. label Nov 8, 2024
Copy link

gardener-prow bot commented Nov 8, 2024

LGTM label has been added.

Git tree hash: 790764bc527482a3867e684fea0cc63c6520a8d0

Copy link

gardener-prow bot commented Nov 8, 2024

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: MartinWeindel

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

The pull request process is described here

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

@gardener-prow gardener-prow bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Nov 8, 2024
@gardener-prow gardener-prow bot merged commit aa2ad79 into master Nov 8, 2024
11 checks passed
@gardener-robot-ci-1 gardener-robot-ci-1 added the reviewed/ok-to-test Has approval for testing (check PR in detail before setting this label because PR is run on CI/CD) label Nov 8, 2024
@marc1404
Copy link
Member Author

marc1404 commented Nov 8, 2024

I've fixed a minor issuer on the fly. The pebble HTTP server was not closed in the cleanup.

@MartinWeindel Great catch! My bad, thank you! 🙇

@marc1404 marc1404 deleted the test/use-pebble-acme-server-in-integration-test branch November 8, 2024 12:07
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. cla: yes Indicates the PR's author has signed the cla-assistant.io CLA. kind/enhancement Enhancement, improvement, extension kind/test Test lgtm Indicates that a PR is ready to be merged. needs/ok-to-test Needs approval for testing (check PR in detail before setting this label because PR is run on CI/CD) reviewed/ok-to-test Has approval for testing (check PR in detail before setting this label because PR is run on CI/CD) 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.

4 participants