istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/integration/security/util/reachability/context.go (about) 1 //go:build integ 2 // +build integ 3 4 // Copyright Istio Authors 5 // 6 // Licensed under the Apache License, Version 2.0 (the "License"); 7 // you may not use this file except in compliance with the License. 8 // You may obtain a copy of the License at 9 // 10 // http://www.apache.org/licenses/LICENSE-2.0 11 // 12 // Unless required by applicable law or agreed to in writing, software 13 // distributed under the License is distributed on an "AS IS" BASIS, 14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 // See the License for the specific language governing permissions and 16 // limitations under the License. 17 18 package reachability 19 20 import ( 21 "fmt" 22 "path/filepath" 23 "strings" 24 "time" 25 26 "istio.io/istio/pkg/test/echo/common/scheme" 27 "istio.io/istio/pkg/test/framework" 28 "istio.io/istio/pkg/test/framework/components/echo" 29 "istio.io/istio/pkg/test/framework/components/echo/check" 30 "istio.io/istio/pkg/test/framework/components/echo/common/deployment" 31 "istio.io/istio/pkg/test/framework/components/echo/match" 32 "istio.io/istio/pkg/test/framework/components/namespace" 33 "istio.io/istio/pkg/test/framework/resource/config/apply" 34 "istio.io/istio/pkg/test/util/retry" 35 ) 36 37 // TestCase represents reachability test cases. 38 type TestCase struct { 39 // ConfigFile is the name of the yaml contains the authentication policy and destination rule CRs 40 // that are needed for the test setup. 41 // The file is expected in the tests/integration/security/reachability/testdata folder. 42 ConfigFile string 43 Namespace namespace.Instance 44 45 // CallOpts specified the call options for destination service. If not specified, use the default 46 // framework provided ones. 47 CallOpts []echo.CallOptions 48 49 // Indicates whether a test should be created for the given configuration. 50 Include func(from echo.Instance, opts echo.CallOptions) bool 51 52 // Indicates whether the test should expect a successful response. 53 ExpectSuccess func(from echo.Instance, opts echo.CallOptions) bool 54 55 // Allows filtering the destinations we expect to reach (optional). 56 ExpectDestinations func(from echo.Instance, to echo.Target) echo.Instances 57 58 // Indicates whether the test should expect a MTLS response. 59 ExpectMTLS func(from echo.Instance, opts echo.CallOptions) bool 60 61 // Indicates whether a test should be run in the multicluster environment. 62 // This is a temporary flag during the converting tests into multicluster supported. 63 // TODO: Remove this flag when all tests support multicluster 64 SkippedForMulticluster bool 65 } 66 67 var ( 68 A echo.Instances 69 B echo.Instances 70 C echo.Instances 71 D echo.Instances 72 E echo.Instances 73 Multiversion echo.Instances 74 VM echo.Instances 75 External echo.Instances 76 Naked echo.Instances 77 Headless echo.Instances 78 HeadlessNaked echo.Instances 79 ) 80 81 const ( 82 ASvc = "a" 83 BSvc = "b" 84 CSvc = "c" 85 DSvc = "d" 86 ESvc = "e" 87 MultiversionSvc = "multiversion" 88 VMSvc = "vm" 89 HeadlessSvc = "headless" 90 NakedSvc = "naked" 91 HeadlessNakedSvc = "headless-naked" 92 ExternalSvc = "external" 93 ) 94 95 // Run runs the given reachability test cases with the context. 96 func Run(testCases []TestCase, t framework.TestContext) { 97 callOptions := []echo.CallOptions{ 98 { 99 Port: echo.Port{ 100 Name: "http", 101 }, 102 Scheme: scheme.HTTP, 103 }, 104 { 105 Port: echo.Port{ 106 Name: "http", 107 }, 108 Scheme: scheme.WebSocket, 109 }, 110 { 111 Port: echo.Port{ 112 Name: "tcp", 113 }, 114 Scheme: scheme.TCP, 115 }, 116 { 117 Port: echo.Port{ 118 Name: "grpc", 119 }, 120 Scheme: scheme.GRPC, 121 }, 122 { 123 Port: echo.Port{ 124 Name: "https", 125 }, 126 Scheme: scheme.HTTPS, 127 }, 128 } 129 130 for _, c := range testCases { 131 // Create a copy to avoid races, as tests are run in parallel 132 c := c 133 testName := strings.TrimSuffix(c.ConfigFile, filepath.Ext(c.ConfigFile)) 134 t.NewSubTest(testName).Run(func(t framework.TestContext) { 135 // Apply the policy. 136 cfg := t.ConfigIstio().File(c.Namespace.Name(), filepath.Join("./testdata", c.ConfigFile)) 137 retry.UntilSuccessOrFail(t, func() error { 138 t.Logf("[%s] [%v] Apply config %s", testName, time.Now(), c.ConfigFile) 139 // TODO(https://github.com/istio/istio/issues/20460) We shouldn't need a retry loop 140 return cfg.Apply(apply.Wait) 141 }) 142 for _, clients := range []echo.Instances{A, B, Headless, Naked, HeadlessNaked} { 143 for _, from := range clients { 144 from := from 145 t.NewSubTest(fmt.Sprintf("%s in %s", 146 from.Config().Service, from.Config().Cluster.StableName())).Run(func(t framework.TestContext) { 147 destinationSets := []echo.Instances{ 148 A, 149 B, 150 // only hit same network headless services 151 match.Network(from.Config().Cluster.NetworkName()).GetMatches(Headless), 152 // only hit same cluster multiversion services 153 match.Cluster(from.Config().Cluster).GetMatches(Multiversion), 154 // only hit same cluster naked services 155 match.Cluster(from.Config().Cluster).GetMatches(Naked), 156 VM, 157 } 158 159 for _, to := range destinationSets { 160 to := to 161 if c.ExpectDestinations != nil { 162 to = c.ExpectDestinations(from, to) 163 } 164 toClusters := to.Clusters() 165 if len(toClusters) == 0 { 166 continue 167 } 168 // grabbing the 0th assumes all echos in destinations have the same service name 169 if isNakedToVM(from, to) { 170 // No need to waste time on these tests which will time out on connection instead of fail-fast 171 continue 172 } 173 174 copts := &callOptions 175 // If test case specified service call options, use that instead. 176 if c.CallOpts != nil { 177 copts = &c.CallOpts 178 } 179 for _, opts := range *copts { 180 // Copy the loop variables so they won't change for the subtests. 181 opts := opts 182 183 // Set the target on the call options. 184 opts.To = to 185 if len(toClusters) == 1 { 186 opts.Count = 1 187 } 188 189 // TODO(https://github.com/istio/istio/issues/37629) go back to converge 190 opts.Retry.Options = []retry.Option{retry.Converge(1)} 191 // TODO(https://github.com/istio/istio/issues/37629) go back to 5s 192 opts.Timeout = time.Second * 10 193 194 expectSuccess := c.ExpectSuccess(from, opts) 195 expectMTLS := c.ExpectMTLS(from, opts) 196 var tpe string 197 if expectSuccess { 198 tpe = "positive" 199 opts.Check = check.And( 200 check.OK(), 201 check.ReachedTargetClusters(t)) 202 if expectMTLS { 203 opts.Check = check.And(opts.Check, 204 check.MTLSForHTTP()) 205 } 206 } else { 207 tpe = "negative" 208 opts.Check = check.NotOK() 209 } 210 include := c.Include 211 if include == nil { 212 include = func(_ echo.Instance, _ echo.CallOptions) bool { return true } 213 } 214 if include(from, opts) { 215 subTestName := fmt.Sprintf("%s to %s:%s%s %s", 216 opts.Scheme, 217 to.Config().Service, 218 opts.Port.Name, 219 opts.HTTP.Path, 220 tpe) 221 222 t.NewSubTest(subTestName). 223 Run(func(t framework.TestContext) { 224 if (from.Config().IsNaked()) && len(toClusters) > 1 { 225 // TODO use echotest to generate the cases that would work for multi-network + naked 226 t.Skip("https://github.com/istio/istio/issues/37307") 227 } 228 229 from.CallOrFail(t, opts) 230 }) 231 } 232 } 233 } 234 }) 235 } 236 } 237 }) 238 } 239 } 240 241 // Exclude calls from naked->VM since naked has no Envoy 242 // However, no endpoint exists for VM in k8s, so calls from naked->VM will fail, regardless of mTLS 243 func isNakedToVM(from echo.Instance, to echo.Target) bool { 244 return from.Config().IsNaked() && to.Config().IsVM() 245 } 246 247 func CreateCustomInstances(apps *deployment.SingleNamespaceView) error { 248 for index, namespacedName := range apps.EchoNamespace.All.NamespacedNames() { 249 switch { 250 case namespacedName.Name == ASvc: 251 A = apps.EchoNamespace.All[index] 252 case namespacedName.Name == BSvc: 253 B = apps.EchoNamespace.All[index] 254 case namespacedName.Name == CSvc: 255 C = apps.EchoNamespace.All[index] 256 case namespacedName.Name == DSvc: 257 D = apps.EchoNamespace.All[index] 258 case namespacedName.Name == ESvc: 259 E = apps.EchoNamespace.All[index] 260 case namespacedName.Name == HeadlessSvc: 261 Headless = apps.EchoNamespace.All[index] 262 case namespacedName.Name == HeadlessNakedSvc: 263 HeadlessNaked = apps.EchoNamespace.All[index] 264 case namespacedName.Name == ExternalSvc: 265 External = apps.EchoNamespace.All[index] 266 case namespacedName.Name == NakedSvc: 267 Naked = apps.EchoNamespace.All[index] 268 case namespacedName.Name == VMSvc: 269 VM = apps.EchoNamespace.All[index] 270 case namespacedName.Name == MultiversionSvc: 271 Multiversion = apps.EchoNamespace.All[index] 272 } 273 } 274 return nil 275 }