Skip to content

Commit e6cbab0

Browse files
committed
initial commit for rewrite-target annotation
1 parent b521986 commit e6cbab0

File tree

6 files changed

+77
-3
lines changed

6 files changed

+77
-3
lines changed

internal/configs/annotations.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ const BasicAuthSecretAnnotation = "nginx.org/basic-auth-secret" // #nosec G101
1818
// PathRegexAnnotation is the annotation where the regex location (path) modifier is specified.
1919
const PathRegexAnnotation = "nginx.org/path-regex"
2020

21+
// RewriteTargetAnnotation is the annotation where the regex-based rewrite target is specified.
22+
const RewriteTargetAnnotation = "nginx.org/rewrite-target"
23+
2124
// SSLCiphersAnnotation is the annotation where SSL ciphers are specified.
2225
const SSLCiphersAnnotation = "nginx.org/ssl-ciphers"
2326

@@ -590,6 +593,26 @@ func getRewrites(ctx context.Context, ingEx *IngressEx) map[string]string {
590593
return nil
591594
}
592595

596+
func getRewriteTarget(ctx context.Context, ingEx *IngressEx) (string, Warnings) {
597+
l := nl.LoggerFromContext(ctx)
598+
warnings := newWarnings()
599+
600+
// Check for mutual exclusivity
601+
if _, hasRewrites := ingEx.Ingress.Annotations["nginx.org/rewrites"]; hasRewrites {
602+
if _, hasRewriteTarget := ingEx.Ingress.Annotations[RewriteTargetAnnotation]; hasRewriteTarget {
603+
warningMsg := "nginx.org/rewrites and nginx.org/rewrite-target annotations are mutually exclusive; nginx.org/rewrites will take precedence"
604+
nl.Errorf(l, "Ingress %s/%s: %s", ingEx.Ingress.Namespace, ingEx.Ingress.Name, warningMsg)
605+
warnings.AddWarning(ingEx.Ingress, warningMsg)
606+
return "", warnings
607+
}
608+
}
609+
610+
if value, exists := ingEx.Ingress.Annotations[RewriteTargetAnnotation]; exists {
611+
return value, warnings
612+
}
613+
return "", warnings
614+
}
615+
593616
func getSSLServices(ingEx *IngressEx) map[string]bool {
594617
if value, exists := ingEx.Ingress.Annotations["nginx.org/ssl-services"]; exists {
595618
return ParseServiceList(value)

internal/configs/ingress.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ func generateNginxCfg(p NginxCfgParams) (version1.IngressNginxConfig, Warnings)
103103
wsServices := getWebsocketServices(p.ingEx)
104104
spServices := getSessionPersistenceServices(p.BaseCfgParams.Context, p.ingEx)
105105
rewrites := getRewrites(p.BaseCfgParams.Context, p.ingEx)
106+
rewriteTarget, rewriteTargetWarnings := getRewriteTarget(p.BaseCfgParams.Context, p.ingEx)
106107
sslServices := getSSLServices(p.ingEx)
107108
grpcServices := getGrpcServices(p.ingEx)
108109

@@ -129,6 +130,7 @@ func generateNginxCfg(p NginxCfgParams) (version1.IngressNginxConfig, Warnings)
129130
}
130131

131132
allWarnings := newWarnings()
133+
allWarnings.Add(rewriteTargetWarnings)
132134

133135
var servers []version1.Server
134136
var limitReqZones []version1.LimitReqZone
@@ -255,7 +257,7 @@ func generateNginxCfg(p NginxCfgParams) (version1.IngressNginxConfig, Warnings)
255257
ssl := isSSLEnabled(sslServices[path.Backend.Service.Name], cfgParams, p.staticParams)
256258
proxySSLName := generateProxySSLName(path.Backend.Service.Name, p.ingEx.Ingress.Namespace)
257259
loc := createLocation(pathOrDefault(path.Path), upstreams[upsName], &cfgParams, wsServices[path.Backend.Service.Name], rewrites[path.Backend.Service.Name],
258-
ssl, grpcServices[path.Backend.Service.Name], proxySSLName, path.PathType, path.Backend.Service.Name)
260+
ssl, grpcServices[path.Backend.Service.Name], proxySSLName, path.PathType, path.Backend.Service.Name, rewriteTarget, path.Path)
259261

260262
if p.isMinion && cfgParams.JWTKey != "" {
261263
jwtAuth, redirectLoc, warnings := generateJWTConfig(p.ingEx.Ingress, p.ingEx.SecretRefs, &cfgParams, getNameForRedirectLocation(p.ingEx.Ingress))
@@ -320,7 +322,7 @@ func generateNginxCfg(p NginxCfgParams) (version1.IngressNginxConfig, Warnings)
320322
pathtype := networking.PathTypePrefix
321323

322324
loc := createLocation(pathOrDefault("/"), upstreams[upsName], &cfgParams, wsServices[p.ingEx.Ingress.Spec.DefaultBackend.Service.Name], rewrites[p.ingEx.Ingress.Spec.DefaultBackend.Service.Name],
323-
ssl, grpcServices[p.ingEx.Ingress.Spec.DefaultBackend.Service.Name], proxySSLName, &pathtype, p.ingEx.Ingress.Spec.DefaultBackend.Service.Name)
325+
ssl, grpcServices[p.ingEx.Ingress.Spec.DefaultBackend.Service.Name], proxySSLName, &pathtype, p.ingEx.Ingress.Spec.DefaultBackend.Service.Name, rewriteTarget, "/")
324326
locations = append(locations, loc)
325327

