This commit is contained in:
parent
cf65b7b494
commit
d1254a5e48
|
@ -23,6 +23,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
@ -59,6 +60,7 @@ type Config struct {
|
|||
LocalPort int `mapstructure:"local_port"`
|
||||
SSHHostKeyFile string `mapstructure:"ssh_host_key_file"`
|
||||
SSHAuthorizedKeyFile string `mapstructure:"ssh_authorized_key_file"`
|
||||
ValidExitCodes []int `mapstructure:"valid_exit_codes"`
|
||||
}
|
||||
|
||||
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."))
|
||||
}
|
||||
|
||||
if p.config.ValidExitCodes == nil {
|
||||
p.config.ValidExitCodes = []int{0, 101}
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
@ -170,7 +176,7 @@ func (p *Provisioner) getVersion() 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))
|
||||
if matches == nil {
|
||||
return fmt.Errorf(
|
||||
|
@ -412,9 +418,33 @@ func (p *Provisioner) executeInspec(ui packersdk.Ui, comm packersdk.Communicator
|
|||
return err
|
||||
}
|
||||
wg.Wait()
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Non-zero exit status: %s", err)
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
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
|
||||
|
|
|
@ -30,6 +30,7 @@ type FlatConfig struct {
|
|||
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"`
|
||||
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.
|
||||
|
@ -64,6 +65,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||
"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_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
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
package inspec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -268,12 +271,17 @@ func TestProvisionerPrepare_LocalPort(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestInspecGetVersion(t *testing.T) {
|
||||
if os.Getenv("PACKER_ACC") == "" {
|
||||
t.Skip("This test is only run with PACKER_ACC=1 and it requires InSpec to be installed")
|
||||
var p Provisioner
|
||||
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.Command = "inspec exec"
|
||||
p.config.Backend = "local"
|
||||
err := p.getVersion()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
|
@ -291,3 +299,32 @@ func TestInspecGetVersionError(t *testing.T) {
|
|||
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.
|
||||
|
||||
- `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'
|
||||
|
||||
## Accepting the InSpec license
|
||||
|
|
Loading…
Reference in New Issue