Skip to content

Add --no-proxy option#8201

Merged
wjun merged 1 commit intovmware:masterfrom
wjun:proxy
Aug 31, 2018
Merged

Add --no-proxy option#8201
wjun merged 1 commit intovmware:masterfrom
wjun:proxy

Conversation

@wjun
Copy link
Contributor

@wjun wjun commented Aug 10, 2018

The previous vic-machine proxy configuration behaviour has an issue in that http and https settings will overwrite each other if we only configure one each time. This issue has been fixed as well.

Fixes #8144

@wjun wjun requested a review from a team as a code owner August 10, 2018 06:44
@wjun wjun requested review from hickeng and zjs August 10, 2018 06:47
@wjun wjun force-pushed the proxy branch 3 times, most recently from 710ec58 to c3b156c Compare August 10, 2018 14:25
}
}

noproxy = p.NoProxy
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like we should validate this user input and add unit tests for that validation logic.

We may also need unit or integration tests to ensure that we handle edge cases properly, like a user specifying a --no-proxy setting without specifying either proxy.

Copy link
Contributor Author

@wjun wjun Aug 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The snippet below is how Golang http/transport process no_proxy value, and it is kind of fault tolerant.

for _, p := range strings.Split(no_proxy, ",") {
                p = strings.ToLower(strings.TrimSpace(p))
                if len(p) == 0 {
                        continue
                }
                if hasPort(p) {
                        p = p[:strings.LastIndex(p, ":")]
                }
                if addr == p {
                        return false
                }
                if len(p) == 0 {
                        // There is no host part, likely the entry is malformed; ignore.
                        continue
                }
                if p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:]) {
                        // no_proxy ".foo.com" matches "bar.foo.com" or "foo.com"
                        return false
                }
                if p[0] != '.' && strings.HasSuffix(addr, p) && addr[len(addr)-len(p)-1] == '.' {
                        // no_proxy "foo.com" matches "bar.foo.com"
                        return false
                }
        }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wjun I think what @zjs is talking about is less the edge case parsing of the contents of the no-proxy option, but whether the no-proxy option is valid at all. If the user has not specified any proxies then setting no-proxy is likely a mistake.

Copy link
Contributor Author

@wjun wjun Aug 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand the point, but they are not necessarily set together, as we do when setting them through env variables. I also wonder if we can remove the field of proxies.IsSet field which looks redundant.

updateSessionEnv(vicAdminSession, config.VICAdminHTTPSProxy, sProxy)
}
nProxy := ""
if c.NoProxy != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since proxy exclusions don't make sense if proxies haven't been configured, it seems like this should be nested under the c.proxies.IsSet block.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought maybe we can align with other no_proxy settings which are not only for http protocol but also for other protocols, though we only use http protocols for now.

