Skip to content

Commit f3ede26

Browse files
committed
start kube-apiserver and webhook server only once to shorten the webhook audit test time
1 parent 044478c commit f3ede26

File tree

1 file changed

+106
-68
lines changed

1 file changed

+106
-68
lines changed

test/integration/master/audit_test.go

Lines changed: 106 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -58,18 +58,34 @@ var (
5858
apiVersion: {version}
5959
kind: Policy
6060
rules:
61-
- level: {level}
61+
- level: RequestResponse
62+
namespaces: ["no-webhook-namespace"]
63+
resources:
64+
- group: "" # core
65+
resources: ["configmaps"]
66+
- level: Metadata
67+
namespaces: ["webhook-audit-metadata"]
68+
resources:
69+
- group: "" # core
70+
resources: ["configmaps"]
71+
- level: Request
72+
namespaces: ["webhook-audit-request"]
73+
resources:
74+
- group: "" # core
75+
resources: ["configmaps"]
76+
- level: RequestResponse
77+
namespaces: ["webhook-audit-response"]
6278
resources:
6379
- group: "" # core
6480
resources: ["configmaps"]
6581
6682
`
67-
namespace = "default"
68-
watchTestTimeout int64 = 1
69-
watchOptions = metav1.ListOptions{TimeoutSeconds: &watchTestTimeout}
70-
patch, _ = json.Marshal(jsonpatch.Patch{})
71-
auditTestUser = "system:apiserver"
72-
versions = map[string]schema.GroupVersion{
83+
nonAdmissionWebhookNamespace = "no-webhook-namespace"
84+
watchTestTimeout int64 = 1
85+
watchOptions = metav1.ListOptions{TimeoutSeconds: &watchTestTimeout}
86+
patch, _ = json.Marshal(jsonpatch.Patch{})
87+
auditTestUser = "system:apiserver"
88+
versions = map[string]schema.GroupVersion{
7389
"audit.k8s.io/v1": auditv1.SchemeGroupVersion,
7490
"audit.k8s.io/v1beta1": auditv1beta1.SchemeGroupVersion,
7591
}
@@ -78,96 +94,96 @@ rules:
7894
{
7995
Level: auditinternal.LevelRequestResponse,
8096
Stage: auditinternal.StageResponseComplete,
81-
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps", namespace),
97+
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps", nonAdmissionWebhookNamespace),
8298
Verb: "create",
8399
Code: 201,
84100
User: auditTestUser,
85101
Resource: "configmaps",
86-
Namespace: namespace,
102+
Namespace: nonAdmissionWebhookNamespace,
87103
RequestObject: true,
88104
ResponseObject: true,
89105
AuthorizeDecision: "allow",
90106
}, {
91107
Level: auditinternal.LevelRequestResponse,
92108
Stage: auditinternal.StageResponseComplete,
93-
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", namespace),
109+
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", nonAdmissionWebhookNamespace),
94110
Verb: "get",
95111
Code: 200,
96112
User: auditTestUser,
97113
Resource: "configmaps",
98-
Namespace: namespace,
114+
Namespace: nonAdmissionWebhookNamespace,
99115
RequestObject: false,
100116
ResponseObject: true,
101117
AuthorizeDecision: "allow",
102118
}, {
103119
Level: auditinternal.LevelRequestResponse,
104120
Stage: auditinternal.StageResponseComplete,
105-
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps", namespace),
121+
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps", nonAdmissionWebhookNamespace),
106122
Verb: "list",
107123
Code: 200,
108124
User: auditTestUser,
109125
Resource: "configmaps",
110-
Namespace: namespace,
126+
Namespace: nonAdmissionWebhookNamespace,
111127
RequestObject: false,
112128
ResponseObject: true,
113129
AuthorizeDecision: "allow",
114130
}, {
115131
Level: auditinternal.LevelRequestResponse,
116132
Stage: auditinternal.StageResponseStarted,
117-
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
133+
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps?timeout=%ds&timeoutSeconds=%d&watch=true", nonAdmissionWebhookNamespace, watchTestTimeout, watchTestTimeout),
118134
Verb: "watch",
119135
Code: 200,
120136
User: auditTestUser,
121137
Resource: "configmaps",
122-
Namespace: namespace,
138+
Namespace: nonAdmissionWebhookNamespace,
123139
RequestObject: false,
124140
ResponseObject: false,
125141
AuthorizeDecision: "allow",
126142
}, {
127143
Level: auditinternal.LevelRequestResponse,
128144
Stage: auditinternal.StageResponseComplete,
129-
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
145+
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps?timeout=%ds&timeoutSeconds=%d&watch=true", nonAdmissionWebhookNamespace, watchTestTimeout, watchTestTimeout),
130146
Verb: "watch",
131147
Code: 200,
132148
User: auditTestUser,
133149
Resource: "configmaps",
134-
Namespace: namespace,
150+
Namespace: nonAdmissionWebhookNamespace,
135151
RequestObject: false,
136152
ResponseObject: false,
137153
AuthorizeDecision: "allow",
138154
}, {
139155
Level: auditinternal.LevelRequestResponse,
140156
Stage: auditinternal.StageResponseComplete,
141-
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", namespace),
157+
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", nonAdmissionWebhookNamespace),
142158
Verb: "update",
143159
Code: 200,
144160
User: auditTestUser,
145161
Resource: "configmaps",
146-
Namespace: namespace,
162+
Namespace: nonAdmissionWebhookNamespace,
147163
RequestObject: true,
148164
ResponseObject: true,
149165
AuthorizeDecision: "allow",
150166
}, {
151167
Level: auditinternal.LevelRequestResponse,
152168
Stage: auditinternal.StageResponseComplete,
153-
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", namespace),
169+
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", nonAdmissionWebhookNamespace),
154170
Verb: "patch",
155171
Code: 200,
156172
User: auditTestUser,
157173
Resource: "configmaps",
158-
Namespace: namespace,
174+
Namespace: nonAdmissionWebhookNamespace,
159175
RequestObject: true,
160176
ResponseObject: true,
161177
AuthorizeDecision: "allow",
162178
}, {
163179
Level: auditinternal.LevelRequestResponse,
164180
Stage: auditinternal.StageResponseComplete,
165-
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", namespace),
181+
RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", nonAdmissionWebhookNamespace),
166182
Verb: "delete",
167183
Code: 200,
168184
User: auditTestUser,
169185
Resource: "configmaps",
170-
Namespace: namespace,
186+
Namespace: nonAdmissionWebhookNamespace,
171187
RequestObject: true,
172188
ResponseObject: true,
173189
AuthorizeDecision: "allow",
@@ -177,53 +193,22 @@ rules:
177193

178194
// TestAudit ensures that both v1beta1 and v1 version audit api could work.
179195
func TestAudit(t *testing.T) {
180-
tcs := []struct {
181-
auditLevel auditinternal.Level
182-
enableMutatingWebhook bool
183-
}{
184-
{
185-
auditLevel: auditinternal.LevelRequestResponse,
186-
enableMutatingWebhook: false,
187-
},
188-
{
189-
auditLevel: auditinternal.LevelMetadata,
190-
enableMutatingWebhook: true,
191-
},
192-
{
193-
auditLevel: auditinternal.LevelRequest,
194-
enableMutatingWebhook: true,
195-
},
196-
{
197-
auditLevel: auditinternal.LevelRequestResponse,
198-
enableMutatingWebhook: true,
199-
},
200-
}
201196
for version := range versions {
202-
for _, tc := range tcs {
203-
t.Run(fmt.Sprintf("%s.%s.%t", version, tc.auditLevel, tc.enableMutatingWebhook), func(t *testing.T) {
204-
testAudit(t, version, tc.auditLevel, tc.enableMutatingWebhook)
205-
})
206-
}
197+
runTestWithVersion(t, version)
207198
}
208199
}
209200

210-
func testAudit(t *testing.T, version string, level auditinternal.Level, enableMutatingWebhook bool) {
211-
var url string
212-
var err error
213-
closeFunc := func() {}
214-
if enableMutatingWebhook {
215-
webhookMux := http.NewServeMux()
216-
webhookMux.Handle("/mutation", utils.AdmissionWebhookHandler(t, admitFunc))
217-
url, closeFunc, err = utils.NewAdmissionWebhookServer(webhookMux)
218-
}
201+
func runTestWithVersion(t *testing.T, version string) {
202+
webhookMux := http.NewServeMux()
203+
webhookMux.Handle("/mutation", utils.AdmissionWebhookHandler(t, admitFunc))
204+
url, closeFunc, err := utils.NewAdmissionWebhookServer(webhookMux)
219205
defer closeFunc()
220206
if err != nil {
221207
t.Fatalf("%v", err)
222208
}
223209

224210
// prepare audit policy file
225211
auditPolicy := strings.Replace(auditPolicyPattern, "{version}", version, 1)
226-
auditPolicy = strings.Replace(auditPolicy, "{level}", string(level), 1)
227212
policyFile, err := ioutil.TempFile("", "audit-policy.yaml")
228213
if err != nil {
229214
t.Fatalf("Failed to create audit policy file: %v", err)
@@ -258,24 +243,59 @@ func testAudit(t *testing.T, version string, level auditinternal.Level, enableMu
258243
t.Fatalf("Unexpected error: %v", err)
259244
}
260245

261-
if enableMutatingWebhook {
262-
if err := createV1beta1MutationWebhook(kubeclient, url+"/mutation"); err != nil {
263-
t.Fatal(err)
264-
}
246+
if err := createV1beta1MutationWebhook(kubeclient, url+"/mutation"); err != nil {
247+
t.Fatal(err)
265248
}
266249

250+
tcs := []struct {
251+
auditLevel auditinternal.Level
252+
enableMutatingWebhook bool
253+
namespace string
254+
}{
255+
{
256+
auditLevel: auditinternal.LevelRequestResponse,
257+
enableMutatingWebhook: false,
258+
namespace: nonAdmissionWebhookNamespace,
259+
},
260+
{
261+
auditLevel: auditinternal.LevelMetadata,
262+
enableMutatingWebhook: true,
263+
namespace: "webhook-audit-metadata",
264+
},
265+
{
266+
auditLevel: auditinternal.LevelRequest,
267+
enableMutatingWebhook: true,
268+
namespace: "webhook-audit-request",
269+
},
270+
{
271+
auditLevel: auditinternal.LevelRequestResponse,
272+
enableMutatingWebhook: true,
273+
namespace: "webhook-audit-response",
274+
},
275+
}
276+
277+
for _, tc := range tcs {
278+
t.Run(fmt.Sprintf("%s.%s.%t", version, tc.auditLevel, tc.enableMutatingWebhook), func(t *testing.T) {
279+
testAudit(t, version, tc.auditLevel, tc.enableMutatingWebhook, tc.namespace, kubeclient, logFile)
280+
})
281+
}
282+
}
283+
284+
func testAudit(t *testing.T, version string, level auditinternal.Level, enableMutatingWebhook bool, namespace string, kubeclient kubernetes.Interface, logFile *os.File) {
267285
var lastMissingReport string
286+
createNamespace(t, kubeclient, namespace)
287+
268288
if err := wait.Poll(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
269289
// perform configmap operations
270-
configMapOperations(t, kubeclient)
290+
configMapOperations(t, kubeclient, namespace)
271291

272292
// check for corresponding audit logs
273293
stream, err := os.Open(logFile.Name())
274294
if err != nil {
275295
return false, fmt.Errorf("unexpected error: %v", err)
276296
}
277297
defer stream.Close()
278-
missingReport, err := utils.CheckAuditLines(stream, getExpectedEvents(level, enableMutatingWebhook), versions[version])
298+
missingReport, err := utils.CheckAuditLines(stream, getExpectedEvents(level, enableMutatingWebhook, namespace), versions[version])
279299
if err != nil {
280300
return false, fmt.Errorf("unexpected error: %v", err)
281301
}
@@ -289,7 +309,7 @@ func testAudit(t *testing.T, version string, level auditinternal.Level, enableMu
289309
}
290310
}
291311

292-
func getExpectedEvents(level auditinternal.Level, enableMutatingWebhook bool) []utils.AuditEvent {
312+
func getExpectedEvents(level auditinternal.Level, enableMutatingWebhook bool, namespace string) []utils.AuditEvent {
293313
if !enableMutatingWebhook {
294314
return expectedEvents
295315
}
@@ -350,16 +370,23 @@ func getExpectedEvents(level auditinternal.Level, enableMutatingWebhook bool) []
350370
// configMapOperations is a set of known operations performed on the configmap type
351371
// which correspond to the expected events.
352372
// This is shared by the dynamic test
353-
func configMapOperations(t *testing.T, kubeclient kubernetes.Interface) {
373+
func configMapOperations(t *testing.T, kubeclient kubernetes.Interface, namespace string) {
354374
// create, get, watch, update, patch, list and delete configmap.
355375
configMap := &apiv1.ConfigMap{
356376
ObjectMeta: metav1.ObjectMeta{
357-
Name: "audit-configmap",
377+
Name: "audit-configmap",
378+
Namespace: namespace,
358379
},
359380
Data: map[string]string{
360381
"map-key": "map-value",
361382
},
362383
}
384+
// add admission label to config maps that are to be sent to webhook
385+
if namespace != nonAdmissionWebhookNamespace {
386+
configMap.Labels = map[string]string{
387+
"admission": "true",
388+
}
389+
}
363390

364391
_, err := kubeclient.CoreV1().ConfigMaps(namespace).Create(context.TODO(), configMap, metav1.CreateOptions{})
365392
expectNoError(t, err, "failed to create audit-configmap")
@@ -440,9 +467,20 @@ func createV1beta1MutationWebhook(client clientset.Interface, endpoint string) e
440467
Operations: []admissionv1beta1.OperationType{admissionv1beta1.Create, admissionv1beta1.Update},
441468
Rule: admissionv1beta1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"*/*"}},
442469
}},
470+
ObjectSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"admission": "true"}},
443471
FailurePolicy: &fail,
444472
AdmissionReviewVersions: []string{"v1beta1"},
445473
}},
446474
}, metav1.CreateOptions{})
447475
return err
448476
}
477+
478+
func createNamespace(t *testing.T, kubeclient clientset.Interface, namespace string) {
479+
ns := &apiv1.Namespace{
480+
ObjectMeta: metav1.ObjectMeta{
481+
Name: namespace,
482+
},
483+
}
484+
_, err := kubeclient.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{})
485+
expectNoError(t, err, fmt.Sprintf("failed to create namespace ns %s", namespace))
486+
}

0 commit comments

Comments
 (0)