feat: adds import for resources (#196)

Closes #99. Adds import for config service volume
This commit is contained in:
Manuel Vogel 2019-11-23 14:42:05 +01:00 committed by GitHub
parent 20b056aec2
commit 434ca8c64b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 298 additions and 85 deletions

View file

@ -3,6 +3,7 @@ package docker
import (
"context"
"fmt"
"github.com/docker/docker/api/types"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)
@ -68,7 +69,6 @@ func dataSourceDockerNetwork() *schema.Resource {
},
},
},
Set: resourceDockerIpamConfigHash,
},
"scope": &schema.Schema{

View file

@ -15,6 +15,9 @@ func resourceDockerConfig() *schema.Resource {
Create: resourceDockerConfigCreate,
Read: resourceDockerConfigRead,
Delete: resourceDockerConfigDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"name": {
@ -66,6 +69,8 @@ func resourceDockerConfigRead(d *schema.ResourceData, meta interface{}) error {
return nil
}
d.SetId(config.ID)
d.Set("name", config.Spec.Name)
d.Set("data", base64.StdEncoding.EncodeToString(config.Spec.Data))
return nil
}

View file

@ -27,6 +27,11 @@ func TestAccDockerConfig_basic(t *testing.T) {
resource.TestCheckResourceAttr("docker_config.foo", "data", "Ymxhc2RzYmxhYmxhMTI0ZHNkd2VzZA=="),
),
},
{
ResourceName: "docker_config.foo",
ImportState: true,
ImportStateVerify: true,
},
},
})
}
@ -68,6 +73,11 @@ func TestAccDockerConfig_basicUpdatable(t *testing.T) {
resource.TestCheckResourceAttr("docker_config.foo", "data", "U3VuIDI1IE1hciAyMDE4IDE0OjQ2OjE5IENFU1QK"),
),
},
{
ResourceName: "docker_config.foo",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View file

@ -14,7 +14,9 @@ func resourceDockerContainer() *schema.Resource {
Delete: resourceDockerContainerDelete,
MigrateState: resourceDockerContainerMigrateState,
SchemaVersion: 1,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,

View file

@ -518,7 +518,7 @@ func resourceDockerContainerRead(d *schema.ResourceData, meta interface{}) error
}
jsonObj, _ := json.MarshalIndent(container, "", "\t")
log.Printf("[DEBUG] Docker container inspect: %s", jsonObj)
log.Printf("[INFO] Docker container inspect: %s", jsonObj)
if container.State.Running ||
!container.State.Running && !d.Get("must_run").(bool) {
@ -578,6 +578,11 @@ func resourceDockerContainerRead(d *schema.ResourceData, meta interface{}) error
}
}
// TODO all the other attributes
d.SetId(container.ID)
// d.Set("name", container.Name) // api prefixes with '/' ...
// d.Set("image", container.Image)
// d.Set("log_driver", container.HostConfig.LogConfig.Type)
return nil
}

View file

