Skip to content

Commit 5588f1b

Browse files
authored
Merge pull request #1549 from spaceuptech/v0.21.3
v0.21.3
2 parents a892de2 + 81a7871 commit 5588f1b

File tree

7 files changed

+816
-57
lines changed

7 files changed

+816
-57
lines changed

runner/model/routing.go

+29
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ type Route struct {
2323
ID string `json:"id" yaml:"id"`
2424
RequestRetries int32 `json:"requestRetries" yaml:"requestRetries"`
2525
RequestTimeout int64 `json:"requestTimeout" yaml:"requestTimeout"`
26+
Matchers []*Matcher `json:"matchers" yaml:"matchers"`
2627
Source RouteSource `json:"source" yaml:"source"`
2728
Targets []RouteTarget `json:"targets" yaml:"targets"`
2829
}
@@ -49,6 +50,20 @@ func (r *Route) SelectTarget(ctx context.Context, weight int32) (RouteTarget, er
4950
return RouteTarget{}, helpers.Logger.LogError(helpers.GetRequestID(ctx), fmt.Sprintf("No target found for route (%s) - make sure you have defined atleast one target with proper weights", r.Source.URL), nil, nil)
5051
}
5152

53+
// Matcher store the rules, which are used for traffic splitting between service
54+
type Matcher struct {
55+
URL *HTTPMatcher `json:"url,omitempty" yaml:"url,omitempty"`
56+
Headers []*HTTPMatcher `json:"headers,omitempty" yaml:"headers,omitempty"`
57+
}
58+
59+
// HTTPMatcher is matcher type
60+
type HTTPMatcher struct {
61+
Key string `json:"key,omitempty" yaml:"key,omitempty"`
62+
Value string `json:"value,omitempty" yaml:"value,omitempty"`
63+
Type RouteHTTPMatchType `json:"type,omitempty" yaml:"type,omitempty"`
64+
IgnoreCase bool `json:"ignoreCase,omitempty" yaml:"ignoreCase,omitempty"`
65+
}
66+
5267
// RouteSource is the source of routing
5368
type RouteSource struct {
5469
Protocol Protocol `json:"protocol" yaml:"protocol"`
@@ -90,3 +105,17 @@ const (
90105
// RouteTargetExternal is used to route to external services
91106
RouteTargetExternal RouteTargetType = "external"
92107
)
108+
109+
// RouteHTTPMatchType defines http match type
110+
type RouteHTTPMatchType string
111+
112+
const (
113+
// RouteHTTPMatchTypeExact is used for exact match
114+
RouteHTTPMatchTypeExact RouteHTTPMatchType = "exact"
115+
// RouteHTTPMatchTypeRegex is used for regex match
116+
RouteHTTPMatchTypeRegex RouteHTTPMatchType = "regex"
117+
// RouteHTTPMatchTypePrefix is used for prefix match
118+
RouteHTTPMatchTypePrefix RouteHTTPMatchType = "prefix"
119+
// RouteHTTPMatchTypeCheckPresence is used for only checking the presence of header in the http request
120+
RouteHTTPMatchTypeCheckPresence RouteHTTPMatchType = "check-presence"
121+
)

runner/utils/driver/istio/get.go

+57-6
Original file line numberDiff line numberDiff line change
@@ -269,12 +269,63 @@ func (i *Istio) GetServiceRoutes(ctx context.Context, projectID string) (map[str
269269

270270
for _, service := range services.Items {
271271
serviceID := service.Labels["app"]
272-
routes := make(model.Routes, len(service.Spec.Http)+len(service.Spec.Tcp))
272+
routes := make(model.Routes, 0)
273273

274-
for i, route := range service.Spec.Http {
274+
for _, route := range service.Spec.Http {
275275

276276
// Generate the targets
277277
targets := make([]model.RouteTarget, len(route.Route))
278+
matchers := make([]*model.Matcher, 0)
279+
for _, match := range route.Match {
280+
tempMatcher := new(model.Matcher)
281+
282+
if match.Uri != nil && match.Uri.GetMatchType() != nil {
283+
tempMatcher.URL = new(model.HTTPMatcher)
284+
tempMatcher.URL.IgnoreCase = match.IgnoreUriCase
285+
if exact := match.Uri.GetExact(); exact != "" {
286+
tempMatcher.URL.Type = model.RouteHTTPMatchTypeExact
287+
tempMatcher.URL.Value = exact
288+
}
289+
if prefix := match.Uri.GetPrefix(); prefix != "" {
290+
tempMatcher.URL.Type = model.RouteHTTPMatchTypePrefix
291+
tempMatcher.URL.Value = prefix
292+
}
293+
if regex := match.Uri.GetRegex(); regex != "" {
294+
tempMatcher.URL.Type = model.RouteHTTPMatchTypeRegex
295+
tempMatcher.URL.Value = regex
296+
}
297+
}
298+
299+
tempMatcher.Headers = make([]*model.HTTPMatcher, 0)
300+
for headerKey, headerValue := range match.Headers {
301+
tempHeader := new(model.HTTPMatcher)
302+
tempHeader.Key = headerKey
303+
304+
if exact := headerValue.GetExact(); exact != "" {
305+
tempHeader.Type = model.RouteHTTPMatchTypeExact
306+
tempHeader.Value = exact
307+
}
308+
if prefix := headerValue.GetPrefix(); prefix != "" {
309+
tempHeader.Type = model.RouteHTTPMatchTypePrefix
310+
tempHeader.Value = prefix
311+
}
312+
if regex := headerValue.GetRegex(); regex != "" {
313+
tempHeader.Type = model.RouteHTTPMatchTypeRegex
314+
tempHeader.Value = regex
315+
}
316+
317+
if tempHeader.Value == "" || tempHeader.Type == "" {
318+
tempHeader.Type = model.RouteHTTPMatchTypeCheckPresence
319+
}
320+
321+
tempMatcher.Headers = append(tempMatcher.Headers, tempHeader)
322+
}
323+
324+
if len(tempMatcher.Headers) > 0 || tempMatcher.URL != nil {
325+
matchers = append(matchers, tempMatcher)
326+
}
327+
}
328+
278329
for j, destination := range route.Route {
279330
target := model.RouteTarget{Weight: destination.Weight}
280331

@@ -307,10 +358,10 @@ func (i *Istio) GetServiceRoutes(ctx context.Context, projectID string) (map[str
307358
}
308359

309360
// Set the route
310-
routes[i] = &model.Route{ID: serviceID, RequestTimeout: route.Retries.PerTryTimeout.Seconds, RequestRetries: route.Retries.Attempts, Source: model.RouteSource{Port: int32(route.Match[0].Port), Protocol: model.HTTP}, Targets: targets}
361+
routes = append(routes, &model.Route{ID: serviceID, RequestTimeout: route.Retries.PerTryTimeout.Seconds, RequestRetries: route.Retries.Attempts, Source: model.RouteSource{Port: int32(route.Match[0].Port), Protocol: model.HTTP}, Targets: targets, Matchers: matchers})
311362
}
312363

313-
for i, route := range service.Spec.Tcp {
364+
for _, route := range service.Spec.Tcp {
314365

315366
// Generate the targets
316367
targets := make([]model.RouteTarget, len(route.Route))
@@ -344,7 +395,7 @@ func (i *Istio) GetServiceRoutes(ctx context.Context, projectID string) (map[str
344395
}
345396

346397
// Set the route
347-
routes[i] = &model.Route{ID: serviceID, Source: model.RouteSource{Port: int32(route.Match[0].Port), Protocol: model.TCP}, Targets: targets}
398+
routes = append(routes, &model.Route{ID: serviceID, Source: model.RouteSource{Port: int32(route.Match[0].Port), Protocol: model.TCP}, Targets: targets})
348399
}
349400

350401
// Set the routes of a service
@@ -367,7 +418,7 @@ func (i *Istio) GetServiceRole(ctx context.Context, projectID string) ([]*model.
367418
if err != nil {
368419
return nil, helpers.Logger.LogError(helpers.GetRequestID(ctx), fmt.Sprintf("Unable to list cluster roles in project (%s)", projectID), err, nil)
369420
}
370-
serviceRole := make([]*model.Role, len(rolelist.Items)+len(clusterRoleList.Items))
421+
serviceRole := make([]*model.Role, 0)
371422

372423
for _, role := range rolelist.Items {
373424
serviceID := role.Labels["app"]

runner/utils/driver/istio/get_test.go

+97-3
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func TestIstio_GetServiceRoutes(t *testing.T) {
103103
wantErr: false,
104104
},
105105
{
106-
name: "Get HTTP Routes with internal & external targets",
106+
name: "Get HTTP Routes with internal, external targets & traffic splitting",
107107
args: args{
108108
ctx: context.Background(),
109109
projectID: "myProject",
@@ -123,8 +123,48 @@ func TestIstio_GetServiceRoutes(t *testing.T) {
123123
Hosts: []string{getServiceDomainName("myProject", "greeter")},
124124
Http: []*networkingv1alpha3.HTTPRoute{
125125
{
126-
Name: fmt.Sprintf("http-%d", 8080),
127-
Match: []*networkingv1alpha3.HTTPMatchRequest{{Port: uint32(8080), Gateways: []string{"mesh"}}},
126+
Name: fmt.Sprintf("http-%d", 8080),
127+
Match: []*networkingv1alpha3.HTTPMatchRequest{
128+
{
129+
Uri: &networkingv1alpha3.StringMatch{MatchType: &networkingv1alpha3.StringMatch_Exact{Exact: "/v2/"}},
130+
Headers: map[string]*networkingv1alpha3.StringMatch{
131+
"version": {
132+
MatchType: &networkingv1alpha3.StringMatch_Exact{Exact: "v2"},
133+
},
134+
},
135+
Port: uint32(8080),
136+
Gateways: []string{"mesh"},
137+
},
138+
{
139+
Uri: &networkingv1alpha3.StringMatch{MatchType: &networkingv1alpha3.StringMatch_Prefix{Prefix: "/v2/"}},
140+
Headers: map[string]*networkingv1alpha3.StringMatch{
141+
"version": {
142+
MatchType: &networkingv1alpha3.StringMatch_Prefix{Prefix: "v2"},
143+
},
144+
},
145+
Port: uint32(8080),
146+
Gateways: []string{"mesh"},
147+
},
148+
{
149+
Uri: &networkingv1alpha3.StringMatch{MatchType: &networkingv1alpha3.StringMatch_Regex{Regex: "/v2/"}},
150+
Headers: map[string]*networkingv1alpha3.StringMatch{
151+
"version": {
152+
MatchType: &networkingv1alpha3.StringMatch_Regex{Regex: "v2"},
153+
},
154+
},
155+
Port: uint32(8080),
156+
Gateways: []string{"mesh"},
157+
},
158+
{
159+
Uri: &networkingv1alpha3.StringMatch{MatchType: &networkingv1alpha3.StringMatch_Exact{Exact: "/v2/"}},
160+
IgnoreUriCase: true,
161+
Headers: map[string]*networkingv1alpha3.StringMatch{
162+
"version": nil,
163+
},
164+
Port: uint32(8080),
165+
Gateways: []string{"mesh"},
166+
},
167+
},
128168
Retries: &networkingv1alpha3.HTTPRetry{Attempts: 5, PerTryTimeout: &types.Duration{Seconds: model.DefaultRequestTimeout}},
129169
Route: []*networkingv1alpha3.HTTPRouteDestination{
130170
{
@@ -163,6 +203,60 @@ func TestIstio_GetServiceRoutes(t *testing.T) {
163203
ID: "greeter",
164204
RequestRetries: 5,
165205
RequestTimeout: model.DefaultRequestTimeout,
206+
Matchers: []*model.Matcher{
207+
{
208+
URL: &model.HTTPMatcher{
209+
Value: "/v2/",
210+
Type: model.RouteHTTPMatchTypeExact,
211+
},
212+
Headers: []*model.HTTPMatcher{
213+
{
214+
Key: "version",
215+
Value: "v2",
216+
Type: model.RouteHTTPMatchTypeExact,
217+
},
218+
},
219+
},
220+
{
221+
URL: &model.HTTPMatcher{
222+
Value: "/v2/",
223+
Type: model.RouteHTTPMatchTypePrefix,
224+
},
225+
Headers: []*model.HTTPMatcher{
226+
{
227+
Key: "version",
228+
Value: "v2",
229+
Type: model.RouteHTTPMatchTypePrefix,
230+
},
231+
},
232+
},
233+
{
234+
URL: &model.HTTPMatcher{
235+
Value: "/v2/",
236+
Type: model.RouteHTTPMatchTypeRegex,
237+
},
238+
Headers: []*model.HTTPMatcher{
239+
{
240+
Key: "version",
241+
Value: "v2",
242+
Type: model.RouteHTTPMatchTypeRegex,
243+
},
244+
},
245+
},
246+
{
247+
URL: &model.HTTPMatcher{
248+
Value: "/v2/",
249+
Type: model.RouteHTTPMatchTypeExact,
250+
IgnoreCase: true,
251+
},
252+
Headers: []*model.HTTPMatcher{
253+
{
254+
Key: "version",
255+
Type: model.RouteHTTPMatchTypeCheckPresence,
256+
},
257+
},
258+
},
259+
},
166260
Source: model.RouteSource{
167261
Protocol: model.HTTP,
168262
Port: 8080,

runner/utils/driver/istio/helpers.go

+44-1
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,53 @@ func prepareVirtualServiceHTTPRoutes(ctx context.Context, projectID, serviceID s
252252
}
253253
}
254254

255+
matchers := make([]*networkingv1alpha3.HTTPMatchRequest, 0)
256+
for _, matcher := range route.Matchers {
257+
tempMatcher := new(networkingv1alpha3.HTTPMatchRequest)
258+
259+
// Add url matchers
260+
if matcher.URL != nil {
261+
tempMatcher.IgnoreUriCase = matcher.URL.IgnoreCase
262+
tempMatcher.Uri = new(networkingv1alpha3.StringMatch)
263+
switch matcher.URL.Type {
264+
case model.RouteHTTPMatchTypeExact:
265+
tempMatcher.Uri.MatchType = &networkingv1alpha3.StringMatch_Exact{Exact: matcher.URL.Value}
266+
case model.RouteHTTPMatchTypePrefix:
267+
tempMatcher.Uri.MatchType = &networkingv1alpha3.StringMatch_Prefix{Prefix: matcher.URL.Value}
268+
case model.RouteHTTPMatchTypeRegex:
269+
tempMatcher.Uri.MatchType = &networkingv1alpha3.StringMatch_Regex{Regex: matcher.URL.Value}
270+
}
271+
}
272+
273+
// Add header matchers
274+
if len(matcher.Headers) > 0 {
275+
tempMatcher.Headers = map[string]*networkingv1alpha3.StringMatch{}
276+
}
277+
for _, header := range matcher.Headers {
278+
switch header.Type {
279+
case model.RouteHTTPMatchTypeExact:
280+
tempMatcher.Headers[header.Key] = &networkingv1alpha3.StringMatch{MatchType: &networkingv1alpha3.StringMatch_Exact{Exact: header.Value}}
281+
case model.RouteHTTPMatchTypePrefix:
282+
tempMatcher.Headers[header.Key] = &networkingv1alpha3.StringMatch{MatchType: &networkingv1alpha3.StringMatch_Prefix{Prefix: header.Value}}
283+
case model.RouteHTTPMatchTypeRegex:
284+
tempMatcher.Headers[header.Key] = &networkingv1alpha3.StringMatch{MatchType: &networkingv1alpha3.StringMatch_Regex{Regex: header.Value}}
285+
case model.RouteHTTPMatchTypeCheckPresence:
286+
tempMatcher.Headers[header.Key] = &networkingv1alpha3.StringMatch{}
287+
}
288+
}
289+
290+
tempMatcher.Port = uint32(route.Source.Port)
291+
tempMatcher.Gateways = []string{"mesh"}
292+
matchers = append(matchers, tempMatcher)
293+
}
294+
if len(matchers) == 0 {
295+
matchers = append(matchers, &networkingv1alpha3.HTTPMatchRequest{Port: uint32(route.Source.Port), Gateways: []string{"mesh"}})
296+
}
297+
255298
// Add the http route
256299
httpRoutes = append(httpRoutes, &networkingv1alpha3.HTTPRoute{
257300
Name: fmt.Sprintf("http-%d", route.Source.Port),
258-
Match: []*networkingv1alpha3.HTTPMatchRequest{{Port: uint32(route.Source.Port), Gateways: []string{"mesh"}}},
301+
Match: matchers,
259302
Retries: &networkingv1alpha3.HTTPRetry{Attempts: route.RequestRetries, PerTryTimeout: &types.Duration{Seconds: route.RequestTimeout}},
260303
Route: destinations,
261304
})

0 commit comments

Comments
 (0)