feat: support tls with context config

This commit is contained in:
soliujing 2026-01-20 03:18:48 +00:00
parent 577797d9ed
commit bcd2807aa7
2 changed files with 99 additions and 8 deletions

View file

@ -78,11 +78,75 @@ func (c *DockerCommand) NewCommandObject(obj CommandObject) CommandObject {
// API version negotiation to support older Docker daemons. // API version negotiation to support older Docker daemons.
// See https://github.com/jesseduffield/lazydocker/issues/715 // See https://github.com/jesseduffield/lazydocker/issues/715
func newDockerClient(dockerHost string) (*client.Client, error) { func newDockerClient(dockerHost string) (*client.Client, error) {
return client.NewClientWithOpts( opts := []client.Opt{
client.WithTLSClientConfigFromEnv(),
client.WithAPIVersionNegotiation(), client.WithAPIVersionNegotiation(),
client.WithHost(dockerHost), client.WithHost(dockerHost),
}
// Load TLS config from environment variables (for backward compatibility)
tlsOpts := []client.Opt{client.WithTLSClientConfigFromEnv()}
// Also try to load TLS config from the current Docker context
contextTLSOpts, err := getContextTLSOptions()
if err == nil && contextTLSOpts != nil {
// If we have context TLS config, use it instead of env config
tlsOpts = contextTLSOpts
}
opts = append(opts, tlsOpts...)
return client.NewClientWithOpts(opts...)
}
// getContextTLSOptions loads TLS configuration from the current Docker context
func getContextTLSOptions() ([]client.Opt, error) {
currentContext := os.Getenv("DOCKER_CONTEXT")
if currentContext == "" {
cf, err := cliconfig.Load(cliconfig.Dir())
if err != nil {
return nil, err
}
currentContext = cf.CurrentContext
}
// Skip if no context or using default context
if currentContext == "" || currentContext == "default" {
return nil, nil
}
storeConfig := ctxstore.NewConfig(
func() interface{} { return &ddocker.EndpointMeta{} },
ctxstore.EndpointTypeGetter(ddocker.DockerEndpoint, func() interface{} { return &ddocker.EndpointMeta{} }),
) )
st := ctxstore.New(cliconfig.ContextStoreDir(), storeConfig)
// First load the endpoint metadata
md, err := st.GetMetadata(currentContext)
if err != nil {
// Context doesn't exist or can't be read - not an error for our use case
return nil, nil
}
dockerEP, ok := md.Endpoints[ddocker.DockerEndpoint]
if !ok {
return nil, nil // No docker endpoint in this context
}
dockerEPMeta, ok := dockerEP.(ddocker.EndpointMeta)
if !ok {
return nil, fmt.Errorf("expected docker.EndpointMeta, got %T", dockerEP)
}
// Now load the endpoint with TLS data
endpoint, err := ddocker.WithTLSData(st, currentContext, dockerEPMeta)
if err != nil {
// If we can't load TLS data, that's okay - just return nil
return nil, nil
}
// Get client options from the endpoint (includes TLS config)
return endpoint.ClientOpts()
} }
// NewDockerCommand it runs docker commands // NewDockerCommand it runs docker commands

View file

@ -53,11 +53,38 @@ func TestNewDockerClientVersionNegotiation(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
defer cli.Close() defer cli.Close()
// Version is NOT locked to the env var value (1.25). // Version should still be negotiable (not locked to env var)
// Instead, it uses the library's default version and will negotiate assert.NotEqual(t, "1.25", cli.ClientVersion())
// with the server on first request. This is the key difference that
// fixes the "version too old" error.
assert.NotEqual(t, "1.25", cli.ClientVersion(),
"client version should not be locked to DOCKER_API_VERSION env var")
}) })
} }
// TestGetContextTLSOptions tests that TLS options can be loaded from Docker contexts
func TestGetContextTLSOptions(t *testing.T) {
// Save original env vars and restore after test
originalContext := os.Getenv("DOCKER_CONTEXT")
defer func() {
if originalContext == "" {
os.Unsetenv("DOCKER_CONTEXT")
} else {
os.Setenv("DOCKER_CONTEXT", originalContext)
}
}()
// Test with no context set (should return nil)
os.Unsetenv("DOCKER_CONTEXT")
opts, err := getContextTLSOptions()
assert.NoError(t, err)
assert.Nil(t, opts)
// Test with default context (should return nil)
os.Setenv("DOCKER_CONTEXT", "default")
opts, err = getContextTLSOptions()
assert.NoError(t, err)
assert.Nil(t, opts)
// Test with non-existent context (should return nil, not error)
os.Setenv("DOCKER_CONTEXT", "nonexistent-context")
opts, err = getContextTLSOptions()
assert.NoError(t, err)
assert.Nil(t, opts)
}