hProxy := ""
if c.HTTPProxy != nil {
hProxy = c.HTTPProxy.String()
updateSessionEnv(personaSession, config.GeneralHTTPProxy, hProxy)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This restructuring fixes one issue as noted in the commit message, but looks like it may introduce another: it no longer seems possible to unset a previously-configured proxy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Fixed the regression.

"format": "uri"
},
"no_proxy": {
"type": "string"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like this should take an array of URIs; we use "rich" data types in the API even if they're not feasible for the CLI. (This will require some changes to the API handler logic.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the snippet above, no_proxy includes not only URIs, but also domain names.

Copy link
Contributor

@hickeng hickeng Aug 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I recall correctly a URI should be able to encode a protocol-relative location (e.g //f.q.d.n) - I'd suggest seeing whether the url package encodes correctly when omitting the protocol (a sanity check in playground says it does). I believe you already test this in the unit test, but maybe not with the actual protocol relative form.

Regardless the data in this option is expected to be an array of valid URIs so we should define it as such.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, URIs can parse '*' or domain names too. I just updated the type to URI array.

"format": "uri"
},
"no_proxy": {
"type": "string"
Copy link
Contributor

@hickeng hickeng Aug 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I recall correctly a URI should be able to encode a protocol-relative location (e.g //f.q.d.n) - I'd suggest seeing whether the url package encodes correctly when omitting the protocol (a sanity check in playground says it does). I believe you already test this in the unit test, but maybe not with the actual protocol relative form.

Regardless the data in this option is expected to be an array of valid URIs so we should define it as such.

}
}

noproxy = p.NoProxy
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wjun I think what @zjs is talking about is less the edge case parsing of the contents of the no-proxy option, but whether the no-proxy option is valid at all. If the user has not specified any proxies then setting no-proxy is likely a mistake.

}
for _, env := range persona.Cmd.Env {
if !strings.HasPrefix(env, httpProxy) && !strings.HasPrefix(env, httpsProxy) {
if !strings.HasPrefix(env, httpProxy) && !strings.HasPrefix(env, httpsProxy) && !strings.HasPrefix(env, noProxy) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is likely much neater if using switch - something like:

for _, env := range persona.Cmd.Env {
  val := strings.Split(env, "=")
  switch {
    case strings.HasPrefix(env, httpProxy):
      ...
    case strings.HasPrefix(env, httpsProxy):
      ...
    case strings.HasPrefix(env, noProxy):
      ...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion. Just refactored to use switch.

@wjun wjun force-pushed the proxy branch 3 times, most recently from afeffe8 to c1fb11e Compare August 15, 2018 07:18
Copy link
Member

@zjs zjs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also ensure the API change is covered by an integration test.

assert.NoError(t, err, "Expected %s and %s to be accepted", ghttp, ghttps)
assert.True(t, gproxy.IsSet, "Expected proxy to be marked as set")
_, _, nproxy, err := gproxy.ProcessProxies()
assert.NoError(t, err, "Expected %s, %s, %s and %s to be accepted", ghttp, ghttps, uri, nproxy)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like this will print the --no-proxy values twice: once from uri and once from nproxy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added uri and nproxy for comparison. nproxy will trim whitespaces of each uri.

}

gnproxies := [...]string{
"*",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this actually a valid value? If so, perhaps it should be mentioned in the help text (which currently says "This should be a comma-separated list of hostnames, domain names, or a mixture of both"),

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Just updated the information.


gnproxies := [...]string{
"*",
"localhost, .example.com",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should also include a test case without a space. (E.g., "localhost,.example.com")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

cli.GenericFlag{
Name: "no-proxy",
Value: flags.NewOptionalString(&p.NoProxy),
Usage: "URLs that should be excluded from proxying. This should be a comma-separated list of hostnames, domain names, or a mixture of both",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the example below, you have ".example.com". Presumably this means example.com and all sub-domains. It's not clear from this help output that this would be accepted. Perhaps @stuclem can suggest a concise way to explain this.

func FromImageFetchProxy(p *models.VCHRegistryImageFetchProxy) common.Proxies {
http := string(p.HTTP)
https := string(p.HTTPS)
var nproxy *string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be consistent with the other functions in this package (where each function converts one object), this should probably be extracted into its own function (e.g., fromProxyExclusionList).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

noproxy is defined as one field of VCHRegistryImageFetchProxy, so I process it here.

buffer.WriteString(",")
buffer.WriteString(string(v))
}
nproxyStr := buffer.String()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could be more clearly written as strings.Join(p.NoProxy, ",")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried initially, but looks p.NoProxy is []strfmt.URL type, and strings.Join does not accept it?

if strings.HasPrefix(env, https+"=") {
httpsProxy = strfmt.URI(strings.SplitN(env, "=", 2)[1])
}
if strings.HasPrefix(env, nproxy+"=") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like config_to_data.go (#8201 (comment)), I think this would be clearer if it used a switch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Done.

nProxyStrs := strings.Split(strings.SplitN(env, "=", 2)[1], ",")
nProxy = make([]strfmt.URI, len(nProxyStrs))
for i := range nProxy {
nProxy[i] = strfmt.URI(strings.TrimSpace(nProxyStrs[i]))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be consistent with the other functions in this package (where each function converts one object), this should probably be extracted into its own function (e.g., asProxyExclusionList).

GeneralNoProxy = "NO_PROXY"
VICAdminHTTPProxy = "VICADMIN_HTTP_PROXY"
VICAdminHTTPSProxy = "VICADMIN_HTTPS_PROXY"
VICAdminNoProxy = "NO_PROXY"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems inconsistent with VICAdminHTTPProxy and VICAdminHTTPSProxy. Should this be VICADMIN_NO_PROXY?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NO_PROXY is used inside http.transport, so I need to put that env name

client.Transport = &http.Transport{Proxy: http.ProxyURL(url)}

@wjun
Copy link
Contributor Author

wjun commented Aug 18, 2018

We should also ensure the API change is covered by an integration test.
It is covered in vch_create_test.go.

@zjs zjs assigned wjun Aug 28, 2018
@wjun wjun merged commit 45a545a into vmware:master Aug 31, 2018
zjs pushed a commit to zjs/vic that referenced this pull request Sep 1, 2018
(cherry picked from commit 45a545a)

Conflicts:
	lib/apiservers/service/restapi/handlers/decode/networking.go
	lib/apiservers/service/restapi/handlers/encode/networking.go
	lib/apiservers/service/restapi/handlers/vch_get.go
zjs pushed a commit to zjs/vic that referenced this pull request Sep 1, 2018
(cherry picked from commit 45a545a)

Conflicts:
	lib/apiservers/service/restapi/handlers/decode/networking.go
	lib/apiservers/service/restapi/handlers/encode/networking.go
	lib/apiservers/service/restapi/handlers/vch_get.go
wjun added a commit to wjun/vic that referenced this pull request Sep 3, 2018
(cherry picked from commit 45a545a)
wjun added a commit that referenced this pull request Sep 3, 2018
(cherry picked from commit 45a545a)

Conflicts:
deleted by us: lib/apiservers/service/restapi/handlers/decode/networking.go
deleted by us: lib/apiservers/service/restapi/handlers/encode/networking.go
both modified: lib/apiservers/service/restapi/handlers/vch_get.go
modified: lib/apiservers/service/restapi/handlers/vch_create.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a --no-proxy option for the VCH

5 participants