-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain_test.go
More file actions
147 lines (135 loc) · 4.38 KB
/
Copy pathmain_test.go
File metadata and controls
147 lines (135 loc) · 4.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package main
import (
"encoding/base64"
"encoding/json"
"strings"
"testing"
)
const (
targetCECName = "cilium-gateway-paperclip-gateway"
targetNS = "gateway"
)
func reviewRequest(t *testing.T, name, ns string, obj map[string]any) []byte {
t.Helper()
raw, err := json.Marshal(obj)
if err != nil {
t.Fatal(err)
}
body, err := json.Marshal(map[string]any{
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"request": map[string]any{
"uid": "test-uid-1",
"name": name,
"namespace": ns,
"object": json.RawMessage(raw),
},
})
if err != nil {
t.Fatal(err)
}
return body
}
func gatewayCEC() map[string]any { return cec(plainChain(), tlsChain(false)) }
func decode(t *testing.T, resp []byte) map[string]any {
t.Helper()
var out struct {
APIVersion string `json:"apiVersion"`
Kind string `json:"kind"`
Response map[string]any `json:"response"`
}
if err := json.Unmarshal(resp, &out); err != nil {
t.Fatalf("response must be valid JSON: %v (%s)", err, resp)
}
if out.APIVersion != "admission.k8s.io/v1" || out.Kind != "AdmissionReview" {
t.Fatalf("wrong envelope: apiVersion=%q kind=%q", out.APIVersion, out.Kind)
}
return out.Response
}
func TestReviewPatchesTargetCEC(t *testing.T) {
resp := decode(t, Review(reviewRequest(t, targetCECName, targetNS, gatewayCEC()), targetCECName, targetNS))
if resp["uid"] != "test-uid-1" {
// The API server matches the response to the request by uid; getting
// this wrong makes the patch silently ignored.
t.Fatalf("uid must be echoed back, got %v", resp["uid"])
}
if resp["allowed"] != true {
t.Fatal("must always allow")
}
if resp["patchType"] != "JSONPatch" {
t.Fatalf("patchType = %v, want JSONPatch", resp["patchType"])
}
decoded, err := base64.StdEncoding.DecodeString(resp["patch"].(string))
if err != nil {
t.Fatalf("patch must be base64-encoded: %v", err)
}
if !strings.Contains(string(decoded), "X25519MLKEM768") {
t.Fatalf("patch missing the hybrid group: %s", decoded)
}
var ops []map[string]any
if err := json.Unmarshal(decoded, &ops); err != nil {
t.Fatalf("patch must be a JSON array of ops: %v", err)
}
if len(ops) != 1 || ops[0]["op"] != "add" {
t.Fatalf("unexpected ops: %v", ops)
}
}
func TestReviewIgnoresNonTargetCEC(t *testing.T) {
// Blast radius. This webhook is registered for every ciliumenvoyconfig
// write in the namespace, so it must touch nothing but its one target.
resp := decode(t, Review(reviewRequest(t, "some-other-cec", targetNS, gatewayCEC()), targetCECName, targetNS))
if resp["allowed"] != true {
t.Fatal("must still allow")
}
if _, has := resp["patch"]; has {
t.Fatal("must not patch a non-target CEC")
}
}
func TestReviewIgnoresNonTargetNamespace(t *testing.T) {
resp := decode(t, Review(reviewRequest(t, targetCECName, "elsewhere", gatewayCEC()), targetCECName, targetNS))
if _, has := resp["patch"]; has {
t.Fatal("must not patch outside the target namespace")
}
}
func TestReviewEmitsNoPatchWhenNothingToDo(t *testing.T) {
resp := decode(t, Review(reviewRequest(t, targetCECName, targetNS, cec(plainChain(), tlsChain(true))),
targetCECName, targetNS))
if _, has := resp["patch"]; has {
t.Fatal("an already-patched CEC must produce no patch")
}
if resp["allowed"] != true {
t.Fatal("must still allow")
}
}
func TestReviewAllowsOnGarbageInput(t *testing.T) {
// Fail-open by construction. failurePolicy: Ignore covers us if the process
// is unreachable, but a running process that returns a malformed or denying
// response would still break CiliumEnvoyConfig writes, and CEC writes are
// how the front door gets configured at all.
for _, body := range [][]byte{[]byte("not json"), []byte("{}"), []byte(`{"request":null}`), nil, {}} {
resp := decode(t, Review(body, targetCECName, targetNS))
if resp["allowed"] != true {
t.Fatalf("garbage input must still be allowed: %q", body)
}
}
}
func TestReviewAllowsWhenObjectIsNotDecodable(t *testing.T) {
body, err := json.Marshal(map[string]any{
"request": map[string]any{
"uid": "u",
"name": targetCECName,
"namespace": targetNS,
"object": json.RawMessage(`"not-an-object"`),
},
})
if err != nil {
t.Fatal(err)
}
resp := decode(t, Review(body, targetCECName, targetNS))
if resp["allowed"] != true {
t.Fatal("must allow")
}
if _, has := resp["patch"]; has {
t.Fatal("must not patch an object it could not parse")
}
}