istio.io/istio@v0.0.0-20240520182934-d79c90f27776/istioctl/pkg/validate/validate_test.go (about) 1 // Copyright Istio Authors. 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 validate 16 17 import ( 18 "bytes" 19 "fmt" 20 "io" 21 "os" 22 "path/filepath" 23 "regexp" 24 "strings" 25 "testing" 26 27 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 28 "sigs.k8s.io/yaml" 29 30 "istio.io/istio/istioctl/pkg/cli" 31 "istio.io/istio/pkg/test/util/assert" 32 ) 33 34 const ( 35 validDeploymentList = ` 36 apiVersion: v1 37 items: 38 - apiVersion: apps/v1 39 kind: Deployment 40 metadata: 41 labels: 42 app: hello 43 version: v1 44 name: hello-v1 45 spec: 46 replicas: 1 47 template: 48 metadata: 49 labels: 50 app: hello 51 version: v1 52 spec: 53 containers: 54 - name: hello 55 image: istio/examples-hello 56 imagePullPolicy: IfNotPresent 57 ports: 58 - containerPort: 9080 59 - apiVersion: apps/v1 60 kind: Deployment 61 metadata: 62 labels: 63 app: details 64 version: v2 65 name: details 66 spec: 67 replicas: 1 68 template: 69 metadata: 70 labels: 71 app: details 72 version: v1 73 spec: 74 containers: 75 - name: details 76 image: istio/examples-bookinfo-details-v1:1.10.1 77 imagePullPolicy: IfNotPresent 78 ports: 79 - containerPort: 9080 80 kind: List 81 metadata: 82 resourceVersion: "" 83 selfLink: ""` 84 invalidSvcList = ` 85 apiVersion: v1 86 items: 87 - 88 apiVersion: v1 89 kind: Service 90 metadata: 91 name: details 92 spec: 93 ports: 94 - 95 name: details 96 port: 9080 97 - 98 apiVersion: v1 99 kind: Service 100 metadata: 101 name: hello 102 spec: 103 ports: 104 - 105 port: 80 106 protocol: TCP 107 kind: List 108 metadata: 109 resourceVersion: ""` 110 udpService = ` 111 kind: Service 112 metadata: 113 name: hello 114 spec: 115 ports: 116 - 117 protocol: udp` 118 skippedService = ` 119 kind: Service 120 metadata: 121 name: hello 122 namespace: istio-system 123 spec: 124 ports: 125 - 126 name: http 127 port: 9080` 128 validPortNamingSvc = ` 129 apiVersion: v1 130 kind: Service 131 metadata: 132 name: hello 133 spec: 134 ports: 135 - name: http 136 port: 9080` 137 validPortNamingWithSuffixSvc = ` 138 apiVersion: v1 139 kind: Service 140 metadata: 141 name: hello 142 spec: 143 ports: 144 - name: http-hello 145 port: 9080` 146 invalidPortNamingSvc = ` 147 apiVersion: v1 148 kind: Service 149 metadata: 150 name: hello 151 spec: 152 ports: 153 - name: hello 154 port: 9080` 155 portNameMissingSvc = ` 156 apiVersion: v1 157 kind: Service 158 metadata: 159 name: hello 160 spec: 161 ports: 162 - protocol: TCP` 163 validVirtualServiceYAML = ` 164 apiVersion: networking.istio.io/v1alpha3 165 kind: VirtualService 166 metadata: 167 name: valid-virtual-service 168 spec: 169 hosts: 170 - c 171 http: 172 - route: 173 - destination: 174 host: c 175 subset: v1 176 weight: 75 177 - destination: 178 host: c 179 subset: v2 180 weight: 25` 181 validVirtualServiceJSON = `{ 182 "apiVersion": "networking.istio.io/v1alpha3", 183 "kind": "VirtualService", 184 "metadata": { 185 "name": "valid-virtual-service" 186 }, 187 "spec": { 188 "hosts": [ 189 "c" 190 ], 191 "http": [ 192 { 193 "route": [ 194 { 195 "destination": { 196 "host": "c", 197 "subset": "v1" 198 }, 199 "weight": 75 200 }, 201 { 202 "destination": { 203 "host": "c", 204 "subset": "v2" 205 }, 206 "weight": 25 207 } 208 ] 209 } 210 ] 211 } 212 }` 213 validVirtualService1YAML = ` 214 apiVersion: networking.istio.io/v1alpha3 215 kind: VirtualService 216 metadata: 217 name: valid-virtual-service1 218 spec: 219 hosts: 220 - d 221 http: 222 - route: 223 - destination: 224 host: c 225 subset: v1 226 weight: 75 227 - destination: 228 host: c 229 subset: v2 230 weight: 25` 231 validVirtualService1JSON = `{ 232 "apiVersion": "networking.istio.io/v1alpha3", 233 "kind": "VirtualService", 234 "metadata": { 235 "name": "valid-virtual-service1" 236 }, 237 "spec": { 238 "hosts": [ 239 "d" 240 ], 241 "http": [ 242 { 243 "route": [ 244 { 245 "destination": { 246 "host": "c", 247 "subset": "v1" 248 }, 249 "weight": 75 250 }, 251 { 252 "destination": { 253 "host": "c", 254 "subset": "v2" 255 }, 256 "weight": 25 257 } 258 ] 259 } 260 ] 261 } 262 }` 263 validVirtualService2YAML = ` 264 apiVersion: networking.istio.io/v1alpha3 265 kind: VirtualService 266 metadata: 267 name: valid-virtual-service2 268 spec: 269 exportTo: 270 - '.' 271 hosts: 272 - d 273 http: 274 - route: 275 - destination: 276 host: c 277 subset: v1` 278 validVirtualService2JSON = `{ 279 "apiVersion": "networking.istio.io/v1alpha3", 280 "kind": "VirtualService", 281 "metadata": { 282 "name": "valid-virtual-service2" 283 }, 284 "spec": { 285 "exportTo": [ 286 "." 287 ], 288 "hosts": [ 289 "d" 290 ], 291 "http": [ 292 { 293 "route": [ 294 { 295 "destination": { 296 "host": "c", 297 "subset": "v1" 298 } 299 } 300 ] 301 } 302 ] 303 } 304 }` 305 invalidVirtualServiceYAML = ` 306 apiVersion: networking.istio.io/v1alpha3 307 kind: VirtualService 308 metadata: 309 name: invalid-virtual-service 310 spec: 311 http: 312 - route: 313 - destination: 314 host: c 315 subset: v1 316 weight: 75 317 - destination: 318 host: c 319 subset: v2 320 weight: -15` 321 invalidVirtualServiceJSON = `{ 322 "apiVersion": "networking.istio.io/v1alpha3", 323 "kind": "VirtualService", 324 "metadata": { 325 "name": "invalid-virtual-service" 326 }, 327 "spec": { 328 "http": [ 329 { 330 "route": [ 331 { 332 "destination": { 333 "host": "c", 334 "subset": "v1" 335 }, 336 "weight": 75 337 }, 338 { 339 "destination": { 340 "host": "c", 341 "subset": "v2" 342 }, 343 "weight": -15 344 } 345 ] 346 } 347 ] 348 } 349 } 350 ` 351 invalidVirtualServiceV1Beta1 = ` 352 apiVersion: networking.istio.io/v1beta1 353 kind: VirtualService 354 metadata: 355 name: invalid-virtual-service 356 spec: 357 http: 358 ` 359 warnDestinationRule = `apiVersion: networking.istio.io/v1beta1 360 kind: DestinationRule 361 metadata: 362 name: reviews-cb-policy 363 spec: 364 host: reviews.prod.svc.cluster.local 365 trafficPolicy: 366 outlierDetection: 367 consecutiveErrors: 7 368 ` 369 invalidYAML = ` 370 (...!)` 371 validKubernetesYAML = ` 372 apiVersion: v1 373 kind: Namespace 374 metadata: 375 name: istio-system 376 ` 377 validKubernetesJSON = `{ 378 "apiVersion": "v1", 379 "kind": "Namespace", 380 "metadata": { 381 "name": "istio-system" 382 } 383 }` 384 invalidUnsupportedKey = ` 385 apiVersion: networking.istio.io/v1alpha3 386 kind: DestinationRule 387 metadata: 388 name: productpage 389 unexpected_junk: 390 still_more_junk: 391 spec: 392 host: productpage` 393 versionLabelMissingDeployment = ` 394 apiVersion: apps/v1 395 kind: Deployment 396 metadata: 397 name: hello 398 spec:` 399 skippedDeployment = ` 400 apiVersion: apps/v1 401 kind: Deployment 402 metadata: 403 name: hello 404 namespace: istio-system 405 spec: ~` 406 invalidIstioConfig = ` 407 apiVersion: install.istio.io/v1alpha1 408 kind: IstioOperator 409 metadata: 410 namespace: istio-system 411 name: example-istiocontrolplane 412 spec: 413 dummy: 414 traffic_management: 415 components: 416 namespace: istio-traffic-management 417 ` 418 validIstioConfig = ` 419 apiVersion: install.istio.io/v1alpha1 420 kind: IstioOperator 421 metadata: 422 namespace: istio-system 423 name: example-istiocontrolplane 424 spec: 425 addonComponents: 426 grafana: 427 enabled: true 428 ` 429 invalidDuplicateKey = ` 430 apiVersion: networking.istio.io/v1alpha3 431 kind: DestinationRule 432 metadata: 433 name: productpage 434 spec: 435 trafficPolicy: {} 436 trafficPolicy: 437 tls: 438 mode: ISTIO_MUTUAL 439 ` 440 validDeployment = ` 441 apiVersion: apps/v1 442 kind: Deployment 443 metadata: 444 name: helloworld-v1 445 labels: 446 app: helloworld 447 version: v1 448 spec: 449 replicas: 1 450 selector: 451 matchLabels: 452 app: helloworld 453 version: v1 454 template: 455 metadata: 456 annotations: 457 sidecar.istio.io/bootstrapOverride: "istio-custom-bootstrap-config" 458 labels: 459 app: helloworld 460 version: v1 461 spec: 462 containers: 463 - name: helloworld 464 image: docker.io/istio/examples-helloworld-v1 465 resources: 466 requests: 467 cpu: "100m" 468 imagePullPolicy: IfNotPresent 469 ports: 470 - containerPort: 5000 471 ` 472 validPortNamingSvcWithAppProtocol = ` 473 apiVersion: v1 474 kind: Service 475 metadata: 476 name: hello 477 spec: 478 ports: 479 - appProtocol: http 480 port: 9080` 481 validPortNamingSvcWithAppProtocol2 = ` 482 apiVersion: v1 483 kind: Service 484 metadata: 485 name: hello 486 spec: 487 ports: 488 - appProtocol: http 489 name: fake # should not have error since the appProtocol field 490 port: 9080` 491 inValidPortNamingSvcWithAppProtocol = ` 492 apiVersion: v1 493 kind: Service 494 metadata: 495 name: hello 496 spec: 497 ports: 498 - appProtocol: fake 499 port: 9080` 500 validK8sRecommendedLabels = ` 501 apiVersion: v1 502 kind: Deployment 503 metadata: 504 name: helloworld-v1 505 labels: 506 app.kubernetes.io/name: helloworld 507 app.kubernetes.io/version: v1 508 spec: 509 replicas: 1 510 ` 511 validIstioCanonical = ` 512 apiVersion: v1 513 kind: Deployment 514 metadata: 515 name: helloworld-v1 516 labels: 517 service.istio.io/canonical-name: helloworld 518 service.istio.io/canonical-revision: v1 519 spec: 520 replicas: 1 521 ` 522 ) 523 524 func fromYAML(in string) *unstructured.Unstructured { 525 var un unstructured.Unstructured 526 if err := yaml.Unmarshal([]byte(in), &un); err != nil { 527 panic(err) 528 } 529 return &un 530 } 531 532 func TestValidateResource(t *testing.T) { 533 cases := []struct { 534 name string 535 in string 536 valid bool 537 warn bool 538 }{ 539 { 540 name: "valid pilot configuration", 541 in: validVirtualServiceYAML, 542 valid: true, 543 }, 544 { 545 name: "invalid pilot configuration", 546 in: invalidVirtualServiceYAML, 547 valid: false, 548 }, 549 { 550 name: "invalid pilot configuration v1beta1", 551 in: invalidVirtualServiceV1Beta1, 552 valid: false, 553 }, 554 { 555 name: "port name missing service", 556 in: portNameMissingSvc, 557 valid: false, 558 }, 559 { 560 name: "version label missing deployment", 561 in: versionLabelMissingDeployment, 562 valid: true, 563 }, 564 { 565 name: "valid port naming service", 566 in: validPortNamingSvc, 567 valid: true, 568 }, 569 { 570 name: "valid port naming with suffix service", 571 in: validPortNamingWithSuffixSvc, 572 valid: true, 573 }, 574 { 575 name: "invalid port naming service", 576 in: invalidPortNamingSvc, 577 valid: false, 578 }, 579 { 580 name: "invalid service list", 581 in: invalidSvcList, 582 valid: false, 583 }, 584 { 585 name: "valid deployment list", 586 in: validDeploymentList, 587 valid: true, 588 }, 589 { 590 name: "skip validating deployment", 591 in: skippedDeployment, 592 valid: true, 593 }, 594 { 595 name: "skip validating service", 596 in: skippedService, 597 valid: true, 598 }, 599 { 600 name: "service with udp port", 601 in: udpService, 602 valid: true, 603 }, 604 { 605 name: "invalid Istio Operator config", 606 in: invalidIstioConfig, 607 valid: false, 608 }, 609 { 610 name: "valid Istio Operator config", 611 in: validIstioConfig, 612 valid: true, 613 }, 614 { 615 name: "warning", 616 in: warnDestinationRule, 617 valid: true, 618 warn: true, 619 }, 620 { 621 name: "exportTo=.", 622 in: validVirtualService2YAML, 623 valid: true, 624 }, 625 { 626 name: "appProtocol=http", 627 in: validPortNamingSvcWithAppProtocol, 628 valid: true, 629 }, 630 { 631 name: "appProtocol=http,name=fake", 632 in: validPortNamingSvcWithAppProtocol2, 633 valid: true, 634 }, 635 { 636 name: "appProtocol=fake", 637 in: inValidPortNamingSvcWithAppProtocol, 638 valid: false, 639 }, 640 { 641 name: "metric labels k8s recommended", 642 in: validK8sRecommendedLabels, 643 valid: true, 644 }, 645 { 646 name: "metric labels istio canonical", 647 in: validIstioCanonical, 648 valid: true, 649 }, 650 } 651 652 for i, c := range cases { 653 t.Run(fmt.Sprintf("[%v] %v ", i, c.name), func(tt *testing.T) { 654 defer func() { recover() }() 655 v := &validator{} 656 var writer io.Writer 657 warn, err := v.validateResource("istio-system", "", fromYAML(c.in), writer) 658 if (err == nil) != c.valid { 659 tt.Fatalf("unexpected validation result: got %v want %v: err=%v", err == nil, c.valid, err) 660 } 661 if (warn != nil) != c.warn { 662 tt.Fatalf("unexpected validation warning result: got %v want %v: warn=%v", warn != nil, c.warn, warn) 663 } 664 }) 665 } 666 } 667 668 func buildMultiDocConfig(docs []string) string { 669 var b strings.Builder 670 for _, r := range docs { 671 if r != "" { 672 b.WriteString(strings.Trim(r, " \t\n")) 673 } 674 b.WriteString("\n---\n") 675 } 676 return b.String() 677 } 678 679 func createTestFile(t *testing.T, data string) (string, io.Closer) { 680 t.Helper() 681 validFile, err := os.CreateTemp("", "TestValidateCommand") 682 if err != nil { 683 t.Fatal(err) 684 } 685 if _, err := validFile.WriteString(data); err != nil { 686 t.Fatal(err) 687 } 688 return validFile.Name(), validFile 689 } 690 691 func createTestDirectory(t *testing.T, files map[string]string) string { 692 t.Helper() 693 tempDir, err := os.MkdirTemp("", "TestValidateCommand") 694 if err != nil { 695 t.Fatal(err) 696 } 697 698 for name, content := range files { 699 filePath := filepath.Join(tempDir, name) 700 if err := os.WriteFile(filePath, []byte(content), 0o644); err != nil { 701 t.Fatal(err) 702 } 703 } 704 705 return tempDir 706 } 707 708 func TestValidateCommand(t *testing.T) { 709 validYAML := buildMultiDocConfig([]string{validVirtualServiceYAML, validVirtualService1YAML}) 710 invalidYAML := buildMultiDocConfig([]string{invalidVirtualServiceYAML, validVirtualService1YAML}) 711 warningsYAML := buildMultiDocConfig([]string{invalidVirtualServiceYAML, validVirtualService1YAML, warnDestinationRule}) 712 713 validJSON := buildMultiDocConfig([]string{validVirtualServiceJSON, validVirtualService1JSON}) 714 invalidJSON := buildMultiDocConfig([]string{invalidVirtualServiceJSON, validVirtualService1JSON}) 715 warningsJSON := buildMultiDocConfig([]string{invalidVirtualServiceYAML, validVirtualService1YAML, warnDestinationRule}) 716 717 validFilenameYAML, closeValidYAMLFile := createTestFile(t, validYAML) 718 defer closeValidYAMLFile.Close() 719 720 validFilenameJSON, closeValidJSONFile := createTestFile(t, validJSON) 721 defer closeValidJSONFile.Close() 722 723 invalidFilenameYAML, closeInvalidYAMLFile := createTestFile(t, invalidYAML) 724 defer closeInvalidYAMLFile.Close() 725 726 warningFilename, closeWarningFile := createTestFile(t, warningsYAML) 727 defer closeWarningFile.Close() 728 729 invalidYAMLFile, closeInvalidYAMLFile := createTestFile(t, invalidYAML) 730 defer closeInvalidYAMLFile.Close() 731 732 validKubernetesYAMLFile, closeKubernetesYAMLFile := createTestFile(t, validKubernetesYAML) 733 defer closeKubernetesYAMLFile.Close() 734 735 validKubernetesJSONFile, closeKubernetesJSONFile := createTestFile(t, validKubernetesYAML) 736 defer closeKubernetesJSONFile.Close() 737 738 versionLabelMissingDeploymentFile, closeVersionLabelMissingDeploymentFile := createTestFile(t, versionLabelMissingDeployment) 739 defer closeVersionLabelMissingDeploymentFile.Close() 740 741 portNameMissingSvcFile, closePortNameMissingSvcFile := createTestFile(t, portNameMissingSvc) 742 defer closePortNameMissingSvcFile.Close() 743 744 unsupportedKeyFilename, closeUnsupportedKeyFile := createTestFile(t, invalidUnsupportedKey) 745 defer closeUnsupportedKeyFile.Close() 746 747 duplicateKeyFilename, closeUnsupportedKeyFile := createTestFile(t, invalidDuplicateKey) 748 defer closeUnsupportedKeyFile.Close() 749 750 validPortNamingSvcFile, closeValidPortNamingSvcFile := createTestFile(t, validPortNamingSvc) 751 defer closeValidPortNamingSvcFile.Close() 752 753 validPortNamingWithSuffixSvcFile, closeValidPortNamingWithSuffixSvcFile := createTestFile(t, validPortNamingWithSuffixSvc) 754 defer closeValidPortNamingWithSuffixSvcFile.Close() 755 756 invalidPortNamingSvcFile, closeInvalidPortNamingSvcFile := createTestFile(t, invalidPortNamingSvc) 757 defer closeInvalidPortNamingSvcFile.Close() 758 759 tempDirYAML := createTestDirectory(t, map[string]string{ 760 "valid.yaml": validYAML, 761 "invalid.yaml": invalidYAML, 762 "warning.yaml": warningsYAML, 763 "invalidYAML.yaml": invalidYAML, 764 }) 765 validTempDirYAML := createTestDirectory(t, map[string]string{ 766 "valid.yaml": validYAML, 767 }) 768 769 tempDirJSON := createTestDirectory(t, map[string]string{ 770 "valid.json": validJSON, 771 "invalid.json": invalidJSON, 772 "warning.json": warningsJSON, 773 "invalidYAML.json": invalidJSON, 774 }) 775 validTempDirJSON := createTestDirectory(t, map[string]string{ 776 "valid.json": validJSON, 777 }) 778 779 t.Cleanup(func() { 780 os.RemoveAll(tempDirYAML) 781 os.RemoveAll(validTempDirYAML) 782 os.RemoveAll(tempDirJSON) 783 os.RemoveAll(validTempDirJSON) 784 }) 785 786 cases := []struct { 787 name string 788 args []string 789 wantError bool 790 expectedRegexp *regexp.Regexp // Expected regexp output 791 }{ 792 { 793 name: "valid port naming service", 794 args: []string{"--filename", validPortNamingSvcFile}, 795 wantError: false, 796 }, 797 { 798 name: "valid port naming with suffix service", 799 args: []string{"--filename", validPortNamingWithSuffixSvcFile}, 800 wantError: false, 801 }, 802 { 803 name: "invalid port naming service", 804 args: []string{"--filename", invalidPortNamingSvcFile}, 805 wantError: true, 806 }, 807 { 808 name: "filename missing", 809 wantError: true, 810 }, 811 { 812 name: "valid resources from file", 813 args: []string{"--filename", validFilenameYAML}, 814 }, 815 { 816 name: "extra args", 817 args: []string{"--filename", validFilenameYAML, "extra-arg"}, 818 wantError: true, 819 }, 820 { 821 name: "invalid resources from file", 822 args: []string{"--filename", invalidFilenameYAML}, 823 wantError: true, 824 }, 825 { 826 name: "invalid filename", 827 args: []string{"--filename", "INVALID_FILE_NAME"}, 828 wantError: true, 829 }, 830 { 831 name: "invalid YAML", 832 args: []string{"--filename", invalidYAMLFile}, 833 wantError: true, 834 }, 835 { 836 name: "valid Kubernetes YAML", 837 args: []string{"--filename", validKubernetesYAMLFile}, 838 expectedRegexp: regexp.MustCompile(`^".*" is valid 839 $`), 840 wantError: false, 841 }, 842 { 843 name: "valid Kubernetes JSON", 844 args: []string{"--filename", validKubernetesJSONFile}, 845 expectedRegexp: regexp.MustCompile(`^".*" is valid 846 $`), 847 wantError: false, 848 }, 849 { 850 name: "invalid top-level key", 851 args: []string{"--filename", unsupportedKeyFilename}, 852 expectedRegexp: regexp.MustCompile(`.*unknown field "unexpected_junk"`), 853 wantError: true, 854 }, 855 { 856 name: "version label missing deployment", 857 args: []string{"--filename", versionLabelMissingDeploymentFile}, 858 wantError: false, 859 }, 860 { 861 name: "port name missing service", 862 args: []string{"--filename", portNameMissingSvcFile}, 863 wantError: true, 864 }, 865 { 866 name: "duplicate key", 867 args: []string{"--filename", duplicateKeyFilename}, 868 expectedRegexp: regexp.MustCompile(`.*key ".*" already set`), 869 wantError: true, 870 }, 871 { 872 name: "warning", 873 args: []string{"--filename", warningFilename}, 874 expectedRegexp: regexp.MustCompile(`(?m)".*" has warnings: 875 \* DestinationRule//reviews-cb-policy: outlier detection consecutive errors is deprecated, use consecutiveGatewayErrors or consecutive5xxErrors instead 876 877 Error: 1 error occurred: 878 \* VirtualService//invalid-virtual-service: weight -15 < 0`), 879 wantError: true, 880 }, 881 { 882 name: "validate all yaml files in a directory", 883 args: []string{"--filename", tempDirYAML}, 884 wantError: true, // Since the directory has invalid files 885 }, 886 { 887 name: "validate valid yaml files in a directory", 888 args: []string{"--filename", validTempDirYAML}, 889 wantError: false, 890 }, 891 { 892 name: "validate combination of yaml files and directories with valid files", 893 args: []string{"--filename", validFilenameYAML, "--filename", validTempDirYAML}, 894 wantError: false, 895 }, 896 { 897 name: "validate combination of yaml files and directories with valid files and invalid files", 898 args: []string{"--filename", validFilenameYAML, "--filename", tempDirYAML, "--filename", validTempDirYAML}, 899 wantError: true, // Since the directory has invalid files 900 }, 901 { 902 name: "validate all json files in a directory", 903 args: []string{"--filename", tempDirJSON}, 904 wantError: true, // Since the directory has invalid files 905 }, 906 { 907 name: "validate valid json files in a directory", 908 args: []string{"--filename", validTempDirJSON}, 909 wantError: false, 910 }, 911 { 912 name: "validate combination of json files and directories with valid files", 913 args: []string{"--filename", validFilenameJSON, "--filename", validTempDirJSON}, 914 wantError: false, 915 }, 916 { 917 name: "validate combination of json files and directories with valid files and invalid files", 918 args: []string{"--filename", validFilenameJSON, "--filename", tempDirJSON, "--filename", validTempDirJSON}, 919 wantError: true, // Since the directory has invalid files 920 }, 921 { 922 name: "validate mix of yaml and json directories with valid files", 923 args: []string{"--filename", validTempDirYAML, "--filename", validTempDirJSON}, 924 wantError: false, 925 }, 926 { 927 name: "validate combination of yaml and json and yaml directories with invalid files", 928 args: []string{"--filename", tempDirYAML, "--filename", tempDirJSON}, 929 wantError: true, // Since the directory has invalid files 930 }, 931 } 932 for i, c := range cases { 933 t.Run(fmt.Sprintf("[%v] %v", i, c.name), func(t *testing.T) { 934 ctx := cli.NewFakeContext(&cli.NewFakeContextOption{ 935 IstioNamespace: "istio-system", 936 }) 937 validateCmd := NewValidateCommand(ctx) 938 validateCmd.SilenceUsage = true 939 validateCmd.SetArgs(c.args) 940 941 // capture output to keep test logs clean 942 var out bytes.Buffer 943 validateCmd.SetOut(&out) 944 validateCmd.SetErr(&out) 945 946 err := validateCmd.Execute() 947 if (err != nil) != c.wantError { 948 t.Errorf("unexpected validate return status: got %v want %v: \nerr=%v", 949 err != nil, c.wantError, err) 950 } 951 output := out.String() 952 if c.expectedRegexp != nil && !c.expectedRegexp.MatchString(output) { 953 t.Errorf("Output didn't match for 'istioctl %s'\n got %v\nwant: %v", 954 strings.Join(c.args, " "), output, c.expectedRegexp) 955 } 956 }) 957 } 958 } 959 960 func TestGetTemplateLabels(t *testing.T) { 961 un := fromYAML(validDeployment) 962 963 labels, err := GetTemplateLabels(un) 964 if err != nil { 965 t.Fatal(err) 966 } 967 assert.Equal(t, labels, map[string]string{ 968 "app": "helloworld", 969 "version": "v1", 970 }) 971 }