From eae20b1a1bce8ba699a8458a527f444df366d4c1 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 30 Mar 2021 15:15:42 +0200 Subject: [PATCH] client: extract FromEnv parts to separate WithXX options Implements three options; - WithTLSClientConfigFromEnv() - WithHostFromEnv() - WithVersionFromEnv() Signed-off-by: Sebastiaan van Stijn --- client/options.go | 87 ++++++++++++++++++++++++++++++------------ client/options_test.go | 38 ++++++++++++++++++ 2 files changed, 100 insertions(+), 25 deletions(-) diff --git a/client/options.go b/client/options.go index 6f77f0955f..77a9abc141 100644 --- a/client/options.go +++ b/client/options.go @@ -24,32 +24,13 @@ type Opt func(*Client) error // DOCKER_CERT_PATH to load the TLS certificates from. // DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default. func FromEnv(c *Client) error { - if dockerCertPath := os.Getenv("DOCKER_CERT_PATH"); dockerCertPath != "" { - options := tlsconfig.Options{ - CAFile: filepath.Join(dockerCertPath, "ca.pem"), - CertFile: filepath.Join(dockerCertPath, "cert.pem"), - KeyFile: filepath.Join(dockerCertPath, "key.pem"), - InsecureSkipVerify: os.Getenv("DOCKER_TLS_VERIFY") == "", - } - tlsc, err := tlsconfig.Client(options) - if err != nil { - return err - } - - c.client = &http.Client{ - Transport: &http.Transport{TLSClientConfig: tlsc}, - CheckRedirect: CheckRedirect, - } + ops := []Opt{ + WithTLSClientConfigFromEnv(), + WithHostFromEnv(), + WithVersionFromEnv(), } - - if host := os.Getenv("DOCKER_HOST"); host != "" { - if err := WithHost(host)(c); err != nil { - return err - } - } - - if version := os.Getenv("DOCKER_API_VERSION"); version != "" { - if err := WithVersion(version)(c); err != nil { + for _, op := range ops { + if err := op(c); err != nil { return err } } @@ -93,6 +74,18 @@ func WithHost(host string) Opt { } } +// WithHostFromEnv overrides the client host with the host specified in the +// DOCKER_HOST environment variable. If DOCKER_HOST is not set, the host is +// not modified. +func WithHostFromEnv() Opt { + return func(c *Client) error { + if host := os.Getenv("DOCKER_HOST"); host != "" { + return WithHost(host)(c) + } + return nil + } +} + // WithHTTPClient overrides the client http client with the specified one func WithHTTPClient(client *http.Client) Opt { return func(c *Client) error { @@ -148,6 +141,38 @@ func WithTLSClientConfig(cacertPath, certPath, keyPath string) Opt { } } +// WithTLSClientConfigFromEnv configures the client's TLS settings with the +// settings in the DOCKER_CERT_PATH and DOCKER_TLS_VERIFY environment variables. +// If DOCKER_CERT_PATH is not set or empty, TLS configuration is not modified. +// +// Supported environment variables: +// DOCKER_CERT_PATH directory to load the TLS certificates (ca.pem, cert.pem, key.pem) from. +// DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default. +func WithTLSClientConfigFromEnv() Opt { + return func(c *Client) error { + dockerCertPath := os.Getenv("DOCKER_CERT_PATH") + if dockerCertPath == "" { + return nil + } + options := tlsconfig.Options{ + CAFile: filepath.Join(dockerCertPath, "ca.pem"), + CertFile: filepath.Join(dockerCertPath, "cert.pem"), + KeyFile: filepath.Join(dockerCertPath, "key.pem"), + InsecureSkipVerify: os.Getenv("DOCKER_TLS_VERIFY") == "", + } + tlsc, err := tlsconfig.Client(options) + if err != nil { + return err + } + + c.client = &http.Client{ + Transport: &http.Transport{TLSClientConfig: tlsc}, + CheckRedirect: CheckRedirect, + } + return nil + } +} + // WithVersion overrides the client version with the specified one. If an empty // version is specified, the value will be ignored to allow version negotiation. func WithVersion(version string) Opt { @@ -160,6 +185,18 @@ func WithVersion(version string) Opt { } } +// WithVersionFromEnv overrides the client version with the version specified in +// the DOCKER_API_VERSION environment variable. If DOCKER_API_VERSION is not set, +// the version is not modified. +func WithVersionFromEnv() Opt { + return func(c *Client) error { + if version := os.Getenv("DOCKER_API_VERSION"); version != "" { + return WithVersion(version)(c) + } + return nil + } +} + // WithAPIVersionNegotiation enables automatic API version negotiation for the client. // With this option enabled, the client automatically negotiates the API version // to use when making requests. API version negotiation is performed on the first diff --git a/client/options_test.go b/client/options_test.go index e853357123..b4ea0902e6 100644 --- a/client/options_test.go +++ b/client/options_test.go @@ -4,9 +4,31 @@ import ( "testing" "time" + "github.com/docker/docker/api" "gotest.tools/v3/assert" + "gotest.tools/v3/env" ) +func TestOptionWithHostFromEnv(t *testing.T) { + c, err := NewClientWithOpts(WithHostFromEnv()) + assert.NilError(t, err) + assert.Check(t, c.client != nil) + assert.Equal(t, c.host, DefaultDockerHost) + assert.Equal(t, c.proto, defaultProto) + assert.Equal(t, c.addr, defaultAddr) + assert.Equal(t, c.basePath, "") + + defer env.Patch(t, "DOCKER_HOST", "tcp://foo.example.com:2376/test/")() + + c, err = NewClientWithOpts(WithHostFromEnv()) + assert.NilError(t, err) + assert.Check(t, c.client != nil) + assert.Equal(t, c.host, "tcp://foo.example.com:2376/test/") + assert.Equal(t, c.proto, "tcp") + assert.Equal(t, c.addr, "foo.example.com:2376") + assert.Equal(t, c.basePath, "/test/") +} + func TestOptionWithTimeout(t *testing.T) { timeout := 10 * time.Second c, err := NewClientWithOpts(WithTimeout(timeout)) @@ -14,3 +36,19 @@ func TestOptionWithTimeout(t *testing.T) { assert.Check(t, c.client != nil) assert.Equal(t, c.client.Timeout, timeout) } + +func TestOptionWithVersionFromEnv(t *testing.T) { + c, err := NewClientWithOpts(WithVersionFromEnv()) + assert.NilError(t, err) + assert.Check(t, c.client != nil) + assert.Equal(t, c.version, api.DefaultVersion) + assert.Equal(t, c.manualOverride, false) + + defer env.Patch(t, "DOCKER_API_VERSION", "2.9999")() + + c, err = NewClientWithOpts(WithVersionFromEnv()) + assert.NilError(t, err) + assert.Check(t, c.client != nil) + assert.Equal(t, c.version, "2.9999") + assert.Equal(t, c.manualOverride, true) +}