feat: support to import some docker_container's attributes (#234)

This commit is contained in:
Shunsuke Suzuki 2020-02-02 00:15:36 +09:00 committed by GitHub
parent ef47f0cbdb
commit 33c17570c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 215 additions and 25 deletions

View file

@ -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,

View file

@ -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
}

View file

@ -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
}