istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/framework/components/istio/istio.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 "net/netip" 19 "regexp" 20 "strconv" 21 "strings" 22 "time" 23 24 "google.golang.org/protobuf/types/known/structpb" 25 corev1 "k8s.io/api/core/v1" 26 "k8s.io/apimachinery/pkg/types" 27 28 meshconfig "istio.io/api/mesh/v1alpha1" 29 "istio.io/istio/pkg/kube/inject" 30 "istio.io/istio/pkg/test" 31 "istio.io/istio/pkg/test/framework/components/cluster" 32 "istio.io/istio/pkg/test/framework/components/istio/ingress" 33 "istio.io/istio/pkg/test/framework/resource" 34 "istio.io/istio/pkg/test/framework/resource/config/cleanup" 35 "istio.io/istio/pkg/test/scopes" 36 ) 37 38 // OperatorValues is the map of the values from the installed operator yaml. 39 type OperatorValues map[string]*structpb.Value 40 41 // This regular expression matches list object index selection expression such as 42 // abc[100], Tba_a[0]. 43 var listObjRex = regexp.MustCompile(`^([a-zA-Z]?[a-z_A-Z\d]*)\[([ ]*[\d]+)[ ]*\]$`) 44 45 func getConfigValue(path []string, val map[string]*structpb.Value) *structpb.Value { 46 retVal := structpb.NewNullValue() 47 if len(path) > 0 { 48 match := listObjRex.FindStringSubmatch(path[0]) 49 // valid list index 50 switch len(match) { 51 case 0: // does not match list object selection, should be name of a field, should be struct value 52 thisVal := val[path[0]] 53 // If it is a struct and looking for more down the path 54 if thisVal.GetStructValue() != nil && len(path) > 1 { 55 return getConfigValue(path[1:], thisVal.GetStructValue().Fields) 56 } 57 retVal = thisVal 58 case 3: // match something like aaa[100] 59 thisVal := val[match[1]] 60 // If it is a list and looking for more down the path 61 if thisVal.GetListValue() != nil && len(path) > 1 { 62 index, _ := strconv.Atoi(match[2]) 63 return getConfigValue(path[1:], thisVal.GetListValue().Values[index].GetStructValue().Fields) 64 } 65 retVal = thisVal 66 } 67 } 68 return retVal 69 } 70 71 // GetConfigValue returns a structpb value from a structpb map by 72 // using a dotted path such as `pilot.env.LOCAL_CLUSTER_SECRET_WATCHER`. 73 func (v OperatorValues) GetConfigValue(path string) *structpb.Value { 74 return getConfigValue(strings.Split(path, "."), v) 75 } 76 77 // Instance represents a deployed Istio instance 78 type Instance interface { 79 resource.Resource 80 81 Settings() Config 82 // Ingresses returns all ingresses for "istio-ingressgateway" in each cluster. 83 Ingresses() ingress.Instances 84 // IngressFor returns an ingress used for reaching workloads in the given cluster. 85 // The ingress's service name will be "istio-ingressgateway" and the istio label will be "ingressgateway". 86 IngressFor(cluster cluster.Cluster) ingress.Instance 87 // EastWestGatewayFor returns an ingress used for east-west traffic and accessing the control plane 88 // from outside of the cluster. 89 EastWestGatewayFor(cluster cluster.Cluster) ingress.Instance 90 // CustomIngressFor returns an ingress with a specific service name and "istio" label used for reaching workloads 91 // in the given cluster. 92 CustomIngressFor(cluster cluster.Cluster, service types.NamespacedName, istioLabel string) ingress.Instance 93 94 // RemoteDiscoveryAddressFor returns the external address of the discovery server that controls 95 // the given cluster. This allows access to the discovery server from 96 // outside its cluster. 97 RemoteDiscoveryAddressFor(cluster cluster.Cluster) (netip.AddrPort, error) 98 // CreateRemoteSecret on the cluster with the given options. 99 CreateRemoteSecret(ctx resource.Context, c cluster.Cluster, opts ...string) (string, error) 100 // InternalDiscoveryAddressFor returns an internal (port-forwarded) address for an Istiod instance in the 101 // cluster. 102 InternalDiscoveryAddressFor(cluster cluster.Cluster) (string, error) 103 104 // Return POD IPs for the pod with the specified label in the specified namespace 105 PodIPsFor(cluster cluster.Cluster, namespace string, label string) ([]corev1.PodIP, error) 106 107 // Values returns the operator values for the installed control plane. 108 Values() (OperatorValues, error) 109 ValuesOrFail(test.Failer) OperatorValues 110 // MeshConfig used by the Istio installation. 111 MeshConfig() (*meshconfig.MeshConfig, error) 112 MeshConfigOrFail(test.Failer) *meshconfig.MeshConfig 113 // UpdateMeshConfig used by the Istio installation. 114 UpdateMeshConfig(resource.Context, func(*meshconfig.MeshConfig) error, cleanup.Strategy) error 115 UpdateMeshConfigOrFail(resource.Context, test.Failer, func(*meshconfig.MeshConfig) error, cleanup.Strategy) 116 // PatchMeshConfig with the given patch yaml. 117 PatchMeshConfig(resource.Context, string) error 118 PatchMeshConfigOrFail(resource.Context, test.Failer, string) 119 UpdateInjectionConfig(resource.Context, func(*inject.Config) error, cleanup.Strategy) error 120 InjectionConfig() (*inject.Config, error) 121 } 122 123 // SetupConfigFn is a setup function that specifies the overrides of the configuration to deploy Istio. 124 type SetupConfigFn func(ctx resource.Context, cfg *Config) 125 126 // SetupContextFn is a setup function that uses Context for configuration. 127 type SetupContextFn func(ctx resource.Context) error 128 129 // Get returns the Istio component from the context. If there is none an error is returned. 130 func Get(ctx resource.Context) (Instance, error) { 131 var i Instance 132 if err := ctx.GetResource(&i); err != nil { 133 return nil, err 134 } 135 return i, nil 136 } 137 138 // GetOrFail returns the Istio component from the context. If there is none the test is failed. 139 func GetOrFail(t test.Failer, ctx resource.Context) Instance { 140 t.Helper() 141 i, err := Get(ctx) 142 if err != nil { 143 t.Fatal(err) 144 } 145 return i 146 } 147 148 // DefaultIngress returns the ingress installed in the default cluster. The ingress's service name 149 // will be "istio-ingressgateway" and the istio label will be "ingressgateway". 150 func DefaultIngress(ctx resource.Context) (ingress.Instance, error) { 151 i, err := Get(ctx) 152 if err != nil { 153 return nil, err 154 } 155 return i.IngressFor(ctx.Clusters().Default()), nil 156 } 157 158 // DefaultIngressOrFail calls DefaultIngress and fails if an error is encountered. 159 func DefaultIngressOrFail(t test.Failer, ctx resource.Context) ingress.Instance { 160 t.Helper() 161 i, err := DefaultIngress(ctx) 162 if err != nil { 163 t.Fatal(err) 164 } 165 return i 166 } 167 168 // Ingresses returns all ingresses for "istio-ingressgateway" in each cluster. 169 func Ingresses(ctx resource.Context) (ingress.Instances, error) { 170 i, err := Get(ctx) 171 if err != nil { 172 return nil, err 173 } 174 return i.Ingresses(), nil 175 } 176 177 // IngressesOrFail calls Ingresses and fails if an error is encountered. 178 func IngressesOrFail(t test.Failer, ctx resource.Context) ingress.Instances { 179 t.Helper() 180 i, err := Ingresses(ctx) 181 if err != nil { 182 t.Fatal(err) 183 } 184 return i 185 } 186 187 // Setup is a setup function that will deploy Istio on Kubernetes environment 188 func Setup(i *Instance, cfn SetupConfigFn, ctxFns ...SetupContextFn) resource.SetupFn { 189 return func(ctx resource.Context) error { 190 cfg, err := DefaultConfig(ctx) 191 if err != nil { 192 return err 193 } 194 if cfn != nil { 195 cfn(ctx, &cfg) 196 } 197 for _, ctxFn := range ctxFns { 198 if ctxFn != nil { 199 err := ctxFn(ctx) 200 if err != nil { 201 scopes.Framework.Infof("=== FAILED: context setup function [err=%v] ===", err) 202 return err 203 } 204 scopes.Framework.Info("=== SUCCESS: context setup function ===") 205 } 206 } 207 208 t0 := time.Now() 209 scopes.Framework.Infof("=== BEGIN: Deploy Istio [Suite=%s] ===", ctx.Settings().TestID) 210 211 ins, err := newKube(ctx, cfg) 212 if err != nil { 213 scopes.Framework.Infof("=== FAILED: Deploy Istio in %v [Suite=%s] ===", time.Since(t0), ctx.Settings().TestID) 214 return err 215 } 216 217 if i != nil { 218 *i = ins 219 } 220 scopes.Framework.Infof("=== SUCCEEDED: Deploy Istio in %v [Suite=%s]===", time.Since(t0), ctx.Settings().TestID) 221 return nil 222 } 223 }