github.com/ddnomad/packer@v1.3.2/provisioner/ansible-local/provisioner_test.go (about)

     1  package ansiblelocal
     2  
     3  import (
     4  	"io/ioutil"
     5  	"os"
     6  	"path/filepath"
     7  	"strings"
     8  	"testing"
     9  
    10  	"fmt"
    11  	"os/exec"
    12  
    13  	"github.com/hashicorp/packer/builder/docker"
    14  	"github.com/hashicorp/packer/packer"
    15  	"github.com/hashicorp/packer/provisioner/file"
    16  	"github.com/hashicorp/packer/template"
    17  )
    18  
    19  func TestProvisioner_Impl(t *testing.T) {
    20  	var raw interface{}
    21  	raw = &Provisioner{}
    22  	if _, ok := raw.(packer.Provisioner); !ok {
    23  		t.Fatalf("must be a Provisioner")
    24  	}
    25  }
    26  
    27  func TestProvisionerPrepare_Defaults(t *testing.T) {
    28  	var p Provisioner
    29  	config := testConfig()
    30  
    31  	playbook_file, err := ioutil.TempFile("", "playbook")
    32  	if err != nil {
    33  		t.Fatalf("err: %s", err)
    34  	}
    35  	defer os.Remove(playbook_file.Name())
    36  
    37  	config["playbook_file"] = playbook_file.Name()
    38  	err = p.Prepare(config)
    39  	if err != nil {
    40  		t.Fatalf("err: %s", err)
    41  	}
    42  
    43  	if !strings.HasPrefix(filepath.ToSlash(p.config.StagingDir), DefaultStagingDir) {
    44  		t.Fatalf("unexpected staging dir %s, expected %s",
    45  			p.config.StagingDir, DefaultStagingDir)
    46  	}
    47  }
    48  
    49  func TestProvisionerPrepare_PlaybookFile(t *testing.T) {
    50  	var p Provisioner
    51  	config := testConfig()
    52  
    53  	err := p.Prepare(config)
    54  	if err == nil {
    55  		t.Fatal("should have error")
    56  	}
    57  
    58  	config["playbook_file"] = ""
    59  	err = p.Prepare(config)
    60  	if err == nil {
    61  		t.Fatal("should have error")
    62  	}
    63  
    64  	playbook_file, err := ioutil.TempFile("", "playbook")
    65  	if err != nil {
    66  		t.Fatalf("err: %s", err)
    67  	}
    68  	defer os.Remove(playbook_file.Name())
    69  
    70  	config["playbook_file"] = playbook_file.Name()
    71  	err = p.Prepare(config)
    72  	if err != nil {
    73  		t.Fatalf("err: %s", err)
    74  	}
    75  }
    76  
    77  func TestProvisionerPrepare_PlaybookFiles(t *testing.T) {
    78  	var p Provisioner
    79  	config := testConfig()
    80  
    81  	err := p.Prepare(config)
    82  	if err == nil {
    83  		t.Fatal("should have error")
    84  	}
    85  
    86  	config["playbook_file"] = ""
    87  	config["playbook_files"] = []string{}
    88  	err = p.Prepare(config)
    89  	if err == nil {
    90  		t.Fatal("should have error")
    91  	}
    92  
    93  	playbook_file, err := ioutil.TempFile("", "playbook")
    94  	if err != nil {
    95  		t.Fatalf("err: %s", err)
    96  	}
    97  	defer os.Remove(playbook_file.Name())
    98  
    99  	config["playbook_file"] = playbook_file.Name()
   100  	config["playbook_files"] = []string{"some_other_file"}
   101  	err = p.Prepare(config)
   102  	if err == nil {
   103  		t.Fatal("should have error")
   104  	}
   105  
   106  	p = Provisioner{}
   107  	config["playbook_file"] = playbook_file.Name()
   108  	config["playbook_files"] = []string{}
   109  	err = p.Prepare(config)
   110  	if err != nil {
   111  		t.Fatalf("err: %s", err)
   112  	}
   113  
   114  	config["playbook_file"] = ""
   115  	config["playbook_files"] = []string{playbook_file.Name()}
   116  	err = p.Prepare(config)
   117  	if err != nil {
   118  		t.Fatalf("err: %s", err)
   119  	}
   120  }
   121  
   122  func TestProvisionerProvision_PlaybookFiles(t *testing.T) {
   123  	var p Provisioner
   124  	config := testConfig()
   125  
   126  	playbooks := createTempFiles("", 3)
   127  	defer removeFiles(playbooks...)
   128  
   129  	config["playbook_files"] = playbooks
   130  	err := p.Prepare(config)
   131  	if err != nil {
   132  		t.Fatalf("err: %s", err)
   133  	}
   134  
   135  	comm := &communicatorMock{}
   136  	if err := p.Provision(new(packer.NoopUi), comm); err != nil {
   137  		t.Fatalf("err: %s", err)
   138  	}
   139  
   140  	assertPlaybooksUploaded(comm, playbooks)
   141  	assertPlaybooksExecuted(comm, playbooks)
   142  }
   143  
   144  func TestProvisionerProvision_PlaybookFilesWithPlaybookDir(t *testing.T) {
   145  	var p Provisioner
   146  	config := testConfig()
   147  
   148  	playbook_dir, err := ioutil.TempDir("", "")
   149  	if err != nil {
   150  		t.Fatalf("Failed to create playbook_dir: %s", err)
   151  	}
   152  	defer os.RemoveAll(playbook_dir)
   153  	playbooks := createTempFiles(playbook_dir, 3)
   154  
   155  	playbookNames := make([]string, 0, len(playbooks))
   156  	playbooksInPlaybookDir := make([]string, 0, len(playbooks))
   157  	for _, playbook := range playbooks {
   158  		playbooksInPlaybookDir = append(playbooksInPlaybookDir, strings.TrimPrefix(playbook, playbook_dir))
   159  		playbookNames = append(playbookNames, filepath.Base(playbook))
   160  	}
   161  
   162  	config["playbook_files"] = playbooks
   163  	config["playbook_dir"] = playbook_dir
   164  	err = p.Prepare(config)
   165  	if err != nil {
   166  		t.Fatalf("err: %s", err)
   167  	}
   168  
   169  	comm := &communicatorMock{}
   170  	if err := p.Provision(new(packer.NoopUi), comm); err != nil {
   171  		t.Fatalf("err: %s", err)
   172  	}
   173  
   174  	assertPlaybooksNotUploaded(comm, playbookNames)
   175  	assertPlaybooksExecuted(comm, playbooksInPlaybookDir)
   176  }
   177  
   178  func TestProvisionerPrepare_InventoryFile(t *testing.T) {
   179  	var p Provisioner
   180  	config := testConfig()
   181  
   182  	err := p.Prepare(config)
   183  	if err == nil {
   184  		t.Fatal("should have error")
   185  	}
   186  
   187  	config["playbook_file"] = ""
   188  	err = p.Prepare(config)
   189  	if err == nil {
   190  		t.Fatal("should have error")
   191  	}
   192  
   193  	playbook_file, err := ioutil.TempFile("", "playbook")
   194  	if err != nil {
   195  		t.Fatalf("err: %s", err)
   196  	}
   197  	defer os.Remove(playbook_file.Name())
   198  
   199  	config["playbook_file"] = playbook_file.Name()
   200  	err = p.Prepare(config)
   201  	if err != nil {
   202  		t.Fatalf("err: %s", err)
   203  	}
   204  
   205  	inventory_file, err := ioutil.TempFile("", "inventory")
   206  	if err != nil {
   207  		t.Fatalf("err: %s", err)
   208  	}
   209  	defer os.Remove(inventory_file.Name())
   210  
   211  	config["inventory_file"] = inventory_file.Name()
   212  	err = p.Prepare(config)
   213  	if err != nil {
   214  		t.Fatalf("err: %s", err)
   215  	}
   216  }
   217  
   218  func TestProvisionerPrepare_Dirs(t *testing.T) {
   219  	var p Provisioner
   220  	config := testConfig()
   221  
   222  	err := p.Prepare(config)
   223  	if err == nil {
   224  		t.Fatal("should have error")
   225  	}
   226  
   227  	config["playbook_file"] = ""
   228  	err = p.Prepare(config)
   229  	if err == nil {
   230  		t.Fatal("should have error")
   231  	}
   232  
   233  	playbook_file, err := ioutil.TempFile("", "playbook")
   234  	if err != nil {
   235  		t.Fatalf("err: %s", err)
   236  	}
   237  	defer os.Remove(playbook_file.Name())
   238  
   239  	config["playbook_file"] = playbook_file.Name()
   240  	err = p.Prepare(config)
   241  	if err != nil {
   242  		t.Fatalf("err: %s", err)
   243  	}
   244  
   245  	config["playbook_paths"] = []string{playbook_file.Name()}
   246  	err = p.Prepare(config)
   247  	if err == nil {
   248  		t.Fatal("should error if playbook paths is not a dir")
   249  	}
   250  
   251  	config["playbook_paths"] = []string{os.TempDir()}
   252  	err = p.Prepare(config)
   253  	if err != nil {
   254  		t.Fatalf("err: %s", err)
   255  	}
   256  
   257  	config["role_paths"] = []string{playbook_file.Name()}
   258  	err = p.Prepare(config)
   259  	if err == nil {
   260  		t.Fatal("should error if role paths is not a dir")
   261  	}
   262  
   263  	config["role_paths"] = []string{os.TempDir()}
   264  	err = p.Prepare(config)
   265  	if err != nil {
   266  		t.Fatalf("err: %s", err)
   267  	}
   268  
   269  	config["group_vars"] = playbook_file.Name()
   270  	err = p.Prepare(config)
   271  	if err == nil {
   272  		t.Fatalf("should error if group_vars path is not a dir")
   273  	}
   274  
   275  	config["group_vars"] = os.TempDir()
   276  	err = p.Prepare(config)
   277  	if err != nil {
   278  		t.Fatalf("err: %s", err)
   279  	}
   280  
   281  	config["host_vars"] = playbook_file.Name()
   282  	err = p.Prepare(config)
   283  	if err == nil {
   284  		t.Fatalf("should error if host_vars path is not a dir")
   285  	}
   286  
   287  	config["host_vars"] = os.TempDir()
   288  	err = p.Prepare(config)
   289  	if err != nil {
   290  		t.Fatalf("err: %s", err)
   291  	}
   292  }
   293  
   294  func TestProvisionerPrepare_CleanStagingDir(t *testing.T) {
   295  	var p Provisioner
   296  	config := testConfig()
   297  
   298  	playbook_file, err := ioutil.TempFile("", "playbook")
   299  	if err != nil {
   300  		t.Fatalf("err: %s", err)
   301  	}
   302  	defer os.Remove(playbook_file.Name())
   303  
   304  	config["playbook_file"] = playbook_file.Name()
   305  	config["clean_staging_directory"] = true
   306  
   307  	err = p.Prepare(config)
   308  	if err != nil {
   309  		t.Fatalf("err: %s", err)
   310  	}
   311  
   312  	if !p.config.CleanStagingDir {
   313  		t.Fatalf("expected clean_staging_directory to be set")
   314  	}
   315  }
   316  
   317  func TestProvisionerProvisionDocker_PlaybookFiles(t *testing.T) {
   318  	testProvisionerProvisionDockerWithPlaybookFiles(t, playbookFilesDockerTemplate)
   319  }
   320  
   321  func TestProvisionerProvisionDocker_PlaybookFilesWithPlaybookDir(t *testing.T) {
   322  	testProvisionerProvisionDockerWithPlaybookFiles(t, playbookFilesWithPlaybookDirDockerTemplate)
   323  }
   324  
   325  func testProvisionerProvisionDockerWithPlaybookFiles(t *testing.T, templateString string) {
   326  	if os.Getenv("PACKER_ACC") == "" {
   327  		t.Skip("This test is only run with PACKER_ACC=1")
   328  	}
   329  
   330  	ui := packer.TestUi(t)
   331  	cache := &packer.FileCache{CacheDir: os.TempDir()}
   332  
   333  	tpl, err := template.Parse(strings.NewReader(templateString))
   334  	if err != nil {
   335  		t.Fatalf("Unable to parse config: %s", err)
   336  	}
   337  
   338  	// Check if docker executable can be found.
   339  	_, err = exec.LookPath("docker")
   340  	if err != nil {
   341  		t.Error("docker command not found; please make sure docker is installed")
   342  	}
   343  
   344  	// Setup the builder
   345  	builder := &docker.Builder{}
   346  	warnings, err := builder.Prepare(tpl.Builders["docker"].Config)
   347  	if err != nil {
   348  		t.Fatalf("Error preparing configuration %s", err)
   349  	}
   350  	if len(warnings) > 0 {
   351  		t.Fatal("Encountered configuration warnings; aborting")
   352  	}
   353  
   354  	ansible := &Provisioner{}
   355  	err = ansible.Prepare(tpl.Provisioners[0].Config)
   356  	if err != nil {
   357  		t.Fatalf("Error preparing ansible-local provisioner: %s", err)
   358  	}
   359  
   360  	download := &file.Provisioner{}
   361  	err = download.Prepare(tpl.Provisioners[1].Config)
   362  	if err != nil {
   363  		t.Fatalf("Error preparing download: %s", err)
   364  	}
   365  
   366  	// Add hooks so the provisioners run during the build
   367  	hooks := map[string][]packer.Hook{}
   368  	hooks[packer.HookProvision] = []packer.Hook{
   369  		&packer.ProvisionHook{
   370  			Provisioners: []*packer.HookedProvisioner{
   371  				{Provisioner: ansible, Config: nil, TypeName: ""},
   372  				{Provisioner: download, Config: nil, TypeName: ""},
   373  			},
   374  		},
   375  	}
   376  	hook := &packer.DispatchHook{Mapping: hooks}
   377  
   378  	artifact, err := builder.Run(ui, hook, cache)
   379  	if err != nil {
   380  		t.Fatalf("Error running build %s", err)
   381  	}
   382  	defer os.Remove("hello_world")
   383  	defer artifact.Destroy()
   384  
   385  	actualContent, err := ioutil.ReadFile("hello_world")
   386  	if err != nil {
   387  		t.Fatalf("Expected file not found: %s", err)
   388  	}
   389  
   390  	expectedContent := "Hello world!"
   391  	if string(actualContent) != expectedContent {
   392  		t.Fatalf(`Unexpected file content: expected="%s", actual="%s"`, expectedContent, actualContent)
   393  	}
   394  }
   395  
   396  func assertPlaybooksExecuted(comm *communicatorMock, playbooks []string) {
   397  	cmdIndex := 0
   398  	for _, playbook := range playbooks {
   399  		playbook = filepath.ToSlash(playbook)
   400  		for ; cmdIndex < len(comm.startCommand); cmdIndex++ {
   401  			cmd := comm.startCommand[cmdIndex]
   402  			if strings.Contains(cmd, "ansible-playbook") && strings.Contains(cmd, playbook) {
   403  				break
   404  			}
   405  		}
   406  		if cmdIndex == len(comm.startCommand) {
   407  			panic(fmt.Sprintf("Playbook %s was not executed", playbook))
   408  		}
   409  	}
   410  }
   411  
   412  func assertPlaybooksUploaded(comm *communicatorMock, playbooks []string) {
   413  	uploadIndex := 0
   414  	for _, playbook := range playbooks {
   415  		playbook = filepath.ToSlash(playbook)
   416  		for ; uploadIndex < len(comm.uploadDestination); uploadIndex++ {
   417  			dest := comm.uploadDestination[uploadIndex]
   418  			if strings.HasSuffix(dest, playbook) {
   419  				break
   420  			}
   421  		}
   422  		if uploadIndex == len(comm.uploadDestination) {
   423  			panic(fmt.Sprintf("Playbook %s was not uploaded", playbook))
   424  		}
   425  	}
   426  }
   427  
   428  func assertPlaybooksNotUploaded(comm *communicatorMock, playbooks []string) {
   429  	for _, playbook := range playbooks {
   430  		playbook = filepath.ToSlash(playbook)
   431  		for _, destination := range comm.uploadDestination {
   432  			if strings.HasSuffix(destination, playbook) {
   433  				panic(fmt.Sprintf("Playbook %s was uploaded", playbook))
   434  			}
   435  		}
   436  	}
   437  }
   438  
   439  func testConfig() map[string]interface{} {
   440  	m := make(map[string]interface{})
   441  	return m
   442  }
   443  
   444  func createTempFile(dir string) string {
   445  	file, err := ioutil.TempFile(dir, "")
   446  	if err != nil {
   447  		panic(fmt.Sprintf("err: %s", err))
   448  	}
   449  	return file.Name()
   450  }
   451  
   452  func createTempFiles(dir string, numFiles int) []string {
   453  	files := make([]string, 0, numFiles)
   454  	defer func() {
   455  		// Cleanup the files if not all were created.
   456  		if len(files) < numFiles {
   457  			for _, file := range files {
   458  				os.Remove(file)
   459  			}
   460  		}
   461  	}()
   462  
   463  	for i := 0; i < numFiles; i++ {
   464  		files = append(files, createTempFile(dir))
   465  	}
   466  	return files
   467  }
   468  
   469  func removeFiles(files ...string) {
   470  	for _, file := range files {
   471  		os.Remove(file)
   472  	}
   473  }
   474  
   475  const playbookFilesDockerTemplate = `
   476  {
   477  	"builders": [
   478  		{
   479  			"type": "docker",
   480  			"image": "williamyeh/ansible:centos7",
   481  			"discard": true
   482  		}
   483  	],
   484  	"provisioners": [
   485  		{
   486  			"type": "ansible-local",
   487  			"playbook_files": [
   488  				"test-fixtures/hello.yml",
   489  				"test-fixtures/world.yml"
   490  			]
   491  		},
   492  		{
   493  			"type": "file",
   494  			"source": "/tmp/hello_world",
   495  			"destination": "hello_world",
   496  			"direction": "download"
   497  		}
   498  	]
   499  }
   500  `
   501  
   502  const playbookFilesWithPlaybookDirDockerTemplate = `
   503  {
   504  	"builders": [
   505  		{
   506  			"type": "docker",
   507  			"image": "williamyeh/ansible:centos7",
   508  			"discard": true
   509  		}
   510  	],
   511  	"provisioners": [
   512  		{
   513  			"type": "ansible-local",
   514  			"playbook_files": [
   515  				"test-fixtures/hello.yml",
   516  				"test-fixtures/world.yml"
   517  			],
   518  			"playbook_dir": "test-fixtures"
   519  		},
   520  		{
   521  			"type": "file",
   522  			"source": "/tmp/hello_world",
   523  			"destination": "hello_world",
   524  			"direction": "download"
   525  		}
   526  	]
   527  }
   528  `