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