-
Notifications
You must be signed in to change notification settings - Fork 23
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
test: Use Pebble as the ACME server in integration tests #339
Conversation
restConfig *rest.Config | ||
testEnv *envtest.Environment | ||
testClient client.Client | ||
acmeDirectoryAddress string |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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)) |
There was a problem hiding this comment.
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()) |
There was a problem hiding this comment.
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.
test/utils/logbridge.go
Outdated
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) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test/utils/pebble.go
Outdated
// 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) | ||
} |
There was a problem hiding this comment.
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.
test/utils/pebble.go
Outdated
err := http.ListenAndServeTLS( | ||
listenAddress, | ||
certificatePath, | ||
privateKeyPath, | ||
muxHandler) | ||
cmd.FailOnError(err, "Calling ListenAndServeTLS()") |
There was a problem hiding this comment.
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.
test/utils/pebble.go
Outdated
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} |
There was a problem hiding this comment.
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.
There was a problem hiding this 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.
There was a problem hiding this 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!
LGTM label has been added. Git tree hash: 790764bc527482a3867e684fea0cc63c6520a8d0
|
[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 |
@MartinWeindel Great catch! My bad, thank you! 🙇 |
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: