git.iko.soy/typeable/oauth2-proxy
on master help sign in
rename Upstreams to UpstreamConfig and its Configs member to Upstreams then
commit88f32aeaa15dfe3f6596518e0d9970dbff4eff33 authorFabian Stelzer <fs@gigacodes.de> dateFri 17 Sep 2021, 11:08 (5 years ago) parentfe91595
14 files changed+79−80
docs/docs/configuration/alpha_config.md+5−5 main_test.go+4−4 oauthproxy_test.go+17−17 pkg/apis/options/alpha_options.go+4−5 pkg/apis/options/legacy_options.go+4−4 pkg/apis/options/legacy_options_test.go+3−3 pkg/apis/options/load_test.go+4−4 pkg/apis/options/options.go+1−1 pkg/apis/options/upstreams.go+4−4 pkg/upstream/proxy.go+2−2 pkg/upstream/proxy_test.go+4−4 pkg/validation/options_test.go+1−1 pkg/validation/upstreams.go+2−2 pkg/validation/upstreams_test.go+24−24
docs/docs/configuration/alpha_config.md+5−5
@@ -124,7 +124,7 @@ They may change between releases without notice.
124124
125125| Field | Type | Description |
126126| ----- | ---- | ----------- |
127−| `upstreams` | _[Upstreams](#upstreams)_ | Upstreams is used to configure upstream servers.<br/>Once a user is authenticated, requests to the server will be proxied to<br/>these upstream servers based on the path mappings defined in this list. |
127+| `upstreamConfig` | _[UpstreamConfig](#upstreamconfig)_ | UpstreamConfig is used to configure upstream servers.<br/>Once a user is authenticated, requests to the server will be proxied to<br/>these upstream servers based on the path mappings defined in this list. |
128128| `injectRequestHeaders` | _[[]Header](#header)_ | InjectRequestHeaders is used to configure headers that should be added<br/>to requests to upstream servers.<br/>Headers may source values from either the authenticated user's session<br/>or from a static secret value. |
129129| `injectResponseHeaders` | _[[]Header](#header)_ | InjectResponseHeaders is used to configure headers that should be added<br/>to responses from the proxy.<br/>This is typically used when using the proxy as an external authentication<br/>provider in conjunction with another proxy such as NGINX and its<br/>auth_request module.<br/>Headers may source values from either the authenticated user's session<br/>or from a static secret value. |
130130| `server` | _[Server](#server)_ | Server is used to configure the HTTP(S) server for the proxy application.<br/>You may choose to run both HTTP and HTTPS servers simultaneously.<br/>This can be done by setting the BindAddress and the SecureBindAddress simultaneously.<br/>To use the secure server you must configure a TLS certificate and key. |
@@ -364,7 +364,7 @@ TLS contains the information for loading a TLS certifcate and key.
364364
365365### Upstream
366366
367−(**Appears on:** [Upstreams](#upstreams))
367+(**Appears on:** [UpstreamConfig](#upstreamconfig))
368368
369369Upstream represents the configuration for an upstream server.
370370Requests will be proxied to this upstream if the path matches the request path.
@@ -382,13 +382,13 @@ Requests will be proxied to this upstream if the path matches the request path.
382382| `passHostHeader` | _bool_ | PassHostHeader determines whether the request host header should be proxied<br/>to the upstream server.<br/>Defaults to true. |
383383| `proxyWebSockets` | _bool_ | ProxyWebSockets enables proxying of websockets to upstream servers<br/>Defaults to true. |
384384
385−### Upstreams
385+### UpstreamConfig
386386
387387(**Appears on:** [AlphaOptions](#alphaoptions))
388388
389−Upstreams is a collection of definitions for upstream servers.
389+UpstreamConfig is a collection of definitions for upstream servers.
390390
391391| Field | Type | Description |
392392| ----- | ---- | ----------- |
393393| `proxyRawPath` | _bool_ | ProxyRawPath will pass the raw url path to upstream allowing for url's<br/>like: "/%2F/" which would otherwise be redirected to "/" |
394−| `configs` | _[[]Upstream](#upstream)_ | Upstream represents the configuration for an upstream server.<br/>Requests will be proxied to this upstream if the path matches the request path. |
394+| `upstreams` | _[[]Upstream](#upstream)_ | Upstreams represents the configuration for the upstream servers.<br/>Requests will be proxied to this upstream if the path matches the request path. |
main_test.go+4−4
@@ -26,9 +26,9 @@ client_secret="b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK"
2626`
2727
2828 const testAlphaConfig = `
29−upstreams:
29+upstreamConfig:
3030 proxyrawpath: false
31− configs:
31+ upstreams:
3232 - id: /
3333 path: /
3434 uri: http://httpbin
@@ -104,8 +104,8 @@ redirect_url="http://localhost:4180/oauth2/callback"
104104 opts.Cookie.Secure = false
105105 opts.RawRedirectURL = "http://localhost:4180/oauth2/callback"
106106
107− opts.UpstreamServers = options.Upstreams{
108− Configs: []options.Upstream{
107+ opts.UpstreamServers = options.UpstreamConfig{
108+ Upstreams: []options.Upstream{
109109 {
110110 ID: "/",
111111 Path: "/",
oauthproxy_test.go+17−17
@@ -197,8 +197,8 @@ func TestBasicAuthPassword(t *testing.T) {
197197
198198 basicAuthPassword := "This is a secure password"
199199 opts := baseTestOptions()
200− opts.UpstreamServers = options.Upstreams{
201− Configs: []options.Upstream{
200+ opts.UpstreamServers = options.UpstreamConfig{
201+ Upstreams: []options.Upstream{
202202 {
203203 ID: providerServer.URL,
204204 Path: "/",
@@ -348,8 +348,8 @@ func NewPassAccessTokenTest(opts PassAccessTokenTestOptions) (*PassAccessTokenTe
348348 }))
349349
350350 patt.opts = baseTestOptions()
351− patt.opts.UpstreamServers = options.Upstreams{
352− Configs: []options.Upstream{
351+ patt.opts.UpstreamServers = options.UpstreamConfig{
352+ Upstreams: []options.Upstream{
353353 {
354354 ID: patt.providerServer.URL,
355355 Path: "/",
@@ -358,7 +358,7 @@ func NewPassAccessTokenTest(opts PassAccessTokenTestOptions) (*PassAccessTokenTe
358358 },
359359 }
360360 if opts.ProxyUpstream.ID != "" {
361− patt.opts.UpstreamServers.Configs = append(patt.opts.UpstreamServers.Configs, opts.ProxyUpstream)
361+ patt.opts.UpstreamServers.Upstreams = append(patt.opts.UpstreamServers.Upstreams, opts.ProxyUpstream)
362362 }
363363
364364 patt.opts.Cookie.Secure = false
@@ -1273,8 +1273,8 @@ func TestAuthSkippedForPreflightRequests(t *testing.T) {
12731273 t.Cleanup(upstreamServer.Close)
12741274
12751275 opts := baseTestOptions()
1276− opts.UpstreamServers = options.Upstreams{
1277− Configs: []options.Upstream{
1276+ opts.UpstreamServers = options.UpstreamConfig{
1277+ Upstreams: []options.Upstream{
12781278 {
12791279 ID: upstreamServer.URL,
12801280 Path: "/",
@@ -1350,8 +1350,8 @@ func NewSignatureTest() (*SignatureTest, error) {
13501350 if err != nil {
13511351 return nil, err
13521352 }
1353− opts.UpstreamServers = options.Upstreams{
1354− Configs: []options.Upstream{
1353+ opts.UpstreamServers = options.UpstreamConfig{
1354+ Upstreams: []options.Upstream{
13551355 {
13561356 ID: upstreamServer.URL,
13571357 Path: "/",
@@ -1788,8 +1788,8 @@ func Test_noCacheHeaders(t *testing.T) {
17881788 t.Cleanup(upstreamServer.Close)
17891789
17901790 opts := baseTestOptions()
1791− opts.UpstreamServers = options.Upstreams{
1792− Configs: []options.Upstream{
1791+ opts.UpstreamServers = options.UpstreamConfig{
1792+ Upstreams: []options.Upstream{
17931793 {
17941794 ID: upstreamServer.URL,
17951795 Path: "/",
@@ -2060,8 +2060,8 @@ func TestTrustedIPs(t *testing.T) {
20602060 for _, tt := range tests {
20612061 t.Run(tt.name, func(t *testing.T) {
20622062 opts := baseTestOptions()
2063− opts.UpstreamServers = options.Upstreams{
2064− Configs: []options.Upstream{
2063+ opts.UpstreamServers = options.UpstreamConfig{
2064+ Upstreams: []options.Upstream{
20652065 {
20662066 ID: "static",
20672067 Path: "/",
@@ -2255,8 +2255,8 @@ func TestAllowedRequest(t *testing.T) {
22552255 t.Cleanup(upstreamServer.Close)
22562256
22572257 opts := baseTestOptions()
2258− opts.UpstreamServers = options.Upstreams{
2259− Configs: []options.Upstream{
2258+ opts.UpstreamServers = options.UpstreamConfig{
2259+ Upstreams: []options.Upstream{
22602260 {
22612261 ID: upstreamServer.URL,
22622262 Path: "/",
@@ -2372,8 +2372,8 @@ func TestProxyAllowedGroups(t *testing.T) {
23722372
23732373 test, err := NewProcessCookieTestWithOptionsModifiers(func(opts *options.Options) {
23742374 opts.Providers[0].AllowedGroups = tt.allowedGroups
2375− opts.UpstreamServers = options.Upstreams{
2376− Configs: []options.Upstream{
2375+ opts.UpstreamServers = options.UpstreamConfig{
2376+ Upstreams: []options.Upstream{
23772377 {
23782378 ID: upstreamServer.URL,
23792379 Path: "/",
pkg/apis/options/alpha_options.go+4−5
@@ -9,10 +9,10 @@ package options
99// They may change between releases without notice.
1010// :::
1111type AlphaOptions struct {
12− // Upstreams is used to configure upstream servers.
12+ // UpstreamConfig is used to configure upstream servers.
1313 // Once a user is authenticated, requests to the server will be proxied to
1414 // these upstream servers based on the path mappings defined in this list.
15− Upstreams Upstreams `json:"upstreams,omitempty"`
15+ UpstreamConfig UpstreamConfig `json:"upstreamConfig,omitempty"`
1616
1717 // InjectRequestHeaders is used to configure headers that should be added
1818 // to requests to upstream servers.
@@ -48,19 +48,18 @@ type AlphaOptions struct {
4848// MergeInto replaces alpha options in the Options struct with the values
4949// from the AlphaOptions
5050func (a *AlphaOptions) MergeInto(opts *Options) {
51− opts.UpstreamServers = a.Upstreams
51+ opts.UpstreamServers = a.UpstreamConfig
5252 opts.InjectRequestHeaders = a.InjectRequestHeaders
5353 opts.InjectResponseHeaders = a.InjectResponseHeaders
5454 opts.Server = a.Server
5555 opts.MetricsServer = a.MetricsServer
5656 opts.Providers = a.Providers
57−
5857}
5958
6059// ExtractFrom populates the fields in the AlphaOptions with the values from
6160// the Options
6261func (a *AlphaOptions) ExtractFrom(opts *Options) {
63− a.Upstreams = opts.UpstreamServers
62+ a.UpstreamConfig = opts.UpstreamServers
6463 a.InjectRequestHeaders = opts.InjectRequestHeaders
6564 a.InjectResponseHeaders = opts.InjectResponseHeaders
6665 a.Server = opts.Server
pkg/apis/options/legacy_options.go+4−4
@@ -114,13 +114,13 @@ func legacyUpstreamsFlagSet() *pflag.FlagSet {
114114 return flagSet
115115}
116116
117−func (l *LegacyUpstreams) convert() (Upstreams, error) {
118− upstreams := Upstreams{}
117+func (l *LegacyUpstreams) convert() (UpstreamConfig, error) {
118+ upstreams := UpstreamConfig{}
119119
120120 for _, upstreamString := range l.Upstreams {
121121 u, err := url.Parse(upstreamString)
122122 if err != nil {
123− return Upstreams{}, fmt.Errorf("could not parse upstream %q: %v", upstreamString, err)
123+ return UpstreamConfig{}, fmt.Errorf("could not parse upstream %q: %v", upstreamString, err)
124124 }
125125
126126 if u.Path == "" {
@@ -169,7 +169,7 @@ func (l *LegacyUpstreams) convert() (Upstreams, error) {
169169 upstream.FlushInterval = nil
170170 }
171171
172− upstreams.Configs = append(upstreams.Configs, upstream)
172+ upstreams.Upstreams = append(upstreams.Upstreams, upstream)
173173 }
174174
175175 return upstreams, nil
pkg/apis/options/legacy_options_test.go+3−3
@@ -26,8 +26,8 @@ var _ = Describe("Legacy Options", func() {
2626
2727 truth := true
2828 staticCode := 204
29− opts.UpstreamServers = Upstreams{
30− Configs: []Upstream{
29+ opts.UpstreamServers = UpstreamConfig{
30+ Upstreams: []Upstream{
3131 {
3232 ID: "/baz",
3333 Path: "/baz",
@@ -221,7 +221,7 @@ var _ = Describe("Legacy Options", func() {
221221 Expect(err).ToNot(HaveOccurred())
222222 }
223223
224− Expect(upstreams.Configs).To(ConsistOf(in.expectedUpstreams))
224+ Expect(upstreams.Upstreams).To(ConsistOf(in.expectedUpstreams))
225225 },
226226 Entry("with no upstreams", &convertUpstreamsTableInput{
227227 upstreamStrings: []string{},
pkg/apis/options/load_test.go+4−4
@@ -469,8 +469,8 @@ sub:
469469
470470 It("should load a full example AlphaOptions", func() {
471471 config := []byte(`
472−upstreams:
473− configs:
472+upstreamConfig:
473+ upstreams:
474474 - id: httpbin
475475 path: /
476476 uri: http://httpbin
@@ -503,8 +503,8 @@ injectResponseHeaders:
503503 flushInterval := Duration(500 * time.Millisecond)
504504
505505 Expect(into).To(Equal(&AlphaOptions{
506− Upstreams: Upstreams{
507− Configs: []Upstream{
506+ UpstreamConfig: UpstreamConfig{
507+ Upstreams: []Upstream{
508508 {
509509 ID: "httpbin",
510510 Path: "/",
pkg/apis/options/options.go+1−1
@@ -41,7 +41,7 @@ type Options struct {
4141
4242 // Not used in the legacy config, name not allowed to match an external key (upstreams)
4343 // TODO(JoelSpeed): Rename when legacy config is removed
44− UpstreamServers Upstreams `cfg:",internal"`
44+ UpstreamServers UpstreamConfig `cfg:",internal"`
4545
4646 InjectRequestHeaders []Header `cfg:",internal"`
4747 InjectResponseHeaders []Header `cfg:",internal"`
pkg/apis/options/upstreams.go+4−4
@@ -7,15 +7,15 @@ const (
77 DefaultUpstreamFlushInterval = 1 * time.Second
88)
99
10−// Upstreams is a collection of definitions for upstream servers.
11−type Upstreams struct {
10+// UpstreamConfig is a collection of definitions for upstream servers.
11+type UpstreamConfig struct {
1212 // ProxyRawPath will pass the raw url path to upstream allowing for url's
1313 // like: "/%2F/" which would otherwise be redirected to "/"
1414 ProxyRawPath bool `json:"proxyRawPath,omitempty"`
1515
16− // Upstream represents the configuration for an upstream server.
16+ // Upstreams represents the configuration for the upstream servers.
1717 // Requests will be proxied to this upstream if the path matches the request path.
18− Configs []Upstream `json:"configs,omitempty"`
18+ Upstreams []Upstream `json:"upstreams,omitempty"`
1919}
2020
2121// Upstream represents the configuration for an upstream server.
pkg/upstream/proxy.go+2−2
@@ -22,7 +22,7 @@ type ProxyErrorHandler func(http.ResponseWriter, *http.Request, error)
2222
2323// NewProxy creates a new multiUpstreamProxy that can serve requests directed to
2424// multiple upstreams.
25−func NewProxy(upstreams options.Upstreams, sigData *options.SignatureData, writer pagewriter.Writer) (http.Handler, error) {
25+func NewProxy(upstreams options.UpstreamConfig, sigData *options.SignatureData, writer pagewriter.Writer) (http.Handler, error) {
2626 m := &multiUpstreamProxy{
2727 serveMux: mux.NewRouter(),
2828 }
@@ -31,7 +31,7 @@ func NewProxy(upstreams options.Upstreams, sigData *options.SignatureData, write
3131 m.serveMux.UseEncodedPath()
3232 }
3333
34− for _, upstream := range sortByPathLongest(upstreams.Configs) {
34+ for _, upstream := range sortByPathLongest(upstreams.Upstreams) {
3535 if upstream.Static {
3636 if err := m.registerStaticResponseHandler(upstream, writer); err != nil {
3737 return nil, fmt.Errorf("could not register static upstream %q: %v", upstream.ID, err)
pkg/upstream/proxy_test.go+4−4
@@ -20,7 +20,7 @@ var _ = Describe("Proxy Suite", func() {
2020 target string
2121 response testHTTPResponse
2222 upstream string
23− upstreams options.Upstreams
23+ upstreams options.UpstreamConfig
2424 }
2525
2626 Context("multiUpstreamProxy", func() {
@@ -40,8 +40,8 @@ var _ = Describe("Proxy Suite", func() {
4040
4141 // Allows for specifying settings and even individual upstreams for specific tests and uses the default upstreams/configs otherwise
4242 upstreams := in.upstreams
43− if len(in.upstreams.Configs) == 0 {
44− upstreams.Configs = []options.Upstream{
43+ if len(in.upstreams.Upstreams) == 0 {
44+ upstreams.Upstreams = []options.Upstream{
4545 {
4646 ID: "http-backend",
4747 Path: "/http/",
@@ -325,7 +325,7 @@ var _ = Describe("Proxy Suite", func() {
325325 upstream: "",
326326 }),
327327 Entry("containing an escaped '/' with ProxyRawPath", &proxyTableInput{
328− upstreams: options.Upstreams{ProxyRawPath: true},
328+ upstreams: options.UpstreamConfig{ProxyRawPath: true},
329329 target: "http://example.localhost/%2F/test1/%2F/test2",
330330 response: testHTTPResponse{
331331 code: 404,
pkg/validation/options_test.go+1−1
@@ -22,7 +22,7 @@ const (
2222
2323func testOptions() *options.Options {
2424 o := options.NewOptions()
25− o.UpstreamServers.Configs = append(o.UpstreamServers.Configs, options.Upstream{
25+ o.UpstreamServers.Upstreams = append(o.UpstreamServers.Upstreams, options.Upstream{
2626 ID: "upstream",
2727 Path: "/",
2828 URI: "http://127.0.0.1:8080/",
pkg/validation/upstreams.go+2−2
@@ -7,12 +7,12 @@ import (
77 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
88)
99
10−func validateUpstreams(upstreams options.Upstreams) []string {
10+func validateUpstreams(upstreams options.UpstreamConfig) []string {
1111 msgs := []string{}
1212 ids := make(map[string]struct{})
1313 paths := make(map[string]struct{})
1414
15− for _, upstream := range upstreams.Configs {
15+ for _, upstream := range upstreams.Upstreams {
1616 msgs = append(msgs, validateUpstream(upstream, ids, paths)...)
1717 }
1818
pkg/validation/upstreams_test.go+24−24
@@ -11,7 +11,7 @@ import (
1111
1212var _ = Describe("Upstreams", func() {
1313 type validateUpstreamTableInput struct {
14− upstreams options.Upstreams
14+ upstreams options.UpstreamConfig
1515 errStrings []string
1616 }
1717
@@ -54,12 +54,12 @@ var _ = Describe("Upstreams", func() {
5454 Expect(validateUpstreams(o.upstreams)).To(ConsistOf(o.errStrings))
5555 },
5656 Entry("with no upstreams", &validateUpstreamTableInput{
57− upstreams: options.Upstreams{},
57+ upstreams: options.UpstreamConfig{},
5858 errStrings: []string{},
5959 }),
6060 Entry("with valid upstreams", &validateUpstreamTableInput{
61− upstreams: options.Upstreams{
62− Configs: []options.Upstream{
61+ upstreams: options.UpstreamConfig{
62+ Upstreams: []options.Upstream{
6363 validHTTPUpstream,
6464 validStaticUpstream,
6565 validFileUpstream,
@@ -68,8 +68,8 @@ var _ = Describe("Upstreams", func() {
6868 errStrings: []string{},
6969 }),
7070 Entry("with an empty ID", &validateUpstreamTableInput{
71− upstreams: options.Upstreams{
72− Configs: []options.Upstream{
71+ upstreams: options.UpstreamConfig{
72+ Upstreams: []options.Upstream{
7373 {
7474 ID: "",
7575 Path: "/foo",
@@ -80,8 +80,8 @@ var _ = Describe("Upstreams", func() {
8080 errStrings: []string{emptyIDMsg},
8181 }),
8282 Entry("with an empty Path", &validateUpstreamTableInput{
83− upstreams: options.Upstreams{
84− Configs: []options.Upstream{
83+ upstreams: options.UpstreamConfig{
84+ Upstreams: []options.Upstream{
8585 {
8686 ID: "foo",
8787 Path: "",
@@ -92,8 +92,8 @@ var _ = Describe("Upstreams", func() {
9292 errStrings: []string{emptyPathMsg},
9393 }),
9494 Entry("with an empty Path", &validateUpstreamTableInput{
95− upstreams: options.Upstreams{
96− Configs: []options.Upstream{
95+ upstreams: options.UpstreamConfig{
96+ Upstreams: []options.Upstream{
9797 {
9898 ID: "foo",
9999 Path: "",
@@ -104,8 +104,8 @@ var _ = Describe("Upstreams", func() {
104104 errStrings: []string{emptyPathMsg},
105105 }),
106106 Entry("with an empty URI", &validateUpstreamTableInput{
107− upstreams: options.Upstreams{
108− Configs: []options.Upstream{
107+ upstreams: options.UpstreamConfig{
108+ Upstreams: []options.Upstream{
109109 {
110110 ID: "foo",
111111 Path: "/foo",
@@ -116,8 +116,8 @@ var _ = Describe("Upstreams", func() {
116116 errStrings: []string{emptyURIMsg},
117117 }),
118118 Entry("with an invalid URI", &validateUpstreamTableInput{
119− upstreams: options.Upstreams{
120− Configs: []options.Upstream{
119+ upstreams: options.UpstreamConfig{
120+ Upstreams: []options.Upstream{
121121 {
122122 ID: "foo",
123123 Path: "/foo",
@@ -128,8 +128,8 @@ var _ = Describe("Upstreams", func() {
128128 errStrings: []string{invalidURIMsg},
129129 }),
130130 Entry("with an invalid URI scheme", &validateUpstreamTableInput{
131− upstreams: options.Upstreams{
132− Configs: []options.Upstream{
131+ upstreams: options.UpstreamConfig{
132+ Upstreams: []options.Upstream{
133133 {
134134 ID: "foo",
135135 Path: "/foo",
@@ -140,8 +140,8 @@ var _ = Describe("Upstreams", func() {
140140 errStrings: []string{invalidURISchemeMsg},
141141 }),
142142 Entry("with a static upstream and invalid optons", &validateUpstreamTableInput{
143− upstreams: options.Upstreams{
144− Configs: []options.Upstream{
143+ upstreams: options.UpstreamConfig{
144+ Upstreams: []options.Upstream{
145145 {
146146 ID: "foo",
147147 Path: "/foo",
@@ -163,8 +163,8 @@ var _ = Describe("Upstreams", func() {
163163 },
164164 }),
165165 Entry("with duplicate IDs", &validateUpstreamTableInput{
166− upstreams: options.Upstreams{
167− Configs: []options.Upstream{
166+ upstreams: options.UpstreamConfig{
167+ Upstreams: []options.Upstream{
168168 {
169169 ID: "foo",
170170 Path: "/foo1",
@@ -180,8 +180,8 @@ var _ = Describe("Upstreams", func() {
180180 errStrings: []string{multipleIDsMsg},
181181 }),
182182 Entry("with duplicate Paths", &validateUpstreamTableInput{
183− upstreams: options.Upstreams{
184− Configs: []options.Upstream{
183+ upstreams: options.UpstreamConfig{
184+ Upstreams: []options.Upstream{
185185 {
186186 ID: "foo1",
187187 Path: "/foo",
@@ -197,8 +197,8 @@ var _ = Describe("Upstreams", func() {
197197 errStrings: []string{multiplePathsMsg},
198198 }),
199199 Entry("when a static code is supplied without static", &validateUpstreamTableInput{
200− upstreams: options.Upstreams{
201− Configs: []options.Upstream{
200+ upstreams: options.UpstreamConfig{
201+ Upstreams: []options.Upstream{
202202 {
203203 ID: "foo",
204204 Path: "/foo",