diff --git a/docs/sources/istio.md b/docs/sources/istio.md index 960deb823..e2c046e38 100644 --- a/docs/sources/istio.md +++ b/docs/sources/istio.md @@ -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" diff --git a/registry/txt_test.go b/registry/txt_test.go index c71424058..d59ce90ef 100644 --- a/registry/txt_test.go +++ b/registry/txt_test.go @@ -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) } diff --git a/source/istio_gateway.go b/source/istio_gateway.go index 0d5c422d7..9043cdcfe 100644 --- a/source/istio_gateway.go +++ b/source/istio_gateway.go @@ -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 { diff --git a/source/istio_gateway_test.go b/source/istio_gateway_test.go index 50f6e4059..21f57009e 100644 --- a/source/istio_gateway_test.go +++ b/source/istio_gateway_test.go @@ -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) } diff --git a/source/istio_virtualservice.go b/source/istio_virtualservice.go index 97b4e79ea..d16c236b0 100644 --- a/source/istio_virtualservice.go +++ b/source/istio_virtualservice.go @@ -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 diff --git a/source/istio_virtualservice_test.go b/source/istio_virtualservice_test.go index 014b1d891..c4c01ba75 100644 --- a/source/istio_virtualservice_test.go +++ b/source/istio_virtualservice_test.go @@ -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{},