github.com/nginxinc/kubernetes-ingress@v1.12.5/internal/k8s/appprotect/app_protect_resources_test.go (about) 1 package appprotect 2 3 import ( 4 "reflect" 5 "strings" 6 "testing" 7 8 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 9 ) 10 11 func TestValidateRequiredFields(t *testing.T) { 12 tests := []struct { 13 obj *unstructured.Unstructured 14 fieldsList [][]string 15 expectErr bool 16 msg string 17 }{ 18 { 19 obj: &unstructured.Unstructured{ 20 Object: map[string]interface{}{ 21 "a": map[string]interface{}{}, 22 "b": map[string]interface{}{}, 23 }, 24 }, 25 fieldsList: [][]string{{"a"}, {"b"}}, 26 expectErr: false, 27 msg: "valid object with 2 fields", 28 }, 29 { 30 obj: &unstructured.Unstructured{ 31 Object: map[string]interface{}{ 32 "a": map[string]interface{}{}, 33 }, 34 }, 35 fieldsList: [][]string{{"a"}, {"b"}}, 36 expectErr: true, 37 msg: "invalid object with a missing field", 38 }, 39 { 40 obj: &unstructured.Unstructured{ 41 Object: map[string]interface{}{ 42 "a": map[string]interface{}{}, 43 "x": map[string]interface{}{}, 44 }, 45 }, 46 fieldsList: [][]string{{"a"}, {"b"}}, 47 expectErr: true, 48 msg: "invalid object with a wrong field", 49 }, 50 { 51 obj: &unstructured.Unstructured{ 52 Object: map[string]interface{}{ 53 "a": map[string]interface{}{ 54 "b": map[string]interface{}{}, 55 }, 56 }, 57 }, 58 fieldsList: [][]string{{"a", "b"}}, 59 expectErr: false, 60 msg: "valid object with nested field", 61 }, 62 { 63 obj: &unstructured.Unstructured{ 64 Object: map[string]interface{}{ 65 "a": map[string]interface{}{ 66 "x": map[string]interface{}{}, 67 }, 68 }, 69 }, 70 fieldsList: [][]string{{"a", "b"}}, 71 expectErr: true, 72 msg: "invalid object with a wrong nested field", 73 }, 74 { 75 obj: &unstructured.Unstructured{ 76 Object: map[string]interface{}{}, 77 }, 78 fieldsList: nil, 79 expectErr: false, 80 msg: "valid object with no validation", 81 }, 82 { 83 obj: &unstructured.Unstructured{ 84 Object: map[string]interface{}{ 85 "a": "wrong-type", // must be map[string]interface{} 86 }, 87 }, 88 fieldsList: [][]string{{"a"}}, 89 expectErr: true, 90 msg: "invalid object with a field of wrong type", 91 }, 92 } 93 94 for _, test := range tests { 95 err := validateRequiredFields(test.obj, test.fieldsList) 96 if test.expectErr && err == nil { 97 t.Errorf("validateRequiredFields() returned no error for the case of %s", test.msg) 98 } 99 if !test.expectErr && err != nil { 100 t.Errorf("validateRequiredFields() returned unexpected error %v for the case of %s", err, test.msg) 101 } 102 } 103 } 104 105 func TestValidateRequiredSlices(t *testing.T) { 106 tests := []struct { 107 obj *unstructured.Unstructured 108 fieldsList [][]string 109 expectErr bool 110 msg string 111 }{ 112 { 113 obj: &unstructured.Unstructured{ 114 Object: map[string]interface{}{ 115 "a": []interface{}{}, 116 "b": []interface{}{}, 117 }, 118 }, 119 fieldsList: [][]string{{"a"}, {"b"}}, 120 expectErr: false, 121 msg: "valid object with 2 fields", 122 }, 123 { 124 obj: &unstructured.Unstructured{ 125 Object: map[string]interface{}{ 126 "a": []interface{}{}, 127 }, 128 }, 129 fieldsList: [][]string{{"a"}, {"b"}}, 130 expectErr: true, 131 msg: "invalid object with a field", 132 }, 133 { 134 obj: &unstructured.Unstructured{ 135 Object: map[string]interface{}{ 136 "a": []interface{}{}, 137 "x": []interface{}{}, 138 }, 139 }, 140 fieldsList: [][]string{{"a"}, {"b"}}, 141 expectErr: true, 142 msg: "invalid object with a wrong field", 143 }, 144 { 145 obj: &unstructured.Unstructured{ 146 Object: map[string]interface{}{ 147 "a": map[string]interface{}{ 148 "b": []interface{}{}, 149 }, 150 }, 151 }, 152 fieldsList: [][]string{{"a", "b"}}, 153 expectErr: false, 154 msg: "valid object with nested field", 155 }, 156 { 157 obj: &unstructured.Unstructured{ 158 Object: map[string]interface{}{ 159 "a": map[string]interface{}{ 160 "x": []interface{}{}, 161 }, 162 }, 163 }, 164 fieldsList: [][]string{{"a", "b"}}, 165 expectErr: true, 166 msg: "invalid object with a wrong nested field", 167 }, 168 { 169 obj: &unstructured.Unstructured{ 170 Object: map[string]interface{}{}, 171 }, 172 fieldsList: nil, 173 expectErr: false, 174 msg: "valid object with no validation", 175 }, 176 { 177 obj: &unstructured.Unstructured{ 178 Object: map[string]interface{}{ 179 "a": "wrong-type", // must be [string]interface{} 180 }, 181 }, 182 fieldsList: [][]string{{"a"}}, 183 expectErr: true, 184 msg: "invalid object with a field of wrong type", 185 }, 186 } 187 188 for _, test := range tests { 189 err := validateRequiredSlices(test.obj, test.fieldsList) 190 if test.expectErr && err == nil { 191 t.Errorf("validateRequiredSlices() returned no error for the case of %s", test.msg) 192 } 193 if !test.expectErr && err != nil { 194 t.Errorf("validateRequiredSlices() returned unexpected error %v for the case of %s", err, test.msg) 195 } 196 } 197 } 198 199 func TestValidateAppProtectPolicy(t *testing.T) { 200 tests := []struct { 201 policy *unstructured.Unstructured 202 expectErr bool 203 msg string 204 }{ 205 { 206 policy: &unstructured.Unstructured{ 207 Object: map[string]interface{}{ 208 "spec": map[string]interface{}{ 209 "policy": map[string]interface{}{}, 210 }, 211 }, 212 }, 213 expectErr: false, 214 msg: "valid policy", 215 }, 216 { 217 policy: &unstructured.Unstructured{ 218 Object: map[string]interface{}{ 219 "spec": map[string]interface{}{ 220 "something": map[string]interface{}{}, 221 }, 222 }, 223 }, 224 expectErr: true, 225 msg: "invalid policy with no policy field", 226 }, 227 { 228 policy: &unstructured.Unstructured{ 229 Object: map[string]interface{}{ 230 "something": map[string]interface{}{ 231 "policy": map[string]interface{}{}, 232 }, 233 }, 234 }, 235 expectErr: true, 236 msg: "invalid policy with no spec field", 237 }, 238 } 239 240 for _, test := range tests { 241 err := validateAppProtectPolicy(test.policy) 242 if test.expectErr && err == nil { 243 t.Errorf("validateAppProtectPolicy() returned no error for the case of %s", test.msg) 244 } 245 if !test.expectErr && err != nil { 246 t.Errorf("validateAppProtectPolicy() returned unexpected error %v for the case of %s", err, test.msg) 247 } 248 } 249 } 250 251 func TestValidateAppProtectLogConf(t *testing.T) { 252 tests := []struct { 253 logConf *unstructured.Unstructured 254 expectErr bool 255 msg string 256 }{ 257 { 258 logConf: &unstructured.Unstructured{ 259 Object: map[string]interface{}{ 260 "spec": map[string]interface{}{ 261 "content": map[string]interface{}{}, 262 "filter": map[string]interface{}{}, 263 }, 264 }, 265 }, 266 expectErr: false, 267 msg: "valid log conf", 268 }, 269 { 270 logConf: &unstructured.Unstructured{ 271 Object: map[string]interface{}{ 272 "spec": map[string]interface{}{ 273 "filter": map[string]interface{}{}, 274 }, 275 }, 276 }, 277 expectErr: true, 278 msg: "invalid log conf with no content field", 279 }, 280 { 281 logConf: &unstructured.Unstructured{ 282 Object: map[string]interface{}{ 283 "spec": map[string]interface{}{ 284 "content": map[string]interface{}{}, 285 }, 286 }, 287 }, 288 expectErr: true, 289 msg: "invalid log conf with no filter field", 290 }, 291 { 292 logConf: &unstructured.Unstructured{ 293 Object: map[string]interface{}{ 294 "something": map[string]interface{}{ 295 "content": map[string]interface{}{}, 296 "filter": map[string]interface{}{}, 297 }, 298 }, 299 }, 300 expectErr: true, 301 msg: "invalid log conf with no spec field", 302 }, 303 } 304 305 for _, test := range tests { 306 err := validateAppProtectLogConf(test.logConf) 307 if test.expectErr && err == nil { 308 t.Errorf("validateAppProtectLogConf() returned no error for the case of %s", test.msg) 309 } 310 if !test.expectErr && err != nil { 311 t.Errorf("validateAppProtectLogConf() returned unexpected error %v for the case of %s", err, test.msg) 312 } 313 } 314 } 315 316 func TestValidateAppProtectLogDestinationAnnotation(t *testing.T) { 317 // Positive test cases 318 var posDstAntns = []string{"stderr", "syslog:server=localhost:9000", "syslog:server=10.1.1.2:9000", "/var/log/ap.log"} 319 320 // Negative test cases item, expected error message 321 var negDstAntns = [][]string{ 322 {"stdout", "Log Destination did not follow format"}, 323 {"syslog:server=localhost:99999", "not a valid port number"}, 324 {"syslog:server=999.99.99.99:5678", "is not a valid ip address"}, 325 } 326 327 for _, tCase := range posDstAntns { 328 err := ValidateAppProtectLogDestination(tCase) 329 if err != nil { 330 t.Errorf("got %v expected nil", err) 331 } 332 } 333 for _, nTCase := range negDstAntns { 334 err := ValidateAppProtectLogDestination(nTCase[0]) 335 if err == nil { 336 t.Errorf("got no error expected error containing %s", nTCase[1]) 337 } else { 338 if !strings.Contains(err.Error(), nTCase[1]) { 339 t.Errorf("got %v expected to contain: %s", err, nTCase[1]) 340 } 341 } 342 } 343 } 344 345 func TestValidateAppProtectUserSig(t *testing.T) { 346 tests := []struct { 347 userSig *unstructured.Unstructured 348 expectErr bool 349 msg string 350 }{ 351 { 352 userSig: &unstructured.Unstructured{ 353 Object: map[string]interface{}{ 354 "spec": map[string]interface{}{ 355 "signatures": []interface{}{}, 356 }, 357 }, 358 }, 359 expectErr: false, 360 msg: "valid user sig", 361 }, 362 { 363 userSig: &unstructured.Unstructured{ 364 Object: map[string]interface{}{ 365 "spec": map[string]interface{}{ 366 "something": []interface{}{}, 367 }, 368 }, 369 }, 370 expectErr: true, 371 msg: "invalid user sig with no signatures", 372 }, 373 { 374 userSig: &unstructured.Unstructured{ 375 Object: map[string]interface{}{ 376 "something": map[string]interface{}{ 377 "signatures": []interface{}{}, 378 }, 379 }, 380 }, 381 expectErr: true, 382 msg: "invalid user sign with no spec field", 383 }, 384 } 385 386 for _, test := range tests { 387 err := validateAppProtectUserSig(test.userSig) 388 if test.expectErr && err == nil { 389 t.Errorf("validateAppProtectUserSig() returned no error for the case of %s", test.msg) 390 } 391 if !test.expectErr && err != nil { 392 t.Errorf("validateAppProtectUserSig() returned unexpected error %v for the case of %s", err, test.msg) 393 } 394 } 395 } 396 397 func TestParseResourceReferenceAnnotation(t *testing.T) { 398 tests := []struct { 399 ns, antn, expected string 400 }{ 401 { 402 ns: "default", 403 antn: "resource", 404 expected: "default/resource", 405 }, 406 { 407 ns: "default", 408 antn: "ns-1/resource", 409 expected: "ns-1/resource", 410 }, 411 } 412 413 for _, test := range tests { 414 result := ParseResourceReferenceAnnotation(test.ns, test.antn) 415 if result != test.expected { 416 t.Errorf("ParseResourceReferenceAnnotation(%q,%q) returned %q but expected %q", test.ns, test.antn, result, test.expected) 417 } 418 } 419 } 420 421 func TestGenNsName(t *testing.T) { 422 obj := &unstructured.Unstructured{ 423 Object: map[string]interface{}{ 424 "metadata": map[string]interface{}{ 425 "namespace": "default", 426 "name": "resource", 427 }, 428 }, 429 } 430 431 expected := "default/resource" 432 433 result := GetNsName(obj) 434 if result != expected { 435 t.Errorf("GetNsName() returned %q but expected %q", result, expected) 436 } 437 } 438 439 func TestParseResourceReferenceAnnotationList(t *testing.T) { 440 namespace := "test_ns" 441 tests := []struct { 442 annotation string 443 expected []string 444 msg string 445 }{ 446 { 447 annotation: "test", 448 expected: []string{namespace + "/test"}, 449 msg: "single resource no namespace", 450 }, 451 { 452 annotation: "different_ns/test", 453 expected: []string{"different_ns/test"}, 454 msg: "single resource with namespace", 455 }, 456 { 457 annotation: "test,test1", 458 expected: []string{namespace + "/test", namespace + "/test1"}, 459 msg: "multiple resource no namespace", 460 }, 461 { 462 annotation: "different_ns/test,different_ns/test1", 463 expected: []string{"different_ns/test", "different_ns/test1"}, 464 msg: "multiple resource with namespaces", 465 }, 466 { 467 annotation: "different_ns/test,test1", 468 expected: []string{"different_ns/test", namespace + "/test1"}, 469 msg: "multiple resource with mixed namespaces", 470 }, 471 } 472 for _, test := range tests { 473 result := ParseResourceReferenceAnnotationList(namespace, test.annotation) 474 if !reflect.DeepEqual(result, test.expected) { 475 t.Errorf("Error in test case %s: got: %v, expected: %v", test.msg, result, test.expected) 476 } 477 } 478 }