Skip to content

Commit dace5c3

Browse files
committed
fix: path bug
1 parent d873eba commit dace5c3

3 files changed

Lines changed: 126 additions & 14 deletions

File tree

containers/aggregator-server/registration/namespace.go

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,9 @@ func deployAggregatorResources(namespace string, tokenEndpoint string, accessTok
301301
}
302302

303303
hostMatch := hostWithoutPort(model.ExternalHost)
304+
configBasePaths := configPathVariants("/config/" + namespace)
305+
servicesBasePaths := configPathVariants("/config/" + namespace + "/services")
306+
transformationsBasePaths := configPathVariants("/config/" + namespace + "/transformations")
304307

305308
irName := "aggregator-instance-ingressroute"
306309
obj := &unstructured.Unstructured{
@@ -315,7 +318,7 @@ func deployAggregatorResources(namespace string, tokenEndpoint string, accessTok
315318
"entryPoints": []string{"web"},
316319
"routes": []interface{}{
317320
map[string]interface{}{
318-
"match": "Host(`" + hostMatch + "`) && (Path(`/config/" + namespace + "`) || Path(`/config/" + namespace + "/`)) && Method(`OPTIONS`)",
321+
"match": "Host(`" + hostMatch + "`) && " + exactPathRule(configBasePaths) + " && Method(`OPTIONS`)",
319322
"kind": "Rule",
320323
"services": []interface{}{
321324
map[string]interface{}{
@@ -327,7 +330,7 @@ func deployAggregatorResources(namespace string, tokenEndpoint string, accessTok
327330
"middlewares": buildCorsOnlyMiddlewares(namespace, true),
328331
},
329332
map[string]interface{}{
330-
"match": "Host(`" + hostMatch + "`) && (Path(`/config/" + namespace + "`) || Path(`/config/" + namespace + "/`))",
333+
"match": "Host(`" + hostMatch + "`) && " + exactPathRule(configBasePaths),
331334
"kind": "Rule",
332335
"services": []interface{}{
333336
map[string]interface{}{
@@ -339,7 +342,7 @@ func deployAggregatorResources(namespace string, tokenEndpoint string, accessTok
339342
"middlewares": buildIngressMiddlewares(useUMA, namespace, true),
340343
},
341344
map[string]interface{}{
342-
"match": "Host(`" + hostMatch + "`) && PathPrefix(`/config/" + namespace + "/services`) && Method(`OPTIONS`)",
345+
"match": "Host(`" + hostMatch + "`) && " + prefixPathRule(servicesBasePaths) + " && Method(`OPTIONS`)",
343346
"kind": "Rule",
344347
"services": []interface{}{
345348
map[string]interface{}{
@@ -351,7 +354,7 @@ func deployAggregatorResources(namespace string, tokenEndpoint string, accessTok
351354
"middlewares": buildCorsOnlyMiddlewares(namespace, false),
352355
},
353356
map[string]interface{}{
354-
"match": "Host(`" + hostMatch + "`) && PathPrefix(`/config/" + namespace + "/services`)",
357+
"match": "Host(`" + hostMatch + "`) && " + prefixPathRule(servicesBasePaths),
355358
"kind": "Rule",
356359
"services": []interface{}{
357360
map[string]interface{}{
@@ -363,7 +366,7 @@ func deployAggregatorResources(namespace string, tokenEndpoint string, accessTok
363366
"middlewares": buildIngressMiddlewares(useUMA, namespace, false),
364367
},
365368
map[string]interface{}{
366-
"match": "Host(`" + hostMatch + "`) && PathPrefix(`/config/" + namespace + "/transformations`) && Method(`OPTIONS`)",
369+
"match": "Host(`" + hostMatch + "`) && " + prefixPathRule(transformationsBasePaths) + " && Method(`OPTIONS`)",
367370
"kind": "Rule",
368371
"services": []interface{}{
369372
map[string]interface{}{
@@ -375,7 +378,7 @@ func deployAggregatorResources(namespace string, tokenEndpoint string, accessTok
375378
"middlewares": buildCorsOnlyMiddlewares(namespace, true),
376379
},
377380
map[string]interface{}{
378-
"match": "Host(`" + hostMatch + "`) && PathPrefix(`/config/" + namespace + "/transformations`)",
381+
"match": "Host(`" + hostMatch + "`) && " + prefixPathRule(transformationsBasePaths),
379382
"kind": "Rule",
380383
"services": []interface{}{
381384
map[string]interface{}{
@@ -408,7 +411,7 @@ func deployAggregatorResources(namespace string, tokenEndpoint string, accessTok
408411
},
409412
"spec": map[string]interface{}{
410413
"stripPrefix": map[string]interface{}{
411-
"prefixes": []string{"/config/" + namespace},
414+
"prefixes": configBasePaths,
412415
},
413416
},
414417
},
@@ -565,6 +568,45 @@ func hostWithoutPort(hostport string) string {
565568
return hostport
566569
}
567570

571+
func configPathVariants(path string) []string {
572+
paths := []string{path}
573+
base := strings.TrimSuffix(model.ExternalBasePath, "/")
574+
if base != "" {
575+
paths = append(paths, base+path)
576+
}
577+
return uniqueStrings(paths)
578+
}
579+
580+
func exactPathRule(paths []string) string {
581+
rules := make([]string, 0, len(paths)*2)
582+
for _, p := range paths {
583+
rules = append(rules, "Path(`"+p+"`)")
584+
rules = append(rules, "Path(`"+p+"/`)")
585+
}
586+
return "(" + strings.Join(rules, " || ") + ")"
587+
}
588+
589+
func prefixPathRule(paths []string) string {
590+
rules := make([]string, 0, len(paths))
591+
for _, p := range paths {
592+
rules = append(rules, "PathPrefix(`"+p+"`)")
593+
}
594+
return "(" + strings.Join(rules, " || ") + ")"
595+
}
596+
597+
func uniqueStrings(values []string) []string {
598+
seen := map[string]struct{}{}
599+
out := make([]string, 0, len(values))
600+
for _, value := range values {
601+
if _, ok := seen[value]; ok {
602+
continue
603+
}
604+
seen[value] = struct{}{}
605+
out = append(out, value)
606+
}
607+
return out
608+
}
609+
568610
func buildIngressMiddlewares(useUMA bool, namespace string, includeStrip bool) []interface{} {
569611
middlewares := make([]interface{}, 0, 3)
570612
middlewares = append(middlewares, map[string]interface{}{

containers/aggregator/services/create-service.go

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"context"
77
"fmt"
88
"net"
9+
"regexp"
910
"strings"
1011
"time"
1112

@@ -341,6 +342,8 @@ func createIngressRoute(service *model.Service, owner model.User, ctx context.Co
341342

342343
useUMA := strings.TrimSpace(owner.AuthzServerURL) != ""
343344
hostMatch := hostWithoutPort(model.ExternalHost)
345+
servicePathPrefixes := publicPathVariants("/services/" + namespace + "/" + service.Id)
346+
rewriteMiddlewareName := "rewrite-service-path-" + service.Id
344347
middlewares := []interface{}{
345348
map[string]interface{}{
346349
"name": "cors",
@@ -354,10 +357,38 @@ func createIngressRoute(service *model.Service, owner model.User, ctx context.Co
354357
})
355358
}
356359
middlewares = append(middlewares, map[string]interface{}{
357-
"name": "replace-path",
358-
"namespace": "aggregator-app",
360+
"name": rewriteMiddlewareName,
361+
"namespace": namespace,
359362
})
360363

364+
middlewareGVR := schema.GroupVersionResource{
365+
Group: "traefik.io",
366+
Version: "v1alpha1",
367+
Resource: "middlewares",
368+
}
369+
rewriteMiddlewareObj := &unstructured.Unstructured{
370+
Object: map[string]interface{}{
371+
"apiVersion": "traefik.io/v1alpha1",
372+
"kind": "Middleware",
373+
"metadata": map[string]interface{}{
374+
"name": rewriteMiddlewareName,
375+
"namespace": namespace,
376+
},
377+
"spec": map[string]interface{}{
378+
"replacePathRegex": map[string]interface{}{
379+
"regex": serviceRewriteRegex(servicePathPrefixes),
380+
"replacement": "/$1",
381+
},
382+
},
383+
},
384+
}
385+
if _, err := model.DynamicClient.
386+
Resource(middlewareGVR).
387+
Namespace(namespace).
388+
Create(ctx, rewriteMiddlewareObj, metav1.CreateOptions{}); err != nil && !errors.IsAlreadyExists(err) {
389+
return fmt.Errorf("failed to create service rewrite middleware %s: %w", rewriteMiddlewareName, err)
390+
}
391+
361392
// Define IngressRoute spec
362393
obj := &unstructured.Unstructured{
363394
Object: map[string]interface{}{
@@ -371,7 +402,7 @@ func createIngressRoute(service *model.Service, owner model.User, ctx context.Co
371402
"entryPoints": []string{"web"},
372403
"routes": []interface{}{
373404
map[string]interface{}{
374-
"match": "Host(`" + hostMatch + "`) && PathPrefix(`/services/" + namespace + "/" + service.Id + "`) && Method(`OPTIONS`)",
405+
"match": "Host(`" + hostMatch + "`) && " + servicePrefixRule(servicePathPrefixes) + " && Method(`OPTIONS`)",
375406
"kind": "Rule",
376407
"services": []interface{}{
377408
map[string]interface{}{
@@ -386,13 +417,13 @@ func createIngressRoute(service *model.Service, owner model.User, ctx context.Co
386417
"namespace": namespace,
387418
},
388419
map[string]interface{}{
389-
"name": "replace-path",
390-
"namespace": "aggregator-app",
420+
"name": rewriteMiddlewareName,
421+
"namespace": namespace,
391422
},
392423
},
393424
},
394425
map[string]interface{}{
395-
"match": "Host(`" + hostMatch + "`) && PathPrefix(`/services/" + namespace + "/" + service.Id + "`)",
426+
"match": "Host(`" + hostMatch + "`) && " + servicePrefixRule(servicePathPrefixes),
396427
"kind": "Rule",
397428
"services": []interface{}{
398429
map[string]interface{}{
@@ -445,3 +476,41 @@ func hostWithoutPort(hostport string) string {
445476
}
446477
return hostport
447478
}
479+
480+
func publicPathVariants(path string) []string {
481+
paths := []string{path}
482+
base := strings.TrimSuffix(model.ExternalBasePath, "/")
483+
if base != "" {
484+
paths = append(paths, base+path)
485+
}
486+
return uniquePathStrings(paths)
487+
}
488+
489+
func servicePrefixRule(paths []string) string {
490+
rules := make([]string, 0, len(paths))
491+
for _, p := range paths {
492+
rules = append(rules, "PathPrefix(`"+p+"`)")
493+
}
494+
return "(" + strings.Join(rules, " || ") + ")"
495+
}
496+
497+
func uniquePathStrings(values []string) []string {
498+
seen := map[string]struct{}{}
499+
out := make([]string, 0, len(values))
500+
for _, value := range values {
501+
if _, ok := seen[value]; ok {
502+
continue
503+
}
504+
seen[value] = struct{}{}
505+
out = append(out, value)
506+
}
507+
return out
508+
}
509+
510+
func serviceRewriteRegex(paths []string) string {
511+
escaped := make([]string, 0, len(paths))
512+
for _, path := range paths {
513+
escaped = append(escaped, regexp.QuoteMeta(path))
514+
}
515+
return "^(?:" + strings.Join(escaped, "|") + ")(?:/(.*))?$"
516+
}

integration-test/helpers_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1178,12 +1178,13 @@ func createService(t *testing.T, collectionURL string, authToken string) service
11781178
t.Helper()
11791179

11801180
// Build FnO Turtle description
1181-
source := "http://example.org/source"
11821181
query := "SELECT * WHERE { ?s ?p ?o }"
11831182

11841183
// Extract transformation catalog from aggregator server
11851184
serverDesc := fetchAggregatorServerDescription(t)
11861185
transformationsCatalog := serverDesc["transformation_catalog"].(string)
1186+
// Use an in-cluster/public aggregator URL as source to avoid external-network flakiness.
1187+
source := transformationsCatalog
11871188

11881189
turtleBody := fmt.Sprintf(`@prefix config: <%s> .
11891190
@prefix fno: <https://w3id.org/function/ontology#> .

0 commit comments

Comments
 (0)