1 package externalissuer
2
3 import (
4 "context"
5 "fmt"
6 "os"
7 "testing"
8 "time"
9
10 "github.com/linkerd/linkerd2/pkg/k8s"
11 "github.com/linkerd/linkerd2/testutil"
12 corev1 "k8s.io/api/core/v1"
13 )
14
15 var TestHelper *testutil.TestHelper
16
17 const (
18 TestAppBackendDeploymentName = "backend"
19 TestAppNamespaceSuffix = "external-issuer-app-test"
20 )
21
22 func TestMain(m *testing.M) {
23 TestHelper = testutil.NewTestHelper()
24
25 TestHelper.WaitUntilDeployReady(testutil.ExternalVizDeployReplicas)
26 os.Exit(m.Run())
27 }
28
29 func TestExternalIssuer(t *testing.T) {
30 ctx := context.Background()
31 TestHelper.WithDataPlaneNamespace(ctx, TestAppNamespaceSuffix, map[string]string{}, t, func(t *testing.T, testNamespace string) {
32 verifyInstallApp(ctx, t)
33 verifyAppWorksBeforeCertRotation(t)
34 verifyRotateExternalCerts(ctx, t)
35 verifyIdentityServiceReloadsIssuerCert(t)
36 ensureNewCSRSAreServed()
37 verifyAppWorksAfterCertRotation(t)
38 })
39 }
40
41 func verifyInstallApp(ctx context.Context, t *testing.T) {
42 out, err := TestHelper.LinkerdRun("inject", "--manual", "testdata/external_issuer_application.yaml")
43 if err != nil {
44 testutil.AnnotatedFatal(t, "'linkerd inject' command failed", err)
45 }
46
47 prefixedNs := TestHelper.GetTestNamespace(TestAppNamespaceSuffix)
48 out, err = TestHelper.KubectlApply(out, prefixedNs)
49 if err != nil {
50 testutil.AnnotatedFatalf(t, "'kubectl apply' command failed", "'kubectl apply' command failed\n%s", out)
51 }
52
53 if err := TestHelper.CheckPods(ctx, prefixedNs, TestAppBackendDeploymentName, 1); err != nil {
54
55 if rce, ok := err.(*testutil.RestartCountError); ok {
56 testutil.AnnotatedWarn(t, "CheckPods timed-out", rce)
57 } else {
58 testutil.AnnotatedError(t, "CheckPods timed-out", err)
59 }
60 }
61
62 if err := TestHelper.CheckPods(ctx, prefixedNs, "slow-cooker", 1); err != nil {
63
64 if rce, ok := err.(*testutil.RestartCountError); ok {
65 testutil.AnnotatedWarn(t, "CheckPods timed-out", rce)
66 } else {
67 testutil.AnnotatedError(t, "CheckPods timed-out", err)
68 }
69 }
70 }
71
72 func checkAppWoks(t *testing.T, timeout time.Duration) error {
73 return testutil.RetryFor(timeout, func() error {
74 args := []string{"viz", "stat", "deploy", "-n", TestHelper.GetTestNamespace(TestAppNamespaceSuffix), "--from", "deploy/slow-cooker", "-t", "1m"}
75 out, err := TestHelper.LinkerdRun(args...)
76 if err != nil {
77 return err
78 }
79 rowStats, err := testutil.ParseRows(out, 1, 8)
80 if err != nil {
81 return err
82 }
83
84 stat := rowStats[TestAppBackendDeploymentName]
85 if stat.Success != "100.00%" {
86 t.Fatalf("Expected no errors in test app but got [%s] success rate", stat.Success)
87 }
88 return nil
89 })
90
91 }
92
93 func verifyAppWorksBeforeCertRotation(t *testing.T) {
94 timeout := 40 * time.Second
95 err := checkAppWoks(t, timeout)
96 if err != nil {
97 testutil.AnnotatedFatal(t, fmt.Sprintf("timed-out while ensuring test app works (before cert rotation) (%s)", timeout), err)
98 }
99 }
100
101 func verifyRotateExternalCerts(ctx context.Context, t *testing.T) {
102
103
104
105 secretWithUpdatedData, err := TestHelper.KubernetesHelper.GetSecret(ctx, TestHelper.GetLinkerdNamespace(), k8s.IdentityIssuerSecretName+"-new")
106 if err != nil {
107 testutil.AnnotatedFatalf(t, "failed to fetch new secret data resource", "failed to fetch new secret data resource: %s", err)
108 }
109
110 roots := secretWithUpdatedData.Data[k8s.IdentityIssuerTrustAnchorsNameExternal]
111 crt := secretWithUpdatedData.Data[corev1.TLSCertKey]
112 key := secretWithUpdatedData.Data[corev1.TLSPrivateKeyKey]
113
114 if err = TestHelper.CreateTLSSecret(k8s.IdentityIssuerSecretName, string(roots), string(crt), string(key)); err != nil {
115 testutil.AnnotatedFatalf(t, "failed to update linkerd-identity-issuer resource", "failed to update linkerd-identity-issuer resource: %s", err)
116 }
117 }
118
119 func verifyIdentityServiceReloadsIssuerCert(t *testing.T) {
120
121 timeout := 90 * time.Second
122 err := testutil.RetryFor(timeout, func() error {
123 out, err := TestHelper.Kubectl("",
124 "--namespace", TestHelper.GetLinkerdNamespace(),
125 "get", "events", "--field-selector", "reason=IssuerUpdated", "-ojson",
126 )
127 if err != nil {
128 testutil.AnnotatedErrorf(t, "'kubectl get events' command failed", "'kubectl get events' command failed with %s\n%s", err, out)
129 }
130
131 events, err := testutil.ParseEvents(out)
132 if err != nil {
133 return err
134 }
135
136 if len(events) != 1 {
137 return fmt.Errorf("expected just one event but got %d", len(events))
138 }
139
140 expectedEventMessage := "Updated identity issuer"
141 issuerUpdatedEvent := events[0]
142
143 if issuerUpdatedEvent.Message != expectedEventMessage {
144 return fmt.Errorf("expected event message [%s] but got [%s]", expectedEventMessage, issuerUpdatedEvent.Message)
145
146 }
147 return nil
148 })
149 if err != nil {
150 testutil.AnnotatedFatal(t, fmt.Sprintf("timed-out verifying identity svc reloads issuer cert (%s)", timeout), err)
151 }
152
153 }
154
155 func ensureNewCSRSAreServed() {
156
157
158
159
160 time.Sleep(20 * time.Second)
161 }
162
163 func verifyAppWorksAfterCertRotation(t *testing.T) {
164 timeout := 40 * time.Second
165 err := checkAppWoks(t, timeout)
166 if err != nil {
167 testutil.AnnotatedFatal(t, fmt.Sprintf("timed-out ensuring test app works (after cert rotation) (%s)", timeout), err)
168 }
169 }
170
View as plain text