mirror of https://github.com/linkerd/linkerd2.git
159 lines
4.7 KiB
Go
159 lines
4.7 KiB
Go
package externalissuer
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/linkerd/linkerd2/pkg/k8s"
|
|
"github.com/linkerd/linkerd2/testutil"
|
|
corev1 "k8s.io/api/core/v1"
|
|
)
|
|
|
|
var TestHelper *testutil.TestHelper
|
|
|
|
const (
|
|
TestAppBackendDeploymentName = "backend"
|
|
TestAppNamespaceSuffix = "external-issuer-app-test"
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
TestHelper = testutil.NewTestHelper()
|
|
if !TestHelper.ExternalIssuer() {
|
|
fmt.Fprintln(os.Stdout, "Skiping as --external-issuer=false")
|
|
os.Exit(0)
|
|
}
|
|
os.Exit(m.Run())
|
|
}
|
|
|
|
func TestExternalIssuer(t *testing.T) {
|
|
verifyInstallApp(t)
|
|
verifyAppWorksBeforeCertRotation(t)
|
|
verifyRotateExternalCerts(t)
|
|
verifyIdentityServiceReloadsIssuerCert(t)
|
|
ensureNewCSRSAreServed()
|
|
verifyAppWorksAfterCertRotation(t)
|
|
}
|
|
|
|
func verifyInstallApp(t *testing.T) {
|
|
out, stderr, err := TestHelper.LinkerdRun("inject", "--manual", "testdata/external_issuer_application.yaml")
|
|
if err != nil {
|
|
t.Fatalf("linkerd inject command failed\n%s\n%s", out, stderr)
|
|
}
|
|
|
|
prefixedNs := TestHelper.GetTestNamespace(TestAppNamespaceSuffix)
|
|
err = TestHelper.CreateDataPlaneNamespaceIfNotExists(prefixedNs, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create %s namespace: %s", prefixedNs, err)
|
|
}
|
|
out, err = TestHelper.KubectlApply(out, prefixedNs)
|
|
if err != nil {
|
|
t.Fatalf("kubectl apply command failed\n%s", out)
|
|
}
|
|
|
|
if err := TestHelper.CheckPods(prefixedNs, TestAppBackendDeploymentName, 1); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if err := TestHelper.CheckPods(prefixedNs, "slow-cooker", 1); err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func checkAppWoks(t *testing.T) error {
|
|
return TestHelper.RetryFor(40*time.Second, func() error {
|
|
args := []string{"stat", "deploy", "-n", TestHelper.GetTestNamespace(TestAppNamespaceSuffix), "--from", "deploy/slow-cooker", "-t", "1m"}
|
|
out, stderr, err := TestHelper.LinkerdRun(args...)
|
|
if err != nil {
|
|
return fmt.Errorf("Unexpected stat error: %s\n%s\n%s", err, out, stderr)
|
|
}
|
|
rowStats, err := testutil.ParseRows(out, 1, 8)
|
|
if err != nil {
|
|
return fmt.Errorf("%s\n%s", err, stderr)
|
|
}
|
|
|
|
stat := rowStats[TestAppBackendDeploymentName]
|
|
if stat.Success != "100.00%" {
|
|
t.Fatalf("Expected no errors in test app but got [%s] succes rate", stat.Success)
|
|
}
|
|
return nil
|
|
})
|
|
|
|
}
|
|
|
|
func verifyAppWorksBeforeCertRotation(t *testing.T) {
|
|
err := checkAppWoks(t)
|
|
if err != nil {
|
|
t.Fatalf("Received error while ensuring test app works (before cert rotation): %s", err)
|
|
}
|
|
}
|
|
|
|
func verifyRotateExternalCerts(t *testing.T) {
|
|
// We rotate the certificates here by simply grabbing
|
|
// the key and cert values from the temporary secret we have
|
|
// created
|
|
secretWithUpdatedData, err := TestHelper.KubernetesHelper.GetSecret(TestHelper.GetLinkerdNamespace(), k8s.IdentityIssuerSecretName+"-new")
|
|
if err != nil {
|
|
t.Fatalf("failed to fetch new secret data resource: %s", err)
|
|
}
|
|
|
|
roots := secretWithUpdatedData.Data[k8s.IdentityIssuerTrustAnchorsNameExternal]
|
|
crt := secretWithUpdatedData.Data[corev1.TLSCertKey]
|
|
key := secretWithUpdatedData.Data[corev1.TLSPrivateKeyKey]
|
|
|
|
if err = TestHelper.CreateTLSSecret(k8s.IdentityIssuerSecretName, string(roots), string(crt), string(key)); err != nil {
|
|
t.Fatalf("failed to update linkerd-identity-issuer resource: %s", err)
|
|
}
|
|
}
|
|
|
|
func verifyIdentityServiceReloadsIssuerCert(t *testing.T) {
|
|
// check that the identity service has received an IssuerUpdated event
|
|
err := TestHelper.RetryFor(90*time.Second, func() error {
|
|
out, err := TestHelper.Kubectl("",
|
|
"--namespace", TestHelper.GetLinkerdNamespace(),
|
|
"get", "events", "--field-selector", "reason=IssuerUpdated", "-ojson",
|
|
)
|
|
if err != nil {
|
|
t.Errorf("kubectl get events command failed with %s\n%s", err, out)
|
|
}
|
|
|
|
events, err := testutil.ParseEvents(out)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(events) != 1 {
|
|
return fmt.Errorf("expected just one event but got %d", len(events))
|
|
}
|
|
|
|
expectedEventMessage := "Updated identity issuer"
|
|
issuerUpdatedEvent := events[0]
|
|
|
|
if issuerUpdatedEvent.Message != expectedEventMessage {
|
|
return fmt.Errorf("expected event message [%s] but got [%s]", expectedEventMessage, issuerUpdatedEvent.Message)
|
|
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err.Error())
|
|
}
|
|
|
|
}
|
|
|
|
func ensureNewCSRSAreServed() {
|
|
// this is to ensure new certs have been issued by the identity service.
|
|
// we know that this will happen because the issuance lifetime is set to 15s.
|
|
// Possible improvement is to provide a more deterministic way of checking that.
|
|
// Perhaps we can emit k8s events when the identity service processed a CSR.
|
|
time.Sleep(20 * time.Second)
|
|
}
|
|
|
|
func verifyAppWorksAfterCertRotation(t *testing.T) {
|
|
err := checkAppWoks(t)
|
|
if err != nil {
|
|
t.Fatalf("Received error while ensuring test app works (after cert rotation): %s", err)
|
|
}
|
|
}
|