Skip to content

Commit

Permalink
support extracting proxy info from docker config
Browse files Browse the repository at this point in the history
  • Loading branch information
BenTheElder committed Dec 18, 2019
1 parent ab2dcbb commit 29fcc0c
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 19 deletions.
5 changes: 4 additions & 1 deletion pkg/cluster/internal/providers/docker/provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,10 @@ func runArgsForLoadBalancer(cfg *config.Cluster, name string, args []string) []s
}

func getProxyEnv(cfg *config.Cluster) (map[string]string, error) {
envs := common.GetProxyEnvs(cfg)
envs, err := common.GetProxyEnvs(cfg)
if err != nil {
return nil, err
}
// Specifically add the docker network subnets to NO_PROXY if we are using a proxy
if len(envs) > 0 {
// Docker default bridge network is named "bridge" (https://docs.docker.com/network/bridge/#use-the-default-bridge-network)
Expand Down
53 changes: 49 additions & 4 deletions pkg/cluster/internal/providers/provider/common/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import (
"os"
"strings"

"sigs.k8s.io/kind/pkg/errors"
"sigs.k8s.io/kind/pkg/exec"

"sigs.k8s.io/kind/pkg/internal/apis/config"
)

Expand All @@ -34,11 +37,11 @@ const (

// GetProxyEnvs returns a map of proxy environment variables to their values
// If proxy settings are set, NO_PROXY is modified to include the cluster subnets
func GetProxyEnvs(cfg *config.Cluster) map[string]string {
return getProxyEnvs(cfg, os.Getenv)
func GetProxyEnvs(cfg *config.Cluster) (map[string]string, error) {
return getProxyEnvs(cfg, os.Getenv, exec.DefaultCmder)
}

func getProxyEnvs(cfg *config.Cluster, getEnv func(string) string) map[string]string {
func getProxyEnvs(cfg *config.Cluster, getEnv func(string) string, cmder exec.Cmder) (map[string]string, error) {
envs := make(map[string]string)
for _, name := range []string{HTTPProxy, HTTPSProxy, NOProxy} {
val := getEnv(name)
Expand All @@ -50,6 +53,18 @@ func getProxyEnvs(cfg *config.Cluster, getEnv func(string) string) map[string]st
envs[strings.ToLower(name)] = val
}
}

// if we didn't detect proxy environment variable settings, probe docker
// we prefer the proxy env to allow more explicit per-invocation override
// of proxy settings
if len(envs) < 1 {
e, err := getProxyEnvFromDocker(cmder)
if err != nil {
return nil, err
}
envs = e
}

// Specifically add the cluster subnets to NO_PROXY if we are using a proxy
if len(envs) > 0 {
noProxy := envs[NOProxy]
Expand All @@ -60,5 +75,35 @@ func getProxyEnvs(cfg *config.Cluster, getEnv func(string) string) map[string]st
envs[NOProxy] = noProxy
envs[strings.ToLower(NOProxy)] = noProxy
}
return envs
return envs, nil
}

// obtains proxy information from docker via docker info against cmder
func getProxyEnvFromDocker(cmder exec.Cmder) (map[string]string, error) {
// get raw fields
lines, err := exec.OutputLines(
cmder.Command(
"docker", "info",
"--format",
// one per line, upper(key)=value
"HTTP_PROXY={{.HTTPSProxy}}\nHTTPS_PROXY={{.HTTPSProxy}}\nNO_PROXY={{.NoProxy}}",
),
)
if err != nil {
return nil, errors.Wrap(err, "failed to get proxy information from docker")
}
// parse out environment settings from the format we specified
envs := make(map[string]string)
for _, line := range lines {
parts := strings.SplitN(line, "=", 2)
if len(parts) != 2 {
return nil, errors.Errorf("failed to parse docker proxy information %q", line)
}
name, val := parts[0], parts[1]
if val != "" {
envs[name] = val
envs[strings.ToLower(name)] = val
}
}
return envs, nil
}
70 changes: 56 additions & 14 deletions pkg/cluster/internal/providers/provider/common/proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,27 @@ limitations under the License.
package common

import (
"errors"
"testing"

"sigs.k8s.io/kind/pkg/exec"

"sigs.k8s.io/kind/pkg/internal/apis/config"
"sigs.k8s.io/kind/pkg/internal/assert"
)

func TestGetProxyEnvs(t *testing.T) {
t.Parallel()
// first test the public method
cfg := &config.Cluster{}
config.SetDefaultsCluster(cfg)
envs := GetProxyEnvs(cfg)
// GetProxyEnvs should always reutrn a valid map
if envs == nil {
t.Errorf("GetProxyEnvs returned nil but should not")
}

// now test the internal one (with all of the logic)
cases := []struct {
name string
cluster *config.Cluster
env map[string]string
want map[string]string
name string
cluster *config.Cluster
env map[string]string
dockerInfoOut string
dockerInfoErr error
want map[string]string
expectError bool
}{
{
name: "No environment variables",
Expand All @@ -51,6 +49,17 @@ func TestGetProxyEnvs(t *testing.T) {
}(),
want: map[string]string{},
},
{
name: "No environment variables, but docker config",
cluster: func() *config.Cluster {
c := config.Cluster{}
c.Networking.ServiceSubnet = "10.0.0.0/24"
c.Networking.PodSubnet = "12.0.0.0/24"
return &c
}(),
dockerInfoOut: "HTTP_PROXY=5.5.5.5\nHTTPS_PROXY=5.5.5.5\nNO_PROXY=localhost",
want: map[string]string{"HTTPS_PROXY": "5.5.5.5", "https_proxy": "5.5.5.5", "HTTP_PROXY": "5.5.5.5", "http_proxy": "5.5.5.5", "NO_PROXY": "localhost,10.0.0.0/24,12.0.0.0/24", "no_proxy": "localhost,10.0.0.0/24,12.0.0.0/24"},
},
{
name: "HTTP_PROXY environment variables",
cluster: func() *config.Cluster {
Expand Down Expand Up @@ -91,17 +100,50 @@ func TestGetProxyEnvs(t *testing.T) {
},
want: map[string]string{"HTTPS_PROXY": "5.5.5.5", "https_proxy": "5.5.5.5", "NO_PROXY": "8.8.8.8,10.0.0.0/24,12.0.0.0/24", "no_proxy": "8.8.8.8,10.0.0.0/24,12.0.0.0/24"},
},
{
name: "Invalid docker config",
cluster: func() *config.Cluster {
c := config.Cluster{}
c.Networking.ServiceSubnet = "10.0.0.0/24"
c.Networking.PodSubnet = "12.0.0.0/24"
return &c
}(),
dockerInfoOut: ".....",
expectError: true,
},
{
name: "Failed to exec docker info",
cluster: func() *config.Cluster {
c := config.Cluster{}
c.Networking.ServiceSubnet = "10.0.0.0/24"
c.Networking.PodSubnet = "12.0.0.0/24"
return &c
}(),
dockerInfoErr: errors.New("error"),
expectError: true,
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
result := getProxyEnvs(tc.cluster, func(e string) string {
// fake out getting env
getEnvFake := func(e string) string {
if tc.env == nil {
return ""
}
return tc.env[e]
})
}
// fake out docker info --format ...
getProxyEnvFromDockerFake := &exec.FakeCmder{
FakeCmd: exec.FakeCmd{
Out: []byte(tc.dockerInfoOut),
Error: tc.dockerInfoErr,
},
}
// actuall test
result, err := getProxyEnvs(tc.cluster, getEnvFake, getProxyEnvFromDockerFake)
assert.ExpectError(t, tc.expectError, err)
assert.DeepEqual(t, tc.want, result)
})
}
Expand Down

0 comments on commit 29fcc0c

Please sign in to comment.