package inspec import ( "bytes" "context" "crypto/rand" "fmt" "io" "io/ioutil" "os" "path" "runtime" "strings" "testing" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" ) // Be sure to remove the InSpec stub file in each test with: // defer os.Remove(config["command"].(string)) func testConfig(t *testing.T) map[string]interface{} { m := make(map[string]interface{}) wd, err := os.Getwd() if err != nil { t.Fatalf("err: %s", err) } inspec_stub := path.Join(wd, "packer-inspec-stub.sh") err = ioutil.WriteFile(inspec_stub, []byte("#!/usr/bin/env bash\necho 2.2.16"), 0777) if err != nil { t.Fatalf("err: %s", err) } m["command"] = inspec_stub return m } func TestProvisioner_Impl(t *testing.T) { var raw interface{} raw = &Provisioner{} if _, ok := raw.(packersdk.Provisioner); !ok { t.Fatalf("must be a Provisioner") } } func TestProvisionerPrepare_Defaults(t *testing.T) { var p Provisioner config := testConfig(t) defer os.Remove(config["command"].(string)) err := p.Prepare(config) if err == nil { t.Fatalf("should have error") } hostkey_file, err := ioutil.TempFile("", "hostkey") if err != nil { t.Fatalf("err: %s", err) } defer os.Remove(hostkey_file.Name()) publickey_file, err := ioutil.TempFile("", "publickey") if err != nil { t.Fatalf("err: %s", err) } defer os.Remove(publickey_file.Name()) profile_file, err := ioutil.TempFile("", "test") if err != nil { t.Fatalf("err: %s", err) } defer os.Remove(profile_file.Name()) config["ssh_host_key_file"] = hostkey_file.Name() config["ssh_authorized_key_file"] = publickey_file.Name() config["profile"] = profile_file.Name() err = p.Prepare(config) if err != nil { t.Fatalf("err: %s", err) } defer os.Remove(profile_file.Name()) err = os.Unsetenv("USER") if err != nil { t.Fatalf("err: %s", err) } err = p.Prepare(config) if err != nil { t.Fatalf("err: %s", err) } } func TestProvisionerPrepare_ProfileFile(t *testing.T) { var p Provisioner config := testConfig(t) defer os.Remove(config["command"].(string)) hostkey_file, err := ioutil.TempFile("", "hostkey") if err != nil { t.Fatalf("err: %s", err) } defer os.Remove(hostkey_file.Name()) publickey_file, err := ioutil.TempFile("", "publickey") if err != nil { t.Fatalf("err: %s", err) } defer os.Remove(publickey_file.Name()) config["ssh_host_key_file"] = hostkey_file.Name() config["ssh_authorized_key_file"] = publickey_file.Name() err = p.Prepare(config) if err == nil { t.Fatal("should have error") } profile_file, err := ioutil.TempFile("", "test") if err != nil { t.Fatalf("err: %s", err) } defer os.Remove(profile_file.Name()) config["profile"] = profile_file.Name() err = p.Prepare(config) if err != nil { t.Fatalf("err: %s", err) } test_dir, err := ioutil.TempDir("", "test") if err != nil { t.Fatalf("err: %s", err) } defer os.Remove(test_dir) config["profile"] = test_dir err = p.Prepare(config) if err != nil { t.Fatalf("err: %s", err) } } func TestProvisionerPrepare_HostKeyFile(t *testing.T) { var p Provisioner config := testConfig(t) defer os.Remove(config["command"].(string)) publickey_file, err := ioutil.TempFile("", "publickey") if err != nil { t.Fatalf("err: %s", err) } defer os.Remove(publickey_file.Name()) profile_file, err := ioutil.TempFile("", "test") if err != nil { t.Fatalf("err: %s", err) } defer os.Remove(profile_file.Name()) filename := make([]byte, 10) n, err := io.ReadFull(rand.Reader, filename) if n != len(filename) || err != nil { t.Fatal("could not create random file name") } config["ssh_host_key_file"] = fmt.Sprintf("%x", filename) config["ssh_authorized_key_file"] = publickey_file.Name() config["profile"] = profile_file.Name() err = p.Prepare(config) if err == nil { t.Fatal("should error if ssh_host_key_file does not exist") } hostkey_file, err := ioutil.TempFile("", "hostkey") if err != nil { t.Fatalf("err: %s", err) } defer os.Remove(hostkey_file.Name()) config["ssh_host_key_file"] = hostkey_file.Name() err = p.Prepare(config) if err != nil { t.Fatalf("err: %s", err) } } func TestProvisionerPrepare_AuthorizedKeyFiles(t *testing.T) { var p Provisioner config := testConfig(t) defer os.Remove(config["command"].(string)) hostkey_file, err := ioutil.TempFile("", "hostkey") if err != nil { t.Fatalf("err: %s", err) } defer os.Remove(hostkey_file.Name()) profile_file, err := ioutil.TempFile("", "test") if err != nil { t.Fatalf("err: %s", err) } defer os.Remove(profile_file.Name()) filename := make([]byte, 10) n, err := io.ReadFull(rand.Reader, filename) if n != len(filename) || err != nil { t.Fatal("could not create random file name") } config["ssh_host_key_file"] = hostkey_file.Name() config["profile"] = profile_file.Name() config["ssh_authorized_key_file"] = fmt.Sprintf("%x", filename) err = p.Prepare(config) if err == nil { t.Errorf("should error if ssh_authorized_key_file does not exist") } publickey_file, err := ioutil.TempFile("", "publickey") if err != nil { t.Fatalf("err: %s", err) } defer os.Remove(publickey_file.Name()) config["ssh_authorized_key_file"] = publickey_file.Name() err = p.Prepare(config) if err != nil { t.Errorf("err: %s", err) } } func TestProvisionerPrepare_LocalPort(t *testing.T) { var p Provisioner config := testConfig(t) defer os.Remove(config["command"].(string)) hostkey_file, err := ioutil.TempFile("", "hostkey") if err != nil { t.Fatalf("err: %s", err) } defer os.Remove(hostkey_file.Name()) publickey_file, err := ioutil.TempFile("", "publickey") if err != nil { t.Fatalf("err: %s", err) } defer os.Remove(publickey_file.Name()) profile_file, err := ioutil.TempFile("", "test") if err != nil { t.Fatalf("err: %s", err) } defer os.Remove(profile_file.Name()) config["ssh_host_key_file"] = hostkey_file.Name() config["ssh_authorized_key_file"] = publickey_file.Name() config["profile"] = profile_file.Name() config["local_port"] = 65537 err = p.Prepare(config) if err == nil { t.Fatal("should have error") } config["local_port"] = 22222 err = p.Prepare(config) if err != nil { t.Fatalf("err: %s", err) } } func TestInspecGetVersion(t *testing.T) { 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") } } p.config.Backend = "local" err := p.getVersion() if err != nil { t.Fatalf("err: %s", err) } } func TestInspecGetVersionError(t *testing.T) { var p Provisioner p.config.Command = "./test-fixtures/exit1" err := p.getVersion() if err == nil { t.Fatal("Should return error") } if !strings.Contains(err.Error(), "./test-fixtures/exit1 version") { 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) } }