Merge pull request #10355 from hashicorp/clean_provisioners
Clean provisioners
This commit is contained in:
commit
7a05d0ce15
|
@ -301,15 +301,15 @@ You can run tests for individual packages using commands like this:
|
|||
make test TEST=./builder/amazon/...
|
||||
```
|
||||
|
||||
#### Running Acceptance Tests
|
||||
#### Running Builder Acceptance Tests
|
||||
|
||||
Packer has [acceptance tests](https://en.wikipedia.org/wiki/Acceptance_testing)
|
||||
for various builders. These typically require an API key (AWS, GCE), or
|
||||
additional software to be installed on your computer (VirtualBox, VMware).
|
||||
|
||||
If you're working on a new builder or builder feature and want to verify it is
|
||||
functioning (and also hasn't broken anything else), we recommend running the
|
||||
acceptance tests.
|
||||
functioning (and also hasn't broken anything else), we recommend creating or
|
||||
running the acceptance tests.
|
||||
|
||||
**Warning:** The acceptance tests create/destroy/modify _real resources_, which
|
||||
may incur costs for real money. In the presence of a bug, it is possible that
|
||||
|
@ -340,7 +340,7 @@ Acceptance tests typically require other environment variables to be set for
|
|||
things such as API tokens and keys. Each test should error and tell you which
|
||||
credentials are missing, so those are not documented here.
|
||||
|
||||
#### Running Provisioners Acceptance Tests
|
||||
#### Running Provisioner Acceptance Tests
|
||||
|
||||
**Warning:** The acceptance tests create/destroy/modify _real resources_, which
|
||||
may incur costs for real money. In the presence of a bug, it is possible that
|
||||
|
@ -351,125 +351,165 @@ resources are not accidentally destroyed or overwritten during testing.
|
|||
Also, these typically require an API key (AWS, GCE), or additional software
|
||||
to be installed on your computer (VirtualBox, VMware).
|
||||
|
||||
To run the Provisioners Acceptance Tests you should use both **ACC_TEST_BUILDERS** and **ACC_TEST_PROVISIONERS** variables to
|
||||
tell which provisioner and builder the test should be run against.
|
||||
To run the Provisioners Acceptance Tests you should use the
|
||||
**ACC_TEST_BUILDERS** environment variable to tell the tests which builder the
|
||||
test should be run against.
|
||||
|
||||
Examples of usage:
|
||||
|
||||
- Run the Shell provisioner acceptance tests against the Amazon EBS builder.
|
||||
```
|
||||
ACC_TEST_BUILDERS=amazon-ebs ACC_TEST_PROVISIONERS=shell go test ./provisioner/shell/... -v -timeout=1h
|
||||
ACC_TEST_BUILDERS=amazon-ebs go test ./provisioner/shell/... -v -timeout=1h
|
||||
```
|
||||
- Do the same but using the Makefile
|
||||
```
|
||||
ACC_TEST_BUILDERS=amazon-ebs ACC_TEST_PROVISIONERS=shell make provisioners-acctest
|
||||
ACC_TEST_BUILDERS=amazon-ebs make provisioners-acctest TEST=./provisioner/shell
|
||||
```
|
||||
- Run the all Shell and Powershell provisioners acceptance tests against the Amazon EBS builder.
|
||||
- Run all provisioner acceptance tests against the Amazon EBS builder.
|
||||
```
|
||||
ACC_TEST_BUILDERS=amazon-ebs ACC_TEST_PROVISIONERS=shell,powershell make provisioners-acctest
|
||||
ACC_TEST_BUILDERS=amazon-ebs make provisioners-acctest TEST=./...
|
||||
```
|
||||
- Run the all provisioners acceptance tests against the Amazon EBS builder.
|
||||
- Run all provisioner acceptance tests against all builders whenever they are compatible.
|
||||
```
|
||||
ACC_TEST_BUILDERS=amazon-ebs ACC_TEST_PROVISIONERS=all make provisioners-acctest
|
||||
```
|
||||
- Run the all provisioners acceptance tests against all builders whenever they are compatible.
|
||||
```
|
||||
ACC_TEST_BUILDERS=all ACC_TEST_PROVISIONERS=all make provisioners-acctest
|
||||
ACC_TEST_BUILDERS=all make provisioners-acctest TEST=./...
|
||||
```
|
||||
|
||||
Both **ACC_TEST_BUILDERS** and **ACC_TEST_PROVISIONERS** allows defining a list of builders and provisioners separated by comma
|
||||
(e.g. `ACC_TEST_BUILDERS=amazon-ebs,virtualbox-iso`)
|
||||
The **ACC_TEST_BUILDERS** env variable accepts a list of builders separated by
|
||||
commas. (e.g. `ACC_TEST_BUILDERS=amazon-ebs,virtualbox-iso`)
|
||||
|
||||
|
||||
#### Writing Provisioner Acceptance Tests
|
||||
|
||||
Packer has an already implemented structure that will run the provisioner against builders and you can find it in `helper/tests/acc/provisioners.go`.
|
||||
All provisioners should use this structure in their acceptance tests.
|
||||
|
||||
To start writing a new provisioner acceptance test, you should add a test file named as `provisioner_acc_test.go` in the provisioner folder
|
||||
and the package should be `<provisioner>_test`. This file should have a struct that will implement the ProvisionerAcceptance interface.
|
||||
Packer has implemented a `ProvisionerTestCase` structure to help write
|
||||
provisioner acceptance tests.
|
||||
|
||||
```go
|
||||
type ProvisionerAcceptance interface {
|
||||
GetName() string
|
||||
GetConfig() (string, error)
|
||||
GetProvisionerStore() packer.MapOfProvisioner
|
||||
IsCompatible(builder string, vmOS string) bool
|
||||
RunTest(c *command.BuildCommand, args []string) error
|
||||
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
|
||||
}
|
||||
```
|
||||
|
||||
- **GetName()** should return the provisioner type. For example for the Shell provisioner the method returns "shell".
|
||||
|
||||
- **GetConfig()** should read a text file with the json configuration block for the provisioner and any other necessary provisioner.
|
||||
For the Shell one the file contains:
|
||||
|
||||
```
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": [
|
||||
"echo {{ build `ID`}} > provisioner.{{ build `PackerRunUUID`}}.txt"
|
||||
]
|
||||
|
||||
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/packer-plugin-sdk/acctest/provisioneracc"
|
||||
"github.com/hashicorp/packer/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
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"source": "provisioner.{{ build `PackerRunUUID`}}.txt",
|
||||
"destination": "provisioner.shell.{{ build `PackerRunUUID`}}.txt",
|
||||
"direction": "download"
|
||||
}
|
||||
```
|
||||
The file should be placed under the `test-fixtures` folder.
|
||||
In this case, it's necessary to use the File provisioner to validate if the Shell provisioner test is successful or not.
|
||||
This config should be returned as string that will be later merged with the builder config into a full template.
|
||||
|
||||
- **GetProvisionerStore()** this returns the provisioner store where we declare the available provisioners for running the build.
|
||||
For the Shell provisioners this is:
|
||||
```go
|
||||
func (s *ShellProvisionerAccTest) GetProvisionerStore() packer.MapOfProvisioner {
|
||||
return packer.MapOfProvisioner{
|
||||
"shell": func() (packer.Provisioner, error) { return &shell.Provisioner{}, nil },
|
||||
"file": func() (packer.Provisioner, error) { return &file.Provisioner{}, 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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **IsCompatible(builder string, vmOS string)** returns true or false whether the provisioner should run against a
|
||||
specific builder or/and specific OS.
|
||||
|
||||
- **RunTest(c \*command.BuildCommand, args []string)** it will actually run the build and return any error if it fails the validations.
|
||||
For the Shell provisioner this is:
|
||||
```go
|
||||
func (s *ShellProvisionerAccTest) RunTest(c *command.BuildCommand, args []string) error {
|
||||
// Provisioner specific setup
|
||||
UUID := os.Getenv("PACKER_RUN_UUID")
|
||||
if UUID == "" {
|
||||
UUID, _ = uuid.GenerateUUID()
|
||||
os.Setenv("PACKER_RUN_UUID", UUID)
|
||||
filecontents, err := loadFile("file.txt")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file := "provisioner.shell." + UUID + ".txt"
|
||||
defer testutils.CleanupFiles(file)
|
||||
|
||||
// Run build
|
||||
// All provisioner acc tests should contain this code and validation
|
||||
if code := c.Run(args); code != 0 {
|
||||
ui := c.Meta.Ui.(*packer.BasicUi)
|
||||
out := ui.Writer.(*bytes.Buffer)
|
||||
err := ui.ErrorWriter.(*bytes.Buffer)
|
||||
return fmt.Errorf(
|
||||
"Bad exit code.\n\nStdout:\n\n%s\n\nStderr:\n\n%s",
|
||||
out.String(),
|
||||
err.String())
|
||||
}
|
||||
|
||||
// Any other extra specific validation
|
||||
if !testutils.FileExists(file) {
|
||||
return fmt.Errorf("Expected to find %s", file)
|
||||
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:
|
||||
|
||||
|
@ -480,64 +520,101 @@ func TestShellProvisioner(t *testing.T) {
|
|||
}
|
||||
```
|
||||
|
||||
If the environment variable **ACC_TEST_PROVISIONERS** is set as `all` or contains the provisioner type, then the test should run, otherwise the test should skip.
|
||||
In case of running it, you'll need to call the helper function `acc.TestProvisionersAgainstBuilders` passing a pointer to the test struct created above and the test testing pointer.
|
||||
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:
|
||||
|
||||
The method `TestProvisionersAgainstBuilders` will run the provisioner against all available and compatible builders. An available builder
|
||||
is the one that has the necessary code for running this type of test. In case the builder you want to run against is not available for testing, you can write it following the next 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:
|
||||
|
||||
To add a new builder to the available builders for provisioners acc testing, you'll need to create a new folder under the builder folder
|
||||
called `acceptance` and inside you create the `builder_acceptance.go` file and the package should be `<builder>_acc`. Like the provisioners, you'll need to create a struct that will
|
||||
implement the BuilderAcceptance interface.
|
||||
```go
|
||||
type BuilderAcceptance interface {
|
||||
GetConfigs() (map[string]string, error)
|
||||
GetBuilderStore() packer.MapOfBuilder
|
||||
CleanUp() error
|
||||
}
|
||||
```
|
||||
- **GetConfigs()** should read a text file with the json configuration block for the builder and return a map of configs by OS type.
|
||||
For the Amazon EBS builder the file contains:
|
||||
```
|
||||
```json
|
||||
{
|
||||
"type": "amazon-ebs",
|
||||
"ami_name": "packer-acc-test",
|
||||
"instance_type": "m1.small",
|
||||
"instance_type": "t2.micro",
|
||||
"region": "us-east-1",
|
||||
"ssh_username": "ubuntu",
|
||||
"source_ami": "ami-0568456c",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
```
|
||||
The file should be placed under the `test-fixtures` folder.
|
||||
In case you need to make references to another file, you'll need to add the relative path to provisioners folder like:
|
||||
`../../builder/amazon/ebs/acceptance/test-fixtures/file.txt`.
|
||||
|
||||
- **GetBuilderStore()** this returns the builder store where we declare the available builders for running the build.
|
||||
For the Amazon EBS builder this is:
|
||||
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
|
||||
func (s *AmazonEBSAccTest) GetBuilderStore() packer.MapOfBuilder {
|
||||
return packer.MapOfBuilder{
|
||||
"amazon-ebs": func() (packer.Builder, error) { return &amazonebsbuilder.Builder{}, nil },
|
||||
}
|
||||
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`
|
||||
|
||||
- **CleanUp()** cleans any resource created by the builder whether local or remote.
|
||||
|
||||
Once you created the builder necessary code, the last step is adding it to the `BuildersAccTest` map in `helper/tests/acc/provisioners.go`.
|
||||
```go
|
||||
var BuildersAccTest = map[string]BuilderAcceptance{
|
||||
...
|
||||
"amazon-ebs": new(amazonEBS.AmazonEBSAccTest),
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Once you finish the steps, you should be ready to run your new provisioner acceptance test.
|
||||
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.
|
||||
|
||||
#### Debugging Plugins
|
||||
|
||||
|
|
4
Makefile
4
Makefile
|
@ -140,8 +140,8 @@ test: mode-check vet ## Run unit tests
|
|||
@go test -count $(COUNT) $(TEST) $(TESTARGS) -timeout=3m
|
||||
|
||||
# acctest runs provisioners acceptance tests
|
||||
provisioners-acctest: install-build-deps generate
|
||||
ACC_TEST_BUILDERS=$(ACC_TEST_BUILDERS) ACC_TEST_PROVISIONERS=$(ACC_TEST_PROVISIONERS) go test ./provisioner/... -timeout=1h
|
||||
provisioners-acctest: #install-build-deps generate
|
||||
ACC_TEST_BUILDERS=$(ACC_TEST_BUILDERS) go test $(TEST) $(TESTARGS) -timeout=1h
|
||||
|
||||
# testacc runs acceptance tests
|
||||
testacc: # install-build-deps generate ## Run acceptance tests
|
||||
|
|
|
@ -251,10 +251,12 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
|
|||
if resp, e := ec2conn.DescribeInstances(describeInstance); e == nil {
|
||||
if len(resp.Reservations) > 0 && len(resp.Reservations[0].Instances) > 0 {
|
||||
instance := resp.Reservations[0].Instances[0]
|
||||
if instance.StateTransitionReason != nil && instance.StateReason.Message != nil {
|
||||
ui.Error(fmt.Sprintf("Instance state change details: %s: %s",
|
||||
*instance.StateTransitionReason, *instance.StateReason.Message))
|
||||
}
|
||||
}
|
||||
}
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ func (v *VirtualBoxISOAccTest) GetConfigs() (map[string]string, error) {
|
|||
|
||||
file, err := ioutil.ReadAll(config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Uneble to read %s", filePath)
|
||||
return nil, fmt.Errorf("Unable to read %s", filePath)
|
||||
}
|
||||
return map[string]string{"linux": string(file)}, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package provisioneracc
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer/packer-plugin-sdk/acctest/testutils"
|
||||
)
|
||||
|
||||
var AmasonEBSBuilderFixtureLinux = &BuilderFixture{
|
||||
Name: "Amazon-ebs Linux builder",
|
||||
TemplatePath: "amazon-ebs/amazon-ebs.txt",
|
||||
GuestOS: "linux",
|
||||
HostOS: "any",
|
||||
Teardown: func() error {
|
||||
// TODO
|
||||
// helper := AWSHelper{
|
||||
// Region: "us-east-1",
|
||||
// AMIName: "packer-acc-test",
|
||||
// }
|
||||
// return helper.CleanUpAmi()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var AmasonEBSBuilderFixtureWindows = &BuilderFixture{
|
||||
Name: "Amazon-ebs Windows builder",
|
||||
TemplatePath: "amazon-ebs/amazon-ebs_windows.txt",
|
||||
GuestOS: "windows",
|
||||
HostOS: "any",
|
||||
Teardown: func() error {
|
||||
// TODO
|
||||
// helper := AWSHelper{
|
||||
// Region: "us-east-1",
|
||||
// AMIName: "packer-acc-test",
|
||||
// }
|
||||
// return helper.CleanUpAmi()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var VirtualboxBuilderFixtureWindows = &BuilderFixture{
|
||||
Name: "Virtualbox Windows builder",
|
||||
TemplatePath: "virtualbox/virtualbox-iso.txt",
|
||||
GuestOS: "linux",
|
||||
HostOS: "any",
|
||||
Teardown: func() error {
|
||||
testutils.CleanupFiles("virtualbox-iso-packer-acc-test")
|
||||
testutils.CleanupFiles("packer_cache")
|
||||
return nil
|
||||
},
|
||||
}
|
|
@ -3,91 +3,259 @@ package provisioneracc
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
amazonEBS "github.com/hashicorp/packer/builder/amazon/ebs/acceptance"
|
||||
virtualboxISO "github.com/hashicorp/packer/builder/virtualbox/iso/acceptance"
|
||||
"github.com/hashicorp/packer/command"
|
||||
"github.com/hashicorp/packer/packer-plugin-sdk/acctest/testutils"
|
||||
builderT "github.com/hashicorp/packer/packer-plugin-sdk/acctest"
|
||||
packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
func TestProvisionersAgainstBuilders(provisionerAcc ProvisionerAcceptance, t *testing.T) {
|
||||
provisioner := provisionerAcc.GetName()
|
||||
builders := checkBuilders(t)
|
||||
|
||||
// build template file and run a build for each builder with the provisioner
|
||||
for _, builder := range builders {
|
||||
builderAcc := BuildersAccTest[builder]
|
||||
builderConfigs, err := builderAcc.GetConfigs()
|
||||
if err != nil {
|
||||
t.Fatalf("bad: failed to read builder config: %s", err.Error())
|
||||
// ProvisionerTestCase is a single set of tests to run for a provisioner.
|
||||
// A ProvisionerTestCase should generally map 1:1 to each test method for your
|
||||
// acceptance tests.
|
||||
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
|
||||
}
|
||||
|
||||
for vmOS, builderConfig := range builderConfigs {
|
||||
if !provisionerAcc.IsCompatible(builder, vmOS) {
|
||||
// BuilderFixtures are basic builder test configurations and metadata used
|
||||
// in provisioner acceptance testing. These are frameworks to be used by
|
||||
// provisioner tests, not tests in and of themselves. BuilderFixtures should
|
||||
// generally be simple and not contain excessive or complex configurations.
|
||||
// Instantiations of this struct are stored in the builders.go file in this
|
||||
// module.
|
||||
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
|
||||
}
|
||||
|
||||
func fixtureDir() string {
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
return filepath.Join(filepath.Dir(file), "test-fixtures")
|
||||
}
|
||||
|
||||
func LoadBuilderFragment(templateFragmentPath string) (string, error) {
|
||||
dir := fixtureDir()
|
||||
fragmentAbsPath := filepath.Join(dir, templateFragmentPath)
|
||||
fragmentFile, err := os.Open(fragmentAbsPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Unable find %s", fragmentAbsPath)
|
||||
}
|
||||
defer fragmentFile.Close()
|
||||
|
||||
fragmentString, err := ioutil.ReadAll(fragmentFile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Unable to read %s", fragmentAbsPath)
|
||||
}
|
||||
|
||||
return string(fragmentString), nil
|
||||
}
|
||||
|
||||
func RunProvisionerAccTest(testCase *ProvisionerTestCase, t *testing.T) {
|
||||
TestProvisionersAgainstBuilders(testCase, t)
|
||||
}
|
||||
|
||||
//nolint:errcheck
|
||||
func TestProvisionersAgainstBuilders(testCase *ProvisionerTestCase, t *testing.T) {
|
||||
// retrieve user-desired builders.
|
||||
builderTypes := checkBuilders(t)
|
||||
|
||||
// Run this provisioner test case against each builder type requested.
|
||||
for _, builderType := range builderTypes {
|
||||
buildFixtures := BuildersAccTest[builderType]
|
||||
// loop over individual build templates, merge with provisioner
|
||||
// templates, and shell out to run test.
|
||||
for _, buildFixture := range buildFixtures {
|
||||
if !testCase.IsCompatible(builderType, buildFixture.GuestOS) {
|
||||
continue
|
||||
}
|
||||
|
||||
testName := fmt.Sprintf("testing %s builder against %s provisioner", builder, provisioner)
|
||||
testName := fmt.Sprintf("%s on %s", testCase.Name, buildFixture.Name)
|
||||
|
||||
if testCase.Setup != nil {
|
||||
err := testCase.Setup()
|
||||
if err != nil {
|
||||
t.Fatalf("test %s setup failed: %s", testName, err)
|
||||
}
|
||||
}
|
||||
|
||||
t.Run(testName, func(t *testing.T) {
|
||||
provisionerConfig, err := provisionerAcc.GetConfig()
|
||||
builderFragment, err := LoadBuilderFragment(buildFixture.TemplatePath)
|
||||
if err != nil {
|
||||
t.Fatalf("bad: failed to read provisioner config: %s", err.Error())
|
||||
t.Fatalf("failed to load builder fragment: %s", err)
|
||||
}
|
||||
|
||||
// Write json template
|
||||
// Combine provisioner and builder template fragments; write to
|
||||
// file.
|
||||
out := bytes.NewBuffer(nil)
|
||||
fmt.Fprintf(out, `{"builders": [%s],"provisioners": [%s]}`, builderConfig, provisionerConfig)
|
||||
fileName := fmt.Sprintf("%s_%s.json", builder, provisioner)
|
||||
filePath := filepath.Join("./", fileName)
|
||||
writeJsonTemplate(out, filePath, t)
|
||||
fmt.Fprintf(out, `{"builders": [%s],"provisioners": [%s]}`,
|
||||
builderFragment, testCase.Template)
|
||||
templateName := fmt.Sprintf("%s_%s.json", builderType, testCase.Type)
|
||||
templatePath := filepath.Join("./", templateName)
|
||||
writeJsonTemplate(out, templatePath, t)
|
||||
logfile := fmt.Sprintf("packer_log_%s_%s.txt", builderType, testCase.Type)
|
||||
|
||||
// set pre-config with necessary builder and provisioner
|
||||
c := buildCommand(t, builderAcc, provisionerAcc)
|
||||
args := []string{
|
||||
filePath,
|
||||
}
|
||||
|
||||
err = provisionerAcc.RunTest(c, args)
|
||||
// Cleanup created resources
|
||||
testutils.CleanupFiles(fileName)
|
||||
cleanErr := builderAcc.CleanUp()
|
||||
if cleanErr != nil {
|
||||
log.Printf("bad: failed to clean up resources: %s", cleanErr.Error())
|
||||
}
|
||||
// Make sure packer is installed:
|
||||
packerbin, err := exec.LookPath("packer")
|
||||
if err != nil {
|
||||
t.Fatalf("bad: failed to to run build: %s", err.Error())
|
||||
t.Fatalf("Couldn't find packer binary installed on system: %s", err.Error())
|
||||
}
|
||||
// Run build
|
||||
buildCommand := exec.Command(packerbin, "build", "--machine-readable", templatePath)
|
||||
buildCommand.Env = append(buildCommand.Env, os.Environ()...)
|
||||
buildCommand.Env = append(buildCommand.Env, "PACKER_LOG=1",
|
||||
fmt.Sprintf("PACKER_LOG_PATH=%s", logfile))
|
||||
buildCommand.Run()
|
||||
|
||||
// Check for test custom pass/fail before we clean up
|
||||
var checkErr error
|
||||
if testCase.Check != nil {
|
||||
checkErr = testCase.Check(buildCommand, logfile)
|
||||
}
|
||||
|
||||
// Cleanup stuff created by builder.
|
||||
cleanErr := buildFixture.Teardown()
|
||||
if cleanErr != nil {
|
||||
log.Printf("bad: failed to clean up builder-created resources: %s", cleanErr.Error())
|
||||
}
|
||||
// Clean up anything created in provisioner run
|
||||
if testCase.Teardown != nil {
|
||||
cleanErr = testCase.Teardown()
|
||||
if cleanErr != nil {
|
||||
log.Printf("bad: failed to clean up test-created resources: %s", cleanErr.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Fail test if check failed.
|
||||
if checkErr != nil {
|
||||
cwd, _ := os.Getwd()
|
||||
t.Fatalf(fmt.Sprintf("Error running provisioner acceptance"+
|
||||
" tests: %s\nLogs can be found at %s\nand the "+
|
||||
"acceptance test template can be found at %s",
|
||||
checkErr.Error(), filepath.Join(cwd, logfile),
|
||||
filepath.Join(cwd, templatePath)))
|
||||
} else {
|
||||
os.Remove(templatePath)
|
||||
os.Remove(logfile)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestProvisionersPreCheck checks if the Provisioner with name is set in ACC_TEST_PROVISIONERS environment variable
|
||||
func TestProvisionersPreCheck(name string, t *testing.T) {
|
||||
p := os.Getenv("ACC_TEST_PROVISIONERS")
|
||||
|
||||
if p == "all" {
|
||||
return
|
||||
}
|
||||
|
||||
provisioners := strings.Split(p, ",")
|
||||
for _, provisioner := range provisioners {
|
||||
if provisioner == name {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("Provisioner %q not defined in ACC_TEST_PROVISIONERS", name)
|
||||
t.Skip(msg)
|
||||
|
||||
}
|
||||
|
||||
// checkBuilders retrieves all of the builders that the user has requested to
|
||||
// run acceptance tests against.
|
||||
func checkBuilders(t *testing.T) []string {
|
||||
b := os.Getenv("ACC_TEST_BUILDERS")
|
||||
// validate if we want to run provisioners acc tests
|
||||
|
@ -119,32 +287,52 @@ func writeJsonTemplate(out *bytes.Buffer, filePath string, t *testing.T) {
|
|||
outputFile.Sync()
|
||||
}
|
||||
|
||||
func buildCommand(t *testing.T, builder BuilderAcceptance, provisioner ProvisionerAcceptance) *command.BuildCommand {
|
||||
c := &command.BuildCommand{
|
||||
Meta: TestMetaFile(t),
|
||||
}
|
||||
c.CoreConfig.Components.BuilderStore = builder.GetBuilderStore()
|
||||
c.CoreConfig.Components.ProvisionerStore = provisioner.GetProvisionerStore()
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
type ProvisionerAcceptance interface {
|
||||
GetName() string
|
||||
GetConfig() (string, error)
|
||||
GetProvisionerStore() packersdk.MapOfProvisioner
|
||||
IsCompatible(builder string, vmOS string) bool
|
||||
RunTest(c *command.BuildCommand, args []string) error
|
||||
}
|
||||
|
||||
// BuilderAcceptance is specialized tooling implemented by individual builders
|
||||
// To add your builder to the provisioner testing framework, create a struct
|
||||
// that implements this interface, add it to the BuildersAccTest map below.
|
||||
// TODO add this interface to the plugin server so that Packer can request it
|
||||
// From the plugin rather than importing it here.
|
||||
type BuilderAcceptance interface {
|
||||
// GetConfigs provides a mapping of guest OS architecture to builder
|
||||
// template fragment.
|
||||
// The builder template fragment must be a json-formatted string 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.
|
||||
//
|
||||
// Valid keys for the map are "linux" and "windows". These keys will be used
|
||||
// to determine whether a given builder template is compatible with a given
|
||||
// provisioner template.
|
||||
GetConfigs() (map[string]string, error)
|
||||
// GetBuilderStore() returns a MapOfBuilder that contains the actual builder
|
||||
// struct definition being used for this test.
|
||||
GetBuilderStore() packersdk.MapOfBuilder
|
||||
// CleanUp cleans up any side-effects of the builder not already cleaned up
|
||||
// by the builderT framework.
|
||||
CleanUp() error
|
||||
}
|
||||
|
||||
// List of all builders available for acceptance test
|
||||
var BuildersAccTest = map[string]BuilderAcceptance{
|
||||
"virtualbox-iso": new(virtualboxISO.VirtualBoxISOAccTest),
|
||||
"amazon-ebs": new(amazonEBS.AmazonEBSAccTest),
|
||||
// Mapping of all builder fixtures defined for a given builder type.
|
||||
var BuildersAccTest = map[string][]*BuilderFixture{
|
||||
"virtualbox-iso": []*BuilderFixture{VirtualboxBuilderFixtureWindows},
|
||||
"amazon-ebs": []*BuilderFixture{AmasonEBSBuilderFixtureLinux, AmasonEBSBuilderFixtureWindows},
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"type": "amazon-ebs",
|
||||
"region": "us-east-1",
|
||||
"instance_type": "t2.micro",
|
||||
"source_ami_filter": {
|
||||
"filters": {
|
||||
"virtualization-type": "hvm",
|
||||
"name": "*Windows_Server-2012-R2*English-64Bit-Base*",
|
||||
"root-device-type": "ebs"
|
||||
},
|
||||
"most_recent": true,
|
||||
"owners": "amazon"
|
||||
},
|
||||
"ami_name": "packer-acc-test",
|
||||
"user_data_file": "../../builder/amazon/ebs/acceptance/test-fixtures/scripts/bootstrap_win.txt",
|
||||
"communicator": "winrm",
|
||||
"winrm_username": "Administrator",
|
||||
"winrm_password": "SuperS3cr3t!!!!",
|
||||
"force_deregister" : true,
|
||||
"tags": {
|
||||
"packer-test": "true"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<powershell>
|
||||
# Set administrator password
|
||||
net user Administrator SuperS3cr3t!!!!
|
||||
wmic useraccount where "name='Administrator'" set PasswordExpires=FALSE
|
||||
|
||||
# First, make sure WinRM can't be connected to
|
||||
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new enable=yes action=block
|
||||
|
||||
# Delete any existing WinRM listeners
|
||||
winrm delete winrm/config/listener?Address=*+Transport=HTTP 2>$Null
|
||||
winrm delete winrm/config/listener?Address=*+Transport=HTTPS 2>$Null
|
||||
|
||||
# Disable group policies which block basic authentication and unencrypted login
|
||||
|
||||
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Client -Name AllowBasic -Value 1
|
||||
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Client -Name AllowUnencryptedTraffic -Value 1
|
||||
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Service -Name AllowBasic -Value 1
|
||||
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Service -Name AllowUnencryptedTraffic -Value 1
|
||||
|
||||
|
||||
# Create a new WinRM listener and configure
|
||||
winrm create winrm/config/listener?Address=*+Transport=HTTP
|
||||
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="0"}'
|
||||
winrm set winrm/config '@{MaxTimeoutms="7200000"}'
|
||||
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
|
||||
winrm set winrm/config/service '@{MaxConcurrentOperationsPerUser="12000"}'
|
||||
winrm set winrm/config/service/auth '@{Basic="true"}'
|
||||
winrm set winrm/config/client/auth '@{Basic="true"}'
|
||||
|
||||
# Configure UAC to allow privilege elevation in remote shells
|
||||
$Key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
|
||||
$Setting = 'LocalAccountTokenFilterPolicy'
|
||||
Set-ItemProperty -Path $Key -Name $Setting -Value 1 -Force
|
||||
|
||||
# Configure and restart the WinRM Service; Enable the required firewall exception
|
||||
Stop-Service -Name WinRM
|
||||
Set-Service -Name WinRM -StartupType Automatic
|
||||
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new action=allow localip=any remoteip=any
|
||||
Start-Service -Name WinRM
|
||||
</powershell>
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
# Preseeding only locale sets language, country and locale.
|
||||
d-i debian-installer/locale string en_US
|
||||
|
||||
# Keyboard selection.
|
||||
d-i console-setup/ask_detect boolean false
|
||||
d-i keyboard-configuration/xkb-keymap select us
|
||||
|
||||
choose-mirror-bin mirror/http/proxy string
|
||||
d-i base-installer/kernel/override-image string linux-server
|
||||
d-i clock-setup/utc boolean true
|
||||
d-i clock-setup/utc-auto boolean true
|
||||
d-i finish-install/reboot_in_progress note
|
||||
d-i grub-installer/only_debian boolean true
|
||||
d-i grub-installer/with_other_os boolean true
|
||||
d-i mirror/country string manual
|
||||
d-i mirror/http/directory string /ubuntu/
|
||||
d-i mirror/http/hostname string archive.ubuntu.com
|
||||
d-i mirror/http/proxy string
|
||||
d-i partman-auto-lvm/guided_size string max
|
||||
d-i partman-auto/choose_recipe select atomic
|
||||
d-i partman-auto/method string lvm
|
||||
d-i partman-lvm/confirm boolean true
|
||||
d-i partman-lvm/confirm boolean true
|
||||
d-i partman-lvm/confirm_nooverwrite boolean true
|
||||
d-i partman-lvm/device_remove_lvm boolean true
|
||||
d-i partman/choose_partition select finish
|
||||
d-i partman/confirm boolean true
|
||||
d-i partman/confirm_nooverwrite boolean true
|
||||
d-i partman/confirm_write_new_label boolean true
|
||||
d-i passwd/user-fullname string vagrant
|
||||
d-i passwd/user-uid string 1000
|
||||
d-i passwd/user-password password vagrant
|
||||
d-i passwd/user-password-again password vagrant
|
||||
d-i passwd/username string vagrant
|
||||
d-i pkgsel/include string openssh-server cryptsetup build-essential libssl-dev libreadline-dev zlib1g-dev linux-source dkms nfs-kernel-server nfs-common linux-headers-$(uname -r) perl
|
||||
d-i pkgsel/install-language-support boolean false
|
||||
d-i pkgsel/update-policy select none
|
||||
d-i pkgsel/upgrade select full-upgrade
|
||||
d-i time/zone string UTC
|
||||
d-i user-setup/allow-password-weak boolean true
|
||||
d-i user-setup/encrypt-home boolean false
|
||||
tasksel tasksel/first multiselect standard, server
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"type": "virtualbox-iso",
|
||||
"iso_checksum": "sha256:946a6077af6f5f95a51f82fdc44051c7aa19f9cfc5f737954845a6050543d7c2",
|
||||
"iso_url": "http://old-releases.ubuntu.com/releases/14.04.1/ubuntu-14.04.1-server-amd64.iso",
|
||||
"disk_size": "40960",
|
||||
"guest_os_type": "Ubuntu_64",
|
||||
"headless" : "true",
|
||||
|
||||
"ssh_password": "vagrant",
|
||||
"ssh_username": "vagrant",
|
||||
"ssh_wait_timeout": "10000s",
|
||||
|
||||
"http_directory": "../../builder/virtualbox/iso/acceptance/test-fixtures/http",
|
||||
"boot_wait": "10s",
|
||||
"boot_command": [
|
||||
"<esc><wait>",
|
||||
"<esc><wait>",
|
||||
"<enter><wait>",
|
||||
"/install/vmlinuz<wait>",
|
||||
" auto<wait>",
|
||||
" console-setup/ask_detect=false<wait>",
|
||||
" console-setup/layoutcode=us<wait>",
|
||||
" console-setup/modelcode=pc105<wait>",
|
||||
" debconf/frontend=noninteractive<wait>",
|
||||
" debian-installer=en_US.UTF-8<wait>",
|
||||
" fb=false<wait>",
|
||||
" initrd=/install/initrd.gz<wait>",
|
||||
" kbd-chooser/method=us<wait>",
|
||||
" keyboard-configuration/layout=USA<wait>",
|
||||
" keyboard-configuration/variant=USA<wait>",
|
||||
" locale=en_US.UTF-8<wait>",
|
||||
" netcfg/get_domain=vm<wait>",
|
||||
" netcfg/get_hostname=vagrant<wait>",
|
||||
" noapic<wait>",
|
||||
" preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg<wait>",
|
||||
" -- <wait>",
|
||||
"<enter><wait>"
|
||||
],
|
||||
|
||||
"output_directory": "virtualbox-iso-packer-acc-test",
|
||||
"shutdown_command": "echo 'vagrant' | sudo -S shutdown -P now",
|
||||
"post_shutdown_delay": "60s"
|
||||
}
|
|
@ -49,6 +49,15 @@ type TestCase struct {
|
|||
// 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
|
||||
}
|
||||
|
||||
// TestCheckFunc is the callback used for Check in TestStep.
|
||||
|
@ -112,6 +121,12 @@ func Test(t TestT, c TestCase) {
|
|||
return
|
||||
}
|
||||
|
||||
if c.ProvisionerStore == nil {
|
||||
c.ProvisionerStore = packersdk.MapOfProvisioner{
|
||||
"shell": func() (packersdk.Provisioner, error) { return &shellprovisioner.Provisioner{}, nil },
|
||||
"file": func() (packersdk.Provisioner, error) { return &file.Provisioner{}, nil },
|
||||
}
|
||||
}
|
||||
// Build the core
|
||||
log.Printf("[DEBUG] Initializing core...")
|
||||
core := packer.NewCore(&packer.CoreConfig{
|
||||
|
@ -125,10 +140,7 @@ func Test(t TestT, c TestCase) {
|
|||
return nil, nil
|
||||
},
|
||||
},
|
||||
ProvisionerStore: packersdk.MapOfProvisioner{
|
||||
"shell": func() (packersdk.Provisioner, error) { return &shellprovisioner.Provisioner{}, nil },
|
||||
"file": func() (packersdk.Provisioner, error) { return &file.Provisioner{}, nil },
|
||||
},
|
||||
ProvisionerStore: c.ProvisionerStore,
|
||||
},
|
||||
Template: tpl,
|
||||
})
|
||||
|
@ -163,7 +175,6 @@ func Test(t TestT, c TestCase) {
|
|||
// Run it! We use a temporary directory for caching and discard
|
||||
// any UI output. We discard since it shows up in logs anyways.
|
||||
log.Printf("[DEBUG] Running 'test' build")
|
||||
// ui := packersdk.TestUi(t)
|
||||
ui := &packersdk.BasicUi{
|
||||
Reader: os.Stdin,
|
||||
Writer: ioutil.Discard,
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
@ -26,7 +25,7 @@ func TestAdapter_Serve(t *testing.T) {
|
|||
|
||||
config := &ssh.ServerConfig{}
|
||||
|
||||
ui := new(packer.NoopUi)
|
||||
ui := packersdk.TestUi(t)
|
||||
|
||||
sut := NewAdapter(done, &l, config, "", ui, communicator{})
|
||||
go func() {
|
||||
|
|
|
@ -151,6 +151,56 @@ func (rw *BasicUi) TrackProgress(src string, currentSize, totalSize int64, strea
|
|||
return rw.PB.TrackProgress(src, currentSize, totalSize, stream)
|
||||
}
|
||||
|
||||
// Safe is a UI that wraps another UI implementation and
|
||||
// provides concurrency-safe access
|
||||
type SafeUi struct {
|
||||
Sem chan int
|
||||
Ui Ui
|
||||
PB getter.ProgressTracker
|
||||
}
|
||||
|
||||
var _ Ui = new(SafeUi)
|
||||
|
||||
func (u *SafeUi) Ask(s string) (string, error) {
|
||||
u.Sem <- 1
|
||||
ret, err := u.Ui.Ask(s)
|
||||
<-u.Sem
|
||||
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (u *SafeUi) Say(s string) {
|
||||
u.Sem <- 1
|
||||
u.Ui.Say(s)
|
||||
<-u.Sem
|
||||
}
|
||||
|
||||
func (u *SafeUi) Message(s string) {
|
||||
u.Sem <- 1
|
||||
u.Ui.Message(s)
|
||||
<-u.Sem
|
||||
}
|
||||
|
||||
func (u *SafeUi) Error(s string) {
|
||||
u.Sem <- 1
|
||||
u.Ui.Error(s)
|
||||
<-u.Sem
|
||||
}
|
||||
|
||||
func (u *SafeUi) Machine(t string, args ...string) {
|
||||
u.Sem <- 1
|
||||
u.Ui.Machine(t, args...)
|
||||
<-u.Sem
|
||||
}
|
||||
|
||||
func (u *SafeUi) TrackProgress(src string, currentSize, totalSize int64, stream io.ReadCloser) (body io.ReadCloser) {
|
||||
u.Sem <- 1
|
||||
ret := u.Ui.TrackProgress(src, currentSize, totalSize, stream)
|
||||
<-u.Sem
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// NoopProgressTracker is a progress tracker
|
||||
// that displays nothing.
|
||||
type NoopProgressTracker struct{}
|
||||
|
|
|
@ -7,6 +7,8 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
// TestUi creates a simple UI for use in testing.
|
||||
// It's not meant for "real" use.
|
||||
func TestUi(t *testing.T) Ui {
|
||||
var buf bytes.Buffer
|
||||
return &BasicUi{
|
||||
|
|
65
packer/ui.go
65
packer/ui.go
|
@ -30,21 +30,6 @@ const (
|
|||
UiColorCyan = 36
|
||||
)
|
||||
|
||||
type NoopUi struct {
|
||||
PB packersdk.NoopProgressTracker
|
||||
}
|
||||
|
||||
var _ packersdk.Ui = new(NoopUi)
|
||||
|
||||
func (*NoopUi) Ask(string) (string, error) { return "", errors.New("this is a noop ui") }
|
||||
func (*NoopUi) Say(string) { return }
|
||||
func (*NoopUi) Message(string) { return }
|
||||
func (*NoopUi) Error(string) { return }
|
||||
func (*NoopUi) Machine(string, ...string) { return }
|
||||
func (u *NoopUi) TrackProgress(src string, currentSize, totalSize int64, stream io.ReadCloser) io.ReadCloser {
|
||||
return u.PB.TrackProgress(src, currentSize, totalSize, stream)
|
||||
}
|
||||
|
||||
// ColoredUi is a UI that is colored using terminal colors.
|
||||
type ColoredUi struct {
|
||||
Color UiColor
|
||||
|
@ -267,53 +252,3 @@ func (u *TimestampedUi) TrackProgress(src string, currentSize, totalSize int64,
|
|||
func (u *TimestampedUi) timestampLine(string string) string {
|
||||
return fmt.Sprintf("%v: %v", time.Now().Format(time.RFC3339), string)
|
||||
}
|
||||
|
||||
// Safe is a UI that wraps another UI implementation and
|
||||
// provides concurrency-safe access
|
||||
type SafeUi struct {
|
||||
Sem chan int
|
||||
Ui packersdk.Ui
|
||||
PB getter.ProgressTracker
|
||||
}
|
||||
|
||||
var _ packersdk.Ui = new(SafeUi)
|
||||
|
||||
func (u *SafeUi) Ask(s string) (string, error) {
|
||||
u.Sem <- 1
|
||||
ret, err := u.Ui.Ask(s)
|
||||
<-u.Sem
|
||||
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (u *SafeUi) Say(s string) {
|
||||
u.Sem <- 1
|
||||
u.Ui.Say(s)
|
||||
<-u.Sem
|
||||
}
|
||||
|
||||
func (u *SafeUi) Message(s string) {
|
||||
u.Sem <- 1
|
||||
u.Ui.Message(s)
|
||||
<-u.Sem
|
||||
}
|
||||
|
||||
func (u *SafeUi) Error(s string) {
|
||||
u.Sem <- 1
|
||||
u.Ui.Error(s)
|
||||
<-u.Sem
|
||||
}
|
||||
|
||||
func (u *SafeUi) Machine(t string, args ...string) {
|
||||
u.Sem <- 1
|
||||
u.Ui.Machine(t, args...)
|
||||
<-u.Sem
|
||||
}
|
||||
|
||||
func (u *SafeUi) TrackProgress(src string, currentSize, totalSize int64, stream io.ReadCloser) (body io.ReadCloser) {
|
||||
u.Sem <- 1
|
||||
ret := u.Ui.TrackProgress(src, currentSize, totalSize, stream)
|
||||
<-u.Sem
|
||||
|
||||
return ret
|
||||
}
|
||||
|
|
|
@ -12,9 +12,8 @@ import (
|
|||
"os/exec"
|
||||
|
||||
"github.com/hashicorp/packer/builder/docker"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
builderT "github.com/hashicorp/packer/packer-plugin-sdk/acctest"
|
||||
packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer/packer-plugin-sdk/template"
|
||||
"github.com/hashicorp/packer/provisioner/file"
|
||||
)
|
||||
|
||||
|
@ -135,7 +134,7 @@ func TestProvisionerProvision_PlaybookFiles(t *testing.T) {
|
|||
}
|
||||
|
||||
comm := &communicatorMock{}
|
||||
if err := p.Provision(context.Background(), new(packer.NoopUi), comm, make(map[string]interface{})); err != nil {
|
||||
if err := p.Provision(context.Background(), packersdk.TestUi(t), comm, make(map[string]interface{})); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
|
@ -169,7 +168,7 @@ func TestProvisionerProvision_PlaybookFilesWithPlaybookDir(t *testing.T) {
|
|||
}
|
||||
|
||||
comm := &communicatorMock{}
|
||||
if err := p.Provision(context.Background(), new(packer.NoopUi), comm, make(map[string]interface{})); err != nil {
|
||||
if err := p.Provision(context.Background(), packersdk.TestUi(t), comm, make(map[string]interface{})); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
|
@ -329,69 +328,38 @@ func testProvisionerProvisionDockerWithPlaybookFiles(t *testing.T, templateStrin
|
|||
t.Skip("This test is only run with PACKER_ACC=1")
|
||||
}
|
||||
|
||||
ui := packersdk.TestUi(t)
|
||||
|
||||
tpl, err := template.Parse(strings.NewReader(templateString))
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to parse config: %s", err)
|
||||
}
|
||||
|
||||
// Check if docker executable can be found.
|
||||
_, err = exec.LookPath("docker")
|
||||
// this should be a precheck
|
||||
cmd := exec.Command("docker", "-v")
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
t.Error("docker command not found; please make sure docker is installed")
|
||||
}
|
||||
|
||||
// Setup the builder
|
||||
builder := &docker.Builder{}
|
||||
_, warnings, err := builder.Prepare(tpl.Builders["docker"].Config)
|
||||
if err != nil {
|
||||
t.Fatalf("Error preparing configuration %s", err)
|
||||
}
|
||||
if len(warnings) > 0 {
|
||||
t.Fatal("Encountered configuration warnings; aborting")
|
||||
}
|
||||
|
||||
ansible := &Provisioner{}
|
||||
err = ansible.Prepare(tpl.Provisioners[0].Config)
|
||||
if err != nil {
|
||||
t.Fatalf("Error preparing ansible-local provisioner: %s", err)
|
||||
}
|
||||
|
||||
download := &file.Provisioner{}
|
||||
err = download.Prepare(tpl.Provisioners[1].Config)
|
||||
if err != nil {
|
||||
t.Fatalf("Error preparing download: %s", err)
|
||||
}
|
||||
|
||||
// Add hooks so the provisioners run during the build
|
||||
hooks := map[string][]packersdk.Hook{}
|
||||
hooks[packersdk.HookProvision] = []packersdk.Hook{
|
||||
&packer.ProvisionHook{
|
||||
Provisioners: []*packer.HookedProvisioner{
|
||||
{Provisioner: ansible, Config: nil, TypeName: ""},
|
||||
{Provisioner: download, Config: nil, TypeName: ""},
|
||||
},
|
||||
},
|
||||
}
|
||||
hook := &packersdk.DispatchHook{Mapping: hooks}
|
||||
|
||||
artifact, err := builder.Run(context.Background(), ui, hook)
|
||||
if err != nil {
|
||||
t.Fatalf("Error running build %s", err)
|
||||
}
|
||||
defer os.Remove("hello_world")
|
||||
defer artifact.Destroy()
|
||||
builderT.Test(t, builderT.TestCase{
|
||||
Builder: &docker.Builder{},
|
||||
Template: templateString,
|
||||
Check: func(a []packersdk.Artifact) error {
|
||||
|
||||
actualContent, err := ioutil.ReadFile("hello_world")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected file not found: %s", err)
|
||||
return fmt.Errorf("Expected file not found: %s", err)
|
||||
}
|
||||
|
||||
expectedContent := "Hello world!"
|
||||
if string(actualContent) != expectedContent {
|
||||
t.Fatalf(`Unexpected file content: expected="%s", actual="%s"`, expectedContent, actualContent)
|
||||
return fmt.Errorf(`Unexpected file content: expected="%s", actual="%s"`, expectedContent, actualContent)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Teardown: func() error {
|
||||
os.Remove("hello_world")
|
||||
return nil
|
||||
},
|
||||
ProvisionerStore: packersdk.MapOfProvisioner{
|
||||
"ansible-local": func() (packersdk.Provisioner, error) { return &Provisioner{}, nil },
|
||||
"file": func() (packersdk.Provisioner, error) { return &file.Provisioner{}, nil },
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func assertPlaybooksExecuted(comm *communicatorMock, playbooks []string) {
|
||||
|
@ -477,7 +445,7 @@ const playbookFilesDockerTemplate = `
|
|||
{
|
||||
"builders": [
|
||||
{
|
||||
"type": "docker",
|
||||
"type": "test",
|
||||
"image": "williamyeh/ansible:centos7",
|
||||
"discard": true
|
||||
}
|
||||
|
@ -504,7 +472,7 @@ const playbookFilesWithPlaybookDirDockerTemplate = `
|
|||
{
|
||||
"builders": [
|
||||
{
|
||||
"type": "docker",
|
||||
"type": "test",
|
||||
"image": "williamyeh/ansible:centos7",
|
||||
"discard": true
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ import (
|
|||
"golang.org/x/crypto/ssh"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/packer-plugin-sdk/adapter"
|
||||
"github.com/hashicorp/packer/packer-plugin-sdk/common"
|
||||
"github.com/hashicorp/packer/packer-plugin-sdk/multistep/commonsteps"
|
||||
|
@ -433,7 +432,7 @@ func (p *Provisioner) setupAdapter(ui packersdk.Ui, comm packersdk.Communicator)
|
|||
return "", err
|
||||
}
|
||||
|
||||
ui = &packer.SafeUi{
|
||||
ui = &packersdk.SafeUi{
|
||||
Sem: make(chan int, 1),
|
||||
Ui: ui,
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ import (
|
|||
"golang.org/x/crypto/ssh"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/packer-plugin-sdk/adapter"
|
||||
"github.com/hashicorp/packer/packer-plugin-sdk/common"
|
||||
packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer"
|
||||
|
@ -304,7 +303,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe
|
|||
return err
|
||||
}
|
||||
|
||||
ui = &packer.SafeUi{
|
||||
ui = &packersdk.SafeUi{
|
||||
Sem: make(chan int, 1),
|
||||
Ui: ui,
|
||||
}
|
||||
|
|
|
@ -1,94 +1,110 @@
|
|||
package powershell_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/packer/command"
|
||||
"github.com/hashicorp/packer/packer-plugin-sdk/acctest/provisioneracc"
|
||||
packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer/provisioner/powershell"
|
||||
windowsshellprovisioner "github.com/hashicorp/packer/provisioner/windows-shell"
|
||||
)
|
||||
|
||||
const TestProvisionerName = "powershell"
|
||||
const TestProvisionerType = "powershell"
|
||||
|
||||
func TestAccPowershellProvisioner_basic(t *testing.T) {
|
||||
provisioneracc.TestProvisionersPreCheck(TestProvisionerName, t)
|
||||
|
||||
testProvisioner := PowershellProvisionerAccTest{"powershell-provisioner-cleanup.txt"}
|
||||
provisioneracc.TestProvisionersAgainstBuilders(&testProvisioner, t)
|
||||
}
|
||||
|
||||
func TestAccPowershellProvisioner_Inline(t *testing.T) {
|
||||
provisioneracc.TestProvisionersPreCheck(TestProvisionerName, t)
|
||||
|
||||
testProvisioner := PowershellProvisionerAccTest{"powershell-inline-provisioner.txt"}
|
||||
provisioneracc.TestProvisionersAgainstBuilders(&testProvisioner, t)
|
||||
}
|
||||
|
||||
func TestAccPowershellProvisioner_Script(t *testing.T) {
|
||||
provisioneracc.TestProvisionersPreCheck(TestProvisionerName, t)
|
||||
|
||||
testProvisioner := PowershellProvisionerAccTest{"powershell-script-provisioner.txt"}
|
||||
provisioneracc.TestProvisionersAgainstBuilders(&testProvisioner, t)
|
||||
}
|
||||
|
||||
type PowershellProvisionerAccTest struct {
|
||||
ConfigName string
|
||||
}
|
||||
|
||||
func (s *PowershellProvisionerAccTest) GetName() string {
|
||||
return TestProvisionerName
|
||||
}
|
||||
|
||||
func (s *PowershellProvisionerAccTest) GetConfig() (string, error) {
|
||||
filePath := filepath.Join("./test-fixtures", s.ConfigName)
|
||||
config, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("os.Open:%v", err)
|
||||
}
|
||||
defer config.Close()
|
||||
|
||||
file, err := ioutil.ReadAll(config)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ioutil.ReadAll:%v", err)
|
||||
}
|
||||
return string(file), nil
|
||||
}
|
||||
|
||||
func (s *PowershellProvisionerAccTest) GetProvisionerStore() packersdk.MapOfProvisioner {
|
||||
return packersdk.MapOfProvisioner{
|
||||
TestProvisionerName: func() (packersdk.Provisioner, error) { return &powershell.Provisioner{}, nil },
|
||||
"windows-shell": func() (packersdk.Provisioner, error) { return &windowsshellprovisioner.Provisioner{}, nil },
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PowershellProvisionerAccTest) IsCompatible(builder string, vmOS string) bool {
|
||||
func powershellIsCompatible(builder string, vmOS string) bool {
|
||||
return vmOS == "windows"
|
||||
}
|
||||
|
||||
func (s *PowershellProvisionerAccTest) RunTest(c *command.BuildCommand, args []string) error {
|
||||
UUID := os.Getenv("PACKER_RUN_UUID")
|
||||
if UUID == "" {
|
||||
UUID, _ = uuid.GenerateUUID()
|
||||
os.Setenv("PACKER_RUN_UUID", UUID)
|
||||
func fixtureDir() string {
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
return filepath.Join(filepath.Dir(file), "test-fixtures")
|
||||
}
|
||||
|
||||
if code := c.Run(args); code != 0 {
|
||||
ui := c.Meta.Ui.(*packersdk.BasicUi)
|
||||
out := ui.Writer.(*bytes.Buffer)
|
||||
err := ui.ErrorWriter.(*bytes.Buffer)
|
||||
return fmt.Errorf(
|
||||
"Bad exit code.\n\nStdout:\n\n%s\n\nStderr:\n\n%s",
|
||||
out.String(),
|
||||
err.String())
|
||||
func LoadProvisionerFragment(templateFragmentPath string) (string, error) {
|
||||
dir := fixtureDir()
|
||||
fragmentAbsPath := filepath.Join(dir, templateFragmentPath)
|
||||
fragmentFile, err := os.Open(fragmentAbsPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Unable find %s", fragmentAbsPath)
|
||||
}
|
||||
defer fragmentFile.Close()
|
||||
|
||||
fragmentString, err := ioutil.ReadAll(fragmentFile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Unable to read %s", fragmentAbsPath)
|
||||
}
|
||||
|
||||
return string(fragmentString), nil
|
||||
}
|
||||
|
||||
func TestAccPowershellProvisioner_basic(t *testing.T) {
|
||||
templateString, err := LoadProvisionerFragment("powershell-provisioner-cleanup.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't load test fixture; %s", err.Error())
|
||||
}
|
||||
testCase := &provisioneracc.ProvisionerTestCase{
|
||||
IsCompatible: powershellIsCompatible,
|
||||
Name: "powershell-provisioner-cleanup",
|
||||
Template: templateString,
|
||||
Type: TestProvisionerType,
|
||||
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)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
provisioneracc.TestProvisionersAgainstBuilders(testCase, t)
|
||||
}
|
||||
|
||||
func TestAccPowershellProvisioner_Inline(t *testing.T) {
|
||||
templateString, err := LoadProvisionerFragment("powershell-inline-provisioner.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't load test fixture; %s", err.Error())
|
||||
}
|
||||
testCase := &provisioneracc.ProvisionerTestCase{
|
||||
IsCompatible: powershellIsCompatible,
|
||||
Name: "powershell-provisioner-inline",
|
||||
Template: templateString,
|
||||
Type: TestProvisionerType,
|
||||
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)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
provisioneracc.TestProvisionersAgainstBuilders(testCase, t)
|
||||
}
|
||||
|
||||
func TestAccPowershellProvisioner_Script(t *testing.T) {
|
||||
templateString, err := LoadProvisionerFragment("powershell-script-provisioner.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't load test fixture; %s", err.Error())
|
||||
}
|
||||
testCase := &provisioneracc.ProvisionerTestCase{
|
||||
IsCompatible: powershellIsCompatible,
|
||||
Name: "powershell-provisioner-script",
|
||||
Template: templateString,
|
||||
Type: TestProvisionerType,
|
||||
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)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
provisioneracc.TestProvisionersAgainstBuilders(testCase, t)
|
||||
}
|
||||
|
|
|
@ -1,65 +1,76 @@
|
|||
package shell_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/packer-plugin-sdk/acctest/provisioneracc"
|
||||
|
||||
"github.com/hashicorp/packer/provisioner/shell"
|
||||
|
||||
packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer"
|
||||
|
||||
"github.com/hashicorp/packer/command"
|
||||
"github.com/hashicorp/packer/packer-plugin-sdk/acctest/testutils"
|
||||
)
|
||||
|
||||
func TestShellLocalProvisionerWithRetryOption(t *testing.T) {
|
||||
provisioneracc.TestProvisionersPreCheck("shell-local", t)
|
||||
provisioneracc.TestProvisionersAgainstBuilders(new(ShellLocalProvisionerAccTest), t)
|
||||
func fixtureDir() string {
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
return filepath.Join(filepath.Dir(file), "test-fixtures")
|
||||
}
|
||||
|
||||
type ShellLocalProvisionerAccTest struct{}
|
||||
|
||||
func (s *ShellLocalProvisionerAccTest) GetName() string {
|
||||
return "file"
|
||||
}
|
||||
|
||||
func (s *ShellLocalProvisionerAccTest) GetConfig() (string, error) {
|
||||
filePath := filepath.Join("./test-fixtures", "shell-local-provisioner.txt")
|
||||
config, err := os.Open(filePath)
|
||||
func loadFile(templateFragmentPath string) (string, error) {
|
||||
dir := fixtureDir()
|
||||
fragmentAbsPath := filepath.Join(dir, templateFragmentPath)
|
||||
fragmentFile, err := os.Open(fragmentAbsPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Expected to find %s", filePath)
|
||||
return "", fmt.Errorf("Unable find %s", fragmentAbsPath)
|
||||
}
|
||||
defer config.Close()
|
||||
defer fragmentFile.Close()
|
||||
|
||||
file, err := ioutil.ReadAll(config)
|
||||
return string(file), err
|
||||
fragmentString, err := ioutil.ReadAll(fragmentFile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Unable to read %s", fragmentAbsPath)
|
||||
}
|
||||
|
||||
func (s *ShellLocalProvisionerAccTest) GetProvisionerStore() packersdk.MapOfProvisioner {
|
||||
return packersdk.MapOfProvisioner{
|
||||
"shell-local": func() (packersdk.Provisioner, error) { return &shell.Provisioner{}, nil },
|
||||
}
|
||||
return string(fragmentString), nil
|
||||
}
|
||||
|
||||
func (s *ShellLocalProvisionerAccTest) IsCompatible(builder string, vmOS string) bool {
|
||||
func IsCompatible(builder string, vmOS string) bool {
|
||||
return vmOS == "linux"
|
||||
}
|
||||
|
||||
func (s *ShellLocalProvisionerAccTest) RunTest(c *command.BuildCommand, args []string) error {
|
||||
if code := c.Run(args); code != 0 {
|
||||
ui := c.Meta.Ui.(*packersdk.BasicUi)
|
||||
out := ui.Writer.(*bytes.Buffer)
|
||||
err := ui.ErrorWriter.(*bytes.Buffer)
|
||||
return fmt.Errorf(
|
||||
"Bad exit code.\n\nStdout:\n\n%s\n\nStderr:\n\n%s",
|
||||
out.String(),
|
||||
err.String())
|
||||
func TestAccShellProvisioner_basic(t *testing.T) {
|
||||
templateString, err := loadFile("shell-local-provisioner.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't load test fixture; %s", err.Error())
|
||||
}
|
||||
|
||||
testCase := &provisioneracc.ProvisionerTestCase{
|
||||
IsCompatible: IsCompatible,
|
||||
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\n", 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)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [[ ! -f file.txt ]] ; then
|
||||
echo 'hello' > file.txt
|
||||
# On first try, exits 1; on second try, passes.
|
||||
if [[ ! -f test-fixtures/file.txt ]] ; then
|
||||
echo 'hello' > test-fixtures/file.txt
|
||||
exit 1
|
||||
fi
|
|
@ -1,80 +1,77 @@
|
|||
package shell_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/packer-plugin-sdk/acctest/provisioneracc"
|
||||
"github.com/hashicorp/packer/packer-plugin-sdk/acctest/testutils"
|
||||
"github.com/hashicorp/packer/provisioner/file"
|
||||
"github.com/hashicorp/packer/provisioner/shell"
|
||||
|
||||
packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer"
|
||||
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/packer/command"
|
||||
)
|
||||
|
||||
func TestShellProvisioner(t *testing.T) {
|
||||
provisioneracc.TestProvisionersPreCheck("shell", t)
|
||||
provisioneracc.TestProvisionersAgainstBuilders(new(ShellProvisionerAccTest), t)
|
||||
func fixtureDir() string {
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
return filepath.Join(filepath.Dir(file), "test-fixtures")
|
||||
}
|
||||
|
||||
type ShellProvisionerAccTest struct{}
|
||||
|
||||
func (s *ShellProvisionerAccTest) GetName() string {
|
||||
return "shell"
|
||||
}
|
||||
|
||||
func (s *ShellProvisionerAccTest) GetConfig() (string, error) {
|
||||
filePath := filepath.Join("./test-fixtures", "shell-provisioner.txt")
|
||||
config, err := os.Open(filePath)
|
||||
func loadFile(templateFragmentPath string) (string, error) {
|
||||
dir := fixtureDir()
|
||||
fragmentAbsPath := filepath.Join(dir, templateFragmentPath)
|
||||
fragmentFile, err := os.Open(fragmentAbsPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Expected to find %s", filePath)
|
||||
return "", fmt.Errorf("Unable find %s", fragmentAbsPath)
|
||||
}
|
||||
defer config.Close()
|
||||
defer fragmentFile.Close()
|
||||
|
||||
file, err := ioutil.ReadAll(config)
|
||||
return string(file), err
|
||||
fragmentString, err := ioutil.ReadAll(fragmentFile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Unable to read %s", fragmentAbsPath)
|
||||
}
|
||||
|
||||
func (s *ShellProvisionerAccTest) GetProvisionerStore() packersdk.MapOfProvisioner {
|
||||
return packersdk.MapOfProvisioner{
|
||||
"shell": func() (packersdk.Provisioner, error) { return &shell.Provisioner{}, nil },
|
||||
"file": func() (packersdk.Provisioner, error) { return &file.Provisioner{}, nil },
|
||||
}
|
||||
return string(fragmentString), nil
|
||||
}
|
||||
|
||||
func (s *ShellProvisionerAccTest) IsCompatible(builder string, vmOS string) bool {
|
||||
func IsCompatible(builder string, vmOS string) bool {
|
||||
return vmOS == "linux"
|
||||
}
|
||||
|
||||
func (s *ShellProvisionerAccTest) RunTest(c *command.BuildCommand, args []string) error {
|
||||
UUID := os.Getenv("PACKER_RUN_UUID")
|
||||
if UUID == "" {
|
||||
UUID, _ = uuid.GenerateUUID()
|
||||
os.Setenv("PACKER_RUN_UUID", UUID)
|
||||
func TestAccShellProvisioner_basic(t *testing.T) {
|
||||
templateString, err := loadFile("shell-provisioner.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't load test fixture; %s", err.Error())
|
||||
}
|
||||
|
||||
file := "provisioner.shell." + UUID + ".txt"
|
||||
defer testutils.CleanupFiles(file)
|
||||
|
||||
if code := c.Run(args); code != 0 {
|
||||
ui := c.Meta.Ui.(*packersdk.BasicUi)
|
||||
out := ui.Writer.(*bytes.Buffer)
|
||||
err := ui.ErrorWriter.(*bytes.Buffer)
|
||||
return fmt.Errorf(
|
||||
"Bad exit code.\n\nStdout:\n\n%s\n\nStderr:\n\n%s",
|
||||
out.String(),
|
||||
err.String())
|
||||
testCase := &provisioneracc.ProvisionerTestCase{
|
||||
IsCompatible: IsCompatible,
|
||||
Name: "shell-provisioner-basic",
|
||||
Teardown: func() error {
|
||||
testutils.CleanupFiles("test-fixtures/provisioner.shell.txt")
|
||||
return nil
|
||||
},
|
||||
Template: templateString,
|
||||
Type: "shell",
|
||||
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)
|
||||
}
|
||||
|
||||
if !testutils.FileExists(file) {
|
||||
return fmt.Errorf("Expected to find %s", file)
|
||||
}
|
||||
filecontents, err := loadFile("provisioner.shell.txt")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
re := regexp.MustCompile(`build ID is .* and build UUID is [[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}`)
|
||||
if !re.MatchString(filecontents) {
|
||||
return fmt.Errorf("Bad file contents \"%s\"", filecontents)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
provisioneracc.TestProvisionersAgainstBuilders(testCase, t)
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"type": "shell",
|
||||
"inline": [
|
||||
"echo {{ build `ID`}} > provisioner.{{ build `PackerRunUUID`}}.txt"
|
||||
"echo build ID is {{ build `ID`}} and build UUID is {{ build `PackerRunUUID` }}> provisioner.{{ build `PackerRunUUID`}}.txt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"source": "provisioner.{{ build `PackerRunUUID`}}.txt",
|
||||
"destination": "provisioner.shell.{{ build `PackerRunUUID`}}.txt",
|
||||
"destination": "./test-fixtures/provisioner.shell.txt",
|
||||
"direction": "download"
|
||||
}
|
Loading…
Reference in New Issue