acctest

package
v0.0.12 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 8, 2021 License: MPL-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package acctest provides an acceptance testing framework for testing builders and provisioners.

Writing Provisioner Acceptance Tests

Packer has implemented a `ProvisionerTestCase` structure to help write provisioner acceptance tests.

```go

type ProvisionerTestCase struct {
  // Check is called after this step is executed in order to test that
  // the step executed successfully. If this is not set, then the next
  // step will be called
  Check func(*exec.Cmd, string) error
  // IsCompatible checks whether a provisioner is able to run against a
  // given builder type and guest operating system, and returns a boolean.
  // if it returns true, the test combination is okay to run. If false, the
  // test combination is not okay to run.
  IsCompatible func(builderType string, BuilderGuestOS string) bool
  // Name is the name of the test case. Be simple but unique and descriptive.
  Name string
  // Setup, if non-nil, will be called once before the test case
  // runs. This can be used for some setup like setting environment
  // variables, or for validation prior to the
  // test running. For example, you can use this to make sure certain
  // binaries are installed, or text fixtures are in place.
  Setup func() error
  // Teardown will be called before the test case is over regardless
  // of if the test succeeded or failed. This should return an error
  // in the case that the test can't guarantee all resources were
  // properly cleaned up.
  Teardown builderT.TestTeardownFunc
  // Template is the provisioner template to use.
  // The provisioner template fragment must be a json-formatted string
  // containing the provisioner definition but no other portions of a packer
  // template. For
  // example:
  //
  // ```json
  // {
  //  "type": "shell-local",
  //  "inline", ["echo hello world"]
  // }
  //```
  //
  // is a valid entry for "template" here, but the complete Packer template:
  //
  // ```json
  // {
  //  "provisioners": [
  //    {
  //      "type": "shell-local",
  //      "inline", ["echo hello world"]
  //    }
  //  ]
  // }
  // ```
  //
  // is invalid as input.
  //
  // You may provide multiple provisioners in the same template. For example:
  // ```json
  // {
  //  "type": "shell-local",
  //  "inline", ["echo hello world"]
  // },
  // {
  //  "type": "shell-local",
  //  "inline", ["echo hello world 2"]
  // }
  // ```
  Template string
  // Type is the type of provisioner.
  Type string
}

```

To start writing a new provisioner acceptance test, you should add a test file named `provisioner_acc_test.go` in the same folder as your provisioner is defined. Create a test case by implementing the above struct, and run it by calling `provisioneracc.TestProvisionersAgainstBuilders(testCase, t)`

The following example has been adapted from a shell-local provisioner test:

``` import (

"github.com/hashicorp/packer-plugin-sdk/acctest/provisioneracc"
"github.com/hashicorp/packer-plugin-sdk/acctest/testutils"

)

// ...

func TestAccShellProvisioner_basic(t *testing.T) {
  // Create a json template fragment containing just the provisioners you want
  // to run.
  templateString := `{
    "type": "shell-local",
    "script": "test-fixtures/script.sh",
    "max_retries" : 5
}`
  // instantiate a test case.
  testCase := &provisioneracc.ProvisionerTestCase{
    IsCompatible: func() bool {return true},
    Name:         "shell-local-provisioner-basic",
    Teardown: func() error {
      testutils.CleanupFiles("test-fixtures/file.txt")
      return nil
    },
    Template: templateString,
    Type:     "shell-local",
    Check: func(buildcommand *exec.Cmd, logfile string) error {
      if buildcommand.ProcessState != nil {
        if buildcommand.ProcessState.ExitCode() != 0 {
          return fmt.Errorf("Bad exit code. Logfile: %s", logfile)
        }
      }
      filecontents, err := loadFile("file.txt")
      if err != nil {
        return err
      }
      if !strings.Contains(filecontents, "hello") {
        return fmt.Errorf("file contents were wrong: %s", filecontents)
      }
      return nil
    },
  }

  provisioneracc.TestProvisionersAgainstBuilders(testCase, t)
}

```

After writing the struct and implementing the interface, now is time to write the test that will run all of this code you wrote. Your test should be like:

```go

func TestShellProvisioner(t *testing.T) {
	acc.TestProvisionersPreCheck("shell", t)
	acc.TestProvisionersAgainstBuilders(new(ShellProvisionerAccTest), t)
}

