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