feat: Implement caching of docker provider (#808)
Some checks failed
Acc Tests / acc-test (TestAccDockerConfig, 0.15.x) (push) Has been cancelled
Acc Tests / acc-test (TestAccDockerConfig, 1.8.x) (push) Has been cancelled
Acc Tests / acc-test (TestAccDockerNetwork, 0.15.x) (push) Has been cancelled
Acc Tests / acc-test (TestAccDockerNetwork, 1.8.x) (push) Has been cancelled
Acc Tests / acc-test (TestAccDockerPlugin, 0.15.x) (push) Has been cancelled
Acc Tests / acc-test (TestAccDockerPlugin, 1.8.x) (push) Has been cancelled
Acc Tests / acc-test (TestAccDockerSecret, 0.15.x) (push) Has been cancelled
Acc Tests / acc-test (TestAccDockerSecret, 1.8.x) (push) Has been cancelled
Acc Tests / acc-test (TestAccDockerTag, 0.15.x) (push) Has been cancelled
Acc Tests / acc-test (TestAccDockerTag, 1.8.x) (push) Has been cancelled
Acc Tests / acc-test (TestAccDockerVolume, 0.15.x) (push) Has been cancelled
Acc Tests / acc-test (TestAccDockerVolume, 1.8.x) (push) Has been cancelled
Acc Tests / acc-test (true, TestAccDockerContainer, 0.15.x) (push) Has been cancelled
Acc Tests / acc-test (true, TestAccDockerContainer, 1.8.x) (push) Has been cancelled
Acc Tests / acc-test (true, TestAccDockerImage, 0.15.x) (push) Has been cancelled
Acc Tests / acc-test (true, TestAccDockerImage, 1.8.x) (push) Has been cancelled
Acc Tests / acc-test (true, TestAccDockerRegistryImage, 0.15.x) (push) Has been cancelled
Acc Tests / acc-test (true, TestAccDockerRegistryImage, 1.8.x) (push) Has been cancelled
Acc Tests / acc-test (true, TestAccDockerService, 0.15.x) (push) Has been cancelled
Acc Tests / acc-test (true, TestAccDockerService, 1.8.x) (push) Has been cancelled
Compile Binaries / compile-fast (push) Has been cancelled
Compile Binaries / compile (push) Has been cancelled
golangci-lint / lint (push) Has been cancelled
Unit Tests / unit-test (push) Has been cancelled
Website Checks / markdown-link-check (push) Has been cancelled
Docs and Website Lint / website-generation (push) Has been cancelled
Docs and Website Lint / website-lint-spellcheck-tffmt (push) Has been cancelled
Docs and Website Lint / markdown-lint (push) Has been cancelled

This commit is contained in:
Martin 2025-10-16 20:18:34 +02:00 committed by GitHub
parent 138787119a
commit 70852379ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 445 additions and 192 deletions

View file

@ -1,31 +1,62 @@
package provider
import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"hash/fnv"
"log"
"net"
"net/http"
"path/filepath"
"runtime"
"sort"
"strings"
"sync"
"time"
"github.com/docker/cli/cli/connhelper"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/client"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
// Config is the structure that stores the configuration to talk to a
// Docker API compatible host.
type Config struct {
Host string
SSHOpts []string
Ca string
Cert string
Key string
CertPath string
Host string
SSHOpts []string
Ca string
Cert string
Key string
CertPath string
DisableDockerDaemonCheck bool
}
func (c *Config) Hash() uint64 {
var SSHOpts []string
copy(SSHOpts, c.SSHOpts)
sort.Strings(SSHOpts)
hash := fnv.New64()
_, err := hash.Write([]byte(strings.Join([]string{
c.Host,
c.Ca,
c.Cert,
c.Key,
c.CertPath,
strings.Join(SSHOpts, "|")},
"|",
)))
if err != nil {
panic(err)
}
return hash.Sum64()
}
// buildHTTPClientFromBytes builds the http client from bytes (content of the files)
@ -82,60 +113,100 @@ func defaultPooledTransport() *http.Transport {
}
// NewClient returns a new Docker client.
func (c *Config) NewClient() (*client.Client, error) {
if c.Cert != "" || c.Key != "" {
if c.Cert == "" || c.Key == "" {
func (c *ProviderConfig) MakeClient(ctx context.Context, d *schema.ResourceData) (*client.Client, error) {
var dockerClient *client.Client
var err error
config := *c.DefaultConfig
configHash := config.Hash()
cached, found := c.clientCache.Load(configHash)
if found {
log.Printf("[DEBUG] Found cached client! Hash:%d Host:%s", configHash, config.Host)
return cached.(*client.Client), nil
}
if config.Cert != "" || config.Key != "" {
if config.Cert == "" || config.Key == "" {
return nil, fmt.Errorf("cert_material, and key_material must be specified")
}
if c.CertPath != "" {
if config.CertPath != "" {
return nil, fmt.Errorf("cert_path must not be specified")
}
httpClient, err := buildHTTPClientFromBytes([]byte(c.Ca), []byte(c.Cert), []byte(c.Key))
httpClient, err := buildHTTPClientFromBytes([]byte(config.Ca), []byte(config.Cert), []byte(config.Key))
if err != nil {
return nil, err
}
// Note: don't change the order here, because the custom client
// needs to be set first them we overwrite the other options: host, version
return client.NewClientWithOpts(
dockerClient, err = client.NewClientWithOpts(
client.WithHTTPClient(httpClient),
client.WithHost(c.Host),
client.WithHost(config.Host),
client.WithAPIVersionNegotiation(),
)
}
if c.CertPath != "" {
if err != nil {
return nil, err
}
} else if config.CertPath != "" {
// If there is cert information, load it and use it.
ca := filepath.Join(c.CertPath, "ca.pem")
cert := filepath.Join(c.CertPath, "cert.pem")
key := filepath.Join(c.CertPath, "key.pem")
return client.NewClientWithOpts(
client.WithHost(c.Host),
ca := filepath.Join(config.CertPath, "ca.pem")
cert := filepath.Join(config.CertPath, "cert.pem")
key := filepath.Join(config.CertPath, "key.pem")
dockerClient, err = client.NewClientWithOpts(
client.WithHost(config.Host),
client.WithTLSClientConfig(ca, cert, key),
client.WithAPIVersionNegotiation(),
)
}
// If there is no cert information, then check for ssh://
helper, err := connhelper.GetConnectionHelperWithSSHOpts(c.Host, c.SSHOpts)
if err != nil {
return nil, err
}
if helper != nil {
return client.NewClientWithOpts(
client.WithHost(helper.Host),
client.WithDialContext(helper.Dialer),
if err != nil {
return nil, err
}
} else if strings.HasPrefix(config.Host, "ssh://") {
// If there is no cert information, then check for ssh://
helper, err := connhelper.GetConnectionHelperWithSSHOpts(config.Host, config.SSHOpts)
if err != nil {
return nil, err
}
if helper != nil {
dockerClient, err = client.NewClientWithOpts(
client.WithHost(helper.Host),
client.WithDialContext(helper.Dialer),
client.WithAPIVersionNegotiation(),
)
if err != nil {
return nil, err
}
}
} else {
// If there is no ssh://, then just return the direct client
dockerClient, err = client.NewClientWithOpts(
client.WithHost(config.Host),
client.WithAPIVersionNegotiation(),
)
}
if err != nil {
return nil, err
}
// If there is no ssh://, then just return the direct client
return client.NewClientWithOpts(
client.WithHost(c.Host),
client.WithAPIVersionNegotiation(),
)
if config.DisableDockerDaemonCheck {
log.Printf("[DEBUG] Skipping Docker daemon check")
} else {
_, err = dockerClient.Ping(ctx)
if err != nil {
return nil, fmt.Errorf("Error pinging Docker server, please make sure that %s is reachable and has a '_ping' endpoint. Error: %s", config.Host, err)
}
_, err = dockerClient.ServerVersion(ctx)
if err != nil {
log.Printf("[WARN] Error connecting to Docker daemon. Is your endpoint a valid docker host? This warning will be changed to an error in the next major version. Error: %s", err)
}
}
c.clientCache.LoadOrStore(configHash, dockerClient)
log.Printf("[INFO] New client with Hash:%d Host:%s", configHash, config.Host)
return dockerClient, nil
}
// Data structure for holding data that we fetch from Docker.
@ -145,8 +216,10 @@ type Data struct {
// ProviderConfig for the custom registry provider
type ProviderConfig struct {
DockerClient *client.Client
AuthConfigs *AuthConfigs
DefaultConfig *Config
Hosts map[string]*schema.ResourceData
AuthConfigs *AuthConfigs
clientCache sync.Map
}
// The registry address can be referenced in various places (registry auth, docker config file, image name)

View file

@ -32,7 +32,10 @@ func dataSourceDockerImage() *schema.Resource {
}
func dataSourceDockerImageRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
var data Data
if err := fetchLocalImages(ctx, &data, client); err != nil {

View file

@ -166,7 +166,10 @@ func pullImageForTest(t *testing.T, imageName string) {
}
func removeImageForTest(ctx context.Context, s *terraform.State, imageName string) error {
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
// for images with tag and digest like e.g.
// 'nginx:1.19.1@sha256:36b74457bccb56fbf8b05f79c85569501b721d4db813b684391d63e02287c0b2'

View file

@ -84,7 +84,10 @@ func dataSourceDockerLogs() *schema.Resource {
}
func dataSourceDockerLogsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
containerId := d.Get("name").(string)
d.SetId(containerId)

View file

@ -101,7 +101,10 @@ func dataSourceDockerNetworkRead(ctx context.Context, d *schema.ResourceData, me
return diag.Errorf("One of id or name must be assigned")
}
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
network, err := client.NetworkInspect(ctx, name.(string), network.InspectOptions{})
if err != nil {

View file

@ -3,8 +3,8 @@ package provider
import (
"context"
"errors"
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
@ -12,7 +12,7 @@ func dataSourceDockerPlugin() *schema.Resource {
return &schema.Resource{
Description: "Reads the local Docker plugin. The plugin must be installed locally.",
Read: dataSourceDockerPluginRead,
ReadContext: dataSourceDockerPluginRead,
Schema: map[string]*schema.Schema{
"id": {
@ -72,16 +72,18 @@ func getDataSourcePluginKey(d *schema.ResourceData) (string, error) {
return "", errDataSourceKeyIsMissing
}
func dataSourceDockerPluginRead(d *schema.ResourceData, meta interface{}) error {
func dataSourceDockerPluginRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
key, err := getDataSourcePluginKey(d)
if err != nil {
return err
return diag.FromErr(err)
}
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
client := meta.(*ProviderConfig).DockerClient
ctx := context.Background()
plugin, _, err := client.PluginInspectWithRaw(ctx, key)
if err != nil {
return fmt.Errorf("inspect a Docker plugin "+key+": %w", err)
return diag.Errorf("inspect a Docker plugin "+key+": %w", err)
}
setDockerPlugin(d, plugin)

View file

@ -10,6 +10,7 @@ import (
"os/user"
"runtime"
"strings"
"sync"
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/docker/api/types/registry"
@ -208,36 +209,21 @@ func configure(version string, p *schema.Provider) func(context.Context, *schema
for i, s := range SSHOptsI {
SSHOpts[i] = s.(string)
}
config := Config{
Host: host,
SSHOpts: SSHOpts,
Ca: d.Get("ca_material").(string),
Cert: d.Get("cert_material").(string),
Key: d.Get("key_material").(string),
CertPath: d.Get("cert_path").(string),
}
client, err := config.NewClient()
if err != nil {
return nil, diag.Errorf("Error initializing Docker client: %s", err)
}
// Check if the Docker daemon is running
if !d.Get("disable_docker_daemon_check").(bool) {
_, err = client.Ping(ctx)
if err != nil {
return nil, diag.Errorf("Error pinging Docker server, please make sure that %s is reachable and has a '_ping' endpoint. Error: %s", host, err)
}
_, err = client.ServerVersion(ctx)
if err != nil {
log.Printf("[WARN] Error connecting to Docker daemon. Is your endpoint a valid docker host? This warning will be changed to an error in the next major version. Error: %s", err)
}
} else {
log.Printf("[DEBUG] Skipping Docker daemon check")
defaultConfig := Config{
Host: host,
SSHOpts: SSHOpts,
Ca: d.Get("ca_material").(string),
Cert: d.Get("cert_material").(string),
Key: d.Get("key_material").(string),
CertPath: d.Get("cert_path").(string),
DisableDockerDaemonCheck: d.Get("disable_docker_daemon_check").(bool),
}
authConfigs := &AuthConfigs{}
var err error
if v, ok := d.GetOk("registry_auth"); ok {
authConfigs, err = providerSetToRegistryAuth(v.(*schema.Set))
if err != nil {
@ -246,8 +232,10 @@ func configure(version string, p *schema.Provider) func(context.Context, *schema
}
providerConfig := ProviderConfig{
DockerClient: client,
AuthConfigs: authConfigs,
DefaultConfig: &defaultConfig,
Hosts: make(map[string]*schema.ResourceData),
clientCache: sync.Map{},
AuthConfigs: authConfigs,
}
return &providerConfig, nil

View file

@ -429,7 +429,10 @@ func resourceDockerBuildxBuilderCreate(ctx context.Context, d *schema.ResourceDa
driverOptions = processDriverOptions(remote)
}
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to create Docker client: %w", err))
}
t, error := command.NewDockerCli()
if error != nil {
@ -437,7 +440,7 @@ func resourceDockerBuildxBuilderCreate(ctx context.Context, d *schema.ResourceDa
}
log.Printf("[DEBUG] Docker CLI initialized %#v, %#v", client, client.DaemonHost())
err := t.Initialize(&flags.ClientOptions{Hosts: []string{client.DaemonHost()}})
err = t.Initialize(&flags.ClientOptions{Hosts: []string{client.DaemonHost()}})
if err != nil {
return diag.FromErr(fmt.Errorf("failed to initialize Docker CLI: %w", err))
@ -520,13 +523,16 @@ func processDriverOptions(driverOptionsMap map[string]interface{}) []string {
// resourceDockerBuildxBuilderRead handles reading the state of a Buildx builder
// corresponding file in buildx repo: https://github.com/docker/buildx/blob/master/commands/inspect.go
func resourceDockerBuildxBuilderRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to create Docker client: %w", err))
}
dockerCli, error := command.NewDockerCli()
if error != nil {
return diag.FromErr(fmt.Errorf("failed to create Docker CLI: %w", error))
}
err := dockerCli.Initialize(&flags.ClientOptions{Hosts: []string{client.DaemonHost()}})
err = dockerCli.Initialize(&flags.ClientOptions{Hosts: []string{client.DaemonHost()}})
if err != nil {
return diag.FromErr(fmt.Errorf("failed to initialize Docker CLI: %w", err))
}
@ -557,7 +563,10 @@ func resourceDockerBuildxBuilderDelete(ctx context.Context, d *schema.ResourceDa
name := d.Id()
log.Printf("[DEBUG] Deleting Buildx builder: %s", name)
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to create Docker client: %w", err))
}
dockerCli, err := command.NewDockerCli()
if err != nil {

View file

@ -4,6 +4,7 @@ import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"log"
"github.com/docker/docker/api/types/swarm"
@ -42,7 +43,10 @@ func resourceDockerConfig() *schema.Resource {
}
func resourceDockerConfigCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to create Docker client: %w", err))
}
data, _ := base64.StdEncoding.DecodeString(d.Get("data").(string))
configSpec := swarm.ConfigSpec{
@ -62,7 +66,10 @@ func resourceDockerConfigCreate(ctx context.Context, d *schema.ResourceData, met
}
func resourceDockerConfigRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to create Docker client: %w", err))
}
config, _, err := client.ConfigInspectWithRaw(ctx, d.Id())
if err != nil {
log.Printf("[WARN] Config (%s) not found, removing from state", d.Id())
@ -80,8 +87,11 @@ func resourceDockerConfigRead(ctx context.Context, d *schema.ResourceData, meta
}
func resourceDockerConfigDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
err := client.ConfigRemove(ctx, d.Id())
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to create Docker client: %w", err))
}
err = client.ConfigRemove(ctx, d.Id())
if err != nil {
return diag.FromErr(err)
}

View file

@ -115,7 +115,10 @@ func TestAccDockerConfig_basicUpdatable(t *testing.T) {
// Helpers
// ///////////
func testCheckDockerConfigDestroy(ctx context.Context, s *terraform.State) error {
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "configs" {
continue
@ -144,7 +147,10 @@ func testAccServiceConfigCreated(resourceName string, config *swarm.Config) reso
return fmt.Errorf("No ID is set")
}
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
inspectedConfig, _, err := client.ConfigInspectWithRaw(ctx, rs.Primary.ID)
if err != nil {
return fmt.Errorf("Config with ID '%s': %w", rs.Primary.ID, err)

View file

@ -47,8 +47,10 @@ var (
var creationTime time.Time
func resourceDockerContainerCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var err error
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to create Docker client: %w", err))
}
authConfigs := meta.(*ProviderConfig).AuthConfigs
image := d.Get("image").(string)
_, err = findImage(ctx, image, client, authConfigs, "")
@ -678,7 +680,10 @@ func resourceDockerContainerRead(ctx context.Context, d *schema.ResourceData, me
containerReadRefreshTimeoutMilliseconds = containerReadRefreshTimeoutMillisecondsDefault
}
log.Printf("[INFO] Waiting for container: '%s' to run: max '%v seconds'", d.Id(), containerReadRefreshTimeoutMilliseconds/1000)
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to create Docker client: %w", err))
}
apiContainer, err := fetchDockerContainer(ctx, d.Id(), client)
if err != nil {
@ -693,7 +698,7 @@ func resourceDockerContainerRead(ctx context.Context, d *schema.ResourceData, me
stateConf := &retry.StateChangeConf{
Pending: []string{"pending"},
Target: []string{"running"},
Refresh: resourceDockerContainerReadRefreshFunc(ctx, d, meta),
Refresh: resourceDockerContainerReadRefreshFunc(ctx, d, meta, client),
Timeout: time.Duration(containerReadRefreshTimeoutMilliseconds) * time.Millisecond,
MinTimeout: containerReadRefreshWaitBeforeRefreshes,
Delay: containerReadRefreshDelay,
@ -874,9 +879,8 @@ func resourceDockerContainerRead(ctx context.Context, d *schema.ResourceData, me
}
func resourceDockerContainerReadRefreshFunc(ctx context.Context,
d *schema.ResourceData, meta interface{}) retry.StateRefreshFunc {
d *schema.ResourceData, meta interface{}, client *client.Client) retry.StateRefreshFunc {
return func() (interface{}, string, error) {
client := meta.(*ProviderConfig).DockerClient
containerID := d.Id()
var container container.InspectResponse
@ -962,8 +966,11 @@ func resourceDockerContainerUpdate(ctx context.Context, d *schema.ResourceData,
// QF1008: could remove embedded field "Resources" from selector
updateConfig.Resources.MemorySwap = a //nolint:staticcheck
}
client := meta.(*ProviderConfig).DockerClient
_, err := client.ContainerUpdate(ctx, d.Id(), updateConfig)
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to create Docker client: %w", err))
}
_, err = client.ContainerUpdate(ctx, d.Id(), updateConfig)
if err != nil {
return diag.Errorf("Unable to update a container: %v", err)
}
@ -974,7 +981,10 @@ func resourceDockerContainerUpdate(ctx context.Context, d *schema.ResourceData,
}
func resourceDockerContainerDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to create Docker client: %w", err))
}
if !d.Get("attach").(bool) {
// Stop the container before removing if destroy_grace_seconds is defined

View file

@ -666,7 +666,10 @@ func TestAccDockerContainer_customized(t *testing.T) {
func testAccCheckSwapLimit(t *testing.T) {
ctx := context.Background()
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
t.Fatalf("failed to create Docker client: %v", err)
}
info, err := client.Info(ctx)
if err != nil {
t.Fatalf("Failed to check swap limit capability: %s", err)
@ -683,7 +686,10 @@ func TestAccDockerContainer_uploadPermission(t *testing.T) {
testCheck := func(expected_mode string) func(*terraform.State) error {
return func(*terraform.State) error {
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
srcPath := "/terraform/test.txt"
r, _, err := client.CopyFromContainer(ctx, c.ID, srcPath)
@ -758,7 +764,10 @@ func TestAccDockerContainer_uploadSource(t *testing.T) {
testFileContent, _ := os.ReadFile(testFile)
testCheck := func(*terraform.State) error {
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
srcPath := "/terraform/test.txt"
r, _, err := client.CopyFromContainer(ctx, c.ID, srcPath)
@ -876,7 +885,10 @@ func TestAccDockerContainer_uploadAsBase64(t *testing.T) {
testCheck := func(srcPath, wantedContent, filePerm string) func(*terraform.State) error {
return func(*terraform.State) error {
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
r, _, err := client.CopyFromContainer(ctx, c.ID, srcPath)
if err != nil {
@ -1022,7 +1034,10 @@ func TestAccDockerContainer_device(t *testing.T) {
ctx := context.Background()
testCheck := func(*terraform.State) error {
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
createExecOpts := container.ExecOptions{
Cmd: []string{"dd", "if=/dev/zero_test", "of=/tmp/test.txt", "count=10", "bs=1"},
@ -1684,7 +1699,10 @@ func testAccContainerRunning(resourceName string, runningContainer *container.In
return fmt.Errorf("No ID is set")
}
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
containers, err := client.ContainerList(ctx, container.ListOptions{})
if err != nil {
return err
@ -1717,7 +1735,10 @@ func testAccContainerNotRunning(n string, runningContainer *container.InspectRes
return fmt.Errorf("No ID is set")
}
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
containers, err := client.ContainerList(ctx, container.ListOptions{
All: true,
})
@ -1755,7 +1776,10 @@ func testAccContainerWaitConditionNotRunning(n string, ct *container.InspectResp
return fmt.Errorf("No ID is set")
}
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
@ -1784,7 +1808,10 @@ func testAccContainerWaitConditionRemoved(ctx context.Context, n string, ct *con
return fmt.Errorf("No ID is set")
}
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()

View file

@ -32,7 +32,10 @@ import (
)
func resourceDockerImageCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to create Docker client: %w", err))
}
imageName := d.Get("name").(string)
if value, ok := d.GetOk("build"); ok {
@ -94,7 +97,10 @@ func buildImage(ctx context.Context, rawBuild interface{}, client *client.Client
}
func resourceDockerImageRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to create Docker client: %w", err))
}
var data Data
if err := fetchLocalImages(ctx, &data, client); err != nil {
return diag.Errorf("Error reading docker image list: %s", err)
@ -124,9 +130,12 @@ func resourceDockerImageRead(ctx context.Context, d *schema.ResourceData, meta i
func resourceDockerImageUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
// We need to re-read in case switching parameters affects
// the value of "latest" or others
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to create Docker client: %w", err))
}
imageName := d.Get("name").(string)
_, err := findImage(ctx, imageName, client, meta.(*ProviderConfig).AuthConfigs, d.Get("platform").(string))
_, err = findImage(ctx, imageName, client, meta.(*ProviderConfig).AuthConfigs, d.Get("platform").(string))
if err != nil {
return diag.Errorf("Unable to read Docker image into resource: %s", err)
}
@ -135,9 +144,12 @@ func resourceDockerImageUpdate(ctx context.Context, d *schema.ResourceData, meta
}
func resourceDockerImageDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to create Docker client: %w", err))
}
// TODO mavogel: add retries. see e.g. service updateFailsAndRollbackConvergeConfig test
err := removeImage(ctx, d, client)
err = removeImage(ctx, d, client)
if err != nil {
return diag.Errorf("Unable to remove Docker image: %s", err)
}

View file

@ -207,8 +207,11 @@ func TestAccDockerImage_destroy(t *testing.T) {
continue
}
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
_, err := client.ImageInspect(ctx, rs.Primary.Attributes["name"])
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
_, err = client.ImageInspect(ctx, rs.Primary.Attributes["name"])
if err != nil {
return err
}
@ -380,8 +383,12 @@ func testAccDockerImageDestroy(ctx context.Context, s *terraform.State) error {
continue
}
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
_, err := client.ImageInspect(ctx, rs.Primary.Attributes["name"])
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
_, err = client.ImageInspect(ctx, rs.Primary.Attributes["name"])
if err == nil {
return fmt.Errorf("Image still exists")
}
@ -736,7 +743,10 @@ func testAccImageCreated(resourceName string, image *image.InspectResponse) reso
// so we need to strip away the name
strippedID := strings.ReplaceAll(rs.Primary.ID, name, "")
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
inspectedImage, err := client.ImageInspect(ctx, strippedID)
if err != nil {
return fmt.Errorf("Image with ID '%s': %w", strippedID, err)

View file

@ -3,10 +3,12 @@ package provider
import (
"context"
"encoding/json"
"fmt"
"log"
"time"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@ -22,7 +24,10 @@ const (
)
func resourceDockerNetworkCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to create Docker client: %w", err))
}
createOpts := network.CreateOptions{}
if v, ok := d.GetOk("labels"); ok {
@ -80,17 +85,22 @@ func resourceDockerNetworkCreate(ctx context.Context, d *schema.ResourceData, me
func resourceDockerNetworkRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
log.Printf("[INFO] Waiting for network: '%s' to expose all fields: max '%v seconds'", d.Id(), networkReadRefreshTimeout)
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to create Docker client: %w", err))
}
stateConf := &retry.StateChangeConf{
Pending: []string{"pending"},
Target: []string{"all_fields", "removed"},
Refresh: resourceDockerNetworkReadRefreshFunc(ctx, d, meta),
Refresh: resourceDockerNetworkReadRefreshFunc(ctx, d, meta, client),
Timeout: networkReadRefreshTimeout,
MinTimeout: networkReadRefreshWaitBeforeRefreshes,
Delay: networkReadRefreshDelay,
}
// Wait, catching any errors
_, err := stateConf.WaitForStateContext(ctx)
_, err = stateConf.WaitForStateContext(ctx)
if err != nil {
return diag.FromErr(err)
}
@ -101,17 +111,22 @@ func resourceDockerNetworkRead(ctx context.Context, d *schema.ResourceData, meta
func resourceDockerNetworkDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
log.Printf("[INFO] Waiting for network: '%s' to be removed: max '%v seconds'", d.Id(), networkRemoveRefreshTimeout)
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to create Docker client: %w", err))
}
stateConf := &retry.StateChangeConf{
Pending: []string{"pending"},
Target: []string{"removed"},
Refresh: resourceDockerNetworkRemoveRefreshFunc(ctx, d, meta),
Refresh: resourceDockerNetworkRemoveRefreshFunc(ctx, d, meta, client),
Timeout: networkRemoveRefreshTimeout,
MinTimeout: networkRemoveRefreshWaitBeforeRefreshes,
Delay: networkRemoveRefreshDelay,
}
// Wait, catching any errors
_, err := stateConf.WaitForStateContext(ctx)
_, err = stateConf.WaitForStateContext(ctx)
if err != nil {
return diag.FromErr(err)
}
@ -144,9 +159,8 @@ func ipamConfigSetToIpamConfigs(ipamConfigSet *schema.Set) []network.IPAMConfig
}
func resourceDockerNetworkReadRefreshFunc(ctx context.Context,
d *schema.ResourceData, meta interface{}) retry.StateRefreshFunc {
d *schema.ResourceData, meta interface{}, client *client.Client) retry.StateRefreshFunc {
return func() (interface{}, string, error) {
client := meta.(*ProviderConfig).DockerClient
networkID := d.Id()
retNetwork, _, err := client.NetworkInspectWithRaw(ctx, networkID, network.InspectOptions{})
@ -190,9 +204,8 @@ func resourceDockerNetworkReadRefreshFunc(ctx context.Context,
}
func resourceDockerNetworkRemoveRefreshFunc(ctx context.Context,
d *schema.ResourceData, meta interface{}) retry.StateRefreshFunc {
d *schema.ResourceData, meta interface{}, client *client.Client) retry.StateRefreshFunc {
return func() (interface{}, string, error) {
client := meta.(*ProviderConfig).DockerClient
networkID := d.Id()
_, _, err := client.NetworkInspectWithRaw(ctx, networkID, network.InspectOptions{})

View file

@ -133,7 +133,10 @@ func testAccNetwork(n string, networkToCheck *network.Inspect) resource.TestChec
return fmt.Errorf("No ID is set")
}
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
networks, err := client.NetworkList(ctx, network.ListOptions{})
if err != nil {
return err
@ -251,7 +254,10 @@ func TestAccDockerNetwork_ingress(t *testing.T) {
}
func removeSwarmIngressNetwork(ctx context.Context, t *testing.T) {
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
t.Errorf("failed to create Docker client: %v", err)
}
networks, err := client.NetworkList(ctx, network.ListOptions{})
if err != nil {
t.Errorf("failed to list swarm networks: %v", err)
@ -270,10 +276,12 @@ func removeSwarmIngressNetwork(ctx context.Context, t *testing.T) {
}
func nodeLeaveSwarm(ctx context.Context, t *testing.T) error {
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
t.Errorf("failed to create Docker client: %v", err)
}
force := true
err := client.SwarmLeave(ctx, force)
err = client.SwarmLeave(ctx, force)
if err != nil {
t.Errorf("node failed to leave the swarm: %v", err)
}

View file

@ -8,10 +8,10 @@ func resourceDockerPlugin() *schema.Resource {
return &schema.Resource{
Description: "Manages the lifecycle of a Docker plugin.",
Create: resourceDockerPluginCreate,
Read: resourceDockerPluginRead,
Update: resourceDockerPluginUpdate,
Delete: resourceDockerPluginDelete,
CreateContext: resourceDockerPluginCreate,
ReadContext: resourceDockerPluginRead,
UpdateContext: resourceDockerPluginUpdate,
DeleteContext: resourceDockerPluginDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

View file

@ -11,12 +11,16 @@ import (
"github.com/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func resourceDockerPluginCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ProviderConfig).DockerClient
ctx := context.Background()
func resourceDockerPluginCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
pluginName := d.Get("name").(string)
alias := d.Get("alias").(string)
log.Printf("[DEBUG] Install a Docker plugin %s", pluginName)
@ -32,7 +36,7 @@ func resourceDockerPluginCreate(d *schema.ResourceData, meta interface{}) error
}
body, err := client.PluginInstall(ctx, alias, opts)
if err != nil {
return fmt.Errorf("install a Docker plugin "+pluginName+": %w", err)
return diag.Errorf("install a Docker plugin "+pluginName+": %w", err)
}
_, _ = io.ReadAll(body)
key := pluginName
@ -41,15 +45,18 @@ func resourceDockerPluginCreate(d *schema.ResourceData, meta interface{}) error
}
plugin, _, err := client.PluginInspectWithRaw(ctx, key)
if err != nil {
return fmt.Errorf("inspect a Docker plugin "+key+": %w", err)
return diag.Errorf("inspect a Docker plugin "+key+": %w", err)
}
setDockerPlugin(d, plugin)
return nil
}
func resourceDockerPluginRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ProviderConfig).DockerClient
ctx := context.Background()
func resourceDockerPluginRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
pluginID := d.Id()
plugin, _, err := client.PluginInspectWithRaw(ctx, pluginID)
if err != nil {
@ -65,15 +72,18 @@ func resourceDockerPluginRead(d *schema.ResourceData, meta interface{}) error {
return nil
}
func resourceDockerPluginDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ProviderConfig).DockerClient
ctx := context.Background()
func resourceDockerPluginDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
pluginID := d.Id()
log.Printf("[DEBUG] Remove a Docker plugin %s", pluginID)
if err := client.PluginRemove(ctx, pluginID, types.PluginRemoveOptions{
Force: d.Get("force_destroy").(bool),
}); err != nil {
return fmt.Errorf("remove the Docker plugin %s: %w", pluginID, err)
return diag.Errorf("remove the Docker plugin %s: %v", pluginID, err)
}
return nil
}
@ -212,7 +222,10 @@ func pluginSet(ctx context.Context, d *schema.ResourceData, cl *client.Client) e
}
func pluginUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) (gErr error) {
cl := meta.(*ProviderConfig).DockerClient
cl, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
o, n := d.GetChange("enabled")
oldEnabled, newEnabled := o.(bool), n.(bool)
if d.HasChange("env") {
@ -257,12 +270,11 @@ func pluginUpdate(ctx context.Context, d *schema.ResourceData, meta interface{})
return nil
}
func resourceDockerPluginUpdate(d *schema.ResourceData, meta interface{}) error {
ctx := context.Background()
func resourceDockerPluginUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
if err := pluginUpdate(ctx, d, meta); err != nil {
return err
return diag.FromErr(err)
}
// call the read function to update the resource's state.
// https://learn.hashicorp.com/tutorials/terraform/provider-update?in=terraform/providers#implement-update
return resourceDockerPluginRead(d, meta)
return resourceDockerPluginRead(ctx, d, meta)
}

View file

@ -397,7 +397,10 @@ func testAccPluginCreated(resourceName string, plugin *types.Plugin) resource.Te
return fmt.Errorf("No ID is set")
}
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
inspectedPlugin, _, err := client.PluginInspectWithRaw(ctx, rs.Primary.ID)
if err != nil {
return fmt.Errorf("Plugin with ID '%s': %w", rs.Primary.ID, err)

View file

@ -33,7 +33,11 @@ func buildAuthConfigFromResource(v interface{}) registry.AuthConfig {
}
func resourceDockerRegistryImageCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
providerConfig := meta.(*ProviderConfig)
name := d.Get("name").(string)
log.Printf("[DEBUG] Creating docker image %s", name)

View file

@ -89,7 +89,10 @@ func resourceDockerSecretV0() *schema.Resource {
}
func resourceDockerSecretCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
data, _ := base64.StdEncoding.DecodeString(d.Get("data").(string))
secretSpec := swarm.SecretSpec{
@ -115,7 +118,10 @@ func resourceDockerSecretCreate(ctx context.Context, d *schema.ResourceData, met
}
func resourceDockerSecretRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
secret, _, err := client.SecretInspectWithRaw(ctx, d.Id())
if err != nil {
log.Printf("[WARN] Secret (%s) not found, removing from state", d.Id())
@ -135,8 +141,11 @@ func resourceDockerSecretRead(ctx context.Context, d *schema.ResourceData, meta
}
func resourceDockerSecretDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
err := client.SecretRemove(ctx, d.Id())
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
err = client.SecretRemove(ctx, d.Id())
if err != nil {
return diag.FromErr(err)
}

View file

@ -140,7 +140,10 @@ func TestAccDockerSecret_labels(t *testing.T) {
// Helpers
// ///////////
func testCheckDockerSecretDestroy(ctx context.Context, s *terraform.State) error {
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "secrets" {
continue
@ -169,7 +172,10 @@ func testAccServiceSecretCreated(resourceName string, secret *swarm.Secret) reso
return fmt.Errorf("No ID is set")
}
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
inspectedSecret, _, err := client.SecretInspectWithRaw(ctx, rs.Primary.ID)
if err != nil {
return fmt.Errorf("Secret with ID '%s': %w", rs.Primary.ID, err)

View file

@ -29,8 +29,10 @@ type convergeConfig struct {
// TF CRUD funcs
// ///////////////
func resourceDockerServiceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var err error
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
serviceSpec, err := createServiceSpec(d)
if err != nil {
@ -54,7 +56,7 @@ func resourceDockerServiceCreate(ctx context.Context, d *schema.ResourceData, me
stateConf := &retry.StateChangeConf{
Pending: serviceCreatePendingStates,
Target: []string{"running", "complete"},
Refresh: resourceDockerServiceCreateRefreshFunc(ctx, service.ID, meta),
Refresh: resourceDockerServiceCreateRefreshFunc(ctx, service.ID, meta, client),
Timeout: timeout,
MinTimeout: 5 * time.Second,
Delay: convergeConfig.delay,
@ -81,17 +83,22 @@ func resourceDockerServiceCreate(ctx context.Context, d *schema.ResourceData, me
func resourceDockerServiceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
log.Printf("[INFO] Waiting for service: '%s' to expose all fields: max '%v seconds'", d.Id(), 30)
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
stateConf := &retry.StateChangeConf{
Pending: []string{"pending"},
Target: []string{"all_fields", "removed"},
Refresh: resourceDockerServiceReadRefreshFunc(ctx, d, meta),
Refresh: resourceDockerServiceReadRefreshFunc(ctx, d, meta, client),
Timeout: 30 * time.Second,
MinTimeout: 5 * time.Second,
Delay: 2 * time.Second,
}
// Wait, catching any errors
_, err := stateConf.WaitForStateContext(ctx)
_, err = stateConf.WaitForStateContext(ctx)
if err != nil {
return diag.FromErr(err)
}
@ -100,9 +107,8 @@ func resourceDockerServiceRead(ctx context.Context, d *schema.ResourceData, meta
}
func resourceDockerServiceReadRefreshFunc(ctx context.Context,
d *schema.ResourceData, meta interface{}) retry.StateRefreshFunc {
d *schema.ResourceData, meta interface{}, client *client.Client) retry.StateRefreshFunc {
return func() (interface{}, string, error) {
client := meta.(*ProviderConfig).DockerClient
serviceID := d.Id()
apiService, err := fetchDockerService(ctx, serviceID, d.Get("name").(string), client)
@ -161,7 +167,10 @@ func resourceDockerServiceReadRefreshFunc(ctx context.Context,
}
func resourceDockerServiceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
service, _, err := client.ServiceInspectWithRaw(ctx, d.Id(), swarm.ServiceInspectOptions{})
if err != nil {
@ -195,7 +204,7 @@ func resourceDockerServiceUpdate(ctx context.Context, d *schema.ResourceData, me
stateConf := &retry.StateChangeConf{
Pending: serviceUpdatePendingStates,
Target: []string{"completed"},
Refresh: resourceDockerServiceUpdateRefreshFunc(ctx, service.ID, meta),
Refresh: resourceDockerServiceUpdateRefreshFunc(ctx, service.ID, meta, client),
Timeout: timeout,
MinTimeout: 5 * time.Second,
Delay: 7 * time.Second,
@ -216,7 +225,10 @@ func resourceDockerServiceUpdate(ctx context.Context, d *schema.ResourceData, me
}
func resourceDockerServiceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
if err := deleteService(ctx, d.Id(), d, client); err != nil {
return diag.FromErr(err)
@ -344,10 +356,8 @@ func (err *DidNotConvergeError) Error() string {
// resourceDockerServiceCreateRefreshFunc refreshes the state of a service when it is created and needs to converge
func resourceDockerServiceCreateRefreshFunc(ctx context.Context,
serviceID string, meta interface{}) retry.StateRefreshFunc {
serviceID string, meta interface{}, client *client.Client) retry.StateRefreshFunc {
return func() (interface{}, string, error) {
client := meta.(*ProviderConfig).DockerClient
var updater progressUpdater
if updater == nil {
@ -394,10 +404,8 @@ func resourceDockerServiceCreateRefreshFunc(ctx context.Context,
// resourceDockerServiceUpdateRefreshFunc refreshes the state of a service when it is updated and needs to converge
func resourceDockerServiceUpdateRefreshFunc(ctx context.Context,
serviceID string, meta interface{}) retry.StateRefreshFunc {
serviceID string, meta interface{}, client *client.Client) retry.StateRefreshFunc {
return func() (interface{}, string, error) {
client := meta.(*ProviderConfig).DockerClient
var (
updater progressUpdater
rollback bool

View file

@ -1352,7 +1352,10 @@ func TestAccDockerService_mounts_issue222(t *testing.T) {
func isServiceRemoved(serviceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
ctx := context.Background()
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
filters := filters.NewArgs()
filters.Add("name", serviceName)
services, err := client.ServiceList(ctx, swarm.ServiceListOptions{
@ -1378,7 +1381,10 @@ func checkAndRemoveImages(ctx context.Context, s *terraform.State) error {
maxRetryDeleteCount := 6
imagePattern := "127.0.0.1:15000/tftest-service*"
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
filters := filters.NewArgs()
filters.Add("reference", imagePattern)
@ -1435,7 +1441,10 @@ func testAccServiceRunning(resourceName string, service *swarm.Service) resource
return fmt.Errorf("No ID is set")
}
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
inspectedService, _, err := client.ServiceInspectWithRaw(ctx, rs.Primary.ID, swarm.ServiceInspectOptions{})
if err != nil {
return fmt.Errorf("Service with ID '%s': %w", rs.Primary.ID, err)

View file

@ -8,10 +8,14 @@ import (
)
func resourceDockerTagCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
sourceImage := d.Get("source_image").(string)
targetImage := d.Get("target_image").(string)
err := client.ImageTag(ctx, sourceImage, targetImage)
err = client.ImageTag(ctx, sourceImage, targetImage)
if err != nil {
return diag.Errorf("failed to create docker tag: %s", err)
}

View file

@ -12,6 +12,7 @@ import (
"github.com/containerd/errdefs"
"github.com/docker/cli/opts"
"github.com/docker/docker/api/types/volume"
"github.com/docker/docker/client"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@ -163,7 +164,10 @@ func resourceDockerVolume() *schema.Resource {
}
func resourceDockerVolumeCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
createOpts := volume.CreateOptions{}
@ -270,7 +274,6 @@ func resourceDockerVolumeCreate(ctx context.Context, d *schema.ResourceData, met
createOpts.ClusterVolumeSpec.AccessibilityRequirements = topology
}
}
var err error
var retVolume volume.Volume
retVolume, err = client.VolumeCreate(ctx, createOpts)
@ -283,7 +286,10 @@ func resourceDockerVolumeCreate(ctx context.Context, d *schema.ResourceData, met
}
func resourceDockerVolumeRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
volume, err := client.VolumeInspect(ctx, d.Id())
@ -355,18 +361,22 @@ func resourceDockerVolumeRead(ctx context.Context, d *schema.ResourceData, meta
func resourceDockerVolumeDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
log.Printf("[INFO] Waiting for volume: `%s` to get removed: max `%v seconds`", d.Id(), volumeReadRefreshTimeout)
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
stateConf := &retry.StateChangeConf{
Pending: []string{"in_use"},
Target: []string{"removed"},
Refresh: resourceDockerVolumeRemoveRefreshFunc(d.Id(), meta),
Refresh: resourceDockerVolumeRemoveRefreshFunc(d.Id(), meta, client),
Timeout: volumeReadRefreshTimeout,
MinTimeout: volumeReadRefreshWaitBeforeRefreshes,
Delay: volumeReadRefreshDelay,
}
// Wait, catching any errors
_, err := stateConf.WaitForStateContext(ctx)
_, err = stateConf.WaitForStateContext(ctx)
if err != nil {
return diag.FromErr(err)
}
@ -379,12 +389,15 @@ func resourceDockerVolumeUpdate(ctx context.Context, d *schema.ResourceData, met
attrs := []string{
"cluster.availability",
}
client, err := meta.(*ProviderConfig).MakeClient(ctx, d)
if err != nil {
return diag.Errorf("failed to create Docker client: %v", err)
}
for _, attr := range attrs {
if d.HasChange(attr) {
clusterList := d.Get("cluster").([]interface{})
if len(clusterList) > 0 {
clusterConfig := clusterList[0].(map[string]interface{})
client := meta.(*ProviderConfig).DockerClient
vol, _, err := client.VolumeInspectWithRaw(ctx, clusterConfig["id"].(string))
if err != nil {
@ -414,9 +427,8 @@ func resourceDockerVolumeUpdate(ctx context.Context, d *schema.ResourceData, met
}
func resourceDockerVolumeRemoveRefreshFunc(
volumeID string, meta interface{}) retry.StateRefreshFunc {
volumeID string, meta interface{}, client *client.Client) retry.StateRefreshFunc {
return func() (interface{}, string, error) {
client := meta.(*ProviderConfig).DockerClient
forceDelete := true
if err := client.VolumeRemove(context.Background(), volumeID, forceDelete); err != nil {

View file

@ -151,9 +151,12 @@ func TestAccDockerVolume_RecreateAfterManualDelete(t *testing.T) {
{
// Simulate manual deletion of the Docker volume
PreConfig: func() {
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
ctx := context.Background()
err := client.VolumeRemove(ctx, volumeName, true)
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
t.Fatalf("failed to create Docker client: %v", err)
}
err = client.VolumeRemove(ctx, volumeName, true)
if err != nil {
t.Fatalf("failed to manually remove docker volume: %v", err)
}
@ -182,7 +185,10 @@ func checkDockerVolumeCreated(n string, volumeToCheck *volume.Volume) resource.T
}
ctx := context.Background()
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
client, err := testAccProvider.Meta().(*ProviderConfig).MakeClient(ctx, nil)
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
v, err := client.VolumeInspect(ctx, rs.Primary.ID)
if err != nil {
return err