feat(source/istio): support version 1.25+ (#5611)

* feat(source/istio): support version 1.22

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>

* feat(source/istio): support version 1.22

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>

* feat(source/istio): support version 1.22

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>

* feat(source/istio): support version 1.22

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>

* feat(source/istio): support version 1.22

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>

* feat(source/istio): support version 1.22

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>

* feat(source/istio): support version 1.22

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>

* feat(source/istio): support version 1.25+

Co-authored-by: mthemis-provenir <168411899+mthemis-provenir@users.noreply.github.com>

---------

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>
Co-authored-by: mthemis-provenir <168411899+mthemis-provenir@users.noreply.github.com>
This commit is contained in:
Ivan Ka 2025-07-07 18:19:28 +01:00 committed by GitHub
parent 5c42ed00c7
commit 252a5e016c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 126 additions and 135 deletions

View File

@ -5,9 +5,13 @@ It is meant to supplement the other provider-specific setup tutorials.
**Note:** Using the Istio Gateway source requires Istio >=1.0.0.
* Manifest (for clusters without RBAC enabled)
* Manifest (for clusters with RBAC enabled)
* Update existing ExternalDNS Deployment
**Note:** Currently supported versions are `1.25` and `1.26` with `v1beta1` stored version.
- [Support status of Istio releases](https://istio.io/latest/docs/releases/supported-releases/)
- Manifest (for clusters without RBAC enabled)
- Manifest (for clusters with RBAC enabled)
- Update existing ExternalDNS Deployment
## Manifest (for clusters without RBAC enabled)
@ -119,9 +123,9 @@ spec:
## Update existing ExternalDNS Deployment
* For clusters with running `external-dns`, you can just update the deployment.
* With access to the `kube-system` namespace, update the existing `external-dns` deployment.
* Add a parameter to the arguments of the container to create dns entries with `--source=istio-gateway`.
- For clusters with running `external-dns`, you can just update the deployment.
- With access to the `kube-system` namespace, update the existing `external-dns` deployment.
- Add a parameter to the arguments of the container to create dns entries with `--source=istio-gateway`.
Execute the following command or update the argument.
@ -148,13 +152,13 @@ The following are relevant snippets from that tutorial.
With automatic sidecar injection:
```bash
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.6/samples/httpbin/httpbin.yaml
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.25/samples/httpbin/httpbin.yaml
```
Otherwise:
```bash
kubectl apply -f <(istioctl kube-inject -f https://raw.githubusercontent.com/istio/istio/release-1.6/samples/httpbin/httpbin.yaml)
kubectl apply -f <(istioctl kube-inject -f https://raw.githubusercontent.com/istio/istio/release-1.25/samples/httpbin/httpbin.yaml)
```
### Using a Gateway as a source
@ -320,13 +324,13 @@ EOF
## Debug ExternalDNS
* Look for the deployment pod to see the status
- Look for the deployment pod to see the status
```console$ kubectl get pods | grep external-dns
external-dns-6b84999479-4knv9 1/1 Running 0 3h29m
```
* Watch for the logs as follows
- Watch for the logs as follows
```console
kubectl logs -f external-dns-6b84999479-4knv9
@ -336,7 +340,7 @@ At this point, you can `create` or `update` any `Istio Gateway` object with `hos
> **ATTENTION**: Make sure to specify those whose account is related to the DNS record.
* Successful executions will print the following
- Successful executions will print the following
```console
time="2020-01-17T06:08:08Z" level=info msg="Desired change: CREATE httpbin.example.com A"
@ -345,7 +349,7 @@ time="2020-01-17T06:08:08Z" level=info msg="2 record(s) in zone example.com. wer
time="2020-01-17T06:09:08Z" level=info msg="All records are already up to date, there are no changes for the matching hosted zones"
```
* If there's any problem around `clusterrole`, you would see the errors showing wrong permissions:
- If there's any problem around `clusterrole`, you would see the errors showing wrong permissions:
```console
source \"gateways\" in API group \"networking.istio.io\" at the cluster scope"

View File

@ -18,7 +18,6 @@ package registry
import (
"context"
"fmt"
"reflect"
"strings"
"testing"
@ -1511,7 +1510,6 @@ func TestNewTXTScheme(t *testing.T) {
assert.Nil(t, ctx.Value(provider.RecordsContextKey))
}
err := r.ApplyChanges(ctx, changes)
fmt.Println(err)
require.NoError(t, err)
}

View File

@ -24,10 +24,10 @@ import (
"text/template"
log "github.com/sirupsen/logrus"
networkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3"
networkingv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1"
istioclient "istio.io/client-go/pkg/clientset/versioned"
istioinformers "istio.io/client-go/pkg/informers/externalversions"
networkingv1alpha3informer "istio.io/client-go/pkg/informers/externalversions/networking/v1alpha3"
networkingv1beta1informer "istio.io/client-go/pkg/informers/externalversions/networking/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
kubeinformers "k8s.io/client-go/informers"
@ -57,7 +57,7 @@ type gatewaySource struct {
combineFQDNAnnotation bool
ignoreHostnameAnnotation bool
serviceInformer coreinformers.ServiceInformer
gatewayInformer networkingv1alpha3informer.GatewayInformer
gatewayInformer networkingv1beta1informer.GatewayInformer
}
// NewIstioGatewaySource creates a new gatewaySource with the given config.
@ -81,10 +81,10 @@ func NewIstioGatewaySource(
informerFactory := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 0, kubeinformers.WithNamespace(namespace))
serviceInformer := informerFactory.Core().V1().Services()
istioInformerFactory := istioinformers.NewSharedInformerFactory(istioClient, 0)
gatewayInformer := istioInformerFactory.Networking().V1alpha3().Gateways()
gatewayInformer := istioInformerFactory.Networking().V1beta1().Gateways()
// Add default resource event handlers to properly initialize informer.
serviceInformer.Informer().AddEventHandler(
_, _ = serviceInformer.Informer().AddEventHandler(
cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
log.Debug("service added")
@ -92,7 +92,7 @@ func NewIstioGatewaySource(
},
)
gatewayInformer.Informer().AddEventHandler(
_, _ = gatewayInformer.Informer().AddEventHandler(
cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
log.Debug("gateway added")
@ -127,7 +127,7 @@ func NewIstioGatewaySource(
// Endpoints returns endpoint objects for each host-target combination that should be processed.
// Retrieves all gateway resources in the source's namespace(s).
func (sc *gatewaySource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) {
gwList, err := sc.istioClient.NetworkingV1alpha3().Gateways(sc.namespace).List(ctx, metav1.ListOptions{})
gwList, err := sc.istioClient.NetworkingV1beta1().Gateways(sc.namespace).List(ctx, metav1.ListOptions{})
if err != nil {
return nil, err
}
@ -140,12 +140,14 @@ func (sc *gatewaySource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, e
var endpoints []*endpoint.Endpoint
log.Debugf("Found %d gateways in namespace %s", len(gateways), sc.namespace)
for _, gateway := range gateways {
// Check controller annotation to see if we are responsible.
controller, ok := gateway.Annotations[controllerAnnotationKey]
if ok && controller != controllerAnnotationValue {
log.Debugf("Skipping gateway %s/%s because controller value does not match, found: %s, required: %s",
gateway.Namespace, gateway.Name, controller, controllerAnnotationValue)
log.Debugf("Skipping gateway %s/%s,%s because controller value does not match, found: %s, required: %s",
gateway.Namespace, gateway.APIVersion, gateway.Name, controller, controllerAnnotationValue)
continue
}
@ -168,6 +170,8 @@ func (sc *gatewaySource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, e
}
}
log.Debugf("Processing gateway '%s/%s.%s' and hosts %q", gateway.Namespace, gateway.APIVersion, gateway.Name, strings.Join(gwHostnames, ","))
if len(gwHostnames) == 0 {
log.Debugf("No hostnames could be generated from gateway %s/%s", gateway.Namespace, gateway.Name)
continue
@ -183,10 +187,11 @@ func (sc *gatewaySource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, e
continue
}
log.Debugf("Endpoints generated from gateway: %s/%s: %v", gateway.Namespace, gateway.Name, gwEndpoints)
log.Debugf("Endpoints generated from %q '%s/%s.%s': %q", gateway.Kind, gateway.Namespace, gateway.APIVersion, gateway.Name, gwEndpoints)
endpoints = append(endpoints, gwEndpoints...)
}
// TODO: sort on endpoint creation
for _, ep := range endpoints {
sort.Sort(ep.Targets)
}
@ -198,11 +203,11 @@ func (sc *gatewaySource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, e
func (sc *gatewaySource) AddEventHandler(ctx context.Context, handler func()) {
log.Debug("Adding event handler for Istio Gateway")
sc.gatewayInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
_, _ = sc.gatewayInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
}
// filterByAnnotations filters a list of configs by a given annotation selector.
func (sc *gatewaySource) filterByAnnotations(gateways []*networkingv1alpha3.Gateway) ([]*networkingv1alpha3.Gateway, error) {
func (sc *gatewaySource) filterByAnnotations(gateways []*networkingv1beta1.Gateway) ([]*networkingv1beta1.Gateway, error) {
selector, err := annotations.ParseFilter(sc.annotationFilter)
if err != nil {
return nil, err
@ -213,7 +218,7 @@ func (sc *gatewaySource) filterByAnnotations(gateways []*networkingv1alpha3.Gate
return gateways, nil
}
var filteredList []*networkingv1alpha3.Gateway
var filteredList []*networkingv1beta1.Gateway
for _, gw := range gateways {
// include if the annotations match the selector
@ -225,7 +230,7 @@ func (sc *gatewaySource) filterByAnnotations(gateways []*networkingv1alpha3.Gate
return filteredList, nil
}
func (sc *gatewaySource) targetsFromIngress(ctx context.Context, ingressStr string, gateway *networkingv1alpha3.Gateway) (endpoint.Targets, error) {
func (sc *gatewaySource) targetsFromIngress(ctx context.Context, ingressStr string, gateway *networkingv1beta1.Gateway) (endpoint.Targets, error) {
namespace, name, err := ParseIngress(ingressStr)
if err != nil {
return nil, fmt.Errorf("failed to parse Ingress annotation on Gateway (%s/%s): %w", gateway.Namespace, gateway.Name, err)
@ -251,7 +256,7 @@ func (sc *gatewaySource) targetsFromIngress(ctx context.Context, ingressStr stri
return targets, nil
}
func (sc *gatewaySource) targetsFromGateway(ctx context.Context, gateway *networkingv1alpha3.Gateway) (endpoint.Targets, error) {
func (sc *gatewaySource) targetsFromGateway(ctx context.Context, gateway *networkingv1beta1.Gateway) (endpoint.Targets, error) {
targets := annotations.TargetsFromTargetAnnotation(gateway.Annotations)
if len(targets) > 0 {
return targets, nil
@ -266,22 +271,21 @@ func (sc *gatewaySource) targetsFromGateway(ctx context.Context, gateway *networ
}
// endpointsFromGatewayConfig extracts the endpoints from an Istio Gateway Config object
func (sc *gatewaySource) endpointsFromGateway(ctx context.Context, hostnames []string, gateway *networkingv1alpha3.Gateway) ([]*endpoint.Endpoint, error) {
func (sc *gatewaySource) endpointsFromGateway(ctx context.Context, hostnames []string, gateway *networkingv1beta1.Gateway) ([]*endpoint.Endpoint, error) {
var endpoints []*endpoint.Endpoint
var err error
resource := fmt.Sprintf("gateway/%s/%s", gateway.Namespace, gateway.Name)
ttl := annotations.TTLFromAnnotations(gateway.Annotations, resource)
targets := annotations.TargetsFromTargetAnnotation(gateway.Annotations)
if len(targets) == 0 {
targets, err = sc.targetsFromGateway(ctx, gateway)
if err != nil {
return nil, err
}
targets, err := sc.targetsFromGateway(ctx, gateway)
if err != nil {
return nil, err
}
if len(targets) == 0 {
return endpoints, nil
}
resource := fmt.Sprintf("gateway/%s/%s", gateway.Namespace, gateway.Name)
ttl := annotations.TTLFromAnnotations(gateway.Annotations, resource)
providerSpecific, setIdentifier := annotations.ProviderSpecificAnnotations(gateway.Annotations)
for _, host := range hostnames {
@ -291,7 +295,7 @@ func (sc *gatewaySource) endpointsFromGateway(ctx context.Context, hostnames []s
return endpoints, nil
}
func (sc *gatewaySource) hostNamesFromGateway(gateway *networkingv1alpha3.Gateway) ([]string, error) {
func (sc *gatewaySource) hostNamesFromGateway(gateway *networkingv1beta1.Gateway) ([]string, error) {
var hostnames []string
for _, server := range gateway.Spec.Servers {
for _, host := range server.Hosts {

View File

@ -24,8 +24,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
networkingv1alpha3api "istio.io/api/networking/v1alpha3"
networkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3"
networkingv1alpha3api "istio.io/api/networking/v1beta1"
networkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1beta1"
istiofake "istio.io/client-go/pkg/clientset/versioned/fake"
v1 "k8s.io/api/core/v1"
networkv1 "k8s.io/api/networking/v1"
@ -1494,7 +1494,7 @@ func testGatewayEndpoints(t *testing.T) {
fakeIstioClient := istiofake.NewSimpleClientset()
for _, config := range ti.configItems {
gatewayCfg := config.Config()
_, err := fakeIstioClient.NetworkingV1alpha3().Gateways(ti.targetNamespace).Create(context.Background(), gatewayCfg, metav1.CreateOptions{})
_, err := fakeIstioClient.NetworkingV1beta1().Gateways(ti.targetNamespace).Create(context.Background(), gatewayCfg, metav1.CreateOptions{})
require.NoError(t, err)
}

View File

@ -20,15 +20,16 @@ import (
"cmp"
"context"
"fmt"
"slices"
"sort"
"strings"
"text/template"
log "github.com/sirupsen/logrus"
networkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3"
v1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1"
istioclient "istio.io/client-go/pkg/clientset/versioned"
istioinformers "istio.io/client-go/pkg/informers/externalversions"
networkingv1alpha3informer "istio.io/client-go/pkg/informers/externalversions/networking/v1alpha3"
networkingv1beta1informer "istio.io/client-go/pkg/informers/externalversions/networking/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
@ -58,8 +59,8 @@ type virtualServiceSource struct {
combineFQDNAnnotation bool
ignoreHostnameAnnotation bool
serviceInformer coreinformers.ServiceInformer
virtualserviceInformer networkingv1alpha3informer.VirtualServiceInformer
gatewayInformer networkingv1alpha3informer.GatewayInformer
vServiceInformer networkingv1beta1informer.VirtualServiceInformer
gatewayInformer networkingv1beta1informer.GatewayInformer
}
// NewIstioVirtualServiceSource creates a new virtualServiceSource with the given config.
@ -83,11 +84,11 @@ func NewIstioVirtualServiceSource(
informerFactory := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 0, kubeinformers.WithNamespace(namespace))
serviceInformer := informerFactory.Core().V1().Services()
istioInformerFactory := istioinformers.NewSharedInformerFactoryWithOptions(istioClient, 0, istioinformers.WithNamespace(namespace))
virtualServiceInformer := istioInformerFactory.Networking().V1alpha3().VirtualServices()
gatewayInformer := istioInformerFactory.Networking().V1alpha3().Gateways()
virtualServiceInformer := istioInformerFactory.Networking().V1beta1().VirtualServices()
gatewayInformer := istioInformerFactory.Networking().V1beta1().Gateways()
// Add default resource event handlers to properly initialize informer.
serviceInformer.Informer().AddEventHandler(
_, _ = serviceInformer.Informer().AddEventHandler(
cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
log.Debug("service added")
@ -95,7 +96,7 @@ func NewIstioVirtualServiceSource(
},
)
virtualServiceInformer.Informer().AddEventHandler(
_, _ = virtualServiceInformer.Informer().AddEventHandler(
cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
log.Debug("virtual service added")
@ -103,7 +104,7 @@ func NewIstioVirtualServiceSource(
},
)
gatewayInformer.Informer().AddEventHandler(
_, _ = gatewayInformer.Informer().AddEventHandler(
cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
log.Debug("gateway added")
@ -131,7 +132,7 @@ func NewIstioVirtualServiceSource(
combineFQDNAnnotation: combineFQDNAnnotation,
ignoreHostnameAnnotation: ignoreHostnameAnnotation,
serviceInformer: serviceInformer,
virtualserviceInformer: virtualServiceInformer,
vServiceInformer: virtualServiceInformer,
gatewayInformer: gatewayInformer,
}, nil
}
@ -139,7 +140,7 @@ func NewIstioVirtualServiceSource(
// Endpoints returns endpoint objects for each host-target combination that should be processed.
// Retrieves all VirtualService resources in the source's namespace(s).
func (sc *virtualServiceSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) {
virtualServices, err := sc.virtualserviceInformer.Lister().VirtualServices(sc.namespace).List(labels.Everything())
virtualServices, err := sc.vServiceInformer.Lister().VirtualServices(sc.namespace).List(labels.Everything())
if err != nil {
return nil, err
}
@ -150,23 +151,25 @@ func (sc *virtualServiceSource) Endpoints(ctx context.Context) ([]*endpoint.Endp
var endpoints []*endpoint.Endpoint
for _, virtualService := range virtualServices {
log.Debugf("Found %d virtualservice in namespace %s", len(virtualServices), sc.namespace)
for _, vService := range virtualServices {
// Check controller annotation to see if we are responsible.
controller, ok := virtualService.Annotations[controllerAnnotationKey]
controller, ok := vService.Annotations[controllerAnnotationKey]
if ok && controller != controllerAnnotationValue {
log.Debugf("Skipping VirtualService %s/%s because controller value does not match, found: %s, required: %s",
virtualService.Namespace, virtualService.Name, controller, controllerAnnotationValue)
log.Debugf("Skipping VirtualService %s/%s.%s because controller value does not match, found: %s, required: %s",
vService.Namespace, vService.APIVersion, vService.Name, controller, controllerAnnotationValue)
continue
}
gwEndpoints, err := sc.endpointsFromVirtualService(ctx, virtualService)
gwEndpoints, err := sc.endpointsFromVirtualService(ctx, vService)
if err != nil {
return nil, err
}
// apply template if host is missing on VirtualService
if (sc.combineFQDNAnnotation || len(gwEndpoints) == 0) && sc.fqdnTemplate != nil {
iEndpoints, err := sc.endpointsFromTemplate(ctx, virtualService)
iEndpoints, err := sc.endpointsFromTemplate(ctx, vService)
if err != nil {
return nil, err
}
@ -179,14 +182,15 @@ func (sc *virtualServiceSource) Endpoints(ctx context.Context) ([]*endpoint.Endp
}
if len(gwEndpoints) == 0 {
log.Debugf("No endpoints could be generated from VirtualService %s/%s", virtualService.Namespace, virtualService.Name)
log.Debugf("No endpoints could be generated from VirtualService %s/%s", vService.Namespace, vService.Name)
continue
}
log.Debugf("Endpoints generated from VirtualService: %s/%s: %v", virtualService.Namespace, virtualService.Name, gwEndpoints)
log.Debugf("Endpoints generated from %q '%s/%s.%s': %q", vService.Kind, vService.Namespace, vService.APIVersion, vService.Name, gwEndpoints)
endpoints = append(endpoints, gwEndpoints...)
}
// TODO: sort on endpoint creation
for _, ep := range endpoints {
sort.Sort(ep.Targets)
}
@ -198,16 +202,16 @@ func (sc *virtualServiceSource) Endpoints(ctx context.Context) ([]*endpoint.Endp
func (sc *virtualServiceSource) AddEventHandler(_ context.Context, handler func()) {
log.Debug("Adding event handler for Istio VirtualService")
sc.virtualserviceInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
_, _ = sc.vServiceInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
}
func (sc *virtualServiceSource) getGateway(_ context.Context, gatewayStr string, virtualService *networkingv1alpha3.VirtualService) (*networkingv1alpha3.Gateway, error) {
func (sc *virtualServiceSource) getGateway(_ context.Context, gatewayStr string, virtualService *v1beta1.VirtualService) (*v1beta1.Gateway, error) {
if gatewayStr == "" || gatewayStr == IstioMeshGateway {
// This refers to "all sidecars in the mesh"; ignore.
return nil, nil
}
namespace, name, err := parseGateway(gatewayStr)
namespace, name, err := ParseIngress(gatewayStr)
if err != nil {
log.Debugf("Failed parsing gatewayStr %s of VirtualService %s/%s", gatewayStr, virtualService.Namespace, virtualService.Name)
return nil, err
@ -229,7 +233,7 @@ func (sc *virtualServiceSource) getGateway(_ context.Context, gatewayStr string,
return gateway, nil
}
func (sc *virtualServiceSource) endpointsFromTemplate(ctx context.Context, virtualService *networkingv1alpha3.VirtualService) ([]*endpoint.Endpoint, error) {
func (sc *virtualServiceSource) endpointsFromTemplate(ctx context.Context, virtualService *v1beta1.VirtualService) ([]*endpoint.Endpoint, error) {
hostnames, err := fqdn.ExecTemplate(sc.fqdnTemplate, virtualService)
if err != nil {
return nil, err
@ -253,7 +257,7 @@ func (sc *virtualServiceSource) endpointsFromTemplate(ctx context.Context, virtu
}
// filterByAnnotations filters a list of configs by a given annotation selector.
func (sc *virtualServiceSource) filterByAnnotations(virtualservices []*networkingv1alpha3.VirtualService) ([]*networkingv1alpha3.VirtualService, error) {
func (sc *virtualServiceSource) filterByAnnotations(vServices []*v1beta1.VirtualService) ([]*v1beta1.VirtualService, error) {
selector, err := annotations.ParseFilter(sc.annotationFilter)
if err != nil {
return nil, err
@ -261,12 +265,12 @@ func (sc *virtualServiceSource) filterByAnnotations(virtualservices []*networkin
// empty filter returns original list
if selector.Empty() {
return virtualservices, nil
return vServices, nil
}
var filteredList []*networkingv1alpha3.VirtualService
var filteredList []*v1beta1.VirtualService
for _, vs := range virtualservices {
for _, vs := range vServices {
// include if the annotations match the selector
if selector.Matches(labels.Set(vs.Annotations)) {
filteredList = append(filteredList, vs)
@ -278,26 +282,24 @@ func (sc *virtualServiceSource) filterByAnnotations(virtualservices []*networkin
// append a target to the list of targets unless it's already in the list
func appendUnique(targets []string, target string) []string {
for _, element := range targets {
if element == target {
return targets
}
if slices.Contains(targets, target) {
return targets
}
return append(targets, target)
}
func (sc *virtualServiceSource) targetsFromVirtualService(ctx context.Context, virtualService *networkingv1alpha3.VirtualService, vsHost string) ([]string, error) {
func (sc *virtualServiceSource) targetsFromVirtualService(ctx context.Context, vService *v1beta1.VirtualService, vsHost string) ([]string, error) {
var targets []string
// for each host we need to iterate through the gateways because each host might match for only one of the gateways
for _, gateway := range virtualService.Spec.Gateways {
gw, err := sc.getGateway(ctx, gateway, virtualService)
for _, gateway := range vService.Spec.Gateways {
gw, err := sc.getGateway(ctx, gateway, vService)
if err != nil {
return nil, err
}
if gw == nil {
continue
}
if !virtualServiceBindsToGateway(virtualService, gw, vsHost) {
if !virtualServiceBindsToGateway(vService, gw, vsHost) {
continue
}
tgs, err := sc.targetsFromGateway(ctx, gw)
@ -308,24 +310,23 @@ func (sc *virtualServiceSource) targetsFromVirtualService(ctx context.Context, v
targets = appendUnique(targets, target)
}
}
return targets, nil
}
// endpointsFromVirtualService extracts the endpoints from an Istio VirtualService Config object
func (sc *virtualServiceSource) endpointsFromVirtualService(ctx context.Context, virtualservice *networkingv1alpha3.VirtualService) ([]*endpoint.Endpoint, error) {
func (sc *virtualServiceSource) endpointsFromVirtualService(ctx context.Context, vService *v1beta1.VirtualService) ([]*endpoint.Endpoint, error) {
var endpoints []*endpoint.Endpoint
var err error
resource := fmt.Sprintf("virtualservice/%s/%s", virtualservice.Namespace, virtualservice.Name)
resource := fmt.Sprintf("virtualservice/%s/%s", vService.Namespace, vService.Name)
ttl := annotations.TTLFromAnnotations(virtualservice.Annotations, resource)
ttl := annotations.TTLFromAnnotations(vService.Annotations, resource)
targetsFromAnnotation := annotations.TargetsFromTargetAnnotation(virtualservice.Annotations)
targetsFromAnnotation := annotations.TargetsFromTargetAnnotation(vService.Annotations)
providerSpecific, setIdentifier := annotations.ProviderSpecificAnnotations(virtualservice.Annotations)
providerSpecific, setIdentifier := annotations.ProviderSpecificAnnotations(vService.Annotations)
for _, host := range virtualservice.Spec.Hosts {
for _, host := range vService.Spec.Hosts {
if host == "" || host == "*" {
continue
}
@ -340,7 +341,7 @@ func (sc *virtualServiceSource) endpointsFromVirtualService(ctx context.Context,
targets := targetsFromAnnotation
if len(targets) == 0 {
targets, err = sc.targetsFromVirtualService(ctx, virtualservice, host)
targets, err = sc.targetsFromVirtualService(ctx, vService, host)
if err != nil {
return endpoints, err
}
@ -351,11 +352,11 @@ func (sc *virtualServiceSource) endpointsFromVirtualService(ctx context.Context,
// Skip endpoints if we do not want entries from annotations
if !sc.ignoreHostnameAnnotation {
hostnameList := annotations.HostnamesFromAnnotations(virtualservice.Annotations)
hostnameList := annotations.HostnamesFromAnnotations(vService.Annotations)
for _, hostname := range hostnameList {
targets := targetsFromAnnotation
if len(targets) == 0 {
targets, err = sc.targetsFromVirtualService(ctx, virtualservice, hostname)
targets, err = sc.targetsFromVirtualService(ctx, vService, hostname)
if err != nil {
return endpoints, err
}
@ -369,13 +370,13 @@ func (sc *virtualServiceSource) endpointsFromVirtualService(ctx context.Context,
// checks if the given VirtualService should actually bind to the given gateway
// see requirements here: https://istio.io/docs/reference/config/networking/gateway/#Server
func virtualServiceBindsToGateway(virtualService *networkingv1alpha3.VirtualService, gateway *networkingv1alpha3.Gateway, vsHost string) bool {
func virtualServiceBindsToGateway(vService *v1beta1.VirtualService, gateway *v1beta1.Gateway, vsHost string) bool {
isValid := false
if len(virtualService.Spec.ExportTo) == 0 {
if len(vService.Spec.ExportTo) == 0 {
isValid = true
} else {
for _, ns := range virtualService.Spec.ExportTo {
if ns == "*" || ns == gateway.Namespace || (ns == "." && gateway.Namespace == virtualService.Namespace) {
for _, ns := range vService.Spec.ExportTo {
if ns == "*" || ns == gateway.Namespace || (ns == "." && gateway.Namespace == vService.Namespace) {
isValid = true
}
}
@ -396,7 +397,7 @@ func virtualServiceBindsToGateway(virtualService *networkingv1alpha3.VirtualServ
continue
}
if namespace == "*" || namespace == virtualService.Namespace || (namespace == "." && virtualService.Namespace == gateway.Namespace) {
if namespace == "*" || namespace == vService.Namespace || (namespace == "." && vService.Namespace == gateway.Namespace) {
if host == "*" {
return true
}
@ -416,23 +417,7 @@ func virtualServiceBindsToGateway(virtualService *networkingv1alpha3.VirtualServ
return false
}
// TODO: similar to ParseIngress
func parseGateway(gateway string) (string, string, error) {
var namespace, name string
var err error
parts := strings.Split(gateway, "/")
if len(parts) == 2 {
namespace, name = parts[0], parts[1]
} else if len(parts) == 1 {
name = parts[0]
} else {
err = fmt.Errorf("invalid gateway name (name or namespace/name) found '%v'", gateway)
}
return namespace, name, err
}
func (sc *virtualServiceSource) targetsFromIngress(ctx context.Context, ingressStr string, gateway *networkingv1alpha3.Gateway) (endpoint.Targets, error) {
func (sc *virtualServiceSource) targetsFromIngress(ctx context.Context, ingressStr string, gateway *v1beta1.Gateway) (endpoint.Targets, error) {
namespace, name, err := ParseIngress(ingressStr)
if err != nil {
return nil, fmt.Errorf("failed to parse Ingress annotation on Gateway (%s/%s): %w", gateway.Namespace, gateway.Name, err)
@ -459,7 +444,7 @@ func (sc *virtualServiceSource) targetsFromIngress(ctx context.Context, ingressS
return targets, nil
}
func (sc *virtualServiceSource) targetsFromGateway(ctx context.Context, gateway *networkingv1alpha3.Gateway) (endpoint.Targets, error) {
func (sc *virtualServiceSource) targetsFromGateway(ctx context.Context, gateway *v1beta1.Gateway) (endpoint.Targets, error) {
targets := annotations.TargetsFromTargetAnnotation(gateway.Annotations)
if len(targets) > 0 {
return targets, nil

View File

@ -25,8 +25,8 @@ import (
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"istio.io/api/meta/v1alpha1"
istionetworking "istio.io/api/networking/v1alpha3"
networkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3"
istionetworking "istio.io/api/networking/v1beta1"
networkingv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1"
istiofake "istio.io/client-go/pkg/clientset/versioned/fake"
v1 "k8s.io/api/core/v1"
networkv1 "k8s.io/api/networking/v1"
@ -44,8 +44,8 @@ type VirtualServiceSuite struct {
source Source
lbServices []*v1.Service
ingresses []*networkv1.Ingress
gwconfig *networkingv1alpha3.Gateway
vsconfig *networkingv1alpha3.VirtualService
gwconfig *networkingv1beta1.Gateway
vsconfig *networkingv1beta1.VirtualService
}
func (suite *VirtualServiceSuite) SetupTest() {
@ -98,7 +98,7 @@ func (suite *VirtualServiceSuite) SetupTest() {
namespace: "istio-system",
dnsnames: [][]string{{"*"}},
}).Config()
_, err = fakeIstioClient.NetworkingV1alpha3().Gateways(suite.gwconfig.Namespace).Create(context.Background(), suite.gwconfig, metav1.CreateOptions{})
_, err = fakeIstioClient.NetworkingV1beta1().Gateways(suite.gwconfig.Namespace).Create(context.Background(), suite.gwconfig, metav1.CreateOptions{})
suite.NoError(err, "should succeed")
suite.vsconfig = (fakeVirtualServiceConfig{
@ -107,7 +107,7 @@ func (suite *VirtualServiceSuite) SetupTest() {
gateways: []string{"istio-system/foo-gateway-with-targets"},
dnsnames: []string{"foo"},
}).Config()
_, err = fakeIstioClient.NetworkingV1alpha3().VirtualServices(suite.vsconfig.Namespace).Create(context.Background(), suite.vsconfig, metav1.CreateOptions{})
_, err = fakeIstioClient.NetworkingV1beta1().VirtualServices(suite.vsconfig.Namespace).Create(context.Background(), suite.vsconfig, metav1.CreateOptions{})
suite.NoError(err, "should succeed")
suite.source, err = NewIstioVirtualServiceSource(
@ -1948,8 +1948,8 @@ func testVirtualServiceEndpoints(t *testing.T) {
t.Run(ti.title, func(t *testing.T) {
t.Parallel()
var gateways []*networkingv1alpha3.Gateway
var virtualservices []*networkingv1alpha3.VirtualService
var gateways []*networkingv1beta1.Gateway
var virtualservices []*networkingv1beta1.VirtualService
for _, gwItem := range ti.gwConfigs {
gateways = append(gateways, gwItem.Config())
@ -1958,7 +1958,7 @@ func testVirtualServiceEndpoints(t *testing.T) {
virtualservices = append(virtualservices, vsItem.Config())
}
fakeKubernetesClient := fake.NewSimpleClientset()
fakeKubernetesClient := fake.NewClientset()
for _, lb := range ti.lbServices {
service := lb.Service()
@ -1975,12 +1975,12 @@ func testVirtualServiceEndpoints(t *testing.T) {
fakeIstioClient := istiofake.NewSimpleClientset()
for _, gateway := range gateways {
_, err := fakeIstioClient.NetworkingV1alpha3().Gateways(gateway.Namespace).Create(context.Background(), gateway, metav1.CreateOptions{})
_, err := fakeIstioClient.NetworkingV1beta1().Gateways(gateway.Namespace).Create(context.Background(), gateway, metav1.CreateOptions{})
require.NoError(t, err)
}
for _, virtualservice := range virtualservices {
_, err := fakeIstioClient.NetworkingV1alpha3().VirtualServices(virtualservice.Namespace).Create(context.Background(), virtualservice, metav1.CreateOptions{})
for _, vService := range virtualservices {
_, err := fakeIstioClient.NetworkingV1beta1().VirtualServices(vService.Namespace).Create(context.Background(), vService, metav1.CreateOptions{})
require.NoError(t, err)
}
@ -2041,7 +2041,7 @@ func testGatewaySelectorMatchesService(t *testing.T) {
}
func newTestVirtualServiceSource(loadBalancerList []fakeIngressGatewayService, ingressList []fakeIngress, gwList []fakeGatewayConfig) (*virtualServiceSource, error) {
fakeKubernetesClient := fake.NewSimpleClientset()
fakeKubernetesClient := fake.NewClientset()
fakeIstioClient := istiofake.NewSimpleClientset()
for _, lb := range loadBalancerList {
@ -2064,7 +2064,7 @@ func newTestVirtualServiceSource(loadBalancerList []fakeIngressGatewayService, i
gwObj := gw.Config()
// use create instead of add
// https://github.com/kubernetes/client-go/blob/92512ee2b8cf6696e9909245624175b7f0c971d9/testing/fixture.go#LL336C3-L336C52
_, err := fakeIstioClient.NetworkingV1alpha3().Gateways(gw.namespace).Create(context.Background(), gwObj, metav1.CreateOptions{})
_, err := fakeIstioClient.NetworkingV1beta1().Gateways(gw.namespace).Create(context.Background(), gwObj, metav1.CreateOptions{})
if err != nil {
return nil, err
}
@ -2101,7 +2101,7 @@ type fakeVirtualServiceConfig struct {
exportTo string
}
func (c fakeVirtualServiceConfig) Config() *networkingv1alpha3.VirtualService {
func (c fakeVirtualServiceConfig) Config() *networkingv1beta1.VirtualService {
vs := istionetworking.VirtualService{
Gateways: c.gateways,
Hosts: c.dnsnames,
@ -2110,7 +2110,7 @@ func (c fakeVirtualServiceConfig) Config() *networkingv1alpha3.VirtualService {
vs.ExportTo = []string{c.exportTo}
}
return &networkingv1alpha3.VirtualService{
return &networkingv1beta1.VirtualService{
ObjectMeta: metav1.ObjectMeta{
Name: c.name,
Namespace: c.namespace,
@ -2127,13 +2127,13 @@ func TestVirtualServiceSourceGetGateway(t *testing.T) {
type args struct {
ctx context.Context
gatewayStr string
virtualService *networkingv1alpha3.VirtualService
virtualService *networkingv1beta1.VirtualService
}
tests := []struct {
name string
fields fields
args args
want *networkingv1alpha3.Gateway
want *networkingv1beta1.Gateway
expectedErrStr string
}{
{name: "EmptyGateway", fields: fields{
@ -2155,7 +2155,7 @@ func TestVirtualServiceSourceGetGateway(t *testing.T) {
}, args: args{
ctx: context.TODO(),
gatewayStr: "doesnt/exist",
virtualService: &networkingv1alpha3.VirtualService{
virtualService: &networkingv1beta1.VirtualService{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{Name: "exist", Namespace: "doesnt"},
Spec: istionetworking.VirtualService{},
@ -2167,8 +2167,8 @@ func TestVirtualServiceSourceGetGateway(t *testing.T) {
}, args: args{
ctx: context.TODO(),
gatewayStr: "1/2/3/",
virtualService: &networkingv1alpha3.VirtualService{},
}, want: nil, expectedErrStr: "invalid gateway name (name or namespace/name) found '1/2/3/'"},
virtualService: &networkingv1beta1.VirtualService{},
}, want: nil, expectedErrStr: "invalid ingress name (name or namespace/name) found \"1/2/3/\""},
{name: "ExistingGateway", fields: fields{
virtualServiceSource: func() *virtualServiceSource {
vs, _ := newTestVirtualServiceSource(nil, nil, []fakeGatewayConfig{{
@ -2180,13 +2180,13 @@ func TestVirtualServiceSourceGetGateway(t *testing.T) {
}, args: args{
ctx: context.TODO(),
gatewayStr: "bar/foo",
virtualService: &networkingv1alpha3.VirtualService{
virtualService: &networkingv1beta1.VirtualService{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
Spec: istionetworking.VirtualService{},
Status: v1alpha1.IstioStatus{},
},
}, want: &networkingv1alpha3.Gateway{
}, want: &networkingv1beta1.Gateway{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
Spec: istionetworking.Gateway{},