istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/integration/security/reachability_test.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 security
    19  
    20  import (
    21  	"testing"
    22  
    23  	"istio.io/api/annotation"
    24  	"istio.io/istio/pilot/pkg/model"
    25  	"istio.io/istio/pkg/test/echo/common/scheme"
    26  	"istio.io/istio/pkg/test/framework"
    27  	"istio.io/istio/pkg/test/framework/components/cluster"
    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/ports"
    31  	"istio.io/istio/pkg/test/framework/components/echo/config"
    32  	"istio.io/istio/pkg/test/framework/components/echo/config/param"
    33  	"istio.io/istio/pkg/test/framework/components/echo/deployment"
    34  	"istio.io/istio/pkg/test/framework/components/echo/echotest"
    35  	"istio.io/istio/pkg/test/framework/components/echo/match"
    36  	"istio.io/istio/pkg/test/framework/components/istio"
    37  	"istio.io/istio/pkg/test/framework/resource"
    38  )
    39  
    40  const (
    41  	migrationServiceName     = "migration"
    42  	migrationVersionIstio    = "vistio"
    43  	migrationVersionNonIstio = "vlegacy"
    44  	migrationPathIstio       = "/" + migrationVersionIstio
    45  	migrationPathNonIstio    = "/" + migrationVersionNonIstio
    46  	mtlsModeParam            = "MTLSMode"
    47  	mtlsModeOverrideParam    = "MTLSModeOverride"
    48  	tlsModeParam             = "TLSMode"
    49  	cMinIstioVersion         = "1.15.0"
    50  	// cMinIstioVersionDS       = "1.16.0"
    51  )
    52  
    53  func TestReachability(t *testing.T) {
    54  	framework.NewTest(t).
    55  		Run(func(t framework.TestContext) {
    56  			systemNS := istio.ClaimSystemNamespaceOrFail(t, t)
    57  
    58  			integIstioVersion := cMinIstioVersion
    59  			var migrationApp echo.Instances
    60  			// if dual stack is enabled, a dual stack echo config should be added
    61  			if !t.Settings().EnableDualStack {
    62  				// Create a custom echo deployment in NS1 with subsets that allows us to test the
    63  				// migration of a workload to istio (from no sidecar to sidecar).
    64  				migrationApp = deployment.New(t).
    65  					WithClusters(t.Clusters()...).WithConfig(echo.Config{
    66  					Namespace:      echo1NS,
    67  					Service:        migrationServiceName,
    68  					ServiceAccount: true,
    69  					Ports:          ports.All(),
    70  					Subsets: []echo.SubsetConfig{
    71  						{
    72  							// Istio deployment, with sidecar.
    73  							Version:     migrationVersionIstio,
    74  							Annotations: map[string]string{annotation.SidecarInject.Name: "true"},
    75  						},
    76  						{
    77  							// Legacy (non-Istio) deployment subset, does not have sidecar injected.
    78  							Version:     migrationVersionNonIstio,
    79  							Annotations: map[string]string{annotation.SidecarInject.Name: "false"},
    80  						},
    81  					},
    82  				}).BuildOrFail(t)
    83  			} else {
    84  				// TODO: remove the MinIstioVersion setting for dual stack integration test for next line
    85  				// integIstioVersion = cMinIstioVersionDS
    86  				// Create a custom echo deployment in NS1 with subsets that allows us to test the
    87  				// migration of a workload to istio (from no sidecar to sidecar).
    88  				migrationApp = deployment.New(t).
    89  					WithClusters(t.Clusters()...).WithConfig(echo.Config{
    90  					Namespace:      echo1NS,
    91  					Service:        migrationServiceName,
    92  					ServiceAccount: true,
    93  					Ports:          ports.All(),
    94  					Subsets: []echo.SubsetConfig{
    95  						{
    96  							// Istio deployment, with sidecar.
    97  							Version:     migrationVersionIstio,
    98  							Annotations: map[string]string{annotation.SidecarInject.Name: "true"},
    99  						},
   100  						{
   101  							// Legacy (non-Istio) deployment subset, does not have sidecar injected.
   102  							Version:     migrationVersionNonIstio,
   103  							Annotations: map[string]string{annotation.SidecarInject.Name: "false"},
   104  						},
   105  					},
   106  					IPFamilies:     "IPv4, IPv6",
   107  					IPFamilyPolicy: "RequireDualStack",
   108  				}).BuildOrFail(t)
   109  			}
   110  
   111  			// Add the migration app to the full list of services.
   112  			allServices := apps.Ns1.All.Append(migrationApp.Services())
   113  
   114  			// Create matchers for the migration app.
   115  			migration := match.ServiceName(migrationApp.NamespacedName())
   116  			notMigration := match.Not(migration)
   117  
   118  			// Call options to be used for tests using the migration app.
   119  			migrationOpts := []echo.CallOptions{
   120  				{
   121  					Port: echo.Port{
   122  						Name: ports.HTTP.Name,
   123  					},
   124  					HTTP: echo.HTTP{
   125  						Path: migrationPathIstio,
   126  					},
   127  				},
   128  				{
   129  					Port: echo.Port{
   130  						Name: ports.HTTP.Name,
   131  					},
   132  					HTTP: echo.HTTP{
   133  						Path: migrationPathNonIstio,
   134  					},
   135  				},
   136  			}
   137  
   138  			cases := []struct {
   139  				name               string
   140  				configs            config.Sources
   141  				fromMatch          match.Matcher
   142  				toMatch            match.Matcher
   143  				callOpts           []echo.CallOptions
   144  				expectMTLS         condition
   145  				expectCrossCluster condition
   146  				expectCrossNetwork condition
   147  				expectSuccess      condition
   148  				// minIstioVersion allows conditionally skipping based on required version
   149  				minIstioVersion string
   150  			}{
   151  				{
   152  					name: "global mtls strict",
   153  					configs: config.Sources{
   154  						config.File("testdata/reachability/global-peer-authn.yaml.tmpl"),
   155  						config.File("testdata/reachability/global-dr.yaml.tmpl"),
   156  					}.WithParams(param.Params{
   157  						mtlsModeParam:            model.MTLSStrict.String(),
   158  						tlsModeParam:             "ISTIO_MUTUAL",
   159  						param.Namespace.String(): systemNS,
   160  					}),
   161  					fromMatch:          notMigration,
   162  					toMatch:            notMigration,
   163  					expectMTLS:         notNaked,
   164  					expectCrossCluster: notFromNaked,
   165  					expectCrossNetwork: notNaked,
   166  					expectSuccess:      notNaked,
   167  					minIstioVersion:    integIstioVersion,
   168  				},
   169  				{
   170  					name: "global mtls permissive",
   171  					configs: config.Sources{
   172  						config.File("testdata/reachability/global-peer-authn.yaml.tmpl"),
   173  						config.File("testdata/reachability/global-dr.yaml.tmpl"),
   174  					}.WithParams(param.Params{
   175  						mtlsModeParam:            model.MTLSPermissive.String(),
   176  						tlsModeParam:             "ISTIO_MUTUAL",
   177  						param.Namespace.String(): systemNS,
   178  					}),
   179  					fromMatch:          notMigration,
   180  					toMatch:            notMigration,
   181  					expectMTLS:         notNaked,
   182  					expectCrossCluster: notFromNaked,
   183  					expectCrossNetwork: notNaked,
   184  					expectSuccess:      notToNaked,
   185  					minIstioVersion:    integIstioVersion,
   186  				},
   187  				{
   188  					name: "global mtls disabled",
   189  					configs: config.Sources{
   190  						config.File("testdata/reachability/global-peer-authn.yaml.tmpl"),
   191  						config.File("testdata/reachability/global-dr.yaml.tmpl"),
   192  					}.WithParams(param.Params{
   193  						mtlsModeParam:            model.MTLSDisable.String(),
   194  						tlsModeParam:             "DISABLE",
   195  						param.Namespace.String(): systemNS,
   196  					}),
   197  					fromMatch:          notMigration,
   198  					toMatch:            notMigration,
   199  					expectMTLS:         never,
   200  					expectCrossCluster: notFromNaked,
   201  					expectCrossNetwork: never,
   202  					expectSuccess:      always,
   203  					minIstioVersion:    integIstioVersion,
   204  				},
   205  				{
   206  					name: "global plaintext to mtls permissive",
   207  					configs: config.Sources{
   208  						config.File("testdata/reachability/global-peer-authn.yaml.tmpl"),
   209  						config.File("testdata/reachability/global-dr.yaml.tmpl"),
   210  					}.WithParams(param.Params{
   211  						mtlsModeParam:            model.MTLSPermissive.String(),
   212  						tlsModeParam:             "DISABLE",
   213  						param.Namespace.String(): systemNS,
   214  					}),
   215  					fromMatch:          notMigration,
   216  					toMatch:            notMigration,
   217  					expectMTLS:         never,
   218  					expectCrossCluster: notFromNaked,
   219  					expectCrossNetwork: never,
   220  					expectSuccess:      always,
   221  					minIstioVersion:    integIstioVersion,
   222  				},
   223  				{
   224  					name: "global automtls strict",
   225  					configs: config.Sources{
   226  						// No DR is added for this test. enableAutoMtls is expected on by default.
   227  						config.File("testdata/reachability/global-peer-authn.yaml.tmpl"),
   228  					}.WithParams(param.Params{
   229  						mtlsModeParam:            model.MTLSStrict.String(),
   230  						param.Namespace.String(): systemNS,
   231  					}),
   232  					fromMatch:          notMigration,
   233  					toMatch:            notMigration,
   234  					expectMTLS:         notNaked,
   235  					expectCrossCluster: notFromNaked,
   236  					expectCrossNetwork: notNaked,
   237  					expectSuccess:      notFromNaked,
   238  				},
   239  				{
   240  					name: "global automtls disable",
   241  					configs: config.Sources{
   242  						// No DR is added for this test. enableAutoMtls is expected on by default.
   243  						config.File("testdata/reachability/global-peer-authn.yaml.tmpl"),
   244  					}.WithParams(param.Params{
   245  						mtlsModeParam:            model.MTLSDisable.String(),
   246  						param.Namespace.String(): systemNS,
   247  					}),
   248  					fromMatch:          notMigration,
   249  					toMatch:            notMigration,
   250  					expectMTLS:         never,
   251  					expectCrossCluster: notFromNaked,
   252  					expectCrossNetwork: never,
   253  					expectSuccess:      always,
   254  				},
   255  				{
   256  					name: "global automtls passthrough",
   257  					configs: config.Sources{
   258  						config.File("testdata/reachability/automtls-passthrough.yaml.tmpl"),
   259  					}.WithNamespace(systemNS),
   260  					fromMatch: notMigration,
   261  					// VM passthrough doesn't work. We will send traffic to the ClusterIP of
   262  					// the VM service, which will have 0 Endpoints. If we generated
   263  					// EndpointSlice's for VMs this might work.
   264  					toMatch:    match.And(match.NotVM, notMigration),
   265  					expectMTLS: notNaked,
   266  					// Since we are doing pass-through, all requests will stay in the same cluster,
   267  					// as we are bypassing Istio load balancing.
   268  					// TODO(https://github.com/istio/istio/issues/39700): Why does headless behave differently?
   269  					expectCrossCluster: and(notFromNaked, or(toHeadless, toStatefulSet)),
   270  					expectCrossNetwork: never,
   271  					expectSuccess:      always,
   272  					minIstioVersion:    integIstioVersion,
   273  				},
   274  				{
   275  					name: "global no peer authn",
   276  					configs: config.Sources{
   277  						config.File("testdata/reachability/global-dr.yaml.tmpl"),
   278  					}.WithParams(param.Params{
   279  						tlsModeParam:             "ISTIO_MUTUAL",
   280  						param.Namespace.String(): systemNS,
   281  					}),
   282  					fromMatch:          notMigration,
   283  					toMatch:            notMigration,
   284  					expectMTLS:         notNaked,
   285  					expectCrossCluster: notFromNaked,
   286  					expectCrossNetwork: notNaked,
   287  					expectSuccess:      notToNaked,
   288  					minIstioVersion:    integIstioVersion,
   289  				},
   290  				{
   291  					name: "mtls strict",
   292  					configs: config.Sources{
   293  						config.File("testdata/reachability/workload-peer-authn.yaml.tmpl"),
   294  						config.File("testdata/reachability/workload-dr.yaml.tmpl"),
   295  					}.WithParams(param.Params{
   296  						mtlsModeParam: model.MTLSStrict.String(),
   297  						tlsModeParam:  "ISTIO_MUTUAL",
   298  					}),
   299  					fromMatch:          notMigration,
   300  					toMatch:            notMigration,
   301  					expectMTLS:         notNaked,
   302  					expectCrossCluster: notFromNaked,
   303  					expectCrossNetwork: notNaked,
   304  					expectSuccess:      notNaked,
   305  				},
   306  				{
   307  					name: "mtls permissive",
   308  					configs: config.Sources{
   309  						config.File("testdata/reachability/workload-peer-authn.yaml.tmpl"),
   310  						config.File("testdata/reachability/workload-dr.yaml.tmpl"),
   311  					}.WithParams(param.Params{
   312  						mtlsModeParam: model.MTLSPermissive.String(),
   313  						tlsModeParam:  "ISTIO_MUTUAL",
   314  					}),
   315  					fromMatch:          notMigration,
   316  					toMatch:            notMigration,
   317  					expectMTLS:         notNaked,
   318  					expectCrossCluster: notFromNaked,
   319  					expectCrossNetwork: notNaked,
   320  					expectSuccess:      notToNaked,
   321  				},
   322  				{
   323  					name: "mtls disabled",
   324  					configs: config.Sources{
   325  						config.File("testdata/reachability/workload-peer-authn.yaml.tmpl"),
   326  						config.File("testdata/reachability/workload-dr.yaml.tmpl"),
   327  					}.WithParams(param.Params{
   328  						mtlsModeParam: model.MTLSDisable.String(),
   329  						tlsModeParam:  "DISABLE",
   330  					}),
   331  					fromMatch:          notMigration,
   332  					toMatch:            notMigration,
   333  					expectMTLS:         never,
   334  					expectCrossCluster: notFromNaked,
   335  					expectCrossNetwork: never,
   336  					expectSuccess:      always,
   337  				},
   338  				{
   339  					name: "mtls port override",
   340  					configs: config.Sources{
   341  						config.File("testdata/reachability/workload-peer-authn-port-override.yaml.tmpl"),
   342  					}.WithParams(param.Params{
   343  						mtlsModeParam:         model.MTLSStrict.String(),
   344  						mtlsModeOverrideParam: model.MTLSDisable.String(),
   345  					}),
   346  					fromMatch: notMigration,
   347  					// TODO(https://github.com/istio/istio/issues/39439):
   348  					toMatch:            match.And(match.NotHeadless, notMigration),
   349  					expectMTLS:         never,
   350  					expectCrossCluster: notFromNaked,
   351  					expectCrossNetwork: never,
   352  					expectSuccess:      always,
   353  				},
   354  
   355  				// --------start of auto mtls partial test cases ---------------
   356  				// The follow three consecutive test together ensures the auto mtls works as intended
   357  				// for sidecar migration scenario.
   358  				{
   359  					name: "migration no tls",
   360  					configs: config.Sources{
   361  						config.File("testdata/reachability/global-peer-authn.yaml.tmpl"),
   362  						config.File("testdata/reachability/migration.yaml.tmpl"),
   363  					}.WithParams(param.Params{
   364  						mtlsModeParam:            model.MTLSStrict.String(),
   365  						tlsModeParam:             "", // No TLS settings will be included.
   366  						param.Namespace.String(): apps.Ns1.Namespace,
   367  					}),
   368  					fromMatch:          match.And(match.NotNaked, notMigration),
   369  					toMatch:            migration,
   370  					callOpts:           migrationOpts,
   371  					expectMTLS:         toMigrationIstioSubset,
   372  					expectCrossCluster: notFromNaked,
   373  					expectCrossNetwork: toMigrationIstioSubset,
   374  					expectSuccess:      always,
   375  				},
   376  				{
   377  					name: "migration tls disabled",
   378  					configs: config.Sources{
   379  						config.File("testdata/reachability/global-peer-authn.yaml.tmpl"),
   380  						config.File("testdata/reachability/migration.yaml.tmpl"),
   381  					}.WithParams(param.Params{
   382  						mtlsModeParam:            model.MTLSStrict.String(),
   383  						tlsModeParam:             "DISABLE",
   384  						param.Namespace.String(): apps.Ns1.Namespace,
   385  					}),
   386  					fromMatch:          match.And(match.NotNaked, notMigration),
   387  					toMatch:            migration,
   388  					callOpts:           migrationOpts,
   389  					expectMTLS:         never,
   390  					expectCrossCluster: notFromNaked,
   391  					expectCrossNetwork: never,
   392  					// Only the request to legacy one succeeds as we disable mtls explicitly.
   393  					expectSuccess: toMigrationNonIstioSubset,
   394  				},
   395  				{
   396  					name: "migration tls mutual",
   397  					configs: config.Sources{
   398  						config.File("testdata/reachability/global-peer-authn.yaml.tmpl"),
   399  						config.File("testdata/reachability/migration.yaml.tmpl"),
   400  					}.WithParams(param.Params{
   401  						mtlsModeParam:            model.MTLSStrict.String(),
   402  						tlsModeParam:             "ISTIO_MUTUAL",
   403  						param.Namespace.String(): apps.Ns1.Namespace,
   404  					}),
   405  					fromMatch:          match.And(match.NotNaked, notMigration),
   406  					toMatch:            migration,
   407  					callOpts:           migrationOpts,
   408  					expectMTLS:         toMigrationIstioSubset,
   409  					expectCrossCluster: notFromNaked,
   410  					expectCrossNetwork: toMigrationIstioSubset,
   411  					// Only the request to vistio one succeeds as we enable mtls explicitly.
   412  					expectSuccess: toMigrationIstioSubset,
   413  				},
   414  			}
   415  
   416  			for _, c := range cases {
   417  				c := c
   418  
   419  				t.NewSubTest(c.name).Run(func(t framework.TestContext) {
   420  					if c.minIstioVersion != "" {
   421  						skipMV := !t.Settings().Revisions.AtLeast(resource.IstioVersion(c.minIstioVersion))
   422  						if skipMV {
   423  							t.SkipNow()
   424  						}
   425  					}
   426  					// Apply the configs.
   427  					config.New(t).
   428  						Source(c.configs...).
   429  						BuildAll(nil, allServices).
   430  						Apply()
   431  					// Run the test against a number of ports.
   432  					allOpts := append([]echo.CallOptions{}, c.callOpts...)
   433  					if len(allOpts) == 0 {
   434  						allOpts = []echo.CallOptions{
   435  							{
   436  								Port: echo.Port{
   437  									Name: ports.HTTP.Name,
   438  								},
   439  							},
   440  							{
   441  								Port: echo.Port{
   442  									Name: ports.HTTP.Name,
   443  								},
   444  								Scheme: scheme.WebSocket,
   445  							},
   446  							{
   447  								Port: echo.Port{
   448  									Name: ports.HTTP2.Name,
   449  								},
   450  							},
   451  							{
   452  								Port: echo.Port{
   453  									Name: ports.HTTPS.Name,
   454  								},
   455  							},
   456  							{
   457  								Port: echo.Port{
   458  									Name: ports.TCP.Name,
   459  								},
   460  							},
   461  							{
   462  								Port: echo.Port{
   463  									Name: ports.GRPC.Name,
   464  								},
   465  							},
   466  						}
   467  					}
   468  
   469  					// Iterate over all protocols outside, rather than inside, the destination match
   470  					// This is to workaround a known bug (https://github.com/istio/istio/issues/38982) causing
   471  					// connection resets when sending traffic to multiple ports at once
   472  					for _, opts := range allOpts {
   473  						opts := opts
   474  
   475  						schemeStr := string(opts.Scheme)
   476  						if len(schemeStr) == 0 {
   477  							schemeStr = opts.Port.Name
   478  						}
   479  						t.NewSubTestf("%s%s", schemeStr, opts.HTTP.Path).Run(func(t framework.TestContext) {
   480  							// Run the test cases.
   481  							echotest.New(t, allServices.Instances()).
   482  								FromMatch(match.And(c.fromMatch, match.NotProxylessGRPC)).
   483  								ToMatch(match.And(c.toMatch, match.NotProxylessGRPC)).
   484  								WithDefaultFilters(1, 1).
   485  								ConditionallyTo(echotest.NoSelfCalls).
   486  								Run(func(t framework.TestContext, from echo.Instance, to echo.Target) {
   487  									opts := opts.DeepCopy()
   488  									opts.To = to
   489  
   490  									if c.expectSuccess(from, opts) {
   491  										opts.Check = check.OK()
   492  
   493  										// Check HTTP headers to confirm expected use of mTLS in the request.
   494  										if c.expectMTLS(from, opts) {
   495  											opts.Check = check.And(opts.Check, check.MTLSForHTTP())
   496  										} else {
   497  											opts.Check = check.And(opts.Check, check.PlaintextForHTTP())
   498  										}
   499  
   500  										// Check that the correct clusters/networks were reached.
   501  										if c.expectCrossNetwork(from, opts) {
   502  											if !check.IsDNSCaptureEnabled(t) && opts.To.Config().Headless {
   503  												opts.Check = check.And(opts.Check, check.ReachedSourceCluster(t.Clusters()))
   504  											} else {
   505  												opts.Check = check.And(opts.Check, check.ReachedTargetClusters(t))
   506  											}
   507  										} else if c.expectCrossCluster(from, opts) {
   508  											// Expect to stay in the same network as the source pod.
   509  											expectedClusters := to.Clusters().ForNetworks(from.Config().Cluster.NetworkName())
   510  											if !check.IsDNSCaptureEnabled(t) && opts.To.Config().Headless {
   511  												opts.Check = check.And(opts.Check, check.ReachedSourceCluster(t.Clusters()))
   512  											} else {
   513  												opts.Check = check.And(opts.Check, check.ReachedClusters(t.Clusters(), expectedClusters))
   514  											}
   515  										} else {
   516  											// Expect to stay in the same cluster as the source pod.
   517  											expectedClusters := cluster.Clusters{from.Config().Cluster}
   518  											if !check.IsDNSCaptureEnabled(t) && opts.To.Config().Headless {
   519  												opts.Check = check.And(opts.Check, check.ReachedSourceCluster(t.Clusters()))
   520  											} else {
   521  												opts.Check = check.And(opts.Check, check.ReachedClusters(t.Clusters(), expectedClusters))
   522  											}
   523  										}
   524  									} else {
   525  										opts.Check = check.NotOK()
   526  									}
   527  									from.CallOrFail(t, opts)
   528  								})
   529  						})
   530  					}
   531  				})
   532  			}
   533  		})
   534  }
   535  
   536  type condition func(from echo.Instance, opts echo.CallOptions) bool
   537  
   538  func not(c condition) condition {
   539  	return func(from echo.Instance, opts echo.CallOptions) bool {
   540  		return !c(from, opts)
   541  	}
   542  }
   543  
   544  func and(conds ...condition) condition {
   545  	return func(from echo.Instance, opts echo.CallOptions) bool {
   546  		for _, c := range conds {
   547  			if !c(from, opts) {
   548  				return false
   549  			}
   550  		}
   551  		return true
   552  	}
   553  }
   554  
   555  func or(conds ...condition) condition {
   556  	return func(from echo.Instance, opts echo.CallOptions) bool {
   557  		for _, c := range conds {
   558  			if c(from, opts) {
   559  				return true
   560  			}
   561  		}
   562  		return false
   563  	}
   564  }
   565  
   566  var fromNaked condition = func(from echo.Instance, _ echo.CallOptions) bool {
   567  	return from.Config().IsNaked()
   568  }
   569  
   570  var toNaked condition = func(_ echo.Instance, opts echo.CallOptions) bool {
   571  	return opts.To.Config().IsNaked()
   572  }
   573  
   574  var toHeadless condition = func(_ echo.Instance, opts echo.CallOptions) bool {
   575  	return opts.To.Config().IsHeadless()
   576  }
   577  
   578  var toStatefulSet condition = func(_ echo.Instance, opts echo.CallOptions) bool {
   579  	return opts.To.Config().IsStatefulSet()
   580  }
   581  
   582  var toMigrationIstioSubset condition = func(_ echo.Instance, opts echo.CallOptions) bool {
   583  	return opts.HTTP.Path == migrationPathIstio
   584  }
   585  
   586  var toMigrationNonIstioSubset condition = func(_ echo.Instance, opts echo.CallOptions) bool {
   587  	return opts.HTTP.Path == migrationPathNonIstio
   588  }
   589  
   590  var anyNaked = or(fromNaked, toNaked)
   591  
   592  var notNaked = not(anyNaked)
   593  
   594  var notFromNaked = not(fromNaked)
   595  
   596  var notToNaked = not(toNaked)
   597  
   598  var always condition = func(echo.Instance, echo.CallOptions) bool {
   599  	return true
   600  }
   601  
   602  var never condition = func(echo.Instance, echo.CallOptions) bool {
   603  	return false
   604  }