istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/framework/components/ambient/waypoint.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 ambient 16 17 import ( 18 "errors" 19 "fmt" 20 "io" 21 "time" 22 23 v1 "k8s.io/api/core/v1" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 26 "istio.io/istio/pkg/config/constants" 27 istioKube "istio.io/istio/pkg/kube" 28 "istio.io/istio/pkg/maps" 29 "istio.io/istio/pkg/test/framework" 30 "istio.io/istio/pkg/test/framework/components/crd" 31 "istio.io/istio/pkg/test/framework/components/istioctl" 32 "istio.io/istio/pkg/test/framework/components/namespace" 33 "istio.io/istio/pkg/test/framework/resource" 34 testKube "istio.io/istio/pkg/test/kube" 35 "istio.io/istio/pkg/test/scopes" 36 "istio.io/istio/pkg/test/util/retry" 37 ) 38 39 var _ io.Closer = &kubeComponent{} 40 41 type kubeComponent struct { 42 id resource.ID 43 44 ns namespace.Instance 45 inbound istioKube.PortForwarder 46 outbound istioKube.PortForwarder 47 pod v1.Pod 48 } 49 50 func (k kubeComponent) Namespace() namespace.Instance { 51 return k.ns 52 } 53 54 func (k kubeComponent) PodIP() string { 55 return k.pod.Status.PodIP 56 } 57 58 func (k kubeComponent) Inbound() string { 59 return k.inbound.Address() 60 } 61 62 func (k kubeComponent) Outbound() string { 63 return k.outbound.Address() 64 } 65 66 func (k kubeComponent) ID() resource.ID { 67 return k.id 68 } 69 70 func (k kubeComponent) Close() error { 71 if k.inbound != nil { 72 k.inbound.Close() 73 } 74 if k.outbound != nil { 75 k.outbound.Close() 76 } 77 return nil 78 } 79 80 // WaypointProxy describes a waypoint proxy deployment 81 type WaypointProxy interface { 82 Namespace() namespace.Instance 83 Inbound() string 84 Outbound() string 85 PodIP() string 86 } 87 88 // NewWaypointProxy creates a new WaypointProxy. 89 func NewWaypointProxy(ctx resource.Context, ns namespace.Instance, name string) (WaypointProxy, error) { 90 server := &kubeComponent{ 91 ns: ns, 92 } 93 server.id = ctx.TrackResource(server) 94 if err := crd.DeployGatewayAPI(ctx); err != nil { 95 return nil, err 96 } 97 98 // TODO support multicluster 99 ik, err := istioctl.New(ctx, istioctl.Config{}) 100 if err != nil { 101 return nil, err 102 } 103 // TODO: detect from UseWaypointProxy in echo.Config 104 _, _, err = ik.Invoke([]string{ 105 "x", 106 "waypoint", 107 "apply", 108 "--namespace", 109 ns.Name(), 110 "--name", 111 name, 112 "--for", 113 constants.AllTraffic, 114 }) 115 if err != nil { 116 return nil, err 117 } 118 119 cls := ctx.Clusters().Kube().Default() 120 // Find the Waypoint pod and service, and start forwarding a local port. 121 fetchFn := testKube.NewSinglePodFetch(cls, ns.Name(), fmt.Sprintf("%s=%s", constants.GatewayNameLabel, name)) 122 pods, err := testKube.WaitUntilPodsAreReady(fetchFn) 123 if err != nil { 124 return nil, err 125 } 126 pod := pods[0] 127 inbound, err := cls.NewPortForwarder(pod.Name, pod.Namespace, "", 0, 15008) 128 if err != nil { 129 return nil, err 130 } 131 132 if err := inbound.Start(); err != nil { 133 return nil, err 134 } 135 outbound, err := cls.NewPortForwarder(pod.Name, pod.Namespace, "", 0, 15001) 136 if err != nil { 137 return nil, err 138 } 139 140 if err := outbound.Start(); err != nil { 141 return nil, err 142 } 143 server.inbound = inbound 144 server.outbound = outbound 145 server.pod = pod 146 return server, nil 147 } 148 149 // NewWaypointProxyOrFail calls NewWaypointProxy and fails if an error occurs. 150 func NewWaypointProxyOrFail(t framework.TestContext, ns namespace.Instance, name string) WaypointProxy { 151 t.Helper() 152 s, err := NewWaypointProxy(t, ns, name) 153 if err != nil { 154 t.Fatal(err) 155 } 156 return s 157 } 158 159 func SetWaypointForService(t framework.TestContext, ns namespace.Instance, service, waypoint string) { 160 if service == "" { 161 return 162 } 163 164 cs := t.AllClusters().Kube() 165 for _, c := range cs { 166 oldSvc, err := c.Kube().CoreV1().Services(ns.Name()).Get(t.Context(), service, metav1.GetOptions{}) 167 if err != nil { 168 t.Fatalf("error getting svc %s, err %v", service, err) 169 } 170 oldLabels := oldSvc.ObjectMeta.GetLabels() 171 if oldLabels == nil { 172 oldLabels = make(map[string]string, 1) 173 } 174 newLabels := maps.Clone(oldLabels) 175 if waypoint != "" { 176 newLabels[constants.AmbientUseWaypointLabel] = waypoint 177 } else { 178 delete(newLabels, constants.AmbientUseWaypointLabel) 179 } 180 181 doLabel := func(labels map[string]string) error { 182 // update needs the latest version 183 svc, err := c.Kube().CoreV1().Services(ns.Name()).Get(t.Context(), service, metav1.GetOptions{}) 184 if err != nil { 185 return err 186 } 187 svc.ObjectMeta.SetLabels(labels) 188 _, err = c.Kube().CoreV1().Services(ns.Name()).Update(t.Context(), svc, metav1.UpdateOptions{}) 189 return err 190 } 191 192 if err = doLabel(newLabels); err != nil { 193 t.Fatalf("error updating svc %s, err %v", service, err) 194 } 195 t.Cleanup(func() { 196 if err := doLabel(oldLabels); err != nil { 197 scopes.Framework.Errorf("failed resetting waypoint for %s/%s; this will likely break other tests", ns.Name(), service) 198 } 199 }) 200 201 } 202 } 203 204 func DeleteWaypoint(t framework.TestContext, ns namespace.Instance, waypoint string) { 205 istioctl.NewOrFail(t, t, istioctl.Config{}).InvokeOrFail(t, []string{ 206 "x", 207 "waypoint", 208 "delete", 209 "--namespace", 210 ns.Name(), 211 waypoint, 212 }) 213 waypointError := retry.UntilSuccess(func() error { 214 fetch := testKube.NewPodFetch(t.AllClusters()[0], ns.Name(), constants.GatewayNameLabel+"="+waypoint) 215 pods, err := testKube.CheckPodsAreReady(fetch) 216 if err != nil && !errors.Is(err, testKube.ErrNoPodsFetched) { 217 return fmt.Errorf("cannot fetch pod: %v", err) 218 } else if len(pods) != 0 { 219 return fmt.Errorf("waypoint pod is not deleted") 220 } 221 return nil 222 }, retry.Timeout(time.Minute), retry.BackoffDelay(time.Millisecond*100)) 223 if waypointError != nil { 224 t.Fatal(waypointError) 225 } 226 } 227 228 func RemoveWaypointFromService(t framework.TestContext, ns namespace.Instance, service, waypoint string) { 229 if service != "" { 230 cs := t.AllClusters().Configs() 231 for _, c := range cs { 232 oldSvc, err := c.Kube().CoreV1().Services(ns.Name()).Get(t.Context(), service, metav1.GetOptions{}) 233 if err != nil { 234 t.Fatalf("error getting svc %s, err %v", service, err) 235 } 236 labels := oldSvc.ObjectMeta.GetLabels() 237 if labels != nil { 238 delete(labels, constants.AmbientUseWaypointLabel) 239 oldSvc.ObjectMeta.SetLabels(labels) 240 } 241 _, err = c.Kube().CoreV1().Services(ns.Name()).Update(t.Context(), oldSvc, metav1.UpdateOptions{}) 242 if err != nil { 243 t.Fatalf("error updating svc %s, err %v", service, err) 244 } 245 } 246 } 247 }