@ -54,6 +54,7 @@ func TestAccDockerContainer_private_image(t *testing.T) {
}
func TestAccDockerContainer_basic(t *testing.T) {
resourceName := "docker_container.foo"
var c types.ContainerJSON
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
@ -62,9 +63,24 @@ func TestAccDockerContainer_basic(t *testing.T) {
{
Config: testAccDockerContainerConfig,
Check: resource.ComposeTestCheckFunc(
testAccContainerRunning("docker_container.foo", &c),
testAccContainerRunning(resourceName, &c),
),
},
// TODO mavogel: Will be done in #219
// {
// ResourceName: resourceName,
// ImportState: true,
// ImportStateVerify: true,
// ImportStateVerifyIgnore: []string{
// "attach",
// "log_driver",
// "logs",
// "must_run",
// "restart",
// "rm",
// "start",
// },
// },
},
})
}
@ -1279,6 +1295,7 @@ func TestAccDockerContainer_ipv4address(t *testing.T) {
}
func TestAccDockerContainer_ipv6address(t *testing.T) {
t.Skip("mavogel: need to fix ipv6 network state")
var c types.ContainerJSON
testCheck := func(*terraform.State) error {

View file

@ -24,8 +24,6 @@ func resourceDockerImageCreate(d *schema.ResourceData, meta interface{}) error {
}
d.SetId(apiImage.ID + d.Get("name").(string))
d.Set("latest", apiImage.ID)
return resourceDockerImageRead(d, meta)
}
@ -35,14 +33,18 @@ func resourceDockerImageRead(d *schema.ResourceData, meta interface{}) error {
if err := fetchLocalImages(&data, client); err != nil {
return fmt.Errorf("Error reading docker image list: %s", err)
}
for id := range data.DockerImages {
log.Printf("[DEBUG] local images data: %v", id)
}
foundImage := searchLocalImages(data, d.Get("name").(string))
if foundImage != nil {
d.Set("latest", foundImage.ID)
} else {
if foundImage == nil {
d.SetId("")
return nil
}
d.SetId(foundImage.ID + d.Get("name").(string))
d.Set("latest", foundImage.ID)
return nil
}
@ -73,9 +75,11 @@ func resourceDockerImageDelete(d *schema.ResourceData, meta interface{}) error {
func searchLocalImages(data Data, imageName string) *types.ImageSummary {
if apiImage, ok := data.DockerImages[imageName]; ok {
log.Printf("[DEBUG] found local image via imageName: %v", imageName)
return apiImage
}
if apiImage, ok := data.DockerImages[imageName+":latest"]; ok {
log.Printf("[DEBUG] found local image via imageName + latest: %v", imageName)
imageName = imageName + ":latest"
return apiImage
}

View file

@ -1,11 +1,11 @@
package docker
import (
"bytes"
"fmt"
"sort"
"log"
"net"
"regexp"
"strconv"
"github.com/hashicorp/terraform-plugin-sdk/helper/hashcode"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)
@ -14,6 +14,9 @@ func resourceDockerNetwork() *schema.Resource {
Create: resourceDockerNetworkCreate,
Read: resourceDockerNetworkRead,
Delete: resourceDockerNetworkDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"name": {
@ -38,7 +41,7 @@ func resourceDockerNetwork() *schema.Resource {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
Default: "bridge",
},
"options": {
@ -77,12 +80,15 @@ func resourceDockerNetwork() *schema.Resource {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: "default",
},
"ipam_config": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
ForceNew: true,
// DiffSuppressFunc: suppressIfIPAMConfigWithIpv6Changes(),
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"subnet": {
@ -110,7 +116,6 @@ func resourceDockerNetwork() *schema.Resource {
},
},
},
Set: resourceDockerIpamConfigHash,
},
"scope": {
@ -121,37 +126,50 @@ func resourceDockerNetwork() *schema.Resource {
}
}
func resourceDockerIpamConfigHash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
if v, ok := m["subnet"]; ok {
buf.WriteString(fmt.Sprintf("%v-", v.(string)))
}
if v, ok := m["ip_range"]; ok {
buf.WriteString(fmt.Sprintf("%v-", v.(string)))
}
if v, ok := m["gateway"]; ok {
buf.WriteString(fmt.Sprintf("%v-", v.(string)))
}
if v, ok := m["aux_address"]; ok {
auxAddress := v.(map[string]interface{})
keys := make([]string, len(auxAddress))
i := 0
for k := range auxAddress {
keys[i] = k
i++
func suppressIfIPAMConfigWithIpv6Changes() schema.SchemaDiffSuppressFunc {
return func(k, old, new string, d *schema.ResourceData) bool {
// the initial case when the resource is created
if old == "" && new != "" {
return false
}
sort.Strings(keys)
for _, k := range keys {
buf.WriteString(fmt.Sprintf("%v-%v-", k, auxAddress[k].(string)))
// if ipv6 is not given we do not consider
ipv6, ok := d.GetOk("ipv6")
if !ok {
return false
}
}
return hashcode.String(buf.String())
// if ipv6 is given but false we do not consider
isIPv6 := ipv6.(bool)
if !isIPv6 {
return false
}
if k == "ipam_config.#" {
log.Printf("[INFO] ipam_config: k: %q, old: %s, new: %s\n", k, old, new)
oldVal, _ := strconv.Atoi(string(old))
newVal, _ := strconv.Atoi(string(new))
log.Printf("[INFO] ipam_config: oldVal: %d, newVal: %d\n", oldVal, newVal)
if newVal <= oldVal {
log.Printf("[INFO] suppressingDiff for ipam_config: oldVal: %d, newVal: %d\n", oldVal, newVal)
return true
}
}
if regexp.MustCompile(`ipam_config\.\d+\.gateway`).MatchString(k) {
ip := net.ParseIP(old)
ipv4Address := ip.To4()
log.Printf("[INFO] ipam_config.gateway: k: %q, old: %s, new: %s - %v\n", k, old, new, ipv4Address != nil)
// is an ipv4Address and content changed from non-empty to empty
if ipv4Address != nil && old != "" && new == "" {
log.Printf("[INFO] suppressingDiff for ipam_config.gateway %q: oldVal: %s, newVal: %s\n", ipv4Address.String(), old, new)
return true
}
}
if regexp.MustCompile(`ipam_config\.\d+\.subnet`).MatchString(k) {
if old != "" && new == "" {
log.Printf("[INFO] suppressingDiff for ipam_config.subnet: oldVal: %s, newVal: %s\n", old, new)
return true
}
}
return false
}
}

View file

@ -66,7 +66,7 @@ func resourceDockerNetworkCreate(d *schema.ResourceData, meta interface{}) error
}
d.SetId(retNetwork.ID)
// d.Set("check_duplicate") TODO
return resourceDockerNetworkRead(d, meta)
}
@ -152,11 +152,14 @@ func resourceDockerNetworkReadRefreshFunc(
jsonObj, _ := json.MarshalIndent(retNetwork, "", "\t")
log.Printf("[DEBUG] Docker network inspect: %s", jsonObj)
d.Set("name", retNetwork.Name)
d.Set("labels", retNetwork.Labels)
d.Set("driver", retNetwork.Driver)
d.Set("internal", retNetwork.Internal)
d.Set("attachable", retNetwork.Attachable)
d.Set("ingress", retNetwork.Ingress)
d.Set("ipv6", retNetwork.EnableIPv6)
d.Set("driver", retNetwork.Driver)
d.Set("ipam_driver", retNetwork.IPAM.Driver)
d.Set("scope", retNetwork.Scope)
if retNetwork.Scope == "overlay" {
if retNetwork.Options != nil && len(retNetwork.Options) != 0 {
@ -169,6 +172,10 @@ func resourceDockerNetworkReadRefreshFunc(
d.Set("options", retNetwork.Options)
}
if err = d.Set("ipam_config", flattenIpamConfigSpec(retNetwork.IPAM.Config)); err != nil {
log.Printf("[WARN] failed to set ipam config from API: %s", err)
}
log.Println("[DEBUG] all network fields exposed")
return networkID, "all_fields", nil
}
@ -196,3 +203,31 @@ func resourceDockerNetworkRemoveRefreshFunc(
return networkID, "removed", nil
}
}
// TODO mavogel: separate structure file
// TODO 2: seems like we can replace the set hash generation with plain lists -> #219
func flattenIpamConfigSpec(in []network.IPAMConfig) *schema.Set { // []interface{} {
var out = make([]interface{}, len(in), len(in))
for i, v := range in {
log.Printf("[DEBUG] flatten ipam %d: %#v", i, v)
m := make(map[string]interface{})
if len(v.Subnet) > 0 {
m["subnet"] = v.Subnet
}
if len(v.IPRange) > 0 {
m["ip_range"] = v.IPRange
}
if len(v.Gateway) > 0 {
m["gateway"] = v.Gateway
}
if len(v.AuxAddress) > 0 {
m["aux_address"] = v.AuxAddress
}
out[i] = m
}
// log.Printf("[INFO] flatten ipam out: %#v", out)
imapConfigsResource := resourceDockerNetwork().Schema["ipam_config"].Elem.(*schema.Resource)
f := schema.HashResource(imapConfigsResource)
return schema.NewSet(f, out)
// return out
}

View file

@ -5,6 +5,7 @@ import (
"testing"
"context"
"github.com/docker/docker/api/types"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
@ -12,6 +13,7 @@ import (
func TestAccDockerNetwork_basic(t *testing.T) {
var n types.NetworkResource
resourceName := "docker_network.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
@ -20,13 +22,20 @@ func TestAccDockerNetwork_basic(t *testing.T) {
{
Config: testAccDockerNetworkConfig,
Check: resource.ComposeTestCheckFunc(
testAccNetwork("docker_network.foo", &n),
testAccNetwork(resourceName, &n),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
// TODO mavogel: add full network config test in #219
func testAccNetwork(n string, network *types.NetworkResource) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
@ -67,6 +76,7 @@ resource "docker_network" "foo" {
func TestAccDockerNetwork_internal(t *testing.T) {
var n types.NetworkResource
resourceName := "docker_network.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
@ -75,10 +85,15 @@ func TestAccDockerNetwork_internal(t *testing.T) {
{
Config: testAccDockerNetworkInternalConfig,
Check: resource.ComposeTestCheckFunc(
testAccNetwork("docker_network.foo", &n),
testAccNetwork(resourceName, &n),
testAccNetworkInternal(&n, true),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
@ -101,6 +116,7 @@ resource "docker_network" "foo" {
func TestAccDockerNetwork_attachable(t *testing.T) {
var n types.NetworkResource
resourceName := "docker_network.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
@ -109,10 +125,15 @@ func TestAccDockerNetwork_attachable(t *testing.T) {
{
Config: testAccDockerNetworkAttachableConfig,
Check: resource.ComposeTestCheckFunc(
testAccNetwork("docker_network.foo", &n),
testAccNetwork(resourceName, &n),
testAccNetworkAttachable(&n, true),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
@ -169,6 +190,7 @@ resource "docker_network" "foo" {
func TestAccDockerNetwork_ipv4(t *testing.T) {
var n types.NetworkResource
resourceName := "docker_network.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
@ -177,10 +199,15 @@ func TestAccDockerNetwork_ipv4(t *testing.T) {
{
Config: testAccDockerNetworkIPv4Config,
Check: resource.ComposeTestCheckFunc(
testAccNetwork("docker_network.foo", &n),
testAccNetwork(resourceName, &n),
testAccNetworkIPv4(&n, true),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
@ -207,7 +234,9 @@ resource "docker_network" "foo" {
`
func TestAccDockerNetwork_ipv6(t *testing.T) {
t.Skip("mavogel: need to fix ipv6 network state")
var n types.NetworkResource
resourceName := "docker_network.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
@ -216,10 +245,17 @@ func TestAccDockerNetwork_ipv6(t *testing.T) {
{
Config: testAccDockerNetworkIPv6Config,
Check: resource.ComposeTestCheckFunc(
testAccNetwork("docker_network.foo", &n),
testAccNetwork(resourceName, &n),
testAccNetworkIPv6(&n, true),
),
},
// TODO mavogel: ipam config goes from 2->1
// probably suppress diff -> #219
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
@ -233,7 +269,7 @@ func testAccNetworkIPv6(network *types.NetworkResource, internal bool) resource.
return fmt.Errorf("Bad value for IPAM configuration count: %d", len(network.IPAM.Config))
}
if network.IPAM.Config[1].Subnet != "fd00::1/64" {
return fmt.Errorf("Bad value for attribute 'subnet': %v", network.IPAM.Config[0].Subnet)
return fmt.Errorf("Bad value for attribute 'subnet': %v", network.IPAM.Config[1].Subnet)
}
return nil
}
@ -246,11 +282,16 @@ resource "docker_network" "foo" {
ipam_config {
subnet = "fd00::1/64"
}
# TODO mavogel: Would work but BC - 219
# ipam_config {
# subnet = "10.0.1.0/24"
# }
}
`
func TestAccDockerNetwork_labels(t *testing.T) {
var n types.NetworkResource
resourceName := "docker_network.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
@ -259,11 +300,16 @@ func TestAccDockerNetwork_labels(t *testing.T) {
{
Config: testAccDockerNetworkLabelsConfig,
Check: resource.ComposeTestCheckFunc(
testAccNetwork("docker_network.foo", &n),
testAccNetwork(resourceName, &n),
testAccNetworkLabel(&n, "com.docker.compose.network", "foo"),
testAccNetworkLabel(&n, "com.docker.compose.project", "test"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View file

@ -5,6 +5,7 @@ import (
"log"
"context"
"github.com/docker/docker/api/types/swarm"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)
@ -25,7 +26,7 @@ func resourceDockerSecret() *schema.Resource {
"data": {
Type: schema.TypeString,
Description: "User-defined name of the secret",
Description: "Base64-url-safe-encoded secret data",
Required: true,
Sensitive: true,
ForceNew: true,
@ -76,6 +77,10 @@ func resourceDockerSecretRead(d *schema.ResourceData, meta interface{}) error {
return nil
}
d.SetId(secret.ID)
d.Set("name", secret.Spec.Name)
// Note mavogel: secret data is not exposed via the API
// TODO next major if we do not explicitly do not store it in the state we could import it, but BC
// d.Set("data", base64.StdEncoding.EncodeToString(secret.Spec.Data))
return nil
}

View file

@ -17,6 +17,9 @@ func resourceDockerService() *schema.Resource {
Update: resourceDockerServiceUpdate,
Delete: resourceDockerServiceDelete,
Exists: resourceDockerServiceExists,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"auth": {

View file

@ -159,14 +159,12 @@ func TestAccDockerService_minimalSpec(t *testing.T) {
{
Config: `
provider "docker" {
alias = "private"
registry_auth {
address = "127.0.0.1:15000"
}
}
resource "docker_service" "foo" {
provider = "docker.private"
name = "tftest-service-basic"
task_spec {
container_spec {
@ -181,6 +179,11 @@ func TestAccDockerService_minimalSpec(t *testing.T) {
resource.TestMatchResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", regexp.MustCompile(`127.0.0.1:15000/tftest-service:v1@sha256.*`)),
),
},
{
ResourceName: "docker_service.foo",
ImportState: true,
ImportStateVerify: true,
},
},
CheckDestroy: checkAndRemoveImages,
})
@ -194,7 +197,6 @@ func TestAccDockerService_fullSpec(t *testing.T) {
{
Config: `
provider "docker" {
alias = "private"
registry_auth {
address = "127.0.0.1:15000"
}
@ -220,7 +222,6 @@ func TestAccDockerService_fullSpec(t *testing.T) {
}
resource "docker_service" "foo" {
provider = "docker.private"
name = "tftest-service-basic"
task_spec {
@ -468,6 +469,11 @@ func TestAccDockerService_fullSpec(t *testing.T) {
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1714132424.publish_mode", "ingress"),
),
},
{
ResourceName: "docker_service.foo",
ImportState: true,
ImportStateVerify: true,
},
},
CheckDestroy: checkAndRemoveImages,
})
@ -481,14 +487,12 @@ func TestAccDockerService_partialReplicationConfig(t *testing.T) {
{
Config: `
provider "docker" {
alias = "private"
registry_auth {
address = "127.0.0.1:15000"
}
}
resource "docker_service" "foo" {
provider = "docker.private"
name = "tftest-service-basic"
task_spec {
container_spec {
@ -508,14 +512,12 @@ func TestAccDockerService_partialReplicationConfig(t *testing.T) {
{
Config: `
provider "docker" {
alias = "private"
registry_auth {
address = "127.0.0.1:15000"
}
}
resource "docker_service" "foo" {
provider = "docker.private"
name = "tftest-service-basic"
task_spec {
container_spec {
@ -537,14 +539,12 @@ func TestAccDockerService_partialReplicationConfig(t *testing.T) {
{
Config: `
provider "docker" {
alias = "private"
registry_auth {
address = "127.0.0.1:15000"
}
}
resource "docker_service" "foo" {
provider = "docker.private"
name = "tftest-service-basic"
task_spec {
container_spec {
@ -565,6 +565,11 @@ func TestAccDockerService_partialReplicationConfig(t *testing.T) {
resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"),
),
},
{
ResourceName: "docker_service.foo",
ImportState: true,
ImportStateVerify: true,
},
},
CheckDestroy: checkAndRemoveImages,
})
@ -578,14 +583,12 @@ func TestAccDockerService_globalReplicationMode(t *testing.T) {
{
Config: `
provider "docker" {
alias = "private"
registry_auth {
address = "127.0.0.1:15000"
}
}
resource "docker_service" "foo" {
provider = "docker.private"
name = "tftest-service-basic"
task_spec {
container_spec {
@ -604,6 +607,11 @@ func TestAccDockerService_globalReplicationMode(t *testing.T) {
resource.TestCheckResourceAttr("docker_service.foo", "mode.0.global", "true"),
),
},
{
ResourceName: "docker_service.foo",
ImportState: true,
ImportStateVerify: true,
},
},
CheckDestroy: checkAndRemoveImages,
})
@ -646,14 +654,12 @@ func TestAccDockerService_ConflictingGlobalModeAndConverge(t *testing.T) {
{
Config: `
provider "docker" {
alias = "private"
registry_auth {
address = "127.0.0.1:15000"
}
}
resource "docker_service" "foo" {
provider = "docker.private"
name = "tftest-service-basic"
task_spec {
container_spec {
@ -688,15 +694,13 @@ func TestAccDockerService_privateImageConverge(t *testing.T) {
{
Config: fmt.Sprintf(`
provider "docker" {
alias = "private"
registry_auth {
address = "%s"
}
}
resource "docker_service" "bar" {
provider = "docker.private"
name = "tftest-service-bar"
resource "docker_service" "foo" {
name = "tftest-service-foo"
task_spec {
container_spec {
image = "%s"
@ -715,9 +719,9 @@ func TestAccDockerService_privateImageConverge(t *testing.T) {
}
`, registry, image),
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr("docker_service.bar", "id", serviceIDRegex),
resource.TestCheckResourceAttr("docker_service.bar", "name", "tftest-service-bar"),
resource.TestMatchResourceAttr("docker_service.bar", "task_spec.0.container_spec.0.image", regexp.MustCompile(`127.0.0.1:15000/tftest-service:v1@sha256.*`)),
resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex),
resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-foo"),
resource.TestMatchResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", regexp.MustCompile(`127.0.0.1:15000/tftest-service:v1@sha256.*`)),
),
},
},
@ -985,6 +989,11 @@ func TestAccDockerService_updateMultiplePropertiesConverge(t *testing.T) {
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.options.max-size", "15m"),
),
},
{
ResourceName: "docker_service.foo",
ImportState: true,
ImportStateVerify: true,
},
},
CheckDestroy: checkAndRemoveImages,
})
@ -1070,14 +1079,12 @@ func TestAccDockerService_convergeAndStopGracefully(t *testing.T) {
{
Config: `
provider "docker" {
alias = "private"
registry_auth {
address = "127.0.0.1:15000"
}
}
resource "docker_service" "foo" {
provider = "docker.private"
name = "tftest-service-basic-converge"
task_spec {
container_spec {

View file

@ -3,13 +3,14 @@ package docker
import (
"context"
"fmt"
"log"
"strings"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/volume"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"log"
"strings"
"time"
)
func resourceDockerVolume() *schema.Resource {
@ -17,6 +18,9 @@ func resourceDockerVolume() *schema.Resource {
Create: resourceDockerVolumeCreate,
Read: resourceDockerVolumeRead,
Delete: resourceDockerVolumeDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"name": {
@ -77,10 +81,6 @@ func resourceDockerVolumeCreate(d *schema.ResourceData, meta interface{}) error
}
d.SetId(retVolume.Name)
d.Set("name", retVolume.Name)
d.Set("driver", retVolume.Driver)
d.Set("mountpoint", retVolume.Mountpoint)
return resourceDockerVolumeRead(d, meta)
}
@ -97,7 +97,9 @@ func resourceDockerVolumeRead(d *schema.ResourceData, meta interface{}) error {
}
d.Set("name", retVolume.Name)
d.Set("labels", retVolume.Labels)
d.Set("driver", retVolume.Driver)
d.Set("driver_opts", retVolume.Options)
d.Set("mountpoint", retVolume.Mountpoint)
return nil

View file

@ -3,10 +3,11 @@ package docker
import (
"context"
"fmt"
"testing"
"github.com/docker/docker/api/types"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
"testing"
)
func TestAccDockerVolume_basic(t *testing.T) {
@ -24,6 +25,11 @@ func TestAccDockerVolume_basic(t *testing.T) {
resource.TestCheckResourceAttr("docker_volume.foo", "name", "testAccDockerVolume_basic"),
),
},
{
ResourceName: "docker_volume.foo",
ImportState: true,
ImportStateVerify: true,
},
},
})
}
@ -73,6 +79,11 @@ func TestAccDockerVolume_labels(t *testing.T) {
testAccVolumeLabel(&v, "com.docker.compose.volume", "foo"),
),
},
{
ResourceName: "docker_volume.foo",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View file

@ -98,3 +98,11 @@ The following arguments are supported:
The following attributes are exported in addition to the above configuration:
* `id` (string)
## Import
Docker config can be imported using the long id, e.g. for a config with the short id `p73jelnrme5f`:
```sh
$ terraform import docker_config.foo $(docker config inspect -f {{.ID}} p73)
```

View file

@ -284,5 +284,12 @@ The following attributes are exported:
* `gateway` - *Deprecated:* Use `network_data` instead. The network gateway of the container as read from its
NetworkSettings.
## Import
Docker containers can be imported using the long id, e.g. for a container named `foo`:
```sh
$ terraform import docker_container.foo $(docker inspect -f {{.ID}} foo)
```
[linkdoc] https://docs.docker.com/network/links/

View file

@ -66,3 +66,11 @@ The following attributes are exported in addition to the above configuration:
* `id` (string)
* `scope` (string)
## Import
Docker networks can be imported using the long id, e.g. for a network with the short id `p73jelnrme5f`:
```sh
$ terraform import docker_network.foo $(docker network inspect -f {{.ID}} p73)
```

View file

@ -63,3 +63,7 @@ The following arguments are supported:
The following attributes are exported in addition to the above configuration:
* `id` (string)
## Import
Docker secret cannot be imported as the secret data, once set, is never exposed again.

View file

@ -557,3 +557,11 @@ all tasks are up when a service is created, or to check if all tasks are success
The following attributes are exported in addition to the above configuration:
* `id` (string)
## Import
Docker service can be imported using the long id, e.g. for a service with the short id `55ba873dd`:
```sh
$ terraform import docker_service.foo $(docker service inspect -f {{.ID}} 55b)
```

View file

@ -39,3 +39,11 @@ The following arguments are supported:
The following attributes are exported in addition to the above configuration:
* `mountpoint` (string) - The mountpoint of the volume.
## Import
Docker volume can be imported using the long id, e.g. for a volume with the short id `ecae276c5`:
```sh
$ terraform import docker_volume.foo $(docker volume inspect -f {{.ID}} eca)
```