mirror of
https://github.com/kreuzwerker/terraform-provider-docker.git
synced 2025-12-18 23:06:10 -05:00
feat: Implement support for docker context (#704)
This commit is contained in:
parent
e47077458c
commit
54649a1909
3 changed files with 113 additions and 1 deletions
|
|
@ -154,6 +154,7 @@ provider "docker" {
|
|||
- `ca_material` (String) PEM-encoded content of Docker host CA certificate
|
||||
- `cert_material` (String) PEM-encoded content of Docker client certificate
|
||||
- `cert_path` (String) Path to directory with Docker TLS config
|
||||
- `context` (String) The name of the Docker context to use. Can also be set via `DOCKER_CONTEXT` environment variable. Overrides the `host` if set.
|
||||
- `disable_docker_daemon_check` (Boolean) If set to `true`, the provider will not check if the Docker daemon is running. This is useful for resources/data_sourcess that do not require a running Docker daemon, such as the data source `docker_registry_image`.
|
||||
- `host` (String) The Docker daemon address
|
||||
- `key_material` (String) PEM-encoded content of Docker client private key
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package provider
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
|
|
@ -53,6 +54,12 @@ func New(version string) func() *schema.Provider {
|
|||
},
|
||||
Description: "The Docker daemon address",
|
||||
},
|
||||
"context": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("DOCKER_CONTEXT", ""),
|
||||
Description: "The name of the Docker context to use. Can also be set via `DOCKER_CONTEXT` environment variable. Overrides the `host` if set.",
|
||||
},
|
||||
"ssh_opts": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
|
|
@ -178,13 +185,29 @@ func New(version string) func() *schema.Provider {
|
|||
|
||||
func configure(version string, p *schema.Provider) func(context.Context, *schema.ResourceData) (interface{}, diag.Diagnostics) {
|
||||
return func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {
|
||||
var host string
|
||||
if contextName := d.Get("context").(string); contextName != "" {
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
return nil, diag.Errorf("Could not determine current user. We don't know what the homedir is to look for docker contexts: %v", err)
|
||||
}
|
||||
log.Printf("[DEBUG] Homedir %s", usr.HomeDir)
|
||||
contextHost, err := getContextHost(contextName, usr.HomeDir)
|
||||
if err != nil {
|
||||
return nil, diag.Errorf("Error loading Docker context '%s': %s", contextName, err)
|
||||
}
|
||||
host = contextHost
|
||||
} else {
|
||||
host = d.Get("host").(string)
|
||||
}
|
||||
|
||||
SSHOptsI := d.Get("ssh_opts").([]interface{})
|
||||
SSHOpts := make([]string, len(SSHOptsI))
|
||||
for i, s := range SSHOptsI {
|
||||
SSHOpts[i] = s.(string)
|
||||
}
|
||||
config := Config{
|
||||
Host: d.Get("host").(string),
|
||||
Host: host,
|
||||
SSHOpts: SSHOpts,
|
||||
Ca: d.Get("ca_material").(string),
|
||||
Cert: d.Get("cert_material").(string),
|
||||
|
|
@ -229,6 +252,45 @@ func configure(version string, p *schema.Provider) func(context.Context, *schema
|
|||
}
|
||||
}
|
||||
|
||||
func getContextHost(contextName string, homedir string) (string, error) {
|
||||
contextsDir := fmt.Sprintf("%s/.docker/contexts/meta", homedir)
|
||||
files, err := os.ReadDir(contextsDir)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not read contexts directory: %v", err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
metaFilePath := fmt.Sprintf("%s/%s/meta.json", contextsDir, file.Name())
|
||||
metaFile, err := os.Open(metaFilePath)
|
||||
if err != nil {
|
||||
log.Printf("[DEBUG] Skipping file %s due to error: %v", metaFilePath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
var meta struct {
|
||||
Name string `json:"Name"`
|
||||
Endpoints map[string]struct {
|
||||
Host string `json:"Host"`
|
||||
} `json:"Endpoints"`
|
||||
}
|
||||
err = json.NewDecoder(metaFile).Decode(&meta)
|
||||
// Ensure the file is closed immediately after reading
|
||||
metaFile.Close() // nolint:errcheck
|
||||
if err != nil {
|
||||
log.Printf("[DEBUG] Skipping file %s due to JSON parsing error: %v", metaFilePath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if meta.Name == contextName {
|
||||
if endpoint, ok := meta.Endpoints["docker"]; ok {
|
||||
return endpoint.Host, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("context '%s' not found", contextName)
|
||||
}
|
||||
|
||||
// AuthConfigs represents authentication options to use for the
|
||||
// PushImage method accommodating the new X-Registry-Config header
|
||||
type AuthConfigs struct {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package provider
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
|
@ -110,6 +111,54 @@ func testAccPreCheck(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGetContextHost_ValidContext(t *testing.T) {
|
||||
// Create a temporary directory to simulate Docker contexts
|
||||
tempDir := t.TempDir()
|
||||
contextName := "test-context"
|
||||
contextUUID := "1234-5678-91011"
|
||||
contextFilePath := fmt.Sprintf("%s/.docker/contexts/meta/%s/meta.json", tempDir, contextUUID)
|
||||
|
||||
// Simulate a valid Docker context file
|
||||
contextData := `{
|
||||
"Name": "test-context",
|
||||
"Endpoints": {
|
||||
"docker": {
|
||||
"Host": "tcp://docker:2375"
|
||||
}
|
||||
}
|
||||
}`
|
||||
if err := os.MkdirAll(fmt.Sprintf("%s/.docker/contexts/meta/%s", tempDir, contextUUID), 0755); err != nil {
|
||||
t.Fatalf("Failed to create context directory: %s", err)
|
||||
}
|
||||
if err := os.WriteFile(contextFilePath, []byte(contextData), 0644); err != nil {
|
||||
t.Fatalf("Failed to write context file: %s", err)
|
||||
}
|
||||
|
||||
// Test the function
|
||||
host, err := getContextHost(contextName, tempDir)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got: %s", err)
|
||||
}
|
||||
if host != "tcp://docker:2375" {
|
||||
t.Fatalf("Expected host 'tcp://docker:2375', got: %s", host)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetContextHost_InvalidContext(t *testing.T) {
|
||||
// Create a temporary directory to simulate Docker contexts
|
||||
tempDir := t.TempDir()
|
||||
|
||||
if err := os.MkdirAll(fmt.Sprintf("%s/.docker/contexts/meta/foobar", tempDir), 0755); err != nil {
|
||||
t.Fatalf("Failed to create context directory: %s", err)
|
||||
}
|
||||
|
||||
// Test the function with a non-existent context
|
||||
_, err := getContextHost("non-existent-context", tempDir)
|
||||
if err == nil || err.Error() != "context 'non-existent-context' not found" {
|
||||
t.Fatalf("Expected error 'context 'non-existent-context' not found', got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
const testAccDockerProviderWithIncompleteAuthConfig = `
|
||||
provider "docker" {
|
||||
alias = "private"
|
||||
|
|
|
|||
Loading…
Reference in a new issue