Skip to content

Commit 45d073f

Browse files
committed
update based on reviews
1 parent 06f2968 commit 45d073f

File tree

14 files changed

+244
-195
lines changed

14 files changed

+244
-195
lines changed

internal/mode/static/nginx/config/generator.go

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package config
22

33
import (
4-
"encoding/json"
5-
"fmt"
64
"path/filepath"
75

86
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/file"
@@ -57,8 +55,13 @@ func NewGeneratorImpl(plus bool) GeneratorImpl {
5755
return GeneratorImpl{plus: plus}
5856
}
5957

58+
type executeResult struct {
59+
dest string
60+
data []byte
61+
}
62+
6063
// executeFunc is a function that generates NGINX configuration from internal representation.
61-
type executeFunc func(configuration dataplane.Configuration) []byte
64+
type executeFunc func(configuration dataplane.Configuration) []executeResult
6265

6366
// Generate generates NGINX configuration files from internal representation.
6467
// It is the responsibility of the caller to validate the configuration before calling this function.
@@ -113,40 +116,36 @@ func generateCertBundleFileName(id dataplane.CertBundleID) string {
113116
}
114117

115118
func (g GeneratorImpl) generateHTTPConfig(conf dataplane.Configuration) []file.File {
119+
files := make([]file.File, 0)
116120
var c []byte
121+
117122
for _, execute := range g.getExecuteFuncs() {
118-
c = append(c, execute(conf)...)
123+
results := execute(conf)
124+
for _, res := range results {
125+
if res.dest == httpConfigFile {
126+
c = append(c, res.data...)
127+
} else {
128+
files = append(files, file.File{
129+
Path: res.dest,
130+
Content: res.data,
131+
Type: file.TypeRegular,
132+
})
133+
}
134+
}
119135
}
120136

121-
servers, httpMatchPairs := executeServers(conf)
122-
123-
// create server conf
124-
serverConf := execute(serversTemplate, servers)
125-
c = append(c, serverConf...)
126-
127-
httpConf := file.File{
128-
Content: c,
137+
files = append(files, file.File{
129138
Path: httpConfigFile,
139+
Content: c,
130140
Type: file.TypeRegular,
131-
}
132-
133-
// create httpMatchPair conf
134-
b, err := json.Marshal(httpMatchPairs)
135-
if err != nil {
136-
// panic is safe here because we should never fail to marshal the match unless we constructed it incorrectly.
137-
panic(fmt.Errorf("could not marshal http match pairs: %w", err))
138-
}
139-
matchConf := file.File{
140-
Content: b,
141-
Path: httpMatchVarsFile,
142-
Type: file.TypeRegular,
143-
}
141+
})
144142

145-
return []file.File{httpConf, matchConf}
143+
return files
146144
}
147145

148146
func (g GeneratorImpl) getExecuteFuncs() []executeFunc {
149147
return []executeFunc{
148+
executeServers,
150149
g.executeUpstreams,
151150
executeSplitClients,
152151
executeMaps,

internal/mode/static/nginx/config/generator_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,19 +78,19 @@ func TestGenerate(t *testing.T) {
7878
Content: []byte("test-cert\ntest-key"),
7979
}))
8080

81+
g.Expect(files[1].Path).To(Equal("/etc/nginx/conf.d/match.json"))
8182
g.Expect(files[1].Type).To(Equal(file.TypeRegular))
82-
g.Expect(files[1].Path).To(Equal("/etc/nginx/conf.d/http.conf"))
83-
httpCfg := string(files[1].Content) // converting to string so that on failure gomega prints strings not byte arrays
83+
84+
g.Expect(files[2].Type).To(Equal(file.TypeRegular))
85+
g.Expect(files[2].Path).To(Equal("/etc/nginx/conf.d/http.conf"))
86+
httpCfg := string(files[2].Content) // converting to string so that on failure gomega prints strings not byte arrays
8487
// Note: this only verifies that Generate() returns a byte array with upstream, server, and split_client blocks.
8588
// It does not test the correctness of those blocks. That functionality is covered by other tests in this package.
8689
g.Expect(httpCfg).To(ContainSubstring("listen 80"))
8790
g.Expect(httpCfg).To(ContainSubstring("listen 443"))
8891
g.Expect(httpCfg).To(ContainSubstring("upstream"))
8992
g.Expect(httpCfg).To(ContainSubstring("split_clients"))
9093

91-
g.Expect(files[2].Path).To(Equal("/etc/nginx/conf.d/match.json"))
92-
g.Expect(files[2].Type).To(Equal(file.TypeRegular))
93-
9494
g.Expect(files[3].Type).To(Equal(file.TypeRegular))
9595
g.Expect(files[3].Path).To(Equal("/etc/nginx/conf.d/config-version.conf"))
9696
configVersion := string(files[3].Content)

internal/mode/static/nginx/config/http/config.go

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -93,21 +93,3 @@ type ProxySSLVerify struct {
9393
TrustedCertificate string
9494
Name string
9595
}
96-
97-
// httpMatch is an internal representation of an HTTPRouteMatch.
98-
// This struct is marshaled into a string and stored as a variable in the nginx location block for the route's path.
99-
// The NJS httpmatches module will look up this variable on the request object and compare the request against the
100-
// Method, Headers, and QueryParams contained in httpMatch.
101-
// If the request satisfies the httpMatch, NGINX will redirect the request to the location RedirectPath.
102-
type RouteMatch struct {
103-
// Method is the HTTPMethod of the HTTPRouteMatch.
104-
Method string `json:"method,omitempty"`
105-
// RedirectPath is the path to redirect the request to if the request satisfies the match conditions.
106-
RedirectPath string `json:"redirectPath,omitempty"`
107-
// Headers is a list of HTTPHeaders name value pairs with the format "{name}:{value}".
108-
Headers []string `json:"headers,omitempty"`
109-
// QueryParams is a list of HTTPQueryParams name value pairs with the format "{name}={value}".
110-
QueryParams []string `json:"params,omitempty"`
111-
// Any represents a match with no match conditions.
112-
Any bool `json:"any,omitempty"`
113-
}

internal/mode/static/nginx/config/maps.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@ import (
1010

1111
var mapsTemplate = gotemplate.Must(gotemplate.New("maps").Parse(mapsTemplateText))
1212

13-
func executeMaps(conf dataplane.Configuration) []byte {
13+
func executeMaps(conf dataplane.Configuration) []executeResult {
1414
maps := buildAddHeaderMaps(append(conf.HTTPServers, conf.SSLServers...))
15-
return execute(mapsTemplate, maps)
15+
result := executeResult{
16+
dest: httpConfigFile,
17+
data: execute(mapsTemplate, maps),
18+
}
19+
return []executeResult{result}
1620
}
1721

1822
func buildAddHeaderMaps(servers []dataplane.VirtualServer) []http.Map {

internal/mode/static/nginx/config/maps_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ func TestExecuteMaps(t *testing.T) {
8888
"map $http_upgrade $connection_upgrade {": 1,
8989
}
9090

91-
maps := string(executeMaps(conf))
91+
mapResult := executeMaps(conf)
92+
maps := string(mapResult[0].data)
9293
for expSubStr, expCount := range expSubStrings {
9394
g.Expect(expCount).To(Equal(strings.Count(maps, expSubStr)))
9495
}

internal/mode/static/nginx/config/servers.go

Lines changed: 64 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package config
22

33
import (
4+
"encoding/json"
45
"fmt"
5-
"regexp"
66
"strconv"
77
"strings"
88
gotemplate "text/template"
@@ -39,30 +39,44 @@ var baseHeaders = []http.Header{
3939
},
4040
}
4141

42-
func executeServers(conf dataplane.Configuration) ([]http.Server, map[string][]http.RouteMatch) {
42+
func executeServers(conf dataplane.Configuration) []executeResult {
4343
servers, httpMatchPairs := createServers(conf.HTTPServers, conf.SSLServers)
4444

45-
return servers, httpMatchPairs
45+
serverResult := executeResult{
46+
dest: httpConfigFile,
47+
data: execute(serversTemplate, servers),
48+
}
49+
50+
// create httpMatchPair conf
51+
httpMatchConf, err := json.Marshal(httpMatchPairs)
52+
if err != nil {
53+
// panic is safe here because we should never fail to marshal the match unless we constructed it incorrectly.
54+
panic(fmt.Errorf("could not marshal http match pairs: %w", err))
55+
}
56+
57+
httpMatchResult := executeResult{
58+
dest: httpMatchVarsFile,
59+
data: httpMatchConf,
60+
}
61+
62+
return []executeResult{serverResult, httpMatchResult}
4663
}
4764

48-
func createServers(httpServers, sslServers []dataplane.VirtualServer) (
49-
[]http.Server,
50-
map[string][]http.RouteMatch,
51-
) {
65+
func createServers(httpServers, sslServers []dataplane.VirtualServer) ([]http.Server, httpMatchPairs) {
5266
servers := make([]http.Server, 0, len(httpServers)+len(sslServers))
53-
finalMatchPairs := make(map[string][]http.RouteMatch)
67+
finalMatchPairs := make(httpMatchPairs)
5468

55-
for _, s := range httpServers {
56-
httpServer, matchPair := createServer(s)
69+
for serverID, s := range httpServers {
70+
httpServer, matchPair := createServer(s, serverID)
5771
servers = append(servers, httpServer)
5872

5973
for key, val := range matchPair {
6074
finalMatchPairs[key] = val
6175
}
6276
}
6377

64-
for _, s := range sslServers {
65-
sslServer, matchPair := createSSLServer(s)
78+
for serverID, s := range sslServers {
79+
sslServer, matchPair := createSSLServer(s, serverID)
6680
servers = append(servers, sslServer)
6781

6882
for key, val := range matchPair {
@@ -73,18 +87,15 @@ func createServers(httpServers, sslServers []dataplane.VirtualServer) (
7387
return servers, finalMatchPairs
7488
}
7589

76-
func createSSLServer(virtualServer dataplane.VirtualServer) (
77-
http.Server,
78-
map[string][]http.RouteMatch,
79-
) {
90+
func createSSLServer(virtualServer dataplane.VirtualServer, serverID int) (http.Server, httpMatchPairs) {
8091
if virtualServer.IsDefault {
8192
return http.Server{
8293
IsDefaultSSL: true,
8394
Port: virtualServer.Port,
8495
}, nil
8596
}
8697

87-
locs, matchPairs := createLocations(virtualServer)
98+
locs, matchPairs := createLocations(&virtualServer, serverID)
8899

89100
return http.Server{
90101
ServerName: virtualServer.Hostname,
@@ -97,18 +108,15 @@ func createSSLServer(virtualServer dataplane.VirtualServer) (
97108
}, matchPairs
98109
}
99110

100-
func createServer(virtualServer dataplane.VirtualServer) (
101-
http.Server,
102-
map[string][]http.RouteMatch,
103-
) {
111+
func createServer(virtualServer dataplane.VirtualServer, serverID int) (http.Server, httpMatchPairs) {
104112
if virtualServer.IsDefault {
105113
return http.Server{
106114
IsDefaultHTTP: true,
107115
Port: virtualServer.Port,
108116
}, nil
109117
}
110118

111-
locs, matchPairs := createLocations(virtualServer)
119+
locs, matchPairs := createLocations(&virtualServer, serverID)
112120

113121
return http.Server{
114122
ServerName: virtualServer.Hostname,
@@ -124,19 +132,16 @@ type rewriteConfig struct {
124132
Rewrite string
125133
}
126134

127-
type httpMatchPairs map[string][]http.RouteMatch
135+
type httpMatchPairs map[string][]RouteMatch
128136

129-
func createLocations(server dataplane.VirtualServer) (
130-
[]http.Location,
131-
map[string][]http.RouteMatch,
132-
) {
137+
func createLocations(server *dataplane.VirtualServer, serverID int) ([]http.Location, httpMatchPairs) {
133138
maxLocs, pathsAndTypes := getMaxLocationCountAndPathMap(server.PathRules)
134139
locs := make([]http.Location, 0, maxLocs)
135140
matchPairs := make(httpMatchPairs)
136141
var rootPathExists bool
137142

138143
for pathRuleIdx, rule := range server.PathRules {
139-
matches := make([]http.RouteMatch, 0, len(rule.MatchRules))
144+
matches := make([]RouteMatch, 0, len(rule.MatchRules))
140145

141146
if rule.Path == rootPath {
142147
rootPathExists = true
@@ -157,9 +162,16 @@ func createLocations(server dataplane.VirtualServer) (
157162
}
158163

159164
if len(matches) > 0 {
160-
for i := range extLocations {
161-
key := server.Hostname + extLocations[i].Path + strconv.Itoa(int(server.Port))
162-
extLocations[i].HTTPMatchKey = sanitizeKey(key)
165+
for i, loc := range extLocations {
166+
var key string
167+
if server.SSL != nil {
168+
key += "SSL"
169+
}
170+
key += strconv.Itoa(serverID) + "_" + strconv.Itoa(pathRuleIdx)
171+
if strings.Contains(loc.Path, "= /") {
172+
key += "EXACT"
173+
}
174+
extLocations[i].HTTPMatchKey = key
163175
matchPairs[extLocations[i].HTTPMatchKey] = matches
164176
}
165177
locs = append(locs, extLocations...)
@@ -173,13 +185,6 @@ func createLocations(server dataplane.VirtualServer) (
173185
return locs, matchPairs
174186
}
175187

176-
// removeSpecialCharacters removes '/', '.' from key and replaces '= ' with 'EXACT',
177-
// to avoid compilation issues with NJS and NGINX Conf.
178-
func sanitizeKey(input string) string {
179-
s := regexp.MustCompile("[./]").ReplaceAllString(input, "")
180-
return regexp.MustCompile("= ").ReplaceAllString(s, "EXACT")
181-
}
182-
183188
// pathAndTypeMap contains a map of paths and any path types defined for that path
184189
// for example, {/foo: {exact: {}, prefix: {}}}
185190
type pathAndTypeMap map[string]map[dataplane.PathType]struct{}
@@ -256,7 +261,7 @@ func initializeInternalLocation(
256261
pathruleIdx,
257262
matchRuleIdx int,
258263
match dataplane.Match,
259-
) (http.Location, http.RouteMatch) {
264+
) (http.Location, RouteMatch) {
260265
path := fmt.Sprintf("@rule%d-route%d", pathruleIdx, matchRuleIdx)
261266
return createMatchLocation(path), createRouteMatch(match, path)
262267
}
@@ -431,8 +436,26 @@ func createRewritesValForRewriteFilter(filter *dataplane.HTTPURLRewriteFilter, p
431436
return rewrites
432437
}
433438

434-
func createRouteMatch(match dataplane.Match, redirectPath string) http.RouteMatch {
435-
hm := http.RouteMatch{
439+
// httpMatch is an internal representation of an HTTPRouteMatch.
440+
// This struct is marshaled into a string and stored as a variable in the nginx location block for the route's path.
441+
// The NJS httpmatches module will look up this variable on the request object and compare the request against the
442+
// Method, Headers, and QueryParams contained in httpMatch.
443+
// If the request satisfies the httpMatch, NGINX will redirect the request to the location RedirectPath.
444+
type RouteMatch struct {
445+
// Method is the HTTPMethod of the HTTPRouteMatch.
446+
Method string `json:"method,omitempty"`
447+
// RedirectPath is the path to redirect the request to if the request satisfies the match conditions.
448+
RedirectPath string `json:"redirectPath,omitempty"`
449+
// Headers is a list of HTTPHeaders name value pairs with the format "{name}:{value}".
450+
Headers []string `json:"headers,omitempty"`
451+
// QueryParams is a list of HTTPQueryParams name value pairs with the format "{name}={value}".
452+
QueryParams []string `json:"params,omitempty"`
453+
// Any represents a match with no match conditions.
454+
Any bool `json:"any,omitempty"`
455+
}
456+
457+
func createRouteMatch(match dataplane.Match, redirectPath string) RouteMatch {
458+
hm := RouteMatch{
436459
RedirectPath: redirectPath,
437460
}
438461

internal/mode/static/nginx/config/servers_template.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package config
22

33
var serversTemplateText = `
4+
js_preload_object matches from /etc/nginx/conf.d/match.json;
45
{{- range $s := . -}}
56
{{ if $s.IsDefaultSSL -}}
67
server {
@@ -17,7 +18,6 @@ server {
1718
}
1819
{{- else }}
1920
server {
20-
js_preload_object matches from /etc/nginx/conf.d/match.json;
2121
{{- if $s.SSL }}
2222
listen {{ $s.Port }} ssl;
2323
ssl_certificate {{ $s.SSL.Certificate }};

0 commit comments

Comments
 (0)