github.com/mitchellh/packer@v1.3.2/builder/docker/communicator_test.go (about)

     1  package docker
     2  
     3  import (
     4  	"crypto/sha256"
     5  	"io/ioutil"
     6  	"os"
     7  	"os/exec"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/hashicorp/packer/packer"
    12  	"github.com/hashicorp/packer/provisioner/file"
    13  	"github.com/hashicorp/packer/provisioner/shell"
    14  	"github.com/hashicorp/packer/template"
    15  )
    16  
    17  func TestCommunicator_impl(t *testing.T) {
    18  	var _ packer.Communicator = new(Communicator)
    19  }
    20  
    21  // TestUploadDownload verifies that basic upload / download functionality works
    22  func TestUploadDownload(t *testing.T) {
    23  	ui := packer.TestUi(t)
    24  	cache := &packer.FileCache{CacheDir: os.TempDir()}
    25  
    26  	tpl, err := template.Parse(strings.NewReader(dockerBuilderConfig))
    27  	if err != nil {
    28  		t.Fatalf("Unable to parse config: %s", err)
    29  	}
    30  
    31  	if os.Getenv("PACKER_ACC") == "" {
    32  		t.Skip("This test is only run with PACKER_ACC=1")
    33  	}
    34  	cmd := exec.Command("docker", "-v")
    35  	cmd.Run()
    36  	if !cmd.ProcessState.Success() {
    37  		t.Error("docker command not found; please make sure docker is installed")
    38  	}
    39  
    40  	// Setup the builder
    41  	builder := &Builder{}
    42  	warnings, err := builder.Prepare(tpl.Builders["docker"].Config)
    43  	if err != nil {
    44  		t.Fatalf("Error preparing configuration %s", err)
    45  	}
    46  	if len(warnings) > 0 {
    47  		t.Fatal("Encountered configuration warnings; aborting")
    48  	}
    49  
    50  	// Setup the provisioners
    51  	upload := &file.Provisioner{}
    52  	err = upload.Prepare(tpl.Provisioners[0].Config)
    53  	if err != nil {
    54  		t.Fatalf("Error preparing upload: %s", err)
    55  	}
    56  	download := &file.Provisioner{}
    57  	err = download.Prepare(tpl.Provisioners[1].Config)
    58  	if err != nil {
    59  		t.Fatalf("Error preparing download: %s", err)
    60  	}
    61  	// Preemptive cleanup. Honestly I don't know why you would want to get rid
    62  	// of my strawberry cake. It's so tasty! Do you not like cake? Are you a
    63  	// cake-hater? Or are you keeping all the cake all for yourself? So selfish!
    64  	defer os.Remove("my-strawberry-cake")
    65  
    66  	// Add hooks so the provisioners run during the build
    67  	hooks := map[string][]packer.Hook{}
    68  	hooks[packer.HookProvision] = []packer.Hook{
    69  		&packer.ProvisionHook{
    70  			Provisioners: []*packer.HookedProvisioner{
    71  				{Provisioner: upload, Config: nil, TypeName: ""},
    72  				{Provisioner: download, Config: nil, TypeName: ""},
    73  			},
    74  		},
    75  	}
    76  	hook := &packer.DispatchHook{Mapping: hooks}
    77  
    78  	// Run things
    79  	artifact, err := builder.Run(ui, hook, cache)
    80  	if err != nil {
    81  		t.Fatalf("Error running build %s", err)
    82  	}
    83  	// Preemptive cleanup
    84  	defer artifact.Destroy()
    85  
    86  	// Verify that the thing we downloaded is the same thing we sent up.
    87  	// Complain loudly if it isn't.
    88  	inputFile, err := ioutil.ReadFile("test-fixtures/onecakes/strawberry")
    89  	if err != nil {
    90  		t.Fatalf("Unable to read input file: %s", err)
    91  	}
    92  	outputFile, err := ioutil.ReadFile("my-strawberry-cake")
    93  	if err != nil {
    94  		t.Fatalf("Unable to read output file: %s", err)
    95  	}
    96  	if sha256.Sum256(inputFile) != sha256.Sum256(outputFile) {
    97  		t.Fatalf("Input and output files do not match\n"+
    98  			"Input:\n%s\nOutput:\n%s\n", inputFile, outputFile)
    99  	}
   100  }
   101  
   102  // TestLargeDownload verifies that files are the appropriate size after being
   103  // downloaded. This is to identify and fix the race condition in #2793. You may
   104  // need to use github.com/cbednarski/rerun to verify since this problem occurs
   105  // only intermittently.
   106  func TestLargeDownload(t *testing.T) {
   107  	ui := packer.TestUi(t)
   108  	cache := &packer.FileCache{CacheDir: os.TempDir()}
   109  
   110  	tpl, err := template.Parse(strings.NewReader(dockerLargeBuilderConfig))
   111  	if err != nil {
   112  		t.Fatalf("Unable to parse config: %s", err)
   113  	}
   114  
   115  	if os.Getenv("PACKER_ACC") == "" {
   116  		t.Skip("This test is only run with PACKER_ACC=1")
   117  	}
   118  	cmd := exec.Command("docker", "-v")
   119  	cmd.Run()
   120  	if !cmd.ProcessState.Success() {
   121  		t.Error("docker command not found; please make sure docker is installed")
   122  	}
   123  
   124  	// Setup the builder
   125  	builder := &Builder{}
   126  	warnings, err := builder.Prepare(tpl.Builders["docker"].Config)
   127  	if err != nil {
   128  		t.Fatalf("Error preparing configuration %s", err)
   129  	}
   130  	if len(warnings) > 0 {
   131  		t.Fatal("Encountered configuration warnings; aborting")
   132  	}
   133  
   134  	// Setup the provisioners
   135  	shell := &shell.Provisioner{}
   136  	err = shell.Prepare(tpl.Provisioners[0].Config)
   137  	if err != nil {
   138  		t.Fatalf("Error preparing shell provisioner: %s", err)
   139  	}
   140  	downloadCupcake := &file.Provisioner{}
   141  	err = downloadCupcake.Prepare(tpl.Provisioners[1].Config)
   142  	if err != nil {
   143  		t.Fatalf("Error preparing downloadCupcake: %s", err)
   144  	}
   145  	downloadBigcake := &file.Provisioner{}
   146  	err = downloadBigcake.Prepare(tpl.Provisioners[2].Config)
   147  	if err != nil {
   148  		t.Fatalf("Error preparing downloadBigcake: %s", err)
   149  	}
   150  
   151  	// Preemptive cleanup.
   152  	defer os.Remove("cupcake")
   153  	defer os.Remove("bigcake")
   154  
   155  	// Add hooks so the provisioners run during the build
   156  	hooks := map[string][]packer.Hook{}
   157  	hooks[packer.HookProvision] = []packer.Hook{
   158  		&packer.ProvisionHook{
   159  			Provisioners: []*packer.HookedProvisioner{
   160  				{Provisioner: shell, Config: nil, TypeName: ""},
   161  				{Provisioner: downloadCupcake, Config: nil, TypeName: ""},
   162  				{Provisioner: downloadBigcake, Config: nil, TypeName: ""},
   163  			},
   164  		},
   165  	}
   166  	hook := &packer.DispatchHook{Mapping: hooks}
   167  
   168  	// Run things
   169  	artifact, err := builder.Run(ui, hook, cache)
   170  	if err != nil {
   171  		t.Fatalf("Error running build %s", err)
   172  	}
   173  	// Preemptive cleanup
   174  	defer artifact.Destroy()
   175  
   176  	// Verify that the things we downloaded are the right size. Complain loudly
   177  	// if they are not.
   178  	//
   179  	// cupcake should be 2097152 bytes
   180  	// bigcake should be 104857600 bytes
   181  	cupcake, err := os.Stat("cupcake")
   182  	if err != nil {
   183  		t.Fatalf("Unable to stat cupcake file: %s", err)
   184  	}
   185  	cupcakeExpected := int64(2097152)
   186  	if cupcake.Size() != cupcakeExpected {
   187  		t.Errorf("Expected cupcake to be %d bytes; found %d", cupcakeExpected, cupcake.Size())
   188  	}
   189  
   190  	bigcake, err := os.Stat("bigcake")
   191  	if err != nil {
   192  		t.Fatalf("Unable to stat bigcake file: %s", err)
   193  	}
   194  	bigcakeExpected := int64(104857600)
   195  	if bigcake.Size() != bigcakeExpected {
   196  		t.Errorf("Expected bigcake to be %d bytes; found %d", bigcakeExpected, bigcake.Size())
   197  	}
   198  
   199  	// TODO if we can, calculate a sha inside the container and compare to the
   200  	// one we get after we pull it down. We will probably have to parse the log
   201  	// or ui output to do this because we use /dev/urandom to create the file.
   202  
   203  	// if sha256.Sum256(inputFile) != sha256.Sum256(outputFile) {
   204  	//	t.Fatalf("Input and output files do not match\n"+
   205  	//		"Input:\n%s\nOutput:\n%s\n", inputFile, outputFile)
   206  	// }
   207  
   208  }
   209  
   210  // TestFixUploadOwner verifies that owner of uploaded files is the user the container is running as.
   211  func TestFixUploadOwner(t *testing.T) {
   212  	ui := packer.TestUi(t)
   213  	cache := &packer.FileCache{CacheDir: os.TempDir()}
   214  
   215  	tpl, err := template.Parse(strings.NewReader(testFixUploadOwnerTemplate))
   216  	if err != nil {
   217  		t.Fatalf("Unable to parse config: %s", err)
   218  	}
   219  
   220  	if os.Getenv("PACKER_ACC") == "" {
   221  		t.Skip("This test is only run with PACKER_ACC=1")
   222  	}
   223  	cmd := exec.Command("docker", "-v")
   224  	cmd.Run()
   225  	if !cmd.ProcessState.Success() {
   226  		t.Error("docker command not found; please make sure docker is installed")
   227  	}
   228  
   229  	// Setup the builder
   230  	builder := &Builder{}
   231  	warnings, err := builder.Prepare(tpl.Builders["docker"].Config)
   232  	if err != nil {
   233  		t.Fatalf("Error preparing configuration %s", err)
   234  	}
   235  	if len(warnings) > 0 {
   236  		t.Fatal("Encountered configuration warnings; aborting")
   237  	}
   238  
   239  	// Setup the provisioners
   240  	fileProvisioner := &file.Provisioner{}
   241  	err = fileProvisioner.Prepare(tpl.Provisioners[0].Config)
   242  	if err != nil {
   243  		t.Fatalf("Error preparing single file upload provisioner: %s", err)
   244  	}
   245  
   246  	dirProvisioner := &file.Provisioner{}
   247  	err = dirProvisioner.Prepare(tpl.Provisioners[1].Config)
   248  	if err != nil {
   249  		t.Fatalf("Error preparing directory upload provisioner: %s", err)
   250  	}
   251  
   252  	shellProvisioner := &shell.Provisioner{}
   253  	err = shellProvisioner.Prepare(tpl.Provisioners[2].Config)
   254  	if err != nil {
   255  		t.Fatalf("Error preparing shell provisioner: %s", err)
   256  	}
   257  
   258  	verifyProvisioner := &shell.Provisioner{}
   259  	err = verifyProvisioner.Prepare(tpl.Provisioners[3].Config)
   260  	if err != nil {
   261  		t.Fatalf("Error preparing verification provisioner: %s", err)
   262  	}
   263  
   264  	// Add hooks so the provisioners run during the build
   265  	hooks := map[string][]packer.Hook{}
   266  	hooks[packer.HookProvision] = []packer.Hook{
   267  		&packer.ProvisionHook{
   268  			Provisioners: []*packer.HookedProvisioner{
   269  				{Provisioner: fileProvisioner, Config: nil, TypeName: ""},
   270  				{Provisioner: dirProvisioner, Config: nil, TypeName: ""},
   271  				{Provisioner: shellProvisioner, Config: nil, TypeName: ""},
   272  				{Provisioner: verifyProvisioner, Config: nil, TypeName: ""},
   273  			},
   274  		},
   275  	}
   276  	hook := &packer.DispatchHook{Mapping: hooks}
   277  
   278  	artifact, err := builder.Run(ui, hook, cache)
   279  	if err != nil {
   280  		t.Fatalf("Error running build %s", err)
   281  	}
   282  	defer artifact.Destroy()
   283  }
   284  
   285  const dockerBuilderConfig = `
   286  {
   287    "builders": [
   288      {
   289        "type": "docker",
   290        "image": "ubuntu",
   291        "discard": true,
   292        "run_command": ["-d", "-i", "-t", "{{.Image}}", "/bin/sh"]
   293      }
   294    ],
   295    "provisioners": [
   296      {
   297        "type": "file",
   298        "source": "test-fixtures/onecakes/strawberry",
   299        "destination": "/strawberry-cake"
   300      },
   301      {
   302        "type": "file",
   303        "source": "/strawberry-cake",
   304        "destination": "my-strawberry-cake",
   305        "direction": "download"
   306      }
   307    ]
   308  }
   309  `
   310  
   311  const dockerLargeBuilderConfig = `
   312  {
   313    "builders": [
   314      {
   315        "type": "docker",
   316        "image": "ubuntu",
   317        "discard": true
   318      }
   319    ],
   320    "provisioners": [
   321      {
   322        "type": "shell",
   323        "inline": [
   324          "dd if=/dev/urandom of=/tmp/cupcake bs=1M count=2",
   325          "dd if=/dev/urandom of=/tmp/bigcake bs=1M count=100",
   326          "sync",
   327          "md5sum /tmp/cupcake /tmp/bigcake"
   328        ]
   329      },
   330      {
   331        "type": "file",
   332        "source": "/tmp/cupcake",
   333        "destination": "cupcake",
   334        "direction": "download"
   335      },
   336      {
   337        "type": "file",
   338        "source": "/tmp/bigcake",
   339        "destination": "bigcake",
   340        "direction": "download"
   341      }
   342    ]
   343  }
   344  `
   345  
   346  const testFixUploadOwnerTemplate = `
   347  {
   348    "builders": [
   349      {
   350        "type": "docker",
   351        "image": "ubuntu",
   352        "discard": true,
   353        "run_command": ["-d", "-i", "-t", "-u", "42", "{{.Image}}", "/bin/sh"]
   354      }
   355    ],
   356    "provisioners": [
   357      {
   358        "type": "file",
   359        "source": "test-fixtures/onecakes/strawberry",
   360        "destination": "/tmp/strawberry-cake"
   361      },
   362      {
   363        "type": "file",
   364        "source": "test-fixtures/manycakes",
   365        "destination": "/tmp/"
   366      },
   367      {
   368        "type": "shell",
   369        "inline": "touch /tmp/testUploadOwner"
   370      },
   371      {
   372        "type": "shell",
   373        "inline": [
   374          "[ $(stat -c %u /tmp/strawberry-cake) -eq 42 ] || (echo 'Invalid owner of /tmp/strawberry-cake' && exit 1)",
   375          "[ $(stat -c %u /tmp/testUploadOwner) -eq 42 ] || (echo 'Invalid owner of /tmp/testUploadOwner' && exit 1)",
   376          "find /tmp/manycakes | xargs -n1 -IFILE /bin/sh -c '[ $(stat -c %u FILE) -eq 42 ] || (echo \"Invalid owner of FILE\" && exit 1)'"
   377        ]
   378      }
   379    ]
   380  }
   381  `