diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index bb348965e7..520b4a26c8 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,6 +1,6 @@ { "name": "istio build-tools", - "image": "gcr.io/istio-testing/build-tools:master-8dcf63149d5bdaa83d1407a121098e8e8d1626dd", + "image": "gcr.io/istio-testing/build-tools:master-3858f8db549f6f7f73de4b55fba5075f71d2651d", "privileged": true, "remoteEnv": { "USE_GKE_GCLOUD_AUTH_PLUGIN": "True", diff --git a/cni/pkg/install/cniconfig_test.go b/cni/pkg/install/cniconfig_test.go index 1fa2d8dd04..c0cf19cd6b 100644 --- a/cni/pkg/install/cniconfig_test.go +++ b/cni/pkg/install/cniconfig_test.go @@ -227,8 +227,7 @@ func TestGetCNIConfigFilepath(t *testing.T) { // Handle chained CNI plugin cases // Call with goroutine to test fsnotify watcher - parent, cancel := context.WithCancel(context.Background()) - defer cancel() + parent := t.Context() resultChan, errChan := make(chan string, 1), make(chan error, 1) go func(resultChan chan string, errChan chan error, ctx context.Context, cniConfName, mountedCNINetDir string, chained bool) { result, err := getCNIConfigFilepath(ctx, cniConfName, mountedCNINetDir, chained) diff --git a/cni/pkg/install/install_test.go b/cni/pkg/install/install_test.go index df874d1cdb..99afbc0994 100644 --- a/cni/pkg/install/install_test.go +++ b/cni/pkg/install/install_test.go @@ -220,8 +220,7 @@ func TestSleepCheckInstall(t *testing.T) { tempDir := t.TempDir() // Initialize parameters - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() if c.istioOwnedCNIConfig && len(c.istioOwnedCNIConfigFilename) == 0 { c.istioOwnedCNIConfigFilename = "02-istio-conf.conflist" diff --git a/cni/pkg/nftables/kubeletuid_linux.go b/cni/pkg/nftables/kubeletuid_linux.go index 4c315de198..6a0e947732 100644 --- a/cni/pkg/nftables/kubeletuid_linux.go +++ b/cni/pkg/nftables/kubeletuid_linux.go @@ -22,7 +22,9 @@ import ( "github.com/prometheus/procfs" ) -// getKubeletUIDFromPath finds the kubelet process UID by inspecting the proc filesystem path. +// getKubeletUIDFromPath finds the kubelet or kubelite process UID by inspecting the proc filesystem path. +// In standard Kubernetes distributions, it looks for the "kubelet" process. +// On some platforms like MicroK8s, where multiple k8s components are consolidated, it looks for the "kubelite" process. func getKubeletUIDFromPath(procPath string) (string, error) { fs, err := procfs.NewFS(procPath) if err != nil { @@ -34,7 +36,9 @@ func getKubeletUIDFromPath(procPath string) (string, error) { return "", fmt.Errorf("failed to read processes from %s: %v", procPath, err) } - // Find kubelet process + // List of process names to search for, in order of preference + processNames := []string{"kubelet", "kubelite"} + for _, proc := range procs { comm, err := proc.Comm() if err != nil { @@ -42,36 +46,43 @@ func getKubeletUIDFromPath(procPath string) (string, error) { continue } - if comm == "kubelet" { - // Lets check the command line to ensure it's really kubelet - cmdline, err := proc.CmdLine() - if err != nil { - continue - } + for _, targetName := range processNames { + if comm == targetName { + // Lets check the command line to ensure it's really the target process + cmdline, err := proc.CmdLine() + if err != nil { + continue + } - kubeletFound := false - for _, arg := range cmdline { - if strings.Contains(strings.ToLower(arg), "kubelet") { - kubeletFound = true - break + // Verify that this process is actually related to kubelet by checking + // if "kubelet" appears in any of the command line arguments. + // This works for both: + // - Standard kubelet: /usr/bin/kubelet [args...] + // - MicroK8s kubelite: /snap/microk8s/.../kubelite --kubelet-args-file=... + processFound := false + for _, arg := range cmdline { + if strings.Contains(strings.ToLower(arg), "kubelet") { + processFound = true + break + } + } + if !processFound { + continue } - } - if !kubeletFound { - continue - } - // Get process status with UIDs - status, err := proc.NewStatus() - if err != nil { - continue - } + // Get process status with UIDs + status, err := proc.NewStatus() + if err != nil { + continue + } - realUID := status.UIDs[0] - realUIDStr := strconv.FormatUint(realUID, 10) + realUID := status.UIDs[0] + realUIDStr := strconv.FormatUint(realUID, 10) - return realUIDStr, nil + return realUIDStr, nil + } } } - return "", fmt.Errorf("kubelet process not found in %s", procPath) + return "", fmt.Errorf("kubelet or kubelite process not found in %s", procPath) } diff --git a/cni/pkg/nftables/nftables.go b/cni/pkg/nftables/nftables.go index 53493b4baa..94f2eaff36 100644 --- a/cni/pkg/nftables/nftables.go +++ b/cni/pkg/nftables/nftables.go @@ -443,7 +443,7 @@ func (cfg *NftablesConfigurator) CreateHostRulesForHealthChecks() error { // // Challenge: In nftables, there is no direct equivalent to "--socket-exists", so we explored multiple alternatives // - Option-1 (UID-based matching): Since kubelet runs as a specific process with a known UID, we can use - // meta skuid to identify traffic originating from kubelet. + // meta skuid to identify traffic originating from kubelet (or kubelite in MicroK8s). // // - Option-2: Match on kubelet’s source IP (node IP). This works in theory but is a bit unsafe as // other host processes can also send traffic from the node IP, and nodes can have multiple IPs making the diff --git a/cni/pkg/nodeagent/detect_artifacts_linux.go b/cni/pkg/nodeagent/detect_artifacts_linux.go new file mode 100644 index 0000000000..1412f3c3d7 --- /dev/null +++ b/cni/pkg/nodeagent/detect_artifacts_linux.go @@ -0,0 +1,102 @@ +// 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 nodeagent + +import ( + "fmt" + "strings" + + "github.com/vishvananda/netlink" + + "istio.io/istio/cni/pkg/config" + "istio.io/istio/cni/pkg/ipset" + "istio.io/istio/cni/pkg/util" +) + +// detectIptablesArtifacts checks for the presence of Istio iptables artifacts (specifically IPsets) +// on the host network to determine if a previous iptables-based deployment exists. +// Returns: +// - true if iptables artifacts (IPsets) are detected +// - false if no artifacts are detected or if detection fails +// - error if there was a failure during detection +func detectIptablesArtifacts(enableIPv6 bool) (bool, error) { + var detected bool + var detectionErr error + + // Run the detection in the host network namespace + err := util.RunAsHost(func() error { + // Check if the IPv4 IPset exists + v4Name := fmt.Sprintf(ipset.V4Name, config.ProbeIPSet) + v4Exists, err := ipsetExists(v4Name) + if err != nil { + log.Debugf("failed to check for v4 IPset %s: %v", v4Name, err) + detectionErr = fmt.Errorf("v4 IPset detection failed: %w", err) + // Lets continue checking for v6 (if enabled) + } + + if v4Exists { + log.Infof("detected iptables artifact: IPset %s exists", v4Name) + detected = true + // Found v4 artifact, no need to check for v6 + return nil + } + + // Check if the IPv6 IPset exists + if enableIPv6 { + v6Name := fmt.Sprintf(ipset.V6Name, config.ProbeIPSet) + v6Exists, err := ipsetExists(v6Name) + if err != nil { + log.Debugf("failed to check for v6 IPset %s: %v", v6Name, err) + if detectionErr != nil { + detectionErr = fmt.Errorf("%w; v6 IPset detection failed: %w", detectionErr, err) + } else { + detectionErr = fmt.Errorf("v6 IPset detection failed: %w", err) + } + } + + if v6Exists { + log.Infof("detected iptables artifact: IPset %s exists", v6Name) + detected = true + } + } + + return nil + }) + if err != nil { + return false, fmt.Errorf("failed to run detection in host namespace: %w", err) + } + + return detected, detectionErr +} + +// ipsetExists checks if an IPset with the given name exists on the host. +// Returns: +// - true, nil if the IPset exists +// - false, nil if the IPset does not exist (expected for clean/fresh setup) +// - false, error for any errors +func ipsetExists(name string) (bool, error) { + _, err := netlink.IpsetList(name) + if err == nil { + // IPset exists + return true, nil + } + + if strings.Contains(err.Error(), "no such file") { + // IPSet does not exist + return false, nil + } + + return false, err +} diff --git a/cni/pkg/nodeagent/detect_artifacts_linux_test.go b/cni/pkg/nodeagent/detect_artifacts_linux_test.go new file mode 100644 index 0000000000..fe0ca31e05 --- /dev/null +++ b/cni/pkg/nodeagent/detect_artifacts_linux_test.go @@ -0,0 +1,154 @@ +// 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 nodeagent + +import ( + "fmt" + "strings" + "sync" + "testing" + + // Create a new network namespace. This will have the 'lo' interface ready but nothing else. + _ "github.com/howardjohn/unshare-go/netns" + // Create a new user namespace. This will map the current UID/GID to 0. + "github.com/howardjohn/unshare-go/userns" + "github.com/vishvananda/netlink" + "golang.org/x/sys/unix" + + "istio.io/istio/cni/pkg/config" + "istio.io/istio/cni/pkg/ipset" + "istio.io/istio/pkg/test/util/assert" +) + +// TestDetectIptablesArtifacts is an e2e test that validates the iptable artifacts on the network. +// It runs in an isolated network namespace created by unshare-go, which provides a clean environment +// with root-like capabilities for netlink operations. It validates the upgrade path from iptables to +// nftables backend by simulating various scenarios. +func TestDetectIptablesArtifacts(t *testing.T) { + setup(t) + + tests := []struct { + name string + enableIPv6 bool + setupFunc func(t *testing.T) + cleanupFunc func(t *testing.T) + expectedDetected bool + description string + }{ + { + name: "v4_ipset_exists", + enableIPv6: false, + setupFunc: func(t *testing.T) { + v4Name := fmt.Sprintf(ipset.V4Name, config.ProbeIPSet) + err := netlink.IpsetCreate(v4Name, "hash:ip", netlink.IpsetCreateOptions{Replace: true}) + if err != nil && !strings.Contains(err.Error(), "exist") { + t.Fatalf("failed to create v4 ipset: %v", err) + } + t.Logf("Created v4 IPset: %s", v4Name) + }, + cleanupFunc: func(t *testing.T) { + v4Name := fmt.Sprintf(ipset.V4Name, config.ProbeIPSet) + _ = netlink.IpsetDestroy(v4Name) + }, + expectedDetected: true, + description: "Should detect when v4 IPset exists (simulates an existing iptables deployment)", + }, + { + name: "v6_ipset_exists", + enableIPv6: true, + setupFunc: func(t *testing.T) { + v6Name := fmt.Sprintf(ipset.V6Name, config.ProbeIPSet) + err := netlink.IpsetCreate(v6Name, "hash:ip", netlink.IpsetCreateOptions{ + Family: unix.AF_INET6, + Replace: true, + }) + if err != nil && !strings.Contains(err.Error(), "exist") { + t.Fatalf("failed to create v6 ipset: %v", err) + } + t.Logf("Created v6 IPset: %s", v6Name) + }, + cleanupFunc: func(t *testing.T) { + v6Name := fmt.Sprintf(ipset.V6Name, config.ProbeIPSet) + _ = netlink.IpsetDestroy(v6Name) + }, + expectedDetected: true, + description: "Should detect when v6 IPset exists", + }, + { + name: "both_v4_and_v6_ipsets_exist", + enableIPv6: true, + setupFunc: func(t *testing.T) { + v4Name := fmt.Sprintf(ipset.V4Name, config.ProbeIPSet) + v6Name := fmt.Sprintf(ipset.V6Name, config.ProbeIPSet) + + err := netlink.IpsetCreate(v4Name, "hash:ip", netlink.IpsetCreateOptions{Replace: true}) + if err != nil && !strings.Contains(err.Error(), "exist") { + t.Fatalf("failed to create v4 ipset: %v", err) + } + + err = netlink.IpsetCreate(v6Name, "hash:ip", netlink.IpsetCreateOptions{ + Family: unix.AF_INET6, + Replace: true, + }) + if err != nil && !strings.Contains(err.Error(), "exist") { + t.Fatalf("failed to create v6 ipset: %v", err) + } + t.Logf("Created both v4 and v6 IPsets") + }, + cleanupFunc: func(t *testing.T) { + v4Name := fmt.Sprintf(ipset.V4Name, config.ProbeIPSet) + v6Name := fmt.Sprintf(ipset.V6Name, config.ProbeIPSet) + _ = netlink.IpsetDestroy(v4Name) + _ = netlink.IpsetDestroy(v6Name) + }, + expectedDetected: true, + description: "Should detect when both v4 and v6 IPsets exist", + }, + { + name: "no_ipsets_exist", + enableIPv6: true, + setupFunc: func(t *testing.T) {}, + cleanupFunc: func(t *testing.T) {}, + expectedDetected: false, + description: "Should not detect artifacts in clean state (simulates a fresh setup)", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Log(tt.description) + + tt.setupFunc(t) + defer tt.cleanupFunc(t) + + detected, _ := detectIptablesArtifacts(tt.enableIPv6) + assert.Equal(t, detected, tt.expectedDetected) + }) + } +} + +var initialized = &sync.Once{} + +// setup initializes the test environment using unshare-go. +// Importing "github.com/howardjohn/unshare-go/netns" causes the test to run in an isolated network +// namespace and userns provides user namespace mapping. +func setup(t *testing.T) { + initialized.Do(func() { + // Map current GID to root (0) in the user namespace + // This gives us the necessary privileges for netlink operations (CAP_NET_ADMIN) + assert.NoError(t, userns.WriteGroupMap(map[uint32]uint32{userns.OriginalGID(): 0})) + t.Log("Successfully initialized an isolated test network namespace with root capabilities") + }) +} diff --git a/cni/pkg/nodeagent/server_linux.go b/cni/pkg/nodeagent/server_linux.go index 118c910507..a61fcab284 100644 --- a/cni/pkg/nodeagent/server_linux.go +++ b/cni/pkg/nodeagent/server_linux.go @@ -46,8 +46,32 @@ func initMeshDataplane(client kube.Client, args AmbientArgs) (*meshDataplane, er Reconcile: args.ReconcilePodRulesOnStartup, } + useNftables := args.NativeNftables + + // To support safe migration from iptables to nftables backend, detect if the host already has any iptable artifacts. + if useNftables { + log.Info("Native nftables is configured, checking for iptables artifacts...") + iptablesDetected, err := detectIptablesArtifacts(args.EnableIPv6) + + if iptablesDetected { + // Override nftables configuration and continue with iptables backend + log.Warnf("iptables artifacts detected (IPsets exist). " + + "Overriding nftables configuration and continuing with iptables backend. " + + "To complete migration to nftables, reboot the node.") + useNftables = false + } else { + if err != nil { + // Error while detecting the artifacts. Default to nftables (as requested) for a fail-safe behavior. + log.Warnf("iptables artifacts could not be detected (%v). "+ + "Proceeding with nftables backend as configured.", err) + } else { + log.Info("iptables artifacts not found, proceeding with nftables backend") + } + } + } + log.Infof("creating host addressSet manager in the node netns") - setManager, err := createHostNetworkAddrSetManager(args.NativeNftables, hostCfg.EnableIPv6) + setManager, err := createHostNetworkAddrSetManager(useNftables, hostCfg.EnableIPv6) if err != nil { return nil, fmt.Errorf("error initializing host addressSet manager: %w", err) } @@ -59,7 +83,7 @@ func initMeshDataplane(client kube.Client, args AmbientArgs) (*meshDataplane, er } hostTrafficManager, podTrafficManager, err := trafficmanager.NewTrafficRuleManager(&trafficmanager.TrafficRuleManagerConfig{ - NativeNftables: args.NativeNftables, + NativeNftables: useNftables, HostConfig: hostCfg, PodConfig: podCfg, HostDeps: realDependenciesHost(args.ForceIptablesBinary), @@ -70,7 +94,7 @@ func initMeshDataplane(client kube.Client, args AmbientArgs) (*meshDataplane, er return nil, fmt.Errorf("error creating traffic managers: %w", err) } - if !args.NativeNftables { + if !useNftables { // The nftables implementation will automatically flush any pre-existing chains when programming // the rules, so we skip the DeleteHostRules for nftables backend. hostTrafficManager.DeleteHostRules() diff --git a/cni/pkg/repair/netns_linux.go b/cni/pkg/repair/netns_linux.go index 53f29fa413..51b5c64e3b 100644 --- a/cni/pkg/repair/netns_linux.go +++ b/cni/pkg/repair/netns_linux.go @@ -47,22 +47,15 @@ func runInHost[T any](f func() (T, error)) (T, error) { } func checkInterfacesForMatchingAddr(targetAddr net.IP) (match bool, err error) { - var interfaces []net.Interface - if interfaces, err = net.Interfaces(); err != nil { - return false, fmt.Errorf("failed to get interfaces") + var addrs []net.Addr + if addrs, err = net.InterfaceAddrs(); err != nil { + return match, err } - - for _, ief := range interfaces { - var addrs []net.Addr - if addrs, err = ief.Addrs(); err != nil { - return match, err - } - for _, addr := range addrs { - switch v := addr.(type) { - case *net.IPNet: - if v.IP.Equal(targetAddr) { - return true, nil - } + for _, addr := range addrs { + switch v := addr.(type) { + case *net.IPNet: + if v.IP.Equal(targetAddr) { + return true, nil } } } diff --git a/common/.commonfiles.sha b/common/.commonfiles.sha index 74cad0294f..a876820769 100644 --- a/common/.commonfiles.sha +++ b/common/.commonfiles.sha @@ -1 +1 @@ -f335e7753c209c164f5c2d9d8f183fadc1e1cd98 +dec7a26170fc061fee7c159c1cf45220eacd9cf4 diff --git a/common/scripts/setup_env.sh b/common/scripts/setup_env.sh index 02b5ce5717..b7e690662c 100755 --- a/common/scripts/setup_env.sh +++ b/common/scripts/setup_env.sh @@ -77,7 +77,7 @@ fi TOOLS_REGISTRY_PROVIDER=${TOOLS_REGISTRY_PROVIDER:-gcr.io} PROJECT_ID=${PROJECT_ID:-istio-testing} if [[ "${IMAGE_VERSION:-}" == "" ]]; then - IMAGE_VERSION=master-8dcf63149d5bdaa83d1407a121098e8e8d1626dd + IMAGE_VERSION=master-3858f8db549f6f7f73de4b55fba5075f71d2651d fi if [[ "${IMAGE_NAME:-}" == "" ]]; then IMAGE_NAME=build-tools diff --git a/go.mod b/go.mod index d7bd2732d9..161bb326ea 100644 --- a/go.mod +++ b/go.mod @@ -12,14 +12,14 @@ require ( github.com/cenkalti/backoff/v4 v4.3.0 github.com/cespare/xxhash/v2 v2.3.0 github.com/cheggaaa/pb/v3 v3.1.7 - github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 + github.com/cncf/xds/go v0.0.0-20251110193048-8bfbf64dc13e github.com/containernetworking/cni v1.3.0 github.com/containernetworking/plugins v1.7.1 github.com/coreos/go-oidc/v3 v3.15.0 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/docker/cli v28.3.3+incompatible github.com/envoyproxy/go-control-plane/contrib v1.32.5-0.20250627145903-197b96a9c7f8 - github.com/envoyproxy/go-control-plane/envoy v1.35.1-0.20251009075907-cccc37f025be + github.com/envoyproxy/go-control-plane/envoy v1.36.1-0.20251120180717-7c66c7f1d0b2 github.com/evanphx/json-patch/v5 v5.9.11 github.com/fatih/color v1.18.0 github.com/felixge/fgprof v0.9.5 @@ -56,7 +56,7 @@ require ( github.com/prometheus/client_model v0.6.2 github.com/prometheus/common v0.66.1 github.com/prometheus/procfs v0.17.0 - github.com/prometheus/prometheus v0.305.0 + github.com/prometheus/prometheus v0.306.0 github.com/quic-go/quic-go v0.55.0 github.com/ryanuber/go-glob v1.0.0 github.com/spf13/cobra v1.9.1 @@ -76,7 +76,7 @@ require ( go.opentelemetry.io/otel/sdk v1.37.0 go.opentelemetry.io/otel/sdk/metric v1.37.0 go.opentelemetry.io/otel/trace v1.37.0 - go.opentelemetry.io/proto/otlp v1.7.1 + go.opentelemetry.io/proto/otlp v1.9.0 go.uber.org/atomic v1.11.0 go.uber.org/zap v1.27.0 golang.org/x/net v0.46.0 @@ -93,8 +93,8 @@ require ( gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.18.6 - istio.io/api v1.28.0-alpha.0.0.20251118133802-3d6b80ec2d1e - istio.io/client-go v1.28.0-alpha.0.0.20251118134000-fa71d5732509 + istio.io/api v1.28.0-alpha.0.0.20251210001900-ce7b5802387c + istio.io/client-go v1.28.0-alpha.0.0.20251210002059-b67694478a4b k8s.io/api v0.34.1 k8s.io/apiextensions-apiserver v0.34.1 k8s.io/apimachinery v0.34.1 @@ -106,7 +106,7 @@ require ( k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d sigs.k8s.io/controller-runtime v0.22.1 sigs.k8s.io/gateway-api v1.4.0 - sigs.k8s.io/gateway-api-inference-extension v0.0.0-20250917095812-173ad587b675 + sigs.k8s.io/gateway-api-inference-extension v0.0.0-20250926182816-0a3bb2010751 sigs.k8s.io/knftables v0.0.19-0.20250623122614-e4307300abb5 sigs.k8s.io/mcs-api v0.2.0 sigs.k8s.io/yaml v1.6.0 @@ -134,7 +134,6 @@ require ( github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.9.3 // indirect github.com/emicklei/go-restful/v3 v3.13.0 // indirect - github.com/envoyproxy/go-control-plane v0.13.5-0.20251013064519-48f97e33cb02 // indirect github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fatih/camelcase v1.0.0 // indirect @@ -154,7 +153,7 @@ require ( github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect diff --git a/go.sum b/go.sum index 255d42da1c..e40259f817 100644 --- a/go.sum +++ b/go.sum @@ -94,8 +94,8 @@ github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moA github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= -github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls= -github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cncf/xds/go v0.0.0-20251110193048-8bfbf64dc13e h1:gt7U1Igw0xbJdyaCM5H2CnlAlPSkzrhsebQB6WQWjLA= +github.com/cncf/xds/go v0.0.0-20251110193048-8bfbf64dc13e/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8= github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU= github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40= @@ -133,12 +133,12 @@ github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqI github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.13.5-0.20251013064519-48f97e33cb02 h1:VbmMEM1sWPDGm+v2MIa5dD55Ad/KrRXhARGJANxBV6U= -github.com/envoyproxy/go-control-plane v0.13.5-0.20251013064519-48f97e33cb02/go.mod h1:Alz8LEClvR7xKsrq3qzoc4N0guvVNSS8KmSChGYr9hs= +github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= +github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU= github.com/envoyproxy/go-control-plane/contrib v1.32.5-0.20250627145903-197b96a9c7f8 h1:KXgXPtBofHkRHr+8dO058dGZnLHapW7m0yJEgSYdAFA= github.com/envoyproxy/go-control-plane/contrib v1.32.5-0.20250627145903-197b96a9c7f8/go.mod h1:Nx/YcyEeIcgjT13QwKHdcPmS060urxZ835MeO8cLOrg= -github.com/envoyproxy/go-control-plane/envoy v1.35.1-0.20251009075907-cccc37f025be h1:u4SE0TvJwGK/iDu9YIK4zd16VOn19pBRSuKOm9KRskA= -github.com/envoyproxy/go-control-plane/envoy v1.35.1-0.20251009075907-cccc37f025be/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= +github.com/envoyproxy/go-control-plane/envoy v1.36.1-0.20251120180717-7c66c7f1d0b2 h1:BprNEu/ZERE6ACMAisXf1H8/1HmDseGxpvD70ltXiqo= +github.com/envoyproxy/go-control-plane/envoy v1.36.1-0.20251120180717-7c66c7f1d0b2/go.mod h1:6DQL0WI2n/JV923ZcB8Pr3Xi28osKAWBPpo/jDIKL6U= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= @@ -235,8 +235,8 @@ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4z github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -364,8 +364,8 @@ github.com/prometheus/otlptranslator v0.0.0-20250717125610-8549f4ab4f8f h1:QQB6S github.com/prometheus/otlptranslator v0.0.0-20250717125610-8549f4ab4f8f/go.mod h1:P8AwMgdD7XEr6QRUJ2QWLpiAZTgTE2UYgjlu3svompI= github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= -github.com/prometheus/prometheus v0.305.0 h1:UO/LsM32/E9yBDtvQj8tN+WwhbyWKR10lO35vmFLx0U= -github.com/prometheus/prometheus v0.305.0/go.mod h1:JG+jKIDUJ9Bn97anZiCjwCxRyAx+lpcEQ0QnZlUlbwY= +github.com/prometheus/prometheus v0.306.0 h1:Q0Pvz/ZKS6vVWCa1VSgNyNJlEe8hxdRlKklFg7SRhNw= +github.com/prometheus/prometheus v0.306.0/go.mod h1:7hMSGyZHt0dcmZ5r4kFPJ/vxPQU99N5/BGwSPDxeZrQ= github.com/prometheus/sigv4 v0.2.0 h1:qDFKnHYFswJxdzGeRP63c4HlH3Vbn1Yf/Ao2zabtVXk= github.com/prometheus/sigv4 v0.2.0/go.mod h1:D04rqmAaPPEUkjRQxGqjoxdyJuyCh6E0M18fZr0zBiE= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= @@ -470,8 +470,8 @@ go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFh go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= -go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= -go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= +go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= +go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= @@ -545,8 +545,8 @@ gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0 gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/api v0.238.0 h1:+EldkglWIg/pWjkq97sd+XxH7PxakNYoe/rkSTbnvOs= -google.golang.org/api v0.238.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50= +google.golang.org/api v0.239.0 h1:2hZKUnFZEy81eugPs4e2XzIJ5SOwQg0G82bpXD65Puo= +google.golang.org/api v0.239.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50= google.golang.org/genproto/googleapis/api v0.0.0-20251020155222-88f65dc88635 h1:1wvBeYv+A2zfEbxROscJl69OP0m74S8wGEO+Syat26o= google.golang.org/genproto/googleapis/api v0.0.0-20251020155222-88f65dc88635/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo= google.golang.org/genproto/googleapis/rpc v0.0.0-20251020155222-88f65dc88635 h1:3uycTxukehWrxH4HtPRtn1PDABTU331ViDjyqrUbaog= @@ -574,10 +574,10 @@ gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= helm.sh/helm/v3 v3.18.6 h1:S/2CqcYnNfLckkHLI0VgQbxgcDaU3N4A/46E3n9wSNY= helm.sh/helm/v3 v3.18.6/go.mod h1:L/dXDR2r539oPlFP1PJqKAC1CUgqHJDLkxKpDGrWnyg= -istio.io/api v1.28.0-alpha.0.0.20251118133802-3d6b80ec2d1e h1:qfJY+yQTjm0AWhPXITGSAGCLkujfBGh0Hu3RuXQeyq4= -istio.io/api v1.28.0-alpha.0.0.20251118133802-3d6b80ec2d1e/go.mod h1:BD3qv/ekm16kvSgvSpuiDawgKhEwG97wx849CednJSg= -istio.io/client-go v1.28.0-alpha.0.0.20251118134000-fa71d5732509 h1:U0SrRSda3au07r6b4ZbHa+NeKXkV4mVofPvTlQMF7oA= -istio.io/client-go v1.28.0-alpha.0.0.20251118134000-fa71d5732509/go.mod h1:G0jE4yjFmzhXjlTAi6/TYvE8jjFaWM34EpR9OfIb4Ac= +istio.io/api v1.28.0-alpha.0.0.20251210001900-ce7b5802387c h1:/LeROWG1fnEYKmDgSSmphn3IkPEBKZa5+7XU1idNev8= +istio.io/api v1.28.0-alpha.0.0.20251210001900-ce7b5802387c/go.mod h1:BD3qv/ekm16kvSgvSpuiDawgKhEwG97wx849CednJSg= +istio.io/client-go v1.28.0-alpha.0.0.20251210002059-b67694478a4b h1:DQI6BrG0f21heG6fzFgE5tziXnVEttTZlm4Nj5XH0QQ= +istio.io/client-go v1.28.0-alpha.0.0.20251210002059-b67694478a4b/go.mod h1:ttWw9sJTxuTthl9+A+ruUwJtnWWeTH30FtiXFPSpyXc= k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= @@ -608,8 +608,8 @@ sigs.k8s.io/controller-runtime v0.22.1 h1:Ah1T7I+0A7ize291nJZdS1CabF/lB4E++WizgV sigs.k8s.io/controller-runtime v0.22.1/go.mod h1:FwiwRjkRPbiN+zp2QRp7wlTCzbUXxZ/D4OzuQUDwBHY= sigs.k8s.io/gateway-api v1.4.0 h1:ZwlNM6zOHq0h3WUX2gfByPs2yAEsy/EenYJB78jpQfQ= sigs.k8s.io/gateway-api v1.4.0/go.mod h1:AR5RSqciWP98OPckEjOjh2XJhAe2Na4LHyXD2FUY7Qk= -sigs.k8s.io/gateway-api-inference-extension v0.0.0-20250917095812-173ad587b675 h1:CW+VWxazW54YzQnlHkyCE/WmAvF0YK7HOl+OK8IO3Ug= -sigs.k8s.io/gateway-api-inference-extension v0.0.0-20250917095812-173ad587b675/go.mod h1:Tqmt4U654MNQkzC1lFYVa43wbUW7K8r8Lv6Sq2I3HCc= +sigs.k8s.io/gateway-api-inference-extension v0.0.0-20250926182816-0a3bb2010751 h1:02xtteK+vH0484psoJ8QuncdEF8e4ElliMBUuKoWWZY= +sigs.k8s.io/gateway-api-inference-extension v0.0.0-20250926182816-0a3bb2010751/go.mod h1:x4ydLmxI0lGo0g7DY3qkIImagLGq5JuPHaDu3NLDj8k= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/knftables v0.0.19-0.20250623122614-e4307300abb5 h1:IvJLfQapBuBLXavhpQmA1raQx+VmPKTtRIT0Vmc0MvA= diff --git a/istio.deps b/istio.deps index a0a975080e..224d3a2158 100644 --- a/istio.deps +++ b/istio.deps @@ -4,13 +4,13 @@ "name": "PROXY_REPO_SHA", "repoName": "proxy", "file": "", - "lastStableSHA": "c3cad09527225b6a98dbf46ce81d066c580e038f" + "lastStableSHA": "e5bf144f631d45cd1cfd3c22850319d85ce24984" }, { "_comment": "", "name": "ZTUNNEL_REPO_SHA", "repoName": "ztunnel", "file": "", - "lastStableSHA": "d38548df2d52604a996d601cf2c69e8f3461cb76" + "lastStableSHA": "18a1b223ce0ea9ac68a27e163c41dd28488bf285" } ] diff --git a/istioctl/pkg/install/k8sversion/version.go b/istioctl/pkg/install/k8sversion/version.go index 4a88c2c2a8..d3d829527b 100644 --- a/istioctl/pkg/install/k8sversion/version.go +++ b/istioctl/pkg/install/k8sversion/version.go @@ -28,7 +28,7 @@ import ( const ( // MinK8SVersion is the minimum k8s version required to run this version of Istio // https://istio.io/docs/setup/platform-setup/ - MinK8SVersion = 29 + MinK8SVersion = 30 UnSupportedK8SVersionLogMsg = "\nThe Kubernetes version %s is not supported by Istio %s. The minimum supported Kubernetes version is 1.%d.\n" + "Proceeding with the installation, but you might experience problems. " + "See https://istio.io/latest/docs/releases/supported-releases/ for a list of supported versions.\n" diff --git a/istioctl/pkg/install/k8sversion/version_test.go b/istioctl/pkg/install/k8sversion/version_test.go index 52e8369dac..5cbda4bbc6 100644 --- a/istioctl/pkg/install/k8sversion/version_test.go +++ b/istioctl/pkg/install/k8sversion/version_test.go @@ -89,6 +89,11 @@ var ( Minor: "29", GitVersion: "v1.29", } + version1_30 = &version.Info{ + Major: "1", + Minor: "30", + GitVersion: "v1.30", + } version1_19RC = &version.Info{ Major: "1", Minor: "19", @@ -264,6 +269,11 @@ func TestIsK8VersionSupported(t *testing.T) { }, { version: version1_29, + logMsg: fmt.Sprintf(UnSupportedK8SVersionLogMsg, version1_29.GitVersion, pkgVersion.Info.Version, MinK8SVersion), + isValid: false, + }, + { + version: version1_30, isValid: true, }, } diff --git a/istioctl/pkg/proxyconfig/testdata/config_dump_summary.txt b/istioctl/pkg/proxyconfig/testdata/config_dump_summary.txt index d7e5b1307f..72fcd63919 100644 --- a/istioctl/pkg/proxyconfig/testdata/config_dump_summary.txt +++ b/istioctl/pkg/proxyconfig/testdata/config_dump_summary.txt @@ -19,7 +19,7 @@ route/inbound-vip|8000|http|httpbin.default.svc.cluster.local inbound|http|8 RESOURCE NAME TYPE STATUS VALID CERT SERIAL NUMBER NOT AFTER NOT BEFORE secret/default Cert Chain ACTIVE false 6fbee254c22900615cb1f74e3d2f1713 2023-05-16T01:32:52Z 2023-05-15T01:30:52Z -secret/ROOTCA CA ACTIVE true 193a543fe2b0d9cd4847675394dfc54 2033-05-02T03:41:33Z 2023-05-05T03:41:33Z +secret/ROOTCA CA ACTIVE true 0193a543fe2b0d9cd4847675394dfc54 2033-05-02T03:41:33Z 2023-05-05T03:41:33Z NAME STATUS LOCALITY CLUSTER endpoint/envoy://connect_originate/192.168.195.248:800 HEALTHY inbound-vip|8100|http|httpbin.default.svc.cluster.local diff --git a/istioctl/pkg/waypoint/testdata/waypoint/waypoint-notready-wait b/istioctl/pkg/waypoint/testdata/waypoint/waypoint-notready-wait new file mode 100644 index 0000000000..94cd6597ba --- /dev/null +++ b/istioctl/pkg/waypoint/testdata/waypoint/waypoint-notready-wait @@ -0,0 +1,2 @@ +Error: failed to print waypoint status: timed out while retrieving status for waypoint default/waypoint + diff --git a/istioctl/pkg/waypoint/testdata/waypoint/waypoint-status-all b/istioctl/pkg/waypoint/testdata/waypoint/waypoint-status-all new file mode 100644 index 0000000000..c649541ce9 --- /dev/null +++ b/istioctl/pkg/waypoint/testdata/waypoint/waypoint-status-all @@ -0,0 +1,4 @@ +NAMESPACE NAME STATUS TYPE REASON MESSAGE +fake1 waypoint True Programmed +fake2 waypoint True Programmed +fake3 waypoint True Programmed diff --git a/istioctl/pkg/waypoint/testdata/waypoint/waypoint-status-notready b/istioctl/pkg/waypoint/testdata/waypoint/waypoint-status-notready new file mode 100644 index 0000000000..ea4c0da202 --- /dev/null +++ b/istioctl/pkg/waypoint/testdata/waypoint/waypoint-status-notready @@ -0,0 +1,2 @@ +NAME STATUS TYPE REASON MESSAGE +waypoint False Programmed diff --git a/istioctl/pkg/waypoint/testdata/waypoint/waypoint-status-ready b/istioctl/pkg/waypoint/testdata/waypoint/waypoint-status-ready new file mode 100644 index 0000000000..49e6206698 --- /dev/null +++ b/istioctl/pkg/waypoint/testdata/waypoint/waypoint-status-ready @@ -0,0 +1,2 @@ +NAME STATUS TYPE REASON MESSAGE +waypoint True Programmed diff --git a/istioctl/pkg/waypoint/waypoint.go b/istioctl/pkg/waypoint/waypoint.go index 30ddbd1968..b876c12115 100644 --- a/istioctl/pkg/waypoint/waypoint.go +++ b/istioctl/pkg/waypoint/waypoint.go @@ -50,6 +50,7 @@ var ( allNamespaces bool waypointTimeout time.Duration + statusWaitReady bool deleteAll bool @@ -77,8 +78,8 @@ func Cmd(ctx cli.Context) *cobra.Command { } gw := gateway.Gateway{ TypeMeta: metav1.TypeMeta{ - Kind: gvk.KubernetesGateway_v1.Kind, - APIVersion: gvk.KubernetesGateway_v1.GroupVersion(), + Kind: gvk.KubernetesGateway.Kind, + APIVersion: gvk.KubernetesGateway.GroupVersion(), }, ObjectMeta: metav1.ObjectMeta{ Name: waypointName, @@ -120,13 +121,16 @@ func Cmd(ctx cli.Context) *cobra.Command { waypointStatusCmd := &cobra.Command{ Use: "status", - Short: "Show the status of waypoints in a namespace", - Long: "Show the status of waypoints for the namespace provided or default namespace if none is provided", + Short: "Show the status of waypoints", + Long: "Show the status of waypoints in the cluster", Example: ` # Show the status of the waypoint in the default namespace istioctl waypoint status # Show the status of the waypoint in a specific namespace - istioctl waypoint status --namespace default`, + istioctl waypoint status --namespace default + + # Show the status of the waypoint in all namespaces + istioctl waypoint status --all-namespaces`, Args: func(cmd *cobra.Command, args []string) error { if len(args) != 0 { return fmt.Errorf("unknown subcommand %q", args[0]) @@ -139,6 +143,9 @@ func Cmd(ctx cli.Context) *cobra.Command { return fmt.Errorf("failed to create Kubernetes client: %v", err) } ns := ctx.NamespaceOrDefault(ctx.Namespace()) + if allNamespaces { + ns = "" + } gws, err := kubeClient.GatewayAPI().GatewayV1().Gateways(ns). List(context.Background(), metav1.ListOptions{}) if err != nil { @@ -163,7 +170,7 @@ func Cmd(ctx cli.Context) *cobra.Command { } filteredGws = append(filteredGws, gw) } - err = printWaypointStatus(ctx, w, kubeClient, filteredGws) + err = printWaypointStatus(w, kubeClient, filteredGws, ns) if err != nil { return fmt.Errorf("failed to print waypoint status: %v", err) } @@ -464,7 +471,9 @@ func Cmd(ctx cli.Context) *cobra.Command { waypointApplyCmd.Flags().BoolVarP(&waitReady, "wait", "w", false, "Wait for the waypoint to be ready") waypointApplyCmd.Flags().DurationVar(&waypointTimeout, "waypoint-timeout", waitTimeout, "Timeout for waiting for waypoint ready") waypointGenerateCmd.Flags().StringVarP(&revision, "revision", "r", "", "The revision to label the waypoint with") + waypointStatusCmd.Flags().BoolVarP(&statusWaitReady, "wait", "w", true, "Wait for the waypoint to be ready") waypointStatusCmd.Flags().DurationVar(&waypointTimeout, "waypoint-timeout", waitTimeout, "Timeout for retrieving status for waypoint") + waypointStatusCmd.Flags().BoolVarP(&allNamespaces, "all-namespaces", "A", false, "Show the status of waypoints in all namespaces") waypointCmd.AddCommand(waypointGenerateCmd) waypointCmd.AddCommand(waypointDeleteCmd) waypointCmd.AddCommand(waypointListCmd) @@ -600,20 +609,20 @@ func errorWithMessage(errMsg string, gwc *gateway.Gateway, err error) error { return errors.New(errorMsg) } -func printWaypointStatus(ctx cli.Context, w *tabwriter.Writer, kubeClient kube.CLIClient, gw []gateway.Gateway) error { +func printWaypointStatus(w *tabwriter.Writer, kubeClient kube.CLIClient, gws []gateway.Gateway, ns string) error { var cond metav1.Condition startTime := time.Now() ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() - if ctx.Namespace() == "" { + if ns == "" { fmt.Fprintln(w, "NAMESPACE\tNAME\tSTATUS\tTYPE\tREASON\tMESSAGE") } else { fmt.Fprintln(w, "NAME\tSTATUS\tTYPE\tREASON\tMESSAGE") } - for _, gw := range gw { + for _, gw := range gws { for range ticker.C { programmed := false - gwc, err := kubeClient.GatewayAPI().GatewayV1().Gateways(ctx.NamespaceOrDefault(ctx.Namespace())).Get(context.TODO(), gw.Name, metav1.GetOptions{}) + gwc, err := kubeClient.GatewayAPI().GatewayV1().Gateways(gw.Namespace).Get(context.TODO(), gw.Name, metav1.GetOptions{}) if err == nil { // Check if gateway has Programmed condition set to true for _, cond = range gwc.Status.Conditions { @@ -623,13 +632,13 @@ func printWaypointStatus(ctx cli.Context, w *tabwriter.Writer, kubeClient kube.C } } } - if ctx.Namespace() == "" { + if ns == "" { fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", gwc.Namespace, gwc.Name, cond.Status, cond.Type, cond.Reason, cond.Message) } else { fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", gwc.Name, cond.Status, cond.Type, cond.Reason, cond.Message) } - if programmed { + if !statusWaitReady || programmed { break } diff --git a/istioctl/pkg/waypoint/waypoint_test.go b/istioctl/pkg/waypoint/waypoint_test.go index 0874b90462..46838495c0 100644 --- a/istioctl/pkg/waypoint/waypoint_test.go +++ b/istioctl/pkg/waypoint/waypoint_test.go @@ -22,6 +22,7 @@ import ( "strings" "testing" + "github.com/spf13/cobra" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" gateway "sigs.k8s.io/gateway-api/apis/v1" @@ -117,6 +118,89 @@ func TestWaypointList(t *testing.T) { } } +func TestWaypointStatus(t *testing.T) { + cases := []struct { + name string + args []string + gateways []*gateway.Gateway + expectedOutFile string + }{ + { + name: "waypoint ready", + args: strings.Split("status", " "), + gateways: []*gateway.Gateway{ + makeGateway(constants.DefaultNamespaceWaypoint, "default", true, true), + }, + expectedOutFile: "waypoint-status-ready", + }, + { + name: "show waypoints status in all namespaces", + args: strings.Split("status --all-namespaces", " "), + gateways: []*gateway.Gateway{ + makeGateway(constants.DefaultNamespaceWaypoint, "fake1", true, true), + makeGateway(constants.DefaultNamespaceWaypoint, "fake2", true, true), + makeGateway(constants.DefaultNamespaceWaypoint, "fake3", true, true), + }, + expectedOutFile: "waypoint-status-all", + }, + { + name: "waypoint not ready and without specifying --wait=false flag", + args: strings.Split("status --waypoint-timeout 0.5s", " "), + gateways: []*gateway.Gateway{ + makeGateway(constants.DefaultNamespaceWaypoint, "default", false, true), + }, + expectedOutFile: "waypoint-notready-wait", + }, + { + name: "waypoint not ready and specifying --wait=false flag", + args: strings.Split("status --wait=false --waypoint-timeout 0.5s", " "), + gateways: []*gateway.Gateway{ + makeGateway(constants.DefaultNamespaceWaypoint, "default", false, true), + }, + expectedOutFile: "waypoint-status-notready", + }, + } + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + ctx := cli.NewFakeContext(&cli.NewFakeContextOption{ + Namespace: "default", + }) + client, err := ctx.CLIClient() + if err != nil { + t.Fatal(err) + } + + for _, gw := range tt.gateways { + _, _ = client.GatewayAPI().GatewayV1().Gateways(gw.Namespace).Create(context.Background(), gw, metav1.CreateOptions{}) + } + defaultFile, err := os.ReadFile(fmt.Sprintf("testdata/waypoint/%s", tt.expectedOutFile)) + if err != nil { + t.Fatal(err) + } + expectedOut := string(defaultFile) + if len(expectedOut) == 0 { + t.Fatal("expected output is empty") + } + + var out bytes.Buffer + rootCmd := Cmd(ctx) + rootCmd.SetArgs(tt.args) + rootCmd.SetOut(&out) + rootCmd.SetErr(&out) + // disable Usage + rootCmd.SetUsageFunc(func(cmd *cobra.Command) error { + return nil + }) + + rootCmd.Execute() + output := out.String() + if output != expectedOut { + t.Fatalf("expected %s, got %s", expectedOut, output) + } + }) + } +} + func makeGateway(name, namespace string, programmed, isWaypoint bool) *gateway.Gateway { conditions := make([]metav1.Condition, 0) if programmed { diff --git a/istioctl/pkg/writer/compare/sds/util.go b/istioctl/pkg/writer/compare/sds/util.go index e6c9e7be00..26ac81716c 100644 --- a/istioctl/pkg/writer/compare/sds/util.go +++ b/istioctl/pkg/writer/compare/sds/util.go @@ -251,7 +251,7 @@ func secretMetaFromCert(rawCert []byte, trustDomain string) (SecretMeta, error) today := time.Now() return SecretMeta{ - SerialNumber: fmt.Sprintf("%x", cert.SerialNumber), + SerialNumber: fmt.Sprintf("%032x", cert.SerialNumber), NotAfter: cert.NotAfter.Format(time.RFC3339), NotBefore: cert.NotBefore.Format(time.RFC3339), Type: certType, diff --git a/istioctl/pkg/writer/envoy/configdump/testdata/secret/istio/output b/istioctl/pkg/writer/envoy/configdump/testdata/secret/istio/output index 8d4bb70b0c..8eed020107 100644 --- a/istioctl/pkg/writer/envoy/configdump/testdata/secret/istio/output +++ b/istioctl/pkg/writer/envoy/configdump/testdata/secret/istio/output @@ -1,4 +1,4 @@ RESOURCE NAME TYPE STATUS VALID CERT SERIAL NUMBER NOT AFTER NOT BEFORE configmap://some/thing WARMING false default Cert Chain ACTIVE false 6fbee254c22900615cb1f74e3d2f1713 2023-05-16T01:32:52Z 2023-05-15T01:30:52Z -ROOTCA CA ACTIVE true 193a543fe2b0d9cd4847675394dfc54 2033-05-02T03:41:33Z 2023-05-05T03:41:33Z +ROOTCA CA ACTIVE true 0193a543fe2b0d9cd4847675394dfc54 2033-05-02T03:41:33Z 2023-05-05T03:41:33Z diff --git a/manifests/addons/gen.sh b/manifests/addons/gen.sh index 10933d40c8..071577762a 100755 --- a/manifests/addons/gen.sh +++ b/manifests/addons/gen.sh @@ -33,8 +33,8 @@ GRAFANA_VERSION=${GRAFANA_VERSION:-"9.2.2"} { helm3 template kiali-server \ --namespace istio-system \ - --version 2.18.0 \ - --set deployment.image_version=v2.18 \ + --version 2.19.0 \ + --set deployment.image_version=v2.19 \ --include-crds \ kiali-server \ --repo https://kiali.org/helm-charts \ @@ -44,7 +44,7 @@ helm3 template kiali-server \ # Set up prometheus helm3 template prometheus prometheus \ --namespace istio-system \ - --version 27.45.0 \ + --version 27.47.0 \ --repo https://prometheus-community.github.io/helm-charts \ -f "${WD}/values-prometheus.yaml" \ > "${ADDONS}/prometheus.yaml" diff --git a/manifests/charts/base/files/crd-all.gen.yaml b/manifests/charts/base/files/crd-all.gen.yaml index 2a9248b643..9349214daa 100644 --- a/manifests/charts/base/files/crd-all.gen.yaml +++ b/manifests/charts/base/files/crd-all.gen.yaml @@ -10704,7 +10704,7 @@ spec: properties: bytes: description: response body as base64 encoded bytes. - format: binary + format: byte type: string string: type: string @@ -11756,7 +11756,7 @@ spec: properties: bytes: description: response body as base64 encoded bytes. - format: binary + format: byte type: string string: type: string @@ -12808,7 +12808,7 @@ spec: properties: bytes: description: response body as base64 encoded bytes. - format: binary + format: byte type: string string: type: string @@ -17178,12 +17178,16 @@ spec: - environment - required: - header + - required: + - formatter - required: - literal - required: - environment - required: - header + - required: + - formatter properties: environment: description: Environment adds the value of an environment @@ -17200,6 +17204,18 @@ spec: required: - name type: object + formatter: + description: Formatter adds the value of access logging + substitution formatter. + properties: + value: + description: The formatter tag value to use, same + formatter as HTTP access logging (e.g. + minLength: 1 + type: string + required: + - value + type: object header: description: RequestHeader adds the value of an header from the request to each span. @@ -17626,12 +17642,16 @@ spec: - environment - required: - header + - required: + - formatter - required: - literal - required: - environment - required: - header + - required: + - formatter properties: environment: description: Environment adds the value of an environment @@ -17648,6 +17668,18 @@ spec: required: - name type: object + formatter: + description: Formatter adds the value of access logging + substitution formatter. + properties: + value: + description: The formatter tag value to use, same + formatter as HTTP access logging (e.g. + minLength: 1 + type: string + required: + - value + type: object header: description: RequestHeader adds the value of an header from the request to each span. diff --git a/manifests/charts/gateway/values.schema.json b/manifests/charts/gateway/values.schema.json index 739a67b775..970f8a830a 100644 --- a/manifests/charts/gateway/values.schema.json +++ b/manifests/charts/gateway/values.schema.json @@ -11,6 +11,10 @@ "global": { "type": "object" }, + "enabled": { + "description": "Field used as a condition when this chart is included as a dependency. It's allowed in the schema, but the chart itself does not read it. For more information see: https://helm.sh/docs/chart_best_practices/dependencies/#conditions-and-tags.", + "type": "boolean" + }, "affinity": { "type": "object" }, diff --git a/manifests/charts/istio-control/istio-discovery/files/kube-gateway.yaml b/manifests/charts/istio-control/istio-discovery/files/kube-gateway.yaml index 2ddb473fd9..8d909beb83 100644 --- a/manifests/charts/istio-control/istio-discovery/files/kube-gateway.yaml +++ b/manifests/charts/istio-control/istio-discovery/files/kube-gateway.yaml @@ -15,7 +15,7 @@ metadata: {{- if ge .KubeVersion 128 }} # Safe since 1.28: https://github.com/kubernetes/kubernetes/pull/117412 ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: "{{.Name}}" uid: "{{.UID}}" @@ -37,7 +37,7 @@ metadata: "gateway.istio.io/managed" "istio.io-gateway-controller" ) | nindent 4 }} ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: {{.Name}} uid: "{{.UID}}" @@ -338,7 +338,7 @@ metadata: name: {{.DeploymentName | quote}} namespace: {{.Namespace | quote}} ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: {{.Name}} uid: {{.UID}} @@ -373,7 +373,7 @@ metadata: "gateway.networking.k8s.io/gateway-class-name" .GatewayClass ) | nindent 4 }} ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: {{.Name}} uid: "{{.UID}}" @@ -399,7 +399,7 @@ metadata: "gateway.networking.k8s.io/gateway-class-name" .GatewayClass ) | nindent 4 }} ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: {{.Name}} uid: "{{.UID}}" diff --git a/manifests/charts/istio-control/istio-discovery/files/waypoint.yaml b/manifests/charts/istio-control/istio-discovery/files/waypoint.yaml index 967897b362..644d8780c3 100644 --- a/manifests/charts/istio-control/istio-discovery/files/waypoint.yaml +++ b/manifests/charts/istio-control/istio-discovery/files/waypoint.yaml @@ -15,7 +15,7 @@ metadata: {{- if ge .KubeVersion 128 }} # Safe since 1.28: https://github.com/kubernetes/kubernetes/pull/117412 ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: "{{.Name}}" uid: "{{.UID}}" @@ -37,7 +37,7 @@ metadata: "gateway.istio.io/managed" .ControllerLabel ) | nindent 4 }} ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: "{{.Name}}" uid: "{{.UID}}" @@ -336,7 +336,7 @@ metadata: name: {{.DeploymentName | quote}} namespace: {{.Namespace | quote}} ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: "{{.Name}}" uid: "{{.UID}}" @@ -371,7 +371,7 @@ metadata: "gateway.networking.k8s.io/gateway-class-name" .GatewayClass ) | nindent 4 }} ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: {{.Name}} uid: "{{.UID}}" @@ -397,7 +397,7 @@ metadata: "gateway.networking.k8s.io/gateway-class-name" .GatewayClass ) | nindent 4 }} ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: {{.Name}} uid: "{{.UID}}" diff --git a/manifests/charts/istio-control/istio-discovery/templates/reader-clusterrole.yaml b/manifests/charts/istio-control/istio-discovery/templates/reader-clusterrole.yaml index e0b0ff42a4..af795f1f5a 100644 --- a/manifests/charts/istio-control/istio-discovery/templates/reader-clusterrole.yaml +++ b/manifests/charts/istio-control/istio-discovery/templates/reader-clusterrole.yaml @@ -22,7 +22,7 @@ rules: resources: ["*"] verbs: ["get", "list", "watch"] - apiGroups: [""] - resources: ["endpoints", "pods", "services", "nodes", "replicationcontrollers", "namespaces", "secrets"] + resources: ["endpoints", "pods", "services", "nodes", "replicationcontrollers", "namespaces", "secrets", "configmaps"] verbs: ["get", "list", "watch"] - apiGroups: ["networking.istio.io"] verbs: [ "get", "watch", "list" ] diff --git a/pilot/cmd/pilot-agent/status/ready/probe_test.go b/pilot/cmd/pilot-agent/status/ready/probe_test.go index 12cc18d709..8b191b3fd4 100644 --- a/pilot/cmd/pilot-agent/status/ready/probe_test.go +++ b/pilot/cmd/pilot-agent/status/ready/probe_test.go @@ -36,8 +36,7 @@ func TestEnvoyStatsCompleteAndSuccessful(t *testing.T) { server := testserver.CreateAndStartServer(liveServerStats) defer server.Close() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() probe := Probe{AdminPort: uint16(server.Listener.Addr().(*net.TCPAddr).Port)} probe.Context = ctx @@ -111,8 +110,7 @@ server.state: 0`, t.Run(tt.name, func(t *testing.T) { server := testserver.CreateAndStartServer(tt.stats) defer server.Close() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() probe := Probe{AdminPort: uint16(server.Listener.Addr().(*net.TCPAddr).Port)} probe.Context = ctx err := probe.Check() @@ -137,8 +135,7 @@ func TestEnvoyInitializing(t *testing.T) { server := testserver.CreateAndStartServer(initServerStats) defer server.Close() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() probe := Probe{AdminPort: uint16(server.Listener.Addr().(*net.TCPAddr).Port)} probe.Context = ctx err := probe.Check() @@ -151,8 +148,7 @@ func TestEnvoyNoClusterManagerStats(t *testing.T) { server := testserver.CreateAndStartServer(onlyServerStats) defer server.Close() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() probe := Probe{AdminPort: uint16(server.Listener.Addr().(*net.TCPAddr).Port)} probe.Context = ctx err := probe.Check() @@ -165,8 +161,7 @@ func TestEnvoyNoServerStats(t *testing.T) { server := testserver.CreateAndStartServer(noServerStats) defer server.Close() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() probe := Probe{AdminPort: uint16(server.Listener.Addr().(*net.TCPAddr).Port)} probe.Context = ctx err := probe.Check() @@ -178,8 +173,7 @@ func TestEnvoyReadinessCache(t *testing.T) { g := NewWithT(t) server := testserver.CreateAndStartServer(noServerStats) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() probe := Probe{AdminPort: uint16(server.Listener.Addr().(*net.TCPAddr).Port)} probe.Context = ctx err := probe.Check() diff --git a/pilot/pkg/config/kube/crd/conversion_test.go b/pilot/pkg/config/kube/crd/conversion_test.go index e6e4ca1efb..8f1877fab0 100644 --- a/pilot/pkg/config/kube/crd/conversion_test.go +++ b/pilot/pkg/config/kube/crd/conversion_test.go @@ -18,7 +18,7 @@ import ( "encoding/json" "testing" - gateway "sigs.k8s.io/gateway-api/apis/v1beta1" + gateway "sigs.k8s.io/gateway-api/apis/v1" "istio.io/api/meta/v1alpha1" "istio.io/istio/pilot/test/mock" diff --git a/pilot/pkg/config/kube/crdclient/types.gen.go b/pilot/pkg/config/kube/crdclient/types.gen.go index 468f5320d5..d04e1202db 100644 --- a/pilot/pkg/config/kube/crdclient/types.gen.go +++ b/pilot/pkg/config/kube/crdclient/types.gen.go @@ -78,14 +78,14 @@ func create(c kube.Client, cfg config.Config, objMeta metav1.ObjectMeta) (metav1 Spec: *(cfg.Spec.(*istioioapinetworkingv1alpha3.Gateway)), }, metav1.CreateOptions{}) case gvk.GatewayClass: - return c.GatewayAPI().GatewayV1beta1().GatewayClasses().Create(context.TODO(), &sigsk8siogatewayapiapisv1beta1.GatewayClass{ + return c.GatewayAPI().GatewayV1().GatewayClasses().Create(context.TODO(), &sigsk8siogatewayapiapisv1.GatewayClass{ ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*sigsk8siogatewayapiapisv1beta1.GatewayClassSpec)), + Spec: *(cfg.Spec.(*sigsk8siogatewayapiapisv1.GatewayClassSpec)), }, metav1.CreateOptions{}) case gvk.HTTPRoute: - return c.GatewayAPI().GatewayV1beta1().HTTPRoutes(cfg.Namespace).Create(context.TODO(), &sigsk8siogatewayapiapisv1beta1.HTTPRoute{ + return c.GatewayAPI().GatewayV1().HTTPRoutes(cfg.Namespace).Create(context.TODO(), &sigsk8siogatewayapiapisv1.HTTPRoute{ ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*sigsk8siogatewayapiapisv1beta1.HTTPRouteSpec)), + Spec: *(cfg.Spec.(*sigsk8siogatewayapiapisv1.HTTPRouteSpec)), }, metav1.CreateOptions{}) case gvk.InferencePool: return c.GatewayAPIInference().InferenceV1().InferencePools(cfg.Namespace).Create(context.TODO(), &sigsk8siogatewayapiinferenceextensionapiv1.InferencePool{ @@ -93,9 +93,9 @@ func create(c kube.Client, cfg config.Config, objMeta metav1.ObjectMeta) (metav1 Spec: *(cfg.Spec.(*sigsk8siogatewayapiinferenceextensionapiv1.InferencePoolSpec)), }, metav1.CreateOptions{}) case gvk.KubernetesGateway: - return c.GatewayAPI().GatewayV1beta1().Gateways(cfg.Namespace).Create(context.TODO(), &sigsk8siogatewayapiapisv1beta1.Gateway{ + return c.GatewayAPI().GatewayV1().Gateways(cfg.Namespace).Create(context.TODO(), &sigsk8siogatewayapiapisv1.Gateway{ ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*sigsk8siogatewayapiapisv1beta1.GatewaySpec)), + Spec: *(cfg.Spec.(*sigsk8siogatewayapiapisv1.GatewaySpec)), }, metav1.CreateOptions{}) case gvk.PeerAuthentication: return c.Istio().SecurityV1().PeerAuthentications(cfg.Namespace).Create(context.TODO(), &apiistioioapisecurityv1.PeerAuthentication{ @@ -215,14 +215,14 @@ func update(c kube.Client, cfg config.Config, objMeta metav1.ObjectMeta) (metav1 Spec: *(cfg.Spec.(*istioioapinetworkingv1alpha3.Gateway)), }, metav1.UpdateOptions{}) case gvk.GatewayClass: - return c.GatewayAPI().GatewayV1beta1().GatewayClasses().Update(context.TODO(), &sigsk8siogatewayapiapisv1beta1.GatewayClass{ + return c.GatewayAPI().GatewayV1().GatewayClasses().Update(context.TODO(), &sigsk8siogatewayapiapisv1.GatewayClass{ ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*sigsk8siogatewayapiapisv1beta1.GatewayClassSpec)), + Spec: *(cfg.Spec.(*sigsk8siogatewayapiapisv1.GatewayClassSpec)), }, metav1.UpdateOptions{}) case gvk.HTTPRoute: - return c.GatewayAPI().GatewayV1beta1().HTTPRoutes(cfg.Namespace).Update(context.TODO(), &sigsk8siogatewayapiapisv1beta1.HTTPRoute{ + return c.GatewayAPI().GatewayV1().HTTPRoutes(cfg.Namespace).Update(context.TODO(), &sigsk8siogatewayapiapisv1.HTTPRoute{ ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*sigsk8siogatewayapiapisv1beta1.HTTPRouteSpec)), + Spec: *(cfg.Spec.(*sigsk8siogatewayapiapisv1.HTTPRouteSpec)), }, metav1.UpdateOptions{}) case gvk.InferencePool: return c.GatewayAPIInference().InferenceV1().InferencePools(cfg.Namespace).Update(context.TODO(), &sigsk8siogatewayapiinferenceextensionapiv1.InferencePool{ @@ -230,9 +230,9 @@ func update(c kube.Client, cfg config.Config, objMeta metav1.ObjectMeta) (metav1 Spec: *(cfg.Spec.(*sigsk8siogatewayapiinferenceextensionapiv1.InferencePoolSpec)), }, metav1.UpdateOptions{}) case gvk.KubernetesGateway: - return c.GatewayAPI().GatewayV1beta1().Gateways(cfg.Namespace).Update(context.TODO(), &sigsk8siogatewayapiapisv1beta1.Gateway{ + return c.GatewayAPI().GatewayV1().Gateways(cfg.Namespace).Update(context.TODO(), &sigsk8siogatewayapiapisv1.Gateway{ ObjectMeta: objMeta, - Spec: *(cfg.Spec.(*sigsk8siogatewayapiapisv1beta1.GatewaySpec)), + Spec: *(cfg.Spec.(*sigsk8siogatewayapiapisv1.GatewaySpec)), }, metav1.UpdateOptions{}) case gvk.PeerAuthentication: return c.Istio().SecurityV1().PeerAuthentications(cfg.Namespace).Update(context.TODO(), &apiistioioapisecurityv1.PeerAuthentication{ @@ -352,14 +352,14 @@ func updateStatus(c kube.Client, cfg config.Config, objMeta metav1.ObjectMeta) ( Status: *(cfg.Status.(*istioioapimetav1alpha1.IstioStatus)), }, metav1.UpdateOptions{}) case gvk.GatewayClass: - return c.GatewayAPI().GatewayV1beta1().GatewayClasses().UpdateStatus(context.TODO(), &sigsk8siogatewayapiapisv1beta1.GatewayClass{ + return c.GatewayAPI().GatewayV1().GatewayClasses().UpdateStatus(context.TODO(), &sigsk8siogatewayapiapisv1.GatewayClass{ ObjectMeta: objMeta, - Status: *(cfg.Status.(*sigsk8siogatewayapiapisv1beta1.GatewayClassStatus)), + Status: *(cfg.Status.(*sigsk8siogatewayapiapisv1.GatewayClassStatus)), }, metav1.UpdateOptions{}) case gvk.HTTPRoute: - return c.GatewayAPI().GatewayV1beta1().HTTPRoutes(cfg.Namespace).UpdateStatus(context.TODO(), &sigsk8siogatewayapiapisv1beta1.HTTPRoute{ + return c.GatewayAPI().GatewayV1().HTTPRoutes(cfg.Namespace).UpdateStatus(context.TODO(), &sigsk8siogatewayapiapisv1.HTTPRoute{ ObjectMeta: objMeta, - Status: *(cfg.Status.(*sigsk8siogatewayapiapisv1beta1.HTTPRouteStatus)), + Status: *(cfg.Status.(*sigsk8siogatewayapiapisv1.HTTPRouteStatus)), }, metav1.UpdateOptions{}) case gvk.InferencePool: return c.GatewayAPIInference().InferenceV1().InferencePools(cfg.Namespace).UpdateStatus(context.TODO(), &sigsk8siogatewayapiinferenceextensionapiv1.InferencePool{ @@ -372,9 +372,9 @@ func updateStatus(c kube.Client, cfg config.Config, objMeta metav1.ObjectMeta) ( Status: *(cfg.Status.(*k8sioapinetworkingv1.IngressStatus)), }, metav1.UpdateOptions{}) case gvk.KubernetesGateway: - return c.GatewayAPI().GatewayV1beta1().Gateways(cfg.Namespace).UpdateStatus(context.TODO(), &sigsk8siogatewayapiapisv1beta1.Gateway{ + return c.GatewayAPI().GatewayV1().Gateways(cfg.Namespace).UpdateStatus(context.TODO(), &sigsk8siogatewayapiapisv1.Gateway{ ObjectMeta: objMeta, - Status: *(cfg.Status.(*sigsk8siogatewayapiapisv1beta1.GatewayStatus)), + Status: *(cfg.Status.(*sigsk8siogatewayapiapisv1.GatewayStatus)), }, metav1.UpdateOptions{}) case gvk.PeerAuthentication: return c.Istio().SecurityV1().PeerAuthentications(cfg.Namespace).UpdateStatus(context.TODO(), &apiistioioapisecurityv1.PeerAuthentication{ @@ -552,34 +552,34 @@ func patch(c kube.Client, orig config.Config, origMeta metav1.ObjectMeta, mod co return c.Istio().NetworkingV1().Gateways(orig.Namespace). Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) case gvk.GatewayClass: - oldRes := &sigsk8siogatewayapiapisv1beta1.GatewayClass{ + oldRes := &sigsk8siogatewayapiapisv1.GatewayClass{ ObjectMeta: origMeta, - Spec: *(orig.Spec.(*sigsk8siogatewayapiapisv1beta1.GatewayClassSpec)), + Spec: *(orig.Spec.(*sigsk8siogatewayapiapisv1.GatewayClassSpec)), } - modRes := &sigsk8siogatewayapiapisv1beta1.GatewayClass{ + modRes := &sigsk8siogatewayapiapisv1.GatewayClass{ ObjectMeta: modMeta, - Spec: *(mod.Spec.(*sigsk8siogatewayapiapisv1beta1.GatewayClassSpec)), + Spec: *(mod.Spec.(*sigsk8siogatewayapiapisv1.GatewayClassSpec)), } patchBytes, err := genPatchBytes(oldRes, modRes, typ) if err != nil { return nil, err } - return c.GatewayAPI().GatewayV1beta1().GatewayClasses(). + return c.GatewayAPI().GatewayV1().GatewayClasses(). Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) case gvk.HTTPRoute: - oldRes := &sigsk8siogatewayapiapisv1beta1.HTTPRoute{ + oldRes := &sigsk8siogatewayapiapisv1.HTTPRoute{ ObjectMeta: origMeta, - Spec: *(orig.Spec.(*sigsk8siogatewayapiapisv1beta1.HTTPRouteSpec)), + Spec: *(orig.Spec.(*sigsk8siogatewayapiapisv1.HTTPRouteSpec)), } - modRes := &sigsk8siogatewayapiapisv1beta1.HTTPRoute{ + modRes := &sigsk8siogatewayapiapisv1.HTTPRoute{ ObjectMeta: modMeta, - Spec: *(mod.Spec.(*sigsk8siogatewayapiapisv1beta1.HTTPRouteSpec)), + Spec: *(mod.Spec.(*sigsk8siogatewayapiapisv1.HTTPRouteSpec)), } patchBytes, err := genPatchBytes(oldRes, modRes, typ) if err != nil { return nil, err } - return c.GatewayAPI().GatewayV1beta1().HTTPRoutes(orig.Namespace). + return c.GatewayAPI().GatewayV1().HTTPRoutes(orig.Namespace). Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) case gvk.InferencePool: oldRes := &sigsk8siogatewayapiinferenceextensionapiv1.InferencePool{ @@ -597,19 +597,19 @@ func patch(c kube.Client, orig config.Config, origMeta metav1.ObjectMeta, mod co return c.GatewayAPIInference().InferenceV1().InferencePools(orig.Namespace). Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) case gvk.KubernetesGateway: - oldRes := &sigsk8siogatewayapiapisv1beta1.Gateway{ + oldRes := &sigsk8siogatewayapiapisv1.Gateway{ ObjectMeta: origMeta, - Spec: *(orig.Spec.(*sigsk8siogatewayapiapisv1beta1.GatewaySpec)), + Spec: *(orig.Spec.(*sigsk8siogatewayapiapisv1.GatewaySpec)), } - modRes := &sigsk8siogatewayapiapisv1beta1.Gateway{ + modRes := &sigsk8siogatewayapiapisv1.Gateway{ ObjectMeta: modMeta, - Spec: *(mod.Spec.(*sigsk8siogatewayapiapisv1beta1.GatewaySpec)), + Spec: *(mod.Spec.(*sigsk8siogatewayapiapisv1.GatewaySpec)), } patchBytes, err := genPatchBytes(oldRes, modRes, typ) if err != nil { return nil, err } - return c.GatewayAPI().GatewayV1beta1().Gateways(orig.Namespace). + return c.GatewayAPI().GatewayV1().Gateways(orig.Namespace). Patch(context.TODO(), orig.Name, typ, patchBytes, metav1.PatchOptions{FieldManager: "pilot-discovery"}) case gvk.PeerAuthentication: oldRes := &apiistioioapisecurityv1.PeerAuthentication{ @@ -875,13 +875,13 @@ func delete(c kube.Client, typ config.GroupVersionKind, name, namespace string, case gvk.Gateway: return c.Istio().NetworkingV1().Gateways(namespace).Delete(context.TODO(), name, deleteOptions) case gvk.GatewayClass: - return c.GatewayAPI().GatewayV1beta1().GatewayClasses().Delete(context.TODO(), name, deleteOptions) + return c.GatewayAPI().GatewayV1().GatewayClasses().Delete(context.TODO(), name, deleteOptions) case gvk.HTTPRoute: - return c.GatewayAPI().GatewayV1beta1().HTTPRoutes(namespace).Delete(context.TODO(), name, deleteOptions) + return c.GatewayAPI().GatewayV1().HTTPRoutes(namespace).Delete(context.TODO(), name, deleteOptions) case gvk.InferencePool: return c.GatewayAPIInference().InferenceV1().InferencePools(namespace).Delete(context.TODO(), name, deleteOptions) case gvk.KubernetesGateway: - return c.GatewayAPI().GatewayV1beta1().Gateways(namespace).Delete(context.TODO(), name, deleteOptions) + return c.GatewayAPI().GatewayV1().Gateways(namespace).Delete(context.TODO(), name, deleteOptions) case gvk.PeerAuthentication: return c.Istio().SecurityV1().PeerAuthentications(namespace).Delete(context.TODO(), name, deleteOptions) case gvk.ProxyConfig: @@ -1180,7 +1180,7 @@ var translationMap = map[config.GroupVersionKind]func(r runtime.Object) config.C } }, gvk.GatewayClass: func(r runtime.Object) config.Config { - obj := r.(*sigsk8siogatewayapiapisv1beta1.GatewayClass) + obj := r.(*sigsk8siogatewayapiapisv1.GatewayClass) return config.Config{ Meta: config.Meta{ GroupVersionKind: gvk.GatewayClass, @@ -1199,7 +1199,7 @@ var translationMap = map[config.GroupVersionKind]func(r runtime.Object) config.C } }, gvk.HTTPRoute: func(r runtime.Object) config.Config { - obj := r.(*sigsk8siogatewayapiapisv1beta1.HTTPRoute) + obj := r.(*sigsk8siogatewayapiapisv1.HTTPRoute) return config.Config{ Meta: config.Meta{ GroupVersionKind: gvk.HTTPRoute, @@ -1293,7 +1293,7 @@ var translationMap = map[config.GroupVersionKind]func(r runtime.Object) config.C } }, gvk.KubernetesGateway: func(r runtime.Object) config.Config { - obj := r.(*sigsk8siogatewayapiapisv1beta1.Gateway) + obj := r.(*sigsk8siogatewayapiapisv1.Gateway) return config.Config{ Meta: config.Meta{ GroupVersionKind: gvk.KubernetesGateway, diff --git a/pilot/pkg/config/kube/gateway/backend_policies.go b/pilot/pkg/config/kube/gateway/backend_policies.go index 054f642d65..827ea18b0b 100644 --- a/pilot/pkg/config/kube/gateway/backend_policies.go +++ b/pilot/pkg/config/kube/gateway/backend_policies.go @@ -133,17 +133,17 @@ func DestinationRuleCollection( opts krt.OptionsBuilder, ) krt.Collection[*config.Config] { trafficPolicyStatus, backendTrafficPolicies := BackendTrafficPolicyCollection(trafficPolicies, references, domainSuffix, opts) - status.RegisterStatus(c.status, trafficPolicyStatus, GetStatus) + status.RegisterStatus(c.status, trafficPolicyStatus, GetStatus, c.tagWatcher.AccessUnprotected()) // TODO: BackendTrafficPolicy should also probably use ancestorCollection. However, its still up for debate in the // Gateway API community if having the Gateway as an ancestor ref is required or not; we would prefer it to not be if possible. // Until conformance requires it, for now we skip it. ancestorCollection := ancestors.AsCollection(append(opts.WithName("AncestorBackend"), TypedNamespacedNameIndexCollectionFunc)...) tlsPolicyStatus, backendTLSPolicies := BackendTLSPolicyCollection(tlsPolicies, ancestorCollection, references, domainSuffix, opts) - status.RegisterStatus(c.status, tlsPolicyStatus, GetStatus) + status.RegisterStatus(c.status, tlsPolicyStatus, GetStatus, c.tagWatcher.AccessUnprotected()) // We need to merge these by hostname into a single DR - allPolicies := krt.JoinCollection([]krt.Collection[BackendPolicy]{backendTrafficPolicies, backendTLSPolicies}) + allPolicies := krt.JoinCollection([]krt.Collection[BackendPolicy]{backendTrafficPolicies, backendTLSPolicies}, opts.WithName("AllBackendPolicies")...) byTargetAndHost := krt.NewIndex(allPolicies, "targetAndHost", func(o BackendPolicy) []TypedNamespacedNamePerHost { return []TypedNamespacedNamePerHost{{Target: o.Target, Host: o.Host}} }) diff --git a/pilot/pkg/config/kube/gateway/conditions_test.go b/pilot/pkg/config/kube/gateway/conditions_test.go index 4697fd7109..4540956680 100644 --- a/pilot/pkg/config/kube/gateway/conditions_test.go +++ b/pilot/pkg/config/kube/gateway/conditions_test.go @@ -19,7 +19,7 @@ import ( "testing" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - k8s "sigs.k8s.io/gateway-api/apis/v1beta1" + k8s "sigs.k8s.io/gateway-api/apis/v1" "istio.io/istio/pilot/pkg/features" "istio.io/istio/pkg/config" diff --git a/pilot/pkg/config/kube/gateway/controller.go b/pilot/pkg/config/kube/gateway/controller.go index c3000a1963..83dc40f5a7 100644 --- a/pilot/pkg/config/kube/gateway/controller.go +++ b/pilot/pkg/config/kube/gateway/controller.go @@ -139,9 +139,9 @@ type Inputs struct { Secrets krt.Collection[*corev1.Secret] ConfigMaps krt.Collection[*corev1.ConfigMap] - GatewayClasses krt.Collection[*gateway.GatewayClass] - Gateways krt.Collection[*gateway.Gateway] - HTTPRoutes krt.Collection[*gateway.HTTPRoute] + GatewayClasses krt.Collection[*gatewayv1.GatewayClass] + Gateways krt.Collection[*gatewayv1.Gateway] + HTTPRoutes krt.Collection[*gatewayv1.HTTPRoute] GRPCRoutes krt.Collection[*gatewayv1.GRPCRoute] TCPRoutes krt.Collection[*gatewayalpha.TCPRoute] TLSRoutes krt.Collection[*gatewayalpha.TLSRoute] @@ -197,9 +197,9 @@ func NewController( opts.WithName("informer/ConfigMaps")..., ), Services: krt.WrapClient(svcClient, opts.WithName("informer/Services")...), - GatewayClasses: buildClient[*gateway.GatewayClass](c, kc, gvr.GatewayClass, opts, "informer/GatewayClasses"), - Gateways: buildClient[*gateway.Gateway](c, kc, gvr.KubernetesGateway, opts, "informer/Gateways"), - HTTPRoutes: buildClient[*gateway.HTTPRoute](c, kc, gvr.HTTPRoute, opts, "informer/HTTPRoutes"), + GatewayClasses: buildClient[*gatewayv1.GatewayClass](c, kc, gvr.GatewayClass, opts, "informer/GatewayClasses"), + Gateways: buildClient[*gatewayv1.Gateway](c, kc, gvr.KubernetesGateway, opts, "informer/Gateways"), + HTTPRoutes: buildClient[*gatewayv1.HTTPRoute](c, kc, gvr.HTTPRoute, opts, "informer/HTTPRoutes"), GRPCRoutes: buildClient[*gatewayv1.GRPCRoute](c, kc, gvr.GRPCRoute, opts, "informer/GRPCRoutes"), BackendTLSPolicies: buildClient[*gatewayv1.BackendTLSPolicy](c, kc, gvr.BackendTLSPolicy, opts, "informer/BackendTLSPolicies"), @@ -238,7 +238,7 @@ func NewController( httpRoutesByInferencePool := krt.NewIndex(inputs.HTTPRoutes, "inferencepool-route", indexHTTPRouteByInferencePool) GatewayClassStatus, GatewayClasses := GatewayClassesCollection(inputs.GatewayClasses, opts) - status.RegisterStatus(c.status, GatewayClassStatus, GetStatus) + status.RegisterStatus(c.status, GatewayClassStatus, GetStatus, c.tagWatcher.AccessUnprotected()) ReferenceGrants := BuildReferenceGrants(ReferenceGrantsCollection(inputs.ReferenceGrants, opts)) ListenerSetStatus, ListenerSets := ListenerSetCollection( @@ -254,7 +254,7 @@ func NewController( c.tagWatcher, opts, ) - status.RegisterStatus(c.status, ListenerSetStatus, GetStatus) + status.RegisterStatus(c.status, ListenerSetStatus, GetStatus, c.tagWatcher.AccessUnprotected()) // GatewaysStatus is not fully complete until its join with route attachments to report attachedRoutes. // Do not register yet. @@ -289,7 +289,7 @@ func NewController( controllers.WithMaxAttempts(5)) if features.EnableGatewayAPIInferenceExtension { - status.RegisterStatus(c.status, InferencePoolStatus, GetStatus) + status.RegisterStatus(c.status, InferencePoolStatus, GetStatus, c.tagWatcher.AccessUnprotected()) } RouteParents := BuildRouteParents(Gateways) @@ -309,25 +309,25 @@ func NewController( routeInputs, opts, ) - status.RegisterStatus(c.status, tcpRoutes.Status, GetStatus) + status.RegisterStatus(c.status, tcpRoutes.Status, GetStatus, c.tagWatcher.AccessUnprotected()) tlsRoutes := TLSRouteCollection( inputs.TLSRoutes, routeInputs, opts, ) - status.RegisterStatus(c.status, tlsRoutes.Status, GetStatus) + status.RegisterStatus(c.status, tlsRoutes.Status, GetStatus, c.tagWatcher.AccessUnprotected()) httpRoutes := HTTPRouteCollection( inputs.HTTPRoutes, routeInputs, opts, ) - status.RegisterStatus(c.status, httpRoutes.Status, GetStatus) + status.RegisterStatus(c.status, httpRoutes.Status, GetStatus, c.tagWatcher.AccessUnprotected()) grpcRoutes := GRPCRouteCollection( inputs.GRPCRoutes, routeInputs, opts, ) - status.RegisterStatus(c.status, grpcRoutes.Status, GetStatus) + status.RegisterStatus(c.status, grpcRoutes.Status, GetStatus, c.tagWatcher.AccessUnprotected()) RouteAttachments := krt.JoinCollection([]krt.Collection[RouteAttachment]{ tcpRoutes.RouteAttachments, @@ -360,7 +360,7 @@ func NewController( ) GatewayFinalStatus := FinalGatewayStatusCollection(GatewaysStatus, RouteAttachments, RouteAttachmentsIndex, opts) - status.RegisterStatus(c.status, GatewayFinalStatus, GetStatus) + status.RegisterStatus(c.status, GatewayFinalStatus, GetStatus, c.tagWatcher.AccessUnprotected()) VirtualServices := krt.JoinCollection([]krt.Collection[*config.Config]{ tcpRoutes.VirtualServices, diff --git a/pilot/pkg/config/kube/gateway/controller_test.go b/pilot/pkg/config/kube/gateway/controller_test.go index 4073ebff5b..8b454586c2 100644 --- a/pilot/pkg/config/kube/gateway/controller_test.go +++ b/pilot/pkg/config/kube/gateway/controller_test.go @@ -21,7 +21,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" k8s "sigs.k8s.io/gateway-api/apis/v1" - k8sbeta "sigs.k8s.io/gateway-api/apis/v1beta1" networking "istio.io/api/networking/v1alpha3" "istio.io/istio/pilot/pkg/features" @@ -104,20 +103,20 @@ func TestListInvalidGroupVersionKind(t *testing.T) { func TestListGatewayResourceType(t *testing.T) { controller := setupController(t, - &k8sbeta.GatewayClass{ + &k8s.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "gwclass", }, Spec: *gatewayClassSpec, }, - &k8sbeta.Gateway{ + &k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "gwspec", Namespace: "ns1", }, Spec: *gatewaySpec, }, - &k8sbeta.HTTPRoute{ + &k8s.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Name: "http-route", Namespace: "ns1", diff --git a/pilot/pkg/config/kube/gateway/conversion.go b/pilot/pkg/config/kube/gateway/conversion.go index 6525adfc1d..07b10e8494 100644 --- a/pilot/pkg/config/kube/gateway/conversion.go +++ b/pilot/pkg/config/kube/gateway/conversion.go @@ -34,7 +34,6 @@ import ( inferencev1 "sigs.k8s.io/gateway-api-inference-extension/api/v1" k8s "sigs.k8s.io/gateway-api/apis/v1" k8salpha "sigs.k8s.io/gateway-api/apis/v1alpha2" - k8sbeta "sigs.k8s.io/gateway-api/apis/v1beta1" gatewayx "sigs.k8s.io/gateway-api/apisx/v1alpha1" "istio.io/api/annotation" @@ -55,6 +54,7 @@ import ( "istio.io/istio/pkg/config/schema/gvk" "istio.io/istio/pkg/config/schema/kind" schematypes "istio.io/istio/pkg/config/schema/kubetypes" + "istio.io/istio/pkg/config/security" "istio.io/istio/pkg/kube/controllers" "istio.io/istio/pkg/kube/krt" "istio.io/istio/pkg/ptr" @@ -66,6 +66,7 @@ const ( gatewayTLSTerminateModeKey = "gateway.istio.io/tls-terminate-mode" addressTypeOverride = "networking.istio.io/address-type" gatewayClassDefaults = "gateway.istio.io/defaults-for-class" + gatewayTLSCipherSuites = "gateway.istio.io/tls-cipher-suites" ) func sortConfigByCreationTime(configs []config.Config) { @@ -98,7 +99,7 @@ func sortedConfigByCreationTime(configs []config.Config) []config.Config { } func convertHTTPRoute(ctx RouteContext, r k8s.HTTPRouteRule, - obj *k8sbeta.HTTPRoute, pos int, enforceRefGrant bool, + obj *k8s.HTTPRoute, pos int, enforceRefGrant bool, ) (*istio.HTTPRoute, *inferencePoolConfig, *ConfigError) { vs := &istio.HTTPRoute{} if r.Name != nil { @@ -1115,9 +1116,18 @@ func buildDestination(ctx RouteContext, to k8s.BackendRef, ns string, if ipCfg.endpointPickerDst == "" || ipCfg.endpointPickerPort == "" || ipCfg.endpointPickerFailureMode == "" { invalidBackendErr = &ConfigError{Reason: InvalidDestination, Message: "InferencePool service invalid, extensionRef labels not found"} } + + // For InferencePool, always use the first service port (54321). + // The cluster for that service port will include all endpoints for all + // target ports, allowing the EPP to load-balance across them. + var destPort uint32 + if len(svc.Ports) > 0 { + destPort = uint32(svc.Ports[0].Port) + } + return &istio.Destination{ Host: hostname, - // Port: &istio.PortSelector{Number: uint32(*to.Port)}, + Port: &istio.PortSelector{Number: destPort}, }, ipCfg, invalidBackendErr default: return &istio.Destination{}, nil, &ConfigError{ @@ -1641,8 +1651,8 @@ func getListenerNames(spec *k8s.GatewaySpec) sets.Set[k8s.SectionName] { func reportGatewayStatus( r *GatewayContext, - obj *k8sbeta.Gateway, - gs *k8sbeta.GatewayStatus, + obj *k8s.Gateway, + gs *k8s.GatewayStatus, classInfo classInfo, gatewayServices []string, servers []*istio.Server, @@ -1745,7 +1755,7 @@ func reportGatewayStatus( func reportListenerSetStatus( r *GatewayContext, - parentGwObj *k8sbeta.Gateway, + parentGwObj *k8s.Gateway, obj *gatewayx.XListenerSet, gs *gatewayx.ListenerSetStatus, gatewayServices []string, @@ -1816,8 +1826,8 @@ func setProgrammedCondition(gatewayConditions map[string]*condition, internal [] // For these gateways, we don't deploy them. However, all gateways ought to have a status message, even if its basically // just to say something read it func reportUnmanagedGatewayStatus( - status *k8sbeta.GatewayStatus, - obj *k8sbeta.Gateway, + status *k8s.GatewayStatus, + obj *k8s.Gateway, ) { gatewayConditions := map[string]*condition{ string(k8s.GatewayConditionAccepted): { @@ -1919,7 +1929,7 @@ func IsManaged(gw *k8s.GatewaySpec) bool { return false } -func extractGatewayServices(domainSuffix string, kgw *k8sbeta.Gateway, info classInfo) ([]string, *ConfigError) { +func extractGatewayServices(domainSuffix string, kgw *k8s.Gateway, info classInfo) ([]string, *ConfigError) { if IsManaged(&kgw.Spec) { name := model.GetOrDefault(kgw.Annotations[annotation.GatewayNameOverride.Name], getDefaultName(kgw.Name, &kgw.Spec, info.disableNameSuffix)) return []string{fmt.Sprintf("%s.%s.svc.%v", name, kgw.Namespace, domainSuffix)}, nil @@ -2114,7 +2124,7 @@ func buildTLS( return nil, nil } // Explicitly not supported: file mounted - // Not yet implemented: TLS mode, https redirect, max protocol version, SANs, CipherSuites, VerifyCertificate + // Not yet implemented: TLS mode, https redirect, max protocol version, SANs, VerifyCertificate out := &istio.ServerTLSSettings{ HttpsRedirect: false, } @@ -2215,6 +2225,12 @@ func buildTLS( out.Mode = istio.ServerTLSSettings_AUTO_PASSTHROUGH } } + if opts := tls.Options[gatewayTLSCipherSuites]; opts != "" { + ciphers := security.FilterCipherSuites(slices.Map(strings.Split(string(opts), ","), strings.TrimSpace)) + if len(ciphers) > 0 { + out.CipherSuites = ciphers + } + } return out, nil } @@ -2414,7 +2430,7 @@ func namespacesFromSelector(ctx krt.HandlerContext, localNamespace string, names } // namespaceAcceptedByAllowListeners determines a list of allowed namespaces for a given AllowedListener -func namespaceAcceptedByAllowListeners(localNamespace string, parent *k8sbeta.Gateway, lookupNamespace func(string) *corev1.Namespace) bool { +func namespaceAcceptedByAllowListeners(localNamespace string, parent *k8s.Gateway, lookupNamespace func(string) *corev1.Namespace) bool { lr := parent.Spec.AllowedListeners // Default allows none if lr == nil || lr.Namespaces == nil { @@ -2429,6 +2445,8 @@ func namespaceAcceptedByAllowListeners(localNamespace string, parent *k8sbeta.Ga return localNamespace == parent.Namespace case k8s.NamespacesFromNone: return false + case k8s.NamespacesFromSelector: + // Fallthrough default: // Unknown? return false @@ -2488,7 +2506,7 @@ func GetCommonRouteInfo(spec any) ([]k8s.ParentReference, []k8s.Hostname, config return t.Spec.ParentRefs, nil, gvk.TCPRoute case *k8salpha.TLSRoute: return t.Spec.ParentRefs, t.Spec.Hostnames, gvk.TLSRoute - case *k8sbeta.HTTPRoute: + case *k8s.HTTPRoute: return t.Spec.ParentRefs, t.Spec.Hostnames, gvk.HTTPRoute case *k8s.GRPCRoute: return t.Spec.ParentRefs, t.Spec.Hostnames, gvk.GRPCRoute @@ -2504,7 +2522,7 @@ func GetCommonRouteStateParents(spec any) []k8s.RouteParentStatus { return t.Status.Parents case *k8salpha.TLSRoute: return t.Status.Parents - case *k8sbeta.HTTPRoute: + case *k8s.HTTPRoute: return t.Status.Parents case *k8s.GRPCRoute: return t.Status.Parents @@ -2561,13 +2579,13 @@ func GetStatus[I, IS any](spec I) IS { return any(t.Status).(IS) case *k8salpha.TLSRoute: return any(t.Status).(IS) - case *k8sbeta.HTTPRoute: + case *k8s.HTTPRoute: return any(t.Status).(IS) case *k8s.GRPCRoute: return any(t.Status).(IS) - case *k8sbeta.Gateway: + case *k8s.Gateway: return any(t.Status).(IS) - case *k8sbeta.GatewayClass: + case *k8s.GatewayClass: return any(t.Status).(IS) case *gatewayx.XBackendTrafficPolicy: return any(t.Status).(IS) diff --git a/pilot/pkg/config/kube/gateway/conversion_test.go b/pilot/pkg/config/kube/gateway/conversion_test.go index 4b6270350f..61702fd3cc 100644 --- a/pilot/pkg/config/kube/gateway/conversion_test.go +++ b/pilot/pkg/config/kube/gateway/conversion_test.go @@ -77,6 +77,14 @@ var ports = []*model.Port{ }, } +var inferencePoolPorts = []*model.Port{ + { + Name: "http", + Port: 54321, + Protocol: "HTTP", + }, +} + var services = []*model.Service{ { Attributes: model.ServiceAttributes{ @@ -135,7 +143,7 @@ var services = []*model.Service{ InferencePoolExtensionRefFailureMode: "FailClose", }, }, - Ports: ports, + Ports: inferencePoolPorts, Hostname: host.Name(fmt.Sprintf("%s.default.svc.domain.suffix", firstValue(InferencePoolServiceName("infpool-gen")))), }, { @@ -147,7 +155,7 @@ var services = []*model.Service{ InferencePoolExtensionRefFailureMode: "FailClose", }, }, - Ports: ports, + Ports: inferencePoolPorts, Hostname: host.Name(fmt.Sprintf("%s.default.svc.domain.suffix", firstValue(InferencePoolServiceName("infpool-gen2")))), }, @@ -1773,7 +1781,7 @@ func TestHumanReadableJoin(t *testing.T) { func kubernetesObjectsFromString(s string) ([]runtime.Object, error) { var objects []runtime.Object decode := kube.IstioCodec.UniversalDeserializer().Decode - objectStrs := strings.Split(s, "---") + objectStrs := strings.Split(s, "\n---\n") for _, s := range objectStrs { if len(strings.TrimSpace(s)) == 0 { continue diff --git a/pilot/pkg/config/kube/gateway/deploymentcontroller.go b/pilot/pkg/config/kube/gateway/deploymentcontroller.go index cbb366f53d..3b886c2c23 100644 --- a/pilot/pkg/config/kube/gateway/deploymentcontroller.go +++ b/pilot/pkg/config/kube/gateway/deploymentcontroller.go @@ -33,8 +33,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/mergepatch" "k8s.io/apimachinery/pkg/util/strategicpatch" - gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - gateway "sigs.k8s.io/gateway-api/apis/v1beta1" + gateway "sigs.k8s.io/gateway-api/apis/v1" gatewayx "sigs.k8s.io/gateway-api/apisx/v1alpha1" "sigs.k8s.io/yaml" @@ -614,7 +613,7 @@ func translateInfraMeta[K ~string, V ~string](meta map[K]V) map[string]string { return infra } -func extractInfrastructureMetadata(gwInfra *gatewayv1.GatewayInfrastructure, isLabel bool, gw gateway.Gateway) map[string]string { +func extractInfrastructureMetadata(gwInfra *gateway.GatewayInfrastructure, isLabel bool, gw gateway.Gateway) map[string]string { if gwInfra != nil && isLabel && gwInfra.Labels != nil { return translateInfraMeta(gwInfra.Labels) } @@ -867,7 +866,7 @@ func fetchParameters(gw *gateway.Gateway) (*types.NamespacedName, error) { } func (d *DeploymentController) setGatewayControllerVersion(gws gateway.Gateway) error { - patch := fmt.Sprintf(`{"apiVersion":"gateway.networking.k8s.io/v1beta1","kind":"Gateway","metadata":{"annotations":{"%s":"%d"}}}`, + patch := fmt.Sprintf(`{"apiVersion":"gateway.networking.k8s.io/v1","kind":"Gateway","metadata":{"annotations":{"%s":"%d"}}}`, ControllerVersionAnnotation, ControllerVersion) log.Debugf("applying %v", patch) diff --git a/pilot/pkg/config/kube/gateway/deploymentcontroller_test.go b/pilot/pkg/config/kube/gateway/deploymentcontroller_test.go index 8f39168535..470dfd653d 100644 --- a/pilot/pkg/config/kube/gateway/deploymentcontroller_test.go +++ b/pilot/pkg/config/kube/gateway/deploymentcontroller_test.go @@ -32,7 +32,6 @@ import ( kubeVersion "k8s.io/apimachinery/pkg/version" fakediscovery "k8s.io/client-go/discovery/fake" k8s "sigs.k8s.io/gateway-api/apis/v1" - k8sbeta "sigs.k8s.io/gateway-api/apis/v1beta1" "sigs.k8s.io/yaml" "istio.io/api/annotation" @@ -74,7 +73,7 @@ var ( func TestConfigureIstioGateway(t *testing.T) { discoveryNamespacesFilter := buildFilter("default") defaultNamespace := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "default"}} - customClass := &k8sbeta.GatewayClass{ + customClass := &k8s.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "custom", }, @@ -139,7 +138,7 @@ func TestConfigureIstioGateway(t *testing.T) { proxyConfig := model.GetProxyConfigs(store, mesh.DefaultMeshConfig()) tests := []struct { name string - gw k8sbeta.Gateway + gw k8s.Gateway objects []runtime.Object pcs *model.ProxyConfigs values string @@ -149,7 +148,7 @@ func TestConfigureIstioGateway(t *testing.T) { }{ { name: "simple", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", @@ -165,7 +164,7 @@ func TestConfigureIstioGateway(t *testing.T) { }, { name: "simple", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", @@ -180,7 +179,7 @@ func TestConfigureIstioGateway(t *testing.T) { }, { name: "manual-sa", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", @@ -195,7 +194,7 @@ func TestConfigureIstioGateway(t *testing.T) { }, { name: "manual-ip", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", @@ -214,7 +213,7 @@ func TestConfigureIstioGateway(t *testing.T) { }, { name: "cluster-ip", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", @@ -237,7 +236,7 @@ func TestConfigureIstioGateway(t *testing.T) { }, { name: "multinetwork", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", @@ -258,7 +257,7 @@ func TestConfigureIstioGateway(t *testing.T) { }, { name: "waypoint", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "namespace", Namespace: "default", @@ -283,7 +282,7 @@ func TestConfigureIstioGateway(t *testing.T) { }, { name: "waypoint-no-network-label", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "namespace", Namespace: "default", @@ -305,7 +304,7 @@ func TestConfigureIstioGateway(t *testing.T) { }, { name: "istio-east-west", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "eastwestgateway", Namespace: "istio-system", @@ -333,7 +332,7 @@ func TestConfigureIstioGateway(t *testing.T) { }, { name: "proxy-config-crd", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", @@ -347,7 +346,7 @@ func TestConfigureIstioGateway(t *testing.T) { }, { name: "custom-class", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", @@ -360,7 +359,7 @@ func TestConfigureIstioGateway(t *testing.T) { }, { name: "infrastructure-labels-annotations", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", @@ -379,7 +378,7 @@ func TestConfigureIstioGateway(t *testing.T) { }, { name: "kube-gateway-ambient-redirect", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", @@ -396,7 +395,7 @@ func TestConfigureIstioGateway(t *testing.T) { }, { name: "kube-gateway-ambient-redirect-infra", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", @@ -414,7 +413,7 @@ func TestConfigureIstioGateway(t *testing.T) { }, { name: "istio-upgrade-to-1.24", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "test-upgrade", Namespace: "default", @@ -436,7 +435,7 @@ func TestConfigureIstioGateway(t *testing.T) { }, { name: "customizations", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "namespace", Namespace: "default", @@ -482,7 +481,7 @@ spec: }, { name: "illegal_customizations", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "namespace", Namespace: "default", @@ -511,7 +510,7 @@ metadata: }, { name: "copy-labels-annotations-disabled-infra-set", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", @@ -531,7 +530,7 @@ metadata: }, { name: "copy-labels-annotations-disabled-infra-nil", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", @@ -547,7 +546,7 @@ metadata: }, { name: "copy-labels-annotations-enabled-infra-nil", - gw: k8sbeta.Gateway{ + gw: k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "default", Namespace: "default", @@ -571,8 +570,8 @@ metadata: client := kube.NewFakeClient(tt.objects...) kube.SetObjectFilter(client, tt.discoveryNamespaceFilter) client.Kube().Discovery().(*fakediscovery.FakeDiscovery).FakedServerVersion = &kubeVersion.Info{Major: "1", Minor: "28"} - kclient.NewWriteClient[*k8sbeta.GatewayClass](client).Create(customClass) - kclient.NewWriteClient[*k8sbeta.Gateway](client).Create(tt.gw.DeepCopy()) + kclient.NewWriteClient[*k8s.GatewayClass](client).Create(customClass) + kclient.NewWriteClient[*k8s.Gateway](client).Create(tt.gw.DeepCopy()) kclient.NewWriteClient[*appsv1.Deployment](client).Create(upgradeDeployment) stop := test.NewStop(t) env := newTestEnv() @@ -662,7 +661,7 @@ func TestMeshGatewayReconciliation(t *testing.T) { // create the initial gateway for the deployment controller // to reconcile when the meshconfig changes - defaultGateway := &k8sbeta.Gateway{ + defaultGateway := &k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "gw", Namespace: "default", @@ -742,7 +741,7 @@ func TestVersionManagement(t *testing.T) { c.RunAndWait(stop) kube.WaitForCacheSync("test", stop, d.queue.HasSynced) // Create a gateway, we should mark our ownership - defaultGateway := &k8sbeta.Gateway{ + defaultGateway := &k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "gw", Namespace: "default", @@ -805,7 +804,7 @@ func TestVersionManagement(t *testing.T) { func TestHandlerEnqueueFunction(t *testing.T) { defaultNamespace := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "default"}} - defaultGatewayClass := &k8sbeta.GatewayClass{ + defaultGatewayClass := &k8s.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: features.GatewayAPIDefaultGatewayClass, }, @@ -827,7 +826,7 @@ func TestHandlerEnqueueFunction(t *testing.T) { name: "add event", event: controllers.Event{ Event: controllers.EventAdd, - New: &k8sbeta.Gateway{ + New: &k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "new-add", Namespace: defaultNamespace.GetName(), @@ -846,7 +845,7 @@ func TestHandlerEnqueueFunction(t *testing.T) { name: "delete event", event: controllers.Event{ Event: controllers.EventDelete, - Old: &k8sbeta.Gateway{ + Old: &k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "old-delete", Namespace: defaultNamespace.GetName(), @@ -865,7 +864,7 @@ func TestHandlerEnqueueFunction(t *testing.T) { name: "update event change annotation", event: controllers.Event{ Event: controllers.EventUpdate, - New: &k8sbeta.Gateway{ + New: &k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "update-1", Namespace: defaultNamespace.GetName(), @@ -883,7 +882,7 @@ func TestHandlerEnqueueFunction(t *testing.T) { }, }, }, - Old: &k8sbeta.Gateway{ + Old: &k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "update-1", Namespace: defaultNamespace.GetName(), @@ -909,7 +908,7 @@ func TestHandlerEnqueueFunction(t *testing.T) { name: "update event change label", event: controllers.Event{ Event: controllers.EventUpdate, - New: &k8sbeta.Gateway{ + New: &k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "update-2", Namespace: defaultNamespace.GetName(), @@ -927,7 +926,7 @@ func TestHandlerEnqueueFunction(t *testing.T) { }, }, }, - Old: &k8sbeta.Gateway{ + Old: &k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "update-2", Namespace: defaultNamespace.GetName(), @@ -953,7 +952,7 @@ func TestHandlerEnqueueFunction(t *testing.T) { name: "update event change gateway spec", event: controllers.Event{ Event: controllers.EventUpdate, - New: &k8sbeta.Gateway{ + New: &k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "update-3", Namespace: defaultNamespace.GetName(), @@ -972,7 +971,7 @@ func TestHandlerEnqueueFunction(t *testing.T) { }, }, }, - Old: &k8sbeta.Gateway{ + Old: &k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "update-3", Namespace: defaultNamespace.GetName(), @@ -999,7 +998,7 @@ func TestHandlerEnqueueFunction(t *testing.T) { name: "update event no change gateway spec", event: controllers.Event{ Event: controllers.EventUpdate, - New: &k8sbeta.Gateway{ + New: &k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "update-4", Namespace: defaultNamespace.GetName(), @@ -1018,7 +1017,7 @@ func TestHandlerEnqueueFunction(t *testing.T) { }, }, }, - Old: &k8sbeta.Gateway{ + Old: &k8s.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "update-4", Namespace: defaultNamespace.GetName(), @@ -1083,29 +1082,29 @@ func TestHandlerEnqueueFunction(t *testing.T) { switch tt.event.Event { case controllers.EventAdd: - gw := tt.event.New.(*k8sbeta.Gateway) - kclient.NewWriteClient[*k8sbeta.Gateway](client).Create(gw.DeepCopy()) + gw := tt.event.New.(*k8s.Gateway) + kclient.NewWriteClient[*k8s.Gateway](client).Create(gw.DeepCopy()) case controllers.EventDelete: // For delete events, first create the gateway, then delete it - gw := tt.event.Old.(*k8sbeta.Gateway) - kclient.NewWriteClient[*k8sbeta.Gateway](client).Create(gw.DeepCopy()) + gw := tt.event.Old.(*k8s.Gateway) + kclient.NewWriteClient[*k8s.Gateway](client).Create(gw.DeepCopy()) kube.WaitForCacheSync("test", stop, d.queue.HasSynced) // Wait for the create event reconciliation to complete <-reconcileDone // Reset counter after create, so we only count the delete event reconciles.Store(0) - kclient.NewWriteClient[*k8sbeta.Gateway](client).Delete(gw.Name, gw.Namespace) + kclient.NewWriteClient[*k8s.Gateway](client).Delete(gw.Name, gw.Namespace) case controllers.EventUpdate: // For update events, first create the old gateway, then update it - newGw := tt.event.New.(*k8sbeta.Gateway) - oldGw := tt.event.Old.(*k8sbeta.Gateway) - kclient.NewWriteClient[*k8sbeta.Gateway](client).Create(oldGw.DeepCopy()) + newGw := tt.event.New.(*k8s.Gateway) + oldGw := tt.event.Old.(*k8s.Gateway) + kclient.NewWriteClient[*k8s.Gateway](client).Create(oldGw.DeepCopy()) kube.WaitForCacheSync("test", stop, d.queue.HasSynced) // Wait for the create event reconciliation to complete <-reconcileDone // Reset counter after create, so we only count the update event reconciles.Store(0) - kclient.NewWriteClient[*k8sbeta.Gateway](client).Update(newGw.DeepCopy()) + kclient.NewWriteClient[*k8s.Gateway](client).Update(newGw.DeepCopy()) } kube.WaitForCacheSync("test", stop, d.queue.HasSynced) @@ -1154,7 +1153,7 @@ global: // was always used so the function has been // adjusted to hardcode ControllerVersion func buildPatch() string { - return fmt.Sprintf(`apiVersion: gateway.networking.k8s.io/v1beta1 + return fmt.Sprintf(`apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: diff --git a/pilot/pkg/config/kube/gateway/gateway_collection.go b/pilot/pkg/config/kube/gateway/gateway_collection.go index 5910e1629f..f4f6964036 100644 --- a/pilot/pkg/config/kube/gateway/gateway_collection.go +++ b/pilot/pkg/config/kube/gateway/gateway_collection.go @@ -22,7 +22,6 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - gateway "sigs.k8s.io/gateway-api/apis/v1beta1" gatewayx "sigs.k8s.io/gateway-api/apisx/v1alpha1" "istio.io/api/annotation" @@ -74,7 +73,7 @@ func (g ListenerSet) Equals(other ListenerSet) bool { func ListenerSetCollection( listenerSets krt.Collection[*gatewayx.XListenerSet], - gateways krt.Collection[*gateway.Gateway], + gateways krt.Collection[*gatewayv1.Gateway], gatewayClasses krt.Collection[GatewayClass], namespaces krt.Collection[*corev1.Namespace], grants ReferenceGrants, @@ -220,7 +219,7 @@ func ListenerSetCollection( } func GatewayCollection( - gateways krt.Collection[*gateway.Gateway], + gateways krt.Collection[*gatewayv1.Gateway], listenerSets krt.Collection[ListenerSet], gatewayClasses krt.Collection[GatewayClass], namespaces krt.Collection[*corev1.Namespace], @@ -232,13 +231,13 @@ func GatewayCollection( tagWatcher krt.RecomputeProtected[revisions.TagWatcher], opts krt.OptionsBuilder, ) ( - krt.StatusCollection[*gateway.Gateway, gateway.GatewayStatus], + krt.StatusCollection[*gatewayv1.Gateway, gatewayv1.GatewayStatus], krt.Collection[Gateway], ) { listenerIndex := krt.NewIndex(listenerSets, "gatewayParent", func(o ListenerSet) []types.NamespacedName { return []types.NamespacedName{o.GatewayParent} }) - statusCol, gw := krt.NewStatusManyCollection(gateways, func(ctx krt.HandlerContext, obj *gateway.Gateway) (*gateway.GatewayStatus, []Gateway) { + statusCol, gw := krt.NewStatusManyCollection(gateways, func(ctx krt.HandlerContext, obj *gatewayv1.Gateway) (*gatewayv1.GatewayStatus, []Gateway) { // We currently depend on service discovery information not known to krt; mark we depend on it. context := gatewayContext.Get(ctx).Load() if context == nil { @@ -362,14 +361,16 @@ func GatewayCollection( // the attachedRoute count, so we first build a partial Gateway status, then once routes are computed we finalize it with // the attachedRoute count. func FinalGatewayStatusCollection( - gatewayStatuses krt.StatusCollection[*gateway.Gateway, gateway.GatewayStatus], + gatewayStatuses krt.StatusCollection[*gatewayv1.Gateway, gatewayv1.GatewayStatus], routeAttachments krt.Collection[RouteAttachment], routeAttachmentsIndex krt.Index[types.NamespacedName, RouteAttachment], opts krt.OptionsBuilder, -) krt.StatusCollection[*gateway.Gateway, gateway.GatewayStatus] { +) krt.StatusCollection[*gatewayv1.Gateway, gatewayv1.GatewayStatus] { return krt.NewCollection( gatewayStatuses, - func(ctx krt.HandlerContext, i krt.ObjectWithStatus[*gateway.Gateway, gateway.GatewayStatus]) *krt.ObjectWithStatus[*gateway.Gateway, gateway.GatewayStatus] { + func( + ctx krt.HandlerContext, i krt.ObjectWithStatus[*gatewayv1.Gateway, gatewayv1.GatewayStatus], + ) *krt.ObjectWithStatus[*gatewayv1.Gateway, gatewayv1.GatewayStatus] { tcpRoutes := krt.Fetch(ctx, routeAttachments, krt.FilterIndex(routeAttachmentsIndex, config.NamespacedName(i.Obj))) counts := map[string]int32{} for _, r := range tcpRoutes { @@ -380,7 +381,7 @@ func FinalGatewayStatusCollection( s.AttachedRoutes = counts[string(s.Name)] status.Listeners[i] = s } - return &krt.ObjectWithStatus[*gateway.Gateway, gateway.GatewayStatus]{ + return &krt.ObjectWithStatus[*gatewayv1.Gateway, gatewayv1.GatewayStatus]{ Obj: i.Obj, Status: *status, } @@ -400,11 +401,11 @@ func (p RouteParents) fetch(ctx krt.HandlerContext, pk parentKey) []*parentInfo { InternalName: "mesh", // Mesh has no configurable AllowedKinds, so allow all supported - AllowedKinds: []gateway.RouteGroupKind{ - {Group: (*gateway.Group)(ptr.Of(gvk.HTTPRoute.Group)), Kind: gateway.Kind(gvk.HTTPRoute.Kind)}, - {Group: (*gateway.Group)(ptr.Of(gvk.GRPCRoute.Group)), Kind: gateway.Kind(gvk.GRPCRoute.Kind)}, - {Group: (*gateway.Group)(ptr.Of(gvk.TCPRoute.Group)), Kind: gateway.Kind(gvk.TCPRoute.Kind)}, - {Group: (*gateway.Group)(ptr.Of(gvk.TLSRoute.Group)), Kind: gateway.Kind(gvk.TLSRoute.Kind)}, + AllowedKinds: []gatewayv1.RouteGroupKind{ + {Group: (*gatewayv1.Group)(ptr.Of(gvk.HTTPRoute.Group)), Kind: gatewayv1.Kind(gvk.HTTPRoute.Kind)}, + {Group: (*gatewayv1.Group)(ptr.Of(gvk.GRPCRoute.Group)), Kind: gatewayv1.Kind(gvk.GRPCRoute.Kind)}, + {Group: (*gatewayv1.Group)(ptr.Of(gvk.TCPRoute.Group)), Kind: gatewayv1.Kind(gvk.TCPRoute.Kind)}, + {Group: (*gatewayv1.Group)(ptr.Of(gvk.TLSRoute.Group)), Kind: gatewayv1.Kind(gvk.TLSRoute.Kind)}, }, }, } @@ -439,8 +440,8 @@ func detectListenerPortNumber(l gatewayx.ListenerEntry) (gatewayx.PortNumber, er return 0, fmt.Errorf("protocol %v requires a port to be set", l.Protocol) } -func convertStandardStatusToListenerSetStatus(l gatewayx.ListenerEntry) func(e gateway.ListenerStatus) gatewayx.ListenerEntryStatus { - return func(e gateway.ListenerStatus) gatewayx.ListenerEntryStatus { +func convertStandardStatusToListenerSetStatus(l gatewayx.ListenerEntry) func(e gatewayv1.ListenerStatus) gatewayx.ListenerEntryStatus { + return func(e gatewayv1.ListenerStatus) gatewayx.ListenerEntryStatus { return gatewayx.ListenerEntryStatus{ Name: e.Name, Port: l.Port, @@ -451,8 +452,8 @@ func convertStandardStatusToListenerSetStatus(l gatewayx.ListenerEntry) func(e g } } -func convertListenerSetStatusToStandardStatus(e gatewayx.ListenerEntryStatus) gateway.ListenerStatus { - return gateway.ListenerStatus{ +func convertListenerSetStatusToStandardStatus(e gatewayx.ListenerEntryStatus) gatewayv1.ListenerStatus { + return gatewayv1.ListenerStatus{ Name: e.Name, SupportedKinds: e.SupportedKinds, AttachedRoutes: e.AttachedRoutes, @@ -460,7 +461,7 @@ func convertListenerSetStatusToStandardStatus(e gatewayx.ListenerEntryStatus) ga } } -func convertListenerSetToListener(l gatewayx.ListenerEntry) gateway.Listener { +func convertListenerSetToListener(l gatewayx.ListenerEntry) gatewayv1.Listener { // For now, structs are identical enough Go can cast them. I doubt this will hold up forever, but we can adjust as needed. - return gateway.Listener(l) + return gatewayv1.Listener(l) } diff --git a/pilot/pkg/config/kube/gateway/gatewayclass.go b/pilot/pkg/config/kube/gateway/gatewayclass.go index 167d98644f..c71e69ed87 100644 --- a/pilot/pkg/config/kube/gateway/gatewayclass.go +++ b/pilot/pkg/config/kube/gateway/gatewayclass.go @@ -20,7 +20,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" k8sv1 "sigs.k8s.io/gateway-api/apis/v1" - gateway "sigs.k8s.io/gateway-api/apis/v1beta1" "istio.io/istio/pilot/pkg/model/kstatus" "istio.io/istio/pkg/kube" @@ -37,7 +36,7 @@ import ( // and not update there is no need; the first controller to create the GatewayClass wins. type ClassController struct { queue controllers.Queue - classes kclient.Client[*gateway.GatewayClass] + classes kclient.Client[*k8sv1.GatewayClass] } func NewClassController(kc kube.Client) *ClassController { @@ -46,9 +45,9 @@ func NewClassController(kc kube.Client) *ClassController { controllers.WithReconciler(gc.Reconcile), controllers.WithMaxAttempts(25)) - gc.classes = kclient.New[*gateway.GatewayClass](kc) + gc.classes = kclient.New[*k8sv1.GatewayClass](kc) gc.classes.AddEventHandler(controllers.FilteredObjectHandler(gc.queue.AddObject, func(o controllers.Object) bool { - _, f := builtinClasses[gateway.ObjectName(o.GetName())] + _, f := builtinClasses[k8sv1.ObjectName(o.GetName())] return f })) return gc @@ -68,7 +67,7 @@ func (c *ClassController) Reconcile(types.NamespacedName) error { return err.ErrorOrNil() } -func (c *ClassController) reconcileClass(class gateway.ObjectName) error { +func (c *ClassController) reconcileClass(class k8sv1.ObjectName) error { if c.classes.Get(string(class), "") != nil { log.Debugf("GatewayClass/%v already exists, no action", class) return nil @@ -79,12 +78,12 @@ func (c *ClassController) reconcileClass(class gateway.ObjectName) error { // Should only happen when ambient is disabled; otherwise builtinClasses and classInfos should be consistent return nil } - gc := &gateway.GatewayClass{ + gc := &k8sv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: string(class), }, - Spec: gateway.GatewayClassSpec{ - ControllerName: gateway.GatewayController(classInfo.controller), + Spec: k8sv1.GatewayClassSpec{ + ControllerName: k8sv1.GatewayController(classInfo.controller), Description: &classInfo.description, }, } diff --git a/pilot/pkg/config/kube/gateway/gatewayclass_collection.go b/pilot/pkg/config/kube/gateway/gatewayclass_collection.go index a690e954c4..fa18141d1a 100644 --- a/pilot/pkg/config/kube/gateway/gatewayclass_collection.go +++ b/pilot/pkg/config/kube/gateway/gatewayclass_collection.go @@ -16,14 +16,13 @@ package gateway import ( gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - gateway "sigs.k8s.io/gateway-api/apis/v1beta1" "istio.io/istio/pkg/kube/krt" ) type GatewayClass struct { Name string - Controller gateway.GatewayController + Controller gatewayv1.GatewayController } func (g GatewayClass) ResourceName() string { @@ -31,13 +30,13 @@ func (g GatewayClass) ResourceName() string { } func GatewayClassesCollection( - gatewayClasses krt.Collection[*gateway.GatewayClass], + gatewayClasses krt.Collection[*gatewayv1.GatewayClass], opts krt.OptionsBuilder, ) ( - krt.StatusCollection[*gateway.GatewayClass, gateway.GatewayClassStatus], + krt.StatusCollection[*gatewayv1.GatewayClass, gatewayv1.GatewayClassStatus], krt.Collection[GatewayClass], ) { - return krt.NewStatusCollection(gatewayClasses, func(ctx krt.HandlerContext, obj *gateway.GatewayClass) (*gateway.GatewayClassStatus, *GatewayClass) { + return krt.NewStatusCollection(gatewayClasses, func(ctx krt.HandlerContext, obj *gatewayv1.GatewayClass) (*gatewayv1.GatewayClassStatus, *GatewayClass) { _, known := classInfos[obj.Spec.ControllerName] if !known { return nil, nil diff --git a/pilot/pkg/config/kube/gateway/gatewayclass_test.go b/pilot/pkg/config/kube/gateway/gatewayclass_test.go index 90aadb89a1..9725798154 100644 --- a/pilot/pkg/config/kube/gateway/gatewayclass_test.go +++ b/pilot/pkg/config/kube/gateway/gatewayclass_test.go @@ -20,7 +20,7 @@ import ( "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - gateway "sigs.k8s.io/gateway-api/apis/v1beta1" + gateway "sigs.k8s.io/gateway-api/apis/v1" "istio.io/istio/pilot/pkg/features" "istio.io/istio/pkg/kube" diff --git a/pilot/pkg/config/kube/gateway/inferencepool_collection.go b/pilot/pkg/config/kube/gateway/inferencepool_collection.go index a95cd38a6e..da48d27cf5 100644 --- a/pilot/pkg/config/kube/gateway/inferencepool_collection.go +++ b/pilot/pkg/config/kube/gateway/inferencepool_collection.go @@ -27,7 +27,6 @@ import ( "k8s.io/apimachinery/pkg/util/json" inferencev1 "sigs.k8s.io/gateway-api-inference-extension/api/v1" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - gateway "sigs.k8s.io/gateway-api/apis/v1beta1" "istio.io/istio/pkg/config/constants" "istio.io/istio/pkg/config/schema/gvk" @@ -97,9 +96,9 @@ func (i InferencePool) ResourceName() string { func InferencePoolCollection( pools krt.Collection[*inferencev1.InferencePool], services krt.Collection[*corev1.Service], - httpRoutes krt.Collection[*gateway.HTTPRoute], - gateways krt.Collection[*gateway.Gateway], - routesByInferencePool krt.Index[string, *gateway.HTTPRoute], + httpRoutes krt.Collection[*gatewayv1.HTTPRoute], + gateways krt.Collection[*gatewayv1.Gateway], + routesByInferencePool krt.Index[string, *gatewayv1.HTTPRoute], c *Controller, opts krt.OptionsBuilder, ) (krt.StatusCollection[*inferencev1.InferencePool, inferencev1.InferencePoolStatus], krt.Collection[InferencePool]) { @@ -186,8 +185,8 @@ func calculateInferencePoolStatus( pool *inferencev1.InferencePool, gatewayParents sets.Set[types.NamespacedName], services krt.Collection[*corev1.Service], - gateways krt.Collection[*gateway.Gateway], - routeList []*gateway.HTTPRoute, + gateways krt.Collection[*gatewayv1.Gateway], + routeList []*gatewayv1.HTTPRoute, ) *inferencev1.InferencePoolStatus { // Calculate status for each gateway parent existingParents := pool.Status.DeepCopy().Parents @@ -229,7 +228,7 @@ func calculateInferencePoolStatus( // findGatewayParents finds all Gateway parents that reference this InferencePool through HTTPRoutes func findGatewayParents( pool *inferencev1.InferencePool, - routeList []*gateway.HTTPRoute, + routeList []*gatewayv1.HTTPRoute, ) sets.Set[types.NamespacedName] { gatewayParents := sets.New[types.NamespacedName]() @@ -264,7 +263,7 @@ func findGatewayParents( } // routeReferencesInferencePool checks if an HTTPRoute references the given InferencePool -func routeReferencesInferencePool(route *gateway.HTTPRoute, pool *inferencev1.InferencePool) bool { +func routeReferencesInferencePool(route *gatewayv1.HTTPRoute, pool *inferencev1.InferencePool) bool { for _, rule := range route.Spec.Rules { for _, backendRef := range rule.BackendRefs { if !isInferencePoolBackendRef(backendRef.BackendRef) { @@ -302,7 +301,7 @@ func calculateSingleParentStatus( gatewayParent types.NamespacedName, services krt.Collection[*corev1.Service], existingParents []inferencev1.ParentStatus, - routeList []*gateway.HTTPRoute, + routeList []*gatewayv1.HTTPRoute, ) inferencev1.ParentStatus { // Find existing status for this parent to preserve some conditions var existingConditions []metav1.Condition @@ -344,7 +343,7 @@ func calculateSingleParentStatus( func calculateAcceptedStatus( pool *inferencev1.InferencePool, gatewayParent types.NamespacedName, - routeList []*gateway.HTTPRoute, + routeList []*gatewayv1.HTTPRoute, ) *condition { // Check if any HTTPRoute references this InferencePool and has this gateway as an accepted parent for _, route := range routeList { @@ -460,7 +459,7 @@ func isDefaultStatusParent(parent inferencev1.ParentStatus) bool { // isOurManagedGateway checks if a Gateway is managed by one of our supported controllers // This is used to identify stale parent entries that we previously added but are no longer referenced by HTTPRoutes -func isOurManagedGateway(gateways krt.Collection[*gateway.Gateway], namespace, name string) bool { +func isOurManagedGateway(gateways krt.Collection[*gatewayv1.Gateway], namespace, name string) bool { gtw := ptr.Flatten(gateways.GetKey(fmt.Sprintf("%s/%s", namespace, name))) if gtw == nil { return false @@ -506,15 +505,19 @@ func InferencePoolServiceName(poolName string) (string, error) { } func translateShadowServiceToService(shadow shadowServiceInfo, extRef extRefInfo) *corev1.Service { - // Create the ports used by the shadow service + // Create multiple ports for the shadow service - one for each InferencePool targetPort. + // This allows Istio to discover endpoints for all targetPorts. + // We use dummy service ports (54321, 54322, etc.) that map to the actual targetPorts. + baseDummyPort := int32(54321) ports := make([]corev1.ServicePort, 0, len(shadow.targetPorts)) - dummyPort := int32(54321) // Dummy port, not used for anything - for i, port := range shadow.targetPorts { + + for i, tp := range shadow.targetPorts { + portName := fmt.Sprintf("http-%d", i) ports = append(ports, corev1.ServicePort{ - Name: "port" + strconv.Itoa(i), + Name: portName, Protocol: corev1.ProtocolTCP, - Port: dummyPort + int32(i), - TargetPort: intstr.FromInt(int(port.port)), + Port: baseDummyPort + int32(i), + TargetPort: intstr.FromInt(int(tp.port)), }) } @@ -614,7 +617,7 @@ func (c *Controller) canManageShadowServiceForInference(obj *corev1.Service) (bo return inferencePoolManaged, obj.GetResourceVersion() } -func indexHTTPRouteByInferencePool(o *gateway.HTTPRoute) []string { +func indexHTTPRouteByInferencePool(o *gatewayv1.HTTPRoute) []string { var keys []string for _, rule := range o.Spec.Rules { for _, backendRef := range rule.BackendRefs { diff --git a/pilot/pkg/config/kube/gateway/inferencepool_status_test.go b/pilot/pkg/config/kube/gateway/inferencepool_status_test.go index e6d08354d2..70f19efb5d 100644 --- a/pilot/pkg/config/kube/gateway/inferencepool_status_test.go +++ b/pilot/pkg/config/kube/gateway/inferencepool_status_test.go @@ -29,7 +29,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" inferencev1 "sigs.k8s.io/gateway-api-inference-extension/api/v1" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - gateway "sigs.k8s.io/gateway-api/apis/v1beta1" "istio.io/istio/pilot/pkg/features" "istio.io/istio/pilot/pkg/status" @@ -523,30 +522,30 @@ func InNamespace(namespace string) Option { func WithController(name string) Option { return func(obj client.Object) { - gw, ok := obj.(*gateway.GatewayClass) + gw, ok := obj.(*gatewayv1.GatewayClass) if ok { - gw.Spec.ControllerName = gateway.GatewayController(name) + gw.Spec.ControllerName = gatewayv1.GatewayController(name) } } } func WithGatewayClass(name string) Option { return func(obj client.Object) { - gw, ok := obj.(*gateway.Gateway) + gw, ok := obj.(*gatewayv1.Gateway) if ok { - gw.Spec.GatewayClassName = gateway.ObjectName(name) + gw.Spec.GatewayClassName = gatewayv1.ObjectName(name) } } } func WithParentRef(name, namespace string) Option { return func(obj client.Object) { - hr, ok := obj.(*gateway.HTTPRoute) + hr, ok := obj.(*gatewayv1.HTTPRoute) if ok { - namespaceName := gateway.Namespace(namespace) - hr.Spec.ParentRefs = []gateway.ParentReference{ + namespaceName := gatewayv1.Namespace(namespace) + hr.Spec.ParentRefs = []gatewayv1.ParentReference{ { - Name: gateway.ObjectName(name), + Name: gatewayv1.ObjectName(name), Namespace: &namespaceName, }, } @@ -556,26 +555,26 @@ func WithParentRef(name, namespace string) Option { func WithParentRefAndStatus(name, namespace, controllerName string) Option { return func(obj client.Object) { - hr, ok := obj.(*gateway.HTTPRoute) + hr, ok := obj.(*gatewayv1.HTTPRoute) if ok { - namespaceName := gateway.Namespace(namespace) + namespaceName := gatewayv1.Namespace(namespace) if hr.Spec.ParentRefs == nil { - hr.Spec.ParentRefs = []gateway.ParentReference{} + hr.Spec.ParentRefs = []gatewayv1.ParentReference{} } - hr.Spec.ParentRefs = append(hr.Spec.ParentRefs, gateway.ParentReference{ - Name: gateway.ObjectName(name), + hr.Spec.ParentRefs = append(hr.Spec.ParentRefs, gatewayv1.ParentReference{ + Name: gatewayv1.ObjectName(name), Namespace: &namespaceName, }) if hr.Status.Parents == nil { - hr.Status.Parents = []gateway.RouteParentStatus{} + hr.Status.Parents = []gatewayv1.RouteParentStatus{} } - parentStatusRef := &gateway.RouteParentStatus{ - ParentRef: gateway.ParentReference{ - Name: gateway.ObjectName(name), + parentStatusRef := &gatewayv1.RouteParentStatus{ + ParentRef: gatewayv1.ParentReference{ + Name: gatewayv1.ObjectName(name), Namespace: &namespaceName, }, - ControllerName: gateway.GatewayController(controllerName), + ControllerName: gatewayv1.GatewayController(controllerName), } hr.Status.Parents = append(hr.Status.Parents, *parentStatusRef) } @@ -584,7 +583,7 @@ func WithParentRefAndStatus(name, namespace, controllerName string) Option { func WithRouteParentCondition(conditionType string, status metav1.ConditionStatus, reason, message string) Option { return func(obj client.Object) { - hr, ok := obj.(*gateway.HTTPRoute) + hr, ok := obj.(*gatewayv1.HTTPRoute) if ok && len(hr.Status.Parents) > 0 { // Add condition to the last parent status (most recently added) lastParentIdx := len(hr.Status.Parents) - 1 @@ -607,20 +606,20 @@ func WithRouteParentCondition(conditionType string, status metav1.ConditionStatu func WithBackendRef(name, namespace string) Option { return func(obj client.Object) { - hr, ok := obj.(*gateway.HTTPRoute) + hr, ok := obj.(*gatewayv1.HTTPRoute) if ok { - namespaceName := gateway.Namespace(namespace) + namespaceName := gatewayv1.Namespace(namespace) if hr.Spec.Rules == nil { - hr.Spec.Rules = []gateway.HTTPRouteRule{} + hr.Spec.Rules = []gatewayv1.HTTPRouteRule{} } - group := gateway.Group(gvk.InferencePool.Group) - kind := gateway.Kind(gvk.InferencePool.Kind) - hr.Spec.Rules = append(hr.Spec.Rules, gateway.HTTPRouteRule{ - BackendRefs: []gateway.HTTPBackendRef{ + group := gatewayv1.Group(gvk.InferencePool.Group) + kind := gatewayv1.Kind(gvk.InferencePool.Kind) + hr.Spec.Rules = append(hr.Spec.Rules, gatewayv1.HTTPRouteRule{ + BackendRefs: []gatewayv1.HTTPBackendRef{ { - BackendRef: gateway.BackendRef{ - BackendObjectReference: gateway.BackendObjectReference{ - Name: gateway.ObjectName(name), + BackendRef: gatewayv1.BackendRef{ + BackendObjectReference: gatewayv1.BackendObjectReference{ + Name: gatewayv1.ObjectName(name), Namespace: &namespaceName, Kind: &kind, Group: &group, @@ -719,13 +718,13 @@ func WithExtensionRef(kind, name string) Option { // --- Object Creation Functions --- -func NewGateway(name string, opts ...Option) *gateway.Gateway { - gw := &gateway.Gateway{ +func NewGateway(name string, opts ...Option) *gatewayv1.Gateway { + gw := &gatewayv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: DefaultTestNS, }, - Spec: gateway.GatewaySpec{ + Spec: gatewayv1.GatewaySpec{ GatewayClassName: "istio", }, } @@ -735,8 +734,8 @@ func NewGateway(name string, opts ...Option) *gateway.Gateway { return gw } -func NewHTTPRoute(name string, opts ...Option) *gateway.HTTPRoute { - hr := &gateway.HTTPRoute{ +func NewHTTPRoute(name string, opts ...Option) *gatewayv1.HTTPRoute { + hr := &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: DefaultTestNS, diff --git a/pilot/pkg/config/kube/gateway/inferencepool_test.go b/pilot/pkg/config/kube/gateway/inferencepool_test.go index 74f2eb7dee..58c80b32a5 100644 --- a/pilot/pkg/config/kube/gateway/inferencepool_test.go +++ b/pilot/pkg/config/kube/gateway/inferencepool_test.go @@ -40,7 +40,7 @@ func TestReconcileInferencePool(t *testing.T) { expectedAnnotations map[string]string expectedLabels map[string]string expectedServiceName string - expectedTargetPort int32 + expectedTargetPorts []int32 }{ { name: "basic shadow service creation", @@ -72,7 +72,7 @@ func TestReconcileInferencePool(t *testing.T) { constants.InternalServiceSemantics: constants.ServiceSemanticsInferencePool, InferencePoolRefLabel: "test-pool", }, - expectedTargetPort: 8080, + expectedTargetPorts: []int32{8080}, }, { name: "user label and annotation preservation", @@ -136,7 +136,7 @@ func TestReconcileInferencePool(t *testing.T) { "user.example.com/my-label": "user-value", "another.domain.com/label": "another-value", }, - expectedTargetPort: 8080, + expectedTargetPorts: []int32{8080}, }, { name: "very long inferencepool name", @@ -169,7 +169,45 @@ func TestReconcileInferencePool(t *testing.T) { InferencePoolRefLabel: "very-long-inference-pool-name-that-should-be-truncated-properly", }, expectedServiceName: "very-long-inference-pool-name-that-should-be-trunca-ip-6d24df6a", - expectedTargetPort: 9090, + expectedTargetPorts: []int32{9090}, + }, + { + name: "multiple target ports creates single service port", + inferencePool: &inferencev1.InferencePool{ + ObjectMeta: metav1.ObjectMeta{ + Name: "multi-port-pool", + Namespace: "default", + }, + Spec: inferencev1.InferencePoolSpec{ + TargetPorts: []inferencev1.Port{ + { + Number: inferencev1.PortNumber(8000), + }, + { + Number: inferencev1.PortNumber(8001), + }, + { + Number: inferencev1.PortNumber(8002), + }, + }, + Selector: inferencev1.LabelSelector{ + MatchLabels: map[inferencev1.LabelKey]inferencev1.LabelValue{ + "app": "multiport", + }, + }, + EndpointPickerRef: inferencev1.EndpointPickerRef{ + Name: "dummy", + Port: &inferencev1.Port{ + Number: inferencev1.PortNumber(5421), + }, + }, + }, + }, + expectedLabels: map[string]string{ + constants.InternalServiceSemantics: constants.ServiceSemanticsInferencePool, + InferencePoolRefLabel: "multi-port-pool", + }, + expectedTargetPorts: []int32{8000, 8001, 8002}, }, } @@ -217,8 +255,15 @@ func TestReconcileInferencePool(t *testing.T) { for key, expectedValue := range tc.expectedAnnotations { assert.Equal(t, service.Annotations[key], expectedValue, fmt.Sprintf("Annotation '%s' should have value '%s'", key, expectedValue)) } - assert.Equal(t, service.Spec.Ports[0].Port, int32(54321)) // dummyPort + i - assert.Equal(t, service.Spec.Ports[0].TargetPort.IntVal, tc.expectedTargetPort) + expectedPortCount := len(tc.inferencePool.Spec.TargetPorts) + assert.Equal(t, len(service.Spec.Ports), expectedPortCount, fmt.Sprintf("Shadow service should have %d ports", expectedPortCount)) + + for i := 1; i < len(service.Spec.Ports); i++ { + assert.Equal(t, service.Spec.Ports[i].Port, int32(54321+i)) + assert.Equal(t, service.Spec.Ports[i].TargetPort.IntVal, tc.expectedTargetPorts[i]) + assert.Equal(t, service.Spec.Ports[i].Name, fmt.Sprintf("http-%d", i)) + } + assert.Equal(t, service.OwnerReferences[0].Name, tc.inferencePool.Name) }) } diff --git a/pilot/pkg/config/kube/gateway/references_collection.go b/pilot/pkg/config/kube/gateway/references_collection.go index 6399ca5c41..940c98c6a1 100644 --- a/pilot/pkg/config/kube/gateway/references_collection.go +++ b/pilot/pkg/config/kube/gateway/references_collection.go @@ -18,7 +18,8 @@ import ( "fmt" "k8s.io/apimachinery/pkg/types" - gateway "sigs.k8s.io/gateway-api/apis/v1beta1" + gateway "sigs.k8s.io/gateway-api/apis/v1" + gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" creds "istio.io/istio/pilot/pkg/model/credentials" "istio.io/istio/pkg/config" @@ -50,8 +51,8 @@ type ReferenceGrants struct { index krt.Index[ReferencePair, ReferenceGrant] } -func ReferenceGrantsCollection(referenceGrants krt.Collection[*gateway.ReferenceGrant], opts krt.OptionsBuilder) krt.Collection[ReferenceGrant] { - return krt.NewManyCollection(referenceGrants, func(ctx krt.HandlerContext, obj *gateway.ReferenceGrant) []ReferenceGrant { +func ReferenceGrantsCollection(referenceGrants krt.Collection[*gatewayv1beta1.ReferenceGrant], opts krt.OptionsBuilder) krt.Collection[ReferenceGrant] { + return krt.NewManyCollection(referenceGrants, func(ctx krt.HandlerContext, obj *gatewayv1beta1.ReferenceGrant) []ReferenceGrant { rp := obj.Spec results := make([]ReferenceGrant, 0, len(rp.From)*len(rp.To)) for _, from := range rp.From { diff --git a/pilot/pkg/config/kube/gateway/route_collections.go b/pilot/pkg/config/kube/gateway/route_collections.go index 8a59ddfed0..d211879340 100644 --- a/pilot/pkg/config/kube/gateway/route_collections.go +++ b/pilot/pkg/config/kube/gateway/route_collections.go @@ -26,7 +26,6 @@ import ( inferencev1 "sigs.k8s.io/gateway-api-inference-extension/api/v1" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" gatewayalpha "sigs.k8s.io/gateway-api/apis/v1alpha2" - gateway "sigs.k8s.io/gateway-api/apis/v1beta1" istio "istio.io/api/networking/v1alpha3" networkingclient "istio.io/client-go/pkg/apis/networking/v1" @@ -56,18 +55,18 @@ func (a AncestorBackend) ResourceName() string { } func HTTPRouteCollection( - httpRoutes krt.Collection[*gateway.HTTPRoute], + httpRoutes krt.Collection[*gatewayv1.HTTPRoute], inputs RouteContextInputs, opts krt.OptionsBuilder, -) RouteResult[*gateway.HTTPRoute, gateway.HTTPRouteStatus] { +) RouteResult[*gatewayv1.HTTPRoute, gatewayv1.HTTPRouteStatus] { routeCount := gatewayRouteAttachmentCountCollection(inputs, httpRoutes, gvk.HTTPRoute, opts) - ancestorBackends := krt.NewManyCollection(httpRoutes, func(krtctx krt.HandlerContext, obj *gateway.HTTPRoute) []AncestorBackend { - return extractAncestorBackends(obj.Namespace, obj.Spec.ParentRefs, obj.Spec.Rules, func(r gateway.HTTPRouteRule) []gateway.HTTPBackendRef { + ancestorBackends := krt.NewManyCollection(httpRoutes, func(krtctx krt.HandlerContext, obj *gatewayv1.HTTPRoute) []AncestorBackend { + return extractAncestorBackends(obj.Namespace, obj.Spec.ParentRefs, obj.Spec.Rules, func(r gatewayv1.HTTPRouteRule) []gatewayv1.HTTPBackendRef { return r.BackendRefs }) }, opts.WithName("HTTPAncestors")...) - status, baseVirtualServices := krt.NewStatusManyCollection(httpRoutes, func(krtctx krt.HandlerContext, obj *gateway.HTTPRoute) ( - *gateway.HTTPRouteStatus, + status, baseVirtualServices := krt.NewStatusManyCollection(httpRoutes, func(krtctx krt.HandlerContext, obj *gatewayv1.HTTPRoute) ( + *gatewayv1.HTTPRouteStatus, []RouteWithKey, ) { ctx := inputs.WithCtx(krtctx) @@ -77,7 +76,7 @@ func HTTPRouteCollection( }{} status := obj.Status.DeepCopy() route := obj.Spec - parentStatus, parentRefs, meshResult, gwResult := computeRoute(ctx, obj, func(mesh bool, obj *gateway.HTTPRoute) iter.Seq2[*istio.HTTPRoute, *ConfigError] { + parentStatus, parentRefs, meshResult, gwResult := computeRoute(ctx, obj, func(mesh bool, obj *gatewayv1.HTTPRoute) iter.Seq2[*istio.HTTPRoute, *ConfigError] { return func(yield func(*istio.HTTPRoute, *ConfigError) bool) { for n, r := range route.Rules { // split the rule to make sure each rule has up to one match @@ -87,7 +86,7 @@ func HTTPRouteCollection( } for _, m := range matches { if m != nil { - r.Matches = []gateway.HTTPRouteMatch{*m} + r.Matches = []gatewayv1.HTTPRouteMatch{*m} } istioRoute, ipCfg, configErr := convertHTTPRoute(ctx, r, obj, n, !mesh) if istioRoute != nil && ipCfg != nil && ipCfg.enableExtProc { @@ -128,7 +127,7 @@ func HTTPRouteCollection( routeKey += "/" + strconv.Itoa(int(*parent.OriginalReference.Port)) } ref := types.NamespacedName{ - Namespace: string(ptr.OrDefault(parent.OriginalReference.Namespace, gateway.Namespace(obj.Namespace))), + Namespace: string(ptr.OrDefault(parent.OriginalReference.Namespace, gatewayv1.Namespace(obj.Namespace))), Name: string(parent.OriginalReference.Name), } if parent.InternalKind == gvk.ServiceEntry { @@ -143,7 +142,7 @@ func HTTPRouteCollection( vsHosts = []string{ string(parent.OriginalReference.Name) + "." + - string(ptr.OrDefault(parent.OriginalReference.Namespace, gateway.Namespace(obj.Namespace))) + + string(ptr.OrDefault(parent.OriginalReference.Namespace, gatewayv1.Namespace(obj.Namespace))) + ".svc." + ctx.DomainSuffix, } } @@ -204,7 +203,7 @@ func HTTPRouteCollection( }, opts.WithName("HTTPRoute")...) finalVirtualServices := mergeHTTPRoutes(baseVirtualServices, opts.WithName("HTTPRouteMerged")...) - return RouteResult[*gateway.HTTPRoute, gateway.HTTPRouteStatus]{ + return RouteResult[*gatewayv1.HTTPRoute, gatewayv1.HTTPRouteStatus]{ VirtualServices: finalVirtualServices, RouteAttachments: routeCount, Status: status, @@ -212,7 +211,7 @@ func HTTPRouteCollection( } } -func extractAncestorBackends[RT, BT any](ns string, prefs []gateway.ParentReference, rules []RT, extract func(RT) []BT) []AncestorBackend { +func extractAncestorBackends[RT, BT any](ns string, prefs []gatewayv1.ParentReference, rules []RT, extract func(RT) []BT) []AncestorBackend { gateways := sets.Set[types.NamespacedName]{} for _, r := range prefs { ref := normalizeReference(r.Group, r.Kind, gvk.KubernetesGateway) @@ -325,7 +324,7 @@ func GRPCRouteCollection( routeKey += "/" + strconv.Itoa(int(*parent.OriginalReference.Port)) } ref := types.NamespacedName{ - Namespace: string(ptr.OrDefault(parent.OriginalReference.Namespace, gateway.Namespace(obj.Namespace))), + Namespace: string(ptr.OrDefault(parent.OriginalReference.Namespace, gatewayv1.Namespace(obj.Namespace))), Name: string(parent.OriginalReference.Name), } if parent.InternalKind == gvk.ServiceEntry { @@ -338,7 +337,7 @@ func GRPCRouteCollection( } } else { vsHosts = []string{fmt.Sprintf("%s.%s.svc.%s", - parent.OriginalReference.Name, ptr.OrDefault(parent.OriginalReference.Namespace, gateway.Namespace(obj.Namespace)), ctx.DomainSuffix)} + parent.OriginalReference.Name, ptr.OrDefault(parent.OriginalReference.Namespace, gatewayv1.Namespace(obj.Namespace)), ctx.DomainSuffix)} } } if len(routes) == 0 { @@ -412,7 +411,7 @@ func TCPRouteCollection( ) RouteResult[*gatewayalpha.TCPRoute, gatewayalpha.TCPRouteStatus] { routeCount := gatewayRouteAttachmentCountCollection(inputs, tcpRoutes, gvk.TCPRoute, opts) ancestorBackends := krt.NewManyCollection(tcpRoutes, func(krtctx krt.HandlerContext, obj *gatewayalpha.TCPRoute) []AncestorBackend { - return extractAncestorBackends(obj.Namespace, obj.Spec.ParentRefs, obj.Spec.Rules, func(r gatewayalpha.TCPRouteRule) []gateway.BackendRef { + return extractAncestorBackends(obj.Namespace, obj.Spec.ParentRefs, obj.Spec.Rules, func(r gatewayalpha.TCPRouteRule) []gatewayv1.BackendRef { return r.BackendRefs }) }, opts.WithName("TCPAncestors")...) @@ -446,7 +445,7 @@ func TCPRouteCollection( routes = augmentTCPPortMatch(routes, *parent.OriginalReference.Port) } ref := types.NamespacedName{ - Namespace: string(ptr.OrDefault(parent.OriginalReference.Namespace, gateway.Namespace(obj.Namespace))), + Namespace: string(ptr.OrDefault(parent.OriginalReference.Namespace, gatewayv1.Namespace(obj.Namespace))), Name: string(parent.OriginalReference.Name), } if parent.InternalKind == gvk.ServiceEntry { @@ -503,7 +502,7 @@ func TLSRouteCollection( ) RouteResult[*gatewayalpha.TLSRoute, gatewayalpha.TLSRouteStatus] { routeCount := gatewayRouteAttachmentCountCollection(inputs, tlsRoutes, gvk.TLSRoute, opts) ancestorBackends := krt.NewManyCollection(tlsRoutes, func(krtctx krt.HandlerContext, obj *gatewayalpha.TLSRoute) []AncestorBackend { - return extractAncestorBackends(obj.Namespace, obj.Spec.ParentRefs, obj.Spec.Rules, func(r gatewayalpha.TLSRouteRule) []gateway.BackendRef { + return extractAncestorBackends(obj.Namespace, obj.Spec.ParentRefs, obj.Spec.Rules, func(r gatewayalpha.TLSRouteRule) []gatewayv1.BackendRef { return r.BackendRefs }) }, opts.WithName("TLSAncestors")...) @@ -534,7 +533,7 @@ func TLSRouteCollection( if parent.IsMesh() { routes = meshResult.routes ref := types.NamespacedName{ - Namespace: string(ptr.OrDefault(parent.OriginalReference.Namespace, gateway.Namespace(obj.Namespace))), + Namespace: string(ptr.OrDefault(parent.OriginalReference.Namespace, gatewayv1.Namespace(obj.Namespace))), Name: string(parent.OriginalReference.Name), } if parent.InternalKind == gvk.ServiceEntry { @@ -591,7 +590,7 @@ func computeRoute[T controllers.Object, O comparable](ctx RouteContext, obj T, t mesh bool, obj T, ) iter.Seq2[O, *ConfigError], -) ([]gateway.RouteParentStatus, []routeParentReference, conversionResult[O], conversionResult[O]) { +) ([]gatewayv1.RouteParentStatus, []routeParentReference, conversionResult[O], conversionResult[O]) { parentRefs := extractParentReferenceInfo(ctx, ctx.RouteParents, obj) convertRules := func(mesh bool) conversionResult[O] { diff --git a/pilot/pkg/config/kube/gateway/status_test.go b/pilot/pkg/config/kube/gateway/status_test.go index 0e0f9eae06..72e6ad8a2d 100644 --- a/pilot/pkg/config/kube/gateway/status_test.go +++ b/pilot/pkg/config/kube/gateway/status_test.go @@ -48,7 +48,7 @@ func TestStatusCollections(t *testing.T) { fakeCol := krt.NewStaticCollection[Status](nil, []Status{obj1}, krt.WithStop(stop)) status.RegisterStatus(c.status, fakeCol, func(i *v1.ConfigMap) string { return "" - }) + }, c.tagWatcher.AccessUnprotected()) sq1 := &TestStatusQueue{state: map[status.Resource]any{}} setAndWait(t, c, sq1) diff --git a/pilot/pkg/config/kube/gateway/testdata/backend-tls-policy.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/backend-tls-policy.status.yaml.golden index 2576a07437..950d3dec7f 100644 --- a/pilot/pkg/config/kube/gateway/testdata/backend-tls-policy.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/backend-tls-policy.status.yaml.golden @@ -336,7 +336,7 @@ status: type: ResolvedRefs controllerName: istio.io/mesh-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -349,7 +349,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -400,7 +400,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http diff --git a/pilot/pkg/config/kube/gateway/testdata/backend-tls-policy.yaml b/pilot/pkg/config/kube/gateway/testdata/backend-tls-policy.yaml index b61cf7e1c4..b440660638 100644 --- a/pilot/pkg/config/kube/gateway/testdata/backend-tls-policy.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/backend-tls-policy.yaml @@ -1,14 +1,14 @@ # echo-https must be created by the kube-client, because it's used in a test # that verifies `sectionName`, which is internally read from krt, # so it could be just a `model.ServiceInstance` -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -24,7 +24,7 @@ spec: port: 80 protocol: HTTP --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http diff --git a/pilot/pkg/config/kube/gateway/testdata/benchmark-httproute.yaml b/pilot/pkg/config/kube/gateway/testdata/benchmark-httproute.yaml index 7bfa89b790..4b7d20c13b 100644 --- a/pilot/pkg/config/kube/gateway/testdata/benchmark-httproute.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/benchmark-httproute.yaml @@ -1,12 +1,12 @@ # the same as pilot/pkg/config/kube/gateway/testdata/route-precedence.yaml -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -28,7 +28,7 @@ spec: matchLabels: istio.io/test-name-part: allowed --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http @@ -60,7 +60,7 @@ spec: - name: svc2 port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http @@ -106,7 +106,7 @@ spec: - name: svc3 port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http diff --git a/pilot/pkg/config/kube/gateway/testdata/delegated.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/delegated.status.yaml.golden index 42195a7f7f..770d2cd298 100644 --- a/pilot/pkg/config/kube/gateway/testdata/delegated.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/delegated.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -11,7 +11,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -90,7 +90,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http @@ -114,7 +114,7 @@ status: name: gateway namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http diff --git a/pilot/pkg/config/kube/gateway/testdata/delegated.yaml b/pilot/pkg/config/kube/gateway/testdata/delegated.yaml index 20d8689e1f..26e948ea1b 100644 --- a/pilot/pkg/config/kube/gateway/testdata/delegated.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/delegated.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -37,7 +37,7 @@ spec: matchLabels: kubernetes.io/metadata.name: banana --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http @@ -51,7 +51,7 @@ spec: - name: httpbin-apple port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/cluster-ip.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/cluster-ip.yaml index 16ad76b222..2bf3c562aa 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/cluster-ip.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/cluster-ip.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: @@ -17,7 +17,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -35,7 +35,7 @@ metadata: name: default namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -239,7 +239,7 @@ metadata: name: default namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: null diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/copy-labels-annotations-disabled-infra-nil.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/copy-labels-annotations-disabled-infra-nil.yaml index e0e9850957..1f30d602d9 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/copy-labels-annotations-disabled-infra-nil.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/copy-labels-annotations-disabled-infra-nil.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: @@ -16,7 +16,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -33,7 +33,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -235,7 +235,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: null diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/copy-labels-annotations-disabled-infra-set.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/copy-labels-annotations-disabled-infra-set.yaml index e18c17b9cf..7b2a4691b7 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/copy-labels-annotations-disabled-infra-set.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/copy-labels-annotations-disabled-infra-set.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: @@ -18,7 +18,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -37,7 +37,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -243,7 +243,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: null diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/copy-labels-annotations-enabled-infra-nil.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/copy-labels-annotations-enabled-infra-nil.yaml index 4d5ce2bc51..69b0575309 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/copy-labels-annotations-enabled-infra-nil.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/copy-labels-annotations-enabled-infra-nil.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: @@ -18,7 +18,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -37,7 +37,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -243,7 +243,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: null diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/custom-class.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/custom-class.yaml index 6be40d80e7..dcfdb1b54a 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/custom-class.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/custom-class.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: @@ -16,7 +16,7 @@ metadata: name: default-custom namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -33,7 +33,7 @@ metadata: name: default-custom namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -235,7 +235,7 @@ metadata: name: default-custom namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: null diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/customizations.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/customizations.yaml index 2721c32ecd..5f21014a79 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/customizations.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/customizations.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: @@ -17,7 +17,7 @@ metadata: name: namespace-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: namespace uid: "" @@ -36,7 +36,7 @@ metadata: name: namespace-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: namespace uid: "" @@ -244,7 +244,7 @@ metadata: name: namespace-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: namespace uid: null @@ -272,7 +272,7 @@ metadata: name: namespace-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: namespace uid: "" @@ -297,7 +297,7 @@ metadata: name: namespace-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: namespace uid: "" diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/gateway-with-infrerencepool-extproc-infra-label.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/gateway-with-infrerencepool-extproc-infra-label.yaml index 7f0fb687bb..f9684cc7f1 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/gateway-with-infrerencepool-extproc-infra-label.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/gateway-with-infrerencepool-extproc-infra-label.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: @@ -18,7 +18,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -37,7 +37,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -240,7 +240,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: null diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/illegal_customizations.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/illegal_customizations.yaml index 594973fbfa..eedc8db65d 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/illegal_customizations.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/illegal_customizations.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/infrastructure-labels-annotations.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/infrastructure-labels-annotations.yaml index 873794e628..454ad7139e 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/infrastructure-labels-annotations.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/infrastructure-labels-annotations.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: @@ -18,7 +18,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -37,7 +37,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -243,7 +243,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: null diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/istio-east-west.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/istio-east-west.yaml index 566cad3582..f0d5bb0480 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/istio-east-west.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/istio-east-west.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: @@ -16,7 +16,7 @@ metadata: name: eastwestgateway namespace: istio-system ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: eastwestgateway uid: "" @@ -33,7 +33,7 @@ metadata: name: eastwestgateway namespace: istio-system ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: eastwestgateway uid: "" @@ -241,7 +241,7 @@ metadata: name: eastwestgateway namespace: istio-system ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: eastwestgateway uid: "" diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/istio-upgrade-to-1.24.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/istio-upgrade-to-1.24.yaml index cb2253f657..5211e55fcb 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/istio-upgrade-to-1.24.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/istio-upgrade-to-1.24.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: @@ -16,7 +16,7 @@ metadata: name: test-upgrade namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: test-upgrade uid: "" @@ -33,7 +33,7 @@ metadata: name: test-upgrade namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: test-upgrade uid: "" @@ -241,7 +241,7 @@ metadata: name: test-upgrade namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: test-upgrade uid: "" diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/kube-gateway-ambient-redirect-infra.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/kube-gateway-ambient-redirect-infra.yaml index b2b6129833..5637b60150 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/kube-gateway-ambient-redirect-infra.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/kube-gateway-ambient-redirect-infra.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: @@ -16,7 +16,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -33,7 +33,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -235,7 +235,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: null diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/kube-gateway-ambient-redirect.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/kube-gateway-ambient-redirect.yaml index b2b6129833..5637b60150 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/kube-gateway-ambient-redirect.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/kube-gateway-ambient-redirect.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: @@ -16,7 +16,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -33,7 +33,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -235,7 +235,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: null diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/manual-ip.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/manual-ip.yaml index 9486d327f2..29d201f431 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/manual-ip.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/manual-ip.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: @@ -16,7 +16,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -33,7 +33,7 @@ metadata: name: default namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -235,7 +235,7 @@ metadata: name: default namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: null diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/manual-sa.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/manual-sa.yaml index 6356ca9ada..54df6c5755 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/manual-sa.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/manual-sa.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: @@ -16,7 +16,7 @@ metadata: name: custom-sa namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -33,7 +33,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -235,7 +235,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: null diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/multinetwork.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/multinetwork.yaml index ad375d57e8..38da38fe9c 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/multinetwork.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/multinetwork.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: @@ -17,7 +17,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -35,7 +35,7 @@ metadata: name: default namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -243,7 +243,7 @@ metadata: name: default namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: null diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/proxy-config-crd.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/proxy-config-crd.yaml index 55255b3b97..51adc10560 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/proxy-config-crd.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/proxy-config-crd.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: @@ -16,7 +16,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -33,7 +33,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -235,7 +235,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: null diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/simple.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/simple.yaml index 4d5ce2bc51..69b0575309 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/simple.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/simple.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: @@ -18,7 +18,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -37,7 +37,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: "" @@ -243,7 +243,7 @@ metadata: name: default-istio namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: default uid: null diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/waypoint-no-network-label.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/waypoint-no-network-label.yaml index 6cab2365bc..e56943afe5 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/waypoint-no-network-label.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/waypoint-no-network-label.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: @@ -16,7 +16,7 @@ metadata: name: namespace namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: namespace uid: "" @@ -33,7 +33,7 @@ metadata: name: namespace namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: namespace uid: "" @@ -239,7 +239,7 @@ metadata: name: namespace namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: namespace uid: "" diff --git a/pilot/pkg/config/kube/gateway/testdata/deployment/waypoint.yaml b/pilot/pkg/config/kube/gateway/testdata/deployment/waypoint.yaml index 6cab2365bc..e56943afe5 100644 --- a/pilot/pkg/config/kube/gateway/testdata/deployment/waypoint.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/deployment/waypoint.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: @@ -16,7 +16,7 @@ metadata: name: namespace namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: namespace uid: "" @@ -33,7 +33,7 @@ metadata: name: namespace namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: namespace uid: "" @@ -239,7 +239,7 @@ metadata: name: namespace namespace: default ownerReferences: - - apiVersion: gateway.networking.k8s.io/v1beta1 + - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway name: namespace uid: "" diff --git a/pilot/pkg/config/kube/gateway/testdata/east-west-ambient.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/east-west-ambient.status.yaml.golden index 184a78666c..90d5db4e76 100644 --- a/pilot/pkg/config/kube/gateway/testdata/east-west-ambient.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/east-west-ambient.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eastwestgateway @@ -43,7 +43,7 @@ status: name: mesh supportedKinds: [] --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid diff --git a/pilot/pkg/config/kube/gateway/testdata/east-west-ambient.yaml b/pilot/pkg/config/kube/gateway/testdata/east-west-ambient.yaml index d192115824..3092c8e93b 100644 --- a/pilot/pkg/config/kube/gateway/testdata/east-west-ambient.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/east-west-ambient.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eastwestgateway @@ -16,7 +16,7 @@ spec: options: gateway.istio.io/tls-terminate-mode: ISTIO_MUTUAL --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid diff --git a/pilot/pkg/config/kube/gateway/testdata/eastwest-labelport.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/eastwest-labelport.status.yaml.golden index 9c50f17795..c655c82286 100644 --- a/pilot/pkg/config/kube/gateway/testdata/eastwest-labelport.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/eastwest-labelport.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eastwestgateway diff --git a/pilot/pkg/config/kube/gateway/testdata/eastwest-labelport.yaml b/pilot/pkg/config/kube/gateway/testdata/eastwest-labelport.yaml index 3e27754587..362a46fdd6 100644 --- a/pilot/pkg/config/kube/gateway/testdata/eastwest-labelport.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/eastwest-labelport.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eastwestgateway diff --git a/pilot/pkg/config/kube/gateway/testdata/eastwest-remote.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/eastwest-remote.status.yaml.golden index 7c486f4f60..e8e805cae1 100644 --- a/pilot/pkg/config/kube/gateway/testdata/eastwest-remote.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/eastwest-remote.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eastwestgateway diff --git a/pilot/pkg/config/kube/gateway/testdata/eastwest-remote.yaml b/pilot/pkg/config/kube/gateway/testdata/eastwest-remote.yaml index e7e0270703..5042971e57 100644 --- a/pilot/pkg/config/kube/gateway/testdata/eastwest-remote.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/eastwest-remote.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eastwestgateway diff --git a/pilot/pkg/config/kube/gateway/testdata/eastwest-tlsoption.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/eastwest-tlsoption.status.yaml.golden index 9c50f17795..c655c82286 100644 --- a/pilot/pkg/config/kube/gateway/testdata/eastwest-tlsoption.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/eastwest-tlsoption.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eastwestgateway diff --git a/pilot/pkg/config/kube/gateway/testdata/eastwest-tlsoption.yaml b/pilot/pkg/config/kube/gateway/testdata/eastwest-tlsoption.yaml index 88ef8e482d..b8f6c1a862 100644 --- a/pilot/pkg/config/kube/gateway/testdata/eastwest-tlsoption.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/eastwest-tlsoption.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eastwestgateway diff --git a/pilot/pkg/config/kube/gateway/testdata/eastwest.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/eastwest.status.yaml.golden index 9c50f17795..c655c82286 100644 --- a/pilot/pkg/config/kube/gateway/testdata/eastwest.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/eastwest.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eastwestgateway diff --git a/pilot/pkg/config/kube/gateway/testdata/eastwest.yaml b/pilot/pkg/config/kube/gateway/testdata/eastwest.yaml index e10e475042..250e5d4618 100644 --- a/pilot/pkg/config/kube/gateway/testdata/eastwest.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/eastwest.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eastwestgateway diff --git a/pilot/pkg/config/kube/gateway/testdata/grpc.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/grpc.status.yaml.golden index 8d031f3762..27140acf53 100644 --- a/pilot/pkg/config/kube/gateway/testdata/grpc.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/grpc.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -11,7 +11,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway diff --git a/pilot/pkg/config/kube/gateway/testdata/grpc.yaml b/pilot/pkg/config/kube/gateway/testdata/grpc.yaml index a1d3a8f1b6..a945579f9b 100644 --- a/pilot/pkg/config/kube/gateway/testdata/grpc.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/grpc.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway diff --git a/pilot/pkg/config/kube/gateway/testdata/http.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/http.status.yaml.golden index 7e0a520777..56c77da7d7 100644 --- a/pilot/pkg/config/kube/gateway/testdata/http.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/http.status.yaml.golden @@ -14,7 +14,7 @@ metadata: spec: null status: {} --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -27,7 +27,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -78,7 +78,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http @@ -102,7 +102,7 @@ status: name: gateway namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http-not-selected @@ -126,7 +126,7 @@ status: name: gateway namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http-retry-request @@ -150,7 +150,7 @@ status: name: gateway namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http-route-cors @@ -174,7 +174,7 @@ status: name: gateway namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http-timeout-backend-request @@ -198,7 +198,7 @@ status: name: gateway namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http-timeout-request @@ -222,7 +222,7 @@ status: name: gateway namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http2 @@ -246,7 +246,7 @@ status: name: gateway namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: mirror @@ -270,7 +270,7 @@ status: name: gateway namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: multiple-inferencepool-backend-refs @@ -294,7 +294,7 @@ status: name: gateway namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: redirect @@ -318,7 +318,7 @@ status: name: gateway namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: redirect-prefix-replace @@ -342,7 +342,7 @@ status: name: gateway namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: rewrite diff --git a/pilot/pkg/config/kube/gateway/testdata/http.yaml b/pilot/pkg/config/kube/gateway/testdata/http.yaml index 6831abd783..1b84946a0f 100644 --- a/pilot/pkg/config/kube/gateway/testdata/http.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/http.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -24,7 +24,7 @@ spec: namespaces: from: All --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http @@ -60,7 +60,7 @@ spec: - name: httpbin port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http2 @@ -86,7 +86,7 @@ spec: - name: httpbin-wildcard port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: redirect @@ -106,7 +106,7 @@ spec: type: ReplaceFullPath replaceFullPath: /replace-full --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: redirect-prefix-replace @@ -131,7 +131,7 @@ spec: type: ReplacePrefixMatch replacePrefixMatch: /replacement --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: rewrite @@ -184,7 +184,7 @@ spec: - name: httpbin port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: mirror @@ -213,7 +213,7 @@ spec: - name: httpbin port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http-not-selected @@ -232,7 +232,7 @@ spec: - name: httpbin-bad port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http-timeout-request @@ -253,7 +253,7 @@ spec: timeouts: request: 1ms --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http-timeout-backend-request @@ -275,7 +275,7 @@ spec: request: 2ms backendRequest: 1ms --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http-retry-request @@ -317,7 +317,7 @@ spec: retry: attempts: 0 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http-route-cors @@ -352,7 +352,7 @@ spec: - Range type: CORS --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: multiple-inferencepool-backend-refs @@ -398,6 +398,8 @@ metadata: spec: targetPorts: - number: 8000 + - number: 8001 + - number: 8002 selector: matchLabels: app: vllm-llama3-8b-instruct diff --git a/pilot/pkg/config/kube/gateway/testdata/http.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/http.yaml.golden index 6a898e48aa..6238bf4ca0 100644 --- a/pilot/pkg/config/kube/gateway/testdata/http.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/http.yaml.golden @@ -229,6 +229,8 @@ spec: route: - destination: host: infpool-gen-ip-6580eb2c.default.svc.domain.suffix + port: + number: 54321 - match: - headers: my-header: @@ -239,6 +241,8 @@ spec: route: - destination: host: infpool-gen2-ip-97b729d1.default.svc.domain.suffix + port: + number: 54321 --- apiVersion: networking.istio.io/v1 kind: VirtualService diff --git a/pilot/pkg/config/kube/gateway/testdata/invalid.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/invalid.status.yaml.golden index d7b34bf4dd..1c94db9842 100644 --- a/pilot/pkg/config/kube/gateway/testdata/invalid.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/invalid.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -11,7 +11,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid-gateway-address @@ -30,7 +30,7 @@ status: status: "False" type: Programmed --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -81,7 +81,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid-cert-kind @@ -133,7 +133,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid-cert-malformed @@ -183,7 +183,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid-cert-notfound @@ -235,7 +235,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid-frontendvalidation-malformed @@ -286,7 +286,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid-frontendvalidation-missing-refgrant @@ -338,7 +338,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid-frontendvalidation-nonexistent @@ -390,7 +390,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid-frontendvalidation-unknown @@ -442,7 +442,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid-service @@ -491,7 +491,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: protocol-lower-case @@ -536,7 +536,7 @@ status: name: http supportedKinds: [] --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: target-port-reference @@ -587,7 +587,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: udp-protocol @@ -632,7 +632,7 @@ status: name: udp supportedKinds: [] --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: unknown-protocol @@ -677,7 +677,7 @@ status: name: unknown supportedKinds: [] --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-backendRef-hostname @@ -702,7 +702,7 @@ status: kind: Service name: httpbin --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-backendRef-kind @@ -726,7 +726,7 @@ status: name: gateway namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-backendRef-mirror @@ -751,7 +751,7 @@ status: kind: Service name: httpbin --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-backendRef-mixed @@ -775,7 +775,7 @@ status: name: gateway namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-backendRef-notfound @@ -799,7 +799,7 @@ status: name: gateway namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-backendRef-serviceimport @@ -824,7 +824,7 @@ status: kind: Service name: httpbin --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-mirror @@ -848,7 +848,7 @@ status: name: gateway namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-parentRef-port @@ -873,7 +873,7 @@ status: namespace: istio-system port: 1234 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-parentRef-service @@ -898,7 +898,7 @@ status: kind: Service name: not-found --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-parentRef-service-entry @@ -923,7 +923,7 @@ status: kind: ServiceEntry name: not-found --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-sectionname-port @@ -948,7 +948,7 @@ status: namespace: istio-system sectionName: fake --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: no-backend diff --git a/pilot/pkg/config/kube/gateway/testdata/invalid.yaml b/pilot/pkg/config/kube/gateway/testdata/invalid.yaml index 44b5e316c3..32db92287e 100644 --- a/pilot/pkg/config/kube/gateway/testdata/invalid.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/invalid.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -24,7 +24,7 @@ spec: namespaces: from: All --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid-service @@ -40,7 +40,7 @@ spec: - value: fake-service.com type: Hostname --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: target-port-reference @@ -56,7 +56,7 @@ spec: port: 8080 # Test service has port 80 with targetPort 8080 protocol: HTTP --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid-gateway-address @@ -72,7 +72,7 @@ spec: port: 80 protocol: HTTP --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid-cert-kind @@ -94,7 +94,7 @@ spec: group: core kind: unknown --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid-cert-notfound @@ -115,7 +115,7 @@ spec: - name: nonexistent kind: Secret --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid-cert-malformed @@ -136,7 +136,7 @@ spec: - name: malformed kind: Secret --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid-frontendvalidation-malformed @@ -167,7 +167,7 @@ spec: certificateRefs: - name: my-cert-http --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid-frontendvalidation-unknown @@ -195,7 +195,7 @@ spec: certificateRefs: - name: my-cert-http --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid-frontendvalidation-nonexistent @@ -223,7 +223,7 @@ spec: certificateRefs: - name: my-cert-http --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid-frontendvalidation-missing-refgrant @@ -252,7 +252,7 @@ spec: certificateRefs: - name: my-cert-http --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: udp-protocol @@ -264,7 +264,7 @@ spec: port: 1234 protocol: UDP --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: unknown-protocol @@ -276,7 +276,7 @@ spec: port: 1234 protocol: unknown --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: protocol-lower-case @@ -288,7 +288,7 @@ spec: port: 1234 protocol: http --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-backendRef-kind @@ -303,7 +303,7 @@ spec: - name: httpbin kind: GcsBucket --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-backendRef-notfound @@ -318,7 +318,7 @@ spec: - name: nonexistent port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-backendRef-mixed @@ -340,7 +340,7 @@ spec: kind: GcsBucket weight: 1 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-mirror @@ -361,7 +361,7 @@ spec: - name: httpbin port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: no-backend @@ -379,7 +379,7 @@ spec: name: httpbin port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-parentRef-port @@ -396,7 +396,7 @@ spec: port: 80 weight: 1 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-sectionname-port @@ -413,7 +413,7 @@ spec: port: 80 weight: 1 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-parentRef-service @@ -429,7 +429,7 @@ spec: port: 80 weight: 1 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-parentRef-service-entry @@ -445,7 +445,7 @@ spec: port: 80 weight: 1 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-backendRef-hostname @@ -463,7 +463,7 @@ spec: port: 80 weight: 1 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-backendRef-serviceimport @@ -480,7 +480,7 @@ spec: name: unknown-service-import port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-backendRef-mirror diff --git a/pilot/pkg/config/kube/gateway/testdata/isolation.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/isolation.status.yaml.golden index c7a07923c1..0f585d3e59 100644 --- a/pilot/pkg/config/kube/gateway/testdata/isolation.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/isolation.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: isolation @@ -131,7 +131,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: attaches-to-abc-foo-example-com-with-hostname-intersection @@ -157,7 +157,7 @@ status: namespace: gateway-conformance-infra sectionName: abc-foo-example-com --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: attaches-to-empty-hostname-with-hostname-intersection @@ -183,7 +183,7 @@ status: namespace: gateway-conformance-infra sectionName: empty-hostname --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: attaches-to-wildcard-example-com-with-hostname-intersection @@ -209,7 +209,7 @@ status: namespace: gateway-conformance-infra sectionName: wildcard-example-com --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: attaches-to-wildcard-foo-example-com-with-hostname-intersection diff --git a/pilot/pkg/config/kube/gateway/testdata/isolation.yaml b/pilot/pkg/config/kube/gateway/testdata/isolation.yaml index 75960a44a9..d45bd599d2 100644 --- a/pilot/pkg/config/kube/gateway/testdata/isolation.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/isolation.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: isolation @@ -34,7 +34,7 @@ spec: namespaces: from: All --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: attaches-to-empty-hostname-with-hostname-intersection @@ -58,7 +58,7 @@ spec: - name: infra-backend-v1 port: 8080 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: attaches-to-wildcard-example-com-with-hostname-intersection @@ -82,7 +82,7 @@ spec: - name: infra-backend-v1 port: 8080 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: attaches-to-wildcard-foo-example-com-with-hostname-intersection @@ -106,7 +106,7 @@ spec: - name: infra-backend-v1 port: 8080 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: attaches-to-abc-foo-example-com-with-hostname-intersection diff --git a/pilot/pkg/config/kube/gateway/testdata/listenerset-cross-namespace.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/listenerset-cross-namespace.status.yaml.golden index 6c9e33081e..7aac8d491e 100644 --- a/pilot/pkg/config/kube/gateway/testdata/listenerset-cross-namespace.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/listenerset-cross-namespace.status.yaml.golden @@ -175,7 +175,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -188,7 +188,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: parent-gateway diff --git a/pilot/pkg/config/kube/gateway/testdata/listenerset-cross-namespace.yaml b/pilot/pkg/config/kube/gateway/testdata/listenerset-cross-namespace.yaml index 0a5c62ebcd..e2ed4f53c8 100644 --- a/pilot/pkg/config/kube/gateway/testdata/listenerset-cross-namespace.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/listenerset-cross-namespace.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: parent-gateway diff --git a/pilot/pkg/config/kube/gateway/testdata/listenerset-empty-listeners.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/listenerset-empty-listeners.status.yaml.golden index 15375e6bf5..8dae4e8b81 100644 --- a/pilot/pkg/config/kube/gateway/testdata/listenerset-empty-listeners.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/listenerset-empty-listeners.status.yaml.golden @@ -47,7 +47,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -60,7 +60,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: parent-gateway diff --git a/pilot/pkg/config/kube/gateway/testdata/listenerset-empty-listeners.yaml b/pilot/pkg/config/kube/gateway/testdata/listenerset-empty-listeners.yaml index 59b2a15782..e427764274 100644 --- a/pilot/pkg/config/kube/gateway/testdata/listenerset-empty-listeners.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/listenerset-empty-listeners.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: parent-gateway diff --git a/pilot/pkg/config/kube/gateway/testdata/listenerset-invalid.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/listenerset-invalid.status.yaml.golden index 362636f3be..c1c2540553 100644 --- a/pilot/pkg/config/kube/gateway/testdata/listenerset-invalid.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/listenerset-invalid.status.yaml.golden @@ -105,7 +105,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -118,7 +118,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: not-accepted-parent @@ -142,7 +142,7 @@ status: status: "False" type: Programmed --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: parent-gateway @@ -200,7 +200,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: parent-no-allowed-listeners @@ -251,7 +251,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: parent-with-no-children @@ -307,7 +307,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: waypoint diff --git a/pilot/pkg/config/kube/gateway/testdata/listenerset-invalid.yaml b/pilot/pkg/config/kube/gateway/testdata/listenerset-invalid.yaml index 5ce438111c..6e4f418894 100644 --- a/pilot/pkg/config/kube/gateway/testdata/listenerset-invalid.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/listenerset-invalid.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: waypoint @@ -20,7 +20,7 @@ spec: port: 15008 protocol: HBONE --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: parent-gateway @@ -55,7 +55,7 @@ spec: protocol: HTTP port: 12345 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: not-accepted-parent @@ -106,7 +106,7 @@ spec: protocol: HTTP port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: parent-with-no-children @@ -125,7 +125,7 @@ spec: protocol: HTTP port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: parent-no-allowed-listeners diff --git a/pilot/pkg/config/kube/gateway/testdata/listenerset-same-name-different-ns.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/listenerset-same-name-different-ns.status.yaml.golden index 5ab9b07a99..e69de29bb2 100644 --- a/pilot/pkg/config/kube/gateway/testdata/listenerset-same-name-different-ns.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/listenerset-same-name-different-ns.status.yaml.golden @@ -1,219 +0,0 @@ -apiVersion: gateway.networking.x-k8s.io/v1alpha1 -kind: XListenerSet -metadata: - name: same-name-http - namespace: ns1 -spec: null -status: - conditions: - - lastTransitionTime: fake - message: Resource accepted - reason: Accepted - status: "True" - type: Accepted - - lastTransitionTime: fake - message: Resource programmed, assigned to service(s) istio-ingressgateway.istio-system.svc.domain.suffix:80 - reason: Programmed - status: "True" - type: Programmed - listeners: - - attachedRoutes: 0 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: Accepted - status: "True" - type: Accepted - - lastTransitionTime: fake - message: No errors found - reason: NoConflicts - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: Programmed - status: "True" - type: Programmed - - lastTransitionTime: fake - message: No errors found - reason: ResolvedRefs - status: "True" - type: ResolvedRefs - name: http - port: 80 - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute - - group: gateway.networking.k8s.io - kind: GRPCRoute ---- -apiVersion: gateway.networking.x-k8s.io/v1alpha1 -kind: XListenerSet -metadata: - name: same-name-http - namespace: ns2 -spec: null -status: - conditions: - - lastTransitionTime: fake - message: Resource accepted - reason: Accepted - status: "True" - type: Accepted - - lastTransitionTime: fake - message: Resource programmed, assigned to service(s) istio-ingressgateway.istio-system.svc.domain.suffix:80 - reason: Programmed - status: "True" - type: Programmed - listeners: - - attachedRoutes: 0 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: Accepted - status: "True" - type: Accepted - - lastTransitionTime: fake - message: No errors found - reason: NoConflicts - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: Programmed - status: "True" - type: Programmed - - lastTransitionTime: fake - message: No errors found - reason: ResolvedRefs - status: "True" - type: ResolvedRefs - name: http - port: 80 - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute - - group: gateway.networking.k8s.io - kind: GRPCRoute ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: GatewayClass -metadata: - name: istio -spec: null -status: - conditions: - - lastTransitionTime: fake - message: Handled by Istio controller - reason: Accepted - status: "True" - type: Accepted ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: Gateway -metadata: - name: parent-gateway - namespace: istio-system -spec: null -status: - addresses: - - type: IPAddress - value: 1.2.3.4 - conditions: - - lastTransitionTime: fake - message: Resource accepted - reason: Accepted - status: "True" - type: Accepted - - lastTransitionTime: fake - message: At least one ListenerSet is attached - reason: ListenersAttached - status: "True" - type: AttachedListenerSets - - lastTransitionTime: fake - message: Resource programmed, assigned to service(s) istio-ingressgateway.istio-system.svc.domain.suffix:80 - reason: Programmed - status: "True" - type: Programmed - listeners: - - attachedRoutes: 0 - conditions: - - lastTransitionTime: fake - message: No errors found - reason: Accepted - status: "True" - type: Accepted - - lastTransitionTime: fake - message: No errors found - reason: NoConflicts - status: "False" - type: Conflicted - - lastTransitionTime: fake - message: No errors found - reason: Programmed - status: "True" - type: Programmed - - lastTransitionTime: fake - message: No errors found - reason: ResolvedRefs - status: "True" - type: ResolvedRefs - name: default - supportedKinds: - - group: gateway.networking.k8s.io - kind: HTTPRoute - - group: gateway.networking.k8s.io - kind: GRPCRoute ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: bind-ns1 - namespace: ns1 -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: Accepted - status: "True" - type: Accepted - - lastTransitionTime: fake - message: backend(httpbin.ns1.svc.domain.suffix) not found - reason: BackendNotFound - status: "False" - type: ResolvedRefs - controllerName: istio.io/gateway-controller - parentRef: - group: gateway.networking.x-k8s.io - kind: XListenerSet - name: same-name-http - namespace: ns1 ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: bind-ns2 - namespace: ns2 -spec: null -status: - parents: - - conditions: - - lastTransitionTime: fake - message: Route was valid - reason: Accepted - status: "True" - type: Accepted - - lastTransitionTime: fake - message: backend(httpbin.ns2.svc.domain.suffix) not found - reason: BackendNotFound - status: "False" - type: ResolvedRefs - controllerName: istio.io/gateway-controller - parentRef: - group: gateway.networking.x-k8s.io - kind: XListenerSet - name: same-name-http - namespace: ns2 ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/listenerset-same-name-different-ns.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/listenerset-same-name-different-ns.yaml.golden index e24e12b6b7..e69de29bb2 100644 --- a/pilot/pkg/config/kube/gateway/testdata/listenerset-same-name-different-ns.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/listenerset-same-name-different-ns.yaml.golden @@ -1,107 +0,0 @@ -apiVersion: networking.istio.io/v1 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-semantics: gateway - internal.istio.io/gateway-service: istio-ingressgateway.istio-system.svc.domain.suffix - internal.istio.io/parents: Gateway/parent-gateway/default.istio-system - internal.istio.io/service-account-name: "" - name: parent-gateway~istio-autogenerated-k8s-gateway~default - namespace: istio-system -spec: - servers: - - hosts: - - istio-system/*.example.com - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-semantics: gateway - internal.istio.io/gateway-service: istio-ingressgateway.istio-system.svc.domain.suffix - internal.istio.io/parent-namespace: istio-system - internal.istio.io/parents: XListenerSet/same-name-http/http.ns1 - internal.istio.io/service-account-name: parent-gateway-istio - name: same-name-http~istio-autogenerated-k8s-gateway~http - namespace: ns1 -spec: - servers: - - hosts: - - ns1/first.example.com - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1 -kind: Gateway -metadata: - annotations: - internal.istio.io/gateway-semantics: gateway - internal.istio.io/gateway-service: istio-ingressgateway.istio-system.svc.domain.suffix - internal.istio.io/parent-namespace: istio-system - internal.istio.io/parents: XListenerSet/same-name-http/http.ns2 - internal.istio.io/service-account-name: parent-gateway-istio - name: same-name-http~istio-autogenerated-k8s-gateway~http - namespace: ns2 -spec: - servers: - - hosts: - - ns2/second.example.com - port: - name: default - number: 80 - protocol: HTTP ---- -apiVersion: networking.istio.io/v1 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parents: HTTPRoute/bind-ns1.ns1 - internal.istio.io/route-semantics: gateway - name: ns1~same-name-http~istio-autogenerated-k8s-gateway~http~* - namespace: ns1 -spec: - gateways: - - ns1/same-name-http~istio-autogenerated-k8s-gateway~http - hosts: - - '*' - http: - - match: - - uri: - prefix: /ns1 - name: ns1.bind-ns1.0 - route: - - destination: - host: httpbin.ns1.svc.domain.suffix - port: - number: 80 ---- -apiVersion: networking.istio.io/v1 -kind: VirtualService -metadata: - annotations: - internal.istio.io/parents: HTTPRoute/bind-ns2.ns2 - internal.istio.io/route-semantics: gateway - name: ns2~same-name-http~istio-autogenerated-k8s-gateway~http~* - namespace: ns2 -spec: - gateways: - - ns2/same-name-http~istio-autogenerated-k8s-gateway~http - hosts: - - '*' - http: - - match: - - uri: - prefix: /ns2 - name: ns2.bind-ns2.0 - route: - - destination: - host: httpbin.ns2.svc.domain.suffix - port: - number: 80 ---- diff --git a/pilot/pkg/config/kube/gateway/testdata/listenerset.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/listenerset.status.yaml.golden index 9453e9d654..b508c94ec4 100644 --- a/pilot/pkg/config/kube/gateway/testdata/listenerset.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/listenerset.status.yaml.golden @@ -175,7 +175,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -188,7 +188,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: parent-gateway @@ -245,7 +245,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: bind-both @@ -285,7 +285,7 @@ status: parentRef: name: parent-gateway --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: bind-parent @@ -308,7 +308,7 @@ status: parentRef: name: parent-gateway --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: bind-set diff --git a/pilot/pkg/config/kube/gateway/testdata/listenerset.yaml b/pilot/pkg/config/kube/gateway/testdata/listenerset.yaml index 71fa12e239..84e3910653 100644 --- a/pilot/pkg/config/kube/gateway/testdata/listenerset.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/listenerset.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: parent-gateway @@ -88,7 +88,7 @@ spec: group: "" name: my-cert-http --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: bind-set @@ -107,7 +107,7 @@ spec: - name: httpbin port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: bind-both @@ -127,7 +127,7 @@ spec: - name: httpbin port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: bind-parent diff --git a/pilot/pkg/config/kube/gateway/testdata/mcs.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/mcs.status.yaml.golden index 885544ff75..37bb5154a1 100644 --- a/pilot/pkg/config/kube/gateway/testdata/mcs.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/mcs.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway diff --git a/pilot/pkg/config/kube/gateway/testdata/mcs.yaml b/pilot/pkg/config/kube/gateway/testdata/mcs.yaml index 3171f7a74c..268bc11824 100644 --- a/pilot/pkg/config/kube/gateway/testdata/mcs.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/mcs.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway diff --git a/pilot/pkg/config/kube/gateway/testdata/mesh.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/mesh.status.yaml.golden index 978599837a..6d1ba5e59c 100644 --- a/pilot/pkg/config/kube/gateway/testdata/mesh.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/mesh.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -11,7 +11,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -62,7 +62,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: consumer-override @@ -89,7 +89,7 @@ status: namespace: apple port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: dual @@ -129,7 +129,7 @@ status: kind: Service name: example --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: echo @@ -154,7 +154,7 @@ status: kind: Service name: echo --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: echo-port @@ -180,7 +180,7 @@ status: name: echo-port port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: header @@ -205,7 +205,7 @@ status: kind: Service name: echo --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: multi-service diff --git a/pilot/pkg/config/kube/gateway/testdata/mesh.yaml b/pilot/pkg/config/kube/gateway/testdata/mesh.yaml index 6a71b3d9dc..4a9a98891e 100644 --- a/pilot/pkg/config/kube/gateway/testdata/mesh.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/mesh.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -24,7 +24,7 @@ spec: namespaces: from: All --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: echo @@ -39,7 +39,7 @@ spec: - name: echo port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: dual # applies to mesh and explicit gateway @@ -57,7 +57,7 @@ spec: - name: example port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: header @@ -82,7 +82,7 @@ spec: - name: echo port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: echo-port @@ -98,7 +98,7 @@ spec: - name: echo port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: multi-service @@ -121,7 +121,7 @@ spec: - name: echo port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: consumer-override diff --git a/pilot/pkg/config/kube/gateway/testdata/mismatch.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/mismatch.status.yaml.golden index 23cbc37b3d..951c6cea7d 100644 --- a/pilot/pkg/config/kube/gateway/testdata/mismatch.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/mismatch.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio diff --git a/pilot/pkg/config/kube/gateway/testdata/mismatch.yaml b/pilot/pkg/config/kube/gateway/testdata/mismatch.yaml index aa63fd08e6..8ead7d1bd0 100644 --- a/pilot/pkg/config/kube/gateway/testdata/mismatch.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/mismatch.yaml @@ -1,12 +1,12 @@ # Mismatch shows that we don't generate config for Gateways that do not match the GatewayClass -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway diff --git a/pilot/pkg/config/kube/gateway/testdata/multi-gateway.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/multi-gateway.status.yaml.golden index 88490a17a8..149c887889 100644 --- a/pilot/pkg/config/kube/gateway/testdata/multi-gateway.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/multi-gateway.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -11,7 +11,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway diff --git a/pilot/pkg/config/kube/gateway/testdata/multi-gateway.yaml b/pilot/pkg/config/kube/gateway/testdata/multi-gateway.yaml index febd7140f1..599c233747 100644 --- a/pilot/pkg/config/kube/gateway/testdata/multi-gateway.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/multi-gateway.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway diff --git a/pilot/pkg/config/kube/gateway/testdata/reference-policy-inferencepool.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/reference-policy-inferencepool.status.yaml.golden index d544a434be..399373ac26 100644 --- a/pilot/pkg/config/kube/gateway/testdata/reference-policy-inferencepool.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/reference-policy-inferencepool.status.yaml.golden @@ -6,7 +6,7 @@ metadata: spec: null status: {} --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -57,7 +57,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: backend-allowed-ip @@ -81,7 +81,7 @@ status: name: gateway namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: backend-not-allowed-ip diff --git a/pilot/pkg/config/kube/gateway/testdata/reference-policy-inferencepool.yaml b/pilot/pkg/config/kube/gateway/testdata/reference-policy-inferencepool.yaml index 7d46bc1deb..96f7729d02 100644 --- a/pilot/pkg/config/kube/gateway/testdata/reference-policy-inferencepool.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/reference-policy-inferencepool.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -51,7 +51,7 @@ spec: kind: InferencePool name: my-ip --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: backend-allowed-ip @@ -69,7 +69,7 @@ spec: namespace: inferencepool port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: backend-not-allowed-ip diff --git a/pilot/pkg/config/kube/gateway/testdata/reference-policy-service.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/reference-policy-service.status.yaml.golden index c3223f7f3e..1d2bb48ba2 100644 --- a/pilot/pkg/config/kube/gateway/testdata/reference-policy-service.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/reference-policy-service.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -49,7 +49,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: backend-not-allowed @@ -74,7 +74,7 @@ status: name: gateway namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http diff --git a/pilot/pkg/config/kube/gateway/testdata/reference-policy-service.yaml b/pilot/pkg/config/kube/gateway/testdata/reference-policy-service.yaml index 992af91f5f..9959c981a2 100644 --- a/pilot/pkg/config/kube/gateway/testdata/reference-policy-service.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/reference-policy-service.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -32,7 +32,7 @@ spec: kind: Service name: my-svc --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http @@ -48,7 +48,7 @@ spec: namespace: service port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: backend-not-allowed diff --git a/pilot/pkg/config/kube/gateway/testdata/reference-policy-tcp.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/reference-policy-tcp.status.yaml.golden index ca273b6e1c..421841bf53 100644 --- a/pilot/pkg/config/kube/gateway/testdata/reference-policy-tcp.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/reference-policy-tcp.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway diff --git a/pilot/pkg/config/kube/gateway/testdata/reference-policy-tcp.yaml b/pilot/pkg/config/kube/gateway/testdata/reference-policy-tcp.yaml index 0488ccad24..cefe1782c0 100644 --- a/pilot/pkg/config/kube/gateway/testdata/reference-policy-tcp.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/reference-policy-tcp.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway diff --git a/pilot/pkg/config/kube/gateway/testdata/reference-policy-tls.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/reference-policy-tls.status.yaml.golden index d426826e4e..87fbb4e4be 100644 --- a/pilot/pkg/config/kube/gateway/testdata/reference-policy-tls.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/reference-policy-tls.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -11,7 +11,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -62,7 +62,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http diff --git a/pilot/pkg/config/kube/gateway/testdata/reference-policy-tls.yaml b/pilot/pkg/config/kube/gateway/testdata/reference-policy-tls.yaml index a8eb9b5856..9c199e77c0 100644 --- a/pilot/pkg/config/kube/gateway/testdata/reference-policy-tls.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/reference-policy-tls.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -69,7 +69,7 @@ spec: - group: "" kind: ConfigMap --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http diff --git a/pilot/pkg/config/kube/gateway/testdata/route-binding.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/route-binding.status.yaml.golden index 58c41718ce..4f91c7e40c 100644 --- a/pilot/pkg/config/kube/gateway/testdata/route-binding.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/route-binding.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -11,7 +11,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -448,7 +448,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: bind-all @@ -472,7 +472,7 @@ status: name: gateway namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: host-mismatch @@ -497,7 +497,7 @@ status: namespace: istio-system sectionName: foobar --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-bind-cross-namespace @@ -523,7 +523,7 @@ status: namespace: istio-system sectionName: slctr-labels --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: same-namespace-invalid @@ -549,7 +549,7 @@ status: namespace: istio-system sectionName: same-namespace --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: section-name-cross-namespace @@ -574,7 +574,7 @@ status: namespace: istio-system sectionName: foobar --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: bind-cross-namespace @@ -764,7 +764,7 @@ status: namespace: istio-system sectionName: slctr-combined-no --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: bind-cross-namespace @@ -789,7 +789,7 @@ status: namespace: istio-system sectionName: slctr-labels --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: same-namespace-valid diff --git a/pilot/pkg/config/kube/gateway/testdata/route-binding.yaml b/pilot/pkg/config/kube/gateway/testdata/route-binding.yaml index e65384403e..19adbc93fd 100644 --- a/pilot/pkg/config/kube/gateway/testdata/route-binding.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/route-binding.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -190,7 +190,7 @@ spec: values: - group --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: section-name-cross-namespace @@ -206,7 +206,7 @@ spec: - name: httpbin port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: same-namespace-valid @@ -224,7 +224,7 @@ spec: - name: httpbin port: 81 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: same-namespace-invalid @@ -257,7 +257,7 @@ spec: - name: httpbin port: 82 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: host-mismatch @@ -273,7 +273,7 @@ spec: - name: httpbin port: 84 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: bind-all @@ -287,7 +287,7 @@ spec: - name: httpbin port: 85 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: bind-cross-namespace @@ -332,7 +332,7 @@ spec: - name: httpbin port: 86 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: bind-cross-namespace @@ -347,7 +347,7 @@ spec: - name: httpbin port: 87 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: invalid-bind-cross-namespace diff --git a/pilot/pkg/config/kube/gateway/testdata/route-precedence.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/route-precedence.status.yaml.golden index de2cd07b81..197d1cf5cc 100644 --- a/pilot/pkg/config/kube/gateway/testdata/route-precedence.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/route-precedence.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -11,7 +11,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -62,7 +62,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http @@ -118,7 +118,7 @@ status: kind: Service name: a-example --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http @@ -158,7 +158,7 @@ status: kind: Service name: a-example --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http diff --git a/pilot/pkg/config/kube/gateway/testdata/route-precedence.yaml b/pilot/pkg/config/kube/gateway/testdata/route-precedence.yaml index 79330a91a3..c3a526e9c0 100644 --- a/pilot/pkg/config/kube/gateway/testdata/route-precedence.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/route-precedence.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -27,7 +27,7 @@ spec: matchLabels: istio.io/test-name-part: allowed --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http @@ -63,7 +63,7 @@ spec: - name: svc2 port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http @@ -118,7 +118,7 @@ spec: - name: svc2 port: 80 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http diff --git a/pilot/pkg/config/kube/gateway/testdata/serviceentry.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/serviceentry.status.yaml.golden index a529ea9070..a6c6674cae 100644 --- a/pilot/pkg/config/kube/gateway/testdata/serviceentry.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/serviceentry.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -47,7 +47,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: egress @@ -72,7 +72,7 @@ status: kind: ServiceEntry name: egress --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http diff --git a/pilot/pkg/config/kube/gateway/testdata/serviceentry.yaml b/pilot/pkg/config/kube/gateway/testdata/serviceentry.yaml index 579eca7ccc..35aa36f848 100644 --- a/pilot/pkg/config/kube/gateway/testdata/serviceentry.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/serviceentry.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -13,7 +13,7 @@ spec: namespaces: from: All --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http @@ -46,7 +46,7 @@ spec: name: tls protocol: TLS --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: egress diff --git a/pilot/pkg/config/kube/gateway/testdata/status.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/status.status.yaml.golden index 93f7247a18..6756efd478 100644 --- a/pilot/pkg/config/kube/gateway/testdata/status.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/status.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -11,7 +11,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -90,7 +90,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: existing-istio-first @@ -130,7 +130,7 @@ status: name: not-istio namespace: istio-system --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: existing-istio-last @@ -170,7 +170,7 @@ status: parentRef: name: gateway --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: stale-istio-reference @@ -198,7 +198,7 @@ status: parentRef: name: gateway --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: stale-other-reference diff --git a/pilot/pkg/config/kube/gateway/testdata/status.yaml b/pilot/pkg/config/kube/gateway/testdata/status.yaml index e11145ebe3..a78499bc55 100644 --- a/pilot/pkg/config/kube/gateway/testdata/status.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/status.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -25,7 +25,7 @@ spec: port: 80 protocol: HTTP --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: existing-istio-last @@ -65,7 +65,7 @@ status: status: "True" type: dummy --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: existing-istio-first @@ -105,7 +105,7 @@ status: status: "True" type: dummy --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: stale-istio-reference @@ -144,7 +144,7 @@ status: status: "True" type: dummy --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: stale-other-reference diff --git a/pilot/pkg/config/kube/gateway/testdata/tcp.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/tcp.status.yaml.golden index 0b230de6f1..f28b20e332 100644 --- a/pilot/pkg/config/kube/gateway/testdata/tcp.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/tcp.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -11,7 +11,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -60,7 +60,7 @@ status: - group: gateway.networking.k8s.io kind: TCPRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway2 diff --git a/pilot/pkg/config/kube/gateway/testdata/tcp.yaml b/pilot/pkg/config/kube/gateway/testdata/tcp.yaml index 373cc4e66f..7207140724 100644 --- a/pilot/pkg/config/kube/gateway/testdata/tcp.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/tcp.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -24,7 +24,7 @@ spec: from: All --- # Test we can bind to two parents -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway2 diff --git a/pilot/pkg/config/kube/gateway/testdata/tls.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/tls.status.yaml.golden index 5cccf4c17f..f562e518e7 100644 --- a/pilot/pkg/config/kube/gateway/testdata/tls.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/tls.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -11,7 +11,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -256,7 +256,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway2 @@ -305,7 +305,7 @@ status: - group: gateway.networking.k8s.io kind: TLSRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http diff --git a/pilot/pkg/config/kube/gateway/testdata/tls.yaml b/pilot/pkg/config/kube/gateway/testdata/tls.yaml index 648c5b15bf..6ab9531404 100644 --- a/pilot/pkg/config/kube/gateway/testdata/tls.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/tls.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -114,7 +114,7 @@ spec: options: gateway.istio.io/tls-terminate-mode: ISTIO_SIMPLE --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway2 @@ -134,6 +134,72 @@ spec: tls: mode: Passthrough --- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: gateway-valid-ciphers + namespace: istio-system +spec: + addresses: + - value: istio-ingressgateway + type: Hostname + gatewayClassName: istio + listeners: + - name: passthrough + port: 34000 + protocol: TLS + allowedRoutes: + namespaces: + from: All + tls: + mode: Passthrough + options: + gateway.istio.io/tls-cipher-suites: "ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-RSA-AES128-GCM-SHA256" +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: gateway-invalid-ciphers + namespace: istio-system +spec: + addresses: + - value: istio-ingressgateway + type: Hostname + gatewayClassName: istio + listeners: + - name: passthrough + port: 34000 + protocol: TLS + allowedRoutes: + namespaces: + from: All + tls: + mode: Passthrough + options: + gateway.istio.io/tls-cipher-suites: "bogus,bad" +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: gateway-mixed-ciphers + namespace: istio-system +spec: + addresses: + - value: istio-ingressgateway + type: Hostname + gatewayClassName: istio + listeners: + - name: passthrough + port: 34000 + protocol: TLS + allowedRoutes: + namespaces: + from: All + tls: + mode: Passthrough + options: + gateway.istio.io/tls-cipher-suites: "bad, ECDHE-RSA-AES128-GCM-SHA256" +--- apiVersion: gateway.networking.k8s.io/v1alpha2 kind: TLSRoute metadata: @@ -166,7 +232,7 @@ spec: - name: httpbin-foo port: 443 --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http @@ -179,4 +245,4 @@ spec: rules: - backendRefs: - name: httpbin - port: 80 \ No newline at end of file + port: 80 diff --git a/pilot/pkg/config/kube/gateway/testdata/valid-invalid-parent-ref.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/valid-invalid-parent-ref.status.yaml.golden index 66ceaf24b2..f21288892b 100644 --- a/pilot/pkg/config/kube/gateway/testdata/valid-invalid-parent-ref.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/valid-invalid-parent-ref.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -11,7 +11,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -62,7 +62,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: mixed-parent-ref-validity diff --git a/pilot/pkg/config/kube/gateway/testdata/valid-invalid-parent-ref.yaml b/pilot/pkg/config/kube/gateway/testdata/valid-invalid-parent-ref.yaml index 2e6e3dd341..2281f29f46 100644 --- a/pilot/pkg/config/kube/gateway/testdata/valid-invalid-parent-ref.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/valid-invalid-parent-ref.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -24,7 +24,7 @@ spec: namespaces: from: All --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: mixed-parent-ref-validity diff --git a/pilot/pkg/config/kube/gateway/testdata/waypoint.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/waypoint.status.yaml.golden index 44e595079c..5031ee48de 100644 --- a/pilot/pkg/config/kube/gateway/testdata/waypoint.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/waypoint.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid @@ -47,7 +47,7 @@ status: - group: gateway.networking.k8s.io kind: GRPCRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: namespace diff --git a/pilot/pkg/config/kube/gateway/testdata/waypoint.yaml b/pilot/pkg/config/kube/gateway/testdata/waypoint.yaml index 6ec6827ca7..e6dddd37a4 100644 --- a/pilot/pkg/config/kube/gateway/testdata/waypoint.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/waypoint.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: namespace @@ -10,7 +10,7 @@ spec: port: 15008 protocol: HBONE --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: invalid diff --git a/pilot/pkg/config/kube/gateway/testdata/weighted.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/weighted.status.yaml.golden index 86193f4144..f5f1275597 100644 --- a/pilot/pkg/config/kube/gateway/testdata/weighted.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/weighted.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -11,7 +11,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -89,7 +89,7 @@ status: - group: gateway.networking.k8s.io kind: TCPRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http diff --git a/pilot/pkg/config/kube/gateway/testdata/weighted.yaml b/pilot/pkg/config/kube/gateway/testdata/weighted.yaml index 42ec6fe9e3..62d8f32fc7 100644 --- a/pilot/pkg/config/kube/gateway/testdata/weighted.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/weighted.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -30,7 +30,7 @@ spec: namespaces: from: All --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http diff --git a/pilot/pkg/config/kube/gateway/testdata/zero.status.yaml.golden b/pilot/pkg/config/kube/gateway/testdata/zero.status.yaml.golden index 98e8fb395e..b8a889baa1 100644 --- a/pilot/pkg/config/kube/gateway/testdata/zero.status.yaml.golden +++ b/pilot/pkg/config/kube/gateway/testdata/zero.status.yaml.golden @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio @@ -11,7 +11,7 @@ status: status: "True" type: Accepted --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -89,7 +89,7 @@ status: - group: gateway.networking.k8s.io kind: TCPRoute --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http diff --git a/pilot/pkg/config/kube/gateway/testdata/zero.yaml b/pilot/pkg/config/kube/gateway/testdata/zero.yaml index 505bf11175..fdc981b69d 100644 --- a/pilot/pkg/config/kube/gateway/testdata/zero.yaml +++ b/pilot/pkg/config/kube/gateway/testdata/zero.yaml @@ -1,11 +1,11 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: gateway @@ -30,7 +30,7 @@ spec: namespaces: from: All --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: http diff --git a/pilot/pkg/config/kube/ingress/controller.go b/pilot/pkg/config/kube/ingress/controller.go index e2e765e3e7..942a90e694 100644 --- a/pilot/pkg/config/kube/ingress/controller.go +++ b/pilot/pkg/config/kube/ingress/controller.go @@ -37,6 +37,7 @@ import ( "istio.io/istio/pkg/kube/kclient" "istio.io/istio/pkg/kube/krt" "istio.io/istio/pkg/log" + "istio.io/istio/pkg/revisions" "istio.io/istio/pkg/util/sets" ) @@ -93,7 +94,8 @@ type Controller struct { // outputs contains all the output collections for this controller. outputs Outputs - status *status.StatusCollections + status *status.StatusCollections + tagWatcher krt.RecomputeProtected[revisions.TagWatcher] } type Inputs struct { @@ -119,12 +121,17 @@ func NewController( stop := make(chan struct{}) opts := krt.NewOptionsBuilder(stop, "ingress", options.KrtDebugger) + tw := revisions.NewTagWatcher(client, options.Revision, options.SystemNamespace) c := &Controller{ client: client, stop: stop, status: &status.StatusCollections{}, xdsUpdater: xdsUpdater, + tagWatcher: krt.NewRecomputeProtected(tw, false, opts.WithName("tagWatcher")...), } + tw.AddHandler(func(s sets.String) { + c.tagWatcher.TriggerRecomputation() + }) c.inputs = Inputs{ IngressClasses: krt.NewInformer[*knetworking.IngressClass](client, opts.WithName("informer/IngressClasses")...), @@ -140,13 +147,14 @@ func NewController( }), opts.WithName("informer/Services")..., ), - Nodes: krt.NewInformerFiltered[*corev1.Node](client, kclient.Filter{ + Nodes: krt.NewFilteredInformer[*corev1.Node](client, kclient.Filter{ ObjectFilter: client.ObjectFilter(), ObjectTransform: kube.StripNodeUnusedFields, }, opts.WithName("informer/Nodes")...), - Pods: krt.NewInformerFiltered[*corev1.Pod](client, kclient.Filter{ + Pods: krt.NewFilteredInformer[*corev1.Pod](client, kclient.Filter{ ObjectFilter: client.ObjectFilter(), ObjectTransform: kube.StripPodUnusedFields, + FieldSelector: "status.phase!=Failed", }, opts.WithName("informer/Pods")...), MeshConfig: meshConfig.AsCollection(), } @@ -167,7 +175,7 @@ func NewController( ) status.RegisterStatus(c.status, Status, func(ingress *knetworking.Ingress) knetworking.IngressStatus { return ingress.Status - }) + }, c.tagWatcher.AccessUnprotected()) _, RuleHostIndex := RuleCollection( SupportedIngresses, @@ -225,6 +233,14 @@ func (c *Controller) SetStatusWrite(enabled bool, statusManager *status.Manager) func (c *Controller) Run(stop <-chan struct{}) { log.Infof("Starting ingress controller") + + tw := c.tagWatcher.AccessUnprotected() + go tw.Run(stop) + go func() { + kube.WaitForCacheSync("ingress tag watcher", stop, tw.HasSynced) + c.tagWatcher.MarkSynced() + }() + <-stop close(c.stop) } diff --git a/pilot/pkg/config/kube/ingress/status_test.go b/pilot/pkg/config/kube/ingress/status_test.go index aa4475f5aa..cd61821b38 100644 --- a/pilot/pkg/config/kube/ingress/status_test.go +++ b/pilot/pkg/config/kube/ingress/status_test.go @@ -256,13 +256,14 @@ func makeTestInformers(t *testing.T, name string) informers { }), opts.WithName("informer/Services")..., ) - nodes := krt.NewInformerFiltered[*corev1.Node](client, kclient.Filter{ + nodes := krt.NewFilteredInformer[*corev1.Node](client, kclient.Filter{ ObjectFilter: client.ObjectFilter(), ObjectTransform: kube.StripNodeUnusedFields, }, opts.WithName("informer/Nodes")...) - pods := krt.NewInformerFiltered[*corev1.Pod](client, kclient.Filter{ + pods := krt.NewFilteredInformer[*corev1.Pod](client, kclient.Filter{ ObjectFilter: client.ObjectFilter(), ObjectTransform: kube.StripPodUnusedFields, + FieldSelector: "status.phase!=Failed", }, opts.WithName("informer/Pods")...) inf := informers{ mesh: meshHolder, diff --git a/pilot/pkg/controllers/untaint/nodeuntainter.go b/pilot/pkg/controllers/untaint/nodeuntainter.go index d81248098c..93c114d84c 100644 --- a/pilot/pkg/controllers/untaint/nodeuntainter.go +++ b/pilot/pkg/controllers/untaint/nodeuntainter.go @@ -68,6 +68,7 @@ func NewNodeUntainter(stop <-chan struct{}, kubeClient kubelib.Client, cniNs, sy podsClient := kclient.NewFiltered[*v1.Pod](kubeClient, kclient.Filter{ ObjectFilter: kubetypes.NewStaticObjectFilter(filterNamespace(ns)), ObjectTransform: kubelib.StripPodUnusedFields, + FieldSelector: "status.phase!=Failed", }) nodes := kclient.NewFiltered[*v1.Node](kubeClient, kclient.Filter{ObjectTransform: kubelib.StripNodeUnusedFields}) nt := &NodeUntainter{ diff --git a/pilot/pkg/model/jwks_resolver_test.go b/pilot/pkg/model/jwks_resolver_test.go index d7f41153f7..3eb5328c52 100644 --- a/pilot/pkg/model/jwks_resolver_test.go +++ b/pilot/pkg/model/jwks_resolver_test.go @@ -15,7 +15,6 @@ package model import ( - "context" "fmt" "sync/atomic" "testing" @@ -656,8 +655,7 @@ func TestJwtPubKeyRefreshedWhenErrorsGettingOtherURLs(t *testing.T) { } // Start a goroutine that requests an invalid URL repeatedly - goroutineCtx, cancel := context.WithCancel(context.Background()) - defer cancel() + goroutineCtx := t.Context() go func() { for { select { diff --git a/pilot/pkg/model/kstatus/helper_test.go b/pilot/pkg/model/kstatus/helper_test.go index 8ce293fd44..07959415b0 100644 --- a/pilot/pkg/model/kstatus/helper_test.go +++ b/pilot/pkg/model/kstatus/helper_test.go @@ -20,7 +20,7 @@ import ( "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - k8s "sigs.k8s.io/gateway-api/apis/v1beta1" + k8s "sigs.k8s.io/gateway-api/apis/v1" ) func TestUpdateConditionIfChanged(t *testing.T) { diff --git a/pilot/pkg/model/push_context.go b/pilot/pkg/model/push_context.go index ccaacc698e..efbe035b37 100644 --- a/pilot/pkg/model/push_context.go +++ b/pilot/pkg/model/push_context.go @@ -2526,6 +2526,25 @@ func (ps *PushContext) BestEffortInferServiceMTLSMode(tp *networking.TrafficPoli // ServiceEndpointsByPort returns the cached instances by port if it exists. func (ps *PushContext) ServiceEndpointsByPort(svc *Service, port int, labels labels.Instance) []*IstioEndpoint { var out []*IstioEndpoint + + // For InferencePool services, return ALL endpoints regardless of port + // because they may have different target ports but belong to the same cluster + if svc.UseInferenceSemantics() { + allPorts := ps.ServiceIndex.instancesByPort[svc.Key()] + for _, instances := range allPorts { + if len(labels) == 0 { + out = append(out, instances...) + continue + } + for _, instance := range instances { + if labels.SubsetOf(instance.Labels) { + out = append(out, instance) + } + } + } + return out + } + if instances, exists := ps.ServiceIndex.instancesByPort[svc.Key()][port]; exists { // Use cached version of instances by port when labels are empty. if len(labels) == 0 { diff --git a/pilot/pkg/model/telemetry_logging.go b/pilot/pkg/model/telemetry_logging.go index c305c76d61..cb79459db1 100644 --- a/pilot/pkg/model/telemetry_logging.go +++ b/pilot/pkg/model/telemetry_logging.go @@ -23,8 +23,6 @@ import ( fileaccesslog "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3" grpcaccesslog "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/grpc/v3" otelaccesslog "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/open_telemetry/v3" - celformatter "github.com/envoyproxy/go-control-plane/envoy/extensions/formatter/cel/v3" - metadataformatter "github.com/envoyproxy/go-control-plane/envoy/extensions/formatter/metadata/v3" reqwithoutquery "github.com/envoyproxy/go-control-plane/envoy/extensions/formatter/req_without_query/v3" otlpcommon "go.opentelemetry.io/proto/otlp/common/v1" "google.golang.org/protobuf/types/known/structpb" @@ -119,20 +117,6 @@ var ( Name: "envoy.formatter.req_without_query", TypedConfig: protoconv.MessageToAny(&reqwithoutquery.ReqWithoutQuery{}), } - - // metadataFormatter configures additional formatters needed for some of the format strings like "METADATA" - // for more information, see https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/formatter/metadata/v3/metadata.proto - metadataFormatter = &core.TypedExtensionConfig{ - Name: "envoy.formatter.metadata", - TypedConfig: protoconv.MessageToAny(&metadataformatter.Metadata{}), - } - - // celFormatter configures additional formatters needed for some of the format strings like "CEL" - // for more information, see https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/formatter/cel/v3/cel.proto - celFormatter = &core.TypedExtensionConfig{ - Name: "envoy.formatter.cel", - TypedConfig: protoconv.MessageToAny(&celformatter.Cel{}), - } ) // configureFromProviderConfigHandled contains the number of providers we handle below. @@ -141,27 +125,22 @@ var ( const telemetryAccessLogHandled = 15 func telemetryAccessLog(push *PushContext, proxy *Proxy, fp *meshconfig.MeshConfig_ExtensionProvider) *accesslog.AccessLog { - // Skip built-in formatter if Istio version is >= 1.26 - // This is because if we send CEL/METADATA in the access log, - // envoy will report lots of warn message endlessly. - skipBuiltInFormatter := proxy.IstioVersion != nil && proxy.VersionGreaterOrEqual(&IstioVersion{Major: 1, Minor: 26}) - var al *accesslog.AccessLog switch prov := fp.Provider.(type) { case *meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLog: // For built-in provider, fallback to MeshConfig for formatting options when LogFormat unset. if fp.Name == builtinEnvoyAccessLogProvider && prov.EnvoyFileAccessLog.LogFormat == nil && !prov.EnvoyFileAccessLog.OmitEmptyValues { - al = FileAccessLogFromMeshConfig(prov.EnvoyFileAccessLog.Path, push.Mesh, skipBuiltInFormatter) + al = FileAccessLogFromMeshConfig(prov.EnvoyFileAccessLog.Path, push.Mesh) } else { - al = fileAccessLogFromTelemetry(prov.EnvoyFileAccessLog, skipBuiltInFormatter) + al = fileAccessLogFromTelemetry(prov.EnvoyFileAccessLog) } case *meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpAls: al = httpGrpcAccessLogFromTelemetry(push, proxy, prov.EnvoyHttpAls) case *meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpAls: al = tcpGrpcAccessLogFromTelemetry(push, proxy, prov.EnvoyTcpAls) case *meshconfig.MeshConfig_ExtensionProvider_EnvoyOtelAls: - al = openTelemetryLog(push, proxy, prov.EnvoyOtelAls, skipBuiltInFormatter) + al = openTelemetryLog(push, proxy, prov.EnvoyOtelAls) case *meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzHttp, *meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzGrpc, *meshconfig.MeshConfig_ExtensionProvider_Zipkin, @@ -231,7 +210,7 @@ func tcpGrpcAccessLogFromTelemetry(push *PushContext, proxy *Proxy, } } -func fileAccessLogFromTelemetry(prov *meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider, skipBuiltInFormatter bool) *accesslog.AccessLog { +func fileAccessLogFromTelemetry(prov *meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider) *accesslog.AccessLog { p := prov.Path if p == "" { p = DevStdout @@ -244,12 +223,12 @@ func fileAccessLogFromTelemetry(prov *meshconfig.MeshConfig_ExtensionProvider_En if prov.LogFormat != nil { switch logFormat := prov.LogFormat.LogFormat.(type) { case *meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider_LogFormat_Text: - fl.AccessLogFormat, needsFormatter = buildFileAccessTextLogFormat(logFormat.Text, prov.OmitEmptyValues, skipBuiltInFormatter) + fl.AccessLogFormat, needsFormatter = buildFileAccessTextLogFormat(logFormat.Text, prov.OmitEmptyValues) case *meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider_LogFormat_Labels: - fl.AccessLogFormat, needsFormatter = buildFileAccessJSONLogFormat(logFormat, prov.OmitEmptyValues, skipBuiltInFormatter) + fl.AccessLogFormat, needsFormatter = buildFileAccessJSONLogFormat(logFormat, prov.OmitEmptyValues) } } else { - fl.AccessLogFormat, needsFormatter = buildFileAccessTextLogFormat("", prov.OmitEmptyValues, skipBuiltInFormatter) + fl.AccessLogFormat, needsFormatter = buildFileAccessTextLogFormat("", prov.OmitEmptyValues) } if len(needsFormatter) != 0 { fl.GetLogFormat().Formatters = needsFormatter @@ -264,10 +243,10 @@ func fileAccessLogFromTelemetry(prov *meshconfig.MeshConfig_ExtensionProvider_En } func buildFileAccessTextLogFormat( - logFormatText string, omitEmptyValues bool, skipBuiltInFormatter bool, + logFormatText string, omitEmptyValues bool, ) (*fileaccesslog.FileAccessLog_LogFormat, []*core.TypedExtensionConfig) { formatString := fileAccessLogFormat(logFormatText) - formatters := accessLogTextFormatters(formatString, skipBuiltInFormatter) + formatters := accessLogTextFormatters(formatString) return &fileaccesslog.FileAccessLog_LogFormat{ LogFormat: &core.SubstitutionFormatString{ @@ -285,7 +264,7 @@ func buildFileAccessTextLogFormat( func buildFileAccessJSONLogFormat( logFormat *meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider_LogFormat_Labels, - omitEmptyValues bool, skipBuiltInFormatter bool, + omitEmptyValues bool, ) (*fileaccesslog.FileAccessLog_LogFormat, []*core.TypedExtensionConfig) { jsonLogStruct := EnvoyJSONLogFormatIstio if logFormat.Labels != nil { @@ -297,7 +276,7 @@ func buildFileAccessJSONLogFormat( jsonLogStruct = EnvoyJSONLogFormatIstio } - formatters := accessLogJSONFormatters(jsonLogStruct, skipBuiltInFormatter) + formatters := accessLogJSONFormatters(jsonLogStruct) return &fileaccesslog.FileAccessLog_LogFormat{ LogFormat: &core.SubstitutionFormatString{ Format: &core.SubstitutionFormatString_JsonFormat{ @@ -309,50 +288,31 @@ func buildFileAccessJSONLogFormat( }, formatters } -func accessLogJSONFormatters(jsonLogStruct *structpb.Struct, skipBuiltInFormatter bool) []*core.TypedExtensionConfig { +func accessLogJSONFormatters(jsonLogStruct *structpb.Struct) []*core.TypedExtensionConfig { if jsonLogStruct == nil { return nil } - reqWithoutQuery, metadata, cel := false, false, false + reqWithoutQuery := false for _, value := range jsonLogStruct.Fields { if !reqWithoutQuery && strings.Contains(value.GetStringValue(), reqWithoutQueryCommandOperator) { reqWithoutQuery = true } - if !skipBuiltInFormatter && !metadata && strings.Contains(value.GetStringValue(), metadataCommandOperator) { - metadata = true - } - if !skipBuiltInFormatter && !cel && strings.Contains(value.GetStringValue(), celCommandOperator) { - cel = true - } } formatters := make([]*core.TypedExtensionConfig, 0, maxFormattersLength) if reqWithoutQuery { formatters = append(formatters, reqWithoutQueryFormatter) } - if metadata { - formatters = append(formatters, metadataFormatter) - } - if cel { - formatters = append(formatters, celFormatter) - } return formatters } -func accessLogTextFormatters(text string, skipBuiltInFormatter bool) []*core.TypedExtensionConfig { +func accessLogTextFormatters(text string) []*core.TypedExtensionConfig { formatters := make([]*core.TypedExtensionConfig, 0, maxFormattersLength) if strings.Contains(text, reqWithoutQueryCommandOperator) { formatters = append(formatters, reqWithoutQueryFormatter) } - // Start from Istio 1.26+(envoy 1.34), the formatter ``%CEL%`` and ``%METADATA%`` will be treated as built-in formatters - if !skipBuiltInFormatter && strings.Contains(text, metadataCommandOperator) { - formatters = append(formatters, metadataFormatter) - } - if !skipBuiltInFormatter && strings.Contains(text, celCommandOperator) { - formatters = append(formatters, celFormatter) - } return formatters } @@ -415,7 +375,7 @@ func fileAccessLogFormat(formatString string) string { return EnvoyTextLogFormat } -func FileAccessLogFromMeshConfig(path string, mesh *meshconfig.MeshConfig, skipBuiltInFormatter bool) *accesslog.AccessLog { +func FileAccessLogFromMeshConfig(path string, mesh *meshconfig.MeshConfig) *accesslog.AccessLog { // We need to build access log. This is needed either on first access or when mesh config changes. fl := &fileaccesslog.FileAccessLog{ Path: path, @@ -424,7 +384,7 @@ func FileAccessLogFromMeshConfig(path string, mesh *meshconfig.MeshConfig, skipB switch mesh.AccessLogEncoding { case meshconfig.MeshConfig_TEXT: formatString := fileAccessLogFormat(mesh.AccessLogFormat) - formatters = accessLogTextFormatters(formatString, skipBuiltInFormatter) + formatters = accessLogTextFormatters(formatString) fl.AccessLogFormat = &fileaccesslog.FileAccessLog_LogFormat{ LogFormat: &core.SubstitutionFormatString{ Format: &core.SubstitutionFormatString_TextFormatSource{ @@ -446,7 +406,7 @@ func FileAccessLogFromMeshConfig(path string, mesh *meshconfig.MeshConfig, skipB jsonLogStruct = &parsedJSONLogStruct } } - formatters = accessLogJSONFormatters(jsonLogStruct, skipBuiltInFormatter) + formatters = accessLogJSONFormatters(jsonLogStruct) fl.AccessLogFormat = &fileaccesslog.FileAccessLog_LogFormat{ LogFormat: &core.SubstitutionFormatString{ Format: &core.SubstitutionFormatString_JsonFormat{ @@ -471,7 +431,7 @@ func FileAccessLogFromMeshConfig(path string, mesh *meshconfig.MeshConfig, skipB } func openTelemetryLog(pushCtx *PushContext, proxy *Proxy, - provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyOpenTelemetryLogProvider, skipBuiltInFormatter bool, + provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyOpenTelemetryLogProvider, ) *accesslog.AccessLog { hostname, cluster, err := clusterLookupFn(pushCtx, provider.Service, int(provider.Port)) if err != nil { @@ -495,7 +455,7 @@ func openTelemetryLog(pushCtx *PushContext, proxy *Proxy, labels = provider.LogFormat.Labels } - cfg := buildOpenTelemetryAccessLogConfig(proxy, logName, hostname, cluster, f, labels, skipBuiltInFormatter) + cfg := buildOpenTelemetryAccessLogConfig(proxy, logName, hostname, cluster, f, labels) return &accesslog.AccessLog{ Name: OtelEnvoyALSName, @@ -504,7 +464,7 @@ func openTelemetryLog(pushCtx *PushContext, proxy *Proxy, } func buildOpenTelemetryAccessLogConfig(proxy *Proxy, - logName, hostname, clusterName, format string, labels *structpb.Struct, skipBuiltInFormatter bool, + logName, hostname, clusterName, format string, labels *structpb.Struct, ) *otelaccesslog.OpenTelemetryAccessLogConfig { cfg := &otelaccesslog.OpenTelemetryAccessLogConfig{ CommonConfig: &grpcaccesslog.CommonGrpcAccessLogConfig{ @@ -537,12 +497,12 @@ func buildOpenTelemetryAccessLogConfig(proxy *Proxy, } } - cfg.Formatters = accessLogFormatters(format, labels, skipBuiltInFormatter) + cfg.Formatters = accessLogFormatters(format, labels) return cfg } -func accessLogFormatters(text string, labels *structpb.Struct, skipBuiltInFormatter bool) []*core.TypedExtensionConfig { +func accessLogFormatters(text string, labels *structpb.Struct) []*core.TypedExtensionConfig { formatters := make([]*core.TypedExtensionConfig, 0, maxFormattersLength) defer func() { slices.SortBy(formatters, func(f *core.TypedExtensionConfig) string { @@ -550,7 +510,7 @@ func accessLogFormatters(text string, labels *structpb.Struct, skipBuiltInFormat }) }() - formatters = append(formatters, accessLogTextFormatters(text, skipBuiltInFormatter)...) + formatters = append(formatters, accessLogTextFormatters(text)...) if len(formatters) >= maxFormattersLength { // all formatters are added, return if we have reached the limit return formatters @@ -561,7 +521,7 @@ func accessLogFormatters(text string, labels *structpb.Struct, skipBuiltInFormat names.Insert(f.Name) } - for _, f := range accessLogJSONFormatters(labels, skipBuiltInFormatter) { + for _, f := range accessLogJSONFormatters(labels) { if !names.Contains(f.Name) { formatters = append(formatters, f) names.Insert(f.Name) diff --git a/pilot/pkg/model/telemetry_logging_test.go b/pilot/pkg/model/telemetry_logging_test.go index 8fd9493fc0..b9333a0a4f 100644 --- a/pilot/pkg/model/telemetry_logging_test.go +++ b/pilot/pkg/model/telemetry_logging_test.go @@ -978,15 +978,13 @@ func TestBuildOpenTelemetryAccessLogConfig(t *testing.T) { fakeCluster := "outbound|55680||otel-collector.monitoring.svc.cluster.local" fakeAuthority := "otel-collector.monitoring.svc.cluster.local" for _, tc := range []struct { - name string - logName string - clusterName string - hostname string - body string - labels *structpb.Struct - skipBuiltInFormatter bool - expected *otelaccesslog.OpenTelemetryAccessLogConfig - proxyVersion *IstioVersion + name string + logName string + clusterName string + hostname string + body string + labels *structpb.Struct + expected *otelaccesslog.OpenTelemetryAccessLogConfig }{ { name: "default", @@ -1028,56 +1026,6 @@ func TestBuildOpenTelemetryAccessLogConfig(t *testing.T) { "host": {Kind: &structpb.Value_StringValue{StringValue: "%CEL(request.host)%"}}, }, }, - expected: &otelaccesslog.OpenTelemetryAccessLogConfig{ - CommonConfig: &grpcaccesslog.CommonGrpcAccessLogConfig{ - LogName: OtelEnvoyAccessLogFriendlyName, - GrpcService: &core.GrpcService{ - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ - ClusterName: fakeCluster, - Authority: fakeAuthority, - }, - }, - }, - TransportApiVersion: core.ApiVersion_V3, - FilterStateObjectsToLog: envoyWasmStateToLog, - }, - DisableBuiltinLabels: true, - Body: &otlpcommon.AnyValue{ - Value: &otlpcommon.AnyValue_StringValue{ - StringValue: EnvoyTextLogFormat, - }, - }, - Formatters: []*core.TypedExtensionConfig{ - celFormatter, - }, - Attributes: &otlpcommon.KeyValueList{ - Values: []*otlpcommon.KeyValue{ - { - Key: "host", - Value: &otlpcommon.AnyValue{Value: &otlpcommon.AnyValue_StringValue{StringValue: "%CEL(request.host)%"}}, - }, - { - Key: "protocol", - Value: &otlpcommon.AnyValue{Value: &otlpcommon.AnyValue_StringValue{StringValue: "%PROTOCOL%"}}, - }, - }, - }, - }, - }, - { - name: "skip builtin", - logName: OtelEnvoyAccessLogFriendlyName, - clusterName: fakeCluster, - hostname: fakeAuthority, - body: EnvoyTextLogFormat, - labels: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "protocol": {Kind: &structpb.Value_StringValue{StringValue: "%PROTOCOL%"}}, - "host": {Kind: &structpb.Value_StringValue{StringValue: "%CEL(request.host)%"}}, - }, - }, - skipBuiltInFormatter: true, expected: &otelaccesslog.OpenTelemetryAccessLogConfig{ CommonConfig: &grpcaccesslog.CommonGrpcAccessLogConfig{ LogName: OtelEnvoyAccessLogFriendlyName, @@ -1115,7 +1063,7 @@ func TestBuildOpenTelemetryAccessLogConfig(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { got := buildOpenTelemetryAccessLogConfig(sidecar, tc.logName, tc.hostname, - tc.clusterName, tc.body, tc.labels, tc.skipBuiltInFormatter) + tc.clusterName, tc.body, tc.labels) assert.Equal(t, tc.expected, got) }) } @@ -1347,8 +1295,6 @@ func TestTelemetryAccessLog(t *testing.T) { }, }, Formatters: []*core.TypedExtensionConfig{ - celFormatter, - metadataFormatter, reqWithoutQueryFormatter, }, } @@ -1759,10 +1705,9 @@ func TestTelemetryAccessLog(t *testing.T) { func TestAccessLogJSONFormatters(t *testing.T) { cases := []struct { - name string - json *structpb.Struct - skipBuiltInFormatter bool - expected []*core.TypedExtensionConfig + name string + json *structpb.Struct + expected []*core.TypedExtensionConfig }{ { name: "default", @@ -1787,9 +1732,7 @@ func TestAccessLogJSONFormatters(t *testing.T) { "key1": {Kind: &structpb.Value_StringValue{StringValue: "%METADATA(CLUSTER:istio)%"}}, }, }, - expected: []*core.TypedExtensionConfig{ - metadataFormatter, - }, + expected: []*core.TypedExtensionConfig{}, }, { name: "with-both", @@ -1801,7 +1744,6 @@ func TestAccessLogJSONFormatters(t *testing.T) { }, expected: []*core.TypedExtensionConfig{ reqWithoutQueryFormatter, - metadataFormatter, }, }, { @@ -1812,9 +1754,7 @@ func TestAccessLogJSONFormatters(t *testing.T) { "key2": {Kind: &structpb.Value_StringValue{StringValue: "%METADATA(UPSTREAM_HOST:istio)%"}}, }, }, - expected: []*core.TypedExtensionConfig{ - metadataFormatter, - }, + expected: []*core.TypedExtensionConfig{}, }, { name: "more-complex", @@ -1828,7 +1768,6 @@ func TestAccessLogJSONFormatters(t *testing.T) { }, expected: []*core.TypedExtensionConfig{ reqWithoutQueryFormatter, - metadataFormatter, }, }, { @@ -1838,31 +1777,13 @@ func TestAccessLogJSONFormatters(t *testing.T) { "req1": {Kind: &structpb.Value_StringValue{StringValue: "%CEL(request.host)%"}}, }, }, - expected: []*core.TypedExtensionConfig{ - celFormatter, - }, - }, - { - name: "skip builtin", - json: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "req1": {Kind: &structpb.Value_StringValue{StringValue: "%REQ_WITHOUT_QUERY(key1:val1)%"}}, - "req2": {Kind: &structpb.Value_StringValue{StringValue: "%REQ_WITHOUT_QUERY(key2:val1)%"}}, - "key1": {Kind: &structpb.Value_StringValue{StringValue: "%METADATA(CLUSTER:istio)%"}}, - "key2": {Kind: &structpb.Value_StringValue{StringValue: "%METADATA(UPSTREAM_HOST:istio)%"}}, - "host": {Kind: &structpb.Value_StringValue{StringValue: "%CEL(request.host)%"}}, - }, - }, - skipBuiltInFormatter: true, - expected: []*core.TypedExtensionConfig{ - reqWithoutQueryFormatter, - }, + expected: []*core.TypedExtensionConfig{}, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - got := accessLogJSONFormatters(tc.json, tc.skipBuiltInFormatter) + got := accessLogJSONFormatters(tc.json) assert.Equal(t, tc.expected, got) }) } @@ -1870,10 +1791,9 @@ func TestAccessLogJSONFormatters(t *testing.T) { func TestAccessLogTextFormatters(t *testing.T) { cases := []struct { - name string - text string - skipBuiltInFormatter bool - expected []*core.TypedExtensionConfig + name string + text string + expected []*core.TypedExtensionConfig }{ { name: "default", @@ -1888,40 +1808,25 @@ func TestAccessLogTextFormatters(t *testing.T) { }, }, { - name: "with-metadata", - text: EnvoyTextLogFormat + " %METADATA(CLUSTER:istio)%", - expected: []*core.TypedExtensionConfig{ - metadataFormatter, - }, + name: "with-metadata", + text: EnvoyTextLogFormat + " %METADATA(CLUSTER:istio)%", + expected: []*core.TypedExtensionConfig{}, }, { name: "with-both", text: EnvoyTextLogFormat + " %REQ_WITHOUT_QUERY(key1:val1)% %METADATA(CLUSTER:istio)%", expected: []*core.TypedExtensionConfig{ reqWithoutQueryFormatter, - metadataFormatter, }, }, { - name: "with-multi-metadata", - text: EnvoyTextLogFormat + " %METADATA(UPSTREAM_HOST:istio)% %METADATA(CLUSTER:istio)%", - expected: []*core.TypedExtensionConfig{ - metadataFormatter, - }, + name: "with-multi-metadata", + text: EnvoyTextLogFormat + " %METADATA(UPSTREAM_HOST:istio)% %METADATA(CLUSTER:istio)%", + expected: []*core.TypedExtensionConfig{}, }, { name: "more-complex", text: EnvoyTextLogFormat + " %REQ_WITHOUT_QUERY(key1:val1)% REQ_WITHOUT_QUERY(key2:val1)% %METADATA(UPSTREAM_HOST:istio)% %METADATA(CLUSTER:istio)%", - expected: []*core.TypedExtensionConfig{ - reqWithoutQueryFormatter, - metadataFormatter, - }, - }, - { - name: "skip-built-in", - // nolint: lll - text: EnvoyTextLogFormat + "%CEL(request.host)% %REQ_WITHOUT_QUERY(key1:val1)% REQ_WITHOUT_QUERY(key2:val1)% %METADATA(UPSTREAM_HOST:istio)% %METADATA(CLUSTER:istio)%", - skipBuiltInFormatter: true, expected: []*core.TypedExtensionConfig{ reqWithoutQueryFormatter, }, @@ -1930,7 +1835,7 @@ func TestAccessLogTextFormatters(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - got := accessLogTextFormatters(tc.text, tc.skipBuiltInFormatter) + got := accessLogTextFormatters(tc.text) assert.Equal(t, tc.expected, got) }) } @@ -2018,11 +1923,10 @@ func TestTelemetryAccessLogWithFormatter(t *testing.T) { func TestAccessLogFormatters(t *testing.T) { cases := []struct { - name string - text string - labels *structpb.Struct - skipBuiltInFormatter bool - expected []*core.TypedExtensionConfig + name string + text string + labels *structpb.Struct + expected []*core.TypedExtensionConfig }{ { name: "default", @@ -2049,9 +1953,7 @@ func TestAccessLogFormatters(t *testing.T) { "key1": {Kind: &structpb.Value_StringValue{StringValue: "%METADATA(CLUSTER:istio)%"}}, }, }, - expected: []*core.TypedExtensionConfig{ - metadataFormatter, - }, + expected: []*core.TypedExtensionConfig{}, }, { name: "with-both", @@ -2063,7 +1965,6 @@ func TestAccessLogFormatters(t *testing.T) { }, }, expected: []*core.TypedExtensionConfig{ - metadataFormatter, reqWithoutQueryFormatter, }, }, @@ -2077,23 +1978,6 @@ func TestAccessLogFormatters(t *testing.T) { "key3": {Kind: &structpb.Value_StringValue{StringValue: "%CEL(request.host)%"}}, }, }, - expected: []*core.TypedExtensionConfig{ - celFormatter, - metadataFormatter, - reqWithoutQueryFormatter, - }, - }, - { - name: "skip builtin", - text: EnvoyTextLogFormat + " %REQ_WITHOUT_QUERY(key1:val1)% REQ_WITHOUT_QUERY(key2:val1)% %METADATA(UPSTREAM_HOST:istio)% %METADATA(CLUSTER:istio)%", - labels: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "key1": {Kind: &structpb.Value_StringValue{StringValue: "%METADATA(CLUSTER:istio)%"}}, - "key2": {Kind: &structpb.Value_StringValue{StringValue: "%REQ_WITHOUT_QUERY(key1:val1)%"}}, - "key3": {Kind: &structpb.Value_StringValue{StringValue: "%CEL(request.host)%"}}, - }, - }, - skipBuiltInFormatter: true, expected: []*core.TypedExtensionConfig{ reqWithoutQueryFormatter, }, @@ -2102,7 +1986,7 @@ func TestAccessLogFormatters(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - got := accessLogFormatters(tc.text, tc.labels, tc.skipBuiltInFormatter) + got := accessLogFormatters(tc.text, tc.labels) assert.Equal(t, tc.expected, got) }) } diff --git a/pilot/pkg/model/telemetry_test.go b/pilot/pkg/model/telemetry_test.go index c2e4f40ddd..227adc04c6 100644 --- a/pilot/pkg/model/telemetry_test.go +++ b/pilot/pkg/model/telemetry_test.go @@ -116,7 +116,6 @@ var ( LogFormat: &core.SubstitutionFormatString{ Formatters: []*core.TypedExtensionConfig{ reqWithoutQueryFormatter, - metadataFormatter, }, Format: &core.SubstitutionFormatString_JsonFormat{ JsonFormat: &structpb.Struct{ @@ -139,7 +138,6 @@ var ( LogFormat: &core.SubstitutionFormatString{ Formatters: []*core.TypedExtensionConfig{ reqWithoutQueryFormatter, - metadataFormatter, }, Format: &core.SubstitutionFormatString_TextFormatSource{ TextFormatSource: &core.DataSource{ diff --git a/pilot/pkg/networking/core/accesslog.go b/pilot/pkg/networking/core/accesslog.go index e1b295b89f..c6f37664ca 100644 --- a/pilot/pkg/networking/core/accesslog.go +++ b/pilot/pkg/networking/core/accesslog.go @@ -98,7 +98,7 @@ func (b *AccessLogBuilder) setTCPAccessLogWithFilter( if len(cfgs) == 0 { // No Telemetry API configured, fall back to legacy mesh config setting if mesh.AccessLogFile != "" { - tcp.AccessLog = append(tcp.AccessLog, b.coreAccessLog.buildOrFetch(mesh, proxy)) + tcp.AccessLog = append(tcp.AccessLog, b.coreAccessLog.buildOrFetch(mesh)) } if mesh.EnableEnvoyAccessLogService { @@ -124,7 +124,7 @@ func (b *AccessLogBuilder) setHboneOriginationAccessLog(push *model.PushContext, if len(cfgs) == 0 { // No Telemetry API configured, fall back to legacy mesh config setting if mesh.AccessLogFile != "" { - tcp.AccessLog = append(tcp.AccessLog, b.hboneOriginationAccessLog.buildOrFetch(mesh, proxy)) + tcp.AccessLog = append(tcp.AccessLog, b.hboneOriginationAccessLog.buildOrFetch(mesh)) } return } @@ -142,7 +142,7 @@ func (b *AccessLogBuilder) setHboneTerminationAccessLog(push *model.PushContext, if len(cfgs) == 0 { // No Telemetry API configured, fall back to legacy mesh config setting if mesh.AccessLogFile != "" { - connectionManager.AccessLog = append(connectionManager.AccessLog, b.hboneTerminationAccessLog.buildOrFetch(mesh, proxy)) + connectionManager.AccessLog = append(connectionManager.AccessLog, b.hboneTerminationAccessLog.buildOrFetch(mesh)) } return } @@ -205,7 +205,7 @@ func (b *AccessLogBuilder) setHTTPAccessLog(push *model.PushContext, proxy *mode if len(cfgs) == 0 { // No Telemetry API configured, fall back to legacy mesh config setting if mesh.AccessLogFile != "" { - connectionManager.AccessLog = append(connectionManager.AccessLog, b.coreAccessLog.buildOrFetch(mesh, proxy)) + connectionManager.AccessLog = append(connectionManager.AccessLog, b.coreAccessLog.buildOrFetch(mesh)) } if mesh.EnableEnvoyAccessLogService { @@ -232,7 +232,7 @@ func (b *AccessLogBuilder) setListenerAccessLog(push *model.PushContext, proxy * if len(cfgs) == 0 { // No Telemetry API configured, fall back to legacy mesh config setting if mesh.AccessLogFile != "" { - listener.AccessLog = append(listener.AccessLog, b.listenerAccessLog.buildOrFetch(mesh, proxy)) + listener.AccessLog = append(listener.AccessLog, b.listenerAccessLog.buildOrFetch(mesh)) } if mesh.EnableEnvoyAccessLogService { @@ -303,36 +303,32 @@ func buildAccessLogFilter(f ...*accesslog.AccessLogFilter) *accesslog.AccessLogF } func newCachedMeshConfigAccessLog(filter *accesslog.AccessLogFilter) cachedMeshConfigAccessLog { - return cachedMeshConfigAccessLog{filter: filter, cached: make(map[bool]*accesslog.AccessLog)} + return cachedMeshConfigAccessLog{filter: filter} } type cachedMeshConfigAccessLog struct { filter *accesslog.AccessLogFilter mutex sync.RWMutex - cached map[bool]*accesslog.AccessLog + cached *accesslog.AccessLog } -func (b *cachedMeshConfigAccessLog) getCached(skipBuiltInFormatter bool) *accesslog.AccessLog { +func (b *cachedMeshConfigAccessLog) getCached() *accesslog.AccessLog { b.mutex.RLock() defer b.mutex.RUnlock() - return b.cached[skipBuiltInFormatter] + return b.cached } -func (b *cachedMeshConfigAccessLog) buildOrFetch(mesh *meshconfig.MeshConfig, proxy *model.Proxy) *accesslog.AccessLog { - // Skip built-in formatter if Istio version is >= 1.26 - // This is because if we send CEL/METADATA in the access log, - // envoy will report lots of warn message endlessly. - skipBuiltInFormatter := proxy.IstioVersion != nil && proxy.VersionGreaterOrEqual(&model.IstioVersion{Major: 1, Minor: 26}) - if c := b.getCached(skipBuiltInFormatter); c != nil { +func (b *cachedMeshConfigAccessLog) buildOrFetch(mesh *meshconfig.MeshConfig) *accesslog.AccessLog { + if c := b.getCached(); c != nil { return c } // We need to build access log. This is needed either on first access or when mesh config changes. - accessLog := model.FileAccessLogFromMeshConfig(mesh.AccessLogFile, mesh, skipBuiltInFormatter) + accessLog := model.FileAccessLogFromMeshConfig(mesh.AccessLogFile, mesh) accessLog.Filter = b.filter b.mutex.Lock() defer b.mutex.Unlock() - b.cached[skipBuiltInFormatter] = accessLog + b.cached = accessLog return accessLog } @@ -340,7 +336,7 @@ func (b *cachedMeshConfigAccessLog) buildOrFetch(mesh *meshconfig.MeshConfig, pr func (b *cachedMeshConfigAccessLog) reset() { b.mutex.Lock() defer b.mutex.Unlock() - b.cached = make(map[bool]*accesslog.AccessLog) + b.cached = nil } func tcpGrpcAccessLog(isListener bool) *accesslog.AccessLog { diff --git a/pilot/pkg/networking/core/cluster.go b/pilot/pkg/networking/core/cluster.go index 8197fb29b4..aeaa67312f 100644 --- a/pilot/pkg/networking/core/cluster.go +++ b/pilot/pkg/networking/core/cluster.go @@ -315,10 +315,15 @@ func (configgen *ConfigGeneratorImpl) buildOutboundClusters(cb *ClusterBuilder, if service.Resolution == model.Alias || service.Resolution == model.DynamicDNS { continue } - for _, port := range service.Ports { + for i, port := range service.Ports { if port.Protocol == protocol.UDP { continue } + // For InferencePool services, only build cluster for the first port + // All endpoints from all ports are merged into this single cluster + if service.UseInferenceSemantics() && i > 0 { + continue + } clusterKey := buildClusterKey(service, port, cb, proxy, efKeys) cached, allFound := cb.getAllCachedSubsetClusters(clusterKey) if allFound && !features.EnableUnsafeAssertions { diff --git a/pilot/pkg/networking/core/cluster_cache.go b/pilot/pkg/networking/core/cluster_cache.go index 17d7e71da3..19be895ff4 100644 --- a/pilot/pkg/networking/core/cluster_cache.go +++ b/pilot/pkg/networking/core/cluster_cache.go @@ -42,7 +42,7 @@ type clusterCache struct { locality *core.Locality // identifies the locality the cluster is generated for preserveHTTP1HeaderCase bool // indicates whether the original case of HTTP/1.x headers should be preserved proxyClusterID string // identifies the kubernetes cluster a proxy is in - proxySidecar bool // identifies if this proxy is a Sidecar + proxyType model.NodeType // identifies this proxy type hbone bool proxyView model.ProxyView metadataCerts *metadataCerts // metadata certificates of proxy @@ -77,7 +77,7 @@ func (t *clusterCache) Key() any { h.Write(Separator) h.WriteString(t.proxyClusterID) h.Write(Separator) - h.WriteString(strconv.FormatBool(t.proxySidecar)) + h.WriteString(string(t.proxyType)) h.Write(Separator) h.WriteString(strconv.FormatBool(t.http2)) h.Write(Separator) @@ -197,7 +197,7 @@ func buildClusterKey(service *model.Service, port *model.Port, cb *ClusterBuilde locality: cb.locality, preserveHTTP1HeaderCase: shouldPreserveHeaderCase(cb.proxyMetadata, cb.req.Push), proxyClusterID: cb.clusterID, - proxySidecar: cb.sidecarProxy(), + proxyType: cb.proxyType, proxyView: cb.proxyView, hbone: cb.sendHbone, http2: port.Protocol.IsHTTP2(), diff --git a/pilot/pkg/networking/core/cluster_test.go b/pilot/pkg/networking/core/cluster_test.go index bb938d67d0..26dbbd2ea9 100644 --- a/pilot/pkg/networking/core/cluster_test.go +++ b/pilot/pkg/networking/core/cluster_test.go @@ -329,6 +329,18 @@ func TestBuildClustersForInferencePoolServices(t *testing.T) { proxyType: model.SidecarProxy, InferencePoolService: true, }, + { + testName: "InferencePool service creates single cluster for multiple ports", + clusterName: "outbound|8080||*.example.org", + proxyType: model.Router, + InferencePoolService: true, + }, + { + testName: "Regular service creates one cluster per port", + clusterName: "outbound|8080||*.example.org", + proxyType: model.Router, + InferencePoolService: false, + }, } for _, tc := range cases { t.Run(tc.testName, func(t *testing.T) { @@ -362,6 +374,16 @@ func TestBuildClustersForInferencePoolServices(t *testing.T) { g.Expect(overrideHostPolicy.GetOverrideHostSources()).NotTo(BeEmpty()) g.Expect(overrideHostPolicy.GetOverrideHostSources()[0].GetMetadata().GetKey()).To(Equal("envoy.lb")) g.Expect(overrideHostPolicy.GetOverrideHostSources()[0].GetMetadata().GetPath()[0].GetKey()).To(Equal("x-gateway-destination-endpoint")) + var serviceClusters []string + for _, c := range clusters { + if strings.Contains(c.Name, "*.example.org") && strings.HasPrefix(c.Name, "outbound|") { + serviceClusters = append(serviceClusters, c.Name) + } + } + g.Expect(len(serviceClusters)).To(Equal(1), + "expected single cluster but got %d: %v", len(serviceClusters), serviceClusters) + g.Expect(serviceClusters[0]).To(ContainSubstring("|8080|"), + "expected cluster to use first port 8080 but got %s", serviceClusters[0]) } }) } diff --git a/pilot/pkg/networking/core/listener_waypoint.go b/pilot/pkg/networking/core/listener_waypoint.go index 86b1b9fe88..f718990474 100644 --- a/pilot/pkg/networking/core/listener_waypoint.go +++ b/pilot/pkg/networking/core/listener_waypoint.go @@ -576,46 +576,54 @@ func (lb *ListenerBuilder) buildWaypointInternal(wls []model.WorkloadInfo, svcs return nil }() - l := &listener.Listener{ - Name: MainInternalName, - ListenerSpecifier: &listener.Listener_InternalListener{InternalListener: &listener.Listener_InternalListenerConfig{}}, - ListenerFilters: []*listener.ListenerFilter{ - xdsfilters.OriginalDestination, - httpInspector, - }, - TrafficDirection: core.TrafficDirection_INBOUND, - FilterChains: chains, - FilterChainMatcher: &matcher.Matcher{ - MatcherType: &matcher.Matcher_MatcherTree_{ - MatcherTree: &matcher.Matcher_MatcherTree{ - Input: match.DestinationIP, - TreeType: &matcher.Matcher_MatcherTree_CustomMatch{ - CustomMatch: &xds.TypedExtensionConfig{ - Name: "ip", - TypedConfig: protoconv.MessageToAny(ipMatcher), - }, + // by default match IPs first + primaryMatcher := &matcher.Matcher{ + MatcherType: &matcher.Matcher_MatcherTree_{ + MatcherTree: &matcher.Matcher_MatcherTree{ + Input: match.DestinationIP, + TreeType: &matcher.Matcher_MatcherTree_CustomMatch{ + CustomMatch: &xds.TypedExtensionConfig{ + Name: "ip", + TypedConfig: protoconv.MessageToAny(ipMatcher), }, }, }, }, } + // when multi-network is enabled, prefer the hostnames for matching + // VIPs may overlap across networks, especially auto-generated ServiceEntry VIPs + // and if we hit an incorrect VIP match we may attempt to route to the wrong service if len(svcHostnameMap.Map) > 0 && features.EnableAmbientMultiNetwork { - l.FilterChainMatcher.OnNoMatch = &matcher.Matcher_OnMatch{ - OnMatch: &matcher.Matcher_OnMatch_Matcher{ - Matcher: &matcher.Matcher{ - MatcherType: &matcher.Matcher_MatcherTree_{ - MatcherTree: &matcher.Matcher_MatcherTree{ - Input: match.AuthorityFilterStateInput, - TreeType: &matcher.Matcher_MatcherTree_ExactMatchMap{ - ExactMatchMap: svcHostnameMap, - }, - }, + primaryMatcher = &matcher.Matcher{ + OnNoMatch: &matcher.Matcher_OnMatch{ + OnMatch: &matcher.Matcher_OnMatch_Matcher{ + Matcher: primaryMatcher, + }, + }, + MatcherType: &matcher.Matcher_MatcherTree_{ + MatcherTree: &matcher.Matcher_MatcherTree{ + Input: match.AuthorityFilterStateInput, + TreeType: &matcher.Matcher_MatcherTree_ExactMatchMap{ + ExactMatchMap: svcHostnameMap, }, }, }, } } + + l := &listener.Listener{ + Name: MainInternalName, + ListenerSpecifier: &listener.Listener_InternalListener{InternalListener: &listener.Listener_InternalListenerConfig{}}, + ListenerFilters: []*listener.ListenerFilter{ + xdsfilters.OriginalDestination, + httpInspector, + }, + TrafficDirection: core.TrafficDirection_INBOUND, + FilterChains: chains, + FilterChainMatcher: primaryMatcher, + } + if tlsInspector != nil { l.ListenerFilters = append(l.ListenerFilters, tlsInspector) } diff --git a/pilot/pkg/networking/core/route/retry/retry.go b/pilot/pkg/networking/core/route/retry/retry.go index c017c5736a..eb47c1c1c1 100644 --- a/pilot/pkg/networking/core/route/retry/retry.go +++ b/pilot/pkg/networking/core/route/retry/retry.go @@ -30,56 +30,76 @@ import ( var defaultRetryPriorityTypedConfig = protoconv.MessageToAny(buildPreviousPrioritiesConfig()) -// DefaultPolicy gets a copy of the default retry policy. -func DefaultPolicy() *route.RetryPolicy { - policy := defaultPolicy() - policy.RetryHostPredicate = []*route.RetryPolicy_RetryHostPredicate{ - // to configure retries to prefer hosts that haven’t been attempted already, - // the builtin `envoy.retry_host_predicates.previous_hosts` predicate can be used. +// Cached immutable default values to avoid repeated allocations. +// These are shared and must not be modified. +var ( + defaultNumRetries = &wrappers.UInt32Value{Value: 2} + defaultRetryOn = "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes" + + // Shared slice for RetryHostPredicate - immutable, safe to share + defaultRetryHostPredicate = []*route.RetryPolicy_RetryHostPredicate{ xdsfilters.RetryPreviousHosts, } - return policy -} -func defaultPolicy() *route.RetryPolicy { - return &route.RetryPolicy{ - NumRetries: &wrappers.UInt32Value{Value: 2}, - RetryOn: "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes", - // TODO: allow this to be configured via API. + // Cached default policy with RetryHostPredicate (for non-consistent-hash cases) + cachedDefaultPolicy = &route.RetryPolicy{ + NumRetries: defaultNumRetries, + RetryOn: defaultRetryOn, HostSelectionRetryMaxAttempts: 5, + RetryHostPredicate: defaultRetryHostPredicate, + } + + // Cached default policy without RetryHostPredicate (for consistent-hash cases) + cachedDefaultConsistentHashPolicy = &route.RetryPolicy{ + NumRetries: defaultNumRetries, + RetryOn: defaultRetryOn, + HostSelectionRetryMaxAttempts: 5, + } + + cachedRetryRemoteLocalitiesPredicate = &route.RetryPolicy_RetryPriority{ + Name: "envoy.retry_priorities.previous_priorities", + ConfigType: &route.RetryPolicy_RetryPriority_TypedConfig{ + TypedConfig: defaultRetryPriorityTypedConfig, + }, } +) + +// DefaultPolicy returns the default retry policy. +// The returned policy is a shared immutable instance and must not be modified. +// If modification is needed, use newRetryPolicy() instead. +func DefaultPolicy() *route.RetryPolicy { + return cachedDefaultPolicy } -// DefaultConsistentHashPolicy gets a copy of the default retry policy without previous host predicate. +// DefaultConsistentHashPolicy returns the default retry policy without previous host predicate. // When Consistent Hashing is enabled, we don't want to use other hosts during retries. +// The returned policy is a shared immutable instance and must not be modified. func DefaultConsistentHashPolicy() *route.RetryPolicy { - return defaultPolicy() + return cachedDefaultConsistentHashPolicy +} + +// newRetryPolicy creates a new mutable retry policy with default values. +// Use this when the policy needs to be customized. +func newRetryPolicy(hashPolicy bool) *route.RetryPolicy { + policy := &route.RetryPolicy{ + NumRetries: &wrappers.UInt32Value{Value: 2}, + RetryOn: defaultRetryOn, + HostSelectionRetryMaxAttempts: 5, + } + if !hashPolicy { + policy.RetryHostPredicate = defaultRetryHostPredicate + } + return policy } // ConvertPolicy converts the given Istio retry policy to an Envoy policy. -// -// If in is nil, DefaultPolicy is returned. -// -// If in.Attempts == 0, returns nil. -// -// Otherwise, the returned policy is DefaultPolicy with the following overrides: -// -// - NumRetries: set from in.Attempts -// -// - RetryOn, RetriableStatusCodes: set from in.RetryOn (if specified). RetriableStatusCodes -// is appended when encountering parts that are valid HTTP status codes. -// -// - PerTryTimeout: set from in.PerTryTimeout (if specified) func ConvertPolicy(in *networking.HTTPRetry, hashPolicy bool) *route.RetryPolicy { - var out *route.RetryPolicy - if hashPolicy { - out = DefaultConsistentHashPolicy() - } else { - out = DefaultPolicy() - } + // Fast path: if no custom config, return cached immutable default if in == nil { - // No policy was set, use a default. - return out + if hashPolicy { + return cachedDefaultConsistentHashPolicy + } + return cachedDefaultPolicy } if in.Attempts <= 0 { @@ -87,7 +107,10 @@ func ConvertPolicy(in *networking.HTTPRetry, hashPolicy bool) *route.RetryPolicy return nil } - // A policy was specified. Start with the default and override with user-provided fields where appropriate. + // A policy was specified - we need a mutable copy to customize + out := newRetryPolicy(hashPolicy) + + // Override with user-provided fields out.NumRetries = &wrappers.UInt32Value{Value: uint32(in.Attempts)} if in.RetryOn != "" { @@ -108,22 +131,13 @@ func ConvertPolicy(in *networking.HTTPRetry, hashPolicy bool) *route.RetryPolicy if in.RetryIgnorePreviousHosts != nil { var retryHostPredicate []*route.RetryPolicy_RetryHostPredicate if in.RetryIgnorePreviousHosts.GetValue() { - retryHostPredicate = []*route.RetryPolicy_RetryHostPredicate{ - // to configure retries to prefer hosts that haven’t been attempted already, - // the builtin `envoy.retry_host_predicates.previous_hosts` predicate can be used. - xdsfilters.RetryPreviousHosts, - } + retryHostPredicate = defaultRetryHostPredicate } out.RetryHostPredicate = retryHostPredicate } if in.RetryRemoteLocalities != nil && in.RetryRemoteLocalities.GetValue() { - out.RetryPriority = &route.RetryPolicy_RetryPriority{ - Name: "envoy.retry_priorities.previous_priorities", - ConfigType: &route.RetryPolicy_RetryPriority_TypedConfig{ - TypedConfig: defaultRetryPriorityTypedConfig, - }, - } + out.RetryPriority = cachedRetryRemoteLocalitiesPredicate } if in.Backoff != nil { diff --git a/pilot/pkg/networking/core/sidecar_simulation_test.go b/pilot/pkg/networking/core/sidecar_simulation_test.go index 702bd40342..fe2c914da3 100644 --- a/pilot/pkg/networking/core/sidecar_simulation_test.go +++ b/pilot/pkg/networking/core/sidecar_simulation_test.go @@ -1803,7 +1803,7 @@ func (args vsArgs) Config(t *testing.T, variant string) string { } switch variant { case "httproute": - return tmpl.MustEvaluate(`apiVersion: gateway.networking.k8s.io/v1beta1 + return tmpl.MustEvaluate(`apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: "{{.Namespace}}{{.Match | replace "*" "wild"}}{{.Dest}}" diff --git a/pilot/pkg/networking/core/tracing.go b/pilot/pkg/networking/core/tracing.go index 0df1785c2b..136418812a 100644 --- a/pilot/pkg/networking/core/tracing.go +++ b/pilot/pkg/networking/core/tracing.go @@ -18,6 +18,7 @@ import ( "fmt" "net/url" "sort" + "time" core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" tracingcfg "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3" @@ -27,6 +28,7 @@ import ( tracing "github.com/envoyproxy/go-control-plane/envoy/type/tracing/v3" xdstype "github.com/envoyproxy/go-control-plane/envoy/type/v3" "google.golang.org/protobuf/types/known/anypb" + "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/wrapperspb" meshconfig "istio.io/api/mesh/v1alpha1" @@ -177,7 +179,8 @@ func configureFromProviderConfig(pushCtx *model.PushContext, proxy *model.Proxy, return nil, fmt.Errorf("could not find cluster for tracing provider %q: %v", provider, err) } traceContextOption := convertTraceContextOption(provider.Zipkin.GetTraceContextOption()) - return zipkinConfig(hostname, cluster, provider.Zipkin.GetPath(), !provider.Zipkin.GetEnable_64BitTraceId(), traceContextOption, proxy) + return zipkinConfig(hostname, cluster, provider.Zipkin.GetPath(), !provider.Zipkin.GetEnable_64BitTraceId(), + traceContextOption, provider.Zipkin.GetTimeout(), provider.Zipkin.GetHeaders(), proxy) } case *meshconfig.MeshConfig_ExtensionProvider_Datadog: maxTagLength = provider.Datadog.GetMaxTagLength() @@ -247,20 +250,60 @@ func convertTraceContextOption( func zipkinConfig( hostname, cluster, endpoint string, enable128BitTraceID bool, traceContextOption tracingcfg.ZipkinConfig_TraceContextOption, - proxy *model.Proxy, + timeout *durationpb.Duration, headers []*meshconfig.MeshConfig_ExtensionProvider_HttpHeader, proxy *model.Proxy, ) (*anypb.Any, error) { if endpoint == "" { endpoint = "/api/v2/spans" // envoy deprecated v1 support } zc := &tracingcfg.ZipkinConfig{ - CollectorCluster: cluster, - CollectorEndpoint: endpoint, CollectorEndpointVersion: tracingcfg.ZipkinConfig_HTTP_JSON, // use v2 JSON for now - CollectorHostname: hostname, // http host header TraceId_128Bit: enable128BitTraceID, // istio default enable 128 bit trace id SharedSpanContext: wrapperspb.Bool(false), } + // Determine if we should use HttpService (modern approach) or legacy fields + // For newer proxies (Istio 1.29+), always use HttpService as it's the modern approach + // that supports timeout and custom headers. For older proxies, use legacy fields for + // backward compatibility. + proxyVersionGreaterOrEqual129 := proxy.IstioVersion != nil && proxy.VersionGreaterOrEqual(&model.IstioVersion{Major: 1, Minor: 29}) + + if proxyVersionGreaterOrEqual129 { + // Modern configuration using HttpService + // This is required for timeout and custom headers support + + // Set default timeout if not provided, as Envoy requires a timeout to be set. + if timeout == nil { + timeout = durationpb.New(5 * time.Second) + } + + httpService := &core.HttpService{ + HttpUri: &core.HttpUri{ + Uri: fmt.Sprintf("http://%s%s", hostname, endpoint), + HttpUpstreamType: &core.HttpUri_Cluster{ + Cluster: cluster, + }, + Timeout: timeout, + }, + } + // Add custom headers if provided + if len(headers) > 0 { + httpService.RequestHeadersToAdd = buildHTTPHeaders(headers) + } + zc.CollectorService = httpService + } else { + // Legacy configuration - maintains backward compatibility for older proxies + zc.CollectorCluster = cluster + zc.CollectorEndpoint = endpoint + zc.CollectorHostname = hostname // http host header + + useHTTPService := timeout != nil || len(headers) > 0 + // Log when timeout/headers configuration is ignored due to older proxy version + if useHTTPService { + log.Warnf("Proxy %s (version %v) does not support Zipkin timeout/headers configuration: requires Istio 1.29+. "+ + "Configuration will be ignored.", proxy.ID, proxy.IstioVersion) + } + } + // Only set TraceContextOption for proxies that support it to avoid NACK from older proxies // TraceContextOption support requires a recent Envoy version - using conservative version gating if proxy.IstioVersion != nil && proxy.VersionGreaterOrEqual(&model.IstioVersion{Major: 1, Minor: 28}) { @@ -622,7 +665,7 @@ func configureCustomTags(spec *model.TracingSpec, hcmTracing *hcm.HttpConnection if len(providerTags) == 0 { tags = append(tags, buildCustomTagsFromProxyConfig(proxyCfg.GetTracing().GetCustomTags())...) } else { - tags = append(tags, buildCustomTagsFromProvider(providerTags)...) + tags = append(tags, buildCustomTagsFromProvider(node, providerTags)...) } // looping over customTags, a map, results in the returned value @@ -635,48 +678,57 @@ func configureCustomTags(spec *model.TracingSpec, hcmTracing *hcm.HttpConnection hcmTracing.CustomTags = tags } -func buildCustomTagsFromProvider(providerTags map[string]*telemetrypb.Tracing_CustomTag) []*tracing.CustomTag { +func buildCustomTagsFromProvider(node *model.Proxy, providerTags map[string]*telemetrypb.Tracing_CustomTag) []*tracing.CustomTag { var tags []*tracing.CustomTag + + supportFormatterTag := node.VersionGreaterOrEqual(&model.IstioVersion{Major: 1, Minor: 29, Patch: 0}) + hasFormatterTag := false + for tagName, tagInfo := range providerTags { if tagInfo == nil { log.Warnf("while building custom tags from provider, encountered nil custom tag: %s, skipping", tagName) continue } + t := &tracing.CustomTag{ + Tag: tagName, + } switch tag := tagInfo.Type.(type) { case *telemetrypb.Tracing_CustomTag_Environment: - env := &tracing.CustomTag{ - Tag: tagName, - Type: &tracing.CustomTag_Environment_{ - Environment: &tracing.CustomTag_Environment{ - Name: tag.Environment.Name, - DefaultValue: tag.Environment.DefaultValue, - }, + t.Type = &tracing.CustomTag_Environment_{ + Environment: &tracing.CustomTag_Environment{ + Name: tag.Environment.Name, + DefaultValue: tag.Environment.DefaultValue, }, } - tags = append(tags, env) case *telemetrypb.Tracing_CustomTag_Header: - header := &tracing.CustomTag{ - Tag: tagName, - Type: &tracing.CustomTag_RequestHeader{ - RequestHeader: &tracing.CustomTag_Header{ - Name: tag.Header.Name, - DefaultValue: tag.Header.DefaultValue, - }, + t.Type = &tracing.CustomTag_RequestHeader{ + RequestHeader: &tracing.CustomTag_Header{ + Name: tag.Header.Name, + DefaultValue: tag.Header.DefaultValue, }, } - tags = append(tags, header) case *telemetrypb.Tracing_CustomTag_Literal: - env := &tracing.CustomTag{ - Tag: tagName, - Type: &tracing.CustomTag_Literal_{ - Literal: &tracing.CustomTag_Literal{ - Value: tag.Literal.Value, - }, + t.Type = &tracing.CustomTag_Literal_{ + Literal: &tracing.CustomTag_Literal{ + Value: tag.Literal.Value, }, } - tags = append(tags, env) + case *telemetrypb.Tracing_CustomTag_Formatter: + hasFormatterTag = true + if !supportFormatterTag { + continue + } + t.Type = &tracing.CustomTag_Value{ + Value: tag.Formatter.Value, + } } + tags = append(tags, t) } + + if !supportFormatterTag && hasFormatterTag { + log.Debug("Formatter custom tag only support for Istio 1.29+") + } + return tags } diff --git a/pilot/pkg/networking/core/tracing_test.go b/pilot/pkg/networking/core/tracing_test.go index bd6cd760c1..da4329d49f 100644 --- a/pilot/pkg/networking/core/tracing_test.go +++ b/pilot/pkg/networking/core/tracing_test.go @@ -16,6 +16,7 @@ package core import ( "testing" + "time" core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" tracingcfg "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3" @@ -227,7 +228,7 @@ func TestConfigureTracing(t *testing.T) { { name: "basic config (with opentelemetry provider via grpc with initial metadata)", inSpec: fakeTracingSpec(fakeOpenTelemetryGrpcWithInitialMetadata(), 99.999, false, true, true), - opts: fakeOptsOnlyOpenTelemetryGrpcWithInitialMetadataTelemetryAPI(), + opts: fakeOptsOnlyOpenTelemetryGrpcWithInitialMetadataTelemetryAPI(28), want: fakeTracingConfig(fakeOpenTelemetryGrpcWithInitialMetadataProvider(clusterName, authority), 99.999, 256, append(defaultTracingTags(), fakeEnvTag)), wantReqIDExtCtx: &defaultUUIDExtensionCtx, @@ -257,6 +258,14 @@ func TestConfigureTracing(t *testing.T) { 99.999, 256, append(defaultTracingTags(), fakeEnvTag)), wantReqIDExtCtx: &defaultUUIDExtensionCtx, }, + { + name: "with formatter tag", + inSpec: fakeTracingSpec(fakeOpenTelemetryGrpcWithInitialMetadata(), 99.999, false, true, true), + opts: fakeOptsOnlyOpenTelemetryGrpcWithInitialMetadataTelemetryAPI(29), + want: fakeTracingConfig(fakeOpenTelemetryGrpcWithInitialMetadataProvider(clusterName, authority), + 99.999, 256, append(defaultTracingTags(), fakeEnvTag, fakeFormatterTag)), + wantReqIDExtCtx: &defaultUUIDExtensionCtx, + }, } for _, tc := range testcases { @@ -615,7 +624,8 @@ func fakeOptsWithDefaultProviders() gatewayListenerOpts { }, } opts.proxy = &model.Proxy{ - Metadata: &model.NodeMetadata{}, + IstioVersion: &model.IstioVersion{Major: 1, Minor: 28, Patch: 0}, + Metadata: &model.NodeMetadata{}, } return opts @@ -646,6 +656,7 @@ func fakeOptsNoTelemetryAPI() gatewayListenerOpts { }, }, }, + IstioVersion: &model.IstioVersion{Major: 1, Minor: 28, Patch: 0}, } return opts @@ -659,6 +670,7 @@ func fakeOptsNoTelemetryAPIWithNilCustomTag() gatewayListenerOpts { }, } opts.proxy = &model.Proxy{ + IstioVersion: &model.IstioVersion{Major: 1, Minor: 28, Patch: 0}, Metadata: &model.NodeMetadata{ ProxyConfig: &model.NodeMetaProxyConfig{ Tracing: &meshconfig.Tracing{ @@ -697,6 +709,7 @@ func fakeOptsOnlyZipkinTelemetryAPI() gatewayListenerOpts { Metadata: &model.NodeMetadata{ ProxyConfig: &model.NodeMetaProxyConfig{}, }, + IstioVersion: &model.IstioVersion{Major: 1, Minor: 28, Patch: 0}, } return opts @@ -722,6 +735,7 @@ func fakeOptsZipkinTelemetryWithEndpoint() gatewayListenerOpts { }, } opts.proxy = &model.Proxy{ + IstioVersion: &model.IstioVersion{Major: 1, Minor: 28, Patch: 0}, Metadata: &model.NodeMetadata{ ProxyConfig: &model.NodeMetaProxyConfig{}, }, @@ -871,6 +885,7 @@ func fakeOptsOnlyDatadogTelemetryAPI() gatewayListenerOpts { XdsNode: &core.Node{ Cluster: "fake-cluster", }, + IstioVersion: &model.IstioVersion{Major: 1, Minor: 28, Patch: 0}, } return opts @@ -913,6 +928,7 @@ func fakeOptsMeshAndTelemetryAPI(enableTracing bool) gatewayListenerOpts { }, }, }, + IstioVersion: &model.IstioVersion{Major: 1, Minor: 28, Patch: 0}, } return opts @@ -948,6 +964,7 @@ func fakeOptsOnlySkywalkingTelemetryAPI() gatewayListenerOpts { }, } opts.proxy = &model.Proxy{ + IstioVersion: &model.IstioVersion{Major: 1, Minor: 28, Patch: 0}, Metadata: &model.NodeMetadata{ ProxyConfig: &model.NodeMetaProxyConfig{}, }, @@ -1048,6 +1065,7 @@ func fakeOptsOnlyOpenTelemetryGrpcTelemetryAPI() gatewayListenerOpts { }, } opts.proxy = &model.Proxy{ + IstioVersion: &model.IstioVersion{Major: 1, Minor: 28, Patch: 0}, Metadata: &model.NodeMetadata{ ProxyConfig: &model.NodeMetaProxyConfig{}, }, @@ -1056,7 +1074,7 @@ func fakeOptsOnlyOpenTelemetryGrpcTelemetryAPI() gatewayListenerOpts { return opts } -func fakeOptsOnlyOpenTelemetryGrpcWithInitialMetadataTelemetryAPI() gatewayListenerOpts { +func fakeOptsOnlyOpenTelemetryGrpcWithInitialMetadataTelemetryAPI(minor int) gatewayListenerOpts { var opts gatewayListenerOpts opts.push = &model.PushContext{ Mesh: &meshconfig.MeshConfig{ @@ -1086,6 +1104,7 @@ func fakeOptsOnlyOpenTelemetryGrpcWithInitialMetadataTelemetryAPI() gatewayListe }, } opts.proxy = &model.Proxy{ + IstioVersion: &model.IstioVersion{Major: 1, Minor: minor, Patch: 0}, Metadata: &model.NodeMetadata{ ProxyConfig: &model.NodeMetaProxyConfig{}, }, @@ -1125,6 +1144,7 @@ func fakeOptsOnlyOpenTelemetryHTTPTelemetryAPI() gatewayListenerOpts { }, } opts.proxy = &model.Proxy{ + IstioVersion: &model.IstioVersion{Major: 1, Minor: 28, Patch: 0}, Metadata: &model.NodeMetadata{ ProxyConfig: &model.NodeMetaProxyConfig{}, }, @@ -1210,6 +1230,13 @@ func tracingSpec(provider *meshconfig.MeshConfig_ExtensionProvider, sampling flo }, }, }, + "test-formatter": { + Type: &tpb.Tracing_CustomTag_Formatter{ + Formatter: &tpb.Tracing_Formatter{ + Value: "%REQ(fake-header)%", + }, + }, + }, }, UseRequestIDForTraceSampling: useRequestIDForTraceSampling, EnableIstioTags: enableIstioTags, @@ -1277,14 +1304,22 @@ func fakeTracingConfigForSkywalking(provider *tracingcfg.Tracing_Http, randomSam return cfg } -var fakeEnvTag = &tracing.CustomTag{ - Tag: "test", - Type: &tracing.CustomTag_Environment_{ - Environment: &tracing.CustomTag_Environment{ - Name: "FOO", +var ( + fakeEnvTag = &tracing.CustomTag{ + Tag: "test", + Type: &tracing.CustomTag_Environment_{ + Environment: &tracing.CustomTag_Environment{ + Name: "FOO", + }, }, - }, -} + } + fakeFormatterTag = &tracing.CustomTag{ + Tag: "test-formatter", + Type: &tracing.CustomTag_Value{ + Value: "%REQ(fake-header)%", + }, + } +) func fakeZipkinProvider(expectClusterName, expectAuthority, expectEndpoint string, enableTraceID bool) *tracingcfg.Tracing_Http { return fakeZipkinProviderWithTraceContext(expectClusterName, expectAuthority, expectEndpoint, enableTraceID, tracingcfg.ZipkinConfig_USE_B3) @@ -1550,7 +1585,7 @@ func TestZipkinConfig(t *testing.T) { proxy := &model.Proxy{ IstioVersion: &model.IstioVersion{Major: 1, Minor: 28, Patch: 0}, } - result, err := zipkinConfig(tc.hostname, tc.cluster, tc.endpoint, tc.enable128BitTraceID, tc.traceContextOption, proxy) + result, err := zipkinConfig(tc.hostname, tc.cluster, tc.endpoint, tc.enable128BitTraceID, tc.traceContextOption, nil, nil, proxy) assert.NoError(t, err) // Unmarshal the Any proto to compare the actual ZipkinConfig @@ -1624,6 +1659,8 @@ func TestZipkinConfigVersionGating(t *testing.T) { "/api/v2/spans", true, tc.traceContextOption, + nil, + nil, proxy, ) assert.NoError(t, err) @@ -1651,6 +1688,298 @@ func TestZipkinConfigVersionGating(t *testing.T) { } } +// TestZipkinConfigWithTimeoutAndHeaders tests Zipkin configuration with timeout and custom headers +func TestZipkinConfigWithTimeoutAndHeaders(t *testing.T) { + testcases := []struct { + name string + hostname string + cluster string + endpoint string + timeout *durationpb.Duration + headers []*meshconfig.MeshConfig_ExtensionProvider_HttpHeader + expectedConfig *tracingcfg.ZipkinConfig + }{ + { + name: "zipkin with timeout uses HttpService", + hostname: "zipkin.istio-system.svc.cluster.local", + cluster: "zipkin-cluster", + endpoint: "/api/v2/spans", + timeout: durationpb.New(10 * time.Second), // 10s + headers: nil, + expectedConfig: &tracingcfg.ZipkinConfig{ + CollectorEndpointVersion: tracingcfg.ZipkinConfig_HTTP_JSON, + TraceId_128Bit: true, + SharedSpanContext: wrapperspb.Bool(false), + TraceContextOption: tracingcfg.ZipkinConfig_USE_B3, + CollectorService: &core.HttpService{ + HttpUri: &core.HttpUri{ + Uri: "http://zipkin.istio-system.svc.cluster.local/api/v2/spans", + HttpUpstreamType: &core.HttpUri_Cluster{ + Cluster: "zipkin-cluster", + }, + Timeout: durationpb.New(10 * time.Second), + }, + }, + }, + }, + { + name: "zipkin with custom headers uses HttpService", + hostname: "zipkin.istio-system.svc.cluster.local", + cluster: "zipkin-cluster", + endpoint: "/api/v2/spans", + timeout: nil, + headers: []*meshconfig.MeshConfig_ExtensionProvider_HttpHeader{ + { + Name: "Authorization", + HeaderValue: &meshconfig.MeshConfig_ExtensionProvider_HttpHeader_Value{ + Value: "Bearer token123", + }, + }, + }, + expectedConfig: &tracingcfg.ZipkinConfig{ + CollectorEndpointVersion: tracingcfg.ZipkinConfig_HTTP_JSON, + TraceId_128Bit: true, + SharedSpanContext: wrapperspb.Bool(false), + TraceContextOption: tracingcfg.ZipkinConfig_USE_B3, + CollectorService: &core.HttpService{ + HttpUri: &core.HttpUri{ + Uri: "http://zipkin.istio-system.svc.cluster.local/api/v2/spans", + Timeout: durationpb.New(5 * time.Second), + HttpUpstreamType: &core.HttpUri_Cluster{ + Cluster: "zipkin-cluster", + }, + }, + RequestHeadersToAdd: []*core.HeaderValueOption{ + { + AppendAction: core.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD, + Header: &core.HeaderValue{ + Key: "Authorization", + Value: "Bearer token123", + }, + }, + }, + }, + }, + }, + { + name: "zipkin with both timeout and headers", + hostname: "zipkin.istio-system.svc.cluster.local", + cluster: "zipkin-cluster", + endpoint: "/api/v2/spans", + timeout: durationpb.New(5000000000), // 5s + headers: []*meshconfig.MeshConfig_ExtensionProvider_HttpHeader{ + { + Name: "X-Custom-Header", + HeaderValue: &meshconfig.MeshConfig_ExtensionProvider_HttpHeader_Value{ + Value: "custom-value", + }, + }, + { + Name: "X-Tenant-ID", + HeaderValue: &meshconfig.MeshConfig_ExtensionProvider_HttpHeader_Value{ + Value: "tenant-123", + }, + }, + }, + expectedConfig: &tracingcfg.ZipkinConfig{ + CollectorEndpointVersion: tracingcfg.ZipkinConfig_HTTP_JSON, + TraceId_128Bit: true, + SharedSpanContext: wrapperspb.Bool(false), + TraceContextOption: tracingcfg.ZipkinConfig_USE_B3, + CollectorService: &core.HttpService{ + HttpUri: &core.HttpUri{ + Uri: "http://zipkin.istio-system.svc.cluster.local/api/v2/spans", + HttpUpstreamType: &core.HttpUri_Cluster{ + Cluster: "zipkin-cluster", + }, + Timeout: durationpb.New(5000000000), + }, + RequestHeadersToAdd: []*core.HeaderValueOption{ + { + AppendAction: core.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD, + Header: &core.HeaderValue{ + Key: "X-Custom-Header", + Value: "custom-value", + }, + }, + { + AppendAction: core.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD, + Header: &core.HeaderValue{ + Key: "X-Tenant-ID", + Value: "tenant-123", + }, + }, + }, + }, + }, + }, + { + name: "zipkin without timeout and headers uses HttpService (for 1.29+)", + hostname: "zipkin.istio-system.svc.cluster.local", + cluster: "zipkin-cluster", + endpoint: "/api/v2/spans", + timeout: nil, + headers: nil, + expectedConfig: &tracingcfg.ZipkinConfig{ + CollectorEndpointVersion: tracingcfg.ZipkinConfig_HTTP_JSON, + TraceId_128Bit: true, + SharedSpanContext: wrapperspb.Bool(false), + TraceContextOption: tracingcfg.ZipkinConfig_USE_B3, + CollectorService: &core.HttpService{ + HttpUri: &core.HttpUri{ + Uri: "http://zipkin.istio-system.svc.cluster.local/api/v2/spans", + Timeout: durationpb.New(5 * time.Second), + HttpUpstreamType: &core.HttpUri_Cluster{ + Cluster: "zipkin-cluster", + }, + }, + }, + }, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // Use 1.29+ proxy to test timeout/headers support + proxy := &model.Proxy{ + IstioVersion: &model.IstioVersion{Major: 1, Minor: 29, Patch: 0}, + } + result, err := zipkinConfig(tc.hostname, tc.cluster, tc.endpoint, true, tracingcfg.ZipkinConfig_USE_B3, tc.timeout, tc.headers, proxy) + assert.NoError(t, err) + + var actualConfig tracingcfg.ZipkinConfig + err = result.UnmarshalTo(&actualConfig) + assert.NoError(t, err) + + if diff := cmp.Diff(tc.expectedConfig, &actualConfig, protocmp.Transform()); diff != "" { + t.Fatalf("zipkinConfig returned unexpected diff (-want +got):\n%s", diff) + } + }) + } +} + +// TestZipkinConfigTimeoutHeadersVersionGating tests that timeout and headers are only used for proxies that support it +func TestZipkinConfigTimeoutHeadersVersionGating(t *testing.T) { + testcases := []struct { + name string + proxyVersion *model.IstioVersion + timeout *durationpb.Duration + headers []*meshconfig.MeshConfig_ExtensionProvider_HttpHeader + expectHTTPService bool + }{ + { + name: "New proxy (1.29+) with timeout should use HttpService", + proxyVersion: &model.IstioVersion{Major: 1, Minor: 29, Patch: 0}, + timeout: durationpb.New(10 * time.Second), + headers: nil, + expectHTTPService: true, + }, + { + name: "New proxy (1.29+) with headers should use HttpService", + proxyVersion: &model.IstioVersion{Major: 1, Minor: 29, Patch: 0}, + timeout: nil, + headers: []*meshconfig.MeshConfig_ExtensionProvider_HttpHeader{ + { + Name: "Authorization", + HeaderValue: &meshconfig.MeshConfig_ExtensionProvider_HttpHeader_Value{ + Value: "Bearer token", + }, + }, + }, + expectHTTPService: true, + }, + { + name: "Old proxy (1.28) with timeout should NOT use HttpService", + proxyVersion: &model.IstioVersion{Major: 1, Minor: 28, Patch: 0}, + timeout: durationpb.New(10 * time.Second), + headers: nil, + expectHTTPService: false, + }, + { + name: "Old proxy (1.28) with headers should NOT use HttpService", + proxyVersion: &model.IstioVersion{Major: 1, Minor: 28, Patch: 0}, + timeout: nil, + headers: []*meshconfig.MeshConfig_ExtensionProvider_HttpHeader{ + { + Name: "Authorization", + HeaderValue: &meshconfig.MeshConfig_ExtensionProvider_HttpHeader_Value{ + Value: "Bearer token", + }, + }, + }, + expectHTTPService: false, + }, + { + name: "New proxy (1.29+) without timeout/headers should use HttpService", + proxyVersion: &model.IstioVersion{Major: 1, Minor: 29, Patch: 0}, + timeout: nil, + headers: nil, + expectHTTPService: true, + }, + { + name: "Old proxy (1.28) without timeout/headers should NOT use HttpService", + proxyVersion: &model.IstioVersion{Major: 1, Minor: 28, Patch: 0}, + timeout: nil, + headers: nil, + expectHTTPService: false, + }, + { + name: "Proxy with nil version should NOT use HttpService", + proxyVersion: nil, + timeout: durationpb.New(10000000000), + headers: nil, + expectHTTPService: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + proxy := &model.Proxy{ + IstioVersion: tc.proxyVersion, + } + + result, err := zipkinConfig( + "zipkin.istio-system.svc.cluster.local", + "outbound|9411||zipkin.istio-system.svc.cluster.local", + "/api/v2/spans", + true, + tracingcfg.ZipkinConfig_USE_B3, + tc.timeout, + tc.headers, + proxy, + ) + assert.NoError(t, err) + + var actualConfig tracingcfg.ZipkinConfig + err = result.UnmarshalTo(&actualConfig) + assert.NoError(t, err) + + versionStr := "nil" + if tc.proxyVersion != nil { + versionStr = tc.proxyVersion.String() + } + + if tc.expectHTTPService { + // For new proxies, HttpService should be set + if actualConfig.CollectorService == nil { + t.Fatalf("CollectorService should be set for proxy version %s", versionStr) + } + if actualConfig.CollectorCluster != "" { + t.Fatalf("CollectorCluster should be empty when using HttpService for proxy version %s, got: %s", versionStr, actualConfig.CollectorCluster) + } + } else { + // For old proxies, legacy fields should be set + if actualConfig.CollectorService != nil { + t.Fatalf("CollectorService should NOT be set for proxy version %s", versionStr) + } + if actualConfig.CollectorCluster == "" { + t.Fatalf("CollectorCluster should be set when using legacy config for proxy version %s", versionStr) + } + } + }) + } +} + func TestGetHeaderValue(t *testing.T) { t.Setenv("CUSTOM_ENV_NAME", "custom-env-value") cases := []struct { diff --git a/pilot/pkg/networking/util/util.go b/pilot/pkg/networking/util/util.go index 772799abd8..2e3b6c3d2e 100644 --- a/pilot/pkg/networking/util/util.go +++ b/pilot/pkg/networking/util/util.go @@ -46,11 +46,11 @@ import ( "istio.io/istio/pilot/pkg/util/protoconv" "istio.io/istio/pkg/config" "istio.io/istio/pkg/config/constants" + "istio.io/istio/pkg/config/schema/gvk" kubelabels "istio.io/istio/pkg/kube/labels" "istio.io/istio/pkg/log" pm "istio.io/istio/pkg/model" "istio.io/istio/pkg/proto/merge" - "istio.io/istio/pkg/util/strcase" "istio.io/istio/pkg/wellknown" ) @@ -356,17 +356,21 @@ func BuildConfigInfoMetadata(config config.Meta) *core.Metadata { func AddConfigInfoMetadata(metadata *core.Metadata, config config.Meta) *core.Metadata { if metadata == nil { metadata = &core.Metadata{ - FilterMetadata: map[string]*structpb.Struct{}, + FilterMetadata: make(map[string]*structpb.Struct, 1), } } + s := "/apis/" + config.GroupVersionKind.Group + "/" + config.GroupVersionKind.Version + "/namespaces/" + config.Namespace + "/" + - strcase.CamelCaseToKebabCase(config.GroupVersionKind.Kind) + "/" + config.Name - if _, ok := metadata.FilterMetadata[IstioMetadataKey]; !ok { - metadata.FilterMetadata[IstioMetadataKey] = &structpb.Struct{ - Fields: map[string]*structpb.Value{}, + gvk.KebabKind(config.GroupVersionKind.Kind) + "/" + config.Name + + istioMeta, ok := metadata.FilterMetadata[IstioMetadataKey] + if !ok { + istioMeta = &structpb.Struct{ + Fields: make(map[string]*structpb.Value, 1), } + metadata.FilterMetadata[IstioMetadataKey] = istioMeta } - metadata.FilterMetadata[IstioMetadataKey].Fields["config"] = &structpb.Value{ + istioMeta.Fields["config"] = &structpb.Value{ Kind: &structpb.Value_StringValue{ StringValue: s, }, diff --git a/pilot/pkg/networking/util/util_test.go b/pilot/pkg/networking/util/util_test.go index a39b91136c..27f985fda7 100644 --- a/pilot/pkg/networking/util/util_test.go +++ b/pilot/pkg/networking/util/util_test.go @@ -1864,3 +1864,54 @@ func TestMeshNetworksToEnvoyInternalAddressConfig(t *testing.T) { }) } } + +func BenchmarkAddConfigInfoMetadata(b *testing.B) { + configs := []config.Meta{ + { + Name: "my-virtual-service", + Namespace: "default", + GroupVersionKind: gvk.VirtualService, + }, + { + Name: "my-destination-rule", + Namespace: "istio-system", + GroupVersionKind: gvk.DestinationRule, + }, + { + Name: "my-gateway", + Namespace: "production", + GroupVersionKind: gvk.Gateway, + }, + } + + b.Run("NilMetadata", func(b *testing.B) { + cfg := configs[0] + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = AddConfigInfoMetadata(nil, cfg) + } + }) + + b.Run("ExistingMetadata", func(b *testing.B) { + cfg := configs[0] + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + meta := &core.Metadata{ + FilterMetadata: map[string]*structpb.Struct{}, + } + _ = AddConfigInfoMetadata(meta, cfg) + } + }) + + b.Run("MultipleConfigs", func(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + for _, cfg := range configs { + _ = AddConfigInfoMetadata(nil, cfg) + } + } + }) +} diff --git a/pilot/pkg/serviceregistry/aggregate/controller.go b/pilot/pkg/serviceregistry/aggregate/controller.go index 6fb18685a8..fb7e08ff02 100644 --- a/pilot/pkg/serviceregistry/aggregate/controller.go +++ b/pilot/pkg/serviceregistry/aggregate/controller.go @@ -378,6 +378,12 @@ func mergeService(dst, src *model.Service, srcRegistry serviceregistry.Instance) newAddresses := src.ClusterVIPs.GetAddressesFor(clusterID) dst.ClusterVIPs.SetAddressesFor(clusterID, newAddresses) } + // Merge service accounts from different clusters + // Each cluster may have a different trust domain, so we need to collect all unique service accounts + if len(src.ServiceAccounts) > 0 { + dst.ServiceAccounts = append(dst.ServiceAccounts, src.ServiceAccounts...) + dst.ServiceAccounts = slices.FilterDuplicates(dst.ServiceAccounts) + } } // NetworkGateways merges the service-based cross-network gateways from each registry. diff --git a/pilot/pkg/serviceregistry/aggregate/controller_test.go b/pilot/pkg/serviceregistry/aggregate/controller_test.go index e96df430ab..92ad7a2d0d 100644 --- a/pilot/pkg/serviceregistry/aggregate/controller_test.go +++ b/pilot/pkg/serviceregistry/aggregate/controller_test.go @@ -477,3 +477,118 @@ func TestDeferredRun(t *testing.T) { expectRunningOrFail(t, ctrl, true) }) } + +func TestMergeServiceWithSameTrustDomain(t *testing.T) { + svc1 := mock.MakeService(mock.ServiceArgs{ + Hostname: "test.default.svc.cluster.local", + Address: "10.1.0.1", + ServiceAccounts: []string{"spiffe://cluster.local/ns/default/sa/test-sa"}, + ClusterID: "cluster-1", + }) + svc2 := mock.MakeService(mock.ServiceArgs{ + Hostname: "test.default.svc.cluster.local", + Address: "10.2.0.1", + ServiceAccounts: []string{"spiffe://cluster.local/ns/default/sa/test-sa"}, + ClusterID: "cluster-2", + }) + + discovery1 := memory.NewServiceDiscovery(svc1) + discovery2 := memory.NewServiceDiscovery(svc2) + + registry1 := serviceregistry.Simple{ + ProviderID: provider.Kubernetes, + ClusterID: "cluster-1", + DiscoveryController: discovery1, + } + + registry2 := serviceregistry.Simple{ + ProviderID: provider.Kubernetes, + ClusterID: "cluster-2", + DiscoveryController: discovery2, + } + + ctrl := NewController(Options{ + MeshHolder: &mockMeshConfigHolder{}, + }) + ctrl.AddRegistry(registry1) + ctrl.AddRegistry(registry2) + + mergedSvc := ctrl.GetService(svc1.Hostname) + if mergedSvc == nil { + t.Fatal("Failed to get merged service") + } + + expectedClusterVIPs := map[cluster.ID][]string{ + "cluster-1": {"10.1.0.1"}, + "cluster-2": {"10.2.0.1"}, + } + if !reflect.DeepEqual(mergedSvc.ClusterVIPs.Addresses, expectedClusterVIPs) { + t.Errorf("ClusterVIPs mismatch.\nGot: %v\nWant: %v", + mergedSvc.ClusterVIPs.Addresses, expectedClusterVIPs) + } + + expectedServiceAccounts := []string{"spiffe://cluster.local/ns/default/sa/test-sa"} + if !reflect.DeepEqual(mergedSvc.ServiceAccounts, expectedServiceAccounts) { + t.Errorf("ServiceAccounts mismatch.\nGot: %v\nWant: %v", + mergedSvc.ServiceAccounts, expectedServiceAccounts) + } +} + +func TestMergeServiceWithDistinctTrustDomains(t *testing.T) { + svc1 := mock.MakeService(mock.ServiceArgs{ + Hostname: "test.default.svc.cluster.local", + Address: "10.1.0.1", + ServiceAccounts: []string{"spiffe://mesh.east/ns/default/sa/test-sa"}, + ClusterID: "cluster-east", + }) + svc2 := mock.MakeService(mock.ServiceArgs{ + Hostname: "test.default.svc.cluster.local", + Address: "10.2.0.1", + ServiceAccounts: []string{"spiffe://mesh.west/ns/default/sa/test-sa"}, + ClusterID: "cluster-west", + }) + + discovery1 := memory.NewServiceDiscovery(svc1) + discovery2 := memory.NewServiceDiscovery(svc2) + + registry1 := serviceregistry.Simple{ + ProviderID: provider.Kubernetes, + ClusterID: "cluster-east", + DiscoveryController: discovery1, + } + + registry2 := serviceregistry.Simple{ + ProviderID: provider.Kubernetes, + ClusterID: "cluster-west", + DiscoveryController: discovery2, + } + + ctrl := NewController(Options{ + MeshHolder: &mockMeshConfigHolder{}, + }) + ctrl.AddRegistry(registry1) + ctrl.AddRegistry(registry2) + + mergedSvc := ctrl.GetService(svc1.Hostname) + if mergedSvc == nil { + t.Fatal("Failed to get merged service") + } + + expectedClusterVIPs := map[cluster.ID][]string{ + "cluster-east": {"10.1.0.1"}, + "cluster-west": {"10.2.0.1"}, + } + if !reflect.DeepEqual(mergedSvc.ClusterVIPs.Addresses, expectedClusterVIPs) { + t.Errorf("ClusterVIPs mismatch.\nGot: %v\nWant: %v", + mergedSvc.ClusterVIPs.Addresses, expectedClusterVIPs) + } + + expectedServiceAccounts := []string{ + "spiffe://mesh.east/ns/default/sa/test-sa", + "spiffe://mesh.west/ns/default/sa/test-sa", + } + if !reflect.DeepEqual(mergedSvc.ServiceAccounts, expectedServiceAccounts) { + t.Errorf("ServiceAccounts mismatch.\nGot: %v\nWant: %v", + mergedSvc.ServiceAccounts, expectedServiceAccounts) + } +} diff --git a/pilot/pkg/serviceregistry/kube/controller/ambient/ambientindex.go b/pilot/pkg/serviceregistry/kube/controller/ambient/ambientindex.go index fa8f699f64..b64c3d08a7 100644 --- a/pilot/pkg/serviceregistry/kube/controller/ambient/ambientindex.go +++ b/pilot/pkg/serviceregistry/kube/controller/ambient/ambientindex.go @@ -23,7 +23,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" - "sigs.k8s.io/gateway-api/apis/v1beta1" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" "istio.io/api/label" "istio.io/api/meta/v1alpha1" @@ -186,25 +186,34 @@ func New(options Options) Index { )...) authzPolicies := kclient.NewDelayedInformer[*securityclient.AuthorizationPolicy](options.Client, gvr.AuthorizationPolicy, kubetypes.StandardInformer, configFilter) + // Start with a.stop to ensure the informer respects the index's stop channel + authzPolicies.Start(a.stop) AuthzPolicies := krt.WrapClient[*securityclient.AuthorizationPolicy](authzPolicies, opts.WithName("informer/AuthorizationPolicies")...) peerAuths := kclient.NewDelayedInformer[*securityclient.PeerAuthentication](options.Client, gvr.PeerAuthentication, kubetypes.StandardInformer, configFilter) + // Start with a.stop to ensure the informer respects the index's stop channel + peerAuths.Start(a.stop) PeerAuths := krt.WrapClient[*securityclient.PeerAuthentication](peerAuths, opts.WithName("informer/PeerAuthentications")...) - gatewayClient := kclient.NewDelayedInformer[*v1beta1.Gateway](options.Client, gvr.KubernetesGateway, kubetypes.StandardInformer, filter) - Gateways := krt.WrapClient[*v1beta1.Gateway](gatewayClient, opts.With( + gatewayClient := kclient.NewDelayedInformer[*gatewayv1.Gateway](options.Client, gvr.KubernetesGateway, kubetypes.StandardInformer, filter) + // Start with a.stop to ensure the informer respects the index's stop channel + gatewayClient.Start(a.stop) + Gateways := krt.WrapClient[*gatewayv1.Gateway](gatewayClient, opts.With( krt.WithName("informer/Gateways"), krt.WithMetadata(krt.Metadata{ multicluster.ClusterKRTMetadataKey: options.ClusterID, }), )...) - gatewayClassClient := kclient.NewDelayedInformer[*v1beta1.GatewayClass](options.Client, gvr.GatewayClass, kubetypes.StandardInformer, filter) - GatewayClasses := krt.WrapClient[*v1beta1.GatewayClass](gatewayClassClient, opts.WithName("informer/GatewayClasses")...) - Pods := krt.NewInformerFiltered[*corev1.Pod](options.Client, kclient.Filter{ + gatewayClassClient := kclient.NewDelayedInformer[*gatewayv1.GatewayClass](options.Client, gvr.GatewayClass, kubetypes.StandardInformer, filter) + // Start with a.stop to ensure the informer respects the index's stop channel + gatewayClassClient.Start(a.stop) + GatewayClasses := krt.WrapClient[*gatewayv1.GatewayClass](gatewayClassClient, opts.WithName("informer/GatewayClasses")...) + Pods := krt.NewFilteredInformer[*corev1.Pod](options.Client, kclient.Filter{ ObjectFilter: options.Client.ObjectFilter(), ObjectTransform: kubeclient.StripPodUnusedFields, + FieldSelector: "status.phase!=Failed", }, opts.With( krt.WithName("informer/Pods"), krt.WithMetadata(krt.Metadata{ @@ -214,10 +223,14 @@ func New(options Options) Index { serviceEntries := kclient.NewDelayedInformer[*networkingclient.ServiceEntry](options.Client, gvr.ServiceEntry, kubetypes.StandardInformer, configFilter) + // Start with a.stop to ensure the informer respects the index's stop channel + serviceEntries.Start(a.stop) ServiceEntries := krt.WrapClient[*networkingclient.ServiceEntry](serviceEntries, opts.WithName("informer/ServiceEntries")...) workloadEntries := kclient.NewDelayedInformer[*networkingclient.WorkloadEntry](options.Client, gvr.WorkloadEntry, kubetypes.StandardInformer, configFilter) + // Start with a.stop to ensure the informer respects the index's stop channel + workloadEntries.Start(a.stop) WorkloadEntries := krt.WrapClient[*networkingclient.WorkloadEntry](workloadEntries, opts.WithName("informer/WorkloadEntries")...) servicesClient := kclient.NewFiltered[*corev1.Service](options.Client, filter) @@ -227,7 +240,7 @@ func New(options Options) Index { multicluster.ClusterKRTMetadataKey: options.ClusterID, }), )...) - Nodes := krt.NewInformerFiltered[*corev1.Node](options.Client, kclient.Filter{ + Nodes := krt.NewFilteredInformer[*corev1.Node](options.Client, kclient.Filter{ ObjectFilter: options.Client.ObjectFilter(), ObjectTransform: kubeclient.StripNodeUnusedFields, }, opts.With( @@ -237,7 +250,7 @@ func New(options Options) Index { }), )...) - EndpointSlices := krt.NewInformerFiltered[*discovery.EndpointSlice](options.Client, kclient.Filter{ + EndpointSlices := krt.NewFilteredInformer[*discovery.EndpointSlice](options.Client, kclient.Filter{ ObjectFilter: options.Client.ObjectFilter(), }, opts.With( krt.WithName("informer/EndpointSlices"), diff --git a/pilot/pkg/serviceregistry/kube/controller/ambient/ambientindex_multicluster_test.go b/pilot/pkg/serviceregistry/kube/controller/ambient/ambientindex_multicluster_test.go index 9ca534a9ce..766ce28222 100644 --- a/pilot/pkg/serviceregistry/kube/controller/ambient/ambientindex_multicluster_test.go +++ b/pilot/pkg/serviceregistry/kube/controller/ambient/ambientindex_multicluster_test.go @@ -21,7 +21,6 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8sv1 "sigs.k8s.io/gateway-api/apis/v1" - k8sbeta "sigs.k8s.io/gateway-api/apis/v1beta1" "istio.io/api/label" apiv1alpha3 "istio.io/client-go/pkg/apis/networking/v1" @@ -45,8 +44,8 @@ type ambientclients struct { sc clienttest.TestClient[*corev1.Service] sec clienttest.TestWriter[*corev1.Secret] ns clienttest.TestWriter[*corev1.Namespace] - grc clienttest.TestWriter[*k8sbeta.Gateway] - gwcls clienttest.TestWriter[*k8sbeta.GatewayClass] + grc clienttest.TestWriter[*k8sv1.Gateway] + gwcls clienttest.TestWriter[*k8sv1.GatewayClass] se clienttest.TestWriter[*apiv1alpha3.ServiceEntry] we clienttest.TestWriter[*apiv1alpha3.WorkloadEntry] pa clienttest.TestWriter[*clientsecurityv1beta1.PeerAuthentication] @@ -136,8 +135,8 @@ func TestAmbientMulticlusterIndex_WaypointForWorkloadTraffic(t *testing.T) { pc: clienttest.NewDirectClient[*corev1.Pod, corev1.Pod, *corev1.PodList](t, cl), sc: clienttest.NewDirectClient[*corev1.Service, corev1.Service, *corev1.ServiceList](t, cl), ns: clienttest.NewWriter[*corev1.Namespace](t, cl), - grc: clienttest.NewWriter[*k8sbeta.Gateway](t, cl), - gwcls: clienttest.NewWriter[*k8sbeta.GatewayClass](t, cl), + grc: clienttest.NewWriter[*k8sv1.Gateway](t, cl), + gwcls: clienttest.NewWriter[*k8sv1.GatewayClass](t, cl), se: clienttest.NewWriter[*apiv1alpha3.ServiceEntry](t, cl), we: clienttest.NewWriter[*apiv1alpha3.WorkloadEntry](t, cl), pa: clienttest.NewWriter[*clientsecurityv1beta1.PeerAuthentication](t, cl), @@ -209,7 +208,7 @@ func TestAmbientMulticlusterIndex_WaypointForWorkloadTraffic(t *testing.T) { Labels: map[string]string{label.TopologyNetwork.Name: clusterToNetwork[client.clusterID]}, }, }) - client.gwcls.Create(&k8sbeta.GatewayClass{ + client.gwcls.Create(&k8sv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: constants.EastWestGatewayClassName, }, @@ -435,8 +434,8 @@ func TestMulticlusterAmbientIndex_TestServiceMerging(t *testing.T) { pc: clienttest.NewDirectClient[*corev1.Pod, corev1.Pod, *corev1.PodList](t, cl), sc: clienttest.NewDirectClient[*corev1.Service, corev1.Service, *corev1.ServiceList](t, cl), ns: clienttest.NewWriter[*corev1.Namespace](t, cl), - grc: clienttest.NewWriter[*k8sbeta.Gateway](t, cl), - gwcls: clienttest.NewWriter[*k8sbeta.GatewayClass](t, cl), + grc: clienttest.NewWriter[*k8sv1.Gateway](t, cl), + gwcls: clienttest.NewWriter[*k8sv1.GatewayClass](t, cl), se: clienttest.NewWriter[*apiv1alpha3.ServiceEntry](t, cl), we: clienttest.NewWriter[*apiv1alpha3.WorkloadEntry](t, cl), pa: clienttest.NewWriter[*clientsecurityv1beta1.PeerAuthentication](t, cl), @@ -552,6 +551,8 @@ func TestMulticlusterAmbientIndex_TestServiceMerging(t *testing.T) { func TestMulticlusterAmbientIndex_SplitHorizon(t *testing.T) { test.SetForTest(t, &features.EnableAmbientMultiNetwork, true) s := newAmbientTestServer(t, testC, testNW, "") + // Test that we're propagating the trust domain correctly + s.meshConfig.Mesh().TrustDomain = s.DomainSuffix s.AddSecret("s1", "remote-cluster") // overlapping ips remoteClients := krt.NewCollection(s.remoteClusters, func(_ krt.HandlerContext, c *multicluster.Cluster) **remoteAmbientClients { cl := c.Client @@ -561,8 +562,8 @@ func TestMulticlusterAmbientIndex_SplitHorizon(t *testing.T) { pc: clienttest.NewDirectClient[*corev1.Pod, corev1.Pod, *corev1.PodList](t, cl), sc: clienttest.NewDirectClient[*corev1.Service, corev1.Service, *corev1.ServiceList](t, cl), ns: clienttest.NewWriter[*corev1.Namespace](t, cl), - grc: clienttest.NewWriter[*k8sbeta.Gateway](t, cl), - gwcls: clienttest.NewWriter[*k8sbeta.GatewayClass](t, cl), + grc: clienttest.NewWriter[*k8sv1.Gateway](t, cl), + gwcls: clienttest.NewWriter[*k8sv1.GatewayClass](t, cl), se: clienttest.NewWriter[*apiv1alpha3.ServiceEntry](t, cl), we: clienttest.NewWriter[*apiv1alpha3.WorkloadEntry](t, cl), pa: clienttest.NewWriter[*clientsecurityv1beta1.PeerAuthentication](t, cl), @@ -636,6 +637,12 @@ func TestMulticlusterAmbientIndex_SplitHorizon(t *testing.T) { if len(gwwl.Workload.Addresses) != 1 { return fmt.Errorf("expected network gateway workload to have addresses, got %v", gwwl.Workload.Addresses) } + if gwwl.Workload.TrustDomain != s.DomainSuffix { + return fmt.Errorf("expected network gateway workload to have trust domain %s, got %s", + s.DomainSuffix, + gwwl.Workload.TrustDomain, + ) + } expectedAddress := []uint8{172, 0, 1, 2} if !reflect.DeepEqual(gwwl.Workload.Addresses[0], expectedAddress) { return fmt.Errorf("expected network gateway workload to have address %s, got %s", diff --git a/pilot/pkg/serviceregistry/kube/controller/ambient/ambientindex_test.go b/pilot/pkg/serviceregistry/kube/controller/ambient/ambientindex_test.go index e856308501..0b14aa50ec 100644 --- a/pilot/pkg/serviceregistry/kube/controller/ambient/ambientindex_test.go +++ b/pilot/pkg/serviceregistry/kube/controller/ambient/ambientindex_test.go @@ -26,7 +26,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" k8sv1 "sigs.k8s.io/gateway-api/apis/v1" - k8sbeta "sigs.k8s.io/gateway-api/apis/v1beta1" "sigs.k8s.io/yaml" "istio.io/api/annotation" @@ -859,7 +858,7 @@ func TestAmbientIndex_WaypointAddressAddedToWorkloads(t *testing.T) { func TestAmbientIndex_WaypointInboundBinding(t *testing.T) { s := newAmbientTestServer(t, testC, testNW, "") - s.gwcls.Update(&k8sbeta.GatewayClass{ + s.gwcls.Update(&k8sv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: constants.WaypointGatewayClassName, Annotations: map[string]string{ @@ -2172,8 +2171,8 @@ func newAmbientTestServerFromOptions(t *testing.T, networkID network.ID, options pc: clienttest.NewDirectClient[*corev1.Pod, corev1.Pod, *corev1.PodList](t, cl), sc: clienttest.NewDirectClient[*corev1.Service, corev1.Service, *corev1.ServiceList](t, cl), ns: clienttest.NewWriter[*corev1.Namespace](t, cl), - grc: clienttest.NewWriter[*k8sbeta.Gateway](t, cl), - gwcls: clienttest.NewWriter[*k8sbeta.GatewayClass](t, cl), + grc: clienttest.NewWriter[*k8sv1.Gateway](t, cl), + gwcls: clienttest.NewWriter[*k8sv1.GatewayClass](t, cl), se: clienttest.NewWriter[*apiv1alpha3.ServiceEntry](t, cl), we: clienttest.NewWriter[*apiv1alpha3.WorkloadEntry](t, cl), pa: clienttest.NewWriter[*clientsecurityv1beta1.PeerAuthentication](t, cl), @@ -2183,7 +2182,7 @@ func newAmbientTestServerFromOptions(t *testing.T, networkID network.ID, options } // assume this is installed with istio - a.gwcls.Create(&k8sbeta.GatewayClass{ + a.gwcls.Create(&k8sv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: constants.WaypointGatewayClassName, }, @@ -2191,7 +2190,7 @@ func newAmbientTestServerFromOptions(t *testing.T, networkID network.ID, options ControllerName: constants.ManagedGatewayMeshController, }, }) - a.gwcls.Create(&k8sbeta.GatewayClass{ + a.gwcls.Create(&k8sv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: constants.EastWestGatewayClassName, }, @@ -2285,12 +2284,12 @@ func dumpOnFailure(t *testing.T, debugger *krt.DebugHandler) { }) } -func (s *ambientTestServer) deleteNetworkGatewayForClient(t *testing.T, name string, grc clienttest.TestWriter[*k8sbeta.Gateway]) { +func (s *ambientTestServer) deleteNetworkGatewayForClient(t *testing.T, name string, grc clienttest.TestWriter[*k8sv1.Gateway]) { t.Helper() grc.Delete(name, testNS) } -func (s *ambientTestServer) addNetworkGatewayForClient(t *testing.T, ip, network string, grc clienttest.TestWriter[*k8sbeta.Gateway]) { +func (s *ambientTestServer) addNetworkGatewayForClient(t *testing.T, ip, network string, grc clienttest.TestWriter[*k8sv1.Gateway]) { s.addNetworkGatewaySpecificAddressForClient(t, ip, "", "east-west", network, true, grc) } @@ -2298,17 +2297,17 @@ func (s *ambientTestServer) addNetworkGatewaySpecificAddressForClient( t *testing.T, ip, hostname, name, network string, ready bool, - grc clienttest.TestWriter[*k8sbeta.Gateway], + grc clienttest.TestWriter[*k8sv1.Gateway], ) { t.Helper() - gatewaySpec := k8sbeta.GatewaySpec{ + gatewaySpec := k8sv1.GatewaySpec{ GatewayClassName: constants.EastWestGatewayClassName, - Listeners: []k8sbeta.Listener{ + Listeners: []k8sv1.Listener{ { Name: "mesh", Port: 15008, Protocol: "HBONE", - TLS: &k8sbeta.ListenerTLSConfig{ + TLS: &k8sv1.ListenerTLSConfig{ Mode: ptr.Of(k8sv1.TLSModeTerminate), Options: map[k8sv1.AnnotationKey]k8sv1.AnnotationValue{ "gateway.istio.io/tls-terminate-mode": "ISTIO_MUTUAL", @@ -2318,7 +2317,7 @@ func (s *ambientTestServer) addNetworkGatewaySpecificAddressForClient( }, } - gateway := k8sbeta.Gateway{ + gateway := k8sv1.Gateway{ TypeMeta: metav1.TypeMeta{ Kind: gvk.KubernetesGateway.Kind, APIVersion: gvk.KubernetesGateway.GroupVersion(), @@ -2331,18 +2330,18 @@ func (s *ambientTestServer) addNetworkGatewaySpecificAddressForClient( }, }, Spec: gatewaySpec, - Status: k8sbeta.GatewayStatus{}, + Status: k8sv1.GatewayStatus{}, } if ready { addr := []k8sv1.GatewayStatusAddress{} if ip != "" { - addr = append(addr, k8sv1.GatewayStatusAddress{Type: ptr.Of(k8sbeta.IPAddressType), Value: ip}) + addr = append(addr, k8sv1.GatewayStatusAddress{Type: ptr.Of(k8sv1.IPAddressType), Value: ip}) } if hostname != "" { - addr = append(addr, k8sv1.GatewayStatusAddress{Type: ptr.Of(k8sbeta.HostnameAddressType), Value: hostname}) + addr = append(addr, k8sv1.GatewayStatusAddress{Type: ptr.Of(k8sv1.HostnameAddressType), Value: hostname}) } - gateway.Status = k8sbeta.GatewayStatus{ + gateway.Status = k8sv1.GatewayStatus{ Addresses: addr, } } @@ -2354,7 +2353,7 @@ func (s *ambientTestServer) addWaypoint(t *testing.T, ip, name, trafficType stri s.addWaypointForClient(t, ip, name, trafficType, ready, s.grc) } -func (s *ambientTestServer) addWaypointForClient(t *testing.T, ip, name, trafficType string, ready bool, grc clienttest.TestWriter[*k8sbeta.Gateway]) { +func (s *ambientTestServer) addWaypointForClient(t *testing.T, ip, name, trafficType string, ready bool, grc clienttest.TestWriter[*k8sv1.Gateway]) { s.addWaypointSpecificAddressForClient(t, ip, fmt.Sprintf("%s.%s.svc.%s", name, testNS, s.DomainSuffix), name, trafficType, ready, grc) } @@ -2362,19 +2361,19 @@ func (s *ambientTestServer) addWaypointSpecificAddressForClient( t *testing.T, ip, hostname, name, trafficType string, ready bool, - grc clienttest.TestWriter[*k8sbeta.Gateway], + grc clienttest.TestWriter[*k8sv1.Gateway], ) { t.Helper() fromSame := k8sv1.NamespacesFromSame - gatewaySpec := k8sbeta.GatewaySpec{ + gatewaySpec := k8sv1.GatewaySpec{ GatewayClassName: constants.WaypointGatewayClassName, - Listeners: []k8sbeta.Listener{ + Listeners: []k8sv1.Listener{ { Name: "mesh", Port: 15008, Protocol: "HBONE", - AllowedRoutes: &k8sbeta.AllowedRoutes{ - Namespaces: &k8sbeta.RouteNamespaces{ + AllowedRoutes: &k8sv1.AllowedRoutes{ + Namespaces: &k8sv1.RouteNamespaces{ From: &fromSame, }, }, @@ -2382,7 +2381,7 @@ func (s *ambientTestServer) addWaypointSpecificAddressForClient( }, } - gateway := k8sbeta.Gateway{ + gateway := k8sv1.Gateway{ TypeMeta: metav1.TypeMeta{ Kind: gvk.KubernetesGateway.Kind, APIVersion: gvk.KubernetesGateway.GroupVersion(), @@ -2392,7 +2391,7 @@ func (s *ambientTestServer) addWaypointSpecificAddressForClient( Namespace: testNS, }, Spec: gatewaySpec, - Status: k8sbeta.GatewayStatus{}, + Status: k8sv1.GatewayStatus{}, } labels := make(map[string]string, 2) if trafficType != "" && validTrafficTypes.Contains(trafficType) { @@ -2405,12 +2404,12 @@ func (s *ambientTestServer) addWaypointSpecificAddressForClient( if ready { addr := []k8sv1.GatewayStatusAddress{} if ip != "" { - addr = append(addr, k8sv1.GatewayStatusAddress{Type: ptr.Of(k8sbeta.IPAddressType), Value: ip}) + addr = append(addr, k8sv1.GatewayStatusAddress{Type: ptr.Of(k8sv1.IPAddressType), Value: ip}) } if hostname != "" { - addr = append(addr, k8sv1.GatewayStatusAddress{Type: ptr.Of(k8sbeta.HostnameAddressType), Value: hostname}) + addr = append(addr, k8sv1.GatewayStatusAddress{Type: ptr.Of(k8sv1.HostnameAddressType), Value: hostname}) } - gateway.Status = k8sbeta.GatewayStatus{ + gateway.Status = k8sv1.GatewayStatus{ Addresses: addr, } } @@ -2427,7 +2426,7 @@ func (s *ambientTestServer) deleteWaypoint(t *testing.T, name string) { s.deleteWaypointForClient(t, name, s.grc) } -func (s *ambientTestServer) deleteWaypointForClient(t *testing.T, name string, grc clienttest.TestWriter[*k8sbeta.Gateway]) { +func (s *ambientTestServer) deleteWaypointForClient(t *testing.T, name string, grc clienttest.TestWriter[*k8sv1.Gateway]) { t.Helper() grc.Delete(name, testNS) } diff --git a/pilot/pkg/serviceregistry/kube/controller/ambient/authorization_test.go b/pilot/pkg/serviceregistry/kube/controller/ambient/authorization_test.go index f4f5870d70..1520c0aa86 100644 --- a/pilot/pkg/serviceregistry/kube/controller/ambient/authorization_test.go +++ b/pilot/pkg/serviceregistry/kube/controller/ambient/authorization_test.go @@ -15,7 +15,6 @@ package ambient import ( - "context" "fmt" "testing" "time" @@ -23,8 +22,8 @@ import ( v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" gtwapiv1 "sigs.k8s.io/gateway-api/apis/v1" - gtwapiv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" // TODO: should likely update to v1 but this is the type currently recognized byt kclient + // TODO: should likely update to v1 but this is the type currently recognized byt kclient "istio.io/api/label" "istio.io/api/networking/v1alpha3" "istio.io/api/security/v1beta1" @@ -223,8 +222,7 @@ func TestWaypointPolicyStatusCollection(t *testing.T) { stop := test.NewStop(t) opts := krt.NewOptionsBuilder(stop, "", krt.GlobalDebugHandler) c := kube.NewFakeClient() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() clientAuthzPol := kclient.New[*securityclient.AuthorizationPolicy](c) authzPolCol := krt.WrapClient(clientAuthzPol, opts.WithName("authzPolCol")...) @@ -235,7 +233,7 @@ func TestWaypointPolicyStatusCollection(t *testing.T) { clientSe := kclient.New[*networkingclient.ServiceEntry](c) seCol := krt.WrapClient(clientSe, opts.WithName("seCol")...) - clientGwClass := kclient.New[*gtwapiv1beta1.GatewayClass](c) + clientGwClass := kclient.New[*gtwapiv1.GatewayClass](c) gwClassCol := krt.WrapClient(clientGwClass, opts.WithName("gwClassCol")...) meshConfigMock := krttest.NewMock(t, []any{ @@ -248,9 +246,9 @@ func TestWaypointPolicyStatusCollection(t *testing.T) { clientNs := kclient.New[*v1.Namespace](c) nsCol := krt.WrapClient(clientNs, opts.WithName("nsCol")...) - clientGtw := kclient.New[*gtwapiv1beta1.Gateway](c) + clientGtw := kclient.New[*gtwapiv1.Gateway](c) gtwCol := krt.WrapClient(clientGtw, opts.WithName("gtwCol")...) - waypointCol := krt.NewCollection(gtwCol, func(ctx krt.HandlerContext, i *gtwapiv1beta1.Gateway) *Waypoint { + waypointCol := krt.NewCollection(gtwCol, func(ctx krt.HandlerContext, i *gtwapiv1.Gateway) *Waypoint { if i == nil { return nil } @@ -287,7 +285,7 @@ func TestWaypointPolicyStatusCollection(t *testing.T) { assert.NoError(t, err) addrType := gtwapiv1.HostnameAddressType - _, err = clientGtw.Create(>wapiv1beta1.Gateway{ + _, err = clientGtw.Create(>wapiv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "waypoint", Namespace: testNS, @@ -997,12 +995,12 @@ func TestWaypointPolicyStatusCollection(t *testing.T) { }, { testName: "single-bind-gateway-class", - gatewayClasses: []gtwapiv1beta1.GatewayClass{ + gatewayClasses: []gtwapiv1.GatewayClass{ { ObjectMeta: metav1.ObjectMeta{ Name: "istio-waypoint", }, - Spec: gtwapiv1beta1.GatewayClassSpec{ + Spec: gtwapiv1.GatewayClassSpec{ ControllerName: constants.ManagedGatewayMeshController, }, }, @@ -1039,7 +1037,7 @@ func TestWaypointPolicyStatusCollection(t *testing.T) { }, { testName: "nonexistent-gateway-class", - gatewayClasses: []gtwapiv1beta1.GatewayClass{}, + gatewayClasses: []gtwapiv1.GatewayClass{}, policy: securityclient.AuthorizationPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "single-no-gateway-class-pol", @@ -1072,12 +1070,12 @@ func TestWaypointPolicyStatusCollection(t *testing.T) { }, { testName: "non-waypoint-gateway-class", - gatewayClasses: []gtwapiv1beta1.GatewayClass{ + gatewayClasses: []gtwapiv1.GatewayClass{ { ObjectMeta: metav1.ObjectMeta{ Name: "not-for-waypoint", }, - Spec: gtwapiv1beta1.GatewayClassSpec{ + Spec: gtwapiv1.GatewayClassSpec{ ControllerName: "random-controller", }, }, @@ -1114,12 +1112,12 @@ func TestWaypointPolicyStatusCollection(t *testing.T) { }, { testName: "gateway-class-ap-not-in-root-ns", - gatewayClasses: []gtwapiv1beta1.GatewayClass{ + gatewayClasses: []gtwapiv1.GatewayClass{ { ObjectMeta: metav1.ObjectMeta{ Name: "waypoint", }, - Spec: gtwapiv1beta1.GatewayClassSpec{ + Spec: gtwapiv1.GatewayClassSpec{ ControllerName: constants.ManagedGatewayMeshController, }, }, @@ -1195,7 +1193,7 @@ type TestWaypointPolicyStatusCollectionTestCase struct { testName string serviceEntries []networkingclient.ServiceEntry services []v1.Service - gatewayClasses []gtwapiv1beta1.GatewayClass + gatewayClasses []gtwapiv1.GatewayClass policy securityclient.AuthorizationPolicy expect []model.PolicyBindingStatus } diff --git a/pilot/pkg/serviceregistry/kube/controller/ambient/multicluster.go b/pilot/pkg/serviceregistry/kube/controller/ambient/multicluster.go index ec4c8c26f0..64fbb82d43 100644 --- a/pilot/pkg/serviceregistry/kube/controller/ambient/multicluster.go +++ b/pilot/pkg/serviceregistry/kube/controller/ambient/multicluster.go @@ -22,7 +22,7 @@ import ( "google.golang.org/protobuf/types/known/wrapperspb" v1 "k8s.io/api/core/v1" "k8s.io/client-go/rest" - "sigs.k8s.io/gateway-api/apis/v1beta1" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" "istio.io/api/label" networkingclient "istio.io/client-go/pkg/apis/networking/v1" @@ -54,7 +54,7 @@ func (a *index) buildGlobalCollections( localCluster *multicluster.Cluster, localAuthzPolicies krt.Collection[*securityclient.AuthorizationPolicy], localPeerAuths krt.Collection[*securityclient.PeerAuthentication], - localGatewayClasses krt.Collection[*v1beta1.GatewayClass], + localGatewayClasses krt.Collection[*gatewayv1.GatewayClass], localWorkloadEntries krt.Collection[*networkingclient.WorkloadEntry], localServiceEntries krt.Collection[*networkingclient.ServiceEntry], localServiceEntryInformers kclient.Informer[*networkingclient.ServiceEntry], @@ -93,8 +93,8 @@ func (a *index) buildGlobalCollections( ) serviceInformersByCluster := informerIndexByCluster(GlobalServices) - LocalGatewaysWithCluster := krt.MapCollection(LocalGateways, func(obj *v1beta1.Gateway) krt.ObjectWithCluster[*v1beta1.Gateway] { - return krt.ObjectWithCluster[*v1beta1.Gateway]{ + LocalGatewaysWithCluster := krt.MapCollection(LocalGateways, func(obj *gatewayv1.Gateway) krt.ObjectWithCluster[*gatewayv1.Gateway] { + return krt.ObjectWithCluster[*gatewayv1.Gateway]{ ClusterID: localCluster.ID, Object: &obj, } @@ -102,7 +102,7 @@ func (a *index) buildGlobalCollections( GlobalGatewaysWithCluster := nestedCollectionFromLocalAndRemote( LocalGatewaysWithCluster, clusters, - func(ctx krt.HandlerContext, c *multicluster.Cluster) *krt.Collection[krt.ObjectWithCluster[*v1beta1.Gateway]] { + func(ctx krt.HandlerContext, c *multicluster.Cluster) *krt.Collection[krt.ObjectWithCluster[*gatewayv1.Gateway]] { if !kube.WaitForCacheSync(fmt.Sprintf("ambient/informer/gateways[%s]", c.ID), a.stop, c.Gateways().HasSynced) { log.Warnf("Failed to sync gateways informer for cluster %s", c.ID) return nil @@ -116,8 +116,8 @@ func (a *index) buildGlobalCollections( krt.WithDebugging(opts.Debugger()), krt.WithStop(c.GetStop()), } - return ptr.Of(krt.MapCollection(c.Gateways(), func(obj *v1beta1.Gateway) krt.ObjectWithCluster[*v1beta1.Gateway] { - return krt.ObjectWithCluster[*v1beta1.Gateway]{ + return ptr.Of(krt.MapCollection(c.Gateways(), func(obj *gatewayv1.Gateway) krt.ObjectWithCluster[*gatewayv1.Gateway] { + return krt.ObjectWithCluster[*gatewayv1.Gateway]{ ClusterID: c.ID, Object: &obj, } diff --git a/pilot/pkg/serviceregistry/kube/controller/ambient/multicluster/cluster.go b/pilot/pkg/serviceregistry/kube/controller/ambient/multicluster/cluster.go index 7676f8736f..45fbf7b014 100644 --- a/pilot/pkg/serviceregistry/kube/controller/ambient/multicluster/cluster.go +++ b/pilot/pkg/serviceregistry/kube/controller/ambient/multicluster/cluster.go @@ -24,7 +24,7 @@ import ( discovery "k8s.io/api/discovery/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" - "sigs.k8s.io/gateway-api/apis/v1beta1" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" "istio.io/istio/pilot/pkg/features" "istio.io/istio/pkg/cluster" @@ -77,7 +77,7 @@ type RemoteClusterCollections struct { services krt.Collection[*corev1.Service] endpointSlices krt.Collection[*discovery.EndpointSlice] nodes krt.Collection[*corev1.Node] - gateways krt.Collection[*v1beta1.Gateway] + gateways krt.Collection[*gatewayv1.Gateway] } // Namespaces returns the namespaces collection. @@ -106,7 +106,7 @@ func (r *RemoteClusterCollections) Nodes() krt.Collection[*corev1.Node] { } // Gateways returns the gateways collection. -func (r *RemoteClusterCollections) Gateways() krt.Collection[*v1beta1.Gateway] { +func (r *RemoteClusterCollections) Gateways() krt.Collection[*gatewayv1.Gateway] { return r.gateways } @@ -116,7 +116,7 @@ func NewRemoteClusterCollections( services krt.Collection[*corev1.Service], endpointSlices krt.Collection[*discovery.EndpointSlice], nodes krt.Collection[*corev1.Node], - gateways krt.Collection[*v1beta1.Gateway], + gateways krt.Collection[*gatewayv1.Gateway], ) *RemoteClusterCollections { return &RemoteClusterCollections{ namespaces: namespaces, @@ -216,9 +216,10 @@ func (c *Cluster) Run(localMeshConfig meshwatcher.WatcherCollection, debugger *k ClusterKRTMetadataKey: c.ID, }), )...) - Pods := krt.NewInformerFiltered[*corev1.Pod](c.Client, kclient.Filter{ + Pods := krt.NewFilteredInformer[*corev1.Pod](c.Client, kclient.Filter{ ObjectFilter: c.Client.ObjectFilter(), ObjectTransform: kube.StripPodUnusedFields, + FieldSelector: "status.phase!=Failed", }, opts.With( krt.WithName("informer/Pods"), krt.WithMetadata(krt.Metadata{ @@ -226,8 +227,8 @@ func (c *Cluster) Run(localMeshConfig meshwatcher.WatcherCollection, debugger *k }), )...) - gatewayClient := kclient.NewDelayedInformer[*v1beta1.Gateway](c.Client, gvr.KubernetesGateway, kubetypes.StandardInformer, defaultFilter) - Gateways := krt.WrapClient[*v1beta1.Gateway](gatewayClient, opts.With( + gatewayClient := kclient.NewDelayedInformer[*gatewayv1.Gateway](c.Client, gvr.KubernetesGateway, kubetypes.StandardInformer, defaultFilter) + Gateways := krt.WrapClient[*gatewayv1.Gateway](gatewayClient, opts.With( krt.WithName("informer/Gateways"), krt.WithMetadata(krt.Metadata{ ClusterKRTMetadataKey: c.ID, @@ -241,7 +242,7 @@ func (c *Cluster) Run(localMeshConfig meshwatcher.WatcherCollection, debugger *k }), )...) - Nodes := krt.NewInformerFiltered[*corev1.Node](c.Client, kclient.Filter{ + Nodes := krt.NewFilteredInformer[*corev1.Node](c.Client, kclient.Filter{ ObjectFilter: c.Client.ObjectFilter(), ObjectTransform: kube.StripNodeUnusedFields, }, opts.With( @@ -251,7 +252,7 @@ func (c *Cluster) Run(localMeshConfig meshwatcher.WatcherCollection, debugger *k }), )...) - EndpointSlices := krt.NewInformerFiltered[*discovery.EndpointSlice](c.Client, kclient.Filter{ + EndpointSlices := krt.NewFilteredInformer[*discovery.EndpointSlice](c.Client, kclient.Filter{ ObjectFilter: c.Client.ObjectFilter(), }, opts.With( krt.WithName("informer/EndpointSlices"), diff --git a/pilot/pkg/serviceregistry/kube/controller/ambient/networks.go b/pilot/pkg/serviceregistry/kube/controller/ambient/networks.go index 3d2564a102..84a69d5d51 100644 --- a/pilot/pkg/serviceregistry/kube/controller/ambient/networks.go +++ b/pilot/pkg/serviceregistry/kube/controller/ambient/networks.go @@ -21,7 +21,6 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - "sigs.k8s.io/gateway-api/apis/v1beta1" "istio.io/api/label" "istio.io/istio/pilot/pkg/model" @@ -73,9 +72,9 @@ func (c networkCollections) HasSynced() bool { func buildGlobalNetworkCollections( clusters krt.Collection[*multicluster.Cluster], localNamespaces krt.Collection[*v1.Namespace], - localGateways krt.Collection[*v1beta1.Gateway], - gateways krt.Collection[krt.Collection[krt.ObjectWithCluster[*v1beta1.Gateway]]], - gatewaysByCluster krt.Index[cluster.ID, krt.Collection[krt.ObjectWithCluster[*v1beta1.Gateway]]], + localGateways krt.Collection[*gatewayv1.Gateway], + gateways krt.Collection[krt.Collection[krt.ObjectWithCluster[*gatewayv1.Gateway]]], + gatewaysByCluster krt.Index[cluster.ID, krt.Collection[krt.ObjectWithCluster[*gatewayv1.Gateway]]], options Options, opts krt.OptionsBuilder, ) networkCollections { @@ -121,7 +120,7 @@ func buildGlobalNetworkCollections( return []cluster.ID{o.ClusterID} }) - localNetworkGateways := krt.NewManyCollection(localGateways, func(ctx krt.HandlerContext, gw *v1beta1.Gateway) []krt.ObjectWithCluster[NetworkGateway] { + localNetworkGateways := krt.NewManyCollection(localGateways, func(ctx krt.HandlerContext, gw *gatewayv1.Gateway) []krt.ObjectWithCluster[NetworkGateway] { return k8sGatewayToNetworkGatewaysWithCluster(options.ClusterID, gw, options.ClusterID) }, opts.WithName("LocalNetworkGateways")...) @@ -147,7 +146,7 @@ func buildGlobalNetworkCollections( // sync.Once to ensure we only create the collection once and return that same value nwGateways := krt.NewManyCollection( gateways, - func(ctx krt.HandlerContext, gw krt.ObjectWithCluster[*v1beta1.Gateway]) []krt.ObjectWithCluster[NetworkGateway] { + func(ctx krt.HandlerContext, gw krt.ObjectWithCluster[*gatewayv1.Gateway]) []krt.ObjectWithCluster[NetworkGateway] { innerGw := ptr.Flatten(gw.Object) if innerGw == nil { return nil @@ -202,7 +201,7 @@ func buildGlobalNetworkCollections( func buildNetworkCollections( namespaces krt.Collection[*v1.Namespace], - gateways krt.Collection[*v1beta1.Gateway], + gateways krt.Collection[*gatewayv1.Gateway], options Options, opts krt.OptionsBuilder, ) networkCollections { @@ -238,7 +237,7 @@ func buildNetworkCollections( func k8sGatewayToNetworkGatewaysWithCluster( clusterID cluster.ID, - gw *v1beta1.Gateway, + gw *gatewayv1.Gateway, localClusterID cluster.ID, ) []krt.ObjectWithCluster[NetworkGateway] { gateways := k8sGatewayToNetworkGateways(clusterID, gw, localClusterID) @@ -253,7 +252,7 @@ func k8sGatewayToNetworkGatewaysWithCluster( return networkGateways } -func remoteK8sGatewayToNetworkGateways(clusterID cluster.ID, gw *v1beta1.Gateway) []NetworkGateway { +func remoteK8sGatewayToNetworkGateways(clusterID cluster.ID, gw *gatewayv1.Gateway) []NetworkGateway { netLabel := gw.GetLabels()[label.TopologyNetwork.Name] if netLabel == "" { return nil @@ -278,7 +277,7 @@ func remoteK8sGatewayToNetworkGateways(clusterID cluster.ID, gw *v1beta1.Gateway if addr.Type == nil { continue } - if addrType := *addr.Type; addrType != v1beta1.IPAddressType && addrType != v1beta1.HostnameAddressType { + if addrType := *addr.Type; addrType != gatewayv1.IPAddressType && addrType != gatewayv1.HostnameAddressType { continue } for _, l := range gw.Spec.Listeners { @@ -297,7 +296,7 @@ func remoteK8sGatewayToNetworkGateways(clusterID cluster.ID, gw *v1beta1.Gateway return gateways } -func localK8sGatewayToNetworkGateways(clusterID cluster.ID, gw *v1beta1.Gateway) []NetworkGateway { +func localK8sGatewayToNetworkGateways(clusterID cluster.ID, gw *gatewayv1.Gateway) []NetworkGateway { netLabel := gw.GetLabels()[label.TopologyNetwork.Name] if netLabel == "" { return nil @@ -321,7 +320,7 @@ func localK8sGatewayToNetworkGateways(clusterID cluster.ID, gw *v1beta1.Gateway) if addr.Type == nil { continue } - if addrType := *addr.Type; addrType != v1beta1.IPAddressType && addrType != v1beta1.HostnameAddressType { + if addrType := *addr.Type; addrType != gatewayv1.IPAddressType && addrType != gatewayv1.HostnameAddressType { continue } for _, l := range gw.Spec.Listeners { @@ -341,7 +340,7 @@ func localK8sGatewayToNetworkGateways(clusterID cluster.ID, gw *v1beta1.Gateway) return gateways } -func k8sGatewayToNetworkGateways(clusterID cluster.ID, gw *v1beta1.Gateway, localClusterID cluster.ID) []NetworkGateway { +func k8sGatewayToNetworkGateways(clusterID cluster.ID, gw *gatewayv1.Gateway, localClusterID cluster.ID) []NetworkGateway { if clusterID != localClusterID { // This is a gateway in a remote cluster, use different logic return remoteK8sGatewayToNetworkGateways(clusterID, gw) @@ -350,8 +349,8 @@ func k8sGatewayToNetworkGateways(clusterID cluster.ID, gw *v1beta1.Gateway, loca return localK8sGatewayToNetworkGateways(clusterID, gw) } -func fromGatewayBuilder(clusterID, localClusterID cluster.ID) krt.TransformationMulti[*v1beta1.Gateway, NetworkGateway] { - return func(ctx krt.HandlerContext, gw *v1beta1.Gateway) []NetworkGateway { +func fromGatewayBuilder(clusterID, localClusterID cluster.ID) krt.TransformationMulti[*gatewayv1.Gateway, NetworkGateway] { + return func(ctx krt.HandlerContext, gw *gatewayv1.Gateway) []NetworkGateway { return k8sGatewayToNetworkGateways(clusterID, gw, localClusterID) } } diff --git a/pilot/pkg/serviceregistry/kube/controller/ambient/policies.go b/pilot/pkg/serviceregistry/kube/controller/ambient/policies.go index 7d225e318d..44e32cd9b1 100644 --- a/pilot/pkg/serviceregistry/kube/controller/ambient/policies.go +++ b/pilot/pkg/serviceregistry/kube/controller/ambient/policies.go @@ -21,7 +21,7 @@ import ( "strings" corev1 "k8s.io/api/core/v1" - "sigs.k8s.io/gateway-api/apis/v1beta1" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" "istio.io/api/annotation" networkingclient "istio.io/client-go/pkg/apis/networking/v1" @@ -42,7 +42,7 @@ func WaypointPolicyStatusCollection( waypoints krt.Collection[Waypoint], services krt.Collection[*corev1.Service], serviceEntries krt.Collection[*networkingclient.ServiceEntry], - gatewayClasses krt.Collection[*v1beta1.GatewayClass], + gatewayClasses krt.Collection[*gatewayv1.GatewayClass], meshConfig krt.Singleton[MeshConfig], namespaces krt.Collection[*corev1.Namespace], opts krt.OptionsBuilder, @@ -73,7 +73,7 @@ func WaypointPolicyStatusCollection( reason := "unknown" bound := false switch target.GetKind() { - case gvk.GatewayClass_v1.Kind: + case gvk.GatewayClass.Kind: // first verify the AP is in the root namespace, if not it's ignored if namespace != rootNs { reason = model.WaypointPolicyReasonInvalid diff --git a/pilot/pkg/serviceregistry/kube/controller/ambient/sidecar_interop.go b/pilot/pkg/serviceregistry/kube/controller/ambient/sidecar_interop.go index 564b71bc0c..92746bc457 100644 --- a/pilot/pkg/serviceregistry/kube/controller/ambient/sidecar_interop.go +++ b/pilot/pkg/serviceregistry/kube/controller/ambient/sidecar_interop.go @@ -33,12 +33,31 @@ import ( // This is to map to the eventual EDS structure. type serviceEDS struct { ServiceKey string - WaypointInstance []*workloadapi.Workload + WaypointInstance []model.WorkloadInfo UseWaypoint bool } -func (w serviceEDS) ResourceName() string { - return w.ServiceKey +func (s serviceEDS) ResourceName() string { + return s.ServiceKey +} + +func (s serviceEDS) Equals(other serviceEDS) bool { + if s.ServiceKey != other.ServiceKey { + return false + } + if s.UseWaypoint != other.UseWaypoint { + return false + } + if len(s.WaypointInstance) != len(other.WaypointInstance) { + return false + } + // assumes builder sorted the slices + for i := range s.WaypointInstance { + if !s.WaypointInstance[i].Equals(other.WaypointInstance[i]) { + return false + } + } + return true } // RegisterEdsShim handles triggering xDS events when Envoy EDS needs to change. @@ -93,12 +112,14 @@ func RegisterEdsShim( waypointServiceKey = waypointSvc.ResourceName() } workloads := krt.Fetch(ctx, Workloads, krt.FilterIndex(WorkloadsByServiceKey, waypointServiceKey)) + // for comparison in Equals + workloads = slices.SortBy(workloads, func(i model.WorkloadInfo) string { + return i.Workload.Uid + }) return &serviceEDS{ - ServiceKey: svc.ResourceName(), - UseWaypoint: useWaypoint, - WaypointInstance: slices.Map(workloads, func(e model.WorkloadInfo) *workloadapi.Workload { - return e.Workload - }), + ServiceKey: svc.ResourceName(), + UseWaypoint: useWaypoint, + WaypointInstance: workloads, } }, opts.WithName("ServiceEds")...) diff --git a/pilot/pkg/serviceregistry/kube/controller/ambient/waypoints.go b/pilot/pkg/serviceregistry/kube/controller/ambient/waypoints.go index 810f81eb79..50665d62a1 100644 --- a/pilot/pkg/serviceregistry/kube/controller/ambient/waypoints.go +++ b/pilot/pkg/serviceregistry/kube/controller/ambient/waypoints.go @@ -26,7 +26,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - "sigs.k8s.io/gateway-api/apis/v1beta1" "istio.io/api/annotation" "istio.io/api/label" @@ -218,11 +217,11 @@ func (w Waypoint) ResourceName() string { func gatewayToWaypointTransform( pods krt.Collection[*v1.Pod], - gatewayClasses krt.Collection[*v1beta1.GatewayClass], + gatewayClasses krt.Collection[*gatewayv1.GatewayClass], networkGetter func(ctx krt.HandlerContext) network.ID, -) func(ctx krt.HandlerContext, gateway *v1beta1.Gateway) *Waypoint { +) func(ctx krt.HandlerContext, gateway *gatewayv1.Gateway) *Waypoint { podsByNamespace := krt.NewNamespaceIndex(pods) - return func(ctx krt.HandlerContext, gateway *v1beta1.Gateway) *Waypoint { + return func(ctx krt.HandlerContext, gateway *gatewayv1.Gateway) *Waypoint { if len(gateway.Status.Addresses) == 0 { // gateway.Status.Addresses should only be populated once the Waypoint's deployment has at least 1 ready pod, it should never be removed after going ready // ignore Kubernetes Gateways which aren't waypoints @@ -261,7 +260,7 @@ func GlobalWaypointsCollection( localCluster *multicluster.Cluster, localWaypoints krt.Collection[Waypoint], clusters krt.Collection[*multicluster.Cluster], - gatewayClasses krt.Collection[*v1beta1.GatewayClass], + gatewayClasses krt.Collection[*gatewayv1.GatewayClass], globalNetworks networkCollections, opts krt.OptionsBuilder, ) krt.Collection[krt.Collection[Waypoint]] { @@ -276,7 +275,7 @@ func GlobalWaypointsCollection( podsByNamespace := krt.NewNamespaceIndex(pods) gateways := c.Gateways() - clusterWaypoints := krt.NewCollection(gateways, func(ctx krt.HandlerContext, gateway *v1beta1.Gateway) *Waypoint { + clusterWaypoints := krt.NewCollection(gateways, func(ctx krt.HandlerContext, gateway *gatewayv1.Gateway) *Waypoint { if len(gateway.Status.Addresses) == 0 { // gateway.Status.Addresses should only be populated once the Waypoint's deployment has at least 1 ready pod, it should never be removed after going ready // ignore Kubernetes Gateways which aren't waypoints @@ -323,8 +322,8 @@ func GlobalWaypointsCollection( func (a *index) WaypointsCollection( clusterID cluster.ID, - gateways krt.Collection[*v1beta1.Gateway], - gatewayClasses krt.Collection[*v1beta1.GatewayClass], + gateways krt.Collection[*gatewayv1.Gateway], + gatewayClasses krt.Collection[*gatewayv1.GatewayClass], pods krt.Collection[*v1.Pod], opts krt.OptionsBuilder, ) krt.Collection[Waypoint] { @@ -343,7 +342,7 @@ func (a *index) WaypointsCollection( ) } -func makeInboundBinding(gateway *v1beta1.Gateway, gatewayClass *v1beta1.GatewayClass) *InboundBinding { +func makeInboundBinding(gateway *gatewayv1.Gateway, gatewayClass *gatewayv1.GatewayClass) *InboundBinding { ann, ok := getGatewayOrGatewayClassAnnotation(gateway, gatewayClass) if !ok { return nil @@ -385,7 +384,7 @@ func makeInboundBinding(gateway *v1beta1.Gateway, gatewayClass *v1beta1.GatewayC } } -func getGatewayOrGatewayClassAnnotation(gateway *v1beta1.Gateway, class *v1beta1.GatewayClass) (string, bool) { +func getGatewayOrGatewayClassAnnotation(gateway *gatewayv1.Gateway, class *gatewayv1.GatewayClass) (string, bool) { // Gateway > GatewayClass an, ok := gateway.Annotations[annotation.AmbientWaypointInboundBinding.Name] if ok { @@ -401,8 +400,8 @@ func getGatewayOrGatewayClassAnnotation(gateway *v1beta1.Gateway, class *v1beta1 } func makeWaypoint( - gateway *v1beta1.Gateway, - gatewayClass *v1beta1.GatewayClass, + gateway *gatewayv1.Gateway, + gatewayClass *gatewayv1.GatewayClass, serviceAccounts []string, trafficType string, netw network.ID, @@ -419,7 +418,7 @@ func makeWaypoint( } type WaypointSelector struct { - FromNamespaces v1beta1.FromNamespaces + FromNamespaces gatewayv1.FromNamespaces Selector labels.Selector } @@ -476,7 +475,7 @@ func (w *Waypoint) GetAddress() *workloadapi.GatewayAddress { // makeAllowedRoutes returns a WaypointSelector that matches the listener with the given binding // if we don't have a binding we use the default HBONE listener // if we have a binding we use the protocol and port defined in the binding -func makeAllowedRoutes(gateway *v1beta1.Gateway, binding *InboundBinding) WaypointSelector { +func makeAllowedRoutes(gateway *gatewayv1.Gateway, binding *InboundBinding) WaypointSelector { // First see if we can find a bound listener if listener, found := findBoundListener(gateway, binding); found { return makeWaypointSelector(listener) @@ -496,17 +495,17 @@ func makeAllowedRoutes(gateway *v1beta1.Gateway, binding *InboundBinding) Waypoi } } -func findBoundListener(gateway *v1beta1.Gateway, binding *InboundBinding) (v1beta1.Listener, bool) { +func findBoundListener(gateway *gatewayv1.Gateway, binding *InboundBinding) (gatewayv1.Listener, bool) { if binding == nil { - return v1beta1.Listener{}, false + return gatewayv1.Listener{}, false } - var match func(l v1beta1.Listener) bool + var match func(l gatewayv1.Listener) bool if binding.Port != 0 { - match = func(l v1beta1.Listener) bool { + match = func(l gatewayv1.Listener) bool { return l.Port == gatewayv1.PortNumber(binding.Port) } } else if binding.Protocol == workloadapi.ApplicationTunnel_PROXY { - match = func(l v1beta1.Listener) bool { + match = func(l gatewayv1.Listener) bool { return l.Protocol == constants.WaypointSandwichListenerProxyProtocol } } @@ -515,10 +514,10 @@ func findBoundListener(gateway *v1beta1.Gateway, binding *InboundBinding) (v1bet return l, true } } - return v1beta1.Listener{}, false + return gatewayv1.Listener{}, false } -func makeWaypointSelector(l v1beta1.Listener) WaypointSelector { +func makeWaypointSelector(l gatewayv1.Listener) WaypointSelector { if l.AllowedRoutes == nil || l.AllowedRoutes.Namespaces == nil { return WaypointSelector{ FromNamespaces: gatewayv1.NamespacesFromSame, @@ -533,9 +532,9 @@ func makeWaypointSelector(l v1beta1.Listener) WaypointSelector { } } -func getGatewayAddress(gw *v1beta1.Gateway, netw network.ID) *workloadapi.GatewayAddress { +func getGatewayAddress(gw *gatewayv1.Gateway, netw network.ID) *workloadapi.GatewayAddress { for _, addr := range gw.Status.Addresses { - if addr.Type != nil && *addr.Type == v1beta1.HostnameAddressType { + if addr.Type != nil && *addr.Type == gatewayv1.HostnameAddressType { // Prefer hostname from status, if we can find it. // Hostnames are a more reliable lookup key than IP; hostname is already the unique key for services, and IPs can be re-allocated. // Additionally, a destination can have multiple IPs, which makes handling more challenging. For example, was the IPv4 address @@ -554,7 +553,7 @@ func getGatewayAddress(gw *v1beta1.Gateway, netw network.ID) *workloadapi.Gatewa } // Fallback to IP address for _, addr := range gw.Status.Addresses { - if addr.Type != nil && *addr.Type == v1beta1.IPAddressType { + if addr.Type != nil && *addr.Type == gatewayv1.IPAddressType { ip, err := netip.ParseAddr(addr.Value) if err != nil { log.Warnf("parsed invalid IP address %q: %v", addr.Value, err) diff --git a/pilot/pkg/serviceregistry/kube/controller/ambient/waypoints_test.go b/pilot/pkg/serviceregistry/kube/controller/ambient/waypoints_test.go index 540a40a64f..64abca3d83 100644 --- a/pilot/pkg/serviceregistry/kube/controller/ambient/waypoints_test.go +++ b/pilot/pkg/serviceregistry/kube/controller/ambient/waypoints_test.go @@ -20,7 +20,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" "istio.io/api/annotation" "istio.io/istio/pkg/config/constants" @@ -29,34 +28,34 @@ import ( ) func TestMakeAllowedRoutes(t *testing.T) { - istioWaypointClass := &gatewayv1beta1.GatewayClass{ + istioWaypointClass := &gatewayv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "istio-waypoint", }, - Spec: gatewayv1beta1.GatewayClassSpec{ + Spec: gatewayv1.GatewayClassSpec{ ControllerName: constants.ManagedGatewayMeshController, }, } - sandwichedWaypointClass := &gatewayv1beta1.GatewayClass{ + sandwichedWaypointClass := &gatewayv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: "sandwiched-waypoint", }, - Spec: gatewayv1beta1.GatewayClassSpec{ + Spec: gatewayv1.GatewayClassSpec{ ControllerName: "sandwiched-controller", }, } tests := []struct { name string - gateway *gatewayv1beta1.Gateway - gatewayClass *gatewayv1beta1.GatewayClass + gateway *gatewayv1.Gateway + gatewayClass *gatewayv1.GatewayClass expected WaypointSelector }{ { name: "istio-waypoint defaults to same namespace", - gateway: &gatewayv1beta1.Gateway{ - Spec: gatewayv1beta1.GatewaySpec{ + gateway: &gatewayv1.Gateway{ + Spec: gatewayv1.GatewaySpec{ GatewayClassName: gatewayv1.ObjectName(istioWaypointClass.Name), - Listeners: []gatewayv1beta1.Listener{}, + Listeners: []gatewayv1.Listener{}, }, }, gatewayClass: istioWaypointClass, @@ -66,11 +65,11 @@ func TestMakeAllowedRoutes(t *testing.T) { }, { name: "istio-waypoint matching listener with no allowed routes", - gateway: &gatewayv1beta1.Gateway{ - Spec: gatewayv1beta1.GatewaySpec{ - Listeners: []gatewayv1beta1.Listener{ + gateway: &gatewayv1.Gateway{ + Spec: gatewayv1.GatewaySpec{ + Listeners: []gatewayv1.Listener{ { - Protocol: gatewayv1beta1.ProtocolType("HBONE"), + Protocol: gatewayv1.ProtocolType("HBONE"), Port: 15008, }, }, @@ -83,13 +82,13 @@ func TestMakeAllowedRoutes(t *testing.T) { }, { name: "istio-waypoint matching listener with allowed routes", - gateway: &gatewayv1beta1.Gateway{ - Spec: gatewayv1beta1.GatewaySpec{ - Listeners: []gatewayv1beta1.Listener{ + gateway: &gatewayv1.Gateway{ + Spec: gatewayv1.GatewaySpec{ + Listeners: []gatewayv1.Listener{ { - Protocol: gatewayv1beta1.ProtocolType("HBONE"), + Protocol: gatewayv1.ProtocolType("HBONE"), Port: 15008, - AllowedRoutes: &gatewayv1beta1.AllowedRoutes{ + AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{ From: ptr.Of(gatewayv1.NamespacesFromAll), }, @@ -105,13 +104,13 @@ func TestMakeAllowedRoutes(t *testing.T) { }, { name: "istio-waypoint matching listener with selector", - gateway: &gatewayv1beta1.Gateway{ - Spec: gatewayv1beta1.GatewaySpec{ - Listeners: []gatewayv1beta1.Listener{ + gateway: &gatewayv1.Gateway{ + Spec: gatewayv1.GatewaySpec{ + Listeners: []gatewayv1.Listener{ { - Protocol: gatewayv1beta1.ProtocolType("HBONE"), + Protocol: gatewayv1.ProtocolType("HBONE"), Port: 15008, - AllowedRoutes: &gatewayv1beta1.AllowedRoutes{ + AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{ From: ptr.Of(gatewayv1.NamespacesFromSelector), Selector: &metav1.LabelSelector{ @@ -130,17 +129,17 @@ func TestMakeAllowedRoutes(t *testing.T) { }, { name: "sandwiched waypoint defaults to same namespace", - gateway: &gatewayv1beta1.Gateway{ + gateway: &gatewayv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ annotation.AmbientWaypointInboundBinding.Name: "PROXY/15088", }, }, - Spec: gatewayv1beta1.GatewaySpec{ + Spec: gatewayv1.GatewaySpec{ GatewayClassName: gatewayv1.ObjectName(sandwichedWaypointClass.Name), - Listeners: []gatewayv1beta1.Listener{ + Listeners: []gatewayv1.Listener{ { - Protocol: gatewayv1beta1.ProtocolType(constants.WaypointSandwichListenerProxyProtocol), + Protocol: gatewayv1.ProtocolType(constants.WaypointSandwichListenerProxyProtocol), Port: 15088, }, }, @@ -153,20 +152,20 @@ func TestMakeAllowedRoutes(t *testing.T) { }, { name: "sandwiched waypoint with listener with allowed routes", - gateway: &gatewayv1beta1.Gateway{ + gateway: &gatewayv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ annotation.AmbientWaypointInboundBinding.Name: "PROXY/15088", }, }, - Spec: gatewayv1beta1.GatewaySpec{ + Spec: gatewayv1.GatewaySpec{ GatewayClassName: gatewayv1.ObjectName(sandwichedWaypointClass.Name), - Listeners: []gatewayv1beta1.Listener{ + Listeners: []gatewayv1.Listener{ { Name: "should be ignored", - Protocol: gatewayv1beta1.ProtocolType(constants.WaypointSandwichListenerProxyProtocol), + Protocol: gatewayv1.ProtocolType(constants.WaypointSandwichListenerProxyProtocol), Port: 15089, - AllowedRoutes: &gatewayv1beta1.AllowedRoutes{ + AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{ From: ptr.Of(gatewayv1.NamespacesFromSame), }, @@ -174,9 +173,9 @@ func TestMakeAllowedRoutes(t *testing.T) { }, { Name: "should be used", - Protocol: gatewayv1beta1.ProtocolType(constants.WaypointSandwichListenerProxyProtocol), + Protocol: gatewayv1.ProtocolType(constants.WaypointSandwichListenerProxyProtocol), Port: 15088, - AllowedRoutes: &gatewayv1beta1.AllowedRoutes{ + AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{ From: ptr.Of(gatewayv1.NamespacesFromAll), }, @@ -193,25 +192,25 @@ func TestMakeAllowedRoutes(t *testing.T) { }, { name: "sandwiched waypoint with listener with allowed routes", - gateway: &gatewayv1beta1.Gateway{ + gateway: &gatewayv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ annotation.AmbientWaypointInboundBinding.Name: "PROXY/15088", }, }, - Spec: gatewayv1beta1.GatewaySpec{ + Spec: gatewayv1.GatewaySpec{ GatewayClassName: gatewayv1.ObjectName(sandwichedWaypointClass.Name), - Listeners: []gatewayv1beta1.Listener{ + Listeners: []gatewayv1.Listener{ { Name: "should be ignored", - Protocol: gatewayv1beta1.ProtocolType(constants.WaypointSandwichListenerProxyProtocol), + Protocol: gatewayv1.ProtocolType(constants.WaypointSandwichListenerProxyProtocol), Port: 15089, }, { Name: "should be used", - Protocol: gatewayv1beta1.ProtocolType(constants.WaypointSandwichListenerProxyProtocol), + Protocol: gatewayv1.ProtocolType(constants.WaypointSandwichListenerProxyProtocol), Port: 15088, - AllowedRoutes: &gatewayv1beta1.AllowedRoutes{ + AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{ From: ptr.Of(gatewayv1.NamespacesFromAll), }, @@ -228,20 +227,20 @@ func TestMakeAllowedRoutes(t *testing.T) { }, { name: "sandwiched waypoint with listener with allowed routes on port 0", - gateway: &gatewayv1beta1.Gateway{ + gateway: &gatewayv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ annotation.AmbientWaypointInboundBinding.Name: "PROXY", }, }, - Spec: gatewayv1beta1.GatewaySpec{ + Spec: gatewayv1.GatewaySpec{ GatewayClassName: gatewayv1.ObjectName(sandwichedWaypointClass.Name), - Listeners: []gatewayv1beta1.Listener{ + Listeners: []gatewayv1.Listener{ { Name: "should be used", - Protocol: gatewayv1beta1.ProtocolType(constants.WaypointSandwichListenerProxyProtocol), + Protocol: gatewayv1.ProtocolType(constants.WaypointSandwichListenerProxyProtocol), Port: 15015, - AllowedRoutes: &gatewayv1beta1.AllowedRoutes{ + AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{ From: ptr.Of(gatewayv1.NamespacesFromAll), }, diff --git a/pilot/pkg/serviceregistry/kube/controller/ambient/workloads.go b/pilot/pkg/serviceregistry/kube/controller/ambient/workloads.go index 4c221717c6..5d6930a32e 100644 --- a/pilot/pkg/serviceregistry/kube/controller/ambient/workloads.go +++ b/pilot/pkg/serviceregistry/kube/controller/ambient/workloads.go @@ -115,7 +115,8 @@ func (a *index) WorkloadsCollection( opts.WithName("EndpointSliceWorkloads")...) NetworkGatewayWorkloads := krt.NewManyFromNothing[model.WorkloadInfo](func(ctx krt.HandlerContext) []model.WorkloadInfo { - return slices.Map(a.LookupAllNetworkGateway(ctx), convertGateway) + meshCfg := krt.FetchOne(ctx, meshConfig.AsCollection()) + return slices.Map(a.LookupAllNetworkGateway(ctx), convertGateway(meshCfg)) }, opts.WithName("NetworkGatewayWorkloads")...) Workloads := krt.JoinCollection( @@ -263,10 +264,11 @@ func MergedGlobalWorkloadsCollection( ) GlobalNetworkGatewayWorkloads := krt.NewManyFromNothing[model.WorkloadInfo](func(ctx krt.HandlerContext) []model.WorkloadInfo { + meshCfg := krt.FetchOne(ctx, meshConfig.AsCollection()) return slices.Map(LookupAllNetworkGateway( ctx, globalNetworks.NetworkGateways, - ), convertGateway) + ), convertGateway(meshCfg)) }, opts.WithName("LocalNetworkGatewayWorkloads")...) LocalNetworkGatewayWorkloadsWithCluster := krt.MapCollection( GlobalNetworkGatewayWorkloads, @@ -1544,22 +1546,25 @@ func gatewayUID(gw model.NetworkGateway) string { // convertGateway always converts a NetworkGateway into a Workload. // Workloads have a NetworkGateway field, which is effectively a pointer to another object (Service or Workload); in order // to facilitate this we need to translate our Gateway model down into a WorkloadInfo ztunnel can understand. -func convertGateway(gw NetworkGateway) model.WorkloadInfo { - wl := &workloadapi.Workload{ - Uid: gatewayUID(gw.NetworkGateway), - Name: gatewayUID(gw.NetworkGateway), - ServiceAccount: gw.ServiceAccount.Name, - Namespace: gw.ServiceAccount.Namespace, - Network: gw.Network.String(), - } +func convertGateway(mesh *MeshConfig) func(gw NetworkGateway) model.WorkloadInfo { + return func(gw NetworkGateway) model.WorkloadInfo { + wl := &workloadapi.Workload{ + Uid: gatewayUID(gw.NetworkGateway), + Name: gatewayUID(gw.NetworkGateway), + ServiceAccount: gw.ServiceAccount.Name, + Namespace: gw.ServiceAccount.Namespace, + Network: gw.Network.String(), + TrustDomain: pickTrustDomain(mesh), + } - if ip, err := netip.ParseAddr(gw.Addr); err == nil { - wl.Addresses = append(wl.Addresses, ip.AsSlice()) - } else { - wl.Hostname = gw.Addr - } + if ip, err := netip.ParseAddr(gw.Addr); err == nil { + wl.Addresses = append(wl.Addresses, ip.AsSlice()) + } else { + wl.Hostname = gw.Addr + } - return precomputeWorkload(model.WorkloadInfo{Workload: wl}) + return precomputeWorkload(model.WorkloadInfo{Workload: wl}) + } } func getNetworkGatewayAddress( diff --git a/pilot/pkg/serviceregistry/kube/controller/ambient/workloads_test.go b/pilot/pkg/serviceregistry/kube/controller/ambient/workloads_test.go index c6f5cb6b83..58f3c4a083 100644 --- a/pilot/pkg/serviceregistry/kube/controller/ambient/workloads_test.go +++ b/pilot/pkg/serviceregistry/kube/controller/ambient/workloads_test.go @@ -24,7 +24,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - "sigs.k8s.io/gateway-api/apis/v1beta1" "istio.io/api/annotation" "istio.io/api/label" @@ -1841,7 +1840,7 @@ func newAmbientUnitTest(t test.Failer) *index { Labels: map[string]string{label.TopologyNetwork.Name: testNW}, }, }, - &v1beta1.Gateway{ + &gatewayv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "remote-network-ip", Namespace: "ns-gtw", @@ -1852,9 +1851,9 @@ func newAmbientUnitTest(t test.Failer) *index { label.TopologyNetwork.Name: "remote-network", }, }, - Spec: v1beta1.GatewaySpec{ + Spec: gatewayv1.GatewaySpec{ GatewayClassName: "istio-remote", - Listeners: []v1beta1.Listener{ + Listeners: []gatewayv1.Listener{ { Name: "cross-network", Port: 15008, @@ -1862,7 +1861,7 @@ func newAmbientUnitTest(t test.Failer) *index { }, }, }, - Status: v1beta1.GatewayStatus{ + Status: gatewayv1.GatewayStatus{ Addresses: []gatewayv1.GatewayStatusAddress{ { Type: ptr.Of(gatewayv1.IPAddressType), @@ -1871,7 +1870,7 @@ func newAmbientUnitTest(t test.Failer) *index { }, }, }, - &v1beta1.Gateway{ + &gatewayv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "remote-network-hostname", Namespace: "ns-gtw", @@ -1882,9 +1881,9 @@ func newAmbientUnitTest(t test.Failer) *index { label.TopologyNetwork.Name: "remote-network-hostname", }, }, - Spec: v1beta1.GatewaySpec{ + Spec: gatewayv1.GatewaySpec{ GatewayClassName: "istio-remote", - Listeners: []v1beta1.Listener{ + Listeners: []gatewayv1.Listener{ { Name: "cross-network", Port: 15008, @@ -1892,7 +1891,7 @@ func newAmbientUnitTest(t test.Failer) *index { }, }, }, - Status: v1beta1.GatewayStatus{ + Status: gatewayv1.GatewayStatus{ Addresses: []gatewayv1.GatewayStatusAddress{ { Type: ptr.Of(gatewayv1.HostnameAddressType), @@ -1904,7 +1903,7 @@ func newAmbientUnitTest(t test.Failer) *index { }) networks := buildNetworkCollections( krttest.GetMockCollection[*v1.Namespace](mock), - krttest.GetMockCollection[*v1beta1.Gateway](mock), + krttest.GetMockCollection[*gatewayv1.Gateway](mock), Options{ SystemNamespace: systemNS, ClusterID: testC, diff --git a/pilot/pkg/serviceregistry/kube/controller/controller.go b/pilot/pkg/serviceregistry/kube/controller/controller.go index f96ea48391..d6a8ddaaf5 100644 --- a/pilot/pkg/serviceregistry/kube/controller/controller.go +++ b/pilot/pkg/serviceregistry/kube/controller/controller.go @@ -234,7 +234,7 @@ type Controller struct { // initialSyncTimedout is set to true after performing an initial processing timed out. initialSyncTimedout *atomic.Bool - meshWatcher mesh.Watcher + meshWatcher mesh.RestrictedConfigWatcher podsClient kclient.Client[*v1.Pod] @@ -293,6 +293,7 @@ func NewController(kubeClient kubelib.Client, options Options) *Controller { c.podsClient = kclient.NewFiltered[*v1.Pod](kubeClient, kclient.Filter{ ObjectFilter: kubeClient.ObjectFilter(), ObjectTransform: kubelib.StripPodUnusedFields, + FieldSelector: "status.phase!=Failed", }) c.pods = newPodCache(c, c.podsClient, func(key types.NamespacedName) { c.queue.Push(func() error { @@ -330,7 +331,7 @@ func NewController(kubeClient kubelib.Client, options Options) *Controller { c.exports = newServiceExportCache(c) c.imports = newServiceImportCache(c) - c.meshWatcher = options.MeshWatcher + c.meshWatcher = mesh.NewRestrictedConfigWatcher(options.MeshWatcher) if c.opts.MeshNetworksWatcher != nil { c.networksHandlerRegistration = c.opts.MeshNetworksWatcher.AddNetworksHandler(func() { c.reloadMeshNetworks() @@ -419,7 +420,7 @@ func (c *Controller) onServiceEvent(pre, curr *v1.Service, event model.Event) er log.Debugf("Handle event %s for service %s in namespace %s", event, curr.Name, curr.Namespace) // Create the standard (cluster.local) service. - svcConv := kube.ConvertService(*curr, c.opts.DomainSuffix, c.Cluster(), c.meshWatcher.Mesh()) + svcConv := kube.ConvertService(*curr, c.opts.DomainSuffix, c.Cluster(), c.meshWatcher.TrustDomain()) switch event { case model.EventDelete: diff --git a/pilot/pkg/serviceregistry/kube/controller/controller_test.go b/pilot/pkg/serviceregistry/kube/controller/controller_test.go index cb74099daa..16b1637a43 100644 --- a/pilot/pkg/serviceregistry/kube/controller/controller_test.go +++ b/pilot/pkg/serviceregistry/kube/controller/controller_test.go @@ -2813,16 +2813,16 @@ func TestServiceUpdateNeedsPush(t *testing.T) { name: "target ports changed", prev: &svc, curr: &updatedSvc, - prevConv: kube.ConvertService(svc, constants.DefaultClusterLocalDomain, "", nil), - currConv: kube.ConvertService(updatedSvc, constants.DefaultClusterLocalDomain, "", nil), + prevConv: kube.ConvertService(svc, constants.DefaultClusterLocalDomain, "", ""), + currConv: kube.ConvertService(updatedSvc, constants.DefaultClusterLocalDomain, "", ""), expect: true, }, testcase{ name: "target ports unchanged", prev: &svc, curr: &svc, - prevConv: kube.ConvertService(svc, constants.DefaultClusterLocalDomain, "", nil), - currConv: kube.ConvertService(svc, constants.DefaultClusterLocalDomain, "", nil), + prevConv: kube.ConvertService(svc, constants.DefaultClusterLocalDomain, "", ""), + currConv: kube.ConvertService(svc, constants.DefaultClusterLocalDomain, "", ""), expect: false, }) diff --git a/pilot/pkg/serviceregistry/kube/controller/endpoint_builder.go b/pilot/pkg/serviceregistry/kube/controller/endpoint_builder.go index f727858705..3a32f33e24 100644 --- a/pilot/pkg/serviceregistry/kube/controller/endpoint_builder.go +++ b/pilot/pkg/serviceregistry/kube/controller/endpoint_builder.go @@ -52,7 +52,7 @@ func (c *Controller) NewEndpointBuilder(pod *v1.Pod) *EndpointBuilder { var podLabels labels.Instance if pod != nil { locality = c.getPodLocality(pod) - sa = kube.SecureNamingSAN(pod, c.meshWatcher.Mesh()) + sa = kube.SecureNamingSAN(pod, c.meshWatcher.TrustDomain()) podLabels = pod.Labels namespace = pod.Namespace subdomain = pod.Spec.Subdomain diff --git a/pilot/pkg/serviceregistry/kube/controller/multicluster.go b/pilot/pkg/serviceregistry/kube/controller/multicluster.go index f87f37b199..88e80297b4 100644 --- a/pilot/pkg/serviceregistry/kube/controller/multicluster.go +++ b/pilot/pkg/serviceregistry/kube/controller/multicluster.go @@ -33,9 +33,12 @@ import ( "istio.io/istio/pilot/pkg/serviceregistry/provider" "istio.io/istio/pilot/pkg/serviceregistry/serviceentry" "istio.io/istio/pkg/backoff" + "istio.io/istio/pkg/config/mesh/kubemesh" + "istio.io/istio/pkg/config/mesh/meshwatcher" "istio.io/istio/pkg/config/schema/collection" "istio.io/istio/pkg/config/schema/collections" kubelib "istio.io/istio/pkg/kube" + "istio.io/istio/pkg/kube/krt" "istio.io/istio/pkg/kube/multicluster" "istio.io/istio/pkg/webhooks" ) @@ -123,6 +126,26 @@ func NewMulticluster( } log.Infof("Initializing Kubernetes service registry %q", options.ClusterID) options.ConfigCluster = configCluster + + // Create per-cluster mesh watcher for remote clusters + // This allows controllers to detect cluster-specific settings that may be relevant for the local proxies, e.g. trust domain. + if !configCluster { + meshConfigMapName := mc.getMeshConfigMapName() + meshSource := kubemesh.NewConfigMapSource( + client, + options.SystemNamespace, + meshConfigMapName, + kubemesh.MeshConfigKey, + krt.NewOptionsBuilder(stop, "", nil), + ) + remoteMeshCollection := meshwatcher.NewCollection( + krt.NewOptionsBuilder(stop, "", nil), + meshSource, + ) + options.MeshWatcher = meshwatcher.ConfigAdapter(remoteMeshCollection) + log.Infof("Created mesh watcher for remote cluster %q", cluster.ID) + } + kubeRegistry := NewController(client, options) kubeController := &kubeController{ MeshServiceController: opts.MeshServiceController, @@ -136,6 +159,15 @@ func NewMulticluster( return mc } +// getMeshConfigMapName returns the mesh ConfigMap name based on the revision +func (m *Multicluster) getMeshConfigMapName() string { + name := "istio" + if m.revision == "" || m.revision == "default" { + return name + } + return name + "-" + m.revision +} + // initializeCluster initializes the cluster by setting various handlers. func (m *Multicluster) initializeCluster(cluster *multicluster.Cluster, kubeController *kubeController, kubeRegistry *Controller, options Options, configCluster bool, clusterStopCh <-chan struct{}, diff --git a/pilot/pkg/serviceregistry/kube/controller/network.go b/pilot/pkg/serviceregistry/kube/controller/network.go index e2b7366d8a..389c94739f 100644 --- a/pilot/pkg/serviceregistry/kube/controller/network.go +++ b/pilot/pkg/serviceregistry/kube/controller/network.go @@ -23,7 +23,7 @@ import ( v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/gateway-api/apis/v1beta1" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" "istio.io/api/label" "istio.io/istio/pilot/pkg/features" @@ -46,7 +46,7 @@ type networkManager struct { ranger cidranger.Ranger clusterID cluster.ID - gatewayResourceClient kclient.Informer[*v1beta1.Gateway] + gatewayResourceClient kclient.Informer[*gatewayv1.Gateway] meshNetworksWatcher mesh.NetworksWatcher // Network name for to be used when the meshNetworks fromRegistry nor network label on pod is specified @@ -83,7 +83,7 @@ func initNetworkManager(c *Controller, options Options) *networkManager { } // initialize the gateway resource client when any feature that uses it is enabled if features.MultiNetworkGatewayAPI { - n.gatewayResourceClient = kclient.NewDelayedInformer[*v1beta1.Gateway](c.client, gvr.KubernetesGateway, kubetypes.StandardInformer, kubetypes.Filter{}) + n.gatewayResourceClient = kclient.NewDelayedInformer[*gatewayv1.Gateway](c.client, gvr.KubernetesGateway, kubetypes.StandardInformer, kubetypes.Filter{}) // conditionally register this handler registerHandlers(c, n.gatewayResourceClient, "Gateways", n.handleGatewayResource, nil) } @@ -363,7 +363,7 @@ func (n *networkManager) getGatewayDetails(svc *model.Service) []model.NetworkGa // handleGateway resource adds a NetworkGateway for each combination of address and auto-passthrough listener // discovering duplicates from the generated Service is not a huge concern as we de-duplicate in NetworkGateways // which returns a set, although it's not totally efficient. -func (n *networkManager) handleGatewayResource(_ *v1beta1.Gateway, gw *v1beta1.Gateway, event model.Event) error { +func (n *networkManager) handleGatewayResource(_ *gatewayv1.Gateway, gw *gatewayv1.Gateway, event model.Event) error { if nw := gw.GetLabels()[label.TopologyNetwork.Name]; nw == "" { return nil } @@ -392,7 +392,7 @@ func (n *networkManager) handleGatewayResource(_ *v1beta1.Gateway, gw *v1beta1.G return nil } - autoPassthrough := func(l v1beta1.Listener) bool { + autoPassthrough := func(l gatewayv1.Listener) bool { return kube.IsAutoPassthrough(gw.GetLabels(), l) } @@ -409,7 +409,7 @@ func (n *networkManager) handleGatewayResource(_ *v1beta1.Gateway, gw *v1beta1.G if addr.Type == nil { continue } - if addrType := *addr.Type; addrType != v1beta1.IPAddressType && addrType != v1beta1.HostnameAddressType { + if addrType := *addr.Type; addrType != gatewayv1.IPAddressType && addrType != gatewayv1.HostnameAddressType { continue } for _, l := range slices.Filter(gw.Spec.Listeners, autoPassthrough) { diff --git a/pilot/pkg/serviceregistry/kube/controller/network_test.go b/pilot/pkg/serviceregistry/kube/controller/network_test.go index 4e9cbdbe46..a1706703cb 100644 --- a/pilot/pkg/serviceregistry/kube/controller/network_test.go +++ b/pilot/pkg/serviceregistry/kube/controller/network_test.go @@ -24,7 +24,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" k8sv1 "sigs.k8s.io/gateway-api/apis/v1" - "sigs.k8s.io/gateway-api/apis/v1beta1" "istio.io/api/annotation" "istio.io/api/label" @@ -158,44 +157,44 @@ func removeLabeledServiceGateway(t *testing.T, c *FakeController) { // and it does so on an IP and a hostname func addOrUpdateGatewayResource(t *testing.T, c *FakeController, customPort int) { passthroughMode := k8sv1.TLSModePassthrough - ipType := v1beta1.IPAddressType - hostnameType := v1beta1.HostnameAddressType - clienttest.Wrap(t, kclient.New[*v1beta1.Gateway](c.client)).CreateOrUpdate(&v1beta1.Gateway{ + ipType := k8sv1.IPAddressType + hostnameType := k8sv1.HostnameAddressType + clienttest.Wrap(t, kclient.New[*k8sv1.Gateway](c.client)).CreateOrUpdate(&k8sv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "eastwest-gwapi", Namespace: "istio-system", Labels: map[string]string{label.TopologyNetwork.Name: "nw2"}, }, - Spec: v1beta1.GatewaySpec{ + Spec: k8sv1.GatewaySpec{ GatewayClassName: "istio", - Addresses: []v1beta1.GatewaySpecAddress{ + Addresses: []k8sv1.GatewaySpecAddress{ {Type: &ipType, Value: "1.2.3.4"}, {Type: &hostnameType, Value: "some hostname"}, }, - Listeners: []v1beta1.Listener{ + Listeners: []k8sv1.Listener{ { Name: "detected-by-options", - TLS: &v1beta1.ListenerTLSConfig{ + TLS: &k8sv1.ListenerTLSConfig{ Mode: &passthroughMode, - Options: map[v1beta1.AnnotationKey]v1beta1.AnnotationValue{ + Options: map[k8sv1.AnnotationKey]k8sv1.AnnotationValue{ constants.ListenerModeOption: constants.ListenerModeAutoPassthrough, }, }, - Port: v1beta1.PortNumber(customPort), + Port: k8sv1.PortNumber(customPort), }, { Name: "detected-by-number", - TLS: &v1beta1.ListenerTLSConfig{Mode: &passthroughMode}, + TLS: &k8sv1.ListenerTLSConfig{Mode: &passthroughMode}, Port: 15443, }, }, }, - Status: v1beta1.GatewayStatus{}, + Status: k8sv1.GatewayStatus{}, }) } func removeGatewayResource(t *testing.T, c *FakeController) { - clienttest.Wrap(t, kclient.New[*v1beta1.Gateway](c.client)).Delete("eastwest-gwapi", "istio-system") + clienttest.Wrap(t, kclient.New[*k8sv1.Gateway](c.client)).Delete("eastwest-gwapi", "istio-system") } func addMeshNetworksFromRegistryGateway(t *testing.T, c *FakeController, watcher meshwatcher.TestNetworksWatcher) { @@ -371,9 +370,9 @@ func TestAmbientSync(t *testing.T) { go s.Run(stop) assert.EventuallyEqual(t, s.ambientIndex.HasSynced, true) - gtw := clienttest.NewWriter[*v1beta1.Gateway](t, s.client) + gtw := clienttest.NewWriter[*k8sv1.Gateway](t, s.client) - gateway := &v1beta1.Gateway{ + gateway := &k8sv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "remote-beta", Namespace: "default", @@ -384,32 +383,32 @@ func TestAmbientSync(t *testing.T) { label.TopologyNetwork.Name: "beta", }, }, - Spec: v1beta1.GatewaySpec{ + Spec: k8sv1.GatewaySpec{ GatewayClassName: "istio-remote", - Addresses: []v1beta1.GatewaySpecAddress{ + Addresses: []k8sv1.GatewaySpecAddress{ { - Type: ptr.Of(v1beta1.IPAddressType), + Type: ptr.Of(k8sv1.IPAddressType), Value: "172.18.1.45", }, }, - Listeners: []v1beta1.Listener{ + Listeners: []k8sv1.Listener{ { Name: "cross-network", Port: 15008, - Protocol: v1beta1.ProtocolType("HBONE"), - TLS: &v1beta1.ListenerTLSConfig{ - Mode: ptr.Of(v1beta1.TLSModeType("Passthrough")), - Options: map[v1beta1.AnnotationKey]v1beta1.AnnotationValue{ + Protocol: k8sv1.ProtocolType("HBONE"), + TLS: &k8sv1.ListenerTLSConfig{ + Mode: ptr.Of(k8sv1.TLSModeType("Passthrough")), + Options: map[k8sv1.AnnotationKey]k8sv1.AnnotationValue{ "gateway.istio.io/listener-protocol": "auto-passthrough", }, }, }, }, }, - Status: v1beta1.GatewayStatus{ + Status: k8sv1.GatewayStatus{ Addresses: []k8sv1.GatewayStatusAddress{ { - Type: ptr.Of(v1beta1.IPAddressType), + Type: ptr.Of(k8sv1.IPAddressType), Value: "172.18.1.45", }, }, diff --git a/pilot/pkg/serviceregistry/kube/conversion.go b/pilot/pkg/serviceregistry/kube/conversion.go index 404498e5bd..a0efe35446 100644 --- a/pilot/pkg/serviceregistry/kube/conversion.go +++ b/pilot/pkg/serviceregistry/kube/conversion.go @@ -20,12 +20,11 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/gateway-api/apis/v1beta1" + v1 "sigs.k8s.io/gateway-api/apis/v1" gatewayx "sigs.k8s.io/gateway-api/apisx/v1alpha1" "istio.io/api/annotation" "istio.io/api/label" - meshconfig "istio.io/api/mesh/v1alpha1" "istio.io/istio/pilot/pkg/model" "istio.io/istio/pilot/pkg/serviceregistry/provider" "istio.io/istio/pkg/cluster" @@ -45,7 +44,7 @@ func convertPort(port corev1.ServicePort) *model.Port { } } -func ConvertService(svc corev1.Service, domainSuffix string, clusterID cluster.ID, mesh *meshconfig.MeshConfig) *model.Service { +func ConvertService(svc corev1.Service, domainSuffix string, clusterID cluster.ID, trustDomain string) *model.Service { addrs := []string{constants.UnspecifiedIP} resolution := model.ClientSideLB externalName := "" @@ -80,7 +79,7 @@ func ConvertService(svc corev1.Service, domainSuffix string, clusterID cluster.I } if svc.Annotations[annotation.AlphaKubernetesServiceAccounts.Name] != "" { for _, ksa := range strings.Split(svc.Annotations[annotation.AlphaKubernetesServiceAccounts.Name], ",") { - serviceaccounts = append(serviceaccounts, kubeToIstioServiceAccount(ksa, svc.Namespace, mesh)) + serviceaccounts = append(serviceaccounts, kubeToIstioServiceAccount(ksa, svc.Namespace, trustDomain)) } } if svc.Annotations[annotation.NetworkingExportTo.Name] != "" { @@ -179,13 +178,13 @@ func ServiceHostnameForKR(obj metav1.Object, domainSuffix string) host.Name { } // kubeToIstioServiceAccount converts a K8s service account to an Istio service account -func kubeToIstioServiceAccount(saname string, ns string, mesh *meshconfig.MeshConfig) string { - return spiffe.MustGenSpiffeURI(mesh, ns, saname) +func kubeToIstioServiceAccount(saname string, ns string, trustDomain string) string { + return spiffe.MustGenSpiffeURIForTrustDomain(trustDomain, ns, saname) } // SecureNamingSAN creates the secure naming used for SAN verification from pod metadata -func SecureNamingSAN(pod *corev1.Pod, mesh *meshconfig.MeshConfig) string { - return spiffe.MustGenSpiffeURI(mesh, pod.Namespace, pod.Spec.ServiceAccountName) +func SecureNamingSAN(pod *corev1.Pod, trustDomain string) string { + return spiffe.MustGenSpiffeURIForTrustDomain(trustDomain, pod.Namespace, pod.Spec.ServiceAccountName) } // PodTLSMode returns the tls mode associated with the pod if pod has been injected with sidecar @@ -204,7 +203,7 @@ func PodTLSMode(pod *corev1.Pod) string { // with TLS.Mode Passthrough. // For some backwards compatibility, we assume any listener with TLS specified and a port matching // 15443 (or the label-override for gateway port) is auto-passthrough as well. -func IsAutoPassthrough(gwLabels map[string]string, l v1beta1.Listener) bool { +func IsAutoPassthrough(gwLabels map[string]string, l v1.Listener) bool { if l.TLS == nil { return false } @@ -222,7 +221,7 @@ func IsAutoPassthrough(gwLabels map[string]string, l v1beta1.Listener) bool { return fmt.Sprint(l.Port) == expectedPort } -func hasListenerMode(l v1beta1.Listener, mode string) bool { +func hasListenerMode(l v1.Listener, mode string) bool { // TODO if we add a hybrid mode for detecting HBONE/passthrough, also check that here return l.TLS != nil && l.TLS.Options != nil && string(l.TLS.Options[constants.ListenerModeOption]) == mode } @@ -250,7 +249,7 @@ func hasListenerModeSet(l gatewayx.ListenerEntry, mode string) bool { return l.TLS != nil && l.TLS.Options != nil && string(l.TLS.Options[constants.ListenerModeOption]) == mode } -func GatewaySA(gw *v1beta1.Gateway) string { +func GatewaySA(gw *v1.Gateway) string { name := model.GetOrDefault(gw.GetAnnotations()[annotation.GatewayServiceAccount.Name], "") if name != "" { return name diff --git a/pilot/pkg/serviceregistry/kube/conversion_test.go b/pilot/pkg/serviceregistry/kube/conversion_test.go index 1f0729bfac..f1ede23813 100644 --- a/pilot/pkg/serviceregistry/kube/conversion_test.go +++ b/pilot/pkg/serviceregistry/kube/conversion_test.go @@ -25,7 +25,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "istio.io/api/annotation" - meshconfig "istio.io/api/mesh/v1alpha1" "istio.io/istio/pkg/cluster" "istio.io/istio/pkg/config/kube" "istio.io/istio/pkg/config/protocol" @@ -169,7 +168,7 @@ func TestServiceConversion(t *testing.T) { }, } - service := ConvertService(localSvc, domainSuffix, clusterID, &meshconfig.MeshConfig{TrustDomain: domainSuffix}) + service := ConvertService(localSvc, domainSuffix, clusterID, domainSuffix) if service == nil { t.Fatal("could not convert service") } @@ -255,7 +254,7 @@ func TestServiceConversionWithEmptyServiceAccountsAnnotation(t *testing.T) { }, } - service := ConvertService(localSvc, domainSuffix, clusterID, nil) + service := ConvertService(localSvc, domainSuffix, clusterID, "") if service == nil { t.Fatal("could not convert service") } @@ -310,7 +309,7 @@ func TestServiceConversionWithExportToAnnotation(t *testing.T) { } for _, test := range tests { localSvc.Annotations[annotation.NetworkingExportTo.Name] = test.Annotation - service := ConvertService(localSvc, domainSuffix, clusterID, nil) + service := ConvertService(localSvc, domainSuffix, clusterID, "") if service == nil { t.Fatal("could not convert service") } @@ -343,7 +342,7 @@ func TestExternalServiceConversion(t *testing.T) { }, } - service := ConvertService(extSvc, domainSuffix, clusterID, nil) + service := ConvertService(extSvc, domainSuffix, clusterID, "") if service == nil { t.Fatal("could not convert external service") } @@ -393,7 +392,7 @@ func TestExternalClusterLocalServiceConversion(t *testing.T) { domainSuffix := "cluster.local" - service := ConvertService(extSvc, domainSuffix, clusterID, nil) + service := ConvertService(extSvc, domainSuffix, clusterID, "") if service == nil { t.Fatal("could not convert external service") } @@ -454,7 +453,7 @@ func TestLBServiceConversion(t *testing.T) { }, } - service := ConvertService(extSvc, domainSuffix, clusterID, nil) + service := ConvertService(extSvc, domainSuffix, clusterID, "") if service == nil { t.Fatal("could not convert external service") } @@ -500,7 +499,7 @@ func TestInternalTrafficPolicyServiceConversion(t *testing.T) { }, } - service := ConvertService(svc, domainSuffix, clusterID, nil) + service := ConvertService(svc, domainSuffix, clusterID, "") if service == nil { t.Fatal("could not convert service") } @@ -520,9 +519,7 @@ func TestSecureNamingSAN(t *testing.T) { pod.Namespace = ns pod.Spec.ServiceAccountName = sa - mesh := &meshconfig.MeshConfig{TrustDomain: "td.local"} - - san := SecureNamingSAN(pod, mesh) + san := SecureNamingSAN(pod, "td.local") expectedSAN := fmt.Sprintf("spiffe://td.local/ns/%v/sa/%v", ns, sa) diff --git a/pilot/pkg/status/collections.go b/pilot/pkg/status/collections.go index ee69ea09fa..b5b4b6e35e 100644 --- a/pilot/pkg/status/collections.go +++ b/pilot/pkg/status/collections.go @@ -21,10 +21,13 @@ import ( "strconv" "sync" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + schematypes "istio.io/istio/pkg/config/schema/kubetypes" "istio.io/istio/pkg/kube/controllers" "istio.io/istio/pkg/kube/krt" "istio.io/istio/pkg/log" + "istio.io/istio/pkg/revisions" "istio.io/istio/pkg/slices" ) @@ -72,7 +75,12 @@ func (s *StatusCollections) SetQueue(queue Queue) []krt.Syncer { // krt.ObjectWithStatus, in theory, can contain anything in the "object" field. This function requires it to contain // the current live *status*, and a passed in getStatus to extract it from the object. // It will then compare the live status to the desired status to determine whether to write or not. -func RegisterStatus[I controllers.Object, IS any](s *StatusCollections, statusCol krt.StatusCollection[I, IS], getStatus func(I) IS) { +func RegisterStatus[I controllers.Object, IS any]( + s *StatusCollections, + statusCol krt.StatusCollection[I, IS], + getStatus func(I) IS, + tagWatcher revisions.TagWatcher, +) { reg := func(statusWriter Queue) krt.HandlerRegistration { h := statusCol.Register(func(o krt.Event[krt.ObjectWithStatus[I, IS]]) { l := o.Latest() @@ -85,6 +93,10 @@ func RegisterStatus[I controllers.Object, IS any](s *StatusCollections, statusCo log.Debugf("suppress change for %v %v", l.ResourceName(), l.Obj.GetResourceVersion()) return } + if !tagWatcher.IsMine(metav1.ObjectMeta{Namespace: l.Obj.GetNamespace(), Name: l.Obj.GetName(), Labels: l.Obj.GetLabels()}) { + log.Debugf("suppress change for %v %v because it does not belong to my revision", l.ResourceName(), l.Obj.GetResourceVersion()) + return + } status := &l.Status if o.Event == controllers.EventDelete { // if the object is being deleted, we should not reset status diff --git a/pilot/pkg/xds/endpoints/endpoint_builder.go b/pilot/pkg/xds/endpoints/endpoint_builder.go index 7e4d94aff1..81ab5f0a42 100644 --- a/pilot/pkg/xds/endpoints/endpoint_builder.go +++ b/pilot/pkg/xds/endpoints/endpoint_builder.go @@ -370,9 +370,14 @@ func (b *EndpointBuilder) BuildClusterLoadAssignment(endpointIndex *model.Endpoi svcEps := b.snapshotShards(endpointIndex) svcEps = slices.FilterInPlace(svcEps, func(ep *model.IstioEndpoint) bool { - // filter out endpoints that don't match the service port - if svcPort.Name != ep.ServicePortName { - return false + // For InferencePool services, include endpoints from all service ports + // They use multiple service ports (54321+i) mapped to different targetPorts + // but we want all endpoints in a single cluster so the EPP can load-balance across them + if !b.service.UseInferenceSemantics() { + // filter out endpoints that don't match the service port + if svcPort.Name != ep.ServicePortName { + return false + } } // filter out endpoint that has invalid ip address, mostly domain name. Because this is generated from ServiceEntry. // There are other two cases that should not be filtered out: diff --git a/pilot/pkg/xds/endpoints/endpoint_builder_test.go b/pilot/pkg/xds/endpoints/endpoint_builder_test.go index dc224fef13..5cba69254b 100644 --- a/pilot/pkg/xds/endpoints/endpoint_builder_test.go +++ b/pilot/pkg/xds/endpoints/endpoint_builder_test.go @@ -499,3 +499,118 @@ func TestFilterIstioEndpoint(t *testing.T) { }) } } + +func TestBuildClusterLoadAssignment_InferenceServicePortFiltering(t *testing.T) { + tests := []struct { + name string + InferencePoolService bool + expectedEndpoints int + }{ + { + name: "inference service includes endpoints from all ports", + InferencePoolService: true, + expectedEndpoints: 3, + }, + { + name: "regular service filters endpoints by port name", + InferencePoolService: false, + expectedEndpoints: 1, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + svcLabels := make(map[string]string) + if tt.InferencePoolService { + svcLabels[constants.InternalServiceSemantics] = constants.ServiceSemanticsInferencePool + } + + svc := &model.Service{ + Hostname: "example.ns.svc.cluster.local", + Attributes: model.ServiceAttributes{ + Name: "example", + Namespace: "ns", + Labels: svcLabels, + }, + Ports: model.PortList{ + {Port: 80, Protocol: protocol.HTTP, Name: "http-80"}, + {Port: 8000, Protocol: protocol.HTTP, Name: "http-8000"}, + {Port: 8001, Protocol: protocol.HTTP, Name: "http-8001"}, + }, + } + + proxy := &model.Proxy{ + Type: model.SidecarProxy, + IPAddresses: []string{"127.0.0.1"}, + Metadata: &model.NodeMetadata{ + Namespace: "ns", + NodeName: "example", + }, + ConfigNamespace: "ns", + } + + endpointIndex := model.NewEndpointIndex(model.NewXdsCache()) + shards, _ := endpointIndex.GetOrCreateEndpointShard("example.ns.svc.cluster.local", "ns") + shards.Lock() + shards.Shards[model.ShardKey{Cluster: "cluster1"}] = []*model.IstioEndpoint{ + { + Addresses: []string{"10.0.0.1"}, + ServicePortName: "http-80", + EndpointPort: 80, + HostName: "example.ns.svc.cluster.local", + Namespace: "ns", + }, + { + Addresses: []string{"10.0.0.2"}, + ServicePortName: "http-8000", + EndpointPort: 8000, + HostName: "example.ns.svc.cluster.local", + Namespace: "ns", + }, + { + Addresses: []string{"10.0.0.3"}, + ServicePortName: "http-8001", + EndpointPort: 8001, + HostName: "example.ns.svc.cluster.local", + Namespace: "ns", + }, + } + shards.Unlock() + + env := model.NewEnvironment() + env.ConfigStore = model.NewFakeStore() + env.Watcher = meshwatcher.NewTestWatcher(&meshconfig.MeshConfig{RootNamespace: "istio-system"}) + meshNetworks := meshwatcher.NewFixedNetworksWatcher(nil) + env.NetworksWatcher = meshNetworks + env.ServiceDiscovery = &localServiceDiscovery{ + services: []*model.Service{svc}, + } + xdsUpdater := xdsfake.NewFakeXDS() + if err := env.InitNetworksManager(xdsUpdater); err != nil { + t.Fatal(err) + } + env.Init() + + push := model.NewPushContext() + push.InitContext(env, nil, nil) + env.SetPushContext(push) + + builder := NewCDSEndpointBuilder( + proxy, push, + "outbound|80||example.ns.svc.cluster.local", + model.TrafficDirectionOutbound, "", "example.ns.svc.cluster.local", 80, + svc, nil) + + cla := builder.BuildClusterLoadAssignment(endpointIndex) + + var totalEndpoints int + for _, localityLbEndpoints := range cla.Endpoints { + totalEndpoints += len(localityLbEndpoints.LbEndpoints) + } + + if totalEndpoints != tt.expectedEndpoints { + t.Errorf("expected %d endpoints, got %d", tt.expectedEndpoints, totalEndpoints) + } + }) + } +} diff --git a/pilot/pkg/xds/testdata/benchmarks/waypoint.yaml b/pilot/pkg/xds/testdata/benchmarks/waypoint.yaml index 077aa40c24..f9c2cc040b 100644 --- a/pilot/pkg/xds/testdata/benchmarks/waypoint.yaml +++ b/pilot/pkg/xds/testdata/benchmarks/waypoint.yaml @@ -1,5 +1,5 @@ # Set up a Service associated with our proxy, which will run as 1.1.1.1 IP -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: waypoint diff --git a/pilot/pkg/xds/waypoint_test.go b/pilot/pkg/xds/waypoint_test.go index 83e16cac76..a9332082f2 100644 --- a/pilot/pkg/xds/waypoint_test.go +++ b/pilot/pkg/xds/waypoint_test.go @@ -67,7 +67,7 @@ spec: labels: gateway.networking.k8s.io/gateway-name: waypoint ` - waypointGateway = `apiVersion: gateway.networking.k8s.io/v1beta1 + waypointGateway = `apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: waypoint @@ -423,7 +423,7 @@ spec: labels: gateway.networking.k8s.io/gateway-name: waypoint` - waypointGateway := `apiVersion: gateway.networking.k8s.io/v1beta1 + waypointGateway := `apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: waypoint diff --git a/pilot/test/xds/fake.go b/pilot/test/xds/fake.go index e8f8bf27df..59cbeffd07 100644 --- a/pilot/test/xds/fake.go +++ b/pilot/test/xds/fake.go @@ -529,7 +529,7 @@ func getKubernetesObjects(t test.Failer, opts FakeOptions) map[cluster.ID][]runt func kubernetesObjectsFromString(s string) ([]runtime.Object, error) { var objects []runtime.Object decode := kubelib.IstioCodec.UniversalDeserializer().Decode - objectStrs := strings.Split(s, "---") + objectStrs := strings.Split(s, "\n---\n") for _, s := range objectStrs { if len(strings.TrimSpace(s)) == 0 { continue diff --git a/pkg/bootstrap/config.go b/pkg/bootstrap/config.go index 398b1be10c..fe5ad727c6 100644 --- a/pkg/bootstrap/config.go +++ b/pkg/bootstrap/config.go @@ -26,6 +26,7 @@ import ( "time" core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/wrapperspb" "istio.io/api/annotation" @@ -343,7 +344,8 @@ func getStatsOptions(meta *model.BootstrapNodeMetadata) []option.Instance { } else if statsEvictionInterval%statsFlushInterval != 0 { log.Warnf("StatsEvictionInterval must be a multiple of the StatsFlushInterval") } else { - options = append(options, option.EnvoyStatsEvictionInterval(statsEvictionInterval)) + duration := &durationpb.Duration{Seconds: int64(statsEvictionInterval.Seconds())} + options = append(options, option.EnvoyStatsEvictionInterval(duration)) } } diff --git a/pkg/bootstrap/option/convert.go b/pkg/bootstrap/option/convert.go index 5a0755153d..3f52b4fe0a 100644 --- a/pkg/bootstrap/option/convert.go +++ b/pkg/bootstrap/option/convert.go @@ -206,12 +206,29 @@ func jsonConverter(d any) convertFunc { } } +// Format duration as a time.Duration string representation like +// 605s -> 10m5s func durationConverter(value *durationpb.Duration) convertFunc { return func(*instance) (any, error) { return value.AsDuration().String(), nil } } +// Envoy's json-to-protobuf converter wants durations in fractional seconds +// with "s" suffix like 300s or 10.50000000000s, it doesn't understand formats +// like 10m0s as produced by durationConverter. +func envoyDurationConverter(value *durationpb.Duration) convertFunc { + return func(*instance) (any, error) { + if value == nil { + return "", fmt.Errorf("nil duration passed as option") + } + if value.GetNanos() == 0 { + return fmt.Sprintf("%ds", value.GetSeconds()), nil + } + return fmt.Sprintf("%d.%09ds", value.GetSeconds(), value.GetNanos()), nil + } +} + func convertToJSON(v any) string { if v == nil { return "" diff --git a/pkg/bootstrap/option/instance.go b/pkg/bootstrap/option/instance.go index fa2baf22fc..83fb551c1a 100644 --- a/pkg/bootstrap/option/instance.go +++ b/pkg/bootstrap/option/instance.go @@ -123,10 +123,17 @@ func newOptionOrSkipIfZero(name Name, value any) *instance { return newOption(name, value) } +// Create an option with a time.Duration-compatible stringified format (hours, minutes, seconds, etc) func newDurationOption(name Name, value *durationpb.Duration) *instance { return newOptionOrSkipIfZero(name, value).withConvert(durationConverter(value)) } +// Create an option with a protobuf Duration-compatible stringified format +// (seconds and nanoseconds) as accepted by Envoy's protobuf json parser +func newEnvoyDurationOption(name Name, value *durationpb.Duration) *instance { + return newOptionOrSkipIfZero(name, value).withConvert(envoyDurationConverter(value)) +} + func newTCPKeepaliveOption(name Name, value *networkingAPI.ConnectionPoolSettings_TCPSettings_TcpKeepalive) *instance { return newOptionOrSkipIfZero(name, value).withConvert(keepaliveConverter(value)) } diff --git a/pkg/bootstrap/option/instances.go b/pkg/bootstrap/option/instances.go index aeea2d4bc4..7eb4a48d97 100644 --- a/pkg/bootstrap/option/instances.go +++ b/pkg/bootstrap/option/instances.go @@ -308,6 +308,6 @@ func EnvoyStatsFlushInterval(interval time.Duration) Instance { return newOption("stats_flush_interval", interval) } -func EnvoyStatsEvictionInterval(interval time.Duration) Instance { - return newOption("stats_eviction_interval", interval) +func EnvoyStatsEvictionInterval(interval *durationpb.Duration) Instance { + return newEnvoyDurationOption("stats_eviction_interval", interval) } diff --git a/pkg/bootstrap/option/instances_test.go b/pkg/bootstrap/option/instances_test.go index 19600e0b51..dde0001340 100644 --- a/pkg/bootstrap/option/instances_test.go +++ b/pkg/bootstrap/option/instances_test.go @@ -647,6 +647,24 @@ func TestOptions(t *testing.T) { }, &model.BootstrapNodeMetadata{}, false), expected: `{"name":"envoy.transport_sockets.tls","typed_config":{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext","common_tls_context":{"combined_validation_context":{"default_validation_context":{},"validation_context_sds_secret_config":{"name":"file-root:/etc/tracing/ca.pem","sds_config":{"api_config_source":{"api_type":"GRPC","grpc_services":[{"envoy_grpc":{"cluster_name":"sds-grpc"}}],"set_node_on_first_message_only":true,"transport_api_version":"V3"},"resource_api_version":"V3"}}}}}}`, }, + { + testName: "stats eviction interval normal", + key: "stats_eviction_interval", + option: option.EnvoyStatsEvictionInterval(durationpb.New(time.Second * 10)), + expected: "10s", + }, + { + testName: "stats eviction interval fractional", + key: "stats_eviction_interval", + option: option.EnvoyStatsEvictionInterval(durationpb.New(time.Second*2 + time.Millisecond*5)), + expected: "2.005000000s", + }, + { + testName: "stats eviction interval longer than 60s", + key: "stats_eviction_interval", + option: option.EnvoyStatsEvictionInterval(durationpb.New(time.Second * 120)), + expected: "120s", + }, } for _, c := range cases { diff --git a/pkg/config/mesh/meshwatcher/watcher_test_utils.go b/pkg/config/mesh/meshwatcher/watcher_test_utils.go index 1de43a06bb..c391d6637b 100644 --- a/pkg/config/mesh/meshwatcher/watcher_test_utils.go +++ b/pkg/config/mesh/meshwatcher/watcher_test_utils.go @@ -20,6 +20,8 @@ import ( "istio.io/istio/pkg/kube/krt" ) +var _ mesh.RestrictedConfigWatcher = &TestWatcher{} + // TestWatcher provides an interface that takes a static MeshConfig which can be updated explicitly. // It is intended for tests type TestWatcher struct { @@ -31,6 +33,14 @@ func (w TestWatcher) Set(n *meshconfig.MeshConfig) { w.col.Set(&MeshConfigResource{n}) } +func (w TestWatcher) TrustDomain() string { + return w.Mesh().GetTrustDomain() +} + +func (w TestWatcher) ServiceScopeConfigs() []*meshconfig.MeshConfig_ServiceScopeConfigs { + return w.Mesh().GetServiceScopeConfigs() +} + // NewTestWatcher creates a new Watcher that always returns the given mesh config. func NewTestWatcher(m *meshconfig.MeshConfig) TestWatcher { if m == nil { diff --git a/pkg/config/mesh/watchers.go b/pkg/config/mesh/watchers.go index 267d1b604b..56910dbc82 100644 --- a/pkg/config/mesh/watchers.go +++ b/pkg/config/mesh/watchers.go @@ -57,3 +57,29 @@ func NewWatcherHandlerRegistration(f func()) *WatcherHandlerRegistration { func (r *WatcherHandlerRegistration) Remove() { r.remove() } + +// RestrictedConfigWatcher provides limited access to mesh configuration. +// It exposes only trust domain and service scope, suitable for use in remote clusters +// or components that should not have full mesh config access. +type RestrictedConfigWatcher interface { + TrustDomain() string + ServiceScopeConfigs() []*v1alpha1.MeshConfig_ServiceScopeConfigs +} + +// NewRestrictedConfigWatcher wraps a Holder to expose only trust domain and service scope. +func NewRestrictedConfigWatcher(holder Holder) RestrictedConfigWatcher { + return restrictedConfigAdapter{holder} +} + +// restrictedConfigAdapter wraps a Holder to provide RestrictedConfigWatcher interface. +type restrictedConfigAdapter struct { + Holder +} + +func (r restrictedConfigAdapter) TrustDomain() string { + return r.Mesh().GetTrustDomain() +} + +func (r restrictedConfigAdapter) ServiceScopeConfigs() []*v1alpha1.MeshConfig_ServiceScopeConfigs { + return r.Mesh().GetServiceScopeConfigs() +} diff --git a/pkg/config/schema/codegen/common.go b/pkg/config/schema/codegen/common.go index 49d54de5f7..76adef3277 100644 --- a/pkg/config/schema/codegen/common.go +++ b/pkg/config/schema/codegen/common.go @@ -28,6 +28,7 @@ import ( "istio.io/istio/pilot/pkg/features" "istio.io/istio/pkg/config/schema/ast" "istio.io/istio/pkg/test/env" + "istio.io/istio/pkg/util/strcase" ) func Run() error { @@ -65,9 +66,21 @@ func Run() error { {Resource: &ast.Resource{Identifier: "ServiceImport", Plural: "serviceimports", Version: features.MCSAPIVersion, Group: features.MCSAPIGroup}}, }, inp.Entries...) + // Build a deduplicated list of Kind names for KebabKind function + seenKinds := make(map[string]bool) + var uniqueKinds []string + for _, e := range inp.Entries { + if !seenKinds[e.Resource.Kind] { + seenKinds[e.Resource.Kind] = true + uniqueKinds = append(uniqueKinds, e.Resource.Kind) + } + } + sort.Strings(uniqueKinds) + return errors.Join( writeTemplate("pkg/config/schema/gvk/resources.gen.go", gvkTemplate, map[string]any{ "Entries": inp.Entries, + "UniqueKinds": uniqueKinds, "PackageName": "gvk", }), writeTemplate("pkg/config/schema/gvr/resources.gen.go", gvrTemplate, map[string]any{ @@ -125,9 +138,15 @@ func writeTemplate(path, tmpl string, i any) error { return c.Run() } +// camelCaseToKebabCase wraps strcase.CamelCaseToKebabCase for use in templates. +func camelCaseToKebabCase(s string) string { + return strcase.CamelCaseToKebabCase(s) +} + func applyTemplate(tmpl string, i any) (string, error) { t := template.New("tmpl").Funcs(template.FuncMap{ - "contains": strings.Contains, + "contains": strings.Contains, + "kebabcase": camelCaseToKebabCase, }) t2 := template.Must(t.Parse(tmpl)) diff --git a/pkg/config/schema/codegen/templates/gvk.go.tmpl b/pkg/config/schema/codegen/templates/gvk.go.tmpl index f2193a7ef0..2e292d408c 100644 --- a/pkg/config/schema/codegen/templates/gvk.go.tmpl +++ b/pkg/config/schema/codegen/templates/gvk.go.tmpl @@ -86,3 +86,15 @@ func MustFromGVR(g schema.GroupVersionResource) config.GroupVersionKind { } return r } + +// KebabKind returns the kebab-case version of a Kind string. +// This is a generated mapping to avoid runtime allocations from string conversion. +func KebabKind(k string) string { + switch k { +{{- range .UniqueKinds }} + case "{{.}}": + return "{{ kebabcase . }}" +{{- end }} + } + return "" +} diff --git a/pkg/config/schema/collections/collections.gen.go b/pkg/config/schema/collections/collections.gen.go index c631a54551..f5b93559e8 100755 --- a/pkg/config/schema/collections/collections.gen.go +++ b/pkg/config/schema/collections/collections.gen.go @@ -267,14 +267,14 @@ var ( Group: "gateway.networking.k8s.io", Kind: "GatewayClass", Plural: "gatewayclasses", - Version: "v1beta1", + Version: "v1", VersionAliases: []string{ "v1alpha2", - "v1", + "v1beta1", }, Proto: "k8s.io.gateway_api.api.v1alpha1.GatewayClassSpec", StatusProto: "k8s.io.gateway_api.api.v1alpha1.GatewayClassStatus", - ReflectType: reflect.TypeOf(&sigsk8siogatewayapiapisv1beta1.GatewayClassSpec{}).Elem(), StatusType: reflect.TypeOf(&sigsk8siogatewayapiapisv1beta1.GatewayClassStatus{}).Elem(), - ProtoPackage: "sigs.k8s.io/gateway-api/apis/v1beta1", StatusPackage: "sigs.k8s.io/gateway-api/apis/v1beta1", + ReflectType: reflect.TypeOf(&sigsk8siogatewayapiapisv1.GatewayClassSpec{}).Elem(), StatusType: reflect.TypeOf(&sigsk8siogatewayapiapisv1.GatewayClassStatus{}).Elem(), + ProtoPackage: "sigs.k8s.io/gateway-api/apis/v1", StatusPackage: "sigs.k8s.io/gateway-api/apis/v1", ClusterScoped: true, Synthetic: false, Builtin: false, @@ -286,14 +286,14 @@ var ( Group: "gateway.networking.k8s.io", Kind: "HTTPRoute", Plural: "httproutes", - Version: "v1beta1", + Version: "v1", VersionAliases: []string{ "v1alpha2", - "v1", + "v1beta1", }, Proto: "k8s.io.gateway_api.api.v1alpha1.HTTPRouteSpec", StatusProto: "k8s.io.gateway_api.api.v1alpha1.HTTPRouteStatus", - ReflectType: reflect.TypeOf(&sigsk8siogatewayapiapisv1beta1.HTTPRouteSpec{}).Elem(), StatusType: reflect.TypeOf(&sigsk8siogatewayapiapisv1beta1.HTTPRouteStatus{}).Elem(), - ProtoPackage: "sigs.k8s.io/gateway-api/apis/v1beta1", StatusPackage: "sigs.k8s.io/gateway-api/apis/v1beta1", + ReflectType: reflect.TypeOf(&sigsk8siogatewayapiapisv1.HTTPRouteSpec{}).Elem(), StatusType: reflect.TypeOf(&sigsk8siogatewayapiapisv1.HTTPRouteStatus{}).Elem(), + ProtoPackage: "sigs.k8s.io/gateway-api/apis/v1", StatusPackage: "sigs.k8s.io/gateway-api/apis/v1", ClusterScoped: false, Synthetic: false, Builtin: false, @@ -365,14 +365,14 @@ var ( Group: "gateway.networking.k8s.io", Kind: "Gateway", Plural: "gateways", - Version: "v1beta1", + Version: "v1", VersionAliases: []string{ "v1alpha2", - "v1", + "v1beta1", }, Proto: "k8s.io.gateway_api.api.v1alpha1.GatewaySpec", StatusProto: "k8s.io.gateway_api.api.v1alpha1.GatewayStatus", - ReflectType: reflect.TypeOf(&sigsk8siogatewayapiapisv1beta1.GatewaySpec{}).Elem(), StatusType: reflect.TypeOf(&sigsk8siogatewayapiapisv1beta1.GatewayStatus{}).Elem(), - ProtoPackage: "sigs.k8s.io/gateway-api/apis/v1beta1", StatusPackage: "sigs.k8s.io/gateway-api/apis/v1beta1", + ReflectType: reflect.TypeOf(&sigsk8siogatewayapiapisv1.GatewaySpec{}).Elem(), StatusType: reflect.TypeOf(&sigsk8siogatewayapiapisv1.GatewayStatus{}).Elem(), + ProtoPackage: "sigs.k8s.io/gateway-api/apis/v1", StatusPackage: "sigs.k8s.io/gateway-api/apis/v1", ClusterScoped: false, Synthetic: false, Builtin: false, diff --git a/pkg/config/schema/gvk/resources.gen.go b/pkg/config/schema/gvk/resources.gen.go index 17e3212a53..ae0c4e8f65 100644 --- a/pkg/config/schema/gvk/resources.gen.go +++ b/pkg/config/schema/gvk/resources.gen.go @@ -31,19 +31,19 @@ var ( Gateway = config.GroupVersionKind{Group: "networking.istio.io", Version: "v1", Kind: "Gateway"} Gateway_v1alpha3 = config.GroupVersionKind{Group: "networking.istio.io", Version: "v1alpha3", Kind: "Gateway"} Gateway_v1beta1 = config.GroupVersionKind{Group: "networking.istio.io", Version: "v1beta1", Kind: "Gateway"} - GatewayClass = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1beta1", Kind: "GatewayClass"} + GatewayClass = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1", Kind: "GatewayClass"} GatewayClass_v1alpha2 = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1alpha2", Kind: "GatewayClass"} - GatewayClass_v1 = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1", Kind: "GatewayClass"} - HTTPRoute = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1beta1", Kind: "HTTPRoute"} + GatewayClass_v1beta1 = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1beta1", Kind: "GatewayClass"} + HTTPRoute = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1", Kind: "HTTPRoute"} HTTPRoute_v1alpha2 = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1alpha2", Kind: "HTTPRoute"} - HTTPRoute_v1 = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1", Kind: "HTTPRoute"} + HTTPRoute_v1beta1 = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1beta1", Kind: "HTTPRoute"} HorizontalPodAutoscaler = config.GroupVersionKind{Group: "autoscaling", Version: "v2", Kind: "HorizontalPodAutoscaler"} InferencePool = config.GroupVersionKind{Group: "inference.networking.k8s.io", Version: "v1", Kind: "InferencePool"} Ingress = config.GroupVersionKind{Group: "networking.k8s.io", Version: "v1", Kind: "Ingress"} IngressClass = config.GroupVersionKind{Group: "networking.k8s.io", Version: "v1", Kind: "IngressClass"} - KubernetesGateway = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1beta1", Kind: "Gateway"} + KubernetesGateway = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1", Kind: "Gateway"} KubernetesGateway_v1alpha2 = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1alpha2", Kind: "Gateway"} - KubernetesGateway_v1 = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1", Kind: "Gateway"} + KubernetesGateway_v1beta1 = config.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1beta1", Kind: "Gateway"} Lease = config.GroupVersionKind{Group: "coordination.k8s.io", Version: "v1", Kind: "Lease"} MeshConfig = config.GroupVersionKind{Group: "", Version: "v1alpha1", Kind: "MeshConfig"} MeshNetworks = config.GroupVersionKind{Group: "", Version: "v1alpha1", Kind: "MeshNetworks"} @@ -136,14 +136,14 @@ func ToGVR(g config.GroupVersionKind) (schema.GroupVersionResource, bool) { return gvr.GatewayClass, true case GatewayClass_v1alpha2: return gvr.GatewayClass_v1alpha2, true - case GatewayClass_v1: - return gvr.GatewayClass_v1, true + case GatewayClass_v1beta1: + return gvr.GatewayClass_v1beta1, true case HTTPRoute: return gvr.HTTPRoute, true case HTTPRoute_v1alpha2: return gvr.HTTPRoute_v1alpha2, true - case HTTPRoute_v1: - return gvr.HTTPRoute_v1, true + case HTTPRoute_v1beta1: + return gvr.HTTPRoute_v1beta1, true case HorizontalPodAutoscaler: return gvr.HorizontalPodAutoscaler, true case InferencePool: @@ -156,8 +156,8 @@ func ToGVR(g config.GroupVersionKind) (schema.GroupVersionResource, bool) { return gvr.KubernetesGateway, true case KubernetesGateway_v1alpha2: return gvr.KubernetesGateway_v1alpha2, true - case KubernetesGateway_v1: - return gvr.KubernetesGateway_v1, true + case KubernetesGateway_v1beta1: + return gvr.KubernetesGateway_v1beta1, true case Lease: return gvr.Lease, true case MeshConfig: @@ -491,3 +491,109 @@ func MustFromGVR(g schema.GroupVersionResource) config.GroupVersionKind { } return r } + +// KebabKind returns the kebab-case version of a Kind string. +// This is a generated mapping to avoid runtime allocations from string conversion. +func KebabKind(k string) string { + switch k { + case "AuthorizationPolicy": + return "authorization-policy" + case "BackendTLSPolicy": + return "backend-tls-policy" + case "CertificateSigningRequest": + return "certificate-signing-request" + case "ClusterTrustBundle": + return "cluster-trust-bundle" + case "ConfigMap": + return "config-map" + case "CustomResourceDefinition": + return "custom-resource-definition" + case "DaemonSet": + return "daemon-set" + case "Deployment": + return "deployment" + case "DestinationRule": + return "destination-rule" + case "EndpointSlice": + return "endpoint-slice" + case "Endpoints": + return "endpoints" + case "EnvoyFilter": + return "envoy-filter" + case "GRPCRoute": + return "grpc-route" + case "Gateway": + return "gateway" + case "GatewayClass": + return "gateway-class" + case "HTTPRoute": + return "http-route" + case "HorizontalPodAutoscaler": + return "horizontal-pod-autoscaler" + case "InferencePool": + return "inference-pool" + case "Ingress": + return "ingress" + case "IngressClass": + return "ingress-class" + case "Lease": + return "lease" + case "MeshConfig": + return "mesh-config" + case "MeshNetworks": + return "mesh-networks" + case "MutatingWebhookConfiguration": + return "mutating-webhook-configuration" + case "Namespace": + return "namespace" + case "Node": + return "node" + case "PeerAuthentication": + return "peer-authentication" + case "Pod": + return "pod" + case "PodDisruptionBudget": + return "pod-disruption-budget" + case "ProxyConfig": + return "proxy-config" + case "ReferenceGrant": + return "reference-grant" + case "RequestAuthentication": + return "request-authentication" + case "Secret": + return "secret" + case "Service": + return "service" + case "ServiceAccount": + return "service-account" + case "ServiceEntry": + return "service-entry" + case "Sidecar": + return "sidecar" + case "StatefulSet": + return "stateful-set" + case "TCPRoute": + return "tcp-route" + case "TLSRoute": + return "tls-route" + case "Telemetry": + return "telemetry" + case "UDPRoute": + return "udp-route" + case "ValidatingWebhookConfiguration": + return "validating-webhook-configuration" + case "VirtualService": + return "virtual-service" + case "WasmPlugin": + return "wasm-plugin" + case "WorkloadEntry": + return "workload-entry" + case "WorkloadGroup": + return "workload-group" + case "XBackendTrafficPolicy": + return "x-backend-traffic-policy" + case "XListenerSet": + return "x-listener-set" + } + return "" +} diff --git a/pkg/config/schema/gvr/resources.gen.go b/pkg/config/schema/gvr/resources.gen.go index 2f2207a46b..aa818cb132 100644 --- a/pkg/config/schema/gvr/resources.gen.go +++ b/pkg/config/schema/gvr/resources.gen.go @@ -27,19 +27,19 @@ var ( Gateway = schema.GroupVersionResource{Group: "networking.istio.io", Version: "v1", Resource: "gateways"} Gateway_v1alpha3 = schema.GroupVersionResource{Group: "networking.istio.io", Version: "v1alpha3", Resource: "gateways"} Gateway_v1beta1 = schema.GroupVersionResource{Group: "networking.istio.io", Version: "v1beta1", Resource: "gateways"} - GatewayClass = schema.GroupVersionResource{Group: "gateway.networking.k8s.io", Version: "v1beta1", Resource: "gatewayclasses"} + GatewayClass = schema.GroupVersionResource{Group: "gateway.networking.k8s.io", Version: "v1", Resource: "gatewayclasses"} GatewayClass_v1alpha2 = schema.GroupVersionResource{Group: "gateway.networking.k8s.io", Version: "v1alpha2", Resource: "gatewayclasses"} - GatewayClass_v1 = schema.GroupVersionResource{Group: "gateway.networking.k8s.io", Version: "v1", Resource: "gatewayclasses"} - HTTPRoute = schema.GroupVersionResource{Group: "gateway.networking.k8s.io", Version: "v1beta1", Resource: "httproutes"} + GatewayClass_v1beta1 = schema.GroupVersionResource{Group: "gateway.networking.k8s.io", Version: "v1beta1", Resource: "gatewayclasses"} + HTTPRoute = schema.GroupVersionResource{Group: "gateway.networking.k8s.io", Version: "v1", Resource: "httproutes"} HTTPRoute_v1alpha2 = schema.GroupVersionResource{Group: "gateway.networking.k8s.io", Version: "v1alpha2", Resource: "httproutes"} - HTTPRoute_v1 = schema.GroupVersionResource{Group: "gateway.networking.k8s.io", Version: "v1", Resource: "httproutes"} + HTTPRoute_v1beta1 = schema.GroupVersionResource{Group: "gateway.networking.k8s.io", Version: "v1beta1", Resource: "httproutes"} HorizontalPodAutoscaler = schema.GroupVersionResource{Group: "autoscaling", Version: "v2", Resource: "horizontalpodautoscalers"} InferencePool = schema.GroupVersionResource{Group: "inference.networking.k8s.io", Version: "v1", Resource: "inferencepools"} Ingress = schema.GroupVersionResource{Group: "networking.k8s.io", Version: "v1", Resource: "ingresses"} IngressClass = schema.GroupVersionResource{Group: "networking.k8s.io", Version: "v1", Resource: "ingressclasses"} - KubernetesGateway = schema.GroupVersionResource{Group: "gateway.networking.k8s.io", Version: "v1beta1", Resource: "gateways"} + KubernetesGateway = schema.GroupVersionResource{Group: "gateway.networking.k8s.io", Version: "v1", Resource: "gateways"} KubernetesGateway_v1alpha2 = schema.GroupVersionResource{Group: "gateway.networking.k8s.io", Version: "v1alpha2", Resource: "gateways"} - KubernetesGateway_v1 = schema.GroupVersionResource{Group: "gateway.networking.k8s.io", Version: "v1", Resource: "gateways"} + KubernetesGateway_v1beta1 = schema.GroupVersionResource{Group: "gateway.networking.k8s.io", Version: "v1beta1", Resource: "gateways"} Lease = schema.GroupVersionResource{Group: "coordination.k8s.io", Version: "v1", Resource: "leases"} MeshConfig = schema.GroupVersionResource{Group: "", Version: "v1alpha1", Resource: "meshconfigs"} MeshNetworks = schema.GroupVersionResource{Group: "", Version: "v1alpha1", Resource: "meshnetworks"} @@ -135,13 +135,13 @@ func IsClusterScoped(g schema.GroupVersionResource) bool { return true case GatewayClass_v1alpha2: return true - case GatewayClass_v1: + case GatewayClass_v1beta1: return true case HTTPRoute: return false case HTTPRoute_v1alpha2: return false - case HTTPRoute_v1: + case HTTPRoute_v1beta1: return false case HorizontalPodAutoscaler: return false @@ -155,7 +155,7 @@ func IsClusterScoped(g schema.GroupVersionResource) bool { return false case KubernetesGateway_v1alpha2: return false - case KubernetesGateway_v1: + case KubernetesGateway_v1beta1: return false case Lease: return false diff --git a/pkg/config/schema/kubeclient/resources.gen.go b/pkg/config/schema/kubeclient/resources.gen.go index 686158e591..3d0e909fd0 100755 --- a/pkg/config/schema/kubeclient/resources.gen.go +++ b/pkg/config/schema/kubeclient/resources.gen.go @@ -80,10 +80,10 @@ func GetWriteClient[T runtime.Object](c ClientGetter, namespace string) ktypes.W return c.GatewayAPI().GatewayV1().GRPCRoutes(namespace).(ktypes.WriteAPI[T]) case *apiistioioapinetworkingv1.Gateway: return c.Istio().NetworkingV1().Gateways(namespace).(ktypes.WriteAPI[T]) - case *sigsk8siogatewayapiapisv1beta1.GatewayClass: - return c.GatewayAPI().GatewayV1beta1().GatewayClasses().(ktypes.WriteAPI[T]) - case *sigsk8siogatewayapiapisv1beta1.HTTPRoute: - return c.GatewayAPI().GatewayV1beta1().HTTPRoutes(namespace).(ktypes.WriteAPI[T]) + case *sigsk8siogatewayapiapisv1.GatewayClass: + return c.GatewayAPI().GatewayV1().GatewayClasses().(ktypes.WriteAPI[T]) + case *sigsk8siogatewayapiapisv1.HTTPRoute: + return c.GatewayAPI().GatewayV1().HTTPRoutes(namespace).(ktypes.WriteAPI[T]) case *k8sioapiautoscalingv2.HorizontalPodAutoscaler: return c.Kube().AutoscalingV2().HorizontalPodAutoscalers(namespace).(ktypes.WriteAPI[T]) case *sigsk8siogatewayapiinferenceextensionapiv1.InferencePool: @@ -92,8 +92,8 @@ func GetWriteClient[T runtime.Object](c ClientGetter, namespace string) ktypes.W return c.Kube().NetworkingV1().Ingresses(namespace).(ktypes.WriteAPI[T]) case *k8sioapinetworkingv1.IngressClass: return c.Kube().NetworkingV1().IngressClasses().(ktypes.WriteAPI[T]) - case *sigsk8siogatewayapiapisv1beta1.Gateway: - return c.GatewayAPI().GatewayV1beta1().Gateways(namespace).(ktypes.WriteAPI[T]) + case *sigsk8siogatewayapiapisv1.Gateway: + return c.GatewayAPI().GatewayV1().Gateways(namespace).(ktypes.WriteAPI[T]) case *k8sioapicoordinationv1.Lease: return c.Kube().CoordinationV1().Leases(namespace).(ktypes.WriteAPI[T]) case *k8sioapiadmissionregistrationv1.MutatingWebhookConfiguration: @@ -183,10 +183,10 @@ func GetClient[T, TL runtime.Object](c ClientGetter, namespace string) ktypes.Re return c.GatewayAPI().GatewayV1().GRPCRoutes(namespace).(ktypes.ReadWriteAPI[T, TL]) case *apiistioioapinetworkingv1.Gateway: return c.Istio().NetworkingV1().Gateways(namespace).(ktypes.ReadWriteAPI[T, TL]) - case *sigsk8siogatewayapiapisv1beta1.GatewayClass: - return c.GatewayAPI().GatewayV1beta1().GatewayClasses().(ktypes.ReadWriteAPI[T, TL]) - case *sigsk8siogatewayapiapisv1beta1.HTTPRoute: - return c.GatewayAPI().GatewayV1beta1().HTTPRoutes(namespace).(ktypes.ReadWriteAPI[T, TL]) + case *sigsk8siogatewayapiapisv1.GatewayClass: + return c.GatewayAPI().GatewayV1().GatewayClasses().(ktypes.ReadWriteAPI[T, TL]) + case *sigsk8siogatewayapiapisv1.HTTPRoute: + return c.GatewayAPI().GatewayV1().HTTPRoutes(namespace).(ktypes.ReadWriteAPI[T, TL]) case *k8sioapiautoscalingv2.HorizontalPodAutoscaler: return c.Kube().AutoscalingV2().HorizontalPodAutoscalers(namespace).(ktypes.ReadWriteAPI[T, TL]) case *sigsk8siogatewayapiinferenceextensionapiv1.InferencePool: @@ -195,8 +195,8 @@ func GetClient[T, TL runtime.Object](c ClientGetter, namespace string) ktypes.Re return c.Kube().NetworkingV1().Ingresses(namespace).(ktypes.ReadWriteAPI[T, TL]) case *k8sioapinetworkingv1.IngressClass: return c.Kube().NetworkingV1().IngressClasses().(ktypes.ReadWriteAPI[T, TL]) - case *sigsk8siogatewayapiapisv1beta1.Gateway: - return c.GatewayAPI().GatewayV1beta1().Gateways(namespace).(ktypes.ReadWriteAPI[T, TL]) + case *sigsk8siogatewayapiapisv1.Gateway: + return c.GatewayAPI().GatewayV1().Gateways(namespace).(ktypes.ReadWriteAPI[T, TL]) case *k8sioapicoordinationv1.Lease: return c.Kube().CoordinationV1().Leases(namespace).(ktypes.ReadWriteAPI[T, TL]) case *k8sioapiadmissionregistrationv1.MutatingWebhookConfiguration: @@ -287,9 +287,9 @@ func gvrToObject(g schema.GroupVersionResource) runtime.Object { case gvr.Gateway: return &apiistioioapinetworkingv1.Gateway{} case gvr.GatewayClass: - return &sigsk8siogatewayapiapisv1beta1.GatewayClass{} + return &sigsk8siogatewayapiapisv1.GatewayClass{} case gvr.HTTPRoute: - return &sigsk8siogatewayapiapisv1beta1.HTTPRoute{} + return &sigsk8siogatewayapiapisv1.HTTPRoute{} case gvr.HorizontalPodAutoscaler: return &k8sioapiautoscalingv2.HorizontalPodAutoscaler{} case gvr.InferencePool: @@ -299,7 +299,7 @@ func gvrToObject(g schema.GroupVersionResource) runtime.Object { case gvr.IngressClass: return &k8sioapinetworkingv1.IngressClass{} case gvr.KubernetesGateway: - return &sigsk8siogatewayapiapisv1beta1.Gateway{} + return &sigsk8siogatewayapiapisv1.Gateway{} case gvr.Lease: return &k8sioapicoordinationv1.Lease{} case gvr.MutatingWebhookConfiguration: @@ -464,17 +464,17 @@ func getInformerFiltered(c ClientGetter, opts ktypes.InformerOptions, g schema.G } case gvr.GatewayClass: l = func(options metav1.ListOptions) (runtime.Object, error) { - return c.GatewayAPI().GatewayV1beta1().GatewayClasses().List(context.Background(), options) + return c.GatewayAPI().GatewayV1().GatewayClasses().List(context.Background(), options) } w = func(options metav1.ListOptions) (watch.Interface, error) { - return c.GatewayAPI().GatewayV1beta1().GatewayClasses().Watch(context.Background(), options) + return c.GatewayAPI().GatewayV1().GatewayClasses().Watch(context.Background(), options) } case gvr.HTTPRoute: l = func(options metav1.ListOptions) (runtime.Object, error) { - return c.GatewayAPI().GatewayV1beta1().HTTPRoutes(opts.Namespace).List(context.Background(), options) + return c.GatewayAPI().GatewayV1().HTTPRoutes(opts.Namespace).List(context.Background(), options) } w = func(options metav1.ListOptions) (watch.Interface, error) { - return c.GatewayAPI().GatewayV1beta1().HTTPRoutes(opts.Namespace).Watch(context.Background(), options) + return c.GatewayAPI().GatewayV1().HTTPRoutes(opts.Namespace).Watch(context.Background(), options) } case gvr.HorizontalPodAutoscaler: l = func(options metav1.ListOptions) (runtime.Object, error) { @@ -506,10 +506,10 @@ func getInformerFiltered(c ClientGetter, opts ktypes.InformerOptions, g schema.G } case gvr.KubernetesGateway: l = func(options metav1.ListOptions) (runtime.Object, error) { - return c.GatewayAPI().GatewayV1beta1().Gateways(opts.Namespace).List(context.Background(), options) + return c.GatewayAPI().GatewayV1().Gateways(opts.Namespace).List(context.Background(), options) } w = func(options metav1.ListOptions) (watch.Interface, error) { - return c.GatewayAPI().GatewayV1beta1().Gateways(opts.Namespace).Watch(context.Background(), options) + return c.GatewayAPI().GatewayV1().Gateways(opts.Namespace).Watch(context.Background(), options) } case gvr.Lease: l = func(options metav1.ListOptions) (runtime.Object, error) { diff --git a/pkg/config/schema/kubetypes/resources.gen.go b/pkg/config/schema/kubetypes/resources.gen.go index 70f5fd85e5..25e7a4c9f4 100755 --- a/pkg/config/schema/kubetypes/resources.gen.go +++ b/pkg/config/schema/kubetypes/resources.gen.go @@ -74,9 +74,9 @@ func getGvk(obj any) (config.GroupVersionKind, bool) { return gvk.Gateway, true case *apiistioioapinetworkingv1.Gateway: return gvk.Gateway, true - case *sigsk8siogatewayapiapisv1beta1.GatewayClass: + case *sigsk8siogatewayapiapisv1.GatewayClass: return gvk.GatewayClass, true - case *sigsk8siogatewayapiapisv1beta1.HTTPRoute: + case *sigsk8siogatewayapiapisv1.HTTPRoute: return gvk.HTTPRoute, true case *k8sioapiautoscalingv2.HorizontalPodAutoscaler: return gvk.HorizontalPodAutoscaler, true @@ -86,7 +86,7 @@ func getGvk(obj any) (config.GroupVersionKind, bool) { return gvk.Ingress, true case *k8sioapinetworkingv1.IngressClass: return gvk.IngressClass, true - case *sigsk8siogatewayapiapisv1beta1.Gateway: + case *sigsk8siogatewayapiapisv1.Gateway: return gvk.KubernetesGateway, true case *k8sioapicoordinationv1.Lease: return gvk.Lease, true diff --git a/pkg/config/schema/metadata.yaml b/pkg/config/schema/metadata.yaml index 722b2b3ec8..3dab1da8bb 100644 --- a/pkg/config/schema/metadata.yaml +++ b/pkg/config/schema/metadata.yaml @@ -217,41 +217,41 @@ resources: - kind: "GatewayClass" plural: "gatewayclasses" group: "gateway.networking.k8s.io" - version: "v1beta1" + version: "v1" versionAliases: - "v1alpha2" - - "v1" + - "v1beta1" clusterScoped: true - protoPackage: "sigs.k8s.io/gateway-api/apis/v1beta1" + protoPackage: "sigs.k8s.io/gateway-api/apis/v1" proto: "k8s.io.gateway_api.api.v1alpha1.GatewayClassSpec" statusProto: "k8s.io.gateway_api.api.v1alpha1.GatewayClassStatus" - statusProtoPackage: "sigs.k8s.io/gateway-api/apis/v1beta1" + statusProtoPackage: "sigs.k8s.io/gateway-api/apis/v1" - kind: "Gateway" identifier: KubernetesGateway plural: "gateways" group: "gateway.networking.k8s.io" - version: "v1beta1" + version: "v1" versionAliases: - "v1alpha2" - - "v1" - protoPackage: "sigs.k8s.io/gateway-api/apis/v1beta1" + - "v1beta1" + protoPackage: "sigs.k8s.io/gateway-api/apis/v1" proto: "k8s.io.gateway_api.api.v1alpha1.GatewaySpec" validate: "validation.EmptyValidate" statusProto: "k8s.io.gateway_api.api.v1alpha1.GatewayStatus" - statusProtoPackage: "sigs.k8s.io/gateway-api/apis/v1beta1" + statusProtoPackage: "sigs.k8s.io/gateway-api/apis/v1" - kind: "HTTPRoute" plural: "httproutes" group: "gateway.networking.k8s.io" - version: "v1beta1" + version: "v1" versionAliases: - "v1alpha2" - - "v1" - protoPackage: "sigs.k8s.io/gateway-api/apis/v1beta1" + - "v1beta1" + protoPackage: "sigs.k8s.io/gateway-api/apis/v1" proto: "k8s.io.gateway_api.api.v1alpha1.HTTPRouteSpec" statusProto: "k8s.io.gateway_api.api.v1alpha1.HTTPRouteStatus" - statusProtoPackage: "sigs.k8s.io/gateway-api/apis/v1beta1" + statusProtoPackage: "sigs.k8s.io/gateway-api/apis/v1" - kind: "InferencePool" plural: "inferencepools" diff --git a/pkg/config/xds/filter_types.gen.go b/pkg/config/xds/filter_types.gen.go index e71d6ffe72..ace9960d99 100644 --- a/pkg/config/xds/filter_types.gen.go +++ b/pkg/config/xds/filter_types.gen.go @@ -1,4 +1,5 @@ //go:build !agent + // Copyright Istio Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -73,9 +74,11 @@ import ( _ "github.com/envoyproxy/go-control-plane/envoy/data/tap/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/filters/cel/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/filters/process_ratelimit/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/fluentd/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/grpc/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/open_telemetry/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/stats/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/stream/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/wasm/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/bootstrap/internal_listener/v3" @@ -166,6 +169,7 @@ import ( _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/stateful_session/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/tap/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/thrift_to_metadata/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/transform/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/upstream_codec/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/wasm/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/http_inspector/v3" @@ -267,6 +271,7 @@ import ( _ "github.com/envoyproxy/go-control-plane/envoy/extensions/matching/common_inputs/network/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/matching/common_inputs/ssl/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/matching/common_inputs/stats/v3" + _ "github.com/envoyproxy/go-control-plane/envoy/extensions/matching/common_inputs/transport_socket/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/matching/input_matchers/consistent_hashing/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/matching/input_matchers/ip/v3" _ "github.com/envoyproxy/go-control-plane/envoy/extensions/matching/input_matchers/metadata/v3" diff --git a/pkg/config/xds/filter_types.go b/pkg/config/xds/filter_types.go index 33acd010a0..c604016214 100644 --- a/pkg/config/xds/filter_types.go +++ b/pkg/config/xds/filter_types.go @@ -15,6 +15,7 @@ // nolint: lll // //go:generate sh -c "echo '//go:build !agent' > filter_types.gen.go" +//go:generate sh -c "echo '' >> filter_types.gen.go" //go:generate sh -c "echo '// Copyright Istio Authors' >> filter_types.gen.go" //go:generate sh -c "echo '//' >> filter_types.gen.go" //go:generate sh -c "echo '// Licensed under the Apache License, Version 2.0 (the \"License\");' >> filter_types.gen.go" diff --git a/pkg/dns/server/name_table.go b/pkg/dns/server/name_table.go index 3e55bbf6b3..55788a83f2 100644 --- a/pkg/dns/server/name_table.go +++ b/pkg/dns/server/name_table.go @@ -17,6 +17,8 @@ package server import ( "strings" + "k8s.io/apimachinery/pkg/types" + "istio.io/istio/pilot/pkg/model" "istio.io/istio/pilot/pkg/serviceregistry/provider" "istio.io/istio/pkg/config/constants" @@ -62,6 +64,9 @@ func BuildNameTable(cfg Config) *dnsProto.NameTable { // The IP will be unspecified here if its headless service or if the auto // IP allocation logic for service entry was unable to allocate an IP. if svc.Resolution == model.Passthrough && len(svc.Ports) > 0 { + localAddresses := make(map[string][]string) + remoteAddresses := make(map[string][]string) + hostMetadata := make(map[string]types.NamespacedName) for _, instance := range cfg.Push.ServiceEndpointsByPort(svc, svc.Ports[0].Port, nil) { // addresses may be empty or invalid here isValidInstance := true @@ -87,21 +92,13 @@ func BuildNameTable(cfg Config) *dnsProto.NameTable { if len(parts) != 2 { continue } - address := instance.Addresses shortName := instance.HostName + "." + instance.SubDomain host := shortName + "." + parts[1] // Add cluster domain. - nameInfo := &dnsProto.NameTable_NameInfo{ - Ips: address, - Registry: string(svc.Attributes.ServiceRegistry), - Namespace: svc.Attributes.Namespace, - Shortname: shortName, - } - - if _, f := out.Table[host]; !f || sameCluster { - // We may have the same pod in two clusters (ie mysql-0 deployed in both places). - // We can only return a single IP for these queries. We should prefer the local cluster, - // so if the entry already exists only overwrite it if the instance is in our own cluster. - out.Table[host] = nameInfo + hostMetadata[host] = types.NamespacedName{Name: shortName, Namespace: svc.Attributes.Namespace} + if sameCluster { + localAddresses[host] = append(localAddresses[host], instance.Addresses...) + } else { + remoteAddresses[host] = append(remoteAddresses[host], instance.Addresses...) } } skipForMulticluster := !cfg.MulticlusterHeadlessEnabled && !sameCluster @@ -119,6 +116,28 @@ func BuildNameTable(cfg Config) *dnsProto.NameTable { // TODO: should we skip the node's own IP like we do in listener? addressList = append(addressList, instance.Addresses...) } + // Write local cluster entries first + for host, ips := range localAddresses { + meta := hostMetadata[host] + out.Table[host] = &dnsProto.NameTable_NameInfo{ + Ips: ips, + Registry: string(svc.Attributes.ServiceRegistry), + Namespace: meta.Namespace, + Shortname: meta.Name, + } + } + // Write remote cluster entries only if local doesn't exist + for host, ips := range remoteAddresses { + if _, exists := localAddresses[host]; !exists { + meta := hostMetadata[host] + out.Table[host] = &dnsProto.NameTable_NameInfo{ + Ips: ips, + Registry: string(svc.Attributes.ServiceRegistry), + Namespace: meta.Namespace, + Shortname: meta.Name, + } + } + } } } if len(addressList) == 0 { diff --git a/pkg/dns/server/name_table_test.go b/pkg/dns/server/name_table_test.go index adf907fd96..d65314f8af 100644 --- a/pkg/dns/server/name_table_test.go +++ b/pkg/dns/server/name_table_test.go @@ -695,3 +695,109 @@ func makeInstances(proxy *model.Proxy, svc *model.Service, servicePort int, targ } return ret } + +func TestPodNameTableLocalRemoteAddresses(t *testing.T) { + mesh := &meshconfig.MeshConfig{RootNamespace: "istio-system"} + + headlessService := &model.Service{ + Hostname: host.Name("headless-svc.testns.svc.cluster.local"), + DefaultAddress: constants.UnspecifiedIP, + Ports: model.PortList{&model.Port{ + Name: "tcp-port", + Port: 9000, + Protocol: protocol.TCP, + }}, + Resolution: model.Passthrough, + Attributes: model.ServiceAttributes{ + Name: "headless-svc", + Namespace: "testns", + ServiceRegistry: provider.Kubernetes, + }, + } + + cases := []struct { + name string + proxyCluster cluster.ID + instances []*model.Proxy + expectedEntry string + expectedIPs []string + }{ + { + name: "cumulative IPs for same pod in local cluster", + proxyCluster: "cluster1", + instances: []*model.Proxy{ + {IPAddresses: []string{"10.0.0.1"}, Metadata: &model.NodeMetadata{ClusterID: "cluster1"}, Type: model.SidecarProxy, DNSDomain: "testns.svc.cluster.local"}, + {IPAddresses: []string{"10.0.0.2"}, Metadata: &model.NodeMetadata{ClusterID: "cluster1"}, Type: model.SidecarProxy, DNSDomain: "testns.svc.cluster.local"}, + }, + expectedEntry: "mysql-0.headless-svc.testns.svc.cluster.local", + expectedIPs: []string{"10.0.0.1", "10.0.0.2"}, + }, + { + name: "cumulative IPs for same pod in remote cluster", + proxyCluster: "cluster1", + instances: []*model.Proxy{ + {IPAddresses: []string{"10.0.0.3"}, Metadata: &model.NodeMetadata{ClusterID: "cluster2"}, Type: model.SidecarProxy, DNSDomain: "testns.svc.cluster.local"}, + {IPAddresses: []string{"10.0.0.4"}, Metadata: &model.NodeMetadata{ClusterID: "cluster2"}, Type: model.SidecarProxy, DNSDomain: "testns.svc.cluster.local"}, + }, + expectedEntry: "mysql-0.headless-svc.testns.svc.cluster.local", + expectedIPs: []string{"10.0.0.3", "10.0.0.4"}, + }, + { + name: "local cluster preferred over remote", + proxyCluster: "cluster1", + instances: []*model.Proxy{ + {IPAddresses: []string{"10.0.0.1"}, Metadata: &model.NodeMetadata{ClusterID: "cluster1"}, Type: model.SidecarProxy, DNSDomain: "testns.svc.cluster.local"}, + {IPAddresses: []string{"10.0.0.2"}, Metadata: &model.NodeMetadata{ClusterID: "cluster2"}, Type: model.SidecarProxy, DNSDomain: "testns.svc.cluster.local"}, + }, + expectedEntry: "mysql-0.headless-svc.testns.svc.cluster.local", + expectedIPs: []string{"10.0.0.1"}, + }, + { + name: "multiple local instances preferred over multiple remote", + proxyCluster: "cluster1", + instances: []*model.Proxy{ + {IPAddresses: []string{"10.0.0.1"}, Metadata: &model.NodeMetadata{ClusterID: "cluster1"}, Type: model.SidecarProxy, DNSDomain: "testns.svc.cluster.local"}, + {IPAddresses: []string{"10.0.0.2"}, Metadata: &model.NodeMetadata{ClusterID: "cluster1"}, Type: model.SidecarProxy, DNSDomain: "testns.svc.cluster.local"}, + {IPAddresses: []string{"10.0.0.3"}, Metadata: &model.NodeMetadata{ClusterID: "cluster2"}, Type: model.SidecarProxy, DNSDomain: "testns.svc.cluster.local"}, + {IPAddresses: []string{"10.0.0.4"}, Metadata: &model.NodeMetadata{ClusterID: "cluster2"}, Type: model.SidecarProxy, DNSDomain: "testns.svc.cluster.local"}, + }, + expectedEntry: "mysql-0.headless-svc.testns.svc.cluster.local", + expectedIPs: []string{"10.0.0.1", "10.0.0.2"}, + }, + } + + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + push := model.NewPushContext() + push.Mesh = mesh + push.AddPublicServices([]*model.Service{headlessService}) + for _, inst := range tt.instances { + push.AddServiceInstances(headlessService, + makeServiceInstances(inst, headlessService, "mysql-0", "headless-svc", model.Healthy)) + } + + proxy := &model.Proxy{ + IPAddresses: []string{"9.9.9.9"}, + Metadata: &model.NodeMetadata{ClusterID: tt.proxyCluster}, + Type: model.SidecarProxy, + DNSDomain: "testns.svc.cluster.local", + } + proxy.SetSidecarScope(push) + proxy.DiscoverIPMode() + + nameTable := dnsServer.BuildNameTable(dnsServer.Config{ + Node: proxy, + Push: push, + MulticlusterHeadlessEnabled: true, + }) + + entry, found := nameTable.Table[tt.expectedEntry] + if !found { + t.Fatalf("Expected entry %s not found", tt.expectedEntry) + } + if diff := cmp.Diff(entry.Ips, tt.expectedIPs); diff != "" { + t.Errorf("IPs mismatch (-got +want):\n%s", diff) + } + }) + } +} diff --git a/pkg/kube/krt/conformance_test.go b/pkg/kube/krt/conformance_test.go index bb2137f103..48e13d8e3c 100644 --- a/pkg/kube/krt/conformance_test.go +++ b/pkg/kube/krt/conformance_test.go @@ -136,8 +136,8 @@ func TestConformance(t *testing.T) { runConformance[Named](t, rig) }) t.Run("join", func(t *testing.T) { - col1 := krt.NewStaticCollection[Named](nil, nil, krt.WithStop(test.NewStop(t)), krt.WithDebugging(krt.GlobalDebugHandler)) - col2 := krt.NewStaticCollection[Named](nil, nil, krt.WithStop(test.NewStop(t)), krt.WithDebugging(krt.GlobalDebugHandler)) + col1 := krt.NewStaticCollection[Named](nil, nil, krt.WithStop(test.NewStop(t)), krt.WithDebugging(krt.GlobalDebugHandler), krt.WithName("a")) + col2 := krt.NewStaticCollection[Named](nil, nil, krt.WithStop(test.NewStop(t)), krt.WithDebugging(krt.GlobalDebugHandler), krt.WithName("b")) j := krt.JoinCollection( []krt.Collection[Named]{col1, col2}, krt.WithStop(test.NewStop(t)), diff --git a/pkg/kube/krt/informer.go b/pkg/kube/krt/informer.go index ac65b0a63b..af487d62ce 100644 --- a/pkg/kube/krt/informer.go +++ b/pkg/kube/krt/informer.go @@ -239,12 +239,12 @@ func WrapClient[I controllers.ComparableObject](c kclient.Informer[I], opts ...C // kube.Client before this method is called, otherwise // NewInformer will panic. func NewInformer[I controllers.ComparableObject](c kube.Client, opts ...CollectionOption) Collection[I] { - return NewInformerFiltered[I](c, kubetypes.Filter{}, opts...) + return NewFilteredInformer[I](c, kubetypes.Filter{}, opts...) } -// NewInformerFiltered takes an argument that filters the +// NewFilteredInformer takes an argument that filters the // results from the kube.Client. Otherwise, behaves // the same as NewInformer -func NewInformerFiltered[I controllers.ComparableObject](c kube.Client, filter kubetypes.Filter, opts ...CollectionOption) Collection[I] { +func NewFilteredInformer[I controllers.ComparableObject](c kube.Client, filter kubetypes.Filter, opts ...CollectionOption) Collection[I] { return WrapClient[I](kclient.NewFiltered[I](c, filter), opts...) } diff --git a/pkg/kube/krt/internal.go b/pkg/kube/krt/internal.go index 3e8903aa4f..79d00896ce 100644 --- a/pkg/kube/krt/internal.go +++ b/pkg/kube/krt/internal.go @@ -64,6 +64,11 @@ func buildCollectionOptions(opts ...CollectionOption) collectionOptions { o(c) } if c.stop == nil { + if len(opts) > 0 { + log.Debugf("collection %s did not have a stop channel in opts, creating a default which may cause goroutines to leak", c.name) + } else { + log.Debugf("collection was created with no opts, creating a default stop channel which may cause goroutines to leak") + } c.stop = make(chan struct{}) } return *c diff --git a/pkg/kube/krt/join.go b/pkg/kube/krt/join.go index 825affba32..fc79d1d006 100644 --- a/pkg/kube/krt/join.go +++ b/pkg/kube/krt/join.go @@ -16,13 +16,14 @@ package krt import ( "fmt" + "sync" + "istio.io/istio/pkg/kube/controllers" "istio.io/istio/pkg/ptr" "istio.io/istio/pkg/slices" "istio.io/istio/pkg/util/sets" ) -// TODO: Implement with merging type join[T any] struct { collectionName string id collectionUID @@ -31,6 +32,17 @@ type join[T any] struct { uncheckedOverlap bool syncer Syncer metadata Metadata + + // mu protects both eventHandlers and processedState + // but mu and processedState are ignored when uncheckedOverlap is true + mu sync.RWMutex + // processedState tracks objects we've processed via handleSubCollectionEvents + // This ensures RegisterBatch only sends events for objects that have been fully processed, + // avoiding duplicates from in-flight sub-collection events + processedState map[string]*T + eventHandlers *handlerSet[T] + + stop <-chan struct{} } func (j *join[T]) GetKey(k string) *T { @@ -90,28 +102,177 @@ func (j *join[T]) Register(f func(o Event[T])) HandlerRegistration { } func (j *join[T]) RegisterBatch(f func(o []Event[T]), runExistingState bool) HandlerRegistration { - sync := multiSyncer{} - removes := []func(){} - for _, c := range j.collections { - reg := c.RegisterBatch(f, runExistingState) - removes = append(removes, reg.UnregisterHandler) - sync.syncers = append(sync.syncers, reg) + // Fast path for unchecked overlap: use List() directly without locking or tracking processedState + if j.uncheckedOverlap { + var initialEvents []Event[T] + if runExistingState { + for _, obj := range j.List() { + objCopy := obj + initialEvents = append(initialEvents, Event[T]{ + New: &objCopy, + Event: controllers.EventAdd, + }) + } + } + reg := j.eventHandlers.Insert(f, j, initialEvents, j.stop) + return reg } - return joinHandlerRegistration{ - Syncer: sync, - removes: removes, + + j.mu.Lock() + defer j.mu.Unlock() + + // If we need to run existing state, gather it from processedState instead of List() + // This prevents race where we send events for objects that haven't been processed + // by handleSubCollectionEvents yet (they're in sub-collection state but event is in-flight) + var initialEvents []Event[T] + if runExistingState { + for _, obj := range j.processedState { + initialEvents = append(initialEvents, Event[T]{ + New: obj, + Event: controllers.EventAdd, + }) + } } + + // Register the handler and send initial events if needed + // eventHandlers has its own lock, sub-collection handlers are already registered in the constructor + reg := j.eventHandlers.Insert(f, j, initialEvents, j.stop) + return reg } -type joinHandlerRegistration struct { - Syncer - removes []func() +// handleSubCollectionEvents processes events from a sub-collection, refreshing them +// based on the current state of all collections, then distributes to registered handlers. +func (j *join[T]) handleSubCollectionEvents(events []Event[T], sourceCollectionIdx int) { + // Fast path for unchecked overlap: no conflict resolution, no state tracking, no locking + if j.uncheckedOverlap { + j.eventHandlers.Distribute(events, !j.HasSynced()) + return + } + + j.mu.Lock() + defer j.mu.Unlock() + + refreshedEvents := j.refreshEvents(events, sourceCollectionIdx) + + if len(refreshedEvents) == 0 { + return + } + + // Update processedState to track what we've processed + // This is used by RegisterBatch to provide consistent initial state + for _, ev := range refreshedEvents { + key := GetKey(ev.Latest()) + if ev.Event == controllers.EventDelete { + delete(j.processedState, key) + } else { + // For Add and Update, store the new object pointer (no copy) + j.processedState[key] = ev.New + } + } + + j.eventHandlers.Distribute(refreshedEvents, !j.HasSynced()) } -func (j joinHandlerRegistration) UnregisterHandler() { - for _, remover := range j.removes { - remover() +func (j *join[T]) getFromColIdx(idx int, key string) *T { + if idx < 0 || idx >= len(j.collections) { + if EnableAssertions { + panic("join: getFromColIdx: index out of range:" + fmt.Sprint(idx) + " len: " + fmt.Sprint(len(j.collections))) + } + return nil } + obj := j.collections[idx].GetKey(key) + if obj == nil { + return nil + } + + // HACK: StaticCollectoin (which we use in test) does not care what key you pass to Get + if GetKey(*obj) != key { + if EnableAssertions { + panic("join: getFromColIdx: collection returned object with wrong key. Wanted: " + key + " got: " + GetKey(*obj)) + } + return nil + } + + return obj +} + +// refreshEvents refreshes events by checking the current state of all collections +// to determine which collection is authoritative for each key. This implements +// conflict resolution without storing objects. +func (j *join[T]) refreshEvents(events []Event[T], sourceCollectionIdx int) []Event[T] { + var result []Event[T] + + for _, ev := range events { + key := GetKey(ev.Latest()) + + // Check if any higher-priority collection (0...sourceCollectionIdx-1) has this key + hasHigherPriority := false + + for i := range sourceCollectionIdx { + if o := j.getFromColIdx(i, key); o != nil { + hasHigherPriority = true + break + } + } + + if ev.Event == controllers.EventDelete { + if hasHigherPriority { + // Drop delete event - we weren't using this collection's version anyway + continue + } + + // Collection sourceCollectionIdx was authoritative. Check for fallback in lower-priority collections. + var fallbackObj *T + for i := sourceCollectionIdx + 1; i < len(j.collections); i++ { + if obj := j.getFromColIdx(i, key); obj != nil { + fallbackObj = obj + break + } + } + + if fallbackObj != nil { + // Convert DELETE to UPDATE - fallback to lower-priority collection's version + result = append(result, Event[T]{ + Event: controllers.EventUpdate, + Old: ev.Old, + New: fallbackObj, // pointer from collection, not a copy! + }) + } else { + // No fallback found, send DELETE + result = append(result, ev) + } + } else { + // ADD or UPDATE event + if hasHigherPriority { + // Drop event - higher priority collection owns this key + continue + } + + // No higher-priority collection has it, so this collection is now authoritative + // Check if a lower-priority collection had this key before + var oldObj *T + for i := sourceCollectionIdx + 1; i < len(j.collections); i++ { + if obj := j.getFromColIdx(i, key); obj != nil { + oldObj = obj + break + } + } + + if oldObj != nil && ev.Event == controllers.EventAdd { + // Convert ADD to UPDATE - we're replacing a lower-priority collection's version + result = append(result, Event[T]{ + Event: controllers.EventUpdate, + Old: oldObj, + New: ev.New, + }) + } else { + // Forward the event as-is + result = append(result, ev) + } + } + } + + return result } // nolint: unused // (not true, its to implement an interface) @@ -184,8 +345,8 @@ func (j *join[T]) Metadata() Metadata { } // JoinCollection combines multiple Collection[T] into a single -// Collection[T] merging equal objects into one record -// in the resulting Collection +// Collection[T]. Key conflicts are resolved by picking the item +// produced by the first collections in the list of input collections. func JoinCollection[T any](cs []Collection[T], opts ...CollectionOption) Collection[T] { o := buildCollectionOptions(opts...) if o.name == "" { @@ -204,13 +365,18 @@ func JoinCollection[T any](cs []Collection[T], opts ...CollectionOption) Collect close(synced) log.Infof("%v synced", o.name) }() - // TODO: in the future, we could have a custom merge function. For now, since we just take the first, we optimize around that case + if o.stop == nil { + panic("no stop channel") + } j := &join[T]{ collectionName: o.name, id: nextUID(), synced: synced, collections: c, uncheckedOverlap: o.joinUnchecked, + eventHandlers: newHandlerSet[T](), + processedState: make(map[string]*T), + stop: o.stop, syncer: channelSyncer{ name: o.name, synced: synced, @@ -221,5 +387,26 @@ func JoinCollection[T any](cs []Collection[T], opts ...CollectionOption) Collect j.metadata = o.metadata } + // Register handlers on sub-collections ONCE during construction + // These handlers will process events from sub-collections and distribute to registered handlers + var subHandlerRegs []HandlerRegistration + for idx, subCol := range c { + collectionIdx := idx // capture for closure + handler := func(events []Event[T]) { + j.handleSubCollectionEvents(events, collectionIdx) + } + // Don't run existing state - we handle that in RegisterBatch + reg := subCol.RegisterBatch(handler, false) + subHandlerRegs = append(subHandlerRegs, reg) + } + + // Clean up sub-collection handlers when this collection's stop is closed + go func() { + <-o.stop + for _, reg := range subHandlerRegs { + reg.UnregisterHandler() + } + }() + return j } diff --git a/pkg/kube/krt/join_test.go b/pkg/kube/krt/join_test.go index 35511f867d..924bbbd9b6 100644 --- a/pkg/kube/krt/join_test.go +++ b/pkg/kube/krt/join_test.go @@ -15,6 +15,7 @@ package krt_test import ( + "sync" "testing" "go.uber.org/atomic" @@ -24,6 +25,7 @@ import ( istio "istio.io/api/networking/v1alpha3" istioclient "istio.io/client-go/pkg/apis/networking/v1" "istio.io/istio/pkg/kube" + "istio.io/istio/pkg/kube/controllers" "istio.io/istio/pkg/kube/kclient" "istio.io/istio/pkg/kube/kclient/clienttest" "istio.io/istio/pkg/kube/krt" @@ -32,14 +34,32 @@ import ( "istio.io/istio/pkg/test/util/assert" ) +// NamedValue has the same resource name (from Named) but different values +// for testing conflict resolution with overlapping keys +type NamedValue struct { + Named + Value string +} + func TestJoinCollection(t *testing.T) { - opts := testOptions(t) - c1 := krt.NewStatic[Named](nil, true) - c2 := krt.NewStatic[Named](nil, true) - c3 := krt.NewStatic[Named](nil, true) + t.Run("checked", func(t *testing.T) { + testJoinInner(t) + }) + t.Run("unchecked", func(t *testing.T) { + testJoinInner(t, krt.WithJoinUnchecked()) + }) +} + +func testJoinInner(t *testing.T, options ...krt.CollectionOption) { + options = append(options, krt.WithName("JoinTest")) + opts := testOptions(t).With(options...) + + c1 := krt.NewStatic[Named](nil, true, krt.WithName("c1")) + c2 := krt.NewStatic[Named](nil, true, krt.WithName("c2")) + c3 := krt.NewStatic[Named](nil, true, krt.WithName("c3")) j := krt.JoinCollection( []krt.Collection[Named]{c1.AsCollection(), c2.AsCollection(), c3.AsCollection()}, - opts.WithName("Join")..., + opts..., ) last := atomic.NewString("") j.Register(func(o krt.Event[Named]) { @@ -206,3 +226,102 @@ func TestCollectionJoinSync(t *testing.T) { {Named{"namespace", "name-static"}, NewLabeled(map[string]string{"app": "foo"}), "9.9.9.9"}, }) } + +// TestJoinCollectionConflictResolution tests conflict resolution with overlapping keys. +// Priority order: c1 > c2 > c3 (first collection wins) +func TestJoinCollectionConflictResolution(t *testing.T) { + opts := testOptions(t) + c1 := krt.NewStatic[NamedValue](nil, true, krt.WithName("c1")) + c2 := krt.NewStatic[NamedValue](nil, true, krt.WithName("c2")) + c3 := krt.NewStatic[NamedValue](nil, true, krt.WithName("c3")) + + j := krt.JoinCollection( + []krt.Collection[NamedValue]{c1.AsCollection(), c2.AsCollection(), c3.AsCollection()}, + opts.WithName("Join")..., + ) + + var mu sync.Mutex + events := []krt.Event[NamedValue]{} + j.Register(func(o krt.Event[NamedValue]) { + mu.Lock() + defer mu.Unlock() + events = append(events, o) + }) + + getEventsLen := func() int { + mu.Lock() + defer mu.Unlock() + return len(events) + } + getEvent := func(i int) krt.Event[NamedValue] { + mu.Lock() + defer mu.Unlock() + return events[i] + } + clearEvents := func() { + mu.Lock() + defer mu.Unlock() + events = nil + } + + // Test 1: c2 adds key "a" first - should see ADD + c2.Set(&NamedValue{Named{"ns", "a"}, "c2-value"}) + assert.EventuallyEqual(t, getEventsLen, 1) + assert.Equal(t, getEvent(0).Event, controllers.EventAdd) + assert.Equal(t, getEvent(0).New.Value, "c2-value") + + // Test 2: c1 adds same key - should see UPDATE (c1 has higher priority) + clearEvents() + c1.Set(&NamedValue{Named{"ns", "a"}, "c1-value"}) + assert.EventuallyEqual(t, getEventsLen, 1) + assert.Equal(t, getEvent(0).Event, controllers.EventUpdate) + assert.Equal(t, getEvent(0).Old.Value, "c2-value") + assert.Equal(t, getEvent(0).New.Value, "c1-value") + + // Test 3: c3 adds same key - should see NO event (c1 has priority) + clearEvents() + c3.Set(&NamedValue{Named{"ns", "a"}, "c3-value"}) + assert.Consistently(t, getEventsLen, 0) + assert.Equal(t, j.GetKey("ns/a").Value, "c1-value") + + // Test 4: c2 updates - should see NO event (c1 still owns it) + clearEvents() + c2.Set(&NamedValue{Named{"ns", "a"}, "c2-updated"}) + assert.Consistently(t, getEventsLen, 0) + + // Test 5: c1 deletes - should see UPDATE to c2's version (fallback) + clearEvents() + c1.Set(nil) + assert.EventuallyEqual(t, getEventsLen, 1) + assert.Equal(t, getEvent(0).Event, controllers.EventUpdate) + assert.Equal(t, getEvent(0).Old.Value, "c1-value") + assert.Equal(t, getEvent(0).New.Value, "c2-updated") + + // Test 6: c2 deletes - should see UPDATE to c3's version + clearEvents() + c2.Set(nil) + assert.EventuallyEqual(t, getEventsLen, 1) + assert.Equal(t, getEvent(0).Event, controllers.EventUpdate) + assert.Equal(t, getEvent(0).Old.Value, "c2-updated") + assert.Equal(t, getEvent(0).New.Value, "c3-value") + + // Test 7: c3 deletes - should see DELETE (no more fallbacks) + clearEvents() + c3.Set(nil) + assert.EventuallyEqual(t, getEventsLen, 1) + assert.Equal(t, getEvent(0).Event, controllers.EventDelete) + assert.Equal(t, getEvent(0).Old.Value, "c3-value") + + // Test 8: Delete from lower priority collection when higher priority owns it - NO event + clearEvents() + c1.Set(&NamedValue{Named{"ns", "b"}, "c1-b"}) + assert.EventuallyEqual(t, getEventsLen, 1) // Wait for c1's ADD + + clearEvents() + c2.Set(&NamedValue{Named{"ns", "b"}, "c2-b"}) + assert.Consistently(t, getEventsLen, 0) // c2 adding should be ignored + + c2.Set(nil) // c2 deletes but c1 still owns it + assert.Consistently(t, getEventsLen, 0) // c2 delete should be ignored + assert.Equal(t, j.GetKey("ns/b").Value, "c1-b") +} diff --git a/pkg/kube/krt/krttest/helpers.go b/pkg/kube/krt/krttest/helpers.go index 6ccaa54f8b..b7ec3febae 100644 --- a/pkg/kube/krt/krttest/helpers.go +++ b/pkg/kube/krt/krttest/helpers.go @@ -101,7 +101,7 @@ func Options(t test.Failer) krt.OptionsBuilder { func kubernetesObjectsFromString(s string) ([]any, error) { var objects []any decode := kubelib.IstioCodec.UniversalDeserializer().Decode - objectStrs := strings.Split(s, "---") + objectStrs := strings.Split(s, "\n---\n") for _, s := range objectStrs { if len(strings.TrimSpace(s)) == 0 { continue diff --git a/pkg/kube/multicluster/cluster.go b/pkg/kube/multicluster/cluster.go index 0d8a72e716..8baa76c4a9 100644 --- a/pkg/kube/multicluster/cluster.go +++ b/pkg/kube/multicluster/cluster.go @@ -44,8 +44,12 @@ type Cluster struct { initialSync *atomic.Bool // initialSyncTimeout is set when RunAndWait timed out initialSyncTimeout *atomic.Bool + + syncStatusCallback SyncStatusCallback } +type SyncStatusCallback func(cluster.ID, string) + type ACTION int const ( @@ -53,6 +57,13 @@ const ( Update ) +const ( + SyncStatusSynced = "synced" + SyncStatusSyncing = "syncing" + SyncStatusTimeout = "timeout" + SyncStatusClosed = "closed" +) + func (a ACTION) String() string { switch a { case Add: @@ -66,6 +77,7 @@ func (a ACTION) String() string { // Run starts the cluster's informers and waits for caches to sync. Once caches are synced, we mark the cluster synced. // This should be called after each of the handlers have registered informers, and should be run in a goroutine. func (c *Cluster) Run(mesh mesh.Watcher, handlers []handler, action ACTION) { + c.reportStatus(SyncStatusSyncing) if features.RemoteClusterTimeout > 0 { time.AfterFunc(features.RemoteClusterTimeout, func() { if !c.initialSync.Load() { @@ -73,6 +85,7 @@ func (c *Cluster) Run(mesh mesh.Watcher, handlers []handler, action ACTION) { timeouts.With(clusterLabel.Value(string(c.ID))).Increment() } c.initialSyncTimeout.Store(true) + c.reportStatus(SyncStatusTimeout) }) } @@ -109,6 +122,7 @@ func (c *Cluster) Run(mesh mesh.Watcher, handlers []handler, action ACTION) { } c.initialSync.Store(true) + c.reportStatus(SyncStatusSynced) } // Stop closes the stop channel, if is safe to be called multi times. @@ -143,3 +157,22 @@ func (c *Cluster) Closed() bool { func (c *Cluster) SyncDidTimeout() bool { return !c.initialSync.Load() && c.initialSyncTimeout.Load() } + +func (c *Cluster) SyncStatus() string { + if c.Closed() { + return SyncStatusClosed + } + if c.SyncDidTimeout() { + return SyncStatusTimeout + } + if c.HasSynced() { + return SyncStatusSynced + } + return SyncStatusSyncing +} + +func (c *Cluster) reportStatus(status string) { + if c.syncStatusCallback != nil { + c.syncStatusCallback(c.ID, status) + } +} diff --git a/pkg/kube/multicluster/secretcontroller.go b/pkg/kube/multicluster/secretcontroller.go index 91a700d857..c62678bc06 100644 --- a/pkg/kube/multicluster/secretcontroller.go +++ b/pkg/kube/multicluster/secretcontroller.go @@ -45,6 +45,7 @@ const ( var ( clusterLabel = monitoring.CreateLabel("cluster") + statusLabel = monitoring.CreateLabel("status") timeouts = monitoring.NewSum( "remote_cluster_sync_timeouts_total", "Number of times remote clusters took too long to sync, causing slow startup that excludes remote clusters.", @@ -59,6 +60,19 @@ var ( localClusters = clustersCount.With(clusterType.Value("local")) remoteClusters = clustersCount.With(clusterType.Value("remote")) + + remoteClusterSyncState = monitoring.NewGauge( + "istiod_remote_cluster_sync_status", + "Current synchronization state of remote clusters managed by istiod. "+ + "One sample per cluster and state; a value of 1 indicates the cluster is in that state.", + ) + + eventLabel = monitoring.CreateLabel("event") + + secretEvents = monitoring.NewSum( + "remote_cluster_secret_events_total", + "Number of remote cluster secret events.", + ) ) type handler interface { @@ -144,7 +158,20 @@ func NewController(kubeclientset kube.Client, namespace string, clusterID cluste controller.queue = controllers.NewQueue("multicluster secret", controllers.WithReconciler(controller.processItem)) - secrets.AddEventHandler(controllers.ObjectHandler(controller.queue.AddObject)) + secrets.AddEventHandler(controllers.EventHandler[*corev1.Secret]{ + AddFunc: func(obj *corev1.Secret) { + secretEvents.With(eventLabel.Value("add")).Increment() + controller.queue.AddObject(obj) + }, + UpdateFunc: func(oldObj, newObj *corev1.Secret) { + secretEvents.With(eventLabel.Value("update")).Increment() + controller.queue.AddObject(newObj) + }, + DeleteFunc: func(obj *corev1.Secret) { + secretEvents.With(eventLabel.Value("delete")).Increment() + controller.queue.AddObject(obj) + }, + }) return controller } @@ -193,6 +220,49 @@ func (c *Controller) Run(stopCh <-chan struct{}) error { return nil } +func (c *Controller) onClusterSyncStatusChange(clusterID cluster.ID, status string) { + // Only record metrics for clusters we still manage. + // This avoids resurrecting metrics for clusters that have been deleted. + if !c.cs.Contains(clusterID) { + return + } + c.recordClusterSyncState(clusterID, status) +} + +func (c *Controller) recordClusterSyncState(clusterID cluster.ID, status string) { + for _, s := range []string{ + SyncStatusSynced, + SyncStatusSyncing, + SyncStatusTimeout, + SyncStatusClosed, + } { + v := 0.0 + if s == status { + v = 1.0 + } + remoteClusterSyncState.With( + clusterLabel.Value(string(clusterID)), + clusterType.Value("remote"), + statusLabel.Value(s), + ).Record(v) + } +} + +func (c *Controller) clearClusterSyncState(clusterID cluster.ID) { + for _, s := range []string{ + SyncStatusSynced, + SyncStatusSyncing, + SyncStatusTimeout, + SyncStatusClosed, + } { + remoteClusterSyncState.With( + clusterLabel.Value(string(clusterID)), + clusterType.Value("remote"), + statusLabel.Value(s), + ).Record(0.0) + } +} + func (c *Controller) HasSynced() bool { if !c.queue.HasSynced() { log.Debug("secret controller did not sync secrets presented at startup") @@ -258,6 +328,7 @@ func (c *Controller) createRemoteCluster(kubeConfig []byte, clusterID string) (* initialSync: atomic.NewBool(false), initialSyncTimeout: atomic.NewBool(false), kubeConfigSha: sha256.Sum256(kubeConfig), + syncStatusCallback: c.onClusterSyncStatusChange, }, nil } @@ -333,6 +404,7 @@ func (c *Controller) deleteCluster(secretKey string, cluster *Cluster) { cluster.Stop() c.handleDelete(cluster.ID) c.cs.Delete(secretKey, cluster.ID) + c.clearClusterSyncState(cluster.ID) cluster.Client.Shutdown() // Shutdown all of the informers so that the goroutines won't leak log.Infof("Number of remote clusters: %d", c.cs.Len()) @@ -355,9 +427,9 @@ func (c *Controller) handleDelete(key cluster.ID) { // ListRemoteClusters provides debug info about connected remote clusters. func (c *Controller) ListRemoteClusters() []cluster.DebugInfo { // Start with just the config cluster - configCluster := "syncing" + configCluster := SyncStatusSyncing if kube.AllSynced(c.configClusterSyncers) { - configCluster = "synced" + configCluster = SyncStatusSynced } out := []cluster.DebugInfo{{ ID: c.configClusterID, @@ -366,18 +438,10 @@ func (c *Controller) ListRemoteClusters() []cluster.DebugInfo { // Append each cluster derived from secrets for secretName, clusters := range c.cs.All() { for clusterID, c := range clusters { - syncStatus := "syncing" - if c.Closed() { - syncStatus = "closed" - } else if c.SyncDidTimeout() { - syncStatus = "timeout" - } else if c.HasSynced() { - syncStatus = "synced" - } out = append(out, cluster.DebugInfo{ ID: clusterID, SecretName: secretName, - SyncStatus: syncStatus, + SyncStatus: c.SyncStatus(), }) } } diff --git a/pkg/kube/multicluster/secretcontroller_test.go b/pkg/kube/multicluster/secretcontroller_test.go index 06bfb00cbc..0120b9aec7 100644 --- a/pkg/kube/multicluster/secretcontroller_test.go +++ b/pkg/kube/multicluster/secretcontroller_test.go @@ -163,9 +163,9 @@ func TestListRemoteClusters(t *testing.T) { // before sync assert.EventuallyEqual(t, c.controller.ListRemoteClusters, []cluster.DebugInfo{ - {ID: "config", SyncStatus: "syncing"}, - {ID: "c0", SecretName: "istio-system/s0", SyncStatus: "syncing"}, - {ID: "c1", SecretName: "istio-system/s1", SyncStatus: "syncing"}, + {ID: "config", SyncStatus: SyncStatusSyncing}, + {ID: "c0", SecretName: "istio-system/s0", SyncStatus: SyncStatusSyncing}, + {ID: "c1", SecretName: "istio-system/s1", SyncStatus: SyncStatusSyncing}, }) assert.EventuallyEqual(t, func() int { return len(c.component.All()) }, 3) @@ -176,24 +176,24 @@ func TestListRemoteClusters(t *testing.T) { } } assert.EventuallyEqual(t, c.controller.ListRemoteClusters, []cluster.DebugInfo{ - {ID: "config", SyncStatus: "synced"}, - {ID: "c0", SecretName: "istio-system/s0", SyncStatus: "synced"}, - {ID: "c1", SecretName: "istio-system/s1", SyncStatus: "syncing"}, + {ID: "config", SyncStatus: SyncStatusSynced}, + {ID: "c0", SecretName: "istio-system/s0", SyncStatus: SyncStatusSynced}, + {ID: "c1", SecretName: "istio-system/s1", SyncStatus: SyncStatusSyncing}, }) // Sync the last one c.component.ForCluster("c1").Synced.Store(true) assert.EventuallyEqual(t, c.controller.ListRemoteClusters, []cluster.DebugInfo{ - {ID: "config", SyncStatus: "synced"}, - {ID: "c0", SecretName: "istio-system/s0", SyncStatus: "synced"}, - {ID: "c1", SecretName: "istio-system/s1", SyncStatus: "synced"}, + {ID: "config", SyncStatus: SyncStatusSynced}, + {ID: "c0", SecretName: "istio-system/s0", SyncStatus: SyncStatusSynced}, + {ID: "c1", SecretName: "istio-system/s1", SyncStatus: SyncStatusSynced}, }) // Remove one c.DeleteSecret("s1") assert.EventuallyEqual(t, c.controller.ListRemoteClusters, []cluster.DebugInfo{ - {ID: "config", SyncStatus: "synced"}, - {ID: "c0", SecretName: "istio-system/s0", SyncStatus: "synced"}, + {ID: "config", SyncStatus: SyncStatusSynced}, + {ID: "c0", SecretName: "istio-system/s0", SyncStatus: SyncStatusSynced}, }) } diff --git a/pkg/test/echo/cmd/client/main.go b/pkg/test/echo/cmd/client/main.go index 8f8d819291..f5106a4090 100644 --- a/pkg/test/echo/cmd/client/main.go +++ b/pkg/test/echo/cmd/client/main.go @@ -115,7 +115,7 @@ where the network configuration doesn't support gRPC to the source pod.' fmt.Println(line) } - log.Infof("All requests succeeded") + log.Infof("All %d requests succeeded", len(response.Output)) }, } ) diff --git a/pkg/test/echo/cmd/server/main.go b/pkg/test/echo/cmd/server/main.go index f8b0e791d0..e156e58b1e 100644 --- a/pkg/test/echo/cmd/server/main.go +++ b/pkg/test/echo/cmd/server/main.go @@ -33,28 +33,29 @@ import ( ) var ( - httpPorts []int - grpcPorts []int - tcpPorts []int - udpPorts []int - tlsPorts []int - mtlsPorts []int - hbonePorts []int - doubleHbonePorts []int - instanceIPPorts []int - localhostIPPorts []int - serverFirstPorts []int - proxyProtocolPorts []int - xdsGRPCServers []int - metricsPort int - uds string - version string - cluster string - crt string - key string - ca string - istioVersion string - disableALPN bool + httpPorts []int + grpcPorts []int + tcpPorts []int + udpPorts []int + tlsPorts []int + mtlsPorts []int + hbonePorts []int + doubleHbonePorts []int + instanceIPPorts []int + localhostIPPorts []int + serverFirstPorts []int + proxyProtocolPorts []int + xdsGRPCServers []int + endpointPickerPorts []int + metricsPort int + uds string + version string + cluster string + crt string + key string + ca string + istioVersion string + disableALPN bool loggingOptions = log.DefaultOptions() @@ -66,7 +67,7 @@ var ( PersistentPreRunE: configureLogging, Run: func(cmd *cobra.Command, args []string) { shutdown := NewShutdown() - ports := make(common.PortList, len(httpPorts)+len(grpcPorts)+len(tcpPorts)+len(udpPorts)+len(hbonePorts)+len(doubleHbonePorts)) + ports := make(common.PortList, len(httpPorts)+len(grpcPorts)+len(tcpPorts)+len(udpPorts)+len(hbonePorts)+len(doubleHbonePorts)+len(endpointPickerPorts)) tlsByPort := map[int]bool{} mtlsByPort := map[int]bool{} for _, p := range tlsPorts { @@ -89,6 +90,10 @@ var ( for _, p := range xdsGRPCServers { xdsGRPCByPort[p] = true } + endpointPickerByPort := map[int]bool{} + for _, p := range endpointPickerPorts { + endpointPickerByPort[p] = true + } portIndex := 0 for i, p := range httpPorts { ports[portIndex] = &common.Port{ @@ -151,6 +156,18 @@ var ( } portIndex++ } + for i, p := range endpointPickerPorts { + ports[portIndex] = &common.Port{ + Name: "endpoint-picker-" + strconv.Itoa(i), + Protocol: protocol.GRPC, + Port: p, + TLS: tlsByPort[p], + ServerFirst: serverFirstByPort[p], + ProxyProtocol: proxyProtocolByPort[p], + EndpointPicker: true, + } + portIndex++ + } instanceIPByPort := map[int]struct{}{} for _, p := range instanceIPPorts { @@ -250,6 +267,8 @@ func init() { rootCmd.PersistentFlags().IntSliceVar(&serverFirstPorts, "server-first", []int{}, "Ports that are server first. These must be defined as tcp.") rootCmd.PersistentFlags().IntSliceVar(&proxyProtocolPorts, "proxy-protocol", []int{}, "Ports that are wrapped in HA-PROXY protocol.") rootCmd.PersistentFlags().IntSliceVar(&xdsGRPCServers, "xds-grpc-server", []int{}, "Ports that should rely on XDS configuration to serve.") + rootCmd.PersistentFlags().IntSliceVar(&endpointPickerPorts, "endpoint-picker", []int{}, + "Endpoint picker (ext_proc) ports. These are GRPC ports that implement the Envoy external processor protocol.") rootCmd.PersistentFlags().IntVar(&metricsPort, "metrics", 0, "Metrics port") rootCmd.PersistentFlags().StringVar(&uds, "uds", "", "HTTP server on unix domain socket") rootCmd.PersistentFlags().StringVar(&version, "version", "", "Version string") diff --git a/pkg/test/echo/common/model.go b/pkg/test/echo/common/model.go index 7a432af0b1..bc7bb7183e 100644 --- a/pkg/test/echo/common/model.go +++ b/pkg/test/echo/common/model.go @@ -57,6 +57,10 @@ type Port struct { // RequireClientCert determines if the port will be mTLS. RequireClientCert bool + // EndpointPicker indicates this port should serve as an endpoint picker (ext_proc gRPC service). + // Only valid when Protocol is GRPC. + EndpointPicker bool + // ServerFirst if a port will be server first ServerFirst bool diff --git a/pkg/test/echo/server/endpoint/endpointpicker.go b/pkg/test/echo/server/endpoint/endpointpicker.go new file mode 100644 index 0000000000..0120775a7b --- /dev/null +++ b/pkg/test/echo/server/endpoint/endpointpicker.go @@ -0,0 +1,251 @@ +// 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 endpoint + +import ( + "fmt" + "io" + "net" + + extprocv3 "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3" + "google.golang.org/grpc" + "google.golang.org/protobuf/types/known/structpb" + + "istio.io/istio/pkg/log" +) + +var eppLog = log.RegisterScope("epp", "endpoint picker") + +type endpointPickerServer struct { + extprocv3.UnimplementedExternalProcessorServer +} + +func (s *endpointPickerServer) Process(stream extprocv3.ExternalProcessor_ProcessServer) error { + eppLog.Info("EPP: New stream connection established") + requestCount := 0 + for { + requestCount++ + eppLog.Debugf("EPP: Waiting to receive request #%d...", requestCount) + req, err := stream.Recv() + if err == io.EOF { + eppLog.Debug("EPP: Stream closed by client (EOF)") + return nil + } + if err != nil { + eppLog.Errorf("EPP: Error receiving request: %v", err) + return err + } + + eppLog.Debugf("EPP: Received request #%d, type: %T", requestCount, req.Request) + var resp *extprocv3.ProcessingResponse + var dynamicMetadata *structpb.Struct + eppLog.Debugf("EPP: Request: %s", req.String()) + switch r := req.Request.(type) { + case *extprocv3.ProcessingRequest_RequestHeaders: + var selectedEndpoint string + + headers := r.RequestHeaders.Headers + if headers != nil { + eppLog.Debugf("EPP: Received %d headers", len(headers.Headers)) + // Check for x-endpoint header (client request for specific endpoint) + for _, h := range headers.Headers { + if h.Key == "x-endpoint" { + selectedEndpoint = string(h.RawValue) + eppLog.Infof("EPP: Received x-endpoint header: %s", selectedEndpoint) + } + // Log x-envoy headers which may contain endpoint info + if len(h.Key) > 7 && h.Key[:7] == "x-envoy" { + eppLog.Debugf("EPP: Header %s: %s", h.Key, string(h.RawValue)) + } + } + } + + if selectedEndpoint == "" { + // Default to a placeholder - in production this would query available endpoints + selectedEndpoint = "10.0.0.1:8000" + eppLog.Debugf("Using default endpoint: %s", selectedEndpoint) + } + + // Build response according to EPP specification: + // Set x-gateway-destination-endpoint in dynamic metadata (namespace: envoy.lb) + // to communicate selected endpoint to data plane + lbMetadata, err := structpb.NewStruct(map[string]interface{}{ + "x-gateway-destination-endpoint": selectedEndpoint, + }) + if err != nil { + eppLog.Errorf("Failed to create metadata: %v", err) + resp = &extprocv3.ProcessingResponse{} + } else { + // Dynamic metadata must be set with namespace as top-level key + var err error + dynamicMetadata, err = structpb.NewStruct(map[string]interface{}{ + "envoy.lb": lbMetadata.AsMap(), + }) + if err != nil { + eppLog.Errorf("Failed to create dynamic metadata: %v", err) + resp = &extprocv3.ProcessingResponse{} + } else { + resp = &extprocv3.ProcessingResponse{ + Response: &extprocv3.ProcessingResponse_RequestHeaders{ + RequestHeaders: &extprocv3.HeadersResponse{ + Response: &extprocv3.CommonResponse{ + HeaderMutation: &extprocv3.HeaderMutation{ + RemoveHeaders: []string{"x-endpoint"}, + }, + }, + }, + }, + DynamicMetadata: dynamicMetadata, + } + eppLog.Infof("EPP response: set dynamic_metadata[envoy.lb][x-gateway-destination-endpoint]=%s and removed x-endpoint header", selectedEndpoint) + } + } + + case *extprocv3.ProcessingRequest_RequestBody: + eppLog.Debug("EPP: Processing RequestBody (streaming mode - no mutation)") + // In FULL_DUPLEX_STREAMED mode, send BodyMutation with StreamedBodyResponse + resp = &extprocv3.ProcessingResponse{ + Response: &extprocv3.ProcessingResponse_RequestBody{ + RequestBody: &extprocv3.BodyResponse{ + Response: &extprocv3.CommonResponse{ + BodyMutation: &extprocv3.BodyMutation{ + Mutation: &extprocv3.BodyMutation_StreamedResponse{ + StreamedResponse: &extprocv3.StreamedBodyResponse{ + Body: r.RequestBody.Body, + EndOfStream: r.RequestBody.EndOfStream, + }, + }, + }, + }, + }, + }, + } + + case *extprocv3.ProcessingRequest_RequestTrailers: + eppLog.Debug("EPP: Processing RequestTrailers (no mutation)") + resp = &extprocv3.ProcessingResponse{ + Response: &extprocv3.ProcessingResponse_RequestTrailers{ + RequestTrailers: &extprocv3.TrailersResponse{ + HeaderMutation: &extprocv3.HeaderMutation{}, + }, + }, + } + + case *extprocv3.ProcessingRequest_ResponseHeaders: + eppLog.Debug("EPP: Processing ResponseHeaders (no mutation)") + resp = &extprocv3.ProcessingResponse{ + Response: &extprocv3.ProcessingResponse_ResponseHeaders{ + ResponseHeaders: &extprocv3.HeadersResponse{ + Response: &extprocv3.CommonResponse{}, + }, + }, + } + + case *extprocv3.ProcessingRequest_ResponseBody: + eppLog.Debug("EPP: Processing ResponseBody (streaming mode - no mutation)") + // In FULL_DUPLEX_STREAMED mode, send BodyMutation with StreamedBodyResponse + resp = &extprocv3.ProcessingResponse{ + Response: &extprocv3.ProcessingResponse_ResponseBody{ + ResponseBody: &extprocv3.BodyResponse{ + Response: &extprocv3.CommonResponse{ + BodyMutation: &extprocv3.BodyMutation{ + Mutation: &extprocv3.BodyMutation_StreamedResponse{ + StreamedResponse: &extprocv3.StreamedBodyResponse{ + Body: r.ResponseBody.Body, + EndOfStream: r.ResponseBody.EndOfStream, + }, + }, + }, + }, + }, + }, + } + + case *extprocv3.ProcessingRequest_ResponseTrailers: + eppLog.Debug("EPP: Processing ResponseTrailers (no mutation)") + resp = &extprocv3.ProcessingResponse{ + Response: &extprocv3.ProcessingResponse_ResponseTrailers{ + ResponseTrailers: &extprocv3.TrailersResponse{ + HeaderMutation: &extprocv3.HeaderMutation{}, + }, + }, + } + + default: + // For other request types, skip processing + eppLog.Warnf("EPP: Received unknown request type: %T, skipping", req.Request) + resp = &extprocv3.ProcessingResponse{} + } + + eppLog.Debugf("EPP: Sending response for request #%d", requestCount) + if err := stream.Send(resp); err != nil { + eppLog.Errorf("EPP: Error sending response: %v", err) + return err + } + eppLog.Debugf("EPP: Successfully sent response #%d", requestCount) + } +} + +// endpointPickerInstance implements the Instance interface for endpoint picker +type endpointPickerInstance struct { + Config + server *grpc.Server + listener net.Listener +} + +// newEndpointPicker creates a new endpoint picker endpoint instance. +func newEndpointPicker(config Config) *endpointPickerInstance { + return &endpointPickerInstance{ + Config: config, + } +} + +func (e *endpointPickerInstance) Start(onReady OnReadyFunc) error { + addr := fmt.Sprintf("%s:%d", e.ListenerIP, e.Port.Port) + lis, err := net.Listen("tcp", addr) + if err != nil { + return fmt.Errorf("failed to listen for endpoint picker: %v", err) + } + e.listener = lis + + e.server = grpc.NewServer() + extprocv3.RegisterExternalProcessorServer(e.server, &endpointPickerServer{}) + + go func() { + eppLog.Infof("Endpoint Picker gRPC server READY and listening on %s", addr) + eppLog.Infof("Endpoint Picker is registered and waiting for ext_proc connections from Envoy") + if err := e.server.Serve(lis); err != nil { + eppLog.Errorf("Endpoint picker server failed: %v", err) + } + eppLog.Warnf("Endpoint Picker gRPC server stopped") + }() + + onReady() + return nil +} + +func (e *endpointPickerInstance) Close() error { + if e.server != nil { + e.server.GracefulStop() + } + if e.listener != nil { + return e.listener.Close() + } + return nil +} + +func (e *endpointPickerInstance) GetConfig() Config { + return e.Config +} diff --git a/pkg/test/echo/server/endpoint/instance.go b/pkg/test/echo/server/endpoint/instance.go index 72a15c4cff..63c45736c7 100644 --- a/pkg/test/echo/server/endpoint/instance.go +++ b/pkg/test/echo/server/endpoint/instance.go @@ -30,20 +30,21 @@ type OnReadyFunc func() // Config for a single endpoint Instance. type Config struct { - IsServerReady IsServerReadyFunc - Version string - Cluster string - TLSCert string - TLSKey string - TLSCACert string - UDSServer string - Dialer common.Dialer - Port *common.Port - ListenerIP string - IstioVersion string - Namespace string - DisableALPN bool - ReportRequest func() + IsServerReady IsServerReadyFunc + Version string + Cluster string + TLSCert string + TLSKey string + TLSCACert string + UDSServer string + Dialer common.Dialer + Port *common.Port + ListenerIP string + IstioVersion string + Namespace string + DisableALPN bool + ReportRequest func() + EndpointPicker bool } // Instance of an endpoint that serves the Echo application on a single port/protocol. @@ -64,6 +65,9 @@ func New(cfg Config) (Instance, error) { case protocol.HTTP, protocol.HTTPS: return newHTTP(cfg), nil case protocol.HTTP2, protocol.GRPC: + if cfg.EndpointPicker { + return newEndpointPicker(cfg), nil + } return newGRPC(cfg), nil case protocol.TCP: return newTCP(cfg), nil diff --git a/pkg/test/echo/server/instance.go b/pkg/test/echo/server/instance.go index ab0fd74b18..1423e16777 100644 --- a/pkg/test/echo/server/instance.go +++ b/pkg/test/echo/server/instance.go @@ -233,7 +233,7 @@ func (s *Instance) getListenerIPs(port *common.Port) ([]string, error) { } func (s *Instance) newEndpoint(port *common.Port, listenerIP string, udsServer string) (endpoint.Instance, error) { - return endpoint.New(endpoint.Config{ + epConfig := endpoint.Config{ Port: port, UDSServer: udsServer, IsServerReady: s.isReady, @@ -247,7 +247,11 @@ func (s *Instance) newEndpoint(port *common.Port, listenerIP string, udsServer s ListenerIP: listenerIP, DisableALPN: s.DisableALPN, IstioVersion: s.IstioVersion, - }) + } + if port != nil && port.EndpointPicker { + epConfig.EndpointPicker = true + } + return endpoint.New(epConfig) } func (s *Instance) isReady() bool { diff --git a/pkg/test/framework/components/crd/gateway.go b/pkg/test/framework/components/crd/gateway.go index 637f29dc1a..244a267298 100644 --- a/pkg/test/framework/components/crd/gateway.go +++ b/pkg/test/framework/components/crd/gateway.go @@ -70,7 +70,7 @@ func DeployGatewayAPI(ctx resource.Context) error { // Wait until our GatewayClass is ready return retry.UntilSuccess(func() error { for _, c := range ctx.Clusters().Configs() { - _, err := c.GatewayAPI().GatewayV1beta1().GatewayClasses().Get(context.Background(), "istio", metav1.GetOptions{}) + _, err := c.GatewayAPI().GatewayV1().GatewayClasses().Get(context.Background(), "istio", metav1.GetOptions{}) if err != nil { return err } @@ -96,3 +96,54 @@ func DeployGatewayAPI(ctx resource.Context) error { return nil }) } + +func DeployGatewayAPIInferenceExtensionOrSkip(ctx framework.TestContext) { + res := DeployGatewayAPIInferenceExtension(ctx) + if res == errSkip { + ctx.Skip(errSkip.Error()) + } + if res != nil { + ctx.Fatal(res) + } +} + +func DeployGatewayAPIInferenceExtension(ctx resource.Context) error { + cfg, _ := istio.DefaultConfig(ctx) + // Starting from Openshift 4.19 (1.32), GW API comes pre-installed and should not be deployed. + // But GatewayAPIInferenceExtension should be deployed on Openshift in any condition. + if !cfg.DeployGatewayAPI && !ctx.Settings().OpenShift { + return nil + } + if !SupportsGatewayAPI(ctx) { + return errSkip + } + if err := ctx.ConfigIstio(). + File("", filepath.Join(env.IstioSrc, "tests/integration/pilot/testdata/gateway-api-inference-extension-crd.yaml")). + Apply(apply.NoCleanup); err != nil { + return err + } + // Wait until the InferencePool CRD is ready + return retry.UntilSuccess(func() error { + for _, c := range ctx.Clusters().Configs() { + crdl, err := c.Ext().ApiextensionsV1().CustomResourceDefinitions().List(context.Background(), metav1.ListOptions{}) + if err != nil { + return err + } + for _, crd := range crdl.Items { + if !strings.HasSuffix(crd.Name, "inference.networking.k8s.io") { + continue + } + found := false + for _, c := range crd.Status.Conditions { + if c.Type == apiextensions.Established && c.Status == apiextensions.ConditionTrue { + found = true + } + } + if !found { + return fmt.Errorf("crd %v not ready: %+v", crd.Name, crd.Status) + } + } + } + return nil + }) +} diff --git a/pkg/test/framework/components/echo/kube/deployment.go b/pkg/test/framework/components/echo/kube/deployment.go index 5b45c2cbbd..0ee15d8291 100644 --- a/pkg/test/framework/components/echo/kube/deployment.go +++ b/pkg/test/framework/components/echo/kube/deployment.go @@ -261,7 +261,50 @@ func GenerateDeployment(ctx resource.Context, cfg echo.Config, settings *resourc deploy = getTemplate(vmDeploymentTemplateFile) } - return tmpl.Execute(deploy, params) + deploymentYAML, err := tmpl.Execute(deploy, params) + if err != nil { + return "", err + } + + // Check if any ports are configured as endpoint pickers + var eppPorts []int + for _, port := range cfg.Ports { + if port.EndpointPicker { + eppPorts = append(eppPorts, port.ServicePort) + } + } + + // If there are endpoint picker ports, add a DestinationRule to disable mTLS for those ports + if len(eppPorts) > 0 { + drYAML := generateDestinationRuleForEPP(cfg.Service, cfg.Namespace.Name(), eppPorts) + deploymentYAML += "\n---\n" + drYAML + } + + return deploymentYAML, nil +} + +func generateDestinationRuleForEPP(service, namespace string, eppPorts []int) string { + var portSettings strings.Builder + for i, port := range eppPorts { + if i > 0 { + portSettings.WriteString("\n") + } + portSettings.WriteString(fmt.Sprintf(` - port: + number: %d + tls: + mode: DISABLE`, port)) + } + + return fmt.Sprintf(`apiVersion: networking.istio.io/v1 +kind: DestinationRule +metadata: + name: %s-epp-notls + namespace: %s +spec: + host: %s.%s.svc.cluster.local + trafficPolicy: + portLevelSettings: +%s`, service, namespace, service, namespace, portSettings.String()) } func GenerateService(cfg echo.Config, isOpenShift bool) (string, error) { @@ -675,6 +718,7 @@ func getContainerPorts(cfg echo.Config) echoCommon.PortList { InstanceIP: p.InstanceIP, LocalhostIP: p.LocalhostIP, ProxyProtocol: p.ProxyProtocol, + EndpointPicker: p.EndpointPicker, } containerPorts = append(containerPorts, cport) diff --git a/pkg/test/framework/components/echo/kube/templates/deployment.yaml b/pkg/test/framework/components/echo/kube/templates/deployment.yaml index 246c79e491..91a8338c0e 100644 --- a/pkg/test/framework/components/echo/kube/templates/deployment.yaml +++ b/pkg/test/framework/components/echo/kube/templates/deployment.yaml @@ -109,6 +109,8 @@ spec: {{- range $i, $p := $appContainer.ContainerPorts }} {{- if and $p.XDSServer (eq .Protocol "GRPC") }} - --xds-grpc-server={{ $p.Port }} +{{- else if $p.EndpointPicker }} + - --endpoint-picker={{ $p.Port }} {{- else if eq .Protocol "GRPC" }} - --grpc={{ $p.Port }} {{- else if eq .Protocol "TCP" }} diff --git a/pkg/test/framework/components/echo/port.go b/pkg/test/framework/components/echo/port.go index 13eede92ae..e35166d3f9 100644 --- a/pkg/test/framework/components/echo/port.go +++ b/pkg/test/framework/components/echo/port.go @@ -60,6 +60,10 @@ type Port struct { // ProxyProtocol determines if echo should accept PROXY protocol. ProxyProtocol bool + + // EndpointPicker indicates this port should serve as an endpoint picker (ext_proc gRPC service). + // Only valid when Protocol is GRPC. + EndpointPicker bool } // IsWorkloadOnly returns true if there is no service port specified for this Port. diff --git a/pkg/test/framework/components/istio/gateway.go b/pkg/test/framework/components/istio/gateway.go index 7b8c23a516..b5a43528bb 100644 --- a/pkg/test/framework/components/istio/gateway.go +++ b/pkg/test/framework/components/istio/gateway.go @@ -61,7 +61,7 @@ func DeployGatewayAPI(ctx resource.Context) error { // Wait until our GatewayClass is ready return retry.UntilSuccess(func() error { for _, c := range ctx.Clusters().Configs() { - _, err := c.GatewayAPI().GatewayV1beta1().GatewayClasses().Get(context.Background(), "istio", metav1.GetOptions{}) + _, err := c.GatewayAPI().GatewayV1().GatewayClasses().Get(context.Background(), "istio", metav1.GetOptions{}) if err != nil { return err } diff --git a/pkg/test/framework/components/zipkin/kube.go b/pkg/test/framework/components/zipkin/kube.go index c1c7a3fe7f..6aa71571fa 100644 --- a/pkg/test/framework/components/zipkin/kube.go +++ b/pkg/test/framework/components/zipkin/kube.go @@ -22,9 +22,11 @@ import ( "io" "net" "net/http" + "net/url" "os" "path/filepath" "sort" + "strconv" "strings" "time" @@ -39,7 +41,7 @@ import ( const ( appName = "zipkin" - tracesAPI = "/api/v2/traces?limit=%d&spanName=%s&annotationQuery=%s" + tracesAPI = "/api/v2/traces?%s" zipkinPort = 9411 httpTimeout = 5 * time.Second @@ -275,7 +277,12 @@ func (c *kubeComponent) QueryTraces(limit int, spanName, annotationQuery, hostDo } func (c *kubeComponent) createHTTPClient(limit int, spanName, annotationQuery, hostDomain string) (string, http.Client, error) { - baseURL := fmt.Sprintf(tracesAPI, limit, spanName, annotationQuery) + // Encoding the annotationQuery with URL + queryParams := url.Values{} + queryParams.Add("limit", strconv.Itoa(limit)) + queryParams.Add("spanName", spanName) + queryParams.Add("annotationQuery", annotationQuery) + baseURL := fmt.Sprintf(tracesAPI, queryParams.Encode()) if hostDomain == "" { url := c.address + baseURL scopes.Framework.Debugf("Making GET call to Zipkin API: %s", url) @@ -299,7 +306,7 @@ func (c *kubeComponent) createHTTPClient(limit int, spanName, annotationQuery, h }, } - url := fmt.Sprintf("http://%s%s", ip, baseURL) + url := fmt.Sprintf("http://%s", net.JoinHostPort(ip, baseURL)) scopes.Framework.Debugf("Making GET call to Zipkin API: %s with resolve override: %s", url, hostDomain) return url, http.Client{ diff --git a/pkg/test/util/assert/assert.go b/pkg/test/util/assert/assert.go index 6693b3521c..8bdc2b5d65 100644 --- a/pkg/test/util/assert/assert.go +++ b/pkg/test/util/assert/assert.go @@ -111,6 +111,28 @@ func EventuallyEqual[T any](t test.Failer, fetch func() T, expected T, retryOpts } } +// Consistently polls the fetch function for a duration and fails if the value ever changes from the expected value. +// Use sparingly to avoid flakiness, as this is inherently timing-dependent. +// This is useful for asserting that no events occur (e.g., that a value stays at 0). +// Default duration is 20ms with 2ms polling interval. +func Consistently[T any](t test.Failer, fetch func() T, expected T, duration ...time.Duration) { + t.Helper() + d := time.Millisecond * 20 + if len(duration) > 0 { + d = duration[0] + } + interval := time.Millisecond * 2 + deadline := time.Now().Add(d) + + for time.Now().Before(deadline) { + a := fetch() + if !cmp.Equal(a, expected, opts(expected)...) { + t.Fatalf("value changed unexpectedly: %v\nGot: %v\nWant: %v", cmp.Diff(a, expected, opts(expected)...), a, expected) + } + time.Sleep(interval) + } +} + // Error asserts the provided err is non-nil func Error(t test.Failer, err error) { t.Helper() diff --git a/pkg/util/strcase/camelcase.go b/pkg/util/strcase/camelcase.go index 67a86119ec..458bf5d678 100644 --- a/pkg/util/strcase/camelcase.go +++ b/pkg/util/strcase/camelcase.go @@ -68,6 +68,16 @@ func CamelCaseToKebabCase(s string) string { return "http-route" case "HTTPAPISpecBinding": return "http-api-spec-binding" + case "GRPCRoute": + return "grpc-route" + case "TCPRoute": + return "tcp-route" + case "TLSRoute": + return "tls-route" + case "UDPRoute": + return "udp-route" + case "BackendTLSPolicy": + return "backend-tls-policy" default: var out bytes.Buffer for i := range s { diff --git a/releasenotes/notes/57076.yaml b/releasenotes/notes/57076.yaml new file mode 100644 index 0000000000..be2f2645fe --- /dev/null +++ b/releasenotes/notes/57076.yaml @@ -0,0 +1,10 @@ +apiVersion: release-notes/v2 +kind: feature +area: istioctl +issue: +- 57075 +releaseNotes: +- | + **Added** `--wait` flag to `istioctl waypoint status` to specify whether to wait for the waypoint to become ready (default is true). + + Specifying this flag with `--wait=false` will not wait for the waypoint to be ready, and will directly display the status of waypoint. diff --git a/releasenotes/notes/57638.yaml b/releasenotes/notes/57638.yaml new file mode 100644 index 0000000000..3ee1f1d75a --- /dev/null +++ b/releasenotes/notes/57638.yaml @@ -0,0 +1,7 @@ +apiVersion: release-notes/v2 +kind: feature +area: traffic-management +issue: [57638] +releaseNotes: +- | + **Added** support for multiple targetPorts in an InferencePool. The possibility to have >1 targetPort was added as part of GIE v1.1.0. diff --git a/releasenotes/notes/57734.yaml b/releasenotes/notes/57734.yaml new file mode 100644 index 0000000000..5998669187 --- /dev/null +++ b/releasenotes/notes/57734.yaml @@ -0,0 +1,10 @@ +apiVersion: release-notes/v2 +kind: bug-fix +area: traffic-management + +issue: + - https://github.com/istio/istio/issues/57734 + +releaseNotes: +- | + **Fixed** status conflicts on Route resources when multiple istio revisions are installed. diff --git a/releasenotes/notes/58185.yaml b/releasenotes/notes/58185.yaml new file mode 100644 index 0000000000..338661f648 --- /dev/null +++ b/releasenotes/notes/58185.yaml @@ -0,0 +1,8 @@ +apiVersion: release-notes/v2 +kind: bug-fix +area: traffic-management +issue: + - 58185 +releaseNotes: +- | + **Fixed** Istio CNI node agent startup failure in MicroK8s environments when using ambient mode with nftables backend. diff --git a/releasenotes/notes/58324.yaml b/releasenotes/notes/58324.yaml new file mode 100644 index 0000000000..1e952abad3 --- /dev/null +++ b/releasenotes/notes/58324.yaml @@ -0,0 +1,9 @@ +apiVersion: release-notes/v2 +kind: bug-fix +area: traffic-management +releaseNotes: +- | + **Fixed** rare race condition where deleting a ServiceEntry that shares a + hostname with another ServiceEntry in the same namespace occasionally causing + ambient clients to lose the ability to send traffic to that hostname until + istiod restarts. diff --git a/releasenotes/notes/58353.yaml b/releasenotes/notes/58353.yaml new file mode 100644 index 0000000000..11360dcb1c --- /dev/null +++ b/releasenotes/notes/58353.yaml @@ -0,0 +1,8 @@ +apiVersion: release-notes/v2 +kind: bug-fix +area: traffic-management +issue: + - 58353 +releaseNotes: +- | + **Fixed** use cases where upgrading from the iptables backend to the nftables backend in ambient created stale iptables rules on the network. The code now continues to use iptables on the node until it is rebooted. diff --git a/releasenotes/notes/58366.yaml b/releasenotes/notes/58366.yaml new file mode 100644 index 0000000000..01f4dd969b --- /dev/null +++ b/releasenotes/notes/58366.yaml @@ -0,0 +1,8 @@ +apiVersion: release-notes/v2 +kind: bug-fix +area: traffic-management +issue: + - 58366 +releaseNotes: +- | + **Added** an option, `gateway.istio.io/tls-cipher-suites`, to specify the custom cipher suites on a Gateway. The value is a comma separated list of cipher suites. diff --git a/releasenotes/notes/58394.yaml b/releasenotes/notes/58394.yaml new file mode 100644 index 0000000000..a36ce10a80 --- /dev/null +++ b/releasenotes/notes/58394.yaml @@ -0,0 +1,6 @@ +apiVersion: release-notes/v2 +kind: feature +area: istioctl +releaseNotes: +- | + **Added** `--all-namespaces` flag for `istioctl waypoint status` to display the status of waypoints in all namespaces. diff --git a/releasenotes/notes/58397.yaml b/releasenotes/notes/58397.yaml new file mode 100644 index 0000000000..05e856c8a2 --- /dev/null +++ b/releasenotes/notes/58397.yaml @@ -0,0 +1,8 @@ +apiVersion: release-notes/v2 +kind: bug-fix +area: traffic-management +issue: +- 58397 +releaseNotes: +- | + **Fixed** DNS name table creation for headless services where pods entries did not account for pods to have multiple IPs. \ No newline at end of file diff --git a/releasenotes/notes/58427.yaml b/releasenotes/notes/58427.yaml new file mode 100644 index 0000000000..af9e54f463 --- /dev/null +++ b/releasenotes/notes/58427.yaml @@ -0,0 +1,7 @@ +apiVersion: release-notes/v2 +kind: bug-fix +area: traffic-management +issue: [58427] +releaseNotes: + - | + **Fixed** an issue causing ambient multi-network connections to fail when using a custom trust domain. diff --git a/releasenotes/notes/58518.yaml b/releasenotes/notes/58518.yaml new file mode 100644 index 0000000000..4709af1afc --- /dev/null +++ b/releasenotes/notes/58518.yaml @@ -0,0 +1,9 @@ +apiVersion: release-notes/v2 +kind: bug-fix +area: telemetry +issue: + - https://github.com/istio/istio/issues/58500 +releaseNotes: +- | + **Fixed** `sidecar.istio.io/statsEvictionInterval` annotation with values >= 60s + causing `istio-proxy` sidecars to fail to start. diff --git a/releasenotes/notes/58525.yaml b/releasenotes/notes/58525.yaml new file mode 100644 index 0000000000..8ca7a2d2ed --- /dev/null +++ b/releasenotes/notes/58525.yaml @@ -0,0 +1,7 @@ +apiVersion: release-notes/v2 +kind: bug-fix +area: traffic-management +releaseNotes: + - | + **Fixed** an issue where Envoy proxies that to Waypoint proxies would in rare cases + either get extraraneous XDS updates or miss some updates entirely. diff --git a/releasenotes/notes/align-warmup-aggression.yaml b/releasenotes/notes/align-warmup-aggression.yaml new file mode 100644 index 0000000000..49d0899042 --- /dev/null +++ b/releasenotes/notes/align-warmup-aggression.yaml @@ -0,0 +1,8 @@ +apiVersion: release-notes/v2 +kind: bug-fix +area: traffic-management +issue: + - https://github.com/istio/api/issues/3395 +releaseNotes: + - | + **Fixed** an issue where warmup aggression is not aligned with envoy configuration. diff --git a/releasenotes/notes/formatter-custom-tag.yaml b/releasenotes/notes/formatter-custom-tag.yaml new file mode 100644 index 0000000000..f9d71950e2 --- /dev/null +++ b/releasenotes/notes/formatter-custom-tag.yaml @@ -0,0 +1,6 @@ +apiVersion: release-notes/v2 +kind: feature +area: telemetry +releaseNotes: + - | + **Added** support for `Formatter` type custom tag in Telemetry API. \ No newline at end of file diff --git a/releasenotes/notes/helm-values-shema-enabled-field.yaml b/releasenotes/notes/helm-values-shema-enabled-field.yaml new file mode 100644 index 0000000000..588f263b90 --- /dev/null +++ b/releasenotes/notes/helm-values-shema-enabled-field.yaml @@ -0,0 +1,9 @@ +apiVersion: release-notes/v2 + +kind: bug-fix +area: installation +issue: + - 58277 +releaseNotes: +- | + **Fixed** `istio-gateway` helm chart values schema to allow top-level `enabled` field. diff --git a/releasenotes/notes/istiod-remote-cluster-sync-status.yaml b/releasenotes/notes/istiod-remote-cluster-sync-status.yaml new file mode 100644 index 0000000000..2dc1acd3bb --- /dev/null +++ b/releasenotes/notes/istiod-remote-cluster-sync-status.yaml @@ -0,0 +1,6 @@ +apiVersion: release-notes/v2 +kind: feature +area: telemetry +releaseNotes: + - | + **Added** `istiod_remote_cluster_sync_status` gauge metric to Pilot to track the synchronization status of remote clusters. diff --git a/releasenotes/notes/watch-remote-mesh-config.yaml b/releasenotes/notes/watch-remote-mesh-config.yaml new file mode 100644 index 0000000000..250da87e2a --- /dev/null +++ b/releasenotes/notes/watch-remote-mesh-config.yaml @@ -0,0 +1,9 @@ +apiVersion: release-notes/v2 +kind: feature +area: security + +releaseNotes: +- | + **Improved** remote cluster trust domain handling by implementing watching of remote `meshConfig`. + Istiod now automatically watches and updates trust domain information from remote clusters, + ensuring accurate SAN matching for services, which belong to more than one trust domain. diff --git a/releasenotes/notes/zipkin-timeout-headers.yaml b/releasenotes/notes/zipkin-timeout-headers.yaml new file mode 100644 index 0000000000..530fd97761 --- /dev/null +++ b/releasenotes/notes/zipkin-timeout-headers.yaml @@ -0,0 +1,19 @@ +apiVersion: release-notes/v2 +kind: feature +area: telemetry + +# issue is a list of GitHub issues resolved in this note. +issue: [] + +docs: + - '[envoy] https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/trace/v3/zipkin.proto' + - '[reference] https://istio.io/latest/docs/reference/config/istio.mesh.v1alpha1/#MeshConfig-ExtensionProvider-ZipkinTracingProvider' + - '[usage] https://istio.io/latest/docs/tasks/observability/distributed-tracing/' + +releaseNotes: +- | + **Added** `timeout` and `headers` fields to `ZipkinTracingProvider` in MeshConfig extensionProviders. + The `timeout` field configures the HTTP request timeout when sending spans to the Zipkin collector, + providing better control over trace export reliability. The `headers` field allows including custom + HTTP headers for authentication, authorization, and custom metadata use cases. Headers support both + direct values and environment variable references for secure credential management. diff --git a/samples/addons/kiali.yaml b/samples/addons/kiali.yaml index c3b082c610..aeb4666347 100644 --- a/samples/addons/kiali.yaml +++ b/samples/addons/kiali.yaml @@ -7,12 +7,12 @@ metadata: namespace: "istio-system" labels: - helm.sh/chart: kiali-server-2.18.0 + helm.sh/chart: kiali-server-2.19.0 app: kiali app.kubernetes.io/name: kiali app.kubernetes.io/instance: kiali - version: "v2.18.0" - app.kubernetes.io/version: "v2.18.0" + version: "v2.19.0" + app.kubernetes.io/version: "v2.19.0" app.kubernetes.io/part-of: "kiali" ... --- @@ -24,12 +24,12 @@ metadata: namespace: "istio-system" labels: - helm.sh/chart: kiali-server-2.18.0 + helm.sh/chart: kiali-server-2.19.0 app: kiali app.kubernetes.io/name: kiali app.kubernetes.io/instance: kiali - version: "v2.18.0" - app.kubernetes.io/version: "v2.18.0" + version: "v2.19.0" + app.kubernetes.io/version: "v2.19.0" app.kubernetes.io/part-of: "kiali" data: config.yaml: | @@ -48,6 +48,8 @@ data: label: kiali.io/multiCluster=true clusters: [] deployment: + additional_pod_containers_yaml: [] + additional_pod_init_containers_yaml: [] additional_service_yaml: {} affinity: node: {} @@ -57,6 +59,7 @@ data: configmap_annotations: {} custom_envs: [] custom_secrets: [] + discovery_selectors: {} dns: config: {} policy: "" @@ -69,7 +72,7 @@ data: image_name: quay.io/kiali/kiali image_pull_policy: IfNotPresent image_pull_secrets: [] - image_version: v2.18 + image_version: v2.19 ingress: additional_labels: {} class_name: nginx @@ -116,7 +119,7 @@ data: service_type: "" tolerations: [] topology_spread_constraints: [] - version_label: v2.18.0 + version_label: v2.19.0 view_only_mode: false external_services: custom_dashboards: @@ -153,12 +156,12 @@ metadata: name: kiali labels: - helm.sh/chart: kiali-server-2.18.0 + helm.sh/chart: kiali-server-2.19.0 app: kiali app.kubernetes.io/name: kiali app.kubernetes.io/instance: kiali - version: "v2.18.0" - app.kubernetes.io/version: "v2.18.0" + version: "v2.19.0" + app.kubernetes.io/version: "v2.19.0" app.kubernetes.io/part-of: "kiali" rules: - apiGroups: [""] @@ -263,12 +266,12 @@ metadata: name: kiali labels: - helm.sh/chart: kiali-server-2.18.0 + helm.sh/chart: kiali-server-2.19.0 app: kiali app.kubernetes.io/name: kiali app.kubernetes.io/instance: kiali - version: "v2.18.0" - app.kubernetes.io/version: "v2.18.0" + version: "v2.19.0" + app.kubernetes.io/version: "v2.19.0" app.kubernetes.io/part-of: "kiali" roleRef: apiGroup: rbac.authorization.k8s.io @@ -288,12 +291,12 @@ metadata: namespace: "istio-system" labels: - helm.sh/chart: kiali-server-2.18.0 + helm.sh/chart: kiali-server-2.19.0 app: kiali app.kubernetes.io/name: kiali app.kubernetes.io/instance: kiali - version: "v2.18.0" - app.kubernetes.io/version: "v2.18.0" + version: "v2.19.0" + app.kubernetes.io/version: "v2.19.0" app.kubernetes.io/part-of: "kiali" annotations: spec: @@ -319,12 +322,12 @@ metadata: namespace: "istio-system" labels: - helm.sh/chart: kiali-server-2.18.0 + helm.sh/chart: kiali-server-2.19.0 app: kiali app.kubernetes.io/name: kiali app.kubernetes.io/instance: kiali - version: "v2.18.0" - app.kubernetes.io/version: "v2.18.0" + version: "v2.19.0" + app.kubernetes.io/version: "v2.19.0" app.kubernetes.io/part-of: "kiali" spec: replicas: 1 @@ -342,16 +345,16 @@ spec: name: kiali labels: - helm.sh/chart: kiali-server-2.18.0 + helm.sh/chart: kiali-server-2.19.0 app: kiali app.kubernetes.io/name: kiali app.kubernetes.io/instance: kiali - version: "v2.18.0" - app.kubernetes.io/version: "v2.18.0" + version: "v2.19.0" + app.kubernetes.io/version: "v2.19.0" app.kubernetes.io/part-of: "kiali" sidecar.istio.io/inject: "false" annotations: - checksum/config: 6b627ea63d0ed3a0970e480e5d3ffb6e0fdf60dad85402602e83e84d75531333 + checksum/config: 8376ac11c2e5c09dab12f5c53e61c41e772882efc0822b1250879d73266dfe13 prometheus.io/scrape: "true" prometheus.io/port: "9090" kiali.io/dashboards: go,kiali @@ -359,7 +362,7 @@ spec: spec: serviceAccountName: kiali containers: - - image: "quay.io/kiali/kiali:v2.18" + - image: "quay.io/kiali/kiali:v2.19" imagePullPolicy: IfNotPresent name: kiali command: diff --git a/samples/addons/prometheus.yaml b/samples/addons/prometheus.yaml index a18bfd722a..6556027905 100644 --- a/samples/addons/prometheus.yaml +++ b/samples/addons/prometheus.yaml @@ -8,7 +8,7 @@ metadata: app.kubernetes.io/name: prometheus app.kubernetes.io/instance: prometheus app.kubernetes.io/version: v3.7.3 - helm.sh/chart: prometheus-27.45.0 + helm.sh/chart: prometheus-27.47.0 app.kubernetes.io/part-of: prometheus name: prometheus namespace: istio-system @@ -24,7 +24,7 @@ metadata: app.kubernetes.io/name: prometheus app.kubernetes.io/instance: prometheus app.kubernetes.io/version: v3.7.3 - helm.sh/chart: prometheus-27.45.0 + helm.sh/chart: prometheus-27.47.0 app.kubernetes.io/part-of: prometheus name: prometheus namespace: istio-system @@ -355,7 +355,7 @@ metadata: app.kubernetes.io/name: prometheus app.kubernetes.io/instance: prometheus app.kubernetes.io/version: v3.7.3 - helm.sh/chart: prometheus-27.45.0 + helm.sh/chart: prometheus-27.47.0 app.kubernetes.io/part-of: prometheus name: prometheus rules: @@ -405,7 +405,7 @@ metadata: app.kubernetes.io/name: prometheus app.kubernetes.io/instance: prometheus app.kubernetes.io/version: v3.7.3 - helm.sh/chart: prometheus-27.45.0 + helm.sh/chart: prometheus-27.47.0 app.kubernetes.io/part-of: prometheus name: prometheus subjects: @@ -426,7 +426,7 @@ metadata: app.kubernetes.io/name: prometheus app.kubernetes.io/instance: prometheus app.kubernetes.io/version: v3.7.3 - helm.sh/chart: prometheus-27.45.0 + helm.sh/chart: prometheus-27.47.0 app.kubernetes.io/part-of: prometheus name: prometheus namespace: istio-system @@ -452,7 +452,7 @@ metadata: app.kubernetes.io/name: prometheus app.kubernetes.io/instance: prometheus app.kubernetes.io/version: v3.7.3 - helm.sh/chart: prometheus-27.45.0 + helm.sh/chart: prometheus-27.47.0 app.kubernetes.io/part-of: prometheus name: prometheus namespace: istio-system @@ -474,7 +474,7 @@ spec: app.kubernetes.io/name: prometheus app.kubernetes.io/instance: prometheus app.kubernetes.io/version: v3.7.3 - helm.sh/chart: prometheus-27.45.0 + helm.sh/chart: prometheus-27.47.0 app.kubernetes.io/part-of: prometheus sidecar.istio.io/inject: "false" @@ -483,7 +483,7 @@ spec: serviceAccountName: prometheus containers: - name: prometheus-server-configmap-reload - image: "ghcr.io/prometheus-operator/prometheus-config-reloader:v0.86.2" + image: "ghcr.io/prometheus-operator/prometheus-config-reloader:v0.87.0" imagePullPolicy: "IfNotPresent" args: - --watched-dir=/etc/config diff --git a/samples/multicluster/gen-eastwest-gateway.sh b/samples/multicluster/gen-eastwest-gateway.sh index 436603e1fb..1bbbd16fd0 100755 --- a/samples/multicluster/gen-eastwest-gateway.sh +++ b/samples/multicluster/gen-eastwest-gateway.sh @@ -71,7 +71,7 @@ if [[ "${AMBIENT}" -eq 1 ]]; then exit 1 fi GW=$(cat < 0 && !parentsFound { + parentsFound = true + return false, nil + } else if len(route.Status.Parents) == 0 && parentsFound { + // the status previously had parents, they should not get removed + return true, fmt.Errorf("httproute status was incorrectly overwritten") + } + + return false, nil + } + + var err error + for attempts := 0; attempts <= 10; attempts++ { + complete, localErr := checkStatus() + if complete { + err = localErr + break + } + } + + if err != nil { + t.Fatal(err) + } + }) +} diff --git a/tests/integration/pilot/testdata/gateway-api-inference-extension-crd.yaml b/tests/integration/pilot/testdata/gateway-api-inference-extension-crd.yaml index 709540cbd4..2344ab3735 100644 --- a/tests/integration/pilot/testdata/gateway-api-inference-extension-crd.yaml +++ b/tests/integration/pilot/testdata/gateway-api-inference-extension-crd.yaml @@ -1,4 +1,4 @@ -# Generated with `kubectl kustomize "https://github.com/kubernetes-sigs/gateway-api-inference-extension/config/crd/?ref=173ad587b6752a881884933fef9e792c87420232"` +# Generated with `kubectl kustomize "https://github.com/kubernetes-sigs/gateway-api-inference-extension/config/crd/?ref=0a3bb20107519af18c2fecb6706c3ff59148a645"` apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -346,7 +346,8 @@ spec: targetPorts: description: |- TargetPorts defines a list of ports that are exposed by this InferencePool. - Currently, the list may only include a single port definition. + Every port will be treated as a distinctive endpoint by EPP, + addressable as a 'podIP:portNumber' combination. items: description: Port defines the network port that will be exposed by this InferencePool. @@ -362,10 +363,13 @@ spec: required: - number type: object - maxItems: 1 + maxItems: 8 minItems: 1 type: array x-kubernetes-list-type: atomic + x-kubernetes-validations: + - message: port number must be unique + rule: self.all(p1, self.exists_one(p2, p1.number==p2.number)) required: - endpointPickerRef - selector diff --git a/tests/integration/pilot/workloadentry_test.go b/tests/integration/pilot/workloadentry_test.go index d94ea94475..00a9fcc1a3 100644 --- a/tests/integration/pilot/workloadentry_test.go +++ b/tests/integration/pilot/workloadentry_test.go @@ -63,7 +63,7 @@ func TestWorkloadEntryGateway(t *testing.T) { // we have an imaginary network for each network called {name}-manual-discovery gwTmpl := ` --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: remote-gateway-manual-discovery-%s diff --git a/tests/integration/security/policy_attachment_only/testdata/authz/gateway-api.yaml.tmpl b/tests/integration/security/policy_attachment_only/testdata/authz/gateway-api.yaml.tmpl index 5877a2b5bd..487d0ad1e3 100644 --- a/tests/integration/security/policy_attachment_only/testdata/authz/gateway-api.yaml.tmpl +++ b/tests/integration/security/policy_attachment_only/testdata/authz/gateway-api.yaml.tmpl @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: {{ .To.ServiceName }}-gateway @@ -11,7 +11,7 @@ spec: protocol: HTTP hostname: "*.{{ .To.ServiceName }}.com" --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: {{ .To.ServiceName }} diff --git a/tests/integration/security/policy_attachment_only/testdata/requestauthn/gateway-api.yaml.tmpl b/tests/integration/security/policy_attachment_only/testdata/requestauthn/gateway-api.yaml.tmpl index a36bebf7f6..5048c344a5 100644 --- a/tests/integration/security/policy_attachment_only/testdata/requestauthn/gateway-api.yaml.tmpl +++ b/tests/integration/security/policy_attachment_only/testdata/requestauthn/gateway-api.yaml.tmpl @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: {{ .To.ServiceName }}-gateway @@ -10,7 +10,7 @@ spec: protocol: HTTP hostname: "*.{{ .To.ServiceName }}.com" --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: {{ .To.ServiceName }} diff --git a/tests/integration/security/testdata/requestauthn/gateway-api.yaml.tmpl b/tests/integration/security/testdata/requestauthn/gateway-api.yaml.tmpl index a36bebf7f6..5048c344a5 100644 --- a/tests/integration/security/testdata/requestauthn/gateway-api.yaml.tmpl +++ b/tests/integration/security/testdata/requestauthn/gateway-api.yaml.tmpl @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: {{ .To.ServiceName }}-gateway @@ -10,7 +10,7 @@ spec: protocol: HTTP hostname: "*.{{ .To.ServiceName }}.com" --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: {{ .To.ServiceName }} diff --git a/tests/integration/telemetry/api/testdata/gateway-api.yaml b/tests/integration/telemetry/api/testdata/gateway-api.yaml index a36bebf7f6..5048c344a5 100644 --- a/tests/integration/telemetry/api/testdata/gateway-api.yaml +++ b/tests/integration/telemetry/api/testdata/gateway-api.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: {{ .To.ServiceName }}-gateway @@ -10,7 +10,7 @@ spec: protocol: HTTP hostname: "*.{{ .To.ServiceName }}.com" --- -apiVersion: gateway.networking.k8s.io/v1beta1 +apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: {{ .To.ServiceName }} diff --git a/tests/integration/telemetry/tracing/otelcollector/testdata/otel-tracing.yaml b/tests/integration/telemetry/tracing/otelcollector/testdata/otel-tracing.yaml index cf8a05baf3..9ea4d5ea84 100644 --- a/tests/integration/telemetry/tracing/otelcollector/testdata/otel-tracing.yaml +++ b/tests/integration/telemetry/tracing/otelcollector/testdata/otel-tracing.yaml @@ -10,4 +10,7 @@ spec: customTags: "provider": literal: - value: "otel" \ No newline at end of file + value: "otel" + "key1": + formatter: + value: "%REQ(key1)%" diff --git a/tests/integration/telemetry/tracing/otelcollector/tracing_test.go b/tests/integration/telemetry/tracing/otelcollector/tracing_test.go index 1dc584d596..9af3b46c93 100644 --- a/tests/integration/telemetry/tracing/otelcollector/tracing_test.go +++ b/tests/integration/telemetry/tracing/otelcollector/tracing_test.go @@ -77,7 +77,7 @@ func TestProxyTracingOpenTelemetryProvider(t *testing.T) { }{ { name: "grpc exporter", - customAttribute: "provider=otel", + customAttribute: "provider=otel and key1=val1", cfgFile: otelTracingCfg, }, { @@ -116,7 +116,9 @@ func TestProxyTracingOpenTelemetryProvider(t *testing.T) { for _, cluster := range ctx.Clusters().ByNetwork()[ctx.Clusters().Default().NetworkName()] { ctx.NewSubTest(cluster.StableName()).Run(func(ctx framework.TestContext) { retry.UntilSuccessOrFail(ctx, func() error { - err := tracing.SendTraffic(ctx, nil, cluster) + err := tracing.SendTraffic(ctx, map[string][]string{ + "key1": {"val1"}, + }, cluster) if err != nil { return fmt.Errorf("cannot send traffic from cluster %s: %v", cluster.Name(), err) } diff --git a/tests/integration/telemetry/tracing/tracing.go b/tests/integration/telemetry/tracing/tracing.go index 1b6b862576..14bac6acce 100644 --- a/tests/integration/telemetry/tracing/tracing.go +++ b/tests/integration/telemetry/tracing/tracing.go @@ -63,6 +63,14 @@ func GetZipkinInstance() zipkin.Instance { return zipkinInst } +func GetServerInstances() echo.Instances { + return server +} + +func GetClientInstances() echo.Instances { + return client +} + func TestSetup(ctx resource.Context) (err error) { appNsInst, err = namespace.New(ctx, namespace.Config{ Prefix: "echo", diff --git a/tests/integration/telemetry/tracing/zipkin/config_test.go b/tests/integration/telemetry/tracing/zipkin/config_test.go new file mode 100644 index 0000000000..5b84099abb --- /dev/null +++ b/tests/integration/telemetry/tracing/zipkin/config_test.go @@ -0,0 +1,277 @@ +//go:build integ + +// Copyright Istio Authors. All Rights Reserved. +// +// 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 zipkin + +import ( + "strings" + "testing" + + admin "github.com/envoyproxy/go-control-plane/envoy/admin/v3" + listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" + tracingcfg "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3" + hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + + "istio.io/istio/pkg/test/framework" + "istio.io/istio/tests/integration/telemetry/tracing" +) + +// TestZipkinConfigDump verifies that the Zipkin tracing configuration from MeshConfig +// is correctly converted to Envoy configuration in the proxy config dump. +func TestZipkinConfigDump(t *testing.T) { + framework.NewTest(t). + Run(func(ctx framework.TestContext) { + // Get server instances from the tracing package + serverInstances := tracing.GetServerInstances() + + if len(serverInstances) == 0 { + ctx.Fatal("No server instances found") + } + + server := serverInstances[0] + workload := server.WorkloadsOrFail(ctx)[0] + configDump := workload.Sidecar().ConfigOrFail(ctx) + + // Verify tracing configuration exists and has Zipkin provider + verifyZipkinTracingExists(ctx, configDump) + }) +} + +func verifyZipkinTracingExists(t framework.TestContext, configDump *admin.ConfigDump) { + t.Helper() + + foundZipkinTracing := false + var zipkinConfig *tracingcfg.ZipkinConfig + + // Find Zipkin tracing configuration + for _, config := range configDump.Configs { + if config.GetTypeUrl() != "type.googleapis.com/envoy.admin.v3.ListenersConfigDump" { + continue + } + + listenersDump := &admin.ListenersConfigDump{} + if err := config.UnmarshalTo(listenersDump); err != nil { + continue + } + + // Check dynamic listeners + for _, dynamicListener := range listenersDump.DynamicListeners { + if dynamicListener.ActiveState == nil || dynamicListener.ActiveState.Listener == nil { + continue + } + + // Unmarshal the listener + listenerConfig := &listener.Listener{} + if err := dynamicListener.ActiveState.Listener.UnmarshalTo(listenerConfig); err != nil { + continue + } + + // Check filter chains + for _, fc := range listenerConfig.FilterChains { + for _, filter := range fc.Filters { + if filter.Name != "envoy.filters.network.http_connection_manager" { + continue + } + + // Unmarshal the HCM config + hcmConfig := &hcm.HttpConnectionManager{} + if err := filter.GetTypedConfig().UnmarshalTo(hcmConfig); err != nil { + continue + } + + // Check if tracing is configured + if hcmConfig.Tracing == nil || hcmConfig.Tracing.Provider == nil { + continue + } + + providerName := hcmConfig.Tracing.Provider.Name + if !strings.Contains(providerName, "zipkin") { + continue + } + + // Found Zipkin! Try to unmarshal the config + zipkinConfig = &tracingcfg.ZipkinConfig{} + if err := hcmConfig.Tracing.Provider.GetTypedConfig().UnmarshalTo(zipkinConfig); err != nil { + continue + } + + foundZipkinTracing = true + t.Logf("Found Zipkin tracing in listener: %s", dynamicListener.Name) + t.Logf("Provider: %s", providerName) + + // Verify TraceContextOption + if zipkinConfig.TraceContextOption != tracingcfg.ZipkinConfig_USE_B3_WITH_W3C_PROPAGATION { + t.Fatalf("TraceContextOption mismatch: got %v, want USE_B3_WITH_W3C_PROPAGATION", zipkinConfig.TraceContextOption) + } + t.Log("TraceContextOption: USE_B3_WITH_W3C_PROPAGATION") + + // Verify HttpService with timeout and headers + httpService := zipkinConfig.GetCollectorService() + if httpService == nil { + t.Fatal("HttpService is nil - timeout and headers not configured") + return // Satisfy static analyzer (unreachable but makes linter happy) + } + t.Log("Using HttpService (supports timeout/headers)") + + // Verify timeout + if httpService.HttpUri == nil || httpService.HttpUri.Timeout == nil { + t.Fatal("Timeout not configured") + return // Satisfy static analyzer + } + + timeout := httpService.HttpUri.Timeout.AsDuration() + t.Logf("Timeout: %s", timeout) + wanted := "5s" + if timeout.String() != wanted { + t.Fatalf("Timeout mismatch: got %s, want %s", timeout, wanted) + } + + // Verify headers + if len(httpService.RequestHeadersToAdd) == 0 { + t.Fatal("No custom headers configured") + return // Satisfy static analyzer + } + t.Logf("Custom headers: %d", len(httpService.RequestHeadersToAdd)) + + // Check for specific headers + foundCustomHeader := false + foundAuthHeader := false + for _, header := range httpService.RequestHeadersToAdd { + t.Logf(" %s: %s", header.Header.Key, header.Header.Value) + if header.Header.Key == "X-Custom-Header" && header.Header.Value == "test-value" { + foundCustomHeader = true + } + if header.Header.Key == "Authorization" && header.Header.Value == "Bearer test-token" { + foundAuthHeader = true + } + } + + if !foundCustomHeader { + t.Fatal("X-Custom-Header not found") + } + t.Log("X-Custom-Header: test-value") + + if !foundAuthHeader { + t.Fatal("Authorization header not found") + } + t.Log("Authorization: Bearer test-token") + + // Log sampling + if hcmConfig.Tracing.RandomSampling != nil { + t.Logf("Sampling: %.0f%%", hcmConfig.Tracing.RandomSampling.Value) + } + + break + } + if foundZipkinTracing { + break + } + } + if foundZipkinTracing { + break + } + } + if foundZipkinTracing { + break + } + } + + if !foundZipkinTracing { + t.Fatal("No Zipkin tracing configuration found in any HTTP connection manager") + } + + t.Log("Zipkin tracing configuration verified successfully") +} + +// TestZipkinConfigWithDefaultProvider tests that extensionProviders with defaultProviders +// generates Envoy tracing configuration without requiring a Telemetry resource. +func TestZipkinConfigWithDefaultProvider(t *testing.T) { + // This test verifies Option 1: defaultProviders.tracing: [zipkin] + // The current setupConfig in main_test.go uses this approach + + framework.NewTest(t). + Run(func(ctx framework.TestContext) { + // Get server instances from the tracing package + serverInstances := tracing.GetServerInstances() + + if len(serverInstances) == 0 { + ctx.Fatal("No server instances found") + } + + server := serverInstances[0] + workload := server.WorkloadsOrFail(ctx)[0] + configDump := workload.Sidecar().ConfigOrFail(ctx) + + // Verify tracing is configured even without a Telemetry resource + // This proves that extensionProviders + defaultProviders generates Envoy config + foundTracing := false + + for _, config := range configDump.Configs { + if config.GetTypeUrl() == "type.googleapis.com/envoy.admin.v3.ListenersConfigDump" { + listenersDump := &admin.ListenersConfigDump{} + if err := config.UnmarshalTo(listenersDump); err != nil { + continue + } + + for _, dynamicListener := range listenersDump.DynamicListeners { + if dynamicListener.ActiveState == nil || dynamicListener.ActiveState.Listener == nil { + continue + } + + // Unmarshal the listener + listenerConfig := &listener.Listener{} + if err := dynamicListener.ActiveState.Listener.UnmarshalTo(listenerConfig); err != nil { + continue + } + + for _, fc := range listenerConfig.FilterChains { + for _, filter := range fc.Filters { + if filter.Name != "envoy.filters.network.http_connection_manager" { + continue + } + + hcmConfig := &hcm.HttpConnectionManager{} + if err := filter.GetTypedConfig().UnmarshalTo(hcmConfig); err != nil { + continue + } + + if hcmConfig.Tracing != nil && hcmConfig.Tracing.Provider != nil { + foundTracing = true + ctx.Logf("Found tracing provider: %s", hcmConfig.Tracing.Provider.Name) + break + } + } + if foundTracing { + break + } + } + if foundTracing { + break + } + } + if foundTracing { + break + } + } + } + + if !foundTracing { + ctx.Fatal("No tracing configuration found - extensionProviders + defaultProviders did NOT generate Envoy config") + } + + ctx.Log("Confirmed: extensionProviders with defaultProviders generates Envoy tracing configuration") + }) +} diff --git a/tests/integration/telemetry/tracing/zipkin/main_test.go b/tests/integration/telemetry/tracing/zipkin/main_test.go index 4ffe8c8b95..2033c57707 100644 --- a/tests/integration/telemetry/tracing/zipkin/main_test.go +++ b/tests/integration/telemetry/tracing/zipkin/main_test.go @@ -38,8 +38,27 @@ func setupConfig(ctx resource.Context, cfg *istio.Config) { if cfg == nil { return } - cfg.Values["meshConfig.enableTracing"] = "true" - cfg.Values["pilot.traceSampling"] = "100.0" - cfg.Values["global.proxy.tracer"] = "zipkin" - cfg.Values["meshConfig.extensionProviders[0].zipkin.traceContextOption"] = "USE_B3_WITH_W3C_PROPAGATION" + cfg.ControlPlaneValues = ` +values: + pilot: + traceSampling: 100.0 +meshConfig: + enableTracing: true + defaultConfig: + tracing: {} # disable legacy MeshConfig tracing options + defaultProviders: + tracing: + - zipkin + extensionProviders: + - name: zipkin + zipkin: + service: zipkin.istio-system.svc.cluster.local + port: 9411 + traceContextOption: USE_B3_WITH_W3C_PROPAGATION + headers: + - name: X-Custom-Header + value: test-value + - name: Authorization + value: Bearer test-token +` }