blob: 94261f525fb32f5d024234987bd44eda739a8f33 [file] [log] [blame]
// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package istio
import (
"fmt"
"os"
"path"
"path/filepath"
"strings"
)
import (
kubeCore "k8s.io/api/core/v1"
)
import (
"github.com/apache/dubbo-go-pixiu/pkg/test"
"github.com/apache/dubbo-go-pixiu/pkg/test/env"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/namespace"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource"
"github.com/apache/dubbo-go-pixiu/pkg/test/scopes"
)
const (
// DefaultSystemNamespace default value for SystemNamespace
DefaultSystemNamespace = "dubbo-system"
// IntegrationTestDefaultsIOP is the path of the default IstioOperator spec to use
// for integration tests
IntegrationTestDefaultsIOP = "tests/integration/iop-integration-test-defaults.yaml"
// IntegrationTestDefaultsIOPWithQUIC is the path of the default IstioOperator spec to
// use for integration tests involving QUIC
IntegrationTestDefaultsIOPWithQUIC = "tests/integration/iop-integration-test-defaults-with-quic.yaml"
// IntegrationTestRemoteDefaultsIOP is the path of the default IstioOperator spec to use
// on remote clusters for integration tests
IntegrationTestRemoteDefaultsIOP = "tests/integration/iop-remote-integration-test-defaults.yaml"
// BaseIOP is the path of the base IstioOperator spec
BaseIOP = "tests/integration/base.yaml"
// IntegrationTestRemoteGatewaysIOP is the path of the default IstioOperator spec to use
// to install gateways on remote clusters for integration tests
IntegrationTestRemoteGatewaysIOP = "tests/integration/iop-remote-integration-test-gateways.yaml"
// IntegrationTestExternalIstiodPrimaryDefaultsIOP is the path of the default IstioOperator spec to use
// on external istiod primary clusters for integration tests
IntegrationTestExternalIstiodPrimaryDefaultsIOP = "tests/integration/iop-externalistiod-primary-integration-test-defaults.yaml"
// IntegrationTestExternalIstiodConfigDefaultsIOP is the path of the default IstioOperator spec to use
// on external istiod config clusters for integration tests
IntegrationTestExternalIstiodConfigDefaultsIOP = "tests/integration/iop-externalistiod-config-integration-test-defaults.yaml"
// hubValuesKey values key for the Docker image hub.
hubValuesKey = "global.hub"
// tagValuesKey values key for the Docker image tag.
tagValuesKey = "global.tag"
// imagePullPolicyValuesKey values key for the Docker image pull policy.
imagePullPolicyValuesKey = "global.imagePullPolicy"
)
var (
helmValues string
operatorOptions string
settingsFromCommandline = &Config{
SystemNamespace: DefaultSystemNamespace,
TelemetryNamespace: DefaultSystemNamespace,
DeployIstio: true,
PrimaryClusterIOPFile: IntegrationTestDefaultsIOP,
ConfigClusterIOPFile: IntegrationTestDefaultsIOP,
RemoteClusterIOPFile: IntegrationTestRemoteDefaultsIOP,
BaseIOPFile: BaseIOP,
DeployEastWestGW: true,
DumpKubernetesManifests: false,
IstiodlessRemotes: false,
EnableCNI: false,
}
)
// Config provide kube-specific Config from flags.
type Config struct {
// The namespace where the Istio components (<=1.1) reside in a typical deployment (default: "dubbo-system").
SystemNamespace string
// The namespace in which kiali, tracing providers, graphana, prometheus are deployed.
TelemetryNamespace string
// The IstioOperator spec file to be used for Control plane cluster by default
PrimaryClusterIOPFile string
// The IstioOperator spec file to be used for Config cluster by default
ConfigClusterIOPFile string
// The IstioOperator spec file to be used for Remote cluster by default
RemoteClusterIOPFile string
// The IstioOperator spec file used as the base for all installs
BaseIOPFile string
// Override values specifically for the ICP crd
// This is mostly required for cases where --set cannot be used
// These values are applied to non-remote clusters
ControlPlaneValues string
// Override values specifically for the ICP crd
// This is mostly required for cases where --set cannot be used
// These values are only applied to remote clusters
// Default value will be ControlPlaneValues if no remote values provided
RemoteClusterValues string
// Override values specifically for the ICP crd
// This is mostly required for cases where --set cannot be used
// These values are only applied to remote config clusters
// Default value will be ControlPlaneValues if no remote values provided
ConfigClusterValues string
// Overrides for the Helm values file.
Values map[string]string
// Indicates that the test should deploy Istio into the target Kubernetes cluster before running tests.
DeployIstio bool
// Do not wait for the validation webhook before completing the deployment. This is useful for
// doing deployments without Galley.
SkipWaitForValidationWebhook bool
// Indicates that the test should deploy Istio's east west gateway into the target Kubernetes cluster
// before running tests.
DeployEastWestGW bool
// DumpKubernetesManifests will cause Kubernetes YAML generated by istioctl install/generate to be dumped to artifacts.
DumpKubernetesManifests bool
// IstiodlessRemotes makes remote clusters run without istiod, using webhooks/ca from the primary cluster.
// TODO we could set this per-cluster if istiod was smarter about patching remotes.
IstiodlessRemotes bool
// OperatorOptions overrides default operator configuration.
OperatorOptions map[string]string
// EnableCNI indicates the test should have CNI enabled.
EnableCNI bool
}
func (c *Config) OverridesYAML(s *resource.Settings) string {
return fmt.Sprintf(`
global:
hub: %s
tag: %s
`, s.Image.Hub, s.Image.Tag)
}
func (c *Config) IstioOperatorConfigYAML(s *resource.Settings, iopYaml string) string {
data := ""
if iopYaml != "" {
data = Indent(iopYaml, " ")
}
return fmt.Sprintf(`
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
hub: %s
tag: %s
%s
`, s.Image.Hub, s.Image.Tag, data)
}
// Indent indents a block of text with an indent string
func Indent(text, indent string) string {
if text[len(text)-1:] == "\n" {
result := ""
for _, j := range strings.Split(text[:len(text)-1], "\n") {
result += indent + j + "\n"
}
return result
}
result := ""
for _, j := range strings.Split(strings.TrimRight(text, "\n"), "\n") {
result += indent + j + "\n"
}
return result[:len(result)-1]
}
// DefaultConfig creates a new Config from defaults, environments variables, and command-line parameters.
func DefaultConfig(ctx resource.Context) (Config, error) {
// Make a local copy.
s := *settingsFromCommandline
iopFile := s.PrimaryClusterIOPFile
if iopFile != "" && !path.IsAbs(s.PrimaryClusterIOPFile) {
iopFile = filepath.Join(env.IstioSrc, s.PrimaryClusterIOPFile)
}
if err := checkFileExists(iopFile); err != nil {
scopes.Framework.Warnf("Default IOPFile missing: %v", err)
}
var err error
if s.Values, err = newHelmValues(ctx); err != nil {
return Config{}, err
}
if s.OperatorOptions, err = parseConfigOptions(operatorOptions); err != nil {
return Config{}, err
}
return s, nil
}
// DefaultConfigOrFail calls DefaultConfig and fails t if an error occurs.
func DefaultConfigOrFail(t test.Failer, ctx resource.Context) Config {
cfg, err := DefaultConfig(ctx)
if err != nil {
t.Fatalf("Get istio config: %v", err)
}
return cfg
}
func checkFileExists(path string) error {
if _, err := os.Stat(path); os.IsNotExist(err) {
return err
}
return nil
}
func newHelmValues(ctx resource.Context) (map[string]string, error) {
userValues, err := parseConfigOptions(helmValues)
if err != nil {
return nil, err
}
// Copy the defaults first.
values := make(map[string]string)
// Common values
s := ctx.Settings()
values[hubValuesKey] = s.Image.Hub
values[tagValuesKey] = s.Image.Tag
values[imagePullPolicyValuesKey] = s.Image.PullPolicy
// Copy the user values.
for k, v := range userValues {
values[k] = v
}
// Always pull Docker images if using the "latest".
if values[tagValuesKey] == "latest" {
values[imagePullPolicyValuesKey] = string(kubeCore.PullAlways)
}
// We need more information on Envoy logs to detect usage of any deprecated feature
if ctx.Settings().FailOnDeprecation {
values["global.proxy.logLevel"] = "debug"
values["global.proxy.componentLogLevel"] = "misc:debug"
}
return values, nil
}
func parseConfigOptions(options string) (map[string]string, error) {
out := make(map[string]string)
if options == "" {
return out, nil
}
values := strings.Split(options, ",")
for _, v := range values {
parts := strings.Split(v, "=")
if len(parts) != 2 {
return nil, fmt.Errorf("failed parsing config options: %s", options)
}
out[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
}
return out, nil
}
// String implements fmt.Stringer
func (c *Config) String() string {
result := ""
result += fmt.Sprintf("SystemNamespace: %s\n", c.SystemNamespace)
result += fmt.Sprintf("TelemetryNamespace: %s\n", c.TelemetryNamespace)
result += fmt.Sprintf("DeployIstio: %v\n", c.DeployIstio)
result += fmt.Sprintf("DeployEastWestGW: %v\n", c.DeployEastWestGW)
result += fmt.Sprintf("Values: %v\n", c.Values)
result += fmt.Sprintf("PrimaryClusterIOPFile: %s\n", c.PrimaryClusterIOPFile)
result += fmt.Sprintf("ConfigClusterIOPFile: %s\n", c.ConfigClusterIOPFile)
result += fmt.Sprintf("RemoteClusterIOPFile: %s\n", c.RemoteClusterIOPFile)
result += fmt.Sprintf("BaseIOPFile: %s\n", c.BaseIOPFile)
result += fmt.Sprintf("SkipWaitForValidationWebhook: %v\n", c.SkipWaitForValidationWebhook)
result += fmt.Sprintf("DumpKubernetesManifests: %v\n", c.DumpKubernetesManifests)
result += fmt.Sprintf("IstiodlessRemotes: %v\n", c.IstiodlessRemotes)
result += fmt.Sprintf("OperatorOptions: %v\n", c.OperatorOptions)
result += fmt.Sprintf("EnableCNI: %v\n", c.EnableCNI)
return result
}
// ClaimSystemNamespace retrieves the namespace for the Istio system components from the environment.
func ClaimSystemNamespace(ctx resource.Context) (namespace.Instance, error) {
istioCfg, err := DefaultConfig(ctx)
if err != nil {
return nil, err
}
nsCfg := namespace.Config{
Prefix: istioCfg.SystemNamespace,
Inject: false,
}
return namespace.Claim(ctx, nsCfg)
}
// ClaimSystemNamespaceOrFail calls ClaimSystemNamespace, failing the test if an error occurs.
func ClaimSystemNamespaceOrFail(t test.Failer, ctx resource.Context) namespace.Instance {
t.Helper()
i, err := ClaimSystemNamespace(ctx)
if err != nil {
t.Fatal(err)
}
return i
}