agones.dev/agones@v1.54.0/pkg/util/webhooks/webhooks_test.go (about) 1 // Copyright 2018 Google LLC All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package webhooks 16 17 import ( 18 "bytes" 19 "encoding/json" 20 "io" 21 "net/http" 22 "net/http/httptest" 23 "testing" 24 25 "github.com/pkg/errors" 26 "github.com/stretchr/testify/assert" 27 admissionv1 "k8s.io/api/admission/v1" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/runtime" 30 "k8s.io/apimachinery/pkg/runtime/schema" 31 32 agonesv1 "agones.dev/agones/pkg/apis/agones/v1" 33 ) 34 35 func TestWebHookAddHandler(t *testing.T) { 36 t.Parallel() 37 38 type testHandler struct { 39 gk schema.GroupKind 40 op admissionv1.Operation 41 } 42 type expected struct { 43 count int 44 } 45 fixtures := map[string]struct { 46 handlers []testHandler 47 expected expected 48 }{ 49 "single, matching": { 50 handlers: []testHandler{{gk: schema.GroupKind{Group: "group", Kind: "kind"}, op: admissionv1.Create}}, 51 expected: expected{count: 1}, 52 }, 53 "double, one matching op": { 54 expected: expected{count: 1}, 55 handlers: []testHandler{ 56 {gk: schema.GroupKind{Group: "group", Kind: "kind"}, op: admissionv1.Create}, 57 {gk: schema.GroupKind{Group: "group", Kind: "kind"}, op: admissionv1.Update}, 58 }, 59 }, 60 "double, one matching group": { 61 expected: expected{count: 1}, 62 handlers: []testHandler{ 63 {gk: schema.GroupKind{Group: "group", Kind: "kind"}, op: admissionv1.Create}, 64 {gk: schema.GroupKind{Group: "nope", Kind: "kind"}, op: admissionv1.Create}, 65 }, 66 }, 67 "double, one matching kind": { 68 expected: expected{count: 1}, 69 handlers: []testHandler{ 70 {gk: schema.GroupKind{Group: "group", Kind: "kind"}, op: admissionv1.Create}, 71 {gk: schema.GroupKind{Group: "group", Kind: "nope"}, op: admissionv1.Create}, 72 }, 73 }, 74 "double, both matching": { 75 expected: expected{count: 2}, 76 handlers: []testHandler{ 77 {gk: schema.GroupKind{Group: "group", Kind: "kind"}, op: admissionv1.Create}, 78 {gk: schema.GroupKind{Group: "group", Kind: "kind"}, op: admissionv1.Create}, 79 }, 80 }, 81 } 82 83 for k, handles := range fixtures { 84 t.Run(k, func(t *testing.T) { 85 86 stop := make(chan struct{}) 87 defer close(stop) 88 89 fixture := admissionv1.AdmissionReview{Request: &admissionv1.AdmissionRequest{ 90 Kind: metav1.GroupVersionKind{Kind: "kind", Group: "group", Version: "version"}, 91 Operation: admissionv1.Create, 92 UID: "1234"}} 93 94 callCount := 0 95 mux := http.NewServeMux() 96 ts := httptest.NewUnstartedServer(mux) 97 wh := NewWebHook(mux) 98 99 for _, th := range handles.handlers { 100 wh.AddHandler("/test", th.gk, th.op, func(review admissionv1.AdmissionReview) (admissionv1.AdmissionReview, error) { 101 assert.Equal(t, fixture.Request, review.Request) 102 assert.True(t, review.Response.Allowed) 103 callCount++ 104 return review, nil 105 }) 106 } 107 108 ts.StartTLS() 109 defer ts.Close() 110 111 client := ts.Client() 112 url := ts.URL + "/test" 113 114 buf := &bytes.Buffer{} 115 err := json.NewEncoder(buf).Encode(fixture) 116 assert.Nil(t, err) 117 118 r, err := http.NewRequest("GET", url, buf) 119 assert.Nil(t, err) 120 121 resp, err := client.Do(r) 122 assert.Nil(t, err) 123 defer resp.Body.Close() // nolint: errcheck 124 assert.Equal(t, http.StatusOK, resp.StatusCode) 125 126 assert.Equal(t, handles.expected.count, callCount, "[%v] /test should have been called for %#v", k, handles) 127 }) 128 } 129 } 130 131 func TestWebHookFleetValidationHandler(t *testing.T) { 132 t.Parallel() 133 134 type testHandler struct { 135 gk schema.GroupKind 136 op admissionv1.Operation 137 } 138 type expected struct { 139 count int 140 } 141 fixtures := map[string]struct { 142 handlers []testHandler 143 expected expected 144 }{ 145 "single, matching": { 146 handlers: []testHandler{{gk: schema.GroupKind{Group: "group", Kind: "fleet"}, op: admissionv1.Create}}, 147 expected: expected{count: 1}, 148 }, 149 } 150 151 for k, handles := range fixtures { 152 t.Run(k, func(t *testing.T) { 153 154 stop := make(chan struct{}) 155 defer close(stop) 156 157 raw := []byte(`{ 158 "apiVersion": "agones.dev/v1", 159 "kind": "Fleet", 160 "spec": { 161 "replicas": 2, 162 "template": { 163 "spec": { 164 "template": { 165 "spec": { 166 "containers": [{ 167 "image": "us-docker.pkg.dev/agones-images/examples/simple-game-server:0.39", 168 "name": false 169 }] 170 } 171 } 172 } 173 } 174 } 175 }`) 176 fixture := admissionv1.AdmissionReview{Request: &admissionv1.AdmissionRequest{ 177 Kind: metav1.GroupVersionKind{Kind: "fleet", Group: "group", Version: "version"}, 178 Operation: admissionv1.Create, 179 Object: runtime.RawExtension{ 180 Raw: raw, 181 }, 182 UID: "1234"}} 183 184 callCount := 0 185 mux := http.NewServeMux() 186 ts := httptest.NewUnstartedServer(mux) 187 wh := NewWebHook(mux) 188 189 for _, th := range handles.handlers { 190 wh.AddHandler("/test", th.gk, th.op, func(review admissionv1.AdmissionReview) (admissionv1.AdmissionReview, error) { 191 fleet := &agonesv1.Fleet{} 192 193 callCount++ 194 obj := review.Request.Object 195 err := json.Unmarshal(obj.Raw, fleet) 196 assert.NotNil(t, err) 197 if err != nil { 198 return review, errors.Wrapf(err, "error unmarshalling original Fleet json: %s", obj.Raw) 199 } 200 return review, nil 201 }) 202 } 203 204 ts.StartTLS() 205 defer ts.Close() 206 207 client := ts.Client() 208 url := ts.URL + "/test" 209 210 buf := &bytes.Buffer{} 211 err := json.NewEncoder(buf).Encode(fixture) 212 assert.Nil(t, err) 213 214 r, err := http.NewRequest("GET", url, buf) 215 assert.Nil(t, err) 216 217 resp, err := client.Do(r) 218 assert.Nil(t, err) 219 defer resp.Body.Close() // nolint: errcheck 220 assert.Equal(t, http.StatusOK, resp.StatusCode) 221 body, err := io.ReadAll(resp.Body) 222 assert.Nil(t, err) 223 224 expected := "cannot unmarshal bool into Go struct field Container.spec.template.spec.template.spec.containers.name of type string" 225 assert.Contains(t, string(body), expected) 226 227 assert.Equal(t, handles.expected.count, callCount, "[%v] /test should have been called for %#v", k, handles) 228 }) 229 } 230 }