mirror of
https://github.com/kreuzwerker/terraform-provider-docker.git
synced 2025-12-24 08:39:59 -05:00
feat: support to import some docker_container's attributes (#234)
This commit is contained in:
parent
ef47f0cbdb
commit
33c17570c9
3 changed files with 215 additions and 25 deletions
|
|
@ -114,6 +114,7 @@ func resourceDockerContainer() *schema.Resource {
|
|||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"domainname": {
|
||||
|
|
@ -126,6 +127,7 @@ func resourceDockerContainer() *schema.Resource {
|
|||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Computed: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
|
||||
|
|
@ -133,6 +135,7 @@ func resourceDockerContainer() *schema.Resource {
|
|||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Computed: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
|
||||
|
|
@ -186,7 +189,7 @@ func resourceDockerContainer() *schema.Resource {
|
|||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"working_dir": &schema.Schema{
|
||||
"working_dir": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
|
|
@ -196,6 +199,7 @@ func resourceDockerContainer() *schema.Resource {
|
|||
Optional: true,
|
||||
ForceNew: true,
|
||||
MaxItems: 1,
|
||||
// TODO implement DiffSuppressFunc
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"add": {
|
||||
|
|
@ -295,6 +299,7 @@ func resourceDockerContainer() *schema.Resource {
|
|||
Type: schema.TypeList,
|
||||
Description: "Optional configuration for the tmpfs type",
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
|
|
@ -452,6 +457,7 @@ func resourceDockerContainer() *schema.Resource {
|
|||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Computed: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Set: schema.HashString,
|
||||
},
|
||||
|
|
@ -555,6 +561,7 @@ func resourceDockerContainer() *schema.Resource {
|
|||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Computed: true,
|
||||
Elem: labelSchema,
|
||||
},
|
||||
|
||||
|
|
@ -576,6 +583,7 @@ func resourceDockerContainer() *schema.Resource {
|
|||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Computed: true,
|
||||
ValidateFunc: validateIntegerGeqThan(0),
|
||||
},
|
||||
|
||||
|
|
@ -620,6 +628,16 @@ func resourceDockerContainer() *schema.Resource {
|
|||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
DiffSuppressFunc: func(k, oldV, newV string, d *schema.ResourceData) bool {
|
||||
// treat "" as "default", which is Docker's default value
|
||||
if oldV == "" {
|
||||
oldV = "default"
|
||||
}
|
||||
if newV == "" {
|
||||
newV = "default"
|
||||
}
|
||||
return oldV == newV
|
||||
},
|
||||
},
|
||||
|
||||
"networks": {
|
||||
|
|
@ -763,6 +781,7 @@ func resourceDockerContainer() *schema.Resource {
|
|||
Description: "IPC sharing mode for the container",
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Computed: true,
|
||||
},
|
||||
"group_add": {
|
||||
Type: schema.TypeSet,
|
||||
|
|
@ -929,6 +948,16 @@ func resourceDockerContainerV1() *schema.Resource {
|
|||
ForceNew: true,
|
||||
Default: "no",
|
||||
ValidateFunc: validateStringMatchesPattern(`^(no|on-failure|always|unless-stopped)$`),
|
||||
DiffSuppressFunc: func(k, oldV, newV string, d *schema.ResourceData) bool {
|
||||
// treat "" as "no", which is Docker's default value
|
||||
if oldV == "" {
|
||||
oldV = "no"
|
||||
}
|
||||
if newV == "" {
|
||||
newV = "no"
|
||||
}
|
||||
return oldV == newV
|
||||
},
|
||||
},
|
||||
|
||||
"max_retry_count": {
|
||||
|
|
@ -936,7 +965,7 @@ func resourceDockerContainerV1() *schema.Resource {
|
|||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"working_dir": &schema.Schema{
|
||||
"working_dir": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err
|
|||
for _, rawTmpfsOptions := range value.([]interface{}) {
|
||||
rawTmpfsOptions := rawTmpfsOptions.(map[string]interface{})
|
||||
if value, ok := rawTmpfsOptions["size_bytes"]; ok {
|
||||
mountInstance.TmpfsOptions.SizeBytes = value.(int64)
|
||||
mountInstance.TmpfsOptions.SizeBytes = (int64)(value.(int))
|
||||
}
|
||||
if value, ok := rawTmpfsOptions["mode"]; ok {
|
||||
mountInstance.TmpfsOptions.Mode = os.FileMode(value.(int))
|
||||
|
|
@ -580,9 +580,104 @@ 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)
|
||||
d.Set("name", strings.TrimLeft(container.Name, "/")) // api prefixes with '/' ...
|
||||
d.Set("rm", container.HostConfig.AutoRemove)
|
||||
d.Set("read_only", container.HostConfig.ReadonlyRootfs)
|
||||
// "start" can't be imported
|
||||
// attach
|
||||
// logs
|
||||
// "must_run" can't be imported
|
||||
// container_logs
|
||||
d.Set("image", container.Image)
|
||||
d.Set("hostname", container.Config.Hostname)
|
||||
d.Set("domainname", container.Config.Domainname)
|
||||
d.Set("command", container.Config.Cmd)
|
||||
d.Set("entrypoint", container.Config.Entrypoint)
|
||||
d.Set("user", container.Config.User)
|
||||
d.Set("dns", container.HostConfig.DNS)
|
||||
d.Set("dns_opts", container.HostConfig.DNSOptions)
|
||||
d.Set("dns_search", container.HostConfig.DNSSearch)
|
||||
d.Set("publish_all_ports", container.HostConfig.PublishAllPorts)
|
||||
d.Set("restart", container.HostConfig.RestartPolicy.Name)
|
||||
d.Set("max_retry_count", container.HostConfig.RestartPolicy.MaximumRetryCount)
|
||||
d.Set("working_dir", container.Config.WorkingDir)
|
||||
if len(container.HostConfig.CapAdd) > 0 || len(container.HostConfig.CapDrop) > 0 {
|
||||
// TODO implement DiffSuppressFunc
|
||||
d.Set("capabilities", []interface{}{
|
||||
map[string]interface{}{
|
||||
"add": container.HostConfig.CapAdd,
|
||||
"drop": container.HostConfig.CapDrop,
|
||||
},
|
||||
})
|
||||
}
|
||||
d.Set("mounts", getDockerContainerMounts(container))
|
||||
// volumes
|
||||
d.Set("tmpfs", container.HostConfig.Tmpfs)
|
||||
d.Set("host", container.HostConfig.ExtraHosts)
|
||||
ulimits := make([]interface{}, len(container.HostConfig.Ulimits))
|
||||
for i, ul := range container.HostConfig.Ulimits {
|
||||
ulimits[i] = map[string]interface{}{
|
||||
"name": ul.Name,
|
||||
"soft": ul.Soft,
|
||||
"hard": ul.Hard,
|
||||
}
|
||||
}
|
||||
d.Set("ulimit", ulimits)
|
||||
d.Set("env", container.Config.Env)
|
||||
d.Set("links", container.HostConfig.Links)
|
||||
d.Set("privileged", container.HostConfig.Privileged)
|
||||
devices := make([]interface{}, len(container.HostConfig.Devices))
|
||||
for i, device := range container.HostConfig.Devices {
|
||||
devices[i] = map[string]interface{}{
|
||||
"host_path": device.PathOnHost,
|
||||
"container_path": device.PathInContainer,
|
||||
"permissions": device.CgroupPermissions,
|
||||
}
|
||||
}
|
||||
d.Set("devices", devices)
|
||||
// "destroy_grace_seconds" can't be imported
|
||||
labels := make([]interface{}, len(container.Config.Labels))
|
||||
i := 0
|
||||
for k, v := range container.Config.Labels {
|
||||
labels[i] = map[string]interface{}{
|
||||
"label": k,
|
||||
"value": v,
|
||||
}
|
||||
i++
|
||||
}
|
||||
d.Set("labels", labels)
|
||||
d.Set("memory", container.HostConfig.Memory/1024/1024)
|
||||
if container.HostConfig.MemorySwap > 0 {
|
||||
d.Set("memory_swap", container.HostConfig.MemorySwap/1024/1024)
|
||||
} else {
|
||||
d.Set("memory_swap", container.HostConfig.MemorySwap)
|
||||
}
|
||||
d.Set("shm_size", container.HostConfig.ShmSize/1024/1024)
|
||||
d.Set("cpu_shares", container.HostConfig.CPUShares)
|
||||
d.Set("cpu_set", container.HostConfig.CpusetCpus)
|
||||
d.Set("log_driver", container.HostConfig.LogConfig.Type)
|
||||
d.Set("log_opts", container.HostConfig.LogConfig.Config)
|
||||
// "network_alias" is deprecated
|
||||
d.Set("network_mode", container.HostConfig.NetworkMode)
|
||||
// networks
|
||||
// networks_advanced
|
||||
d.Set("pid_mode", container.HostConfig.PidMode)
|
||||
d.Set("userns_mode", container.HostConfig.UsernsMode)
|
||||
// "upload" can't be imported
|
||||
if container.Config.Healthcheck != nil {
|
||||
d.Set("healthcheck", []interface{}{
|
||||
map[string]interface{}{
|
||||
"test": container.Config.Healthcheck.Test,
|
||||
"interval": container.Config.Healthcheck.Interval.String(),
|
||||
"timeout": container.Config.Healthcheck.Timeout.String(),
|
||||
"start_period": container.Config.Healthcheck.StartPeriod.String(),
|
||||
"retries": container.Config.Healthcheck.Retries,
|
||||
},
|
||||
})
|
||||
}
|
||||
d.Set("sysctls", container.HostConfig.Sysctls)
|
||||
d.Set("ipc_mode", container.HostConfig.IpcMode)
|
||||
d.Set("group_add", container.HostConfig.GroupAdd)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -873,3 +968,50 @@ func deviceSetToDockerDevices(devices *schema.Set) []container.DeviceMapping {
|
|||
}
|
||||
return retDevices
|
||||
}
|
||||
|
||||
func getDockerContainerMounts(container types.ContainerJSON) []map[string]interface{} {
|
||||
mounts := []map[string]interface{}{}
|
||||
for _, mount := range container.HostConfig.Mounts {
|
||||
m := map[string]interface{}{
|
||||
"target": mount.Target,
|
||||
"source": mount.Source,
|
||||
"type": mount.Type,
|
||||
"read_only": mount.ReadOnly,
|
||||
}
|
||||
if mount.BindOptions != nil {
|
||||
m["bind_options"] = []map[string]interface{}{
|
||||
{
|
||||
"propagation": mount.BindOptions.Propagation,
|
||||
},
|
||||
}
|
||||
}
|
||||
if mount.VolumeOptions != nil {
|
||||
labels := []map[string]string{}
|
||||
for k, v := range mount.VolumeOptions.Labels {
|
||||
labels = append(labels, map[string]string{
|
||||
"label": k,
|
||||
"volume": v,
|
||||
})
|
||||
}
|
||||
m["volume_options"] = []map[string]interface{}{
|
||||
{
|
||||
"no_copy": mount.VolumeOptions.NoCopy,
|
||||
"labels": labels,
|
||||
"driver_name": mount.VolumeOptions.DriverConfig.Name,
|
||||
"driver_options": mount.VolumeOptions.DriverConfig.Options,
|
||||
},
|
||||
}
|
||||
}
|
||||
if mount.TmpfsOptions != nil {
|
||||
m["tmpfs_options"] = []map[string]interface{}{
|
||||
{
|
||||
"size_bytes": mount.TmpfsOptions.SizeBytes,
|
||||
"mode": mount.TmpfsOptions.Mode,
|
||||
},
|
||||
}
|
||||
}
|
||||
mounts = append(mounts, m)
|
||||
}
|
||||
|
||||
return mounts
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,21 +66,29 @@ func TestAccDockerContainer_basic(t *testing.T) {
|
|||
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",
|
||||
// },
|
||||
// },
|
||||
{
|
||||
ResourceName: "docker_container.foo",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateVerifyIgnore: []string{
|
||||
"attach",
|
||||
"log_driver",
|
||||
"logs",
|
||||
"must_run",
|
||||
"restart",
|
||||
"rm",
|
||||
"start",
|
||||
"container_logs",
|
||||
"destroy_grace_seconds",
|
||||
"upload",
|
||||
|
||||
// TODO mavogel: Will be done in #219
|
||||
"volumes",
|
||||
"network_alias",
|
||||
"networks",
|
||||
"network_advanced",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
@ -257,7 +265,7 @@ func TestAccDockerContainer_tmpfs(t *testing.T) {
|
|||
return fmt.Errorf("Incorrect number of tmpfs: expected 1, got %d", len(c.HostConfig.Tmpfs))
|
||||
}
|
||||
|
||||
for mountPath, _ := range c.HostConfig.Tmpfs {
|
||||
for mountPath := range c.HostConfig.Tmpfs {
|
||||
if mountPath != "/mount/tmpfs" {
|
||||
return fmt.Errorf("Bad destination on tmpfs: expected /mount/tmpfs, got %q", mountPath)
|
||||
}
|
||||
|
|
@ -567,7 +575,7 @@ func TestAccDockerContainer_customized(t *testing.T) {
|
|||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccContainerRunning("docker_container.foo", &c),
|
||||
testCheck,
|
||||
testCheckLabelMap("docker_container.foo", "labels", map[string]string{"env": "prod", "role": "test"}),
|
||||
testCheckLabelMap("docker_container.foo", "labels", map[string]string{"env": "prod", "role": "test", "maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"}),
|
||||
),
|
||||
},
|
||||
},
|
||||
|
|
@ -1548,10 +1556,16 @@ provider "docker" {
|
|||
}
|
||||
}
|
||||
|
||||
resource "docker_image" "foo" {
|
||||
provider = "docker.private"
|
||||
name = "%s"
|
||||
keep_locally = true
|
||||
}
|
||||
|
||||
resource "docker_container" "foo" {
|
||||
provider = "docker.private"
|
||||
name = "tf-test"
|
||||
image = "%s"
|
||||
image = docker_image.foo.latest
|
||||
}
|
||||
`
|
||||
|
||||
|
|
@ -1686,6 +1700,10 @@ resource "docker_container" "foo" {
|
|||
label = "role"
|
||||
value = "test"
|
||||
}
|
||||
labels {
|
||||
label = "maintainer"
|
||||
value = "NGINX Docker Maintainers <docker-maint@nginx.com>"
|
||||
}
|
||||
log_driver = "json-file"
|
||||
log_opts = {
|
||||
max-size = "10m"
|
||||
|
|
@ -1784,6 +1802,7 @@ resource "docker_container" "foo" {
|
|||
devices {
|
||||
host_path = "/dev/zero"
|
||||
container_path = "/dev/zero_test"
|
||||
permissions = "rwm"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
|
@ -1920,7 +1939,7 @@ resource "docker_image" "foo" {
|
|||
|
||||
resource "docker_container" "foo" {
|
||||
name = "tf-test"
|
||||
image = "nginx:latest"
|
||||
image = docker_image.foo.latest
|
||||
start = false
|
||||
must_run = false
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue