Skip to content

Commit 26c08dd

Browse files
committed
Wait for updated keys to be observed
1 parent bf70d28 commit 26c08dd

File tree

1 file changed

+58
-25
lines changed

1 file changed

+58
-25
lines changed

test/integration/serviceaccount/external_jwt_signer_test.go

+58-25
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,19 @@ import (
2727
"testing"
2828
"time"
2929

30-
"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
31-
"k8s.io/kubernetes/test/integration/framework"
32-
"k8s.io/kubernetes/test/utils/ktesting"
33-
3430
authv1 "k8s.io/api/authentication/v1"
3531
corev1 "k8s.io/api/core/v1"
3632
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33+
"k8s.io/apimachinery/pkg/util/wait"
3734
utilfeature "k8s.io/apiserver/pkg/util/feature"
35+
"k8s.io/client-go/kubernetes"
3836
featuregatetesting "k8s.io/component-base/featuregate/testing"
37+
"k8s.io/component-base/metrics/testutil"
38+
"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
3939
"k8s.io/kubernetes/pkg/features"
4040
v1alpha1testing "k8s.io/kubernetes/pkg/serviceaccount/externaljwt/plugin/testing/v1alpha1"
41+
"k8s.io/kubernetes/test/integration/framework"
42+
"k8s.io/kubernetes/test/utils/ktesting"
4143
)
4244

4345
func TestExternalJWTSigningAndAuth(t *testing.T) {
@@ -94,29 +96,29 @@ func TestExternalJWTSigningAndAuth(t *testing.T) {
9496

9597
testCases := []struct {
9698
desc string
97-
preTestSignerUpdate func()
98-
preValidationSignerUpdate func()
99+
preTestSignerUpdate func(t *testing.T)
100+
preValidationSignerUpdate func(t *testing.T)
99101
wantTokenReqErr error
100102
shouldPassAuth bool
101103
}{
102104
{
103105
desc: "signing key supported.",
104-
preTestSignerUpdate: func() { /*no-op*/ },
105-
preValidationSignerUpdate: func() { /*no-op*/ },
106+
preTestSignerUpdate: func(_ *testing.T) { /*no-op*/ },
107+
preValidationSignerUpdate: func(_ *testing.T) { /*no-op*/ },
106108
shouldPassAuth: true,
107109
},
108110
{
109111
desc: "signing key not among supported set",
110-
preTestSignerUpdate: func() {
112+
preTestSignerUpdate: func(t *testing.T) {
111113
mockSigner.SigningKey = key1
112114
mockSigner.SigningKeyID = "updated-kid-1"
113115
},
114-
preValidationSignerUpdate: func() { /*no-op*/ },
116+
preValidationSignerUpdate: func(_ *testing.T) { /*no-op*/ },
115117
shouldPassAuth: false,
116118
},
117119
{
118120
desc: "signing key corresponds to public key that is excluded from OIDC",
119-
preTestSignerUpdate: func() {
121+
preTestSignerUpdate: func(t *testing.T) {
120122
mockSigner.SigningKey = key1
121123
mockSigner.SigningKeyID = "updated-kid-1"
122124

@@ -130,56 +132,57 @@ func TestExternalJWTSigningAndAuth(t *testing.T) {
130132
}
131133
mockSigner.SetSupportedKeys(cpy)
132134
},
133-
preValidationSignerUpdate: func() { /*no-op*/ },
135+
preValidationSignerUpdate: func(_ *testing.T) { /*no-op*/ },
134136
wantTokenReqErr: fmt.Errorf("failed to generate token: while validating header: key used for signing JWT (kid: updated-kid-1) is excluded from OIDC discovery docs"),
135137
},
136138
{
137139
desc: "different signing and supported keys with same id",
138-
preTestSignerUpdate: func() {
140+
preTestSignerUpdate: func(t *testing.T) {
139141
mockSigner.SigningKey = key1
140142
},
141-
preValidationSignerUpdate: func() { /*no-op*/ },
143+
preValidationSignerUpdate: func(_ *testing.T) { /*no-op*/ },
142144
shouldPassAuth: false,
143145
},
144146
{
145147
desc: "token gen failure with un-supported Alg type",
146-
preTestSignerUpdate: func() {
148+
preTestSignerUpdate: func(t *testing.T) {
147149
mockSigner.SigningAlg = "ABC"
148150
},
149-
preValidationSignerUpdate: func() { /*no-op*/ },
151+
preValidationSignerUpdate: func(_ *testing.T) { /*no-op*/ },
150152
wantTokenReqErr: fmt.Errorf("failed to generate token: while validating header: bad signing algorithm \"ABC\""),
151153
},
152154
{
153155
desc: "token gen failure with un-supported token type",
154-
preTestSignerUpdate: func() {
156+
preTestSignerUpdate: func(t *testing.T) {
155157
mockSigner.TokenType = "ABC"
156158
},
157-
preValidationSignerUpdate: func() { /*no-op*/ },
159+
preValidationSignerUpdate: func(_ *testing.T) { /*no-op*/ },
158160
wantTokenReqErr: fmt.Errorf("failed to generate token: while validating header: bad type"),
159161
},
160162
{
161163
desc: "change of supported keys not picked immediately",
162-
preTestSignerUpdate: func() {
164+
preTestSignerUpdate: func(t *testing.T) {
163165
mockSigner.SigningKey = key1
164166
},
165-
preValidationSignerUpdate: func() {
167+
preValidationSignerUpdate: func(_ *testing.T) {
166168
mockSigner.SetSupportedKeys(map[string]v1alpha1testing.KeyT{})
167169
},
168170
shouldPassAuth: false,
169171
},
170172
{
171173
desc: "change of supported keys picked up after periodic sync",
172-
preTestSignerUpdate: func() {
174+
preTestSignerUpdate: func(t *testing.T) {
173175
mockSigner.SigningKey = key1
174176
},
175-
preValidationSignerUpdate: func() {
177+
preValidationSignerUpdate: func(t *testing.T) {
178+
t.Helper()
176179
cpy := make(map[string]v1alpha1testing.KeyT)
177180
for key, value := range mockSigner.GetSupportedKeys() {
178181
cpy[key] = value
179182
}
180183
cpy["kid-1"] = v1alpha1testing.KeyT{Key: pubKey1Bytes}
181184
mockSigner.SetSupportedKeys(cpy)
182-
mockSigner.WaitForSupportedKeysFetch()
185+
waitForDataTimestamp(t, client, time.Now())
183186
},
184187
shouldPassAuth: true,
185188
},
@@ -195,7 +198,7 @@ func TestExternalJWTSigningAndAuth(t *testing.T) {
195198
mockSigner.WaitForSupportedKeysFetch()
196199

197200
// Adjust parameters on mock signer for the test.
198-
tc.preTestSignerUpdate()
201+
tc.preTestSignerUpdate(t)
199202

200203
// Request a token for ns-1:sa-1.
201204
tokenExpirationSec := int64(2 * 60 * 60) // 2h
@@ -214,7 +217,7 @@ func TestExternalJWTSigningAndAuth(t *testing.T) {
214217
}
215218

216219
// Adjust parameters on mock signer for the test.
217-
tc.preValidationSignerUpdate()
220+
tc.preValidationSignerUpdate(t)
218221

219222
// Try Validating the token.
220223
tokenReviewResult, err := client.AuthenticationV1().TokenReviews().Create(ctx, &authv1.TokenReview{
@@ -235,6 +238,36 @@ func TestExternalJWTSigningAndAuth(t *testing.T) {
235238
}
236239
}
237240

241+
func waitForDataTimestamp(t *testing.T, client kubernetes.Interface, minimumDataTimestamp time.Time) {
242+
t.Helper()
243+
minimumSample := float64(minimumDataTimestamp.UnixNano()) / float64(1000000000)
244+
t.Logf("waiting for >=%f", minimumSample)
245+
err := wait.PollImmediate(time.Second, wait.ForeverTestTimeout, func() (bool, error) {
246+
rawMetrics, err := client.CoreV1().RESTClient().Get().AbsPath("/metrics").DoRaw(context.TODO())
247+
if err != nil {
248+
return false, err
249+
}
250+
metrics := testutil.NewMetrics()
251+
if err := testutil.ParseMetrics(string(rawMetrics), &metrics); err != nil {
252+
return false, err
253+
}
254+
samples, ok := metrics["apiserver_externaljwt_fetch_keys_data_timestamp"]
255+
if !ok || len(samples) == 0 {
256+
t.Log("no samples found for apiserver_externaljwt_fetch_keys_data_timestamp, retrying...")
257+
return false, nil
258+
}
259+
if minimumSample > float64(samples[0].Value) {
260+
t.Logf("apiserver_externaljwt_fetch_keys_data_timestamp at %f, waiting until >=%f...", samples[0].Value, minimumSample)
261+
return false, nil
262+
}
263+
t.Logf("saw %f", samples[0].Value)
264+
return true, nil
265+
})
266+
if err != nil {
267+
t.Fatal(err)
268+
}
269+
}
270+
238271
func TestDelayedStartForSigner(t *testing.T) {
239272
// Enable feature gate for external JWT signer.
240273
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExternalServiceAccountTokenSigner, true)

0 commit comments

Comments
 (0)