```

The method `TestProvisionersAgainstBuilders` will run the provisioner against all available and compatible builders. If there are not builders compatible with the test you want to run, you can add a builder using the following steps:

Create a subdirectory in provisioneracc/test-fixtures for the type of builder you are adding. In this subdirectory, add one json file containing a single builder fragment. For example, one of our amazon-ebs builders is defined in provisioneracc/test-fixtures/amazon-ebs/amazon-ebs.txt and contains:

```json

{
  "type": "amazon-ebs",
  "ami_name": "packer-acc-test",
  "instance_type": "t2.micro",
  "region": "us-east-1",
  "ssh_username": "ubuntu",
  "source_ami_filter": {
    "filters": {
      "virtualization-type": "hvm",
      "name": "ubuntu/images/*ubuntu-xenial-16.04-amd64-server-*",
      "root-device-type": "ebs"
    },
    "owners": ["099720109477"],
    "most_recent": true
  },
  "force_deregister" : true,
  "tags": {
    "packer-test": "true"
  }
}

```

note that this fragment does not contain anything other than a single builder definition. The testing framework will combine this with the provisioner fragment to create a working json template.

In order to tell the testing framework how to use this builder fragment, you need to implement a `BuilderFixture` struct:

```go

type BuilderFixture struct {
  // Name is the name of the builder fixture.
  // Be simple and descriptive.
  Name string
  // Setup creates necessary extra test fixtures, and renders their values
  // into the BuilderFixture.Template.
  Setup func()
  // Template is the path to a builder template fragment.
  // The builder template fragment must be a json-formatted file containing
  // the builder definition but no other portions of a packer template. For
  // example:
  //
  // ```json
  // {
  //  "type": "null",
  //  "communicator", "none"
  // }
  //```
  //
  // is a valid entry for "template" here, but the complete Packer template:
  //
  // ```json
  // {
  //  "builders": [
  //    "type": "null",
  //    "communicator": "none"
  //  ]
  // }
  // ```
  //
  // is invalid as input.
  //
  // Only provide one builder template fragment per file.
  TemplatePath string

  // GuestOS says what guest os type the builder template fragment creates.
  // Valid values are "windows", "linux" or "darwin" guests.
  GuestOS string

  // HostOS says what host os type the builder is capable of running on.
  // Valid values are "any", windows", or "posix". If you set "posix", then
  // this builder can run on a "linux" or "darwin" platform. If you set
  // "any", then this builder can be used on any platform.
  HostOS string

  Teardown builderT.TestTeardownFunc
}

