istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/framework/components/istio/config.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 istio
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"path"
    21  	"path/filepath"
    22  	"strings"
    23  
    24  	corev1 "k8s.io/api/core/v1"
    25  
    26  	"istio.io/istio/pkg/test"
    27  	"istio.io/istio/pkg/test/env"
    28  	"istio.io/istio/pkg/test/framework/components/namespace"
    29  	"istio.io/istio/pkg/test/framework/resource"
    30  	"istio.io/istio/pkg/test/scopes"
    31  )
    32  
    33  const (
    34  	// DefaultSystemNamespace default value for SystemNamespace
    35  	DefaultSystemNamespace = "istio-system"
    36  
    37  	// IntegrationTestDefaultsIOP is the path of the default IstioOperator spec to use
    38  	// for integration tests
    39  	IntegrationTestDefaultsIOP = "tests/integration/iop-integration-test-defaults.yaml"
    40  
    41  	// IntegrationTestDefaultsIOPWithQUIC is the path of the default IstioOperator spec to
    42  	// use for integration tests involving QUIC
    43  	IntegrationTestDefaultsIOPWithQUIC = "tests/integration/iop-integration-test-defaults-with-quic.yaml"
    44  
    45  	// IntegrationTestRemoteDefaultsIOP is the path of the default IstioOperator spec to use
    46  	// on remote clusters for integration tests
    47  	IntegrationTestRemoteDefaultsIOP = "tests/integration/iop-remote-integration-test-defaults.yaml"
    48  
    49  	// BaseIOP is the path of the base IstioOperator spec
    50  	BaseIOP = "tests/integration/base.yaml"
    51  
    52  	// IntegrationTestRemoteGatewaysIOP is the path of the default IstioOperator spec to use
    53  	// to install gateways on remote clusters for integration tests
    54  	IntegrationTestRemoteGatewaysIOP = "tests/integration/iop-remote-integration-test-gateways.yaml"
    55  
    56  	// IntegrationTestExternalIstiodPrimaryDefaultsIOP is the path of the default IstioOperator spec to use
    57  	// on external istiod primary clusters for integration tests
    58  	IntegrationTestExternalIstiodPrimaryDefaultsIOP = "tests/integration/iop-externalistiod-primary-integration-test-defaults.yaml"
    59  
    60  	// IntegrationTestExternalIstiodConfigDefaultsIOP is the path of the default IstioOperator spec to use
    61  	// on external istiod config clusters for integration tests
    62  	IntegrationTestExternalIstiodConfigDefaultsIOP = "tests/integration/iop-externalistiod-config-integration-test-defaults.yaml"
    63  
    64  	// IntegrationTestAmbientDefaultsIOP is the path of the default IstioOperator for ambient
    65  	IntegrationTestAmbientDefaultsIOP = "tests/integration/iop-ambient-test-defaults.yaml"
    66  
    67  	// IntegrationTestPeerMetadataDiscoveryDefaultsIOP is the path of the default IstioOperator to force WDS usage
    68  	IntegrationTestPeerMetadataDiscoveryDefaultsIOP = "tests/integration/iop-wds.yaml"
    69  
    70  	// hubValuesKey values key for the Docker image hub.
    71  	hubValuesKey = "global.hub"
    72  
    73  	// tagValuesKey values key for the Docker image tag.
    74  	tagValuesKey = "global.tag"
    75  
    76  	// variantValuesKey values key for the Docker image variant.
    77  	variantValuesKey = "global.variant"
    78  
    79  	// imagePullPolicyValuesKey values key for the Docker image pull policy.
    80  	imagePullPolicyValuesKey = "global.imagePullPolicy"
    81  
    82  	// DefaultEgressGatewayLabel is the default Istio label for the egress gateway.
    83  	DefaultEgressGatewayIstioLabel = "egressgateway"
    84  
    85  	// DefaultEgressGatewayServiceName is the default service name for the egress gateway.
    86  	DefaultEgressGatewayServiceName = "istio-egressgateway"
    87  )
    88  
    89  var (
    90  	helmValues      string
    91  	operatorOptions string
    92  
    93  	settingsFromCommandline = &Config{
    94  		SystemNamespace:               DefaultSystemNamespace,
    95  		TelemetryNamespace:            DefaultSystemNamespace,
    96  		DeployIstio:                   true,
    97  		PrimaryClusterIOPFile:         IntegrationTestDefaultsIOP,
    98  		ConfigClusterIOPFile:          IntegrationTestDefaultsIOP,
    99  		RemoteClusterIOPFile:          IntegrationTestRemoteDefaultsIOP,
   100  		BaseIOPFile:                   BaseIOP,
   101  		DeployEastWestGW:              true,
   102  		DumpKubernetesManifests:       false,
   103  		IstiodlessRemotes:             true,
   104  		EnableCNI:                     false,
   105  		EgressGatewayServiceNamespace: DefaultSystemNamespace,
   106  		EgressGatewayServiceName:      DefaultEgressGatewayServiceName,
   107  		EgressGatewayIstioLabel:       DefaultEgressGatewayIstioLabel,
   108  	}
   109  )
   110  
   111  // Config provide kube-specific Config from flags.
   112  type Config struct {
   113  	// The namespace where the Istio components (<=1.1) reside in a typical deployment (default: "istio-system").
   114  	SystemNamespace string
   115  
   116  	// The namespace in which kiali, tracing providers, graphana, prometheus are deployed.
   117  	TelemetryNamespace string
   118  
   119  	// The IstioOperator spec file to be used for Control plane cluster by default
   120  	PrimaryClusterIOPFile string
   121  
   122  	// The IstioOperator spec file to be used for Config cluster by default
   123  	ConfigClusterIOPFile string
   124  
   125  	// The IstioOperator spec file to be used for Remote cluster by default
   126  	RemoteClusterIOPFile string
   127  
   128  	// The IstioOperator spec file used as the base for all installs
   129  	BaseIOPFile string
   130  
   131  	// Override values specifically for the ICP crd
   132  	// This is mostly required for cases where --set cannot be used
   133  	// These values are applied to non-remote clusters
   134  	ControlPlaneValues string
   135  
   136  	// Override values specifically for the ICP crd
   137  	// This is mostly required for cases where --set cannot be used
   138  	// These values are only applied to remote clusters
   139  	// Default value will be ControlPlaneValues if no remote values provided
   140  	RemoteClusterValues string
   141  
   142  	// Override values specifically for the ICP crd
   143  	// This is mostly required for cases where --set cannot be used
   144  	// These values are only applied to remote config clusters
   145  	// Default value will be ControlPlaneValues if no remote values provided
   146  	ConfigClusterValues string
   147  
   148  	// Overrides for the Helm values file.
   149  	Values map[string]string
   150  
   151  	// Indicates that the test should deploy Istio into the target Kubernetes cluster before running tests.
   152  	DeployIstio bool
   153  
   154  	// Do not wait for the validation webhook before completing the deployment. This is useful for
   155  	// doing deployments without Galley.
   156  	SkipWaitForValidationWebhook bool
   157  
   158  	// Indicates that the test should deploy Istio's east west gateway into the target Kubernetes cluster
   159  	// before running tests.
   160  	DeployEastWestGW bool
   161  
   162  	// DumpKubernetesManifests will cause Kubernetes YAML generated by istioctl install/generate to be dumped to artifacts.
   163  	DumpKubernetesManifests bool
   164  
   165  	// IstiodlessRemotes makes remote clusters run without istiod, using webhooks/ca from the primary cluster.
   166  	// TODO we could set this per-cluster if istiod was smarter about patching remotes.
   167  	IstiodlessRemotes bool
   168  
   169  	// OperatorOptions overrides default operator configuration.
   170  	OperatorOptions map[string]string
   171  
   172  	// EnableCNI indicates the test should have CNI enabled.
   173  	EnableCNI bool
   174  
   175  	// custom deployment for ingress and egress gateway on remote clusters.
   176  	GatewayValues string
   177  
   178  	// Custom deployment for east-west gateway
   179  	EastWestGatewayValues string
   180  
   181  	// IngressGatewayServiceName is the service name to use to reference the ingressgateway
   182  	// This field should only be set when DeployIstio is false
   183  	IngressGatewayServiceName string
   184  
   185  	// IngressGatewayServiceNamespace allows overriding the namespace of the ingressgateway service (defaults to SystemNamespace)
   186  	// This field should only be set when DeployIstio is false
   187  	IngressGatewayServiceNamespace string
   188  
   189  	// IngressGatewayIstioLabel allows overriding the selector of the ingressgateway service (defaults to istio=ingressgateway)
   190  	// This field should only be set when DeployIstio is false
   191  	IngressGatewayIstioLabel string
   192  
   193  	// EgressGatewayServiceName is the service name to use to reference the egressgateway
   194  	// This field should only be set when DeployIstio is false
   195  	EgressGatewayServiceName string
   196  
   197  	// EgressGatewayServiceNamespace allows overriding the namespace of the egressgateway service (defaults to SystemNamespace)
   198  	// This field should only be set when DeployIstio is false
   199  	EgressGatewayServiceNamespace string
   200  
   201  	// EgressGatewayIstioLabel allows overriding the selector of the egressgateway service (defaults to istio=egressgateway)
   202  	// This field should only be set when DeployIstio is false
   203  	EgressGatewayIstioLabel string
   204  
   205  	// SharedMeshConfigName is the name of the user's local ConfigMap to be patched, which the user sets as the SHARED_MESH_CONFIG pilot env variable
   206  	// upon installing Istio.
   207  	// This field should only be set when DeployIstio is false.
   208  	SharedMeshConfigName string
   209  }
   210  
   211  func (c *Config) OverridesYAML(s *resource.Settings) string {
   212  	return fmt.Sprintf(`
   213  global:
   214    hub: %s
   215    tag: %s
   216  `, s.Image.Hub, s.Image.Tag)
   217  }
   218  
   219  func (c *Config) IstioOperatorConfigYAML(iopYaml string) string {
   220  	data := ""
   221  	if iopYaml != "" {
   222  		data = Indent(iopYaml, "  ")
   223  	}
   224  
   225  	return fmt.Sprintf(`
   226  apiVersion: install.istio.io/v1alpha1
   227  kind: IstioOperator
   228  spec:
   229  %s
   230  `, data)
   231  }
   232  
   233  func (c *Config) fillDefaults(ctx resource.Context) {
   234  	if ctx.AllClusters().IsExternalControlPlane() {
   235  		c.PrimaryClusterIOPFile = IntegrationTestExternalIstiodPrimaryDefaultsIOP
   236  		c.ConfigClusterIOPFile = IntegrationTestExternalIstiodConfigDefaultsIOP
   237  		if c.ConfigClusterValues == "" {
   238  			c.ConfigClusterValues = c.RemoteClusterValues
   239  		}
   240  	} else if !c.IstiodlessRemotes {
   241  		c.RemoteClusterIOPFile = IntegrationTestDefaultsIOP
   242  		if c.RemoteClusterValues == "" {
   243  			c.RemoteClusterValues = c.ControlPlaneValues
   244  		}
   245  	}
   246  }
   247  
   248  // Indent indents a block of text with an indent string
   249  func Indent(text, indent string) string {
   250  	if text[len(text)-1:] == "\n" {
   251  		result := ""
   252  		for _, j := range strings.Split(text[:len(text)-1], "\n") {
   253  			result += indent + j + "\n"
   254  		}
   255  		return result
   256  	}
   257  	result := ""
   258  	for _, j := range strings.Split(strings.TrimRight(text, "\n"), "\n") {
   259  		result += indent + j + "\n"
   260  	}
   261  	return result[:len(result)-1]
   262  }
   263  
   264  // DefaultConfig creates a new Config from defaults, environments variables, and command-line parameters.
   265  func DefaultConfig(ctx resource.Context) (Config, error) {
   266  	// Make a local copy.
   267  	s := *settingsFromCommandline
   268  
   269  	iopFile := s.PrimaryClusterIOPFile
   270  	if iopFile != "" && !path.IsAbs(s.PrimaryClusterIOPFile) {
   271  		iopFile = filepath.Join(env.IstioSrc, s.PrimaryClusterIOPFile)
   272  	}
   273  
   274  	if err := checkFileExists(iopFile); err != nil {
   275  		scopes.Framework.Warnf("Default IOPFile missing: %v", err)
   276  	}
   277  
   278  	var err error
   279  	if s.Values, err = newHelmValues(ctx); err != nil {
   280  		return Config{}, err
   281  	}
   282  
   283  	if s.OperatorOptions, err = parseConfigOptions(operatorOptions); err != nil {
   284  		return Config{}, err
   285  	}
   286  
   287  	return s, nil
   288  }
   289  
   290  // DefaultConfigOrFail calls DefaultConfig and fails t if an error occurs.
   291  func DefaultConfigOrFail(t test.Failer, ctx resource.Context) Config {
   292  	cfg, err := DefaultConfig(ctx)
   293  	if err != nil {
   294  		t.Fatalf("Get istio config: %v", err)
   295  	}
   296  	return cfg
   297  }
   298  
   299  func checkFileExists(path string) error {
   300  	if _, err := os.Stat(path); os.IsNotExist(err) {
   301  		return err
   302  	}
   303  	return nil
   304  }
   305  
   306  func newHelmValues(ctx resource.Context) (map[string]string, error) {
   307  	userValues, err := parseConfigOptions(helmValues)
   308  	if err != nil {
   309  		return nil, err
   310  	}
   311  
   312  	// Copy the defaults first.
   313  	values := make(map[string]string)
   314  
   315  	// Common values
   316  	s := ctx.Settings()
   317  	values[hubValuesKey] = s.Image.Hub
   318  	values[tagValuesKey] = s.Image.Tag
   319  	values[variantValuesKey] = s.Image.Variant
   320  	values[imagePullPolicyValuesKey] = s.Image.PullPolicy
   321  
   322  	// Copy the user values.
   323  	for k, v := range userValues {
   324  		values[k] = v
   325  	}
   326  
   327  	// Always pull Docker images if using the "latest".
   328  	if values[tagValuesKey] == "latest" {
   329  		values[imagePullPolicyValuesKey] = string(corev1.PullAlways)
   330  	}
   331  
   332  	// We need more information on Envoy logs to detect usage of any deprecated feature
   333  	if ctx.Settings().FailOnDeprecation {
   334  		values["global.proxy.logLevel"] = "debug"
   335  		values["global.proxy.componentLogLevel"] = "misc:debug"
   336  	}
   337  
   338  	return values, nil
   339  }
   340  
   341  func parseConfigOptions(options string) (map[string]string, error) {
   342  	out := make(map[string]string)
   343  	if options == "" {
   344  		return out, nil
   345  	}
   346  
   347  	values := strings.Split(options, ",")
   348  	for _, v := range values {
   349  		parts := strings.Split(v, "=")
   350  		if len(parts) != 2 {
   351  			return nil, fmt.Errorf("failed parsing config options: %s", options)
   352  		}
   353  		out[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
   354  	}
   355  	return out, nil
   356  }
   357  
   358  // String implements fmt.Stringer
   359  func (c *Config) String() string {
   360  	result := ""
   361  
   362  	result += fmt.Sprintf("SystemNamespace:                %s\n", c.SystemNamespace)
   363  	result += fmt.Sprintf("TelemetryNamespace:             %s\n", c.TelemetryNamespace)
   364  	result += fmt.Sprintf("DeployIstio:                    %v\n", c.DeployIstio)
   365  	result += fmt.Sprintf("DeployEastWestGW:               %v\n", c.DeployEastWestGW)
   366  	result += fmt.Sprintf("Values:                         %v\n", c.Values)
   367  	result += fmt.Sprintf("PrimaryClusterIOPFile:          %s\n", c.PrimaryClusterIOPFile)
   368  	result += fmt.Sprintf("ConfigClusterIOPFile:           %s\n", c.ConfigClusterIOPFile)
   369  	result += fmt.Sprintf("RemoteClusterIOPFile:           %s\n", c.RemoteClusterIOPFile)
   370  	result += fmt.Sprintf("BaseIOPFile:                    %s\n", c.BaseIOPFile)
   371  	result += fmt.Sprintf("SkipWaitForValidationWebhook:   %v\n", c.SkipWaitForValidationWebhook)
   372  	result += fmt.Sprintf("DumpKubernetesManifests:        %v\n", c.DumpKubernetesManifests)
   373  	result += fmt.Sprintf("IstiodlessRemotes:              %v\n", c.IstiodlessRemotes)
   374  	result += fmt.Sprintf("OperatorOptions:                %v\n", c.OperatorOptions)
   375  	result += fmt.Sprintf("EnableCNI:                      %v\n", c.EnableCNI)
   376  	result += fmt.Sprintf("IngressGatewayServiceName:      %v\n", c.IngressGatewayServiceName)
   377  	result += fmt.Sprintf("IngressGatewayServiceNamespace: %v\n", c.IngressGatewayServiceNamespace)
   378  	result += fmt.Sprintf("IngressGatewayIstioLabel:       %v\n", c.IngressGatewayIstioLabel)
   379  	result += fmt.Sprintf("EgressGatewayServiceName:       %v\n", c.EgressGatewayServiceName)
   380  	result += fmt.Sprintf("EgressGatewayServiceNamespace:  %v\n", c.EgressGatewayServiceNamespace)
   381  	result += fmt.Sprintf("EgressGatewayIstioLabel:        %v\n", c.EgressGatewayIstioLabel)
   382  	result += fmt.Sprintf("SharedMeshConfigName:           %v\n", c.SharedMeshConfigName)
   383  
   384  	return result
   385  }
   386  
   387  // ClaimSystemNamespace retrieves the namespace for the Istio system components from the environment.
   388  func ClaimSystemNamespace(ctx resource.Context) (namespace.Instance, error) {
   389  	istioCfg, err := DefaultConfig(ctx)
   390  	if err != nil {
   391  		return nil, err
   392  	}
   393  	nsCfg := namespace.Config{
   394  		Prefix: istioCfg.SystemNamespace,
   395  		Inject: false,
   396  		// Already handled directly
   397  		SkipDump:    true,
   398  		SkipCleanup: true,
   399  	}
   400  	return namespace.Claim(ctx, nsCfg)
   401  }
   402  
   403  // ClaimSystemNamespaceOrFail calls ClaimSystemNamespace, failing the test if an error occurs.
   404  func ClaimSystemNamespaceOrFail(t test.Failer, ctx resource.Context) namespace.Instance {
   405  	t.Helper()
   406  	i, err := ClaimSystemNamespace(ctx)
   407  	if err != nil {
   408  		t.Fatal(err)
   409  	}
   410  	return i
   411  }