2015-03-27 18:33:17 -04:00
package docker
import (
2016-12-05 06:06:34 -05:00
"archive/tar"
"bytes"
2015-03-28 21:45:36 -04:00
"fmt"
2018-04-20 05:30:45 -04:00
"strconv"
"strings"
2015-03-27 18:33:17 -04:00
"testing"
2018-07-03 11:30:53 -04:00
"context"
"github.com/docker/docker/api/types"
2015-03-27 18:33:17 -04:00
"github.com/hashicorp/terraform/helper/resource"
2015-03-28 21:45:36 -04:00
"github.com/hashicorp/terraform/terraform"
2015-03-27 18:33:17 -04:00
)
func TestAccDockerContainer_basic ( t * testing . T ) {
2018-07-03 11:30:53 -04:00
var c types . ContainerJSON
2015-03-27 18:33:17 -04:00
resource . Test ( t , resource . TestCase {
PreCheck : func ( ) { testAccPreCheck ( t ) } ,
Providers : testAccProviders ,
Steps : [ ] resource . TestStep {
resource . TestStep {
Config : testAccDockerContainerConfig ,
Check : resource . ComposeTestCheckFunc (
2015-10-26 17:24:48 -04:00
testAccContainerRunning ( "docker_container.foo" , & c ) ,
2015-03-27 18:33:17 -04:00
) ,
} ,
} ,
} )
}
2017-05-15 06:09:50 -04:00
func TestAccDockerContainerPath_validation ( t * testing . T ) {
cases := [ ] struct {
Value string
ErrCount int
} {
{ Value : "/var/log" , ErrCount : 0 } ,
{ Value : "/tmp" , ErrCount : 0 } ,
{ Value : "C:\\Windows\\System32" , ErrCount : 0 } ,
{ Value : "C:\\Program Files\\MSBuild" , ErrCount : 0 } ,
{ Value : "test" , ErrCount : 1 } ,
{ Value : "C:Test" , ErrCount : 1 } ,
{ Value : "" , ErrCount : 1 } ,
}
for _ , tc := range cases {
_ , errors := validateDockerContainerPath ( tc . Value , "docker_container" )
if len ( errors ) != tc . ErrCount {
t . Fatalf ( "Expected the Docker Container Path to trigger a validation error" )
}
}
}
2016-01-15 16:59:33 -05:00
func TestAccDockerContainer_volume ( t * testing . T ) {
2018-07-03 11:30:53 -04:00
var c types . ContainerJSON
2016-01-15 16:59:33 -05:00
testCheck := func ( * terraform . State ) error {
2016-06-29 04:38:56 -04:00
if len ( c . Mounts ) != 1 {
return fmt . Errorf ( "Incorrect number of mounts: expected 1, got %d" , len ( c . Mounts ) )
2016-01-15 16:59:33 -05:00
}
for _ , v := range c . Mounts {
if v . Name != "testAccDockerContainerVolume_volume" {
continue
}
if v . Destination != "/tmp/volume" {
return fmt . Errorf ( "Bad destination on mount: expected /tmp/volume, got %q" , v . Destination )
}
if v . Mode != "rw" {
return fmt . Errorf ( "Bad mode on mount: expected rw, got %q" , v . Mode )
}
return nil
}
return fmt . Errorf ( "Mount for testAccDockerContainerVolume_volume not found" )
}
resource . Test ( t , resource . TestCase {
PreCheck : func ( ) { testAccPreCheck ( t ) } ,
Providers : testAccProviders ,
Steps : [ ] resource . TestStep {
resource . TestStep {
Config : testAccDockerContainerVolumeConfig ,
Check : resource . ComposeTestCheckFunc (
testAccContainerRunning ( "docker_container.foo" , & c ) ,
testCheck ,
) ,
} ,
} ,
} )
}
2015-10-27 12:08:57 -04:00
func TestAccDockerContainer_customized ( t * testing . T ) {
2018-07-03 11:30:53 -04:00
var c types . ContainerJSON
2015-10-26 17:24:48 -04:00
testCheck := func ( * terraform . State ) error {
if len ( c . Config . Entrypoint ) < 3 ||
( c . Config . Entrypoint [ 0 ] != "/bin/bash" &&
c . Config . Entrypoint [ 1 ] != "-c" &&
c . Config . Entrypoint [ 2 ] != "ping localhost" ) {
return fmt . Errorf ( "Container wrong entrypoint: %s" , c . Config . Entrypoint )
}
2015-10-27 12:08:57 -04:00
2016-04-04 22:43:59 -04:00
if c . Config . User != "root:root" {
return fmt . Errorf ( "Container wrong user: %s" , c . Config . User )
}
2015-10-27 12:08:57 -04:00
if c . HostConfig . RestartPolicy . Name == "on-failure" {
if c . HostConfig . RestartPolicy . MaximumRetryCount != 5 {
return fmt . Errorf ( "Container has wrong restart policy max retry count: %d" , c . HostConfig . RestartPolicy . MaximumRetryCount )
}
} else {
return fmt . Errorf ( "Container has wrong restart policy: %s" , c . HostConfig . RestartPolicy . Name )
}
2015-11-04 15:46:24 -05:00
if c . HostConfig . Memory != ( 512 * 1024 * 1024 ) {
2015-10-27 19:53:49 -04:00
return fmt . Errorf ( "Container has wrong memory setting: %d" , c . HostConfig . Memory )
}
2015-11-04 15:46:24 -05:00
if c . HostConfig . MemorySwap != ( 2048 * 1024 * 1024 ) {
2016-06-29 10:48:15 -04:00
return fmt . Errorf ( "Container has wrong memory swap setting: %d\n\r\tPlease check that you machine supports memory swap (you can do that by running 'docker info' command)." , c . HostConfig . MemorySwap )
2015-10-27 19:53:49 -04:00
}
2015-11-04 15:46:24 -05:00
if c . HostConfig . CPUShares != 32 {
2015-10-27 19:53:49 -04:00
return fmt . Errorf ( "Container has wrong cpu shares setting: %d" , c . HostConfig . CPUShares )
}
2015-11-03 15:20:58 -05:00
2016-06-29 08:38:46 -04:00
if len ( c . HostConfig . DNS ) != 1 {
return fmt . Errorf ( "Container does not have the correct number of dns entries: %d" , len ( c . HostConfig . DNS ) )
}
if c . HostConfig . DNS [ 0 ] != "8.8.8.8" {
return fmt . Errorf ( "Container has wrong dns setting: %v" , c . HostConfig . DNS [ 0 ] )
}
if len ( c . HostConfig . DNSOptions ) != 1 {
return fmt . Errorf ( "Container does not have the correct number of dns option entries: %d" , len ( c . HostConfig . DNS ) )
}
if c . HostConfig . DNSOptions [ 0 ] != "rotate" {
return fmt . Errorf ( "Container has wrong dns option setting: %v" , c . HostConfig . DNS [ 0 ] )
}
if len ( c . HostConfig . DNSSearch ) != 1 {
return fmt . Errorf ( "Container does not have the correct number of dns search entries: %d" , len ( c . HostConfig . DNS ) )
}
if c . HostConfig . DNSSearch [ 0 ] != "example.com" {
return fmt . Errorf ( "Container has wrong dns search setting: %v" , c . HostConfig . DNS [ 0 ] )
}
2017-03-07 11:48:20 -05:00
if len ( c . HostConfig . CapAdd ) != 1 {
return fmt . Errorf ( "Container does not have the correct number of Capabilities in ADD: %d" , len ( c . HostConfig . CapAdd ) )
}
if c . HostConfig . CapAdd [ 0 ] != "ALL" {
return fmt . Errorf ( "Container has wrong CapAdd setting: %v" , c . HostConfig . CapAdd [ 0 ] )
}
if len ( c . HostConfig . CapDrop ) != 1 {
return fmt . Errorf ( "Container does not have the correct number of Capabilities in Drop: %d" , len ( c . HostConfig . CapDrop ) )
}
if c . HostConfig . CapDrop [ 0 ] != "SYS_ADMIN" {
return fmt . Errorf ( "Container has wrong CapDrop setting: %v" , c . HostConfig . CapDrop [ 0 ] )
}
2016-06-29 08:38:46 -04:00
if c . HostConfig . CPUShares != 32 {
return fmt . Errorf ( "Container has wrong cpu shares setting: %d" , c . HostConfig . CPUShares )
}
if c . HostConfig . CPUShares != 32 {
return fmt . Errorf ( "Container has wrong cpu shares setting: %d" , c . HostConfig . CPUShares )
}
2015-11-03 15:20:58 -05:00
if c . Config . Labels [ "env" ] != "prod" || c . Config . Labels [ "role" ] != "test" {
return fmt . Errorf ( "Container does not have the correct labels" )
}
2015-11-04 12:42:55 -05:00
if c . HostConfig . LogConfig . Type != "json-file" {
return fmt . Errorf ( "Container does not have the correct log config: %s" , c . HostConfig . LogConfig . Type )
}
if c . HostConfig . LogConfig . Config [ "max-size" ] != "10m" {
return fmt . Errorf ( "Container does not have the correct max-size log option: %v" , c . HostConfig . LogConfig . Config [ "max-size" ] )
}
if c . HostConfig . LogConfig . Config [ "max-file" ] != "20" {
return fmt . Errorf ( "Container does not have the correct max-file log option: %v" , c . HostConfig . LogConfig . Config [ "max-file" ] )
}
2016-01-14 21:59:07 -05:00
if len ( c . HostConfig . ExtraHosts ) != 2 {
return fmt . Errorf ( "Container does not have correct number of extra host entries, got %d" , len ( c . HostConfig . ExtraHosts ) )
}
2018-02-09 14:11:30 -05:00
if c . HostConfig . ExtraHosts [ 0 ] != "testhost:10.0.1.0" {
return fmt . Errorf ( "Container has incorrect extra host string at 0: %q" , c . HostConfig . ExtraHosts [ 0 ] )
2016-01-14 21:59:07 -05:00
}
2018-02-09 14:11:30 -05:00
if c . HostConfig . ExtraHosts [ 1 ] != "testhost2:10.0.2.0" {
return fmt . Errorf ( "Container has incorrect extra host string at 1: %q" , c . HostConfig . ExtraHosts [ 1 ] )
2016-01-14 21:59:07 -05:00
}
2017-05-22 09:20:32 -04:00
if _ , ok := c . NetworkSettings . Networks [ "test" ] ; ! ok {
return fmt . Errorf ( "Container is not connected to the right user defined network: test" )
}
2018-04-20 05:35:49 -04:00
if len ( c . HostConfig . Ulimits ) != 2 {
return fmt . Errorf ( "Container doesn't have 2 ulimits" )
}
2018-04-20 06:07:35 -04:00
if c . HostConfig . Ulimits [ 1 ] . Name != "nproc" {
2018-04-20 05:35:49 -04:00
return fmt . Errorf ( "Container doesn't have a nproc ulimit" )
}
2018-04-20 06:07:35 -04:00
if c . HostConfig . Ulimits [ 1 ] . Hard != 1024 {
2018-04-20 05:35:49 -04:00
return fmt . Errorf ( "Container doesn't have a correct nproc hard limit" )
}
2018-04-20 06:07:35 -04:00
if c . HostConfig . Ulimits [ 1 ] . Soft != 512 {
2018-04-20 05:35:49 -04:00
return fmt . Errorf ( "Container doesn't have a correct mem nproc limit" )
}
2018-04-20 06:07:35 -04:00
if c . HostConfig . Ulimits [ 0 ] . Name != "nofile" {
2018-04-20 05:35:49 -04:00
return fmt . Errorf ( "Container doesn't have a nofile ulimit" )
}
2018-04-20 06:07:35 -04:00
if c . HostConfig . Ulimits [ 0 ] . Hard != 262144 {
2018-04-20 05:35:49 -04:00
return fmt . Errorf ( "Container doesn't have a correct nofile hard limit" )
}
2018-04-20 06:07:35 -04:00
if c . HostConfig . Ulimits [ 0 ] . Soft != 200000 {
2018-04-20 05:35:49 -04:00
return fmt . Errorf ( "Container doesn't have a correct nofile soft limit" )
}
2015-10-26 17:24:48 -04:00
return nil
}
resource . Test ( t , resource . TestCase {
PreCheck : func ( ) { testAccPreCheck ( t ) } ,
Providers : testAccProviders ,
Steps : [ ] resource . TestStep {
resource . TestStep {
2015-10-27 12:08:57 -04:00
Config : testAccDockerContainerCustomizedConfig ,
2015-10-26 17:24:48 -04:00
Check : resource . ComposeTestCheckFunc (
testAccContainerRunning ( "docker_container.foo" , & c ) ,
testCheck ,
) ,
} ,
} ,
} )
}
2016-12-05 06:06:34 -05:00
func TestAccDockerContainer_upload ( t * testing . T ) {
2018-07-03 11:30:53 -04:00
var c types . ContainerJSON
2016-12-05 06:06:34 -05:00
testCheck := func ( * terraform . State ) error {
2017-11-21 04:14:07 -05:00
client := testAccProvider . Meta ( ) . ( * ProviderConfig ) . DockerClient
2016-12-05 06:06:34 -05:00
2018-07-03 11:30:53 -04:00
srcPath := "/terraform/test.txt"
r , _ , err := client . CopyFromContainer ( context . Background ( ) , c . ID , srcPath )
if err != nil {
2016-12-05 06:06:34 -05:00
return fmt . Errorf ( "Unable to download a file from container: %s" , err )
}
tr := tar . NewReader ( r )
2018-04-20 05:30:45 -04:00
if header , err := tr . Next ( ) ; err != nil {
2016-12-05 06:06:34 -05:00
return fmt . Errorf ( "Unable to read content of tar archive: %s" , err )
2018-04-20 05:30:45 -04:00
} else {
mode := strconv . FormatInt ( header . Mode , 8 )
if ! strings . HasSuffix ( mode , "744" ) {
return fmt . Errorf ( "File permissions are incorrect: %s" , mode )
}
2016-12-05 06:06:34 -05:00
}
fbuf := new ( bytes . Buffer )
fbuf . ReadFrom ( tr )
content := fbuf . String ( )
if content != "foo" {
return fmt . Errorf ( "file content is invalid" )
}
return nil
}
resource . Test ( t , resource . TestCase {
PreCheck : func ( ) { testAccPreCheck ( t ) } ,
Providers : testAccProviders ,
Steps : [ ] resource . TestStep {
resource . TestStep {
Config : testAccDockerContainerUploadConfig ,
Check : resource . ComposeTestCheckFunc (
testAccContainerRunning ( "docker_container.foo" , & c ) ,
testCheck ,
) ,
} ,
} ,
} )
}
2018-04-20 05:14:44 -04:00
func TestAccDockerContainer_device ( t * testing . T ) {
2018-07-03 11:30:53 -04:00
var c types . ContainerJSON
2018-04-20 05:14:44 -04:00
testCheck := func ( * terraform . State ) error {
client := testAccProvider . Meta ( ) . ( * ProviderConfig ) . DockerClient
2018-07-03 11:30:53 -04:00
createExecOpts := types . ExecConfig {
Cmd : [ ] string { "dd" , "if=/dev/zero_test" , "of=/tmp/test.txt" , "count=10" , "bs=1" } ,
2018-04-20 05:14:44 -04:00
}
2018-07-03 11:30:53 -04:00
exec , err := client . ContainerExecCreate ( context . Background ( ) , c . ID , createExecOpts )
2018-04-20 05:14:44 -04:00
if err != nil {
return fmt . Errorf ( "Unable to create a exec instance on container: %s" , err )
}
2018-07-03 11:30:53 -04:00
startExecOpts := types . ExecStartCheck { }
if err := client . ContainerExecStart ( context . Background ( ) , exec . ID , startExecOpts ) ; err != nil {
2018-04-20 05:14:44 -04:00
return fmt . Errorf ( "Unable to run exec a instance on container: %s" , err )
}
2018-07-03 11:30:53 -04:00
srcPath := "/tmp/test.txt"
out , _ , err := client . CopyFromContainer ( context . Background ( ) , c . ID , srcPath )
if err != nil {
2018-04-20 05:14:44 -04:00
return fmt . Errorf ( "Unable to download a file from container: %s" , err )
}
2018-07-03 11:30:53 -04:00
tr := tar . NewReader ( out )
2018-04-20 05:14:44 -04:00
if _ , err := tr . Next ( ) ; err != nil {
return fmt . Errorf ( "Unable to read content of tar archive: %s" , err )
}
fbuf := new ( bytes . Buffer )
fbuf . ReadFrom ( tr )
content := fbuf . Bytes ( )
if len ( content ) != 10 {
return fmt . Errorf ( "Incorrect size of file: %d" , len ( content ) )
}
for _ , value := range content {
if value != 0 {
return fmt . Errorf ( "Incorrect content in file: %v" , content )
}
}
return nil
}
resource . Test ( t , resource . TestCase {
PreCheck : func ( ) { testAccPreCheck ( t ) } ,
Providers : testAccProviders ,
Steps : [ ] resource . TestStep {
resource . TestStep {
Config : testAccDockerContainerDeviceConfig ,
Check : resource . ComposeTestCheckFunc (
testAccContainerRunning ( "docker_container.foo" , & c ) ,
testCheck ,
) ,
} ,
} ,
} )
}
2018-07-03 11:30:53 -04:00
func testAccContainerRunning ( n string , container * types . ContainerJSON ) resource . TestCheckFunc {
2015-03-28 21:45:36 -04:00
return func ( s * terraform . State ) error {
rs , ok := s . RootModule ( ) . Resources [ n ]
if ! ok {
return fmt . Errorf ( "Not found: %s" , n )
}
if rs . Primary . ID == "" {
return fmt . Errorf ( "No ID is set" )
}
2017-11-21 04:14:07 -05:00
client := testAccProvider . Meta ( ) . ( * ProviderConfig ) . DockerClient
2018-07-03 11:30:53 -04:00
containers , err := client . ContainerList ( context . Background ( ) , types . ContainerListOptions { } )
2015-03-28 21:45:36 -04:00
if err != nil {
return err
}
for _ , c := range containers {
if c . ID == rs . Primary . ID {
2018-07-03 11:30:53 -04:00
inspected , err := client . ContainerInspect ( context . Background ( ) , c . ID )
2015-10-26 17:24:48 -04:00
if err != nil {
return fmt . Errorf ( "Container could not be inspected: %s" , err )
}
2018-07-03 11:30:53 -04:00
* container = inspected
2015-03-28 21:45:36 -04:00
return nil
}
}
return fmt . Errorf ( "Container not found: %s" , rs . Primary . ID )
}
}
2015-03-27 18:33:17 -04:00
const testAccDockerContainerConfig = `
resource "docker_image" "foo" {
2015-06-29 17:09:05 -04:00
name = "nginx:latest"
2015-03-27 18:33:17 -04:00
}
resource "docker_container" "foo" {
name = "tf-test"
image = "${docker_image.foo.latest}"
}
`
2016-01-14 21:59:07 -05:00
2016-01-15 16:59:33 -05:00
const testAccDockerContainerVolumeConfig = `
resource "docker_image" "foo" {
name = "nginx:latest"
}
resource "docker_volume" "foo" {
name = "testAccDockerContainerVolume_volume"
}
resource "docker_container" "foo" {
name = "tf-test"
image = "${docker_image.foo.latest}"
volumes {
volume_name = "${docker_volume.foo.name}"
container_path = "/tmp/volume"
read_only = false
}
}
`
2015-10-27 12:08:57 -04:00
const testAccDockerContainerCustomizedConfig = `
2015-10-26 17:24:48 -04:00
resource "docker_image" "foo" {
name = "nginx:latest"
}
resource "docker_container" "foo" {
name = "tf-test"
image = "${docker_image.foo.latest}"
2016-01-14 21:59:07 -05:00
entrypoint = [ "/bin/bash" , "-c" , "ping localhost" ]
2016-04-04 22:43:59 -04:00
user = "root:root"
2016-01-14 21:59:07 -05:00
restart = "on-failure"
2016-07-11 11:03:02 -04:00
destroy_grace_seconds = 10
2016-01-14 21:59:07 -05:00
max_retry_count = 5
memory = 512
memory_swap = 2048
cpu_shares = 32
2017-03-07 11:48:20 -05:00
capabilities {
add = [ "ALL" ]
drop = [ "SYS_ADMIN" ]
}
2016-06-29 08:38:46 -04:00
dns = [ "8.8.8.8" ]
dns_opts = [ "rotate" ]
dns_search = [ "example.com" ]
2016-01-14 21:59:07 -05:00
labels {
env = "prod"
role = "test"
}
log_driver = "json-file"
log_opts = {
max - size = "10m"
max - file = 20
2015-11-04 12:42:55 -05:00
}
2016-01-01 03:57:21 -05:00
network_mode = "bridge"
2016-01-14 21:59:07 -05:00
2017-05-22 09:20:32 -04:00
networks = [ "${docker_network.test_network.name}" ]
network_alias = [ "tftest" ]
2016-01-14 21:59:07 -05:00
host {
host = "testhost"
ip = "10.0.1.0"
}
host {
host = "testhost2"
ip = "10.0.2.0"
}
2018-04-20 05:35:49 -04:00
ulimit {
name = "nproc"
hard = 1024
soft = 512
}
ulimit {
name = "nofile"
hard = 262144
soft = 200000
}
2015-10-26 17:24:48 -04:00
}
2017-05-22 09:20:32 -04:00
resource "docker_network" "test_network" {
name = "test"
}
2015-10-26 17:24:48 -04:00
`
2016-12-05 06:06:34 -05:00
const testAccDockerContainerUploadConfig = `
resource "docker_image" "foo" {
name = "nginx:latest"
}
resource "docker_container" "foo" {
name = "tf-test"
image = "${docker_image.foo.latest}"
upload {
content = "foo"
file = "/terraform/test.txt"
2018-04-20 05:30:45 -04:00
executable = true
2016-12-05 06:06:34 -05:00
}
}
`
2018-04-20 05:14:44 -04:00
const testAccDockerContainerDeviceConfig = `
resource "docker_image" "foo" {
name = "nginx:latest"
}
resource "docker_container" "foo" {
name = "tf-test"
image = "${docker_image.foo.latest}"
devices {
host_path = "/dev/zero"
container_path = "/dev/zero_test"
}
}
`