1
0
mirror of https://github.com/oauth2-proxy/oauth2-proxy.git synced 2025-03-23 21:50:48 +02:00

Make the Upstreams mux configurable

This commit changes Upstreams from []Upstream to a struct{}
moving the previous []Upstream into .Configs and adjusts all uses of it.
This commit is contained in:
Fabian Stelzer 2021-08-09 13:32:15 +00:00
parent ae72beb24e
commit 12ab4ef529
No known key found for this signature in database
11 changed files with 283 additions and 231 deletions

View File

@ -25,6 +25,7 @@ client_secret="b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK"
const testAlphaConfig = ` const testAlphaConfig = `
upstreams: upstreams:
configs:
- id: / - id: /
path: / path: /
uri: http://httpbin uri: http://httpbin
@ -101,13 +102,15 @@ redirect_url="http://localhost:4180/oauth2/callback"
opts.RawRedirectURL = "http://localhost:4180/oauth2/callback" opts.RawRedirectURL = "http://localhost:4180/oauth2/callback"
opts.UpstreamServers = options.Upstreams{ opts.UpstreamServers = options.Upstreams{
{ Configs: []options.Upstream{
ID: "/", {
Path: "/", ID: "/",
URI: "http://httpbin", Path: "/",
FlushInterval: durationPtr(options.DefaultUpstreamFlushInterval), URI: "http://httpbin",
PassHostHeader: boolPtr(true), FlushInterval: durationPtr(options.DefaultUpstreamFlushInterval),
ProxyWebSockets: boolPtr(true), PassHostHeader: boolPtr(true),
ProxyWebSockets: boolPtr(true),
},
}, },
} }
@ -130,7 +133,7 @@ redirect_url="http://localhost:4180/oauth2/callback"
opts.InjectResponseHeaders = append(opts.InjectResponseHeaders, authHeader) opts.InjectResponseHeaders = append(opts.InjectResponseHeaders, authHeader)
opts.Providers = options.Providers{ opts.Providers = options.Providers{
{ options.Provider{
ID: "google=oauth2-proxy", ID: "google=oauth2-proxy",
Type: "google", Type: "google",
ClientSecret: "b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK", ClientSecret: "b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK",
@ -230,7 +233,7 @@ redirect_url="http://localhost:4180/oauth2/callback"
configContent: testCoreConfig, configContent: testCoreConfig,
alphaConfigContent: testAlphaConfig + ":", alphaConfigContent: testAlphaConfig + ":",
expectedOptions: func() *options.Options { return nil }, expectedOptions: func() *options.Options { return nil },
expectedErr: errors.New("failed to load alpha options: error unmarshalling config: error converting YAML to JSON: yaml: line 49: did not find expected key"), expectedErr: errors.New("failed to load alpha options: error unmarshalling config: error converting YAML to JSON: yaml: line 50: did not find expected key"),
}), }),
Entry("with alpha configuration and bad core configuration", loadConfigurationTableInput{ Entry("with alpha configuration and bad core configuration", loadConfigurationTableInput{
configContent: testCoreConfig + "unknown_field=\"something\"", configContent: testCoreConfig + "unknown_field=\"something\"",

View File

@ -198,10 +198,12 @@ func TestBasicAuthPassword(t *testing.T) {
basicAuthPassword := "This is a secure password" basicAuthPassword := "This is a secure password"
opts := baseTestOptions() opts := baseTestOptions()
opts.UpstreamServers = options.Upstreams{ opts.UpstreamServers = options.Upstreams{
{ Configs: []options.Upstream{
ID: providerServer.URL, {
Path: "/", ID: providerServer.URL,
URI: providerServer.URL, Path: "/",
URI: providerServer.URL,
},
}, },
} }
@ -347,14 +349,16 @@ func NewPassAccessTokenTest(opts PassAccessTokenTestOptions) (*PassAccessTokenTe
patt.opts = baseTestOptions() patt.opts = baseTestOptions()
patt.opts.UpstreamServers = options.Upstreams{ patt.opts.UpstreamServers = options.Upstreams{
{ Configs: []options.Upstream{
ID: patt.providerServer.URL, {
Path: "/", ID: patt.providerServer.URL,
URI: patt.providerServer.URL, Path: "/",
URI: patt.providerServer.URL,
},
}, },
} }
if opts.ProxyUpstream.ID != "" { if opts.ProxyUpstream.ID != "" {
patt.opts.UpstreamServers = append(patt.opts.UpstreamServers, opts.ProxyUpstream) patt.opts.UpstreamServers.Configs = append(patt.opts.UpstreamServers.Configs, opts.ProxyUpstream)
} }
patt.opts.Cookie.Secure = false patt.opts.Cookie.Secure = false
@ -1270,10 +1274,12 @@ func TestAuthSkippedForPreflightRequests(t *testing.T) {
opts := baseTestOptions() opts := baseTestOptions()
opts.UpstreamServers = options.Upstreams{ opts.UpstreamServers = options.Upstreams{
{ Configs: []options.Upstream{
ID: upstreamServer.URL, {
Path: "/", ID: upstreamServer.URL,
URI: upstreamServer.URL, Path: "/",
URI: upstreamServer.URL,
},
}, },
} }
opts.SkipAuthPreflight = true opts.SkipAuthPreflight = true
@ -1345,10 +1351,12 @@ func NewSignatureTest() (*SignatureTest, error) {
return nil, err return nil, err
} }
opts.UpstreamServers = options.Upstreams{ opts.UpstreamServers = options.Upstreams{
{ Configs: []options.Upstream{
ID: upstreamServer.URL, {
Path: "/", ID: upstreamServer.URL,
URI: upstreamServer.URL, Path: "/",
URI: upstreamServer.URL,
},
}, },
} }
@ -1781,10 +1789,12 @@ func Test_noCacheHeaders(t *testing.T) {
opts := baseTestOptions() opts := baseTestOptions()
opts.UpstreamServers = options.Upstreams{ opts.UpstreamServers = options.Upstreams{
{ Configs: []options.Upstream{
ID: upstreamServer.URL, {
Path: "/", ID: upstreamServer.URL,
URI: upstreamServer.URL, Path: "/",
URI: upstreamServer.URL,
},
}, },
} }
opts.SkipAuthRegex = []string{".*"} opts.SkipAuthRegex = []string{".*"}
@ -2051,10 +2061,12 @@ func TestTrustedIPs(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
opts := baseTestOptions() opts := baseTestOptions()
opts.UpstreamServers = options.Upstreams{ opts.UpstreamServers = options.Upstreams{
{ Configs: []options.Upstream{
ID: "static", {
Path: "/", ID: "static",
Static: true, Path: "/",
Static: true,
},
}, },
} }
opts.TrustedIPs = tt.trustedIPs opts.TrustedIPs = tt.trustedIPs
@ -2244,10 +2256,12 @@ func TestAllowedRequest(t *testing.T) {
opts := baseTestOptions() opts := baseTestOptions()
opts.UpstreamServers = options.Upstreams{ opts.UpstreamServers = options.Upstreams{
{ Configs: []options.Upstream{
ID: upstreamServer.URL, {
Path: "/", ID: upstreamServer.URL,
URI: upstreamServer.URL, Path: "/",
URI: upstreamServer.URL,
},
}, },
} }
opts.SkipAuthRegex = []string{ opts.SkipAuthRegex = []string{
@ -2359,10 +2373,12 @@ func TestProxyAllowedGroups(t *testing.T) {
test, err := NewProcessCookieTestWithOptionsModifiers(func(opts *options.Options) { test, err := NewProcessCookieTestWithOptionsModifiers(func(opts *options.Options) {
opts.Providers[0].AllowedGroups = tt.allowedGroups opts.Providers[0].AllowedGroups = tt.allowedGroups
opts.UpstreamServers = options.Upstreams{ opts.UpstreamServers = options.Upstreams{
{ Configs: []options.Upstream{
ID: upstreamServer.URL, {
Path: "/", ID: upstreamServer.URL,
URI: upstreamServer.URL, Path: "/",
URI: upstreamServer.URL,
},
}, },
} }
}) })

View File

@ -120,7 +120,7 @@ func (l *LegacyUpstreams) convert() (Upstreams, error) {
for _, upstreamString := range l.Upstreams { for _, upstreamString := range l.Upstreams {
u, err := url.Parse(upstreamString) u, err := url.Parse(upstreamString)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not parse upstream %q: %v", upstreamString, err) return Upstreams{}, fmt.Errorf("could not parse upstream %q: %v", upstreamString, err)
} }
if u.Path == "" { if u.Path == "" {
@ -169,7 +169,7 @@ func (l *LegacyUpstreams) convert() (Upstreams, error) {
upstream.FlushInterval = nil upstream.FlushInterval = nil
} }
upstreams = append(upstreams, upstream) upstreams.Configs = append(upstreams.Configs, upstream)
} }
return upstreams, nil return upstreams, nil

View File

@ -27,34 +27,36 @@ var _ = Describe("Legacy Options", func() {
truth := true truth := true
staticCode := 204 staticCode := 204
opts.UpstreamServers = Upstreams{ opts.UpstreamServers = Upstreams{
{ Configs: []Upstream{
ID: "/baz", {
Path: "/baz", ID: "/baz",
URI: "http://foo.bar/baz", Path: "/baz",
FlushInterval: &flushInterval, URI: "http://foo.bar/baz",
InsecureSkipTLSVerify: true, FlushInterval: &flushInterval,
PassHostHeader: &truth, InsecureSkipTLSVerify: true,
ProxyWebSockets: &truth, PassHostHeader: &truth,
}, ProxyWebSockets: &truth,
{ },
ID: "/bar", {
Path: "/bar", ID: "/bar",
URI: "file:///var/lib/website", Path: "/bar",
FlushInterval: &flushInterval, URI: "file:///var/lib/website",
InsecureSkipTLSVerify: true, FlushInterval: &flushInterval,
PassHostHeader: &truth, InsecureSkipTLSVerify: true,
ProxyWebSockets: &truth, PassHostHeader: &truth,
}, ProxyWebSockets: &truth,
{ },
ID: "static://204", {
Path: "/", ID: "static://204",
URI: "", Path: "/",
Static: true, URI: "",
StaticCode: &staticCode, Static: true,
FlushInterval: nil, StaticCode: &staticCode,
InsecureSkipTLSVerify: false, FlushInterval: nil,
PassHostHeader: nil, InsecureSkipTLSVerify: false,
ProxyWebSockets: nil, PassHostHeader: nil,
ProxyWebSockets: nil,
},
}, },
} }
@ -124,7 +126,7 @@ var _ = Describe("Legacy Options", func() {
Context("Legacy Upstreams", func() { Context("Legacy Upstreams", func() {
type convertUpstreamsTableInput struct { type convertUpstreamsTableInput struct {
upstreamStrings []string upstreamStrings []string
expectedUpstreams Upstreams expectedUpstreams []Upstream
errMsg string errMsg string
} }
@ -219,51 +221,51 @@ var _ = Describe("Legacy Options", func() {
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} }
Expect(upstreams).To(ConsistOf(in.expectedUpstreams)) Expect(upstreams.Configs).To(ConsistOf(in.expectedUpstreams))
}, },
Entry("with no upstreams", &convertUpstreamsTableInput{ Entry("with no upstreams", &convertUpstreamsTableInput{
upstreamStrings: []string{}, upstreamStrings: []string{},
expectedUpstreams: Upstreams{}, expectedUpstreams: []Upstream{},
errMsg: "", errMsg: "",
}), }),
Entry("with a valid HTTP upstream", &convertUpstreamsTableInput{ Entry("with a valid HTTP upstream", &convertUpstreamsTableInput{
upstreamStrings: []string{validHTTP}, upstreamStrings: []string{validHTTP},
expectedUpstreams: Upstreams{validHTTPUpstream}, expectedUpstreams: []Upstream{validHTTPUpstream},
errMsg: "", errMsg: "",
}), }),
Entry("with a HTTP upstream with an empty path", &convertUpstreamsTableInput{ Entry("with a HTTP upstream with an empty path", &convertUpstreamsTableInput{
upstreamStrings: []string{emptyPathHTTP}, upstreamStrings: []string{emptyPathHTTP},
expectedUpstreams: Upstreams{emptyPathHTTPUpstream}, expectedUpstreams: []Upstream{emptyPathHTTPUpstream},
errMsg: "", errMsg: "",
}), }),
Entry("with a valid File upstream with a fragment", &convertUpstreamsTableInput{ Entry("with a valid File upstream with a fragment", &convertUpstreamsTableInput{
upstreamStrings: []string{validFileWithFragment}, upstreamStrings: []string{validFileWithFragment},
expectedUpstreams: Upstreams{validFileWithFragmentUpstream}, expectedUpstreams: []Upstream{validFileWithFragmentUpstream},
errMsg: "", errMsg: "",
}), }),
Entry("with a valid static upstream", &convertUpstreamsTableInput{ Entry("with a valid static upstream", &convertUpstreamsTableInput{
upstreamStrings: []string{validStatic}, upstreamStrings: []string{validStatic},
expectedUpstreams: Upstreams{validStaticUpstream}, expectedUpstreams: []Upstream{validStaticUpstream},
errMsg: "", errMsg: "",
}), }),
Entry("with an invalid static upstream, code is 200", &convertUpstreamsTableInput{ Entry("with an invalid static upstream, code is 200", &convertUpstreamsTableInput{
upstreamStrings: []string{invalidStatic}, upstreamStrings: []string{invalidStatic},
expectedUpstreams: Upstreams{invalidStaticUpstream}, expectedUpstreams: []Upstream{invalidStaticUpstream},
errMsg: "", errMsg: "",
}), }),
Entry("with an invalid HTTP upstream", &convertUpstreamsTableInput{ Entry("with an invalid HTTP upstream", &convertUpstreamsTableInput{
upstreamStrings: []string{invalidHTTP}, upstreamStrings: []string{invalidHTTP},
expectedUpstreams: Upstreams{}, expectedUpstreams: []Upstream{},
errMsg: invalidHTTPErrMsg, errMsg: invalidHTTPErrMsg,
}), }),
Entry("with an invalid HTTP upstream and other upstreams", &convertUpstreamsTableInput{ Entry("with an invalid HTTP upstream and other upstreams", &convertUpstreamsTableInput{
upstreamStrings: []string{validHTTP, invalidHTTP}, upstreamStrings: []string{validHTTP, invalidHTTP},
expectedUpstreams: Upstreams{}, expectedUpstreams: []Upstream{},
errMsg: invalidHTTPErrMsg, errMsg: invalidHTTPErrMsg,
}), }),
Entry("with multiple valid upstreams", &convertUpstreamsTableInput{ Entry("with multiple valid upstreams", &convertUpstreamsTableInput{
upstreamStrings: []string{validHTTP, validFileWithFragment, validStatic}, upstreamStrings: []string{validHTTP, validFileWithFragment, validStatic},
expectedUpstreams: Upstreams{validHTTPUpstream, validFileWithFragmentUpstream, validStaticUpstream}, expectedUpstreams: []Upstream{validHTTPUpstream, validFileWithFragmentUpstream, validStaticUpstream},
errMsg: "", errMsg: "",
}), }),
) )

View File

@ -470,10 +470,11 @@ sub:
It("should load a full example AlphaOptions", func() { It("should load a full example AlphaOptions", func() {
config := []byte(` config := []byte(`
upstreams: upstreams:
- id: httpbin configs:
path: / - id: httpbin
uri: http://httpbin path: /
flushInterval: 500ms uri: http://httpbin
flushInterval: 500ms
injectRequestHeaders: injectRequestHeaders:
- name: X-Forwarded-User - name: X-Forwarded-User
values: values:
@ -502,12 +503,14 @@ injectResponseHeaders:
flushInterval := Duration(500 * time.Millisecond) flushInterval := Duration(500 * time.Millisecond)
Expect(into).To(Equal(&AlphaOptions{ Expect(into).To(Equal(&AlphaOptions{
Upstreams: []Upstream{ Upstreams: Upstreams{
{ Configs: []Upstream{
ID: "httpbin", {
Path: "/", ID: "httpbin",
URI: "http://httpbin", Path: "/",
FlushInterval: &flushInterval, URI: "http://httpbin",
FlushInterval: &flushInterval,
},
}, },
}, },
InjectRequestHeaders: []Header{ InjectRequestHeaders: []Header{

View File

@ -8,7 +8,11 @@ const (
) )
// Upstreams is a collection of definitions for upstream servers. // Upstreams is a collection of definitions for upstream servers.
type Upstreams []Upstream type Upstreams struct {
// Upstream represents the configuration for an upstream server.
// Requests will be proxied to this upstream if the path matches the request path.
Configs []Upstream `json:"configs,omitempty"`
}
// Upstream represents the configuration for an upstream server. // Upstream represents the configuration for an upstream server.
// Requests will be proxied to this upstream if the path matches the request path. // Requests will be proxied to this upstream if the path matches the request path.

View File

@ -27,7 +27,7 @@ func NewProxy(upstreams options.Upstreams, sigData *options.SignatureData, write
serveMux: mux.NewRouter(), serveMux: mux.NewRouter(),
} }
for _, upstream := range sortByPathLongest(upstreams) { for _, upstream := range sortByPathLongest(upstreams.Configs) {
if upstream.Static { if upstream.Static {
if err := m.registerStaticResponseHandler(upstream, writer); err != nil { if err := m.registerStaticResponseHandler(upstream, writer); err != nil {
return nil, fmt.Errorf("could not register static upstream %q: %v", upstream.ID, err) return nil, fmt.Errorf("could not register static upstream %q: %v", upstream.ID, err)
@ -153,7 +153,7 @@ func registerTrailingSlashHandler(serveMux *mux.Router) {
// precedence (note this is the input to the rewrite logic). // precedence (note this is the input to the rewrite logic).
// This does not account for when a rewrite would actually make the path shorter. // This does not account for when a rewrite would actually make the path shorter.
// This should maintain the sorting behaviour of the standard go serve mux. // This should maintain the sorting behaviour of the standard go serve mux.
func sortByPathLongest(in options.Upstreams) options.Upstreams { func sortByPathLongest(in []options.Upstream) []options.Upstream {
sort.Slice(in, func(i, j int) bool { sort.Slice(in, func(i, j int) bool {
iRW := in[i].RewriteTarget iRW := in[i].RewriteTarget
jRW := in[j].RewriteTarget jRW := in[j].RewriteTarget

View File

@ -33,61 +33,63 @@ var _ = Describe("Proxy Suite", func() {
accepted := http.StatusAccepted accepted := http.StatusAccepted
upstreams := options.Upstreams{ upstreams := options.Upstreams{
{ Configs: []options.Upstream{
ID: "http-backend", {
Path: "/http/", ID: "http-backend",
URI: serverAddr, Path: "/http/",
}, URI: serverAddr,
{ },
ID: "file-backend", {
Path: "/files/", ID: "file-backend",
URI: fmt.Sprintf("file:///%s", filesDir), Path: "/files/",
}, URI: fmt.Sprintf("file:///%s", filesDir),
{ },
ID: "static-backend", {
Path: "/static/", ID: "static-backend",
Static: true, Path: "/static/",
StaticCode: &ok, Static: true,
}, StaticCode: &ok,
{ },
ID: "static-backend-no-trailing-slash", {
Path: "/static", ID: "static-backend-no-trailing-slash",
Static: true, Path: "/static",
StaticCode: &accepted, Static: true,
}, StaticCode: &accepted,
{ },
ID: "static-backend-long", {
Path: "/static/long", ID: "static-backend-long",
Static: true, Path: "/static/long",
StaticCode: &accepted, Static: true,
}, StaticCode: &accepted,
{ },
ID: "bad-http-backend", {
Path: "/bad-http/", ID: "bad-http-backend",
URI: "http://::1", Path: "/bad-http/",
}, URI: "http://::1",
{ },
ID: "single-path-backend", {
Path: "/single-path", ID: "single-path-backend",
Static: true, Path: "/single-path",
StaticCode: &ok, Static: true,
}, StaticCode: &ok,
{ },
ID: "backend-with-rewrite-prefix", {
Path: "^/rewrite-prefix/(.*)", ID: "backend-with-rewrite-prefix",
RewriteTarget: "/different/backend/path/$1", Path: "^/rewrite-prefix/(.*)",
URI: serverAddr, RewriteTarget: "/different/backend/path/$1",
}, URI: serverAddr,
{ },
ID: "double-match-plain", {
Path: "/double-match/", ID: "double-match-plain",
URI: serverAddr, Path: "/double-match/",
}, URI: serverAddr,
{ },
ID: "double-match-rewrite", {
Path: "^/double-match/(.*)", ID: "double-match-rewrite",
RewriteTarget: "/double-match/rewrite/$1", Path: "^/double-match/(.*)",
URI: serverAddr, RewriteTarget: "/double-match/rewrite/$1",
URI: serverAddr,
},
}, },
} }
@ -315,8 +317,8 @@ var _ = Describe("Proxy Suite", func() {
Context("sortByPathLongest", func() { Context("sortByPathLongest", func() {
type sortByPathLongestTableInput struct { type sortByPathLongestTableInput struct {
input options.Upstreams input []options.Upstream
expectedOutput options.Upstreams expectedOutput []options.Upstream
} }
var httpPath = options.Upstream{ var httpPath = options.Upstream{
@ -346,40 +348,40 @@ var _ = Describe("Proxy Suite", func() {
Expect(sortByPathLongest(in.input)).To(Equal(in.expectedOutput)) Expect(sortByPathLongest(in.input)).To(Equal(in.expectedOutput))
}, },
Entry("with a mix of paths registered", sortByPathLongestTableInput{ Entry("with a mix of paths registered", sortByPathLongestTableInput{
input: options.Upstreams{httpPath, httpSubPath, shortSubPathWithRewrite, longerPath, shortPathWithRewrite}, input: []options.Upstream{httpPath, httpSubPath, shortSubPathWithRewrite, longerPath, shortPathWithRewrite},
expectedOutput: options.Upstreams{shortSubPathWithRewrite, shortPathWithRewrite, longerPath, httpSubPath, httpPath}, expectedOutput: []options.Upstream{shortSubPathWithRewrite, shortPathWithRewrite, longerPath, httpSubPath, httpPath},
}), }),
Entry("when a subpath is registered (in order)", sortByPathLongestTableInput{ Entry("when a subpath is registered (in order)", sortByPathLongestTableInput{
input: options.Upstreams{httpSubPath, httpPath}, input: []options.Upstream{httpSubPath, httpPath},
expectedOutput: options.Upstreams{httpSubPath, httpPath}, expectedOutput: []options.Upstream{httpSubPath, httpPath},
}), }),
Entry("when a subpath is registered (out of order)", sortByPathLongestTableInput{ Entry("when a subpath is registered (out of order)", sortByPathLongestTableInput{
input: options.Upstreams{httpPath, httpSubPath}, input: []options.Upstream{httpPath, httpSubPath},
expectedOutput: options.Upstreams{httpSubPath, httpPath}, expectedOutput: []options.Upstream{httpSubPath, httpPath},
}), }),
Entry("when longer paths are registered (in order)", sortByPathLongestTableInput{ Entry("when longer paths are registered (in order)", sortByPathLongestTableInput{
input: options.Upstreams{longerPath, httpPath}, input: []options.Upstream{longerPath, httpPath},
expectedOutput: options.Upstreams{longerPath, httpPath}, expectedOutput: []options.Upstream{longerPath, httpPath},
}), }),
Entry("when longer paths are registered (out of order)", sortByPathLongestTableInput{ Entry("when longer paths are registered (out of order)", sortByPathLongestTableInput{
input: options.Upstreams{httpPath, longerPath}, input: []options.Upstream{httpPath, longerPath},
expectedOutput: options.Upstreams{longerPath, httpPath}, expectedOutput: []options.Upstream{longerPath, httpPath},
}), }),
Entry("when a rewrite target is registered (in order)", sortByPathLongestTableInput{ Entry("when a rewrite target is registered (in order)", sortByPathLongestTableInput{
input: options.Upstreams{shortPathWithRewrite, longerPath}, input: []options.Upstream{shortPathWithRewrite, longerPath},
expectedOutput: options.Upstreams{shortPathWithRewrite, longerPath}, expectedOutput: []options.Upstream{shortPathWithRewrite, longerPath},
}), }),
Entry("when a rewrite target is registered (out of order)", sortByPathLongestTableInput{ Entry("when a rewrite target is registered (out of order)", sortByPathLongestTableInput{
input: options.Upstreams{longerPath, shortPathWithRewrite}, input: []options.Upstream{longerPath, shortPathWithRewrite},
expectedOutput: options.Upstreams{shortPathWithRewrite, longerPath}, expectedOutput: []options.Upstream{shortPathWithRewrite, longerPath},
}), }),
Entry("with multiple rewrite targets registered (in order)", sortByPathLongestTableInput{ Entry("with multiple rewrite targets registered (in order)", sortByPathLongestTableInput{
input: options.Upstreams{shortSubPathWithRewrite, shortPathWithRewrite}, input: []options.Upstream{shortSubPathWithRewrite, shortPathWithRewrite},
expectedOutput: options.Upstreams{shortSubPathWithRewrite, shortPathWithRewrite}, expectedOutput: []options.Upstream{shortSubPathWithRewrite, shortPathWithRewrite},
}), }),
Entry("with multiple rewrite targets registered (out of order)", sortByPathLongestTableInput{ Entry("with multiple rewrite targets registered (out of order)", sortByPathLongestTableInput{
input: options.Upstreams{shortPathWithRewrite, shortSubPathWithRewrite}, input: []options.Upstream{shortPathWithRewrite, shortSubPathWithRewrite},
expectedOutput: options.Upstreams{shortSubPathWithRewrite, shortPathWithRewrite}, expectedOutput: []options.Upstream{shortSubPathWithRewrite, shortPathWithRewrite},
}), }),
) )
}) })

View File

@ -22,7 +22,7 @@ const (
func testOptions() *options.Options { func testOptions() *options.Options {
o := options.NewOptions() o := options.NewOptions()
o.UpstreamServers = append(o.UpstreamServers, options.Upstream{ o.UpstreamServers.Configs = append(o.UpstreamServers.Configs, options.Upstream{
ID: "upstream", ID: "upstream",
Path: "/", Path: "/",
URI: "http://127.0.0.1:8080/", URI: "http://127.0.0.1:8080/",

View File

@ -12,7 +12,7 @@ func validateUpstreams(upstreams options.Upstreams) []string {
ids := make(map[string]struct{}) ids := make(map[string]struct{})
paths := make(map[string]struct{}) paths := make(map[string]struct{})
for _, upstream := range upstreams { for _, upstream := range upstreams.Configs {
msgs = append(msgs, validateUpstream(upstream, ids, paths)...) msgs = append(msgs, validateUpstream(upstream, ids, paths)...)
} }

View File

@ -59,83 +59,99 @@ var _ = Describe("Upstreams", func() {
}), }),
Entry("with valid upstreams", &validateUpstreamTableInput{ Entry("with valid upstreams", &validateUpstreamTableInput{
upstreams: options.Upstreams{ upstreams: options.Upstreams{
validHTTPUpstream, Configs: []options.Upstream{
validStaticUpstream, validHTTPUpstream,
validFileUpstream, validStaticUpstream,
validFileUpstream,
},
}, },
errStrings: []string{}, errStrings: []string{},
}), }),
Entry("with an empty ID", &validateUpstreamTableInput{ Entry("with an empty ID", &validateUpstreamTableInput{
upstreams: options.Upstreams{ upstreams: options.Upstreams{
{ Configs: []options.Upstream{
ID: "", {
Path: "/foo", ID: "",
URI: "http://localhost:8080", Path: "/foo",
URI: "http://localhost:8080",
},
}, },
}, },
errStrings: []string{emptyIDMsg}, errStrings: []string{emptyIDMsg},
}), }),
Entry("with an empty Path", &validateUpstreamTableInput{ Entry("with an empty Path", &validateUpstreamTableInput{
upstreams: options.Upstreams{ upstreams: options.Upstreams{
{ Configs: []options.Upstream{
ID: "foo", {
Path: "", ID: "foo",
URI: "http://localhost:8080", Path: "",
URI: "http://localhost:8080",
},
}, },
}, },
errStrings: []string{emptyPathMsg}, errStrings: []string{emptyPathMsg},
}), }),
Entry("with an empty Path", &validateUpstreamTableInput{ Entry("with an empty Path", &validateUpstreamTableInput{
upstreams: options.Upstreams{ upstreams: options.Upstreams{
{ Configs: []options.Upstream{
ID: "foo", {
Path: "", ID: "foo",
URI: "http://localhost:8080", Path: "",
URI: "http://localhost:8080",
},
}, },
}, },
errStrings: []string{emptyPathMsg}, errStrings: []string{emptyPathMsg},
}), }),
Entry("with an empty URI", &validateUpstreamTableInput{ Entry("with an empty URI", &validateUpstreamTableInput{
upstreams: options.Upstreams{ upstreams: options.Upstreams{
{ Configs: []options.Upstream{
ID: "foo", {
Path: "/foo", ID: "foo",
URI: "", Path: "/foo",
URI: "",
},
}, },
}, },
errStrings: []string{emptyURIMsg}, errStrings: []string{emptyURIMsg},
}), }),
Entry("with an invalid URI", &validateUpstreamTableInput{ Entry("with an invalid URI", &validateUpstreamTableInput{
upstreams: options.Upstreams{ upstreams: options.Upstreams{
{ Configs: []options.Upstream{
ID: "foo", {
Path: "/foo", ID: "foo",
URI: ":", Path: "/foo",
URI: ":",
},
}, },
}, },
errStrings: []string{invalidURIMsg}, errStrings: []string{invalidURIMsg},
}), }),
Entry("with an invalid URI scheme", &validateUpstreamTableInput{ Entry("with an invalid URI scheme", &validateUpstreamTableInput{
upstreams: options.Upstreams{ upstreams: options.Upstreams{
{ Configs: []options.Upstream{
ID: "foo", {
Path: "/foo", ID: "foo",
URI: "ftp://foo", Path: "/foo",
URI: "ftp://foo",
},
}, },
}, },
errStrings: []string{invalidURISchemeMsg}, errStrings: []string{invalidURISchemeMsg},
}), }),
Entry("with a static upstream and invalid optons", &validateUpstreamTableInput{ Entry("with a static upstream and invalid optons", &validateUpstreamTableInput{
upstreams: options.Upstreams{ upstreams: options.Upstreams{
{ Configs: []options.Upstream{
ID: "foo", {
Path: "/foo", ID: "foo",
URI: "ftp://foo", Path: "/foo",
Static: true, URI: "ftp://foo",
FlushInterval: &flushInterval, Static: true,
PassHostHeader: &truth, FlushInterval: &flushInterval,
ProxyWebSockets: &truth, PassHostHeader: &truth,
InsecureSkipTLSVerify: true, ProxyWebSockets: &truth,
InsecureSkipTLSVerify: true,
},
}, },
}, },
errStrings: []string{ errStrings: []string{
@ -148,40 +164,46 @@ var _ = Describe("Upstreams", func() {
}), }),
Entry("with duplicate IDs", &validateUpstreamTableInput{ Entry("with duplicate IDs", &validateUpstreamTableInput{
upstreams: options.Upstreams{ upstreams: options.Upstreams{
{ Configs: []options.Upstream{
ID: "foo", {
Path: "/foo1", ID: "foo",
URI: "http://foo", Path: "/foo1",
}, URI: "http://foo",
{ },
ID: "foo", {
Path: "/foo2", ID: "foo",
URI: "http://foo", Path: "/foo2",
URI: "http://foo",
},
}, },
}, },
errStrings: []string{multipleIDsMsg}, errStrings: []string{multipleIDsMsg},
}), }),
Entry("with duplicate Paths", &validateUpstreamTableInput{ Entry("with duplicate Paths", &validateUpstreamTableInput{
upstreams: options.Upstreams{ upstreams: options.Upstreams{
{ Configs: []options.Upstream{
ID: "foo1", {
Path: "/foo", ID: "foo1",
URI: "http://foo", Path: "/foo",
}, URI: "http://foo",
{ },
ID: "foo2", {
Path: "/foo", ID: "foo2",
URI: "http://foo", Path: "/foo",
URI: "http://foo",
},
}, },
}, },
errStrings: []string{multiplePathsMsg}, errStrings: []string{multiplePathsMsg},
}), }),
Entry("when a static code is supplied without static", &validateUpstreamTableInput{ Entry("when a static code is supplied without static", &validateUpstreamTableInput{
upstreams: options.Upstreams{ upstreams: options.Upstreams{
{ Configs: []options.Upstream{
ID: "foo", {
Path: "/foo", ID: "foo",
StaticCode: &staticCode200, Path: "/foo",
StaticCode: &staticCode200,
},
}, },
}, },
errStrings: []string{emptyURIMsg, staticCodeMsg}, errStrings: []string{emptyURIMsg, staticCodeMsg},