326328
if cfgParams.HealthCheckEnabled {
@@ -487,9 +489,10 @@ func generateIngressPath(path string, pathType *networking.PathType) string {
487489
return path
488490
}
489491

490-
func createLocation(path string, upstream version1.Upstream, cfg *ConfigParams, websocket bool, rewrite string, ssl bool, grpc bool, proxySSLName string, pathType *networking.PathType, serviceName string) version1.Location {
492+
func createLocation(path string, upstream version1.Upstream, cfg *ConfigParams, websocket bool, rewrite string, ssl bool, grpc bool, proxySSLName string, pathType *networking.PathType, serviceName string, rewriteTarget string, originalPath string) version1.Location {
491493
loc := version1.Location{
492494
Path: generateIngressPath(path, pathType),
495+
OriginalPath: originalPath,
493496
Upstream: upstream,
494497
ProxyConnectTimeout: cfg.ProxyConnectTimeout,
495498
ProxyReadTimeout: cfg.ProxyReadTimeout,
@@ -498,6 +501,7 @@ func createLocation(path string, upstream version1.Upstream, cfg *ConfigParams,
498501
ClientMaxBodySize: cfg.ClientMaxBodySize,
499502
Websocket: websocket,
500503
Rewrite: rewrite,
504+
RewriteTarget: rewriteTarget,
501505
SSL: ssl,
502506
GRPC: grpc,
503507
ProxyBuffering: cfg.ProxyBuffering,

internal/configs/version1/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ type LimitReq struct {
169169
type Location struct {
170170
LocationSnippets []string
171171
Path string
172+
OriginalPath string
172173
Upstream Upstream
173174
ProxyConnectTimeout string
174175
ProxyReadTimeout string
@@ -177,6 +178,7 @@ type Location struct {
177178
ClientMaxBodySize string
178179
Websocket bool
179180
Rewrite string
181+
RewriteTarget string
180182
SSL bool
181183
GRPC bool
182184
ProxyBuffering bool

internal/configs/version1/nginx-plus.ingress.tmpl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,9 @@ server {
202202
set $resource_name "{{$location.MinionIngress.Name}}";
203203
set $resource_namespace "{{$location.MinionIngress.Namespace}}";
204204
{{- end}}
205+
{{- if $location.RewriteTarget}}
206+
rewrite {{ makeRewritePattern $location $.Ingress.Annotations }} {{$location.RewriteTarget}} break;
207+
{{- end}}
205208
{{- if $location.GRPC}}
206209
{{- if not $server.GRPCOnly}}
207210
error_page 400 @grpcerror400;

internal/configs/version1/nginx.ingress.tmpl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ server {
124124
set $resource_name "{{$location.MinionIngress.Name}}";
125125
set $resource_namespace "{{$location.MinionIngress.Namespace}}";
126126
{{- end}}
127+
{{- if $location.RewriteTarget}}
128+
rewrite {{ makeRewritePattern $location $.Ingress.Annotations }} {{$location.RewriteTarget}} break;
129+
{{- end}}
127130
{{- if $location.GRPC}}
128131
{{- if not $server.GRPCOnly}}
129132
error_page 400 @grpcerror400;

internal/configs/version1/template_helper.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,44 @@ func makeResolver(resolverAddresses []string, resolverValid string, resolverIPV6
202202
return builder.String()
203203
}
204204

205+
// makeRewritePattern takes a location and Ingress annotations and returns
206+
// a rewrite pattern that matches the location pattern used.
207+
// This ensures the rewrite regex matches the same requests as the location.
208+
func makeRewritePattern(loc *Location, ingressAnnotations map[string]string) string {
209+
var regexType string
210+
var hasRegex bool
211+
212+
// Check for path-regex annotation (same logic as makeLocationPath)
213+
if loc.MinionIngress != nil {
214+
ingressType, isMergeable := loc.MinionIngress.Annotations["nginx.org/mergeable-ingress-type"]
215+
regexType, hasRegex = loc.MinionIngress.Annotations["nginx.org/path-regex"]
216+
if !(isMergeable && ingressType == "minion" && hasRegex) {
217+
hasRegex = false
218+
}
219+
}
220+
221+
if !hasRegex {
222+
regexType, hasRegex = ingressAnnotations["nginx.org/path-regex"]
223+
}
224+
225+
// If no path-regex annotation, return original path
226+
if !hasRegex {
227+
return loc.OriginalPath
228+
}
229+
230+
// Generate rewrite pattern based on regex type
231+
switch regexType {
232+
case "case_sensitive":
233+
return fmt.Sprintf("^%s", loc.OriginalPath)
234+
case "case_insensitive":
235+
return fmt.Sprintf("(?i)^%s", loc.OriginalPath)
236+
case "exact":
237+
return loc.OriginalPath // exact matches don't need anchors in rewrite
238+
default:
239+
return loc.OriginalPath
240+
}
241+
}
242+
205243
var helperFunctions = template.FuncMap{
206244
"split": split,
207245
"trim": trim,
@@ -212,6 +250,7 @@ var helperFunctions = template.FuncMap{
212250
"toUpper": strings.ToUpper,
213251
"replaceAll": strings.ReplaceAll,
214252
"makeLocationPath": makeLocationPath,
253+
"makeRewritePattern": makeRewritePattern,
215254
"makeSecretPath": commonhelpers.MakeSecretPath,
216255
"makeOnOffFromBool": commonhelpers.MakeOnOffFromBool,
217256
"generateProxySetHeaders": generateProxySetHeaders,

0 commit comments

Comments
 (0)