``` Implement this struct to the file "provisioneracc/builders.go", then add the new implementation to the `BuildersAccTest` map in `provisioneracc/provisioners.go`

Once you finish these steps, you should be ready to run your new provisioner acceptance test by setting the name used in the BuildersAccTest map as your `ACC_TEST_BUILDERS` environment variable.

Index

Constants

View Source
const TestEnvVar = "PACKER_ACC"

TestEnvVar must be set to a non-empty value for acceptance tests to run.

Variables

This section is empty.

Functions

func Test

func Test(t TestT, c TestCase)

Test performs an acceptance test on a backend with the given test case.

Tests are not run unless an environmental variable "PACKER_ACC" is set to some non-empty value. This is to avoid test cases surprising a user by creating real resources.

Tests will fail unless the verbose flag (`go test -v`, or explicitly the "-test.v" flag) is set. Because some acceptance tests take quite long, we require the verbose flag so users are able to see progress output.

func TestDatasource

func TestDatasource(t *testing.T, testCase *DatasourceTestCase)

func TestPlugin

func TestPlugin(t *testing.T, testCase *PluginTestCase)

Types

type DatasourceTestCase

type DatasourceTestCase struct {
	// Check is called after this step is executed in order to test that
	// the step executed successfully. If this is not set, then the next
	// step will be called
	Check func(*exec.Cmd, string) error
	// Name is the name of the test case. Be simple but unique and descriptive.
	Name string
	// Setup, if non-nil, will be called once before the test case
	// runs. This can be used for some setup like setting environment
	// variables, or for validation prior to the
	// test running. For example, you can use this to make sure certain
	// binaries are installed, or text fixtures are in place.
	Setup func() error
	// Teardown will be called before the test case is over regardless
	// of if the test succeeded or failed. This should return an error
	// in the case that the test can't guarantee all resources were
	// properly cleaned up.
	Teardown TestTeardownFunc
	// Template is the testing HCL2 template to use.
	Template string
	// Type is the type of data source.
	Type string
}

DatasourceTestCase is a single set of tests to run for a data source. A DatasourceTestCase should generally map 1:1 to each test method for your acceptance tests. Requirements: - The plugin to be tested must be previously installed so that Packer can discover it. - Packer must be installed

type PluginTestCase

type PluginTestCase struct {
	// Check is called after this step is executed in order to test that
	// the step executed successfully. If this is not set, then the next
	// step will be called
	Check func(*exec.Cmd, string) error
	// Name is the name of the test case. Be simple but unique and descriptive.
	Name string
	// Setup, if non-nil, will be called once before the test case
	// runs. This can be used for some setup like setting environment
	// variables, or for validation prior to the
	// test running. For example, you can use this to make sure certain
	// binaries are installed, or text fixtures are in place.
	Setup func() error
	// Teardown will be called before the test case is over regardless
	// of if the test succeeded or failed. This should return an error
	// in the case that the test can't guarantee all resources were
	// properly cleaned up.
	Teardown TestTeardownFunc
	// Template is the testing HCL2 template to use.
	Template string
	// Type is the type of the plugin.
	Type string
}

PluginTestCase is a single set of tests to run for a plugin. A PluginTestCase should generally map 1:1 to each test method for your acceptance tests. Requirements: - The plugin to be tested must be previously installed so that Packer can discover it. - Packer must also be installed

type TestBuilderSet

type TestBuilderSet struct {
	packer.BuilderSet
	StartFn func(name string) (packersdk.Builder, error)
}

func (TestBuilderSet) Start

func (tbs TestBuilderSet) Start(name string) (packersdk.Builder, error)

type TestCase

type TestCase struct {
	// Precheck, if non-nil, will be called once before the test case
	// runs at all. This can be used for some validation prior to the
	// test running.
	PreCheck func()

	// Builder is the Builder that will be tested. It will be available
	// as the "test" builder in the template.
	Builder packersdk.Builder

	// Template is the template contents to use.
	Template string

	// Check is called after this step is executed in order to test that
	// the step executed successfully. If this is not set, then the next
	// step will be called
	Check TestCheckFunc

	// Teardown will be called before the test case is over regardless
	// of if the test succeeded or failed. This should return an error
	// in the case that the test can't guarantee all resources were
	// properly cleaned up.
	Teardown TestTeardownFunc

	// If SkipArtifactTeardown is true, we will not attempt to destroy the
	// artifact created in this test run.
	SkipArtifactTeardown bool
	// If set, overrides the default provisioner store with custom provisioners.
	// This can be useful for running acceptance tests for a particular
	// provisioner using a specific builder.
	// Default provisioner store:
	// ProvisionerStore: packersdk.MapOfProvisioner{
	// 	"shell": func() (packersdk.Provisioner, error) { return &shellprovisioner.Provisioner{}, nil },
	// 	"file":  func() (packersdk.Provisioner, error) { return &file.Provisioner{}, nil },
	// },
	ProvisionerStore packersdk.MapOfProvisioner
}

TestCase is a single set of tests to run for a backend. A TestCase should generally map 1:1 to each test method for your acceptance tests.

type TestCheckFunc

type TestCheckFunc func([]packersdk.Artifact) error

TestCheckFunc is the callback used for Check in TestStep.

type TestT

type TestT interface {
	Error(args ...interface{})
	Fatal(args ...interface{})
	Skip(args ...interface{})
}

TestT is the interface used to handle the test lifecycle of a test.

Users should just use a *testing.T object, which implements this.

type TestTeardownFunc

type TestTeardownFunc func() error

TestTeardownFunc is the callback used for Teardown in TestCase.

Directories

Path Synopsis
Package provisioneracc creates a framework for provisioner acceptance testing.
Package provisioneracc creates a framework for provisioner acceptance testing.
Package testutils provides some simple ease-of-use tools for implementing acceptance testing.
Package testutils provides some simple ease-of-use tools for implementing acceptance testing.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL