istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/analysis/analyzers/multicluster_analyzers_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 analyzers 16 17 import ( 18 "fmt" 19 "os" 20 "testing" 21 22 . "github.com/onsi/gomega" 23 24 "istio.io/istio/pilot/pkg/config/file" 25 "istio.io/istio/pkg/cluster" 26 "istio.io/istio/pkg/config" 27 "istio.io/istio/pkg/config/analysis" 28 "istio.io/istio/pkg/config/analysis/analyzers/multicluster" 29 "istio.io/istio/pkg/config/analysis/local" 30 "istio.io/istio/pkg/config/analysis/msg" 31 "istio.io/istio/pkg/config/schema/collections" 32 "istio.io/istio/pkg/kube" 33 ) 34 35 type mcTestCase struct { 36 name string 37 cluster1InputFiles []string 38 cluster2InputFiles []string 39 analyzer analysis.Analyzer 40 expected []message 41 } 42 43 var mcTestGrid = []mcTestCase{ 44 { 45 name: "InconsistentMultiClusterService", 46 cluster1InputFiles: []string{ 47 "testdata/multicluster/inconsistent-service-1.yaml", 48 }, 49 cluster2InputFiles: []string{ 50 "testdata/multicluster/inconsistent-service-2.yaml", 51 }, 52 analyzer: &multicluster.ServiceAnalyzer{}, 53 expected: []message{ 54 {msg.MultiClusterInconsistentService, "Service my-namespace/extra-port"}, 55 {msg.MultiClusterInconsistentService, "Service my-namespace/inconsistent-port-name"}, 56 {msg.MultiClusterInconsistentService, "Service my-namespace/mixed-mode"}, 57 {msg.MultiClusterInconsistentService, "Service my-namespace/mixed-type"}, 58 {msg.MultiClusterInconsistentService, "Service my-namespace/mixed-port-protocol"}, 59 }, 60 }, 61 } 62 63 // TestMultiClusterAnalyzers sets up two clusters and runs the multi-cluster analyzers on them. 64 func TestMultiClusterAnalyzers(t *testing.T) { 65 requestedInputsByAnalyzer := make(map[string]map[config.GroupVersionKind]struct{}) 66 // For each test case, verify we get the expected messages as output 67 for _, tc := range mcTestGrid { 68 tc := tc // Capture range variable so subtests work correctly 69 t.Run(tc.name, func(t *testing.T) { 70 g := NewWithT(t) 71 72 // Set up a hook to record which collections are accessed by each analyzer 73 analyzerName := tc.analyzer.Metadata().Name 74 cr := func(col config.GroupVersionKind) { 75 if _, ok := requestedInputsByAnalyzer[analyzerName]; !ok { 76 requestedInputsByAnalyzer[analyzerName] = make(map[config.GroupVersionKind]struct{}) 77 } 78 requestedInputsByAnalyzer[analyzerName][col] = struct{}{} 79 } 80 81 // Set up Analyzer for this test case 82 sa, err := setupMultiClusterEnvironmentForCase(tc, cr) 83 if err != nil { 84 t.Fatalf("Error setting up analysis for testcase %s: %v", tc.name, err) 85 } 86 87 // Run the analysis 88 result, err := runAnalyzer(sa) 89 if err != nil { 90 t.Fatalf("Error running analysis on testcase %s: %v", tc.name, err) 91 } 92 93 g.Expect(extractFields(result.Messages)).To(ConsistOf(tc.expected), "%v", prettyPrintMessages(result.Messages)) 94 }) 95 } 96 } 97 98 func setupMultiClusterEnvironmentForCase(tc mcTestCase, cr local.CollectionReporterFn) (*local.IstiodAnalyzer, error) { 99 sa := local.NewIstiodAnalyzer(AllMultiClusterCombined(), "", "istio-system", cr) 100 101 // Add the test files to the fake client 102 if err := addStore(sa, "cluster1", tc.cluster1InputFiles); err != nil { 103 return nil, err 104 } 105 if err := addStore(sa, "cluster2", tc.cluster2InputFiles); err != nil { 106 return nil, err 107 } 108 109 // Include default resources 110 if err := sa.AddDefaultResources(); err != nil { 111 return nil, fmt.Errorf("error adding default resources: %v", err) 112 } 113 return sa, nil 114 } 115 116 type fakeClientImpl struct { 117 kube.CLIClient 118 clusterID cluster.ID 119 } 120 121 func (f *fakeClientImpl) ClusterID() cluster.ID { 122 return f.clusterID 123 } 124 125 func addStore(sa *local.IstiodAnalyzer, clusterName string, yamls []string) error { 126 client := &fakeClientImpl{ 127 CLIClient: kube.NewFakeClient(), 128 clusterID: cluster.ID(clusterName), 129 } 130 // Gather test files 131 src := file.NewKubeSource(collections.All) 132 for i, yamlFile := range yamls { 133 data, err := os.ReadFile(yamlFile) 134 if err != nil { 135 return err 136 } 137 err = src.ApplyContent(fmt.Sprintf("%d", i), string(data)) 138 if err != nil { 139 return err 140 } 141 } 142 sa.AddSourceForCluster(src, cluster.ID(clusterName)) 143 sa.AddRunningKubeSourceWithRevision(client, clusterName, false) 144 return nil 145 }