This commit is contained in:
parent
cf65b7b494
commit
d1254a5e48
|
@ -23,6 +23,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
|
@ -59,6 +60,7 @@ type Config struct {
|
||||||
LocalPort int `mapstructure:"local_port"`
|
LocalPort int `mapstructure:"local_port"`
|
||||||
SSHHostKeyFile string `mapstructure:"ssh_host_key_file"`
|
SSHHostKeyFile string `mapstructure:"ssh_host_key_file"`
|
||||||
SSHAuthorizedKeyFile string `mapstructure:"ssh_authorized_key_file"`
|
SSHAuthorizedKeyFile string `mapstructure:"ssh_authorized_key_file"`
|
||||||
|
ValidExitCodes []int `mapstructure:"valid_exit_codes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Provisioner struct {
|
type Provisioner struct {
|
||||||
|
@ -157,6 +159,10 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
||||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("user: could not determine current user from environment."))
|
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("user: could not determine current user from environment."))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if p.config.ValidExitCodes == nil {
|
||||||
|
p.config.ValidExitCodes = []int{0, 101}
|
||||||
|
}
|
||||||
|
|
||||||
if errs != nil && len(errs.Errors) > 0 {
|
if errs != nil && len(errs.Errors) > 0 {
|
||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
@ -170,7 +176,7 @@ func (p *Provisioner) getVersion() error {
|
||||||
"Error running \"%s version\": %s", p.config.Command, err.Error())
|
"Error running \"%s version\": %s", p.config.Command, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
versionRe := regexp.MustCompile(`\w (\d+\.\d+[.\d+]*)`)
|
versionRe := regexp.MustCompile(`(\d+\.\d+[.\d+]*)`)
|
||||||
matches := versionRe.FindStringSubmatch(string(out))
|
matches := versionRe.FindStringSubmatch(string(out))
|
||||||
if matches == nil {
|
if matches == nil {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
|
@ -412,9 +418,33 @@ func (p *Provisioner) executeInspec(ui packersdk.Ui, comm packersdk.Communicator
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
err = cmd.Wait()
|
|
||||||
if err != nil {
|
if err := cmd.Wait(); err != nil {
|
||||||
return fmt.Errorf("Non-zero exit status: %s", err)
|
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||||
|
// The program has exited with an exit code != 0
|
||||||
|
|
||||||
|
// This works on both Unix and Windows. Although package
|
||||||
|
// syscall is generally platform dependent, WaitStatus is
|
||||||
|
// defined for both Unix and Windows and in both cases has
|
||||||
|
// an ExitStatus() method with the same signature.
|
||||||
|
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||||
|
exitStatus := status.ExitStatus()
|
||||||
|
// Check exit code against allowed codes (likely just 0)
|
||||||
|
validExitCode := false
|
||||||
|
for _, v := range p.config.ValidExitCodes {
|
||||||
|
if exitStatus == v {
|
||||||
|
validExitCode = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !validExitCode {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Inspec exited with unexpected exit status: %d. Expected exit codes are: %v",
|
||||||
|
exitStatus, p.config.ValidExitCodes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("Unable to get exit status: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -30,6 +30,7 @@ type FlatConfig struct {
|
||||||
LocalPort *int `mapstructure:"local_port" cty:"local_port" hcl:"local_port"`
|
LocalPort *int `mapstructure:"local_port" cty:"local_port" hcl:"local_port"`
|
||||||
SSHHostKeyFile *string `mapstructure:"ssh_host_key_file" cty:"ssh_host_key_file" hcl:"ssh_host_key_file"`
|
SSHHostKeyFile *string `mapstructure:"ssh_host_key_file" cty:"ssh_host_key_file" hcl:"ssh_host_key_file"`
|
||||||
SSHAuthorizedKeyFile *string `mapstructure:"ssh_authorized_key_file" cty:"ssh_authorized_key_file" hcl:"ssh_authorized_key_file"`
|
SSHAuthorizedKeyFile *string `mapstructure:"ssh_authorized_key_file" cty:"ssh_authorized_key_file" hcl:"ssh_authorized_key_file"`
|
||||||
|
ValidExitCodes []int `mapstructure:"valid_exit_codes" cty:"valid_exit_codes" hcl:"valid_exit_codes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlatMapstructure returns a new FlatConfig.
|
// FlatMapstructure returns a new FlatConfig.
|
||||||
|
@ -64,6 +65,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
"local_port": &hcldec.AttrSpec{Name: "local_port", Type: cty.Number, Required: false},
|
"local_port": &hcldec.AttrSpec{Name: "local_port", Type: cty.Number, Required: false},
|
||||||
"ssh_host_key_file": &hcldec.AttrSpec{Name: "ssh_host_key_file", Type: cty.String, Required: false},
|
"ssh_host_key_file": &hcldec.AttrSpec{Name: "ssh_host_key_file", Type: cty.String, Required: false},
|
||||||
"ssh_authorized_key_file": &hcldec.AttrSpec{Name: "ssh_authorized_key_file", Type: cty.String, Required: false},
|
"ssh_authorized_key_file": &hcldec.AttrSpec{Name: "ssh_authorized_key_file", Type: cty.String, Required: false},
|
||||||
|
"valid_exit_codes": &hcldec.AttrSpec{Name: "valid_exit_codes", Type: cty.List(cty.Number), Required: false},
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
package inspec
|
package inspec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -268,12 +271,17 @@ func TestProvisionerPrepare_LocalPort(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInspecGetVersion(t *testing.T) {
|
func TestInspecGetVersion(t *testing.T) {
|
||||||
if os.Getenv("PACKER_ACC") == "" {
|
var p Provisioner
|
||||||
t.Skip("This test is only run with PACKER_ACC=1 and it requires InSpec to be installed")
|
if os.Getenv("PACKER_ACC") == "1" {
|
||||||
|
p.config.Command = "inspec"
|
||||||
|
} else {
|
||||||
|
p.config.Command = "./test-fixtures/inspec_version.sh"
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skip("This test is only run with PACKER_ACC=1 and it requires InSpec to be installed")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var p Provisioner
|
p.config.Backend = "local"
|
||||||
p.config.Command = "inspec exec"
|
|
||||||
err := p.getVersion()
|
err := p.getVersion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
|
@ -291,3 +299,32 @@ func TestInspecGetVersionError(t *testing.T) {
|
||||||
t.Fatal("Error message should include command name")
|
t.Fatal("Error message should include command name")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInspecValidExitCodes(t *testing.T) {
|
||||||
|
var p Provisioner
|
||||||
|
if os.Getenv("PACKER_ACC") == "1" {
|
||||||
|
p.config.Command = "inspec"
|
||||||
|
} else {
|
||||||
|
p.config.Command = "./test-fixtures/valid_exit_codes.sh"
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skip("This test is only run with PACKER_ACC=1 and it requires InSpec to be installed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.config.Backend = "local"
|
||||||
|
p.config.Profile = "test-fixtures/skip_control.rb"
|
||||||
|
err := p.Prepare()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
comm := &packersdk.MockCommunicator{}
|
||||||
|
ui := &packersdk.BasicUi{
|
||||||
|
Reader: new(bytes.Buffer),
|
||||||
|
Writer: new(bytes.Buffer),
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p.Provision(context.Background(), ui, comm, make(map[string]interface{}))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
cat <<EOB
|
||||||
|
4.26.13
|
||||||
|
EOB
|
||||||
|
|
||||||
|
exit 0
|
|
@ -0,0 +1,13 @@
|
||||||
|
# copyright: 2018, The Authors
|
||||||
|
|
||||||
|
title "skip control"
|
||||||
|
|
||||||
|
control "skip-1.0" do
|
||||||
|
impact 1.0
|
||||||
|
title "skip control"
|
||||||
|
desc "skip control to generate a 101 return code"
|
||||||
|
only_if { 1 == 2 }
|
||||||
|
describe file("/tmp") do
|
||||||
|
it { should be_directory }
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
cat <<EOB
|
||||||
|
Profile: tests from test-fixtures/skip_control.rb (tests from test-fixtures/valid_exit_codes.sh)
|
||||||
|
Version: (not specified)
|
||||||
|
Target: local://
|
||||||
|
|
||||||
|
[38;5;247m ↺ skip-1.0: skip control[0m
|
||||||
|
[38;5;247m ↺ Skipped control due to only_if condition.[0m
|
||||||
|
|
||||||
|
|
||||||
|
Profile Summary: 0 successful controls, 0 control failures, [38;5;247m1 control skipped[0m
|
||||||
|
Test Summary: 0 successful, 0 failures, [38;5;247m1 skipped[0m
|
||||||
|
EOB
|
||||||
|
|
||||||
|
exit 101
|
|
@ -159,6 +159,11 @@ Optional Parameters:
|
||||||
|
|
||||||
- `user` (string) - The `--user` to use. Defaults to the user running Packer.
|
- `user` (string) - The `--user` to use. Defaults to the user running Packer.
|
||||||
|
|
||||||
|
- `valid_exit_codes` (list of ints) - A list of valid exit codes returned by
|
||||||
|
inspec to be accepted by the provisioner. The default is (0,101) (accept
|
||||||
|
skipped tests). See [inspec exit codes](https://docs.chef.io/inspec/cli/#exec)
|
||||||
|
for a list and explanation of inspec exit codes.
|
||||||
|
|
||||||
@include 'provisioners/common-config.mdx'
|
@include 'provisioners/common-config.mdx'
|
||||||
|
|
||||||
## Accepting the InSpec license
|
## Accepting the InSpec license
|
||||||
|
|
Loading…
Reference in New Issue