boulder/test/integration/akamai_purger_drain_queue_t...

135 lines
4.1 KiB
Go

//go:build integration
package integration
import (
"bytes"
"context"
"fmt"
"os"
"os/exec"
"syscall"
"testing"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/balancer/roundrobin"
"google.golang.org/grpc/connectivity"
akamaipb "github.com/letsencrypt/boulder/akamai/proto"
"github.com/letsencrypt/boulder/cmd"
bcreds "github.com/letsencrypt/boulder/grpc/creds"
"github.com/letsencrypt/boulder/metrics"
"github.com/letsencrypt/boulder/test"
)
func setup() (*exec.Cmd, *bytes.Buffer, akamaipb.AkamaiPurgerClient, error) {
purgerCmd := exec.Command("./bin/boulder", "akamai-purger", "--config", "test/integration/testdata/akamai-purger-queue-drain-config.json")
var outputBuffer bytes.Buffer
purgerCmd.Stdout = &outputBuffer
purgerCmd.Stderr = &outputBuffer
purgerCmd.Start()
// If we error, we need to kill the process we started or the test command
// will never exit.
sigterm := func() {
purgerCmd.Process.Signal(syscall.SIGTERM)
purgerCmd.Wait()
}
tlsConfig, err := (&cmd.TLSConfig{
CACertFile: "test/certs/ipki/minica.pem",
CertFile: "test/certs/ipki/ra.boulder/cert.pem",
KeyFile: "test/certs/ipki/ra.boulder/key.pem",
}).Load(metrics.NoopRegisterer)
if err != nil {
sigterm()
return nil, nil, nil, err
}
creds := bcreds.NewClientCredentials(tlsConfig.RootCAs, tlsConfig.Certificates, "akamai-purger.boulder")
conn, err := grpc.Dial(
"dns:///akamai-purger.service.consul:9199",
grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"loadBalancingConfig": [{"%s":{}}]}`, roundrobin.Name)),
grpc.WithTransportCredentials(creds),
)
if err != nil {
sigterm()
return nil, nil, nil, err
}
for i := range 42 {
if conn.GetState() == connectivity.Ready {
break
}
if i > 40 {
sigterm()
return nil, nil, nil, fmt.Errorf("timed out waiting for akamai-purger to come up: %s", outputBuffer.String())
}
time.Sleep(50 * time.Millisecond)
}
purgerClient := akamaipb.NewAkamaiPurgerClient(conn)
return purgerCmd, &outputBuffer, purgerClient, nil
}
func TestAkamaiPurgerDrainQueueFails(t *testing.T) {
purgerCmd, outputBuffer, purgerClient, err := setup()
if err != nil {
t.Fatal(err)
}
// We know that the purger is configured to only process two items per batch,
// so submitting 10 items should give it enough of a backlog to guarantee
// that our SIGTERM reaches the process before it's fully cleared the queue.
for i := range 10 {
_, err = purgerClient.Purge(context.Background(), &akamaipb.PurgeRequest{
Urls: []string{fmt.Sprintf("http://example%d.com/", i)},
})
if err != nil {
// Don't use t.Fatal here because we need to get as far as the SIGTERM or
// we'll hang on exit.
t.Error(err)
}
}
purgerCmd.Process.Signal(syscall.SIGTERM)
err = purgerCmd.Wait()
if err == nil {
t.Error("expected error shutting down akamai-purger that could not reach backend")
}
// Use two asserts because we're not sure what integer (10? 8?) will come in
// the middle of the error message.
test.AssertContains(t, outputBuffer.String(), "failed to purge OCSP responses for")
test.AssertContains(t, outputBuffer.String(), "certificates before exit: all attempts to submit purge request failed")
}
func TestAkamaiPurgerDrainQueueSucceeds(t *testing.T) {
purgerCmd, outputBuffer, purgerClient, err := setup()
if err != nil {
t.Fatal(err)
}
for range 10 {
_, err := purgerClient.Purge(context.Background(), &akamaipb.PurgeRequest{
Urls: []string{"http://example.com/"},
})
if err != nil {
t.Error(err)
}
}
time.Sleep(200 * time.Millisecond)
purgerCmd.Process.Signal(syscall.SIGTERM)
akamaiTestSrvCmd := exec.Command("./bin/akamai-test-srv", "--listen", "localhost:6889",
"--secret", "its-a-secret")
akamaiTestSrvCmd.Stdout = os.Stdout
akamaiTestSrvCmd.Stderr = os.Stderr
akamaiTestSrvCmd.Start()
err = purgerCmd.Wait()
if err != nil {
t.Errorf("unexpected error shutting down akamai-purger: %s. Output was:\n%s", err, outputBuffer.String())
}
test.AssertContains(t, outputBuffer.String(), "Shutting down; finished purging OCSP responses")
akamaiTestSrvCmd.Process.Signal(syscall.SIGTERM)
_ = akamaiTestSrvCmd.Wait()
}