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 }