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 }