istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/framework/components/istio/util.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package istio
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"net"
    21  	"net/netip"
    22  	"strconv"
    23  	"time"
    24  
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  
    27  	"istio.io/istio/pkg/test/framework/components/cluster"
    28  	"istio.io/istio/pkg/test/framework/components/environment/kube"
    29  	"istio.io/istio/pkg/test/framework/resource"
    30  	"istio.io/istio/pkg/test/scopes"
    31  	"istio.io/istio/pkg/test/util/retry"
    32  	"istio.io/istio/pkg/test/util/yml"
    33  )
    34  
    35  var dummyValidationVirtualServiceTemplate = `
    36  apiVersion: networking.istio.io/v1alpha3
    37  kind: VirtualService
    38  metadata:
    39    name: validation-readiness-dummy-virtual-service
    40    namespace: %s
    41  spec:
    42    hosts:
    43      - non-existent-host
    44    http:
    45      - route:
    46        - destination:
    47            host: non-existent-host
    48            subset: v1
    49          weight: 75
    50        - destination:
    51            host: non-existent-host
    52            subset: v2
    53          weight: 25
    54  `
    55  
    56  func waitForValidationWebhook(ctx resource.Context, cluster cluster.Cluster, cfg Config) error {
    57  	dummyValidationVirtualService := fmt.Sprintf(dummyValidationVirtualServiceTemplate, cfg.SystemNamespace)
    58  	defer func() {
    59  		e := ctx.ConfigKube(cluster).YAML("", dummyValidationVirtualService).Delete()
    60  		if e != nil {
    61  			scopes.Framework.Warnf("error deleting dummy virtual service for waiting the validation webhook: %v", e)
    62  		}
    63  	}()
    64  
    65  	scopes.Framework.Info("Creating dummy virtual service to check for validation webhook readiness")
    66  	return retry.UntilSuccess(func() error {
    67  		err := ctx.ConfigKube(cluster).YAML("", dummyValidationVirtualService).Apply()
    68  		if err == nil {
    69  			return nil
    70  		}
    71  
    72  		return fmt.Errorf("validation webhook not ready yet: %v", err)
    73  	}, retry.Timeout(time.Minute))
    74  }
    75  
    76  func getRemoteServiceAddresses(s *kube.Settings, cluster cluster.Cluster, ns, label, svcName string,
    77  	port int,
    78  ) ([]any, bool, error) {
    79  	if !s.LoadBalancerSupported {
    80  		pods, err := cluster.PodsForSelector(context.TODO(), ns, label)
    81  		if err != nil {
    82  			return nil, false, err
    83  		}
    84  
    85  		names := make([]string, 0, len(pods.Items))
    86  		for _, p := range pods.Items {
    87  			names = append(names, p.Name)
    88  		}
    89  		scopes.Framework.Debugf("Querying remote service %s, pods:%v", svcName, names)
    90  		if len(pods.Items) == 0 {
    91  			return nil, false, fmt.Errorf("no remote service pod found")
    92  		}
    93  
    94  		scopes.Framework.Debugf("Found pod: %v", pods.Items[0].Name)
    95  		ip := pods.Items[0].Status.HostIP
    96  		if ip == "" {
    97  			return nil, false, fmt.Errorf("no Host IP available on the remote service node yet")
    98  		}
    99  
   100  		svc, err := cluster.Kube().CoreV1().Services(ns).Get(context.TODO(), svcName, metav1.GetOptions{})
   101  		if err != nil {
   102  			return nil, false, err
   103  		}
   104  
   105  		if len(svc.Spec.Ports) == 0 {
   106  			return nil, false, fmt.Errorf("no ports found in service: %s/%s", ns, svcName)
   107  		}
   108  
   109  		var nodePort int32
   110  		for _, svcPort := range svc.Spec.Ports {
   111  			if svcPort.Protocol == "TCP" && svcPort.Port == int32(port) {
   112  				nodePort = svcPort.NodePort
   113  				break
   114  			}
   115  		}
   116  		if nodePort == 0 {
   117  			return nil, false, fmt.Errorf("no port %d found in service: %s/%s", port, ns, svcName)
   118  		}
   119  
   120  		ipAddr, err := netip.ParseAddr(ip)
   121  		if err != nil {
   122  			return nil, false, err
   123  		}
   124  		return []any{netip.AddrPortFrom(ipAddr, uint16(nodePort))}, true, nil
   125  	}
   126  
   127  	// Otherwise, get the load balancer IP.
   128  	svc, err := cluster.Kube().CoreV1().Services(ns).Get(context.TODO(), svcName, metav1.GetOptions{})
   129  	if err != nil {
   130  		return nil, false, err
   131  	}
   132  
   133  	if len(svc.Status.LoadBalancer.Ingress) == 0 {
   134  		return nil, false, fmt.Errorf("service %s/%s is not available yet: no ingress", svc.Namespace, svc.Name)
   135  	}
   136  	var addrs []any
   137  	for _, ingr := range svc.Status.LoadBalancer.Ingress {
   138  		if ingr.IP == "" && ingr.Hostname == "" {
   139  			return nil, false, fmt.Errorf("service %s/%s is not available yet: no ingress", svc.Namespace, svc.Name)
   140  		}
   141  		if ingr.IP != "" {
   142  			ipaddr, err := netip.ParseAddr(ingr.IP)
   143  			if err != nil {
   144  				return nil, false, err
   145  			}
   146  			addrs = append(addrs, netip.AddrPortFrom(ipaddr, uint16(port)))
   147  		} else {
   148  			addrs = append(addrs, net.JoinHostPort(ingr.Hostname, strconv.Itoa(port)))
   149  		}
   150  	}
   151  	return addrs, true, nil
   152  }
   153  
   154  func removeCRDsSlice(raw []string) string {
   155  	res := make([]string, 0)
   156  	for _, r := range raw {
   157  		res = append(res, removeCRDs(r))
   158  	}
   159  	return yml.JoinString(res...)
   160  }