mirror of
https://github.com/hashicorp/packer.git
synced 2026-03-03 05:51:40 -05:00
Merge pull request #6129 from hashicorp/bc_replace
Replace boot command parser with PEG parser.
This commit is contained in:
commit
024fac4b4f
95 changed files with 8214 additions and 3003 deletions
3
Makefile
3
Makefile
|
|
@ -42,6 +42,7 @@ package:
|
|||
|
||||
deps:
|
||||
@go get golang.org/x/tools/cmd/stringer
|
||||
@go get -u github.com/mna/pigeon
|
||||
@go get github.com/kardianos/govendor
|
||||
@govendor sync
|
||||
|
||||
|
|
@ -73,6 +74,8 @@ fmt-examples:
|
|||
# source files.
|
||||
generate: deps ## Generate dynamically generated code
|
||||
go generate .
|
||||
gofmt -w common/bootcommand/boot_command.go
|
||||
goimports -w common/bootcommand/boot_command.go
|
||||
gofmt -w command/plugin.go
|
||||
|
||||
test: deps fmt-check ## Run unit tests
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
)
|
||||
|
||||
type RunConfig struct {
|
||||
RawBootWait string `mapstructure:"boot_wait"`
|
||||
|
||||
BootWait time.Duration ``
|
||||
}
|
||||
|
||||
func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
if c.RawBootWait == "" {
|
||||
c.RawBootWait = "10s"
|
||||
}
|
||||
|
||||
var errs []error
|
||||
var err error
|
||||
|
||||
if c.RawBootWait != "" {
|
||||
c.BootWait, err = time.ParseDuration(c.RawBootWait)
|
||||
if err != nil {
|
||||
errs = append(
|
||||
errs, fmt.Errorf("Failed parsing boot_wait: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRunConfigPrepare_BootWait(t *testing.T) {
|
||||
var c *RunConfig
|
||||
var errs []error
|
||||
|
||||
// Test a default boot_wait
|
||||
c = new(RunConfig)
|
||||
errs = c.Prepare(testConfigTemplate(t))
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("should not have error: %s", errs)
|
||||
}
|
||||
|
||||
if c.RawBootWait != "10s" {
|
||||
t.Fatalf("bad value: %s", c.RawBootWait)
|
||||
}
|
||||
|
||||
// Test with a bad boot_wait
|
||||
c = new(RunConfig)
|
||||
c.RawBootWait = "this is not good"
|
||||
errs = c.Prepare(testConfigTemplate(t))
|
||||
if len(errs) == 0 {
|
||||
t.Fatalf("bad: %#v", errs)
|
||||
}
|
||||
|
||||
// Test with a good one
|
||||
c = new(RunConfig)
|
||||
c.RawBootWait = "5s"
|
||||
errs = c.Prepare(testConfigTemplate(t))
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("should not have error: %s", errs)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,15 +3,12 @@ package common
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type StepRun struct {
|
||||
BootWait time.Duration
|
||||
|
||||
vmName string
|
||||
}
|
||||
|
||||
|
|
@ -32,22 +29,6 @@ func (s *StepRun) Run(_ context.Context, state multistep.StateBag) multistep.Ste
|
|||
|
||||
s.vmName = vmName
|
||||
|
||||
if int64(s.BootWait) > 0 {
|
||||
ui.Say(fmt.Sprintf("Waiting %s for boot...", s.BootWait))
|
||||
wait := time.After(s.BootWait)
|
||||
WAITLOOP:
|
||||
for {
|
||||
select {
|
||||
case <-wait:
|
||||
break WAITLOOP
|
||||
case <-time.After(1 * time.Second):
|
||||
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,11 @@ package common
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
|
|
@ -22,17 +21,29 @@ type bootCommandTemplateData struct {
|
|||
|
||||
// This step "types" the boot command into the VM via the Hyper-V virtual keyboard
|
||||
type StepTypeBootCommand struct {
|
||||
BootCommand []string
|
||||
BootCommand string
|
||||
BootWait time.Duration
|
||||
SwitchName string
|
||||
Ctx interpolate.Context
|
||||
}
|
||||
|
||||
func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
httpPort := state.Get("http_port").(uint)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
driver := state.Get("driver").(Driver)
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
// Wait the for the vm to boot.
|
||||
if int64(s.BootWait) > 0 {
|
||||
ui.Say(fmt.Sprintf("Waiting %s for boot...", s.BootWait.String()))
|
||||
select {
|
||||
case <-time.After(s.BootWait):
|
||||
break
|
||||
case <-ctx.Done():
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
hostIp, err := driver.GetHostAdapterIpAddressForSwitch(s.SwitchName)
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -51,26 +62,32 @@ func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m
|
|||
vmName,
|
||||
}
|
||||
|
||||
sendCodes := func(codes []string) error {
|
||||
scanCodesToSendString := strings.Join(codes, " ")
|
||||
return driver.TypeScanCodes(vmName, scanCodesToSendString)
|
||||
}
|
||||
d := bootcommand.NewPCXTDriver(sendCodes, -1)
|
||||
|
||||
ui.Say("Typing the boot command...")
|
||||
scanCodesToSend := []string{}
|
||||
command, err := interpolate.Render(s.BootCommand, &s.Ctx)
|
||||
|
||||
for _, command := range s.BootCommand {
|
||||
command, err := interpolate.Render(command, &s.Ctx)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error preparing boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
scanCodesToSend = append(scanCodesToSend, scancodes(command)...)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error preparing boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
scanCodesToSendString := strings.Join(scanCodesToSend, " ")
|
||||
seq, err := bootcommand.GenerateExpressionSequence(command)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error generating boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if err := driver.TypeScanCodes(vmName, scanCodesToSendString); err != nil {
|
||||
err := fmt.Errorf("Error sending boot command: %s", err)
|
||||
if err := seq.Do(ctx, d); err != nil {
|
||||
err := fmt.Errorf("Error running boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
|
|
@ -80,202 +97,3 @@ func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m
|
|||
}
|
||||
|
||||
func (*StepTypeBootCommand) Cleanup(multistep.StateBag) {}
|
||||
|
||||
func scancodes(message string) []string {
|
||||
// Scancodes reference: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html
|
||||
//
|
||||
// Scancodes represent raw keyboard output and are fed to the VM by using
|
||||
// powershell to use Msvm_Keyboard
|
||||
//
|
||||
// Scancodes are recorded here in pairs. The first entry represents
|
||||
// the key press and the second entry represents the key release and is
|
||||
// derived from the first by the addition of 0x80.
|
||||
special := make(map[string][]string)
|
||||
special["<bs>"] = []string{"0e", "8e"}
|
||||
special["<del>"] = []string{"53", "d3"}
|
||||
special["<enter>"] = []string{"1c", "9c"}
|
||||
special["<esc>"] = []string{"01", "81"}
|
||||
special["<f1>"] = []string{"3b", "bb"}
|
||||
special["<f2>"] = []string{"3c", "bc"}
|
||||
special["<f3>"] = []string{"3d", "bd"}
|
||||
special["<f4>"] = []string{"3e", "be"}
|
||||
special["<f5>"] = []string{"3f", "bf"}
|
||||
special["<f6>"] = []string{"40", "c0"}
|
||||
special["<f7>"] = []string{"41", "c1"}
|
||||
special["<f8>"] = []string{"42", "c2"}
|
||||
special["<f9>"] = []string{"43", "c3"}
|
||||
special["<f10>"] = []string{"44", "c4"}
|
||||
special["<return>"] = []string{"1c", "9c"}
|
||||
special["<tab>"] = []string{"0f", "8f"}
|
||||
special["<up>"] = []string{"48", "c8"}
|
||||
special["<down>"] = []string{"50", "d0"}
|
||||
special["<left>"] = []string{"4b", "cb"}
|
||||
special["<right>"] = []string{"4d", "cd"}
|
||||
special["<spacebar>"] = []string{"39", "b9"}
|
||||
special["<insert>"] = []string{"52", "d2"}
|
||||
special["<home>"] = []string{"47", "c7"}
|
||||
special["<end>"] = []string{"4f", "cf"}
|
||||
special["<pageUp>"] = []string{"49", "c9"}
|
||||
special["<pageDown>"] = []string{"51", "d1"}
|
||||
special["<leftAlt>"] = []string{"38", "b8"}
|
||||
special["<leftCtrl>"] = []string{"1d", "9d"}
|
||||
special["<leftShift>"] = []string{"2a", "aa"}
|
||||
special["<rightAlt>"] = []string{"e038", "e0b8"}
|
||||
special["<rightCtrl>"] = []string{"e01d", "e09d"}
|
||||
special["<rightShift>"] = []string{"36", "b6"}
|
||||
|
||||
shiftedChars := "~!@#$%^&*()_+{}|:\"<>?"
|
||||
|
||||
scancodeIndex := make(map[string]uint)
|
||||
scancodeIndex["1234567890-="] = 0x02
|
||||
scancodeIndex["!@#$%^&*()_+"] = 0x02
|
||||
scancodeIndex["qwertyuiop[]"] = 0x10
|
||||
scancodeIndex["QWERTYUIOP{}"] = 0x10
|
||||
scancodeIndex["asdfghjkl;'`"] = 0x1e
|
||||
scancodeIndex[`ASDFGHJKL:"~`] = 0x1e
|
||||
scancodeIndex[`\zxcvbnm,./`] = 0x2b
|
||||
scancodeIndex["|ZXCVBNM<>?"] = 0x2b
|
||||
scancodeIndex[" "] = 0x39
|
||||
|
||||
scancodeMap := make(map[rune]uint)
|
||||
for chars, start := range scancodeIndex {
|
||||
var i uint = 0
|
||||
for len(chars) > 0 {
|
||||
r, size := utf8.DecodeRuneInString(chars)
|
||||
chars = chars[size:]
|
||||
scancodeMap[r] = start + i
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]string, 0, len(message)*2)
|
||||
for len(message) > 0 {
|
||||
var scancode []string
|
||||
|
||||
if strings.HasPrefix(message, "<leftAltOn>") {
|
||||
scancode = []string{"38"}
|
||||
message = message[len("<leftAltOn>"):]
|
||||
log.Printf("Special code '<leftAltOn>' found, replacing with: 38")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftCtrlOn>") {
|
||||
scancode = []string{"1d"}
|
||||
message = message[len("<leftCtrlOn>"):]
|
||||
log.Printf("Special code '<leftCtrlOn>' found, replacing with: 1d")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftShiftOn>") {
|
||||
scancode = []string{"2a"}
|
||||
message = message[len("<leftShiftOn>"):]
|
||||
log.Printf("Special code '<leftShiftOn>' found, replacing with: 2a")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftAltOff>") {
|
||||
scancode = []string{"b8"}
|
||||
message = message[len("<leftAltOff>"):]
|
||||
log.Printf("Special code '<leftAltOff>' found, replacing with: b8")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftCtrlOff>") {
|
||||
scancode = []string{"9d"}
|
||||
message = message[len("<leftCtrlOff>"):]
|
||||
log.Printf("Special code '<leftCtrlOff>' found, replacing with: 9d")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftShiftOff>") {
|
||||
scancode = []string{"aa"}
|
||||
message = message[len("<leftShiftOff>"):]
|
||||
log.Printf("Special code '<leftShiftOff>' found, replacing with: aa")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightAltOn>") {
|
||||
scancode = []string{"e038"}
|
||||
message = message[len("<rightAltOn>"):]
|
||||
log.Printf("Special code '<rightAltOn>' found, replacing with: e038")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightCtrlOn>") {
|
||||
scancode = []string{"e01d"}
|
||||
message = message[len("<rightCtrlOn>"):]
|
||||
log.Printf("Special code '<rightCtrlOn>' found, replacing with: e01d")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightShiftOn>") {
|
||||
scancode = []string{"36"}
|
||||
message = message[len("<rightShiftOn>"):]
|
||||
log.Printf("Special code '<rightShiftOn>' found, replacing with: 36")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightAltOff>") {
|
||||
scancode = []string{"e0b8"}
|
||||
message = message[len("<rightAltOff>"):]
|
||||
log.Printf("Special code '<rightAltOff>' found, replacing with: e0b8")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightCtrlOff>") {
|
||||
scancode = []string{"e09d"}
|
||||
message = message[len("<rightCtrlOff>"):]
|
||||
log.Printf("Special code '<rightCtrlOff>' found, replacing with: e09d")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightShiftOff>") {
|
||||
scancode = []string{"b6"}
|
||||
message = message[len("<rightShiftOff>"):]
|
||||
log.Printf("Special code '<rightShiftOff>' found, replacing with: b6")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<wait>") {
|
||||
log.Printf("Special code <wait> found, will sleep 1 second at this point.")
|
||||
scancode = []string{"wait"}
|
||||
message = message[len("<wait>"):]
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<wait5>") {
|
||||
log.Printf("Special code <wait5> found, will sleep 5 seconds at this point.")
|
||||
scancode = []string{"wait5"}
|
||||
message = message[len("<wait5>"):]
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<wait10>") {
|
||||
log.Printf("Special code <wait10> found, will sleep 10 seconds at this point.")
|
||||
scancode = []string{"wait10"}
|
||||
message = message[len("<wait10>"):]
|
||||
}
|
||||
|
||||
if scancode == nil {
|
||||
for specialCode, specialValue := range special {
|
||||
if strings.HasPrefix(message, specialCode) {
|
||||
log.Printf("Special code '%s' found, replacing with: %s", specialCode, specialValue)
|
||||
scancode = specialValue
|
||||
message = message[len(specialCode):]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if scancode == nil {
|
||||
r, size := utf8.DecodeRuneInString(message)
|
||||
message = message[size:]
|
||||
scancodeInt := scancodeMap[r]
|
||||
keyShift := unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
|
||||
|
||||
scancode = make([]string, 0, 4)
|
||||
if keyShift {
|
||||
scancode = append(scancode, "2a")
|
||||
}
|
||||
|
||||
scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt))
|
||||
|
||||
if keyShift {
|
||||
scancode = append(scancode, "aa")
|
||||
}
|
||||
|
||||
scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt+0x80))
|
||||
log.Printf("Sending char '%c', code '%v', shift %v", r, scancode, keyShift)
|
||||
}
|
||||
|
||||
result = append(result, scancode...)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
hypervcommon "github.com/hashicorp/packer/builder/hyperv/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
powershell "github.com/hashicorp/packer/common/powershell"
|
||||
"github.com/hashicorp/packer/common/powershell/hyperv"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
|
|
@ -51,9 +52,9 @@ type Config struct {
|
|||
common.HTTPConfig `mapstructure:",squash"`
|
||||
common.ISOConfig `mapstructure:",squash"`
|
||||
common.FloppyConfig `mapstructure:",squash"`
|
||||
bootcommand.BootConfig `mapstructure:",squash"`
|
||||
hypervcommon.OutputConfig `mapstructure:",squash"`
|
||||
hypervcommon.SSHConfig `mapstructure:",squash"`
|
||||
hypervcommon.RunConfig `mapstructure:",squash"`
|
||||
hypervcommon.ShutdownConfig `mapstructure:",squash"`
|
||||
|
||||
// The size, in megabytes, of the hard disk to create for the VM.
|
||||
|
|
@ -81,18 +82,17 @@ type Config struct {
|
|||
// By default this is "packer-BUILDNAME", where "BUILDNAME" is the name of the build.
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
|
||||
BootCommand []string `mapstructure:"boot_command"`
|
||||
SwitchName string `mapstructure:"switch_name"`
|
||||
SwitchVlanId string `mapstructure:"switch_vlan_id"`
|
||||
MacAddress string `mapstructure:"mac_address"`
|
||||
VlanId string `mapstructure:"vlan_id"`
|
||||
Cpu uint `mapstructure:"cpu"`
|
||||
Generation uint `mapstructure:"generation"`
|
||||
EnableMacSpoofing bool `mapstructure:"enable_mac_spoofing"`
|
||||
EnableDynamicMemory bool `mapstructure:"enable_dynamic_memory"`
|
||||
EnableSecureBoot bool `mapstructure:"enable_secure_boot"`
|
||||
EnableVirtualizationExtensions bool `mapstructure:"enable_virtualization_extensions"`
|
||||
TempPath string `mapstructure:"temp_path"`
|
||||
SwitchName string `mapstructure:"switch_name"`
|
||||
SwitchVlanId string `mapstructure:"switch_vlan_id"`
|
||||
MacAddress string `mapstructure:"mac_address"`
|
||||
VlanId string `mapstructure:"vlan_id"`
|
||||
Cpu uint `mapstructure:"cpu"`
|
||||
Generation uint `mapstructure:"generation"`
|
||||
EnableMacSpoofing bool `mapstructure:"enable_mac_spoofing"`
|
||||
EnableDynamicMemory bool `mapstructure:"enable_dynamic_memory"`
|
||||
EnableSecureBoot bool `mapstructure:"enable_secure_boot"`
|
||||
EnableVirtualizationExtensions bool `mapstructure:"enable_virtualization_extensions"`
|
||||
TempPath string `mapstructure:"temp_path"`
|
||||
|
||||
// A separate path can be used for storing the VM's disk image. The purpose is to enable
|
||||
// reading and writing to take place on different physical disks (read from VHD temp path
|
||||
|
|
@ -136,9 +136,9 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
warnings = append(warnings, isoWarnings...)
|
||||
errs = packer.MultiErrorAppend(errs, isoErrs...)
|
||||
|
||||
errs = packer.MultiErrorAppend(errs, b.config.BootConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.HTTPConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(&b.config.ctx)...)
|
||||
|
|
@ -404,12 +404,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
SwitchVlanId: b.config.SwitchVlanId,
|
||||
},
|
||||
|
||||
&hypervcommon.StepRun{
|
||||
BootWait: b.config.BootWait,
|
||||
},
|
||||
&hypervcommon.StepRun{},
|
||||
|
||||
&hypervcommon.StepTypeBootCommand{
|
||||
BootCommand: b.config.BootCommand,
|
||||
BootCommand: b.config.FlatBootCommand(),
|
||||
BootWait: b.config.BootWait,
|
||||
SwitchName: b.config.SwitchName,
|
||||
Ctx: b.config.ctx,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -562,7 +562,7 @@ func TestUserVariablesInBootCommand(t *testing.T) {
|
|||
state.Put("vmName", "packer-foo")
|
||||
|
||||
step := &hypervcommon.StepTypeBootCommand{
|
||||
BootCommand: b.config.BootCommand,
|
||||
BootCommand: b.config.FlatBootCommand(),
|
||||
SwitchName: b.config.SwitchName,
|
||||
Ctx: b.config.ctx,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
hypervcommon "github.com/hashicorp/packer/builder/hyperv/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
powershell "github.com/hashicorp/packer/common/powershell"
|
||||
"github.com/hashicorp/packer/common/powershell/hyperv"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
|
|
@ -42,9 +43,9 @@ type Config struct {
|
|||
common.HTTPConfig `mapstructure:",squash"`
|
||||
common.ISOConfig `mapstructure:",squash"`
|
||||
common.FloppyConfig `mapstructure:",squash"`
|
||||
bootcommand.BootConfig `mapstructure:",squash"`
|
||||
hypervcommon.OutputConfig `mapstructure:",squash"`
|
||||
hypervcommon.SSHConfig `mapstructure:",squash"`
|
||||
hypervcommon.RunConfig `mapstructure:",squash"`
|
||||
hypervcommon.ShutdownConfig `mapstructure:",squash"`
|
||||
|
||||
// The size, in megabytes, of the computer memory in the VM.
|
||||
|
|
@ -79,12 +80,11 @@ type Config struct {
|
|||
// Use differencing disk
|
||||
DifferencingDisk bool `mapstructure:"differencing_disk"`
|
||||
|
||||
BootCommand []string `mapstructure:"boot_command"`
|
||||
SwitchName string `mapstructure:"switch_name"`
|
||||
SwitchVlanId string `mapstructure:"switch_vlan_id"`
|
||||
MacAddress string `mapstructure:"mac_address"`
|
||||
VlanId string `mapstructure:"vlan_id"`
|
||||
Cpu uint `mapstructure:"cpu"`
|
||||
SwitchName string `mapstructure:"switch_name"`
|
||||
SwitchVlanId string `mapstructure:"switch_vlan_id"`
|
||||
MacAddress string `mapstructure:"mac_address"`
|
||||
VlanId string `mapstructure:"vlan_id"`
|
||||
Cpu uint `mapstructure:"cpu"`
|
||||
Generation uint
|
||||
EnableMacSpoofing bool `mapstructure:"enable_mac_spoofing"`
|
||||
EnableDynamicMemory bool `mapstructure:"enable_dynamic_memory"`
|
||||
|
|
@ -125,9 +125,9 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
errs = packer.MultiErrorAppend(errs, isoErrs...)
|
||||
}
|
||||
|
||||
errs = packer.MultiErrorAppend(errs, b.config.BootConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.HTTPConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(&b.config.ctx)...)
|
||||
|
|
@ -434,12 +434,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
SwitchVlanId: b.config.SwitchVlanId,
|
||||
},
|
||||
|
||||
&hypervcommon.StepRun{
|
||||
BootWait: b.config.BootWait,
|
||||
},
|
||||
&hypervcommon.StepRun{},
|
||||
|
||||
&hypervcommon.StepTypeBootCommand{
|
||||
BootCommand: b.config.BootCommand,
|
||||
BootCommand: b.config.FlatBootCommand(),
|
||||
BootWait: b.config.BootWait,
|
||||
SwitchName: b.config.SwitchName,
|
||||
Ctx: b.config.ctx,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -530,7 +530,7 @@ func TestUserVariablesInBootCommand(t *testing.T) {
|
|||
state.Put("vmName", "packer-foo")
|
||||
|
||||
step := &hypervcommon.StepTypeBootCommand{
|
||||
BootCommand: b.config.BootCommand,
|
||||
BootCommand: b.config.FlatBootCommand(),
|
||||
SwitchName: b.config.SwitchName,
|
||||
Ctx: b.config.ctx,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
)
|
||||
|
||||
// RunConfig contains the configuration for VM run.
|
||||
type RunConfig struct {
|
||||
RawBootWait string `mapstructure:"boot_wait"`
|
||||
|
||||
BootWait time.Duration ``
|
||||
}
|
||||
|
||||
// Prepare sets the configuration for VM run.
|
||||
func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
if c.RawBootWait == "" {
|
||||
c.RawBootWait = "10s"
|
||||
}
|
||||
|
||||
var err error
|
||||
c.BootWait, err = time.ParseDuration(c.RawBootWait)
|
||||
if err != nil {
|
||||
return []error{fmt.Errorf("Failed parsing boot_wait: %s", err)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRunConfigPrepare_BootWait(t *testing.T) {
|
||||
var c *RunConfig
|
||||
var errs []error
|
||||
|
||||
// Test a default boot_wait
|
||||
c = new(RunConfig)
|
||||
errs = c.Prepare(testConfigTemplate(t))
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("should not have error: %s", errs)
|
||||
}
|
||||
|
||||
if c.RawBootWait != "10s" {
|
||||
t.Fatalf("bad value: %s", c.RawBootWait)
|
||||
}
|
||||
|
||||
// Test with a bad boot_wait
|
||||
c = new(RunConfig)
|
||||
c.RawBootWait = "this is not good"
|
||||
errs = c.Prepare(testConfigTemplate(t))
|
||||
if len(errs) == 0 {
|
||||
t.Fatalf("bad: %#v", errs)
|
||||
}
|
||||
|
||||
// Test with a good one
|
||||
c = new(RunConfig)
|
||||
c.RawBootWait = "5s"
|
||||
errs = c.Prepare(testConfigTemplate(t))
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("should not have error: %s", errs)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@ package common
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
|
@ -18,8 +17,6 @@ import (
|
|||
//
|
||||
// Produces:
|
||||
type StepRun struct {
|
||||
BootWait time.Duration
|
||||
|
||||
vmName string
|
||||
}
|
||||
|
||||
|
|
@ -40,22 +37,6 @@ func (s *StepRun) Run(_ context.Context, state multistep.StateBag) multistep.Ste
|
|||
|
||||
s.vmName = vmName
|
||||
|
||||
if int64(s.BootWait) > 0 {
|
||||
ui.Say(fmt.Sprintf("Waiting %s for boot...", s.BootWait))
|
||||
wait := time.After(s.BootWait)
|
||||
WAITLOOP:
|
||||
for {
|
||||
select {
|
||||
case <-wait:
|
||||
break WAITLOOP
|
||||
case <-time.After(1 * time.Second):
|
||||
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,13 +3,10 @@ package common
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
packer_common "github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
|
|
@ -23,29 +20,32 @@ type bootCommandTemplateData struct {
|
|||
|
||||
// StepTypeBootCommand is a step that "types" the boot command into the VM via
|
||||
// the prltype script, built on the Parallels Virtualization SDK - Python API.
|
||||
//
|
||||
// Uses:
|
||||
// driver Driver
|
||||
// http_port int
|
||||
// ui packer.Ui
|
||||
// vmName string
|
||||
//
|
||||
// Produces:
|
||||
// <nothing>
|
||||
type StepTypeBootCommand struct {
|
||||
BootCommand []string
|
||||
BootCommand string
|
||||
BootWait time.Duration
|
||||
HostInterfaces []string
|
||||
VMName string
|
||||
Ctx interpolate.Context
|
||||
}
|
||||
|
||||
// Run types the boot command by sending key scancodes into the VM.
|
||||
func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
debug := state.Get("debug").(bool)
|
||||
httpPort := state.Get("http_port").(uint)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
driver := state.Get("driver").(Driver)
|
||||
|
||||
// Wait the for the vm to boot.
|
||||
if int64(s.BootWait) > 0 {
|
||||
ui.Say(fmt.Sprintf("Waiting %s for boot...", s.BootWait.String()))
|
||||
select {
|
||||
case <-time.After(s.BootWait):
|
||||
break
|
||||
case <-ctx.Done():
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
var pauseFn multistep.DebugPauseFn
|
||||
if debug {
|
||||
pauseFn = state.Get("pauseFn").(multistep.DebugPauseFn)
|
||||
|
|
@ -76,73 +76,37 @@ func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m
|
|||
s.VMName,
|
||||
}
|
||||
|
||||
sendCodes := func(codes []string) error {
|
||||
return driver.SendKeyScanCodes(s.VMName, codes...)
|
||||
}
|
||||
d := bootcommand.NewPCXTDriver(sendCodes, -1)
|
||||
|
||||
ui.Say("Typing the boot command...")
|
||||
for i, command := range s.BootCommand {
|
||||
command, err := interpolate.Render(command, &s.Ctx)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error preparing boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
command, err := interpolate.Render(s.BootCommand, &s.Ctx)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error preparing boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
codes := []string{}
|
||||
for _, code := range scancodes(command) {
|
||||
if code == "wait" {
|
||||
if err := driver.SendKeyScanCodes(s.VMName, codes...); err != nil {
|
||||
err = fmt.Errorf("Error sending boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
codes = []string{}
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
seq, err := bootcommand.GenerateExpressionSequence(command)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error generating boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if code == "wait5" {
|
||||
if err := driver.SendKeyScanCodes(s.VMName, codes...); err != nil {
|
||||
err = fmt.Errorf("Error sending boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
codes = []string{}
|
||||
time.Sleep(5 * time.Second)
|
||||
continue
|
||||
}
|
||||
if err := seq.Do(ctx, d); err != nil {
|
||||
err := fmt.Errorf("Error running boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if code == "wait10" {
|
||||
if err := driver.SendKeyScanCodes(s.VMName, codes...); err != nil {
|
||||
err = fmt.Errorf("Error sending boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
codes = []string{}
|
||||
time.Sleep(10 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
// Since typing is sometimes so slow, we check for an interrupt
|
||||
// in between each character.
|
||||
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
codes = append(codes, code)
|
||||
}
|
||||
|
||||
if pauseFn != nil {
|
||||
pauseFn(multistep.DebugLocationAfterRun, fmt.Sprintf("boot_command[%d]: %s", i, command), state)
|
||||
}
|
||||
|
||||
log.Printf("Sending scancodes: %#v", codes)
|
||||
if err := driver.SendKeyScanCodes(s.VMName, codes...); err != nil {
|
||||
err = fmt.Errorf("Error sending boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
if pauseFn != nil {
|
||||
pauseFn(multistep.DebugLocationAfterRun, fmt.Sprintf("boot_command: %s", command), state)
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
|
|
@ -150,202 +114,3 @@ func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m
|
|||
|
||||
// Cleanup does nothing.
|
||||
func (*StepTypeBootCommand) Cleanup(multistep.StateBag) {}
|
||||
|
||||
func scancodes(message string) []string {
|
||||
// Scancodes reference: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html
|
||||
//
|
||||
// Scancodes represent raw keyboard output and are fed to the VM by the
|
||||
// Parallels Virtualization SDK - C API, PrlDevKeyboard_SendKeyEvent
|
||||
//
|
||||
// Scancodes are recorded here in pairs. The first entry represents
|
||||
// the key press and the second entry represents the key release and is
|
||||
// derived from the first by the addition of 0x80.
|
||||
special := make(map[string][]string)
|
||||
special["<bs>"] = []string{"0e", "8e"}
|
||||
special["<del>"] = []string{"53", "d3"}
|
||||
special["<enter>"] = []string{"1c", "9c"}
|
||||
special["<esc>"] = []string{"01", "81"}
|
||||
special["<f1>"] = []string{"3b", "bb"}
|
||||
special["<f2>"] = []string{"3c", "bc"}
|
||||
special["<f3>"] = []string{"3d", "bd"}
|
||||
special["<f4>"] = []string{"3e", "be"}
|
||||
special["<f5>"] = []string{"3f", "bf"}
|
||||
special["<f6>"] = []string{"40", "c0"}
|
||||
special["<f7>"] = []string{"41", "c1"}
|
||||
special["<f8>"] = []string{"42", "c2"}
|
||||
special["<f9>"] = []string{"43", "c3"}
|
||||
special["<f10>"] = []string{"44", "c4"}
|
||||
special["<return>"] = []string{"1c", "9c"}
|
||||
special["<tab>"] = []string{"0f", "8f"}
|
||||
|
||||
special["<up>"] = []string{"48", "c8"}
|
||||
special["<down>"] = []string{"50", "d0"}
|
||||
special["<left>"] = []string{"4b", "cb"}
|
||||
special["<right>"] = []string{"4d", "cd"}
|
||||
special["<spacebar>"] = []string{"39", "b9"}
|
||||
special["<insert>"] = []string{"52", "d2"}
|
||||
special["<home>"] = []string{"47", "c7"}
|
||||
special["<end>"] = []string{"4f", "cf"}
|
||||
special["<pageUp>"] = []string{"49", "c9"}
|
||||
special["<pageDown>"] = []string{"51", "d1"}
|
||||
|
||||
special["<leftAlt>"] = []string{"38", "b8"}
|
||||
special["<leftCtrl>"] = []string{"1d", "9d"}
|
||||
special["<leftShift>"] = []string{"2a", "aa"}
|
||||
special["<rightAlt>"] = []string{"e038", "e0b8"}
|
||||
special["<rightCtrl>"] = []string{"e01d", "e09d"}
|
||||
special["<rightShift>"] = []string{"36", "b6"}
|
||||
|
||||
shiftedChars := "!@#$%^&*()_+{}:\"~|<>?"
|
||||
|
||||
scancodeIndex := make(map[string]uint)
|
||||
scancodeIndex["1234567890-="] = 0x02
|
||||
scancodeIndex["!@#$%^&*()_+"] = 0x02
|
||||
scancodeIndex["qwertyuiop[]"] = 0x10
|
||||
scancodeIndex["QWERTYUIOP{}"] = 0x10
|
||||
scancodeIndex["asdfghjkl;'`"] = 0x1e
|
||||
scancodeIndex[`ASDFGHJKL:"~`] = 0x1e
|
||||
scancodeIndex["\\zxcvbnm,./"] = 0x2b
|
||||
scancodeIndex["|ZXCVBNM<>?"] = 0x2b
|
||||
scancodeIndex[" "] = 0x39
|
||||
|
||||
scancodeMap := make(map[rune]uint)
|
||||
for chars, start := range scancodeIndex {
|
||||
var i uint
|
||||
for len(chars) > 0 {
|
||||
r, size := utf8.DecodeRuneInString(chars)
|
||||
chars = chars[size:]
|
||||
scancodeMap[r] = start + i
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]string, 0, len(message)*2)
|
||||
for len(message) > 0 {
|
||||
var scancode []string
|
||||
|
||||
if strings.HasPrefix(message, "<leftAltOn>") {
|
||||
scancode = []string{"38"}
|
||||
message = message[len("<leftAltOn>"):]
|
||||
log.Printf("Special code '<leftAltOn>' found, replacing with: 38")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftCtrlOn>") {
|
||||
scancode = []string{"1d"}
|
||||
message = message[len("<leftCtrlOn>"):]
|
||||
log.Printf("Special code '<leftCtrlOn>' found, replacing with: 1d")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftShiftOn>") {
|
||||
scancode = []string{"2a"}
|
||||
message = message[len("<leftShiftOn>"):]
|
||||
log.Printf("Special code '<leftShiftOn>' found, replacing with: 2a")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftAltOff>") {
|
||||
scancode = []string{"b8"}
|
||||
message = message[len("<leftAltOff>"):]
|
||||
log.Printf("Special code '<leftAltOff>' found, replacing with: b8")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftCtrlOff>") {
|
||||
scancode = []string{"9d"}
|
||||
message = message[len("<leftCtrlOff>"):]
|
||||
log.Printf("Special code '<leftCtrlOff>' found, replacing with: 9d")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftShiftOff>") {
|
||||
scancode = []string{"aa"}
|
||||
message = message[len("<leftShiftOff>"):]
|
||||
log.Printf("Special code '<leftShiftOff>' found, replacing with: aa")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightAltOn>") {
|
||||
scancode = []string{"e038"}
|
||||
message = message[len("<rightAltOn>"):]
|
||||
log.Printf("Special code '<rightAltOn>' found, replacing with: e038")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightCtrlOn>") {
|
||||
scancode = []string{"e01d"}
|
||||
message = message[len("<rightCtrlOn>"):]
|
||||
log.Printf("Special code '<rightCtrlOn>' found, replacing with: e01d")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightShiftOn>") {
|
||||
scancode = []string{"36"}
|
||||
message = message[len("<rightShiftOn>"):]
|
||||
log.Printf("Special code '<rightShiftOn>' found, replacing with: 36")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightAltOff>") {
|
||||
scancode = []string{"e0b8"}
|
||||
message = message[len("<rightAltOff>"):]
|
||||
log.Printf("Special code '<rightAltOff>' found, replacing with: e0b8")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightCtrlOff>") {
|
||||
scancode = []string{"e09d"}
|
||||
message = message[len("<rightCtrlOff>"):]
|
||||
log.Printf("Special code '<rightCtrlOff>' found, replacing with: e09d")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightShiftOff>") {
|
||||
scancode = []string{"b6"}
|
||||
message = message[len("<rightShiftOff>"):]
|
||||
log.Printf("Special code '<rightShiftOff>' found, replacing with: b6")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<wait>") {
|
||||
log.Printf("Special code <wait> found, will sleep 1 second at this point.")
|
||||
scancode = []string{"wait"}
|
||||
message = message[len("<wait>"):]
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<wait5>") {
|
||||
log.Printf("Special code <wait5> found, will sleep 5 seconds at this point.")
|
||||
scancode = []string{"wait5"}
|
||||
message = message[len("<wait5>"):]
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<wait10>") {
|
||||
log.Printf("Special code <wait10> found, will sleep 10 seconds at this point.")
|
||||
scancode = []string{"wait10"}
|
||||
message = message[len("<wait10>"):]
|
||||
}
|
||||
|
||||
if scancode == nil {
|
||||
for specialCode, specialValue := range special {
|
||||
if strings.HasPrefix(message, specialCode) {
|
||||
log.Printf("Special code '%s' found, replacing with: %s", specialCode, specialValue)
|
||||
scancode = specialValue
|
||||
message = message[len(specialCode):]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if scancode == nil {
|
||||
r, size := utf8.DecodeRuneInString(message)
|
||||
message = message[size:]
|
||||
scancodeInt := scancodeMap[r]
|
||||
keyShift := unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
|
||||
|
||||
scancode = make([]string, 0, 4)
|
||||
if keyShift {
|
||||
scancode = append(scancode, "2a")
|
||||
}
|
||||
|
||||
scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt))
|
||||
scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt+0x80))
|
||||
|
||||
if keyShift {
|
||||
scancode = append(scancode, "aa")
|
||||
}
|
||||
}
|
||||
|
||||
result = append(result, scancode...)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
func TestStepTypeBootCommand(t *testing.T) {
|
||||
state := testState(t)
|
||||
|
||||
var bootcommand = []string{
|
||||
"1234567890-=<enter><wait>",
|
||||
"!@#$%^&*()_+<enter>",
|
||||
"qwertyuiop[]<enter>",
|
||||
"QWERTYUIOP{}<enter>",
|
||||
"asdfghjkl;'`<enter>",
|
||||
`ASDFGHJKL:"~<enter>`,
|
||||
"\\zxcvbnm,./<enter>",
|
||||
"|ZXCVBNM<>?<enter>",
|
||||
" <enter>",
|
||||
}
|
||||
|
||||
step := StepTypeBootCommand{
|
||||
BootCommand: bootcommand,
|
||||
HostInterfaces: []string{},
|
||||
VMName: "myVM",
|
||||
Ctx: *testConfigTemplate(t),
|
||||
}
|
||||
|
||||
comm := new(packer.MockCommunicator)
|
||||
state.Put("communicator", comm)
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
driver.VersionResult = "foo"
|
||||
state.Put("http_port", uint(0))
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("should NOT have error")
|
||||
}
|
||||
|
||||
// Verify
|
||||
var expected = [][]string{
|
||||
{"02", "82", "03", "83", "04", "84", "05", "85", "06", "86", "07", "87", "08", "88", "09", "89", "0a", "8a", "0b", "8b", "0c", "8c", "0d", "8d", "1c", "9c"},
|
||||
{},
|
||||
{"2a", "02", "82", "aa", "2a", "03", "83", "aa", "2a", "04", "84", "aa", "2a", "05", "85", "aa", "2a", "06", "86", "aa", "2a", "07", "87", "aa", "2a", "08", "88", "aa", "2a", "09", "89", "aa", "2a", "0a", "8a", "aa", "2a", "0b", "8b", "aa", "2a", "0c", "8c", "aa", "2a", "0d", "8d", "aa", "1c", "9c"},
|
||||
{"10", "90", "11", "91", "12", "92", "13", "93", "14", "94", "15", "95", "16", "96", "17", "97", "18", "98", "19", "99", "1a", "9a", "1b", "9b", "1c", "9c"},
|
||||
{"2a", "10", "90", "aa", "2a", "11", "91", "aa", "2a", "12", "92", "aa", "2a", "13", "93", "aa", "2a", "14", "94", "aa", "2a", "15", "95", "aa", "2a", "16", "96", "aa", "2a", "17", "97", "aa", "2a", "18", "98", "aa", "2a", "19", "99", "aa", "2a", "1a", "9a", "aa", "2a", "1b", "9b", "aa", "1c", "9c"},
|
||||
{"1e", "9e", "1f", "9f", "20", "a0", "21", "a1", "22", "a2", "23", "a3", "24", "a4", "25", "a5", "26", "a6", "27", "a7", "28", "a8", "29", "a9", "1c", "9c"},
|
||||
{"2a", "1e", "9e", "aa", "2a", "1f", "9f", "aa", "2a", "20", "a0", "aa", "2a", "21", "a1", "aa", "2a", "22", "a2", "aa", "2a", "23", "a3", "aa", "2a", "24", "a4", "aa", "2a", "25", "a5", "aa", "2a", "26", "a6", "aa", "2a", "27", "a7", "aa", "2a", "28", "a8", "aa", "2a", "29", "a9", "aa", "1c", "9c"},
|
||||
{"2b", "ab", "2c", "ac", "2d", "ad", "2e", "ae", "2f", "af", "30", "b0", "31", "b1", "32", "b2", "33", "b3", "34", "b4", "35", "b5", "1c", "9c"},
|
||||
{"2a", "2b", "ab", "aa", "2a", "2c", "ac", "aa", "2a", "2d", "ad", "aa", "2a", "2e", "ae", "aa", "2a", "2f", "af", "aa", "2a", "30", "b0", "aa", "2a", "31", "b1", "aa", "2a", "32", "b2", "aa", "2a", "33", "b3", "aa", "2a", "34", "b4", "aa", "2a", "35", "b5", "aa", "1c", "9c"},
|
||||
{"39", "b9", "1c", "9c"},
|
||||
}
|
||||
fail := false
|
||||
|
||||
for i := range driver.SendKeyScanCodesCalls {
|
||||
t.Logf("prltype %s\n", strings.Join(driver.SendKeyScanCodesCalls[i], " "))
|
||||
}
|
||||
|
||||
for i := range expected {
|
||||
for j := range expected[i] {
|
||||
if driver.SendKeyScanCodesCalls[i][j] != expected[i][j] {
|
||||
fail = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if fail {
|
||||
t.Fatalf("Sent bad scancodes: %#v\n Expected: %#v", driver.SendKeyScanCodesCalls, expected)
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
parallelscommon "github.com/hashicorp/packer/builder/parallels/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
|
|
@ -26,16 +27,15 @@ type Config struct {
|
|||
common.HTTPConfig `mapstructure:",squash"`
|
||||
common.ISOConfig `mapstructure:",squash"`
|
||||
common.FloppyConfig `mapstructure:",squash"`
|
||||
bootcommand.BootConfig `mapstructure:",squash"`
|
||||
parallelscommon.OutputConfig `mapstructure:",squash"`
|
||||
parallelscommon.PrlctlConfig `mapstructure:",squash"`
|
||||
parallelscommon.PrlctlPostConfig `mapstructure:",squash"`
|
||||
parallelscommon.PrlctlVersionConfig `mapstructure:",squash"`
|
||||
parallelscommon.RunConfig `mapstructure:",squash"`
|
||||
parallelscommon.ShutdownConfig `mapstructure:",squash"`
|
||||
parallelscommon.SSHConfig `mapstructure:",squash"`
|
||||
parallelscommon.ToolsConfig `mapstructure:",squash"`
|
||||
|
||||
BootCommand []string `mapstructure:"boot_command"`
|
||||
DiskSize uint `mapstructure:"disk_size"`
|
||||
DiskType string `mapstructure:"disk_type"`
|
||||
GuestOSType string `mapstructure:"guest_os_type"`
|
||||
|
|
@ -76,13 +76,13 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.PrlctlConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.PrlctlPostConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.PrlctlVersionConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.ToolsConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.BootConfig.Prepare(&b.config.ctx)...)
|
||||
|
||||
if b.config.DiskSize == 0 {
|
||||
b.config.DiskSize = 40000
|
||||
|
|
@ -185,11 +185,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
Commands: b.config.Prlctl,
|
||||
Ctx: b.config.ctx,
|
||||
},
|
||||
¶llelscommon.StepRun{
|
||||
BootWait: b.config.BootWait,
|
||||
},
|
||||
¶llelscommon.StepRun{},
|
||||
¶llelscommon.StepTypeBootCommand{
|
||||
BootCommand: b.config.BootCommand,
|
||||
BootWait: b.config.BootWait,
|
||||
BootCommand: b.config.FlatBootCommand(),
|
||||
HostInterfaces: b.config.HostInterfaces,
|
||||
VMName: b.config.VMName,
|
||||
Ctx: b.config.ctx,
|
||||
|
|
|
|||
|
|
@ -74,11 +74,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
Commands: b.config.Prlctl,
|
||||
Ctx: b.config.ctx,
|
||||
},
|
||||
¶llelscommon.StepRun{
|
||||
BootWait: b.config.BootWait,
|
||||
},
|
||||
¶llelscommon.StepRun{},
|
||||
¶llelscommon.StepTypeBootCommand{
|
||||
BootCommand: b.config.BootCommand,
|
||||
BootCommand: b.config.FlatBootCommand(),
|
||||
BootWait: b.config.BootWait,
|
||||
HostInterfaces: []string{},
|
||||
VMName: b.config.VMName,
|
||||
Ctx: b.config.ctx,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
parallelscommon "github.com/hashicorp/packer/builder/parallels/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
|
|
@ -19,15 +20,14 @@ type Config struct {
|
|||
parallelscommon.PrlctlConfig `mapstructure:",squash"`
|
||||
parallelscommon.PrlctlPostConfig `mapstructure:",squash"`
|
||||
parallelscommon.PrlctlVersionConfig `mapstructure:",squash"`
|
||||
parallelscommon.RunConfig `mapstructure:",squash"`
|
||||
parallelscommon.SSHConfig `mapstructure:",squash"`
|
||||
parallelscommon.ShutdownConfig `mapstructure:",squash"`
|
||||
bootcommand.BootConfig `mapstructure:",squash"`
|
||||
parallelscommon.ToolsConfig `mapstructure:",squash"`
|
||||
|
||||
BootCommand []string `mapstructure:"boot_command"`
|
||||
SourcePath string `mapstructure:"source_path"`
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
ReassignMAC bool `mapstructure:"reassign_mac"`
|
||||
SourcePath string `mapstructure:"source_path"`
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
ReassignMAC bool `mapstructure:"reassign_mac"`
|
||||
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
|
@ -61,7 +61,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
errs = packer.MultiErrorAppend(errs, c.PrlctlConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.PrlctlPostConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.PrlctlVersionConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.RunConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.BootConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.ShutdownConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.SSHConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.ToolsConfig.Prepare(&c.ctx)...)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
|
|
@ -79,15 +80,15 @@ type Builder struct {
|
|||
}
|
||||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
common.HTTPConfig `mapstructure:",squash"`
|
||||
common.ISOConfig `mapstructure:",squash"`
|
||||
Comm communicator.Config `mapstructure:",squash"`
|
||||
common.FloppyConfig `mapstructure:",squash"`
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
common.HTTPConfig `mapstructure:",squash"`
|
||||
common.ISOConfig `mapstructure:",squash"`
|
||||
bootcommand.VNCConfig `mapstructure:",squash"`
|
||||
Comm communicator.Config `mapstructure:",squash"`
|
||||
common.FloppyConfig `mapstructure:",squash"`
|
||||
|
||||
ISOSkipCache bool `mapstructure:"iso_skip_cache"`
|
||||
Accelerator string `mapstructure:"accelerator"`
|
||||
BootCommand []string `mapstructure:"boot_command"`
|
||||
DiskInterface string `mapstructure:"disk_interface"`
|
||||
DiskSize uint `mapstructure:"disk_size"`
|
||||
DiskCache string `mapstructure:"disk_cache"`
|
||||
|
|
@ -118,10 +119,8 @@ type Config struct {
|
|||
// TODO(mitchellh): deprecate
|
||||
RunOnce bool `mapstructure:"run_once"`
|
||||
|
||||
RawBootWait string `mapstructure:"boot_wait"`
|
||||
RawShutdownTimeout string `mapstructure:"shutdown_timeout"`
|
||||
|
||||
bootWait time.Duration ``
|
||||
shutdownTimeout time.Duration ``
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
|
@ -188,10 +187,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
b.config.QemuBinary = "qemu-system-x86_64"
|
||||
}
|
||||
|
||||
if b.config.RawBootWait == "" {
|
||||
b.config.RawBootWait = "10s"
|
||||
}
|
||||
|
||||
if b.config.SSHHostPortMin == 0 {
|
||||
b.config.SSHHostPortMin = 2222
|
||||
}
|
||||
|
|
@ -291,12 +286,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
b.config.bootWait, err = time.ParseDuration(b.config.RawBootWait)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("Failed parsing boot_wait: %s", err))
|
||||
}
|
||||
|
||||
if b.config.RawShutdownTimeout == "" {
|
||||
b.config.RawShutdownTimeout = "5m"
|
||||
}
|
||||
|
|
@ -388,7 +377,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
steps = append(steps,
|
||||
new(stepConfigureVNC),
|
||||
steprun,
|
||||
&stepBootWait{},
|
||||
&stepTypeBootCommand{},
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -94,46 +94,6 @@ func TestBuilderPrepare_Defaults(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_BootWait(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
// Test a default boot_wait
|
||||
delete(config, "boot_wait")
|
||||
warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if b.config.RawBootWait != "10s" {
|
||||
t.Fatalf("bad value: %s", b.config.RawBootWait)
|
||||
}
|
||||
|
||||
// Test with a bad boot_wait
|
||||
config["boot_wait"] = "this is not good"
|
||||
warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Test with a good one
|
||||
config["boot_wait"] = "5s"
|
||||
b = Builder{}
|
||||
warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_VNCBindAddress(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
package qemu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
// stepBootWait waits the configured time period.
|
||||
type stepBootWait struct{}
|
||||
|
||||
func (s *stepBootWait) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
if int64(config.bootWait) > 0 {
|
||||
ui.Say(fmt.Sprintf("Waiting %s for boot...", config.bootWait))
|
||||
time.Sleep(config.bootWait)
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepBootWait) Cleanup(state multistep.StateBag) {}
|
||||
|
|
@ -5,15 +5,10 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
|
|
@ -40,13 +35,29 @@ type bootCommandTemplateData struct {
|
|||
// <nothing>
|
||||
type stepTypeBootCommand struct{}
|
||||
|
||||
func (s *stepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
func (s *stepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
debug := state.Get("debug").(bool)
|
||||
httpPort := state.Get("http_port").(uint)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
vncPort := state.Get("vnc_port").(uint)
|
||||
|
||||
if config.VNCConfig.DisableVNC {
|
||||
log.Println("Skipping boot command step...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// Wait the for the vm to boot.
|
||||
if int64(config.BootConfig.BootWait) > 0 {
|
||||
ui.Say(fmt.Sprintf("Waiting %s for boot...", config.BootWait.String()))
|
||||
select {
|
||||
case <-time.After(config.BootWait):
|
||||
break
|
||||
case <-ctx.Done():
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
var pauseFn multistep.DebugPauseFn
|
||||
if debug {
|
||||
pauseFn = state.Get("pauseFn").(multistep.DebugPauseFn)
|
||||
|
|
@ -76,276 +87,44 @@ func (s *stepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m
|
|||
|
||||
hostIP := "10.0.2.2"
|
||||
common.SetHTTPIP(hostIP)
|
||||
ctx := config.ctx
|
||||
ctx.Data = &bootCommandTemplateData{
|
||||
configCtx := config.ctx
|
||||
configCtx.Data = &bootCommandTemplateData{
|
||||
hostIP,
|
||||
httpPort,
|
||||
config.VMName,
|
||||
}
|
||||
|
||||
d := bootcommand.NewVNCDriver(c)
|
||||
|
||||
ui.Say("Typing the boot command over VNC...")
|
||||
for i, command := range config.BootCommand {
|
||||
command, err := interpolate.Render(command, &ctx)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error preparing boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
command, err := interpolate.Render(config.VNCConfig.FlatBootCommand(), &configCtx)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error preparing boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Check for interrupts between typing things so we can cancel
|
||||
// since this isn't the fastest thing.
|
||||
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
seq, err := bootcommand.GenerateExpressionSequence(command)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error generating boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if pauseFn != nil {
|
||||
pauseFn(multistep.DebugLocationAfterRun, fmt.Sprintf("boot_command[%d]: %s", i, command), state)
|
||||
}
|
||||
if err := seq.Do(ctx, d); err != nil {
|
||||
err := fmt.Errorf("Error running boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
vncSendString(c, command)
|
||||
if pauseFn != nil {
|
||||
pauseFn(multistep.DebugLocationAfterRun, fmt.Sprintf("boot_command: %s", command), state)
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (*stepTypeBootCommand) Cleanup(multistep.StateBag) {}
|
||||
|
||||
func vncSendString(c *vnc.ClientConn, original string) {
|
||||
// Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h
|
||||
special := make(map[string]uint32)
|
||||
special["<bs>"] = 0xFF08
|
||||
special["<del>"] = 0xFFFF
|
||||
special["<enter>"] = 0xFF0D
|
||||
special["<esc>"] = 0xFF1B
|
||||
special["<f1>"] = 0xFFBE
|
||||
special["<f2>"] = 0xFFBF
|
||||
special["<f3>"] = 0xFFC0
|
||||
special["<f4>"] = 0xFFC1
|
||||
special["<f5>"] = 0xFFC2
|
||||
special["<f6>"] = 0xFFC3
|
||||
special["<f7>"] = 0xFFC4
|
||||
special["<f8>"] = 0xFFC5
|
||||
special["<f9>"] = 0xFFC6
|
||||
special["<f10>"] = 0xFFC7
|
||||
special["<f11>"] = 0xFFC8
|
||||
special["<f12>"] = 0xFFC9
|
||||
special["<return>"] = 0xFF0D
|
||||
special["<tab>"] = 0xFF09
|
||||
special["<up>"] = 0xFF52
|
||||
special["<down>"] = 0xFF54
|
||||
special["<left>"] = 0xFF51
|
||||
special["<right>"] = 0xFF53
|
||||
special["<spacebar>"] = 0x020
|
||||
special["<insert>"] = 0xFF63
|
||||
special["<home>"] = 0xFF50
|
||||
special["<end>"] = 0xFF57
|
||||
special["<pageUp>"] = 0xFF55
|
||||
special["<pageDown>"] = 0xFF56
|
||||
special["<leftAlt>"] = 0xFFE9
|
||||
special["<leftCtrl>"] = 0xFFE3
|
||||
special["<leftShift>"] = 0xFFE1
|
||||
special["<rightAlt>"] = 0xFFEA
|
||||
special["<rightCtrl>"] = 0xFFE4
|
||||
special["<rightShift>"] = 0xFFE2
|
||||
|
||||
shiftedChars := "~!@#$%^&*()_+{}|:\"<>?"
|
||||
waitRe := regexp.MustCompile(`^<wait([0-9hms]+)>`)
|
||||
|
||||
// We delay (default 100ms) between each key event to allow for CPU or
|
||||
// network latency. See PackerKeyEnv for tuning.
|
||||
keyInterval := common.PackerKeyDefault
|
||||
if delay, err := time.ParseDuration(os.Getenv(common.PackerKeyEnv)); err == nil {
|
||||
keyInterval = delay
|
||||
}
|
||||
|
||||
// TODO(mitchellh): Ripe for optimizations of some point, perhaps.
|
||||
for len(original) > 0 {
|
||||
var keyCode uint32
|
||||
keyShift := false
|
||||
|
||||
if strings.HasPrefix(original, "<leftAltOn>") {
|
||||
keyCode = special["<leftAlt>"]
|
||||
original = original[len("<leftAltOn>"):]
|
||||
log.Printf("Special code '<leftAltOn>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, true)
|
||||
time.Sleep(keyInterval)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<leftCtrlOn>") {
|
||||
keyCode = special["<leftCtrl>"]
|
||||
original = original[len("<leftCtrlOn>"):]
|
||||
log.Printf("Special code '<leftCtrlOn>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, true)
|
||||
time.Sleep(keyInterval)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<leftShiftOn>") {
|
||||
keyCode = special["<leftShift>"]
|
||||
original = original[len("<leftShiftOn>"):]
|
||||
log.Printf("Special code '<leftShiftOn>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, true)
|
||||
time.Sleep(keyInterval)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<leftAltOff>") {
|
||||
keyCode = special["<leftAlt>"]
|
||||
original = original[len("<leftAltOff>"):]
|
||||
log.Printf("Special code '<leftAltOff>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, false)
|
||||
time.Sleep(keyInterval)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<leftCtrlOff>") {
|
||||
keyCode = special["<leftCtrl>"]
|
||||
original = original[len("<leftCtrlOff>"):]
|
||||
log.Printf("Special code '<leftCtrlOff>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, false)
|
||||
time.Sleep(keyInterval)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<leftShiftOff>") {
|
||||
keyCode = special["<leftShift>"]
|
||||
original = original[len("<leftShiftOff>"):]
|
||||
log.Printf("Special code '<leftShiftOff>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, false)
|
||||
time.Sleep(keyInterval)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<rightAltOn>") {
|
||||
keyCode = special["<rightAlt>"]
|
||||
original = original[len("<rightAltOn>"):]
|
||||
log.Printf("Special code '<rightAltOn>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, true)
|
||||
time.Sleep(keyInterval)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<rightCtrlOn>") {
|
||||
keyCode = special["<rightCtrl>"]
|
||||
original = original[len("<rightCtrlOn>"):]
|
||||
log.Printf("Special code '<rightCtrlOn>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, true)
|
||||
time.Sleep(keyInterval)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<rightShiftOn>") {
|
||||
keyCode = special["<rightShift>"]
|
||||
original = original[len("<rightShiftOn>"):]
|
||||
log.Printf("Special code '<rightShiftOn>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, true)
|
||||
time.Sleep(keyInterval)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<rightAltOff>") {
|
||||
keyCode = special["<rightAlt>"]
|
||||
original = original[len("<rightAltOff>"):]
|
||||
log.Printf("Special code '<rightAltOff>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, false)
|
||||
time.Sleep(keyInterval)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<rightCtrlOff>") {
|
||||
keyCode = special["<rightCtrl>"]
|
||||
original = original[len("<rightCtrlOff>"):]
|
||||
log.Printf("Special code '<rightCtrlOff>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, false)
|
||||
time.Sleep(keyInterval)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<rightShiftOff>") {
|
||||
keyCode = special["<rightShift>"]
|
||||
original = original[len("<rightShiftOff>"):]
|
||||
log.Printf("Special code '<rightShiftOff>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, false)
|
||||
time.Sleep(keyInterval)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<wait>") {
|
||||
log.Printf("Special code '<wait>' found, sleeping one second")
|
||||
time.Sleep(1 * time.Second)
|
||||
original = original[len("<wait>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<wait5>") {
|
||||
log.Printf("Special code '<wait5>' found, sleeping 5 seconds")
|
||||
time.Sleep(5 * time.Second)
|
||||
original = original[len("<wait5>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<wait10>") {
|
||||
log.Printf("Special code '<wait10>' found, sleeping 10 seconds")
|
||||
time.Sleep(10 * time.Second)
|
||||
original = original[len("<wait10>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
waitMatch := waitRe.FindStringSubmatch(original)
|
||||
if len(waitMatch) > 1 {
|
||||
log.Printf("Special code %s found, sleeping", waitMatch[0])
|
||||
if dt, err := time.ParseDuration(waitMatch[1]); err == nil {
|
||||
time.Sleep(dt)
|
||||
original = original[len(waitMatch[0]):]
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
for specialCode, specialValue := range special {
|
||||
if strings.HasPrefix(original, specialCode) {
|
||||
log.Printf("Special code '%s' found, replacing with: %d", specialCode, specialValue)
|
||||
keyCode = specialValue
|
||||
original = original[len(specialCode):]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if keyCode == 0 {
|
||||
r, size := utf8.DecodeRuneInString(original)
|
||||
original = original[size:]
|
||||
keyCode = uint32(r)
|
||||
keyShift = unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
|
||||
|
||||
log.Printf("Sending char '%c', code %d, shift %v", r, keyCode, keyShift)
|
||||
}
|
||||
|
||||
if keyShift {
|
||||
c.KeyEvent(KeyLeftShift, true)
|
||||
}
|
||||
|
||||
c.KeyEvent(keyCode, true)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
c.KeyEvent(keyCode, false)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
if keyShift {
|
||||
c.KeyEvent(KeyLeftShift, false)
|
||||
}
|
||||
time.Sleep(keyInterval)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,27 +2,19 @@ package common
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
)
|
||||
|
||||
type RunConfig struct {
|
||||
Headless bool `mapstructure:"headless"`
|
||||
RawBootWait string `mapstructure:"boot_wait"`
|
||||
Headless bool `mapstructure:"headless"`
|
||||
|
||||
VRDPBindAddress string `mapstructure:"vrdp_bind_address"`
|
||||
VRDPPortMin uint `mapstructure:"vrdp_port_min"`
|
||||
VRDPPortMax uint `mapstructure:"vrdp_port_max"`
|
||||
|
||||
BootWait time.Duration ``
|
||||
}
|
||||
|
||||
func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
if c.RawBootWait == "" {
|
||||
c.RawBootWait = "10s"
|
||||
}
|
||||
|
||||
func (c *RunConfig) Prepare(ctx *interpolate.Context) (errs []error) {
|
||||
if c.VRDPBindAddress == "" {
|
||||
c.VRDPBindAddress = "127.0.0.1"
|
||||
}
|
||||
|
|
@ -35,17 +27,10 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
|
|||
c.VRDPPortMax = 6000
|
||||
}
|
||||
|
||||
var errs []error
|
||||
var err error
|
||||
c.BootWait, err = time.ParseDuration(c.RawBootWait)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("Failed parsing boot_wait: %s", err))
|
||||
}
|
||||
|
||||
if c.VRDPPortMin > c.VRDPPortMax {
|
||||
errs = append(
|
||||
errs, fmt.Errorf("vrdp_port_min must be less than vrdp_port_max"))
|
||||
}
|
||||
|
||||
return errs
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,38 +4,6 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func TestRunConfigPrepare_BootWait(t *testing.T) {
|
||||
var c *RunConfig
|
||||
var errs []error
|
||||
|
||||
// Test a default boot_wait
|
||||
c = new(RunConfig)
|
||||
errs = c.Prepare(testConfigTemplate(t))
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("should not have error: %s", errs)
|
||||
}
|
||||
|
||||
if c.RawBootWait != "10s" {
|
||||
t.Fatalf("bad value: %s", c.RawBootWait)
|
||||
}
|
||||
|
||||
// Test with a bad boot_wait
|
||||
c = new(RunConfig)
|
||||
c.RawBootWait = "this is not good"
|
||||
errs = c.Prepare(testConfigTemplate(t))
|
||||
if len(errs) == 0 {
|
||||
t.Fatalf("bad: %#v", errs)
|
||||
}
|
||||
|
||||
// Test with a good one
|
||||
c = new(RunConfig)
|
||||
c.RawBootWait = "5s"
|
||||
errs = c.Prepare(testConfigTemplate(t))
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("should not have error: %s", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunConfigPrepare_VRDPBindAddress(t *testing.T) {
|
||||
var c *RunConfig
|
||||
var errs []error
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package common
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
|
@ -18,7 +17,6 @@ import (
|
|||
//
|
||||
// Produces:
|
||||
type StepRun struct {
|
||||
BootWait time.Duration
|
||||
Headless bool
|
||||
|
||||
vmName string
|
||||
|
|
@ -60,22 +58,6 @@ func (s *StepRun) Run(_ context.Context, state multistep.StateBag) multistep.Ste
|
|||
|
||||
s.vmName = vmName
|
||||
|
||||
if int64(s.BootWait) > 0 {
|
||||
ui.Say(fmt.Sprintf("Waiting %s for boot...", s.BootWait))
|
||||
wait := time.After(s.BootWait)
|
||||
WAITLOOP:
|
||||
for {
|
||||
select {
|
||||
case <-wait:
|
||||
break WAITLOOP
|
||||
case <-time.After(1 * time.Second):
|
||||
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,14 +3,10 @@ package common
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
|
|
@ -24,29 +20,31 @@ type bootCommandTemplateData struct {
|
|||
Name string
|
||||
}
|
||||
|
||||
// This step "types" the boot command into the VM over VNC.
|
||||
//
|
||||
// Uses:
|
||||
// driver Driver
|
||||
// http_port int
|
||||
// ui packer.Ui
|
||||
// vmName string
|
||||
//
|
||||
// Produces:
|
||||
// <nothing>
|
||||
type StepTypeBootCommand struct {
|
||||
BootCommand []string
|
||||
BootCommand string
|
||||
BootWait time.Duration
|
||||
VMName string
|
||||
Ctx interpolate.Context
|
||||
}
|
||||
|
||||
func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
debug := state.Get("debug").(bool)
|
||||
driver := state.Get("driver").(Driver)
|
||||
httpPort := state.Get("http_port").(uint)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
// Wait the for the vm to boot.
|
||||
if int64(s.BootWait) > 0 {
|
||||
ui.Say(fmt.Sprintf("Waiting %s for boot...", s.BootWait.String()))
|
||||
select {
|
||||
case <-time.After(s.BootWait):
|
||||
break
|
||||
case <-ctx.Done():
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
var pauseFn multistep.DebugPauseFn
|
||||
if debug {
|
||||
pauseFn = state.Get("pauseFn").(multistep.DebugPauseFn)
|
||||
|
|
@ -60,336 +58,43 @@ func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m
|
|||
s.VMName,
|
||||
}
|
||||
|
||||
sendCodes := func(codes []string) error {
|
||||
args := []string{"controlvm", vmName, "keyboardputscancode"}
|
||||
args = append(args, codes...)
|
||||
|
||||
return driver.VBoxManage(args...)
|
||||
}
|
||||
d := bootcommand.NewPCXTDriver(sendCodes, 25)
|
||||
|
||||
ui.Say("Typing the boot command...")
|
||||
for i, command := range s.BootCommand {
|
||||
command, err := interpolate.Render(command, &s.Ctx)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error preparing boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
command, err := interpolate.Render(s.BootCommand, &s.Ctx)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error preparing boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
for _, code := range scancodes(command) {
|
||||
if code == "wait" {
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
seq, err := bootcommand.GenerateExpressionSequence(command)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error generating boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if code == "wait5" {
|
||||
time.Sleep(5 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
if code == "wait10" {
|
||||
time.Sleep(10 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
// Since typing is sometimes so slow, we check for an interrupt
|
||||
// in between each character.
|
||||
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
var codes []string
|
||||
|
||||
for i := 0; i < len(code)/2; i++ {
|
||||
codes = append(codes, code[i*2:i*2+2])
|
||||
}
|
||||
|
||||
args := []string{"controlvm", vmName, "keyboardputscancode"}
|
||||
args = append(args, codes...)
|
||||
|
||||
if err := driver.VBoxManage(args...); err != nil {
|
||||
err := fmt.Errorf("Error sending boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
if pauseFn != nil {
|
||||
pauseFn(multistep.DebugLocationAfterRun, fmt.Sprintf("boot_command[%d]: %s", i, command), state)
|
||||
}
|
||||
if err := seq.Do(ctx, d); err != nil {
|
||||
err := fmt.Errorf("Error running boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if pauseFn != nil {
|
||||
pauseFn(multistep.DebugLocationAfterRun, fmt.Sprintf("boot_command: %s", command), state)
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (*StepTypeBootCommand) Cleanup(multistep.StateBag) {}
|
||||
|
||||
func scancodes(message string) []string {
|
||||
// Scancodes reference: https://www.win.tue.nl/~aeb/linux/kbd/scancodes-10.html
|
||||
//
|
||||
// Scancodes represent raw keyboard output and are fed to the VM by the
|
||||
// VBoxManage controlvm keyboardputscancode program.
|
||||
//
|
||||
// Scancodes are recorded here in pairs. The first entry represents
|
||||
// the key press and the second entry represents the key release and is
|
||||
// derived from the first by the addition of 0x80.
|
||||
special := make(map[string][]string)
|
||||
special["<bs>"] = []string{"0e", "8e"}
|
||||
special["<del>"] = []string{"e053", "e0d3"}
|
||||
special["<enter>"] = []string{"1c", "9c"}
|
||||
special["<esc>"] = []string{"01", "81"}
|
||||
special["<f1>"] = []string{"3b", "bb"}
|
||||
special["<f2>"] = []string{"3c", "bc"}
|
||||
special["<f3>"] = []string{"3d", "bd"}
|
||||
special["<f4>"] = []string{"3e", "be"}
|
||||
special["<f5>"] = []string{"3f", "bf"}
|
||||
special["<f6>"] = []string{"40", "c0"}
|
||||
special["<f7>"] = []string{"41", "c1"}
|
||||
special["<f8>"] = []string{"42", "c2"}
|
||||
special["<f9>"] = []string{"43", "c3"}
|
||||
special["<f10>"] = []string{"44", "c4"}
|
||||
special["<f11>"] = []string{"57", "d7"}
|
||||
special["<f12>"] = []string{"58", "d8"}
|
||||
special["<return>"] = []string{"1c", "9c"}
|
||||
special["<tab>"] = []string{"0f", "8f"}
|
||||
special["<up>"] = []string{"e048", "e0c8"}
|
||||
special["<down>"] = []string{"e050", "e0d0"}
|
||||
special["<left>"] = []string{"e04b", "e0cb"}
|
||||
special["<right>"] = []string{"e04d", "e0cd"}
|
||||
special["<spacebar>"] = []string{"39", "b9"}
|
||||
special["<insert>"] = []string{"e052", "e0d2"}
|
||||
special["<home>"] = []string{"e047", "e0c7"}
|
||||
special["<end>"] = []string{"e04f", "e0cf"}
|
||||
special["<pageUp>"] = []string{"e049", "e0c9"}
|
||||
special["<pageDown>"] = []string{"e051", "e0d1"}
|
||||
special["<leftAlt>"] = []string{"38", "b8"}
|
||||
special["<leftCtrl>"] = []string{"1d", "9d"}
|
||||
special["<leftShift>"] = []string{"2a", "aa"}
|
||||
special["<rightAlt>"] = []string{"e038", "e0b8"}
|
||||
special["<rightCtrl>"] = []string{"e01d", "e09d"}
|
||||
special["<rightShift>"] = []string{"36", "b6"}
|
||||
special["<leftSuper>"] = []string{"e05b", "e0db"}
|
||||
special["<rightSuper>"] = []string{"e05c", "e0dc"}
|
||||
|
||||
shiftedChars := "~!@#$%^&*()_+{}|:\"<>?"
|
||||
|
||||
scancodeIndex := make(map[string]uint)
|
||||
scancodeIndex["1234567890-="] = 0x02
|
||||
scancodeIndex["!@#$%^&*()_+"] = 0x02
|
||||
scancodeIndex["qwertyuiop[]"] = 0x10
|
||||
scancodeIndex["QWERTYUIOP{}"] = 0x10
|
||||
scancodeIndex["asdfghjkl;'`"] = 0x1e
|
||||
scancodeIndex[`ASDFGHJKL:"~`] = 0x1e
|
||||
scancodeIndex[`\zxcvbnm,./`] = 0x2b
|
||||
scancodeIndex["|ZXCVBNM<>?"] = 0x2b
|
||||
scancodeIndex[" "] = 0x39
|
||||
|
||||
scancodeMap := make(map[rune]uint)
|
||||
for chars, start := range scancodeIndex {
|
||||
var i uint = 0
|
||||
for len(chars) > 0 {
|
||||
r, size := utf8.DecodeRuneInString(chars)
|
||||
chars = chars[size:]
|
||||
scancodeMap[r] = start + i
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
azOnRegex := regexp.MustCompile("^<(?P<ordinary>[a-zA-Z])On>")
|
||||
azOffRegex := regexp.MustCompile("^<(?P<ordinary>[a-zA-Z])Off>")
|
||||
|
||||
result := make([]string, 0, len(message)*2)
|
||||
for len(message) > 0 {
|
||||
var scancode []string
|
||||
|
||||
if azOnRegex.MatchString(message) {
|
||||
m := azOnRegex.FindStringSubmatch(message)
|
||||
r, _ := utf8.DecodeRuneInString(m[1])
|
||||
message = message[len("<aOn>"):]
|
||||
scancodeInt := scancodeMap[r]
|
||||
keyShift := unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
|
||||
|
||||
if keyShift {
|
||||
scancode = append(scancode, "2a")
|
||||
}
|
||||
|
||||
scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt))
|
||||
|
||||
log.Printf("Sending char '%c', code '%v', shift %v", r, scancodeInt, keyShift)
|
||||
}
|
||||
|
||||
if azOffRegex.MatchString(message) {
|
||||
m := azOffRegex.FindStringSubmatch(message)
|
||||
r, _ := utf8.DecodeRuneInString(m[1])
|
||||
message = message[len("<aOff>"):]
|
||||
scancodeInt := scancodeMap[r] + 0x80
|
||||
keyShift := unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
|
||||
|
||||
if keyShift {
|
||||
scancode = append(scancode, "aa")
|
||||
}
|
||||
|
||||
scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt))
|
||||
|
||||
log.Printf("Sending char '%c', code '%v', shift %v", r, scancodeInt, keyShift)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<f12On>") {
|
||||
scancode = append(scancode, "58")
|
||||
message = message[len("<f12On>"):]
|
||||
log.Printf("Special code '<f12On>', replacing with: 58")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftAltOn>") {
|
||||
scancode = append(scancode, "38")
|
||||
message = message[len("<leftAltOn>"):]
|
||||
log.Printf("Special code '<leftAltOn>' found, replacing with: 38")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftCtrlOn>") {
|
||||
scancode = append(scancode, "1d")
|
||||
message = message[len("<leftCtrlOn>"):]
|
||||
log.Printf("Special code '<leftCtrlOn>' found, replacing with: 1d")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftShiftOn>") {
|
||||
scancode = append(scancode, "2a")
|
||||
message = message[len("<leftShiftOn>"):]
|
||||
log.Printf("Special code '<leftShiftOn>' found, replacing with: 2a")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftSuperOn>") {
|
||||
scancode = append(scancode, "e05b")
|
||||
message = message[len("<leftSuperOn>"):]
|
||||
log.Printf("Special code '<leftSuperOn>' found, replacing with: e05b")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<f12Off>") {
|
||||
scancode = append(scancode, "d8")
|
||||
message = message[len("<f12Off>"):]
|
||||
log.Printf("Special code '<f12Off>' found, replacing with: d8")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftAltOff>") {
|
||||
scancode = append(scancode, "b8")
|
||||
message = message[len("<leftAltOff>"):]
|
||||
log.Printf("Special code '<leftAltOff>' found, replacing with: b8")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftCtrlOff>") {
|
||||
scancode = append(scancode, "9d")
|
||||
message = message[len("<leftCtrlOff>"):]
|
||||
log.Printf("Special code '<leftCtrlOff>' found, replacing with: 9d")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftShiftOff>") {
|
||||
scancode = append(scancode, "aa")
|
||||
message = message[len("<leftShiftOff>"):]
|
||||
log.Printf("Special code '<leftShiftOff>' found, replacing with: aa")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftSuperOff>") {
|
||||
scancode = append(scancode, "e0db")
|
||||
message = message[len("<leftSuperOff>"):]
|
||||
log.Printf("Special code '<leftSuperOff>' found, replacing with: e0db")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightAltOn>") {
|
||||
scancode = append(scancode, "e038")
|
||||
message = message[len("<rightAltOn>"):]
|
||||
log.Printf("Special code '<rightAltOn>' found, replacing with: e038")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightCtrlOn>") {
|
||||
scancode = append(scancode, "e01d")
|
||||
message = message[len("<rightCtrlOn>"):]
|
||||
log.Printf("Special code '<rightCtrlOn>' found, replacing with: e01d")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightShiftOn>") {
|
||||
scancode = append(scancode, "36")
|
||||
message = message[len("<rightShiftOn>"):]
|
||||
log.Printf("Special code '<rightShiftOn>' found, replacing with: 36")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightSuperOn>") {
|
||||
scancode = append(scancode, "e05c")
|
||||
message = message[len("<rightSuperOn>"):]
|
||||
log.Printf("Special code '<rightSuperOn>' found, replacing with: e05c")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightAltOff>") {
|
||||
scancode = append(scancode, "e0b8")
|
||||
message = message[len("<rightAltOff>"):]
|
||||
log.Printf("Special code '<rightAltOff>' found, replacing with: e0b8")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightCtrlOff>") {
|
||||
scancode = append(scancode, "e09d")
|
||||
message = message[len("<rightCtrlOff>"):]
|
||||
log.Printf("Special code '<rightCtrlOff>' found, replacing with: e09d")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightShiftOff>") {
|
||||
scancode = append(scancode, "b6")
|
||||
message = message[len("<rightShiftOff>"):]
|
||||
log.Printf("Special code '<rightShiftOff>' found, replacing with: b6")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<rightSuperOff>") {
|
||||
scancode = append(scancode, "e0dc")
|
||||
message = message[len("<rightSuperOff>"):]
|
||||
log.Printf("Special code '<rightSuperOff>' found, replacing with: e0dc")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<wait>") {
|
||||
log.Printf("Special code <wait> found, will sleep 1 second at this point.")
|
||||
scancode = append(scancode, "wait")
|
||||
message = message[len("<wait>"):]
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<wait5>") {
|
||||
log.Printf("Special code <wait5> found, will sleep 5 seconds at this point.")
|
||||
scancode = append(scancode, "wait5")
|
||||
message = message[len("<wait5>"):]
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<wait10>") {
|
||||
log.Printf("Special code <wait10> found, will sleep 10 seconds at this point.")
|
||||
scancode = append(scancode, "wait10")
|
||||
message = message[len("<wait10>"):]
|
||||
}
|
||||
|
||||
if scancode == nil {
|
||||
for specialCode, specialValue := range special {
|
||||
if strings.HasPrefix(message, specialCode) {
|
||||
log.Printf("Special code '%s' found, replacing with: %s", specialCode, specialValue)
|
||||
scancode = append(scancode, specialValue...)
|
||||
message = message[len(specialCode):]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if scancode == nil {
|
||||
r, size := utf8.DecodeRuneInString(message)
|
||||
message = message[size:]
|
||||
scancodeInt := scancodeMap[r]
|
||||
keyShift := unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
|
||||
|
||||
scancode = make([]string, 0, 4)
|
||||
if keyShift {
|
||||
scancode = append(scancode, "2a")
|
||||
}
|
||||
|
||||
scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt))
|
||||
|
||||
if keyShift {
|
||||
scancode = append(scancode, "aa")
|
||||
}
|
||||
|
||||
scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt+0x80))
|
||||
log.Printf("Sending char '%c', code '%v', shift %v", r, scancode, keyShift)
|
||||
}
|
||||
|
||||
result = append(result, scancode...)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestScancodes(t *testing.T) {
|
||||
var bootcommand = []string{
|
||||
"1234567890-=<enter><wait>",
|
||||
"!@#$%^&*()_+<enter>",
|
||||
"qwertyuiop[]<enter>",
|
||||
"QWERTYUIOP{}<enter>",
|
||||
"asdfghjkl;'`<enter>",
|
||||
`ASDFGHJKL:"~<enter>`,
|
||||
"\\zxcvbnm,./<enter>",
|
||||
"|ZXCVBNM<>?<enter>",
|
||||
"<enter>",
|
||||
"<leftAltOn><esc><leftAltOff><wait5>",
|
||||
"<tab><tab><tab><tab><tab><spacebar><wait5>",
|
||||
}
|
||||
|
||||
var expected = [][]string{
|
||||
{"02", "82", "03", "83", "04", "84", "05", "85", "06", "86", "07", "87", "08", "88", "09", "89", "0a", "8a", "0b", "8b", "0c", "8c", "0d", "8d", "1c", "9c", "wait"},
|
||||
{"2a", "02", "aa", "82", "2a", "03", "aa", "83", "2a", "04", "aa", "84", "2a", "05", "aa", "85", "2a", "06", "aa", "86", "2a", "07", "aa", "87", "2a", "08", "aa", "88", "2a", "09", "aa", "89", "2a", "0a", "aa", "8a", "2a", "0b", "aa", "8b", "2a", "0c", "aa", "8c", "2a", "0d", "aa", "8d", "1c", "9c"},
|
||||
{"10", "90", "11", "91", "12", "92", "13", "93", "14", "94", "15", "95", "16", "96", "17", "97", "18", "98", "19", "99", "1a", "9a", "1b", "9b", "1c", "9c"},
|
||||
{"2a", "10", "aa", "90", "2a", "11", "aa", "91", "2a", "12", "aa", "92", "2a", "13", "aa", "93", "2a", "14", "aa", "94", "2a", "15", "aa", "95", "2a", "16", "aa", "96", "2a", "17", "aa", "97", "2a", "18", "aa", "98", "2a", "19", "aa", "99", "2a", "1a", "aa", "9a", "2a", "1b", "aa", "9b", "1c", "9c"},
|
||||
{"1e", "9e", "1f", "9f", "20", "a0", "21", "a1", "22", "a2", "23", "a3", "24", "a4", "25", "a5", "26", "a6", "27", "a7", "28", "a8", "29", "a9", "1c", "9c"},
|
||||
{"2a", "1e", "aa", "9e", "2a", "1f", "aa", "9f", "2a", "20", "aa", "a0", "2a", "21", "aa", "a1", "2a", "22", "aa", "a2", "2a", "23", "aa", "a3", "2a", "24", "aa", "a4", "2a", "25", "aa", "a5", "2a", "26", "aa", "a6", "2a", "27", "aa", "a7", "2a", "28", "aa", "a8", "2a", "29", "aa", "a9", "1c", "9c"},
|
||||
{"2b", "ab", "2c", "ac", "2d", "ad", "2e", "ae", "2f", "af", "30", "b0", "31", "b1", "32", "b2", "33", "b3", "34", "b4", "35", "b5", "1c", "9c"},
|
||||
{"2a", "2b", "aa", "ab", "2a", "2c", "aa", "ac", "2a", "2d", "aa", "ad", "2a", "2e", "aa", "ae", "2a", "2f", "aa", "af", "2a", "30", "aa", "b0", "2a", "31", "aa", "b1", "2a", "32", "aa", "b2", "2a", "33", "aa", "b3", "2a", "34", "aa", "b4", "2a", "35", "aa", "b5", "1c", "9c"},
|
||||
{"1c", "9c"},
|
||||
{"38", "01", "81", "b8", "wait5"},
|
||||
{"0f", "8f", "0f", "8f", "0f", "8f", "0f", "8f", "0f", "8f", "39", "b9", "wait5"},
|
||||
}
|
||||
|
||||
for i, command := range bootcommand {
|
||||
for j, code := range scancodes(command) {
|
||||
if code != expected[i][j] {
|
||||
t.Fatalf("%#v should have become %#v", scancodes(command), expected[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
|
|
@ -27,6 +28,7 @@ type Config struct {
|
|||
common.HTTPConfig `mapstructure:",squash"`
|
||||
common.ISOConfig `mapstructure:",squash"`
|
||||
common.FloppyConfig `mapstructure:",squash"`
|
||||
bootcommand.BootConfig `mapstructure:",squash"`
|
||||
vboxcommon.ExportConfig `mapstructure:",squash"`
|
||||
vboxcommon.ExportOpts `mapstructure:",squash"`
|
||||
vboxcommon.OutputConfig `mapstructure:",squash"`
|
||||
|
|
@ -37,21 +39,20 @@ type Config struct {
|
|||
vboxcommon.VBoxManagePostConfig `mapstructure:",squash"`
|
||||
vboxcommon.VBoxVersionConfig `mapstructure:",squash"`
|
||||
|
||||
BootCommand []string `mapstructure:"boot_command"`
|
||||
DiskSize uint `mapstructure:"disk_size"`
|
||||
GuestAdditionsMode string `mapstructure:"guest_additions_mode"`
|
||||
GuestAdditionsPath string `mapstructure:"guest_additions_path"`
|
||||
GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"`
|
||||
GuestAdditionsURL string `mapstructure:"guest_additions_url"`
|
||||
GuestOSType string `mapstructure:"guest_os_type"`
|
||||
HardDriveDiscard bool `mapstructure:"hard_drive_discard"`
|
||||
HardDriveInterface string `mapstructure:"hard_drive_interface"`
|
||||
SATAPortCount int `mapstructure:"sata_port_count"`
|
||||
HardDriveNonrotational bool `mapstructure:"hard_drive_nonrotational"`
|
||||
ISOInterface string `mapstructure:"iso_interface"`
|
||||
KeepRegistered bool `mapstructure:"keep_registered"`
|
||||
SkipExport bool `mapstructure:"skip_export"`
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
DiskSize uint `mapstructure:"disk_size"`
|
||||
GuestAdditionsMode string `mapstructure:"guest_additions_mode"`
|
||||
GuestAdditionsPath string `mapstructure:"guest_additions_path"`
|
||||
GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"`
|
||||
GuestAdditionsURL string `mapstructure:"guest_additions_url"`
|
||||
GuestOSType string `mapstructure:"guest_os_type"`
|
||||
HardDriveDiscard bool `mapstructure:"hard_drive_discard"`
|
||||
HardDriveInterface string `mapstructure:"hard_drive_interface"`
|
||||
SATAPortCount int `mapstructure:"sata_port_count"`
|
||||
HardDriveNonrotational bool `mapstructure:"hard_drive_nonrotational"`
|
||||
ISOInterface string `mapstructure:"iso_interface"`
|
||||
KeepRegistered bool `mapstructure:"keep_registered"`
|
||||
SkipExport bool `mapstructure:"skip_export"`
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
|
@ -94,6 +95,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
errs = packer.MultiErrorAppend(errs, b.config.VBoxManageConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.VBoxManagePostConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.VBoxVersionConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.BootConfig.Prepare(&b.config.ctx)...)
|
||||
|
||||
if b.config.DiskSize == 0 {
|
||||
b.config.DiskSize = 40000
|
||||
|
|
@ -240,11 +242,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
Ctx: b.config.ctx,
|
||||
},
|
||||
&vboxcommon.StepRun{
|
||||
BootWait: b.config.BootWait,
|
||||
Headless: b.config.Headless,
|
||||
},
|
||||
&vboxcommon.StepTypeBootCommand{
|
||||
BootCommand: b.config.BootCommand,
|
||||
BootWait: b.config.BootWait,
|
||||
BootCommand: b.config.FlatBootCommand(),
|
||||
VMName: b.config.VMName,
|
||||
Ctx: b.config.ctx,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -103,11 +103,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
Ctx: b.config.ctx,
|
||||
},
|
||||
&vboxcommon.StepRun{
|
||||
BootWait: b.config.BootWait,
|
||||
Headless: b.config.Headless,
|
||||
},
|
||||
&vboxcommon.StepTypeBootCommand{
|
||||
BootCommand: b.config.BootCommand,
|
||||
BootWait: b.config.BootWait,
|
||||
BootCommand: b.config.FlatBootCommand(),
|
||||
VMName: b.config.VMName,
|
||||
Ctx: b.config.ctx,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
|
|
@ -16,6 +17,7 @@ type Config struct {
|
|||
common.PackerConfig `mapstructure:",squash"`
|
||||
common.HTTPConfig `mapstructure:",squash"`
|
||||
common.FloppyConfig `mapstructure:",squash"`
|
||||
bootcommand.BootConfig `mapstructure:",squash"`
|
||||
vboxcommon.ExportConfig `mapstructure:",squash"`
|
||||
vboxcommon.ExportOpts `mapstructure:",squash"`
|
||||
vboxcommon.OutputConfig `mapstructure:",squash"`
|
||||
|
|
@ -26,7 +28,6 @@ type Config struct {
|
|||
vboxcommon.VBoxManagePostConfig `mapstructure:",squash"`
|
||||
vboxcommon.VBoxVersionConfig `mapstructure:",squash"`
|
||||
|
||||
BootCommand []string `mapstructure:"boot_command"`
|
||||
Checksum string `mapstructure:"checksum"`
|
||||
ChecksumType string `mapstructure:"checksum_type"`
|
||||
GuestAdditionsMode string `mapstructure:"guest_additions_mode"`
|
||||
|
|
@ -90,6 +91,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
errs = packer.MultiErrorAppend(errs, c.VBoxManageConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.VBoxManagePostConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.VBoxVersionConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.BootConfig.Prepare(&c.ctx)...)
|
||||
|
||||
c.ChecksumType = strings.ToLower(c.ChecksumType)
|
||||
c.Checksum = strings.ToLower(c.Checksum)
|
||||
|
|
|
|||
|
|
@ -2,30 +2,20 @@ package common
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
)
|
||||
|
||||
type RunConfig struct {
|
||||
Headless bool `mapstructure:"headless"`
|
||||
RawBootWait string `mapstructure:"boot_wait"`
|
||||
DisableVNC bool `mapstructure:"disable_vnc"`
|
||||
BootCommand []string `mapstructure:"boot_command"`
|
||||
Headless bool `mapstructure:"headless"`
|
||||
|
||||
VNCBindAddress string `mapstructure:"vnc_bind_address"`
|
||||
VNCPortMin uint `mapstructure:"vnc_port_min"`
|
||||
VNCPortMax uint `mapstructure:"vnc_port_max"`
|
||||
VNCDisablePassword bool `mapstructure:"vnc_disable_password"`
|
||||
|
||||
BootWait time.Duration ``
|
||||
}
|
||||
|
||||
func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
if c.RawBootWait == "" {
|
||||
c.RawBootWait = "10s"
|
||||
}
|
||||
|
||||
func (c *RunConfig) Prepare(ctx *interpolate.Context) (errs []error) {
|
||||
if c.VNCPortMin == 0 {
|
||||
c.VNCPortMin = 5900
|
||||
}
|
||||
|
|
@ -38,25 +28,9 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
|
|||
c.VNCBindAddress = "127.0.0.1"
|
||||
}
|
||||
|
||||
var errs []error
|
||||
var err error
|
||||
if len(c.BootCommand) > 0 && c.DisableVNC {
|
||||
errs = append(errs,
|
||||
fmt.Errorf("A boot command cannot be used when vnc is disabled."))
|
||||
}
|
||||
|
||||
if c.RawBootWait != "" {
|
||||
c.BootWait, err = time.ParseDuration(c.RawBootWait)
|
||||
if err != nil {
|
||||
errs = append(
|
||||
errs, fmt.Errorf("Failed parsing boot_wait: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
if c.VNCPortMin > c.VNCPortMax {
|
||||
errs = append(
|
||||
errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max"))
|
||||
errs = append(errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max"))
|
||||
}
|
||||
|
||||
return errs
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRunConfigPrepare(t *testing.T) {
|
||||
var c *RunConfig
|
||||
|
||||
// Test a default boot_wait
|
||||
c = new(RunConfig)
|
||||
c.RawBootWait = ""
|
||||
errs := c.Prepare(testConfigTemplate(t))
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("bad: %#v", errs)
|
||||
}
|
||||
if c.RawBootWait != "10s" {
|
||||
t.Fatalf("bad value: %s", c.RawBootWait)
|
||||
}
|
||||
|
||||
// Test with a bad boot_wait
|
||||
c = new(RunConfig)
|
||||
c.RawBootWait = "this is not good"
|
||||
errs = c.Prepare(testConfigTemplate(t))
|
||||
if len(errs) == 0 {
|
||||
t.Fatal("should error")
|
||||
}
|
||||
|
||||
// Test with a good one
|
||||
c = new(RunConfig)
|
||||
c.RawBootWait = "5s"
|
||||
errs = c.Prepare(testConfigTemplate(t))
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("bad: %#v", errs)
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,6 @@ import (
|
|||
// Produces:
|
||||
// <nothing>
|
||||
type StepRun struct {
|
||||
BootWait time.Duration
|
||||
DurationBeforeStop time.Duration
|
||||
Headless bool
|
||||
|
||||
|
|
@ -65,24 +64,6 @@ func (s *StepRun) Run(_ context.Context, state multistep.StateBag) multistep.Ste
|
|||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Wait the wait amount
|
||||
if int64(s.BootWait) > 0 {
|
||||
ui.Say(fmt.Sprintf("Waiting %s for boot...", s.BootWait.String()))
|
||||
wait := time.After(s.BootWait)
|
||||
WAITLOOP:
|
||||
for {
|
||||
select {
|
||||
case <-wait:
|
||||
break WAITLOOP
|
||||
case <-time.After(1 * time.Second):
|
||||
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,28 +5,16 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
"github.com/mitchellh/go-vnc"
|
||||
)
|
||||
|
||||
const KeyLeftShift uint32 = 0xFFE1
|
||||
|
||||
type bootCommandTemplateData struct {
|
||||
HTTPIP string
|
||||
HTTPPort uint
|
||||
Name string
|
||||
}
|
||||
|
||||
// This step "types" the boot command into the VM over VNC.
|
||||
//
|
||||
// Uses:
|
||||
|
|
@ -37,13 +25,19 @@ type bootCommandTemplateData struct {
|
|||
// Produces:
|
||||
// <nothing>
|
||||
type StepTypeBootCommand struct {
|
||||
BootCommand string
|
||||
VNCEnabled bool
|
||||
BootCommand []string
|
||||
BootWait time.Duration
|
||||
VMName string
|
||||
Ctx interpolate.Context
|
||||
}
|
||||
type bootCommandTemplateData struct {
|
||||
HTTPIP string
|
||||
HTTPPort uint
|
||||
Name string
|
||||
}
|
||||
|
||||
func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
if !s.VNCEnabled {
|
||||
log.Println("Skipping boot command step...")
|
||||
return multistep.ActionContinue
|
||||
|
|
@ -57,6 +51,17 @@ func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m
|
|||
vncPort := state.Get("vnc_port").(uint)
|
||||
vncPassword := state.Get("vnc_password")
|
||||
|
||||
// Wait the for the vm to boot.
|
||||
if int64(s.BootWait) > 0 {
|
||||
ui.Say(fmt.Sprintf("Waiting %s for boot...", s.BootWait.String()))
|
||||
select {
|
||||
case <-time.After(s.BootWait):
|
||||
break
|
||||
case <-ctx.Done():
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
var pauseFn multistep.DebugPauseFn
|
||||
if debug {
|
||||
pauseFn = state.Get("pauseFn").(multistep.DebugPauseFn)
|
||||
|
|
@ -110,355 +115,38 @@ func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m
|
|||
s.VMName,
|
||||
}
|
||||
|
||||
d := bootcommand.NewVNCDriver(c)
|
||||
|
||||
ui.Say("Typing the boot command over VNC...")
|
||||
for i, command := range s.BootCommand {
|
||||
command, err := interpolate.Render(command, &s.Ctx)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error preparing boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
command, err := interpolate.Render(s.BootCommand, &s.Ctx)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error preparing boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Check for interrupts between typing things so we can cancel
|
||||
// since this isn't the fastest thing.
|
||||
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
seq, err := bootcommand.GenerateExpressionSequence(command)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error generating boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if pauseFn != nil {
|
||||
pauseFn(multistep.DebugLocationAfterRun, fmt.Sprintf("boot_command[%d]: %s", i, command), state)
|
||||
}
|
||||
if err := seq.Do(ctx, d); err != nil {
|
||||
err := fmt.Errorf("Error running boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
vncSendString(c, command)
|
||||
if pauseFn != nil {
|
||||
pauseFn(multistep.DebugLocationAfterRun,
|
||||
fmt.Sprintf("boot_command: %s", command), state)
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (*StepTypeBootCommand) Cleanup(multistep.StateBag) {}
|
||||
|
||||
func vncSendString(c *vnc.ClientConn, original string) {
|
||||
// Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h
|
||||
special := make(map[string]uint32)
|
||||
special["<bs>"] = 0xFF08
|
||||
special["<del>"] = 0xFFFF
|
||||
special["<enter>"] = 0xFF0D
|
||||
special["<esc>"] = 0xFF1B
|
||||
special["<f1>"] = 0xFFBE
|
||||
special["<f2>"] = 0xFFBF
|
||||
special["<f3>"] = 0xFFC0
|
||||
special["<f4>"] = 0xFFC1
|
||||
special["<f5>"] = 0xFFC2
|
||||
special["<f6>"] = 0xFFC3
|
||||
special["<f7>"] = 0xFFC4
|
||||
special["<f8>"] = 0xFFC5
|
||||
special["<f9>"] = 0xFFC6
|
||||
special["<f10>"] = 0xFFC7
|
||||
special["<f11>"] = 0xFFC8
|
||||
special["<f12>"] = 0xFFC9
|
||||
special["<return>"] = 0xFF0D
|
||||
special["<tab>"] = 0xFF09
|
||||
special["<up>"] = 0xFF52
|
||||
special["<down>"] = 0xFF54
|
||||
special["<left>"] = 0xFF51
|
||||
special["<right>"] = 0xFF53
|
||||
special["<spacebar>"] = 0x020
|
||||
special["<insert>"] = 0xFF63
|
||||
special["<home>"] = 0xFF50
|
||||
special["<end>"] = 0xFF57
|
||||
special["<pageUp>"] = 0xFF55
|
||||
special["<pageDown>"] = 0xFF56
|
||||
special["<leftAlt>"] = 0xFFE9
|
||||
special["<leftCtrl>"] = 0xFFE3
|
||||
special["<leftShift>"] = 0xFFE1
|
||||
special["<rightAlt>"] = 0xFFEA
|
||||
special["<rightCtrl>"] = 0xFFE4
|
||||
special["<rightShift>"] = 0xFFE2
|
||||
special["<leftSuper>"] = 0xFFEB
|
||||
special["<rightSuper>"] = 0xFFEC
|
||||
|
||||
shiftedChars := "~!@#$%^&*()_+{}|:\"<>?"
|
||||
|
||||
// We delay (default 100ms) between each key event to allow for CPU or
|
||||
// network latency. See PackerKeyEnv for tuning.
|
||||
keyInterval := common.PackerKeyDefault
|
||||
if delay, err := time.ParseDuration(os.Getenv(common.PackerKeyEnv)); err == nil {
|
||||
keyInterval = delay
|
||||
}
|
||||
|
||||
azOnRegex := regexp.MustCompile("^<(?P<ordinary>[a-zA-Z])On>")
|
||||
azOffRegex := regexp.MustCompile("^<(?P<ordinary>[a-zA-Z])Off>")
|
||||
|
||||
// TODO(mitchellh): Ripe for optimizations of some point, perhaps.
|
||||
for len(original) > 0 {
|
||||
var keyCode uint32
|
||||
keyShift := false
|
||||
|
||||
if strings.HasPrefix(original, "<leftAltOn>") {
|
||||
keyCode = special["<leftAlt>"]
|
||||
original = original[len("<leftAltOn>"):]
|
||||
log.Printf("Special code '<leftAltOn>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, true)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<leftCtrlOn>") {
|
||||
keyCode = special["<leftCtrl>"]
|
||||
original = original[len("<leftCtrlOn>"):]
|
||||
log.Printf("Special code '<leftCtrlOn>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, true)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<leftShiftOn>") {
|
||||
keyCode = special["<leftShift>"]
|
||||
original = original[len("<leftShiftOn>"):]
|
||||
log.Printf("Special code '<leftShiftOn>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, true)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<leftSuperOn>") {
|
||||
keyCode = special["<leftSuper>"]
|
||||
original = original[len("<leftSuperOn>"):]
|
||||
log.Printf("Special code '<leftSuperOn>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, true)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if azOnRegex.MatchString(original) {
|
||||
m := azOnRegex.FindStringSubmatch(original)
|
||||
r, _ := utf8.DecodeRuneInString(m[1])
|
||||
original = original[len("<aOn>"):]
|
||||
keyCode = uint32(r)
|
||||
keyShift = unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
|
||||
|
||||
log.Printf("Special code '%s' found, replacing with %d, shift %v", m[0], keyCode, keyShift)
|
||||
|
||||
if keyShift {
|
||||
c.KeyEvent(KeyLeftShift, true)
|
||||
}
|
||||
|
||||
c.KeyEvent(keyCode, true)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<leftAltOff>") {
|
||||
keyCode = special["<leftAlt>"]
|
||||
original = original[len("<leftAltOff>"):]
|
||||
log.Printf("Special code '<leftAltOff>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, false)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<leftCtrlOff>") {
|
||||
keyCode = special["<leftCtrl>"]
|
||||
original = original[len("<leftCtrlOff>"):]
|
||||
log.Printf("Special code '<leftCtrlOff>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, false)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<leftShiftOff>") {
|
||||
keyCode = special["<leftShift>"]
|
||||
original = original[len("<leftShiftOff>"):]
|
||||
log.Printf("Special code '<leftShiftOff>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, false)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<leftSuperOff>") {
|
||||
keyCode = special["<leftSuper>"]
|
||||
original = original[len("<leftSuperOff>"):]
|
||||
log.Printf("Special code '<leftSuperOff>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, false)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if azOffRegex.MatchString(original) {
|
||||
m := azOffRegex.FindStringSubmatch(original)
|
||||
r, _ := utf8.DecodeRuneInString(m[1])
|
||||
original = original[len("<aOff>"):]
|
||||
keyCode = uint32(r)
|
||||
keyShift = unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
|
||||
|
||||
log.Printf("Special code '%s' found, replacing with %d, shift %v", m[0], keyCode, keyShift)
|
||||
|
||||
if keyShift {
|
||||
c.KeyEvent(KeyLeftShift, false)
|
||||
}
|
||||
|
||||
c.KeyEvent(keyCode, false)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<rightAltOn>") {
|
||||
keyCode = special["<rightAlt>"]
|
||||
original = original[len("<rightAltOn>"):]
|
||||
log.Printf("Special code '<rightAltOn>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, true)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<rightCtrlOn>") {
|
||||
keyCode = special["<rightCtrl>"]
|
||||
original = original[len("<rightCtrlOn>"):]
|
||||
log.Printf("Special code '<rightCtrlOn>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, true)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<rightShiftOn>") {
|
||||
keyCode = special["<rightShift>"]
|
||||
original = original[len("<rightShiftOn>"):]
|
||||
log.Printf("Special code '<rightShiftOn>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, true)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<rightSuperOn>") {
|
||||
keyCode = special["<rightSuper>"]
|
||||
original = original[len("<rightSuperOn>"):]
|
||||
log.Printf("Special code '<rightSuperOn>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, true)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<rightAltOff>") {
|
||||
keyCode = special["<rightAlt>"]
|
||||
original = original[len("<rightAltOff>"):]
|
||||
log.Printf("Special code '<rightAltOff>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, false)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<rightCtrlOff>") {
|
||||
keyCode = special["<rightCtrl>"]
|
||||
original = original[len("<rightCtrlOff>"):]
|
||||
log.Printf("Special code '<rightCtrlOff>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, false)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<rightShiftOff>") {
|
||||
keyCode = special["<rightShift>"]
|
||||
original = original[len("<rightShiftOff>"):]
|
||||
log.Printf("Special code '<rightShiftOff>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, false)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<rightSuperOff>") {
|
||||
keyCode = special["<rightSuper>"]
|
||||
original = original[len("<rightSuperOff>"):]
|
||||
log.Printf("Special code '<rightSuperOff>' found, replacing with: %d", keyCode)
|
||||
|
||||
c.KeyEvent(keyCode, false)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<wait>") {
|
||||
log.Printf("Special code '<wait>' found, sleeping one second")
|
||||
time.Sleep(1 * time.Second)
|
||||
original = original[len("<wait>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<wait5>") {
|
||||
log.Printf("Special code '<wait5>' found, sleeping 5 seconds")
|
||||
time.Sleep(5 * time.Second)
|
||||
original = original[len("<wait5>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<wait10>") {
|
||||
log.Printf("Special code '<wait10>' found, sleeping 10 seconds")
|
||||
time.Sleep(10 * time.Second)
|
||||
original = original[len("<wait10>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
for specialCode, specialValue := range special {
|
||||
if strings.HasPrefix(original, specialCode) {
|
||||
log.Printf("Special code '%s' found, replacing with: %d", specialCode, specialValue)
|
||||
keyCode = specialValue
|
||||
original = original[len(specialCode):]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if keyCode == 0 {
|
||||
r, size := utf8.DecodeRuneInString(original)
|
||||
original = original[size:]
|
||||
keyCode = uint32(r)
|
||||
keyShift = unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
|
||||
|
||||
log.Printf("Sending char '%c', code %d, shift %v", r, keyCode, keyShift)
|
||||
}
|
||||
|
||||
if keyShift {
|
||||
c.KeyEvent(KeyLeftShift, true)
|
||||
}
|
||||
|
||||
c.KeyEvent(keyCode, true)
|
||||
time.Sleep(keyInterval)
|
||||
c.KeyEvent(keyCode, false)
|
||||
time.Sleep(keyInterval)
|
||||
|
||||
if keyShift {
|
||||
c.KeyEvent(KeyLeftShift, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
|
|
@ -30,6 +31,7 @@ type Config struct {
|
|||
common.HTTPConfig `mapstructure:",squash"`
|
||||
common.ISOConfig `mapstructure:",squash"`
|
||||
common.FloppyConfig `mapstructure:",squash"`
|
||||
bootcommand.VNCConfig `mapstructure:",squash"`
|
||||
vmwcommon.DriverConfig `mapstructure:",squash"`
|
||||
vmwcommon.OutputConfig `mapstructure:",squash"`
|
||||
vmwcommon.RunConfig `mapstructure:",squash"`
|
||||
|
|
@ -122,6 +124,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
errs = packer.MultiErrorAppend(errs, b.config.ToolsConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.VMXConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.VNCConfig.Prepare(&b.config.ctx)...)
|
||||
|
||||
if b.config.DiskName == "" {
|
||||
b.config.DiskName = "disk"
|
||||
|
|
@ -319,13 +322,13 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
Format: b.config.Format,
|
||||
},
|
||||
&vmwcommon.StepRun{
|
||||
BootWait: b.config.BootWait,
|
||||
DurationBeforeStop: 5 * time.Second,
|
||||
Headless: b.config.Headless,
|
||||
},
|
||||
&vmwcommon.StepTypeBootCommand{
|
||||
BootWait: b.config.BootWait,
|
||||
VNCEnabled: !b.config.DisableVNC,
|
||||
BootCommand: b.config.BootCommand,
|
||||
BootCommand: b.config.FlatBootCommand(),
|
||||
VMName: b.config.VMName,
|
||||
Ctx: b.config.ctx,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -12,10 +12,11 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/provisioner/shell"
|
||||
"github.com/hashicorp/packer/template"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var vmxTestBuilderConfig = map[string]string{
|
||||
|
|
|
|||
|
|
@ -87,13 +87,13 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
VNCDisablePassword: b.config.VNCDisablePassword,
|
||||
},
|
||||
&vmwcommon.StepRun{
|
||||
BootWait: b.config.BootWait,
|
||||
DurationBeforeStop: 5 * time.Second,
|
||||
Headless: b.config.Headless,
|
||||
},
|
||||
&vmwcommon.StepTypeBootCommand{
|
||||
BootWait: b.config.BootWait,
|
||||
VNCEnabled: !b.config.DisableVNC,
|
||||
BootCommand: b.config.BootCommand,
|
||||
BootCommand: b.config.FlatBootCommand(),
|
||||
VMName: b.config.VMName,
|
||||
Ctx: b.config.ctx,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/bootcommand"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
|
|
@ -16,6 +17,7 @@ type Config struct {
|
|||
common.PackerConfig `mapstructure:",squash"`
|
||||
common.HTTPConfig `mapstructure:",squash"`
|
||||
common.FloppyConfig `mapstructure:",squash"`
|
||||
bootcommand.VNCConfig `mapstructure:",squash"`
|
||||
vmwcommon.DriverConfig `mapstructure:",squash"`
|
||||
vmwcommon.OutputConfig `mapstructure:",squash"`
|
||||
vmwcommon.RunConfig `mapstructure:",squash"`
|
||||
|
|
@ -65,6 +67,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
errs = packer.MultiErrorAppend(errs, c.ToolsConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.VMXConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.FloppyConfig.Prepare(&c.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, c.VNCConfig.Prepare(&c.ctx)...)
|
||||
|
||||
if c.SourcePath == "" {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is blank, but is required"))
|
||||
|
|
|
|||
2072
common/bootcommand/boot_command.go
Normal file
2072
common/bootcommand/boot_command.go
Normal file
File diff suppressed because it is too large
Load diff
79
common/bootcommand/boot_command.pigeon
Normal file
79
common/bootcommand/boot_command.pigeon
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
{
|
||||
package bootcommand
|
||||
|
||||
}
|
||||
|
||||
Input <- expr:Expr EOF {
|
||||
return expr, nil
|
||||
}
|
||||
|
||||
Expr <- l:( Wait / CharToggle / Special / Literal)+ {
|
||||
return l, nil
|
||||
}
|
||||
|
||||
Wait = ExprStart "wait" duration:( Duration / Integer )? ExprEnd {
|
||||
var d time.Duration
|
||||
switch t := duration.(type) {
|
||||
case time.Duration:
|
||||
d = t
|
||||
case int64:
|
||||
d = time.Duration(t) * time.Second
|
||||
default:
|
||||
d = time.Second
|
||||
}
|
||||
return &waitExpression{d}, nil
|
||||
}
|
||||
|
||||
CharToggle = ExprStart lit:(Literal) t:(On / Off) ExprEnd {
|
||||
return &literal{lit.(*literal).s, t.(KeyAction)}, nil
|
||||
}
|
||||
|
||||
Special = ExprStart s:(SpecialKey) t:(On / Off)? ExprEnd {
|
||||
l := strings.ToLower(string(s.([]byte)))
|
||||
if t == nil {
|
||||
return &specialExpression{l, KeyPress}, nil
|
||||
}
|
||||
return &specialExpression{l, t.(KeyAction)}, nil
|
||||
}
|
||||
|
||||
Number = '-'? Integer ( '.' Digit+ )? {
|
||||
return string(c.text), nil
|
||||
}
|
||||
|
||||
Integer = '0' / NonZeroDigit Digit* {
|
||||
return strconv.ParseInt(string(c.text), 10, 64)
|
||||
}
|
||||
|
||||
Duration = ( Number TimeUnit )+ {
|
||||
return time.ParseDuration(string(c.text))
|
||||
}
|
||||
|
||||
On = "on"i {
|
||||
return KeyOn, nil
|
||||
}
|
||||
|
||||
Off = "off"i {
|
||||
return KeyOff, nil
|
||||
}
|
||||
|
||||
Literal = . {
|
||||
r, _ := utf8.DecodeRune(c.text)
|
||||
return &literal{r, KeyPress}, nil
|
||||
}
|
||||
|
||||
ExprEnd = ">"
|
||||
ExprStart = "<"
|
||||
SpecialKey = "bs"i / "del"i / "enter"i / "esc"i / "f10"i / "f11"i / "f12"i
|
||||
/ "f1"i / "f2"i / "f3"i / "f4"i / "f5"i / "f6"i / "f7"i / "f8"i / "f9"i
|
||||
/ "return"i / "tab"i / "up"i / "down"i / "spacebar"i / "insert"i / "home"i
|
||||
/ "end"i / "pageUp"i / "pageDown"i / "leftAlt"i / "leftCtrl"i / "leftShift"i
|
||||
/ "rightAlt"i / "rightCtrl"i / "rightShift"i / "leftSuper"i / "rightSuper"i
|
||||
/ "left"i / "right"i
|
||||
|
||||
NonZeroDigit = [1-9]
|
||||
Digit = [0-9]
|
||||
TimeUnit = ("ns" / "us" / "µs" / "ms" / "s" / "m" / "h")
|
||||
|
||||
_ "whitespace" <- [ \n\t\r]*
|
||||
|
||||
EOF <- !.
|
||||
154
common/bootcommand/boot_command_ast.go
Normal file
154
common/bootcommand/boot_command_ast.go
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
package bootcommand
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// KeysAction represents what we want to do with a key press.
|
||||
// It can take 3 states. We either want to:
|
||||
// * press the key once
|
||||
// * press and hold
|
||||
// * press and release
|
||||
type KeyAction int
|
||||
|
||||
const (
|
||||
KeyOn KeyAction = 1 << iota
|
||||
KeyOff
|
||||
KeyPress
|
||||
)
|
||||
|
||||
func (k KeyAction) String() string {
|
||||
switch k {
|
||||
case KeyOn:
|
||||
return "On"
|
||||
case KeyOff:
|
||||
return "Off"
|
||||
case KeyPress:
|
||||
return "Press"
|
||||
}
|
||||
panic(fmt.Sprintf("Unknwon KeyAction %d", k))
|
||||
}
|
||||
|
||||
type expression interface {
|
||||
// Do executes the expression
|
||||
Do(context.Context, BCDriver) error
|
||||
// Validate validates the expression without executing it
|
||||
Validate() error
|
||||
}
|
||||
|
||||
type expressionSequence []expression
|
||||
|
||||
// Do executes every expression in the sequence and then flushes remaining
|
||||
// scancodes.
|
||||
func (s expressionSequence) Do(ctx context.Context, b BCDriver) error {
|
||||
// validate should never fail here, since it should be called before
|
||||
// expressionSequence.Do. Only reason we don't panic is so we can clean up.
|
||||
if errs := s.Validate(); errs != nil {
|
||||
return fmt.Errorf("Found an invalid boot command. This is likely an error in Packer, so please open a ticket.")
|
||||
}
|
||||
|
||||
for _, exp := range s {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := exp.Do(ctx, b); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return b.Flush()
|
||||
}
|
||||
|
||||
// Validate tells us if every expression in the sequence is valid.
|
||||
func (s expressionSequence) Validate() (errs []error) {
|
||||
for _, exp := range s {
|
||||
if err := exp.Validate(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GenerateExpressionSequence generates a sequence of expressions from the
|
||||
// given command. This is the primary entry point to the boot command parser.
|
||||
func GenerateExpressionSequence(command string) (expressionSequence, error) {
|
||||
got, err := ParseReader("", strings.NewReader(command))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
seq := expressionSequence{}
|
||||
for _, exp := range got.([]interface{}) {
|
||||
seq = append(seq, exp.(expression))
|
||||
}
|
||||
return seq, nil
|
||||
}
|
||||
|
||||
type waitExpression struct {
|
||||
d time.Duration
|
||||
}
|
||||
|
||||
// Do waits the amount of time described by the expression. It is cancellable
|
||||
// through the context.
|
||||
func (w *waitExpression) Do(ctx context.Context, driver BCDriver) error {
|
||||
driver.Flush()
|
||||
log.Printf("[INFO] Waiting %s", w.d)
|
||||
select {
|
||||
case <-time.After(w.d):
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
// Validate returns an error if the time is <= 0
|
||||
func (w *waitExpression) Validate() error {
|
||||
if w.d <= 0 {
|
||||
return fmt.Errorf("Expecting a positive wait value. Got %s", w.d)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *waitExpression) String() string {
|
||||
return fmt.Sprintf("Wait<%s>", w.d)
|
||||
}
|
||||
|
||||
type specialExpression struct {
|
||||
s string
|
||||
action KeyAction
|
||||
}
|
||||
|
||||
// Do sends the special command to the driver, along with the key action.
|
||||
func (s *specialExpression) Do(ctx context.Context, driver BCDriver) error {
|
||||
return driver.SendSpecial(s.s, s.action)
|
||||
}
|
||||
|
||||
// Validate always passes
|
||||
func (s *specialExpression) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *specialExpression) String() string {
|
||||
return fmt.Sprintf("Spec-%s(%s)", s.action, s.s)
|
||||
}
|
||||
|
||||
type literal struct {
|
||||
s rune
|
||||
action KeyAction
|
||||
}
|
||||
|
||||
// Do sends the key to the driver, along with the key action.
|
||||
func (l *literal) Do(ctx context.Context, driver BCDriver) error {
|
||||
return driver.SendKey(l.s, l.action)
|
||||
}
|
||||
|
||||
// Validate always passes
|
||||
func (l *literal) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *literal) String() string {
|
||||
return fmt.Sprintf("LIT-%s(%s)", l.action, string(l.s))
|
||||
}
|
||||
140
common/bootcommand/boot_command_ast_test.go
Normal file
140
common/bootcommand/boot_command_ast_test.go
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
package bootcommand
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func toIfaceSlice(v interface{}) []interface{} {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
return v.([]interface{})
|
||||
}
|
||||
|
||||
func Test_parse(t *testing.T) {
|
||||
in := "<wait><wait20><wait3s><wait4m2ns>"
|
||||
in += "foo/bar > one 界"
|
||||
in += "<fon> b<fOff>"
|
||||
in += "<foo><f3><f12><spacebar><leftalt><rightshift><rightsuper>"
|
||||
expected := []string{
|
||||
"Wait<1s>",
|
||||
"Wait<20s>",
|
||||
"Wait<3s>",
|
||||
"Wait<4m0.000000002s>",
|
||||
"LIT-Press(f)",
|
||||
"LIT-Press(o)",
|
||||
"LIT-Press(o)",
|
||||
"LIT-Press(/)",
|
||||
"LIT-Press(b)",
|
||||
"LIT-Press(a)",
|
||||
"LIT-Press(r)",
|
||||
"LIT-Press( )",
|
||||
"LIT-Press(>)",
|
||||
"LIT-Press( )",
|
||||
"LIT-Press(o)",
|
||||
"LIT-Press(n)",
|
||||
"LIT-Press(e)",
|
||||
"LIT-Press( )",
|
||||
"LIT-Press(界)",
|
||||
"LIT-On(f)",
|
||||
"LIT-Press( )",
|
||||
"LIT-Press(b)",
|
||||
"LIT-Off(f)",
|
||||
"LIT-Press(<)",
|
||||
"LIT-Press(f)",
|
||||
"LIT-Press(o)",
|
||||
"LIT-Press(o)",
|
||||
"LIT-Press(>)",
|
||||
"Spec-Press(f3)",
|
||||
"Spec-Press(f12)",
|
||||
"Spec-Press(spacebar)",
|
||||
"Spec-Press(leftalt)",
|
||||
"Spec-Press(rightshift)",
|
||||
"Spec-Press(rightsuper)",
|
||||
}
|
||||
|
||||
got, err := ParseReader("", strings.NewReader(in))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
gL := toIfaceSlice(got)
|
||||
for i, g := range gL {
|
||||
assert.Equal(t, expected[i], fmt.Sprintf("%s", g))
|
||||
log.Printf("%s\n", g)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_special(t *testing.T) {
|
||||
var specials = []struct {
|
||||
in string
|
||||
out string
|
||||
}{
|
||||
{
|
||||
"<rightShift><rightshift><RIGHTSHIFT>",
|
||||
"Spec-Press(rightshift)",
|
||||
},
|
||||
{
|
||||
"<delon><delON><deLoN><DELON>",
|
||||
"Spec-On(del)",
|
||||
},
|
||||
{
|
||||
"<enteroff><enterOFF><eNtErOfF><ENTEROFF>",
|
||||
"Spec-Off(enter)",
|
||||
},
|
||||
}
|
||||
for _, tt := range specials {
|
||||
got, err := ParseReader("", strings.NewReader(tt.in))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
gL := toIfaceSlice(got)
|
||||
for _, g := range gL {
|
||||
assert.Equal(t, tt.out, g.(*specialExpression).String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_validation(t *testing.T) {
|
||||
var expressions = []struct {
|
||||
in string
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
"<wait1m>",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"<wait-1m>",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"<f1>",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"<",
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tt := range expressions {
|
||||
got, err := ParseReader("", strings.NewReader(tt.in))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
gL := toIfaceSlice(got)
|
||||
assert.Len(t, gL, 1)
|
||||
err = gL[0].(expression).Validate()
|
||||
if tt.valid {
|
||||
assert.NoError(t, err)
|
||||
} else {
|
||||
assert.Error(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
60
common/bootcommand/config.go
Normal file
60
common/bootcommand/config.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
package bootcommand
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
)
|
||||
|
||||
type BootConfig struct {
|
||||
RawBootWait string `mapstructure:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command"`
|
||||
|
||||
BootWait time.Duration ``
|
||||
}
|
||||
|
||||
type VNCConfig struct {
|
||||
BootConfig `mapstructure:",squash"`
|
||||
DisableVNC bool `mapstructure:"disable_vnc"`
|
||||
}
|
||||
|
||||
func (c *BootConfig) Prepare(ctx *interpolate.Context) (errs []error) {
|
||||
if c.RawBootWait == "" {
|
||||
c.RawBootWait = "10s"
|
||||
}
|
||||
if c.RawBootWait != "" {
|
||||
bw, err := time.ParseDuration(c.RawBootWait)
|
||||
if err != nil {
|
||||
errs = append(
|
||||
errs, fmt.Errorf("Failed parsing boot_wait: %s", err))
|
||||
} else {
|
||||
c.BootWait = bw
|
||||
}
|
||||
}
|
||||
|
||||
if c.BootCommand != nil {
|
||||
expSeq, err := GenerateExpressionSequence(c.FlatBootCommand())
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
} else if vErrs := expSeq.Validate(); vErrs != nil {
|
||||
errs = append(errs, vErrs...)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *BootConfig) FlatBootCommand() string {
|
||||
return strings.Join(c.BootCommand, "")
|
||||
}
|
||||
|
||||
func (c *VNCConfig) Prepare(ctx *interpolate.Context) (errs []error) {
|
||||
if len(c.BootCommand) > 0 && c.DisableVNC {
|
||||
errs = append(errs,
|
||||
fmt.Errorf("A boot command cannot be used when vnc is disabled."))
|
||||
}
|
||||
errs = append(errs, c.BootConfig.Prepare(ctx)...)
|
||||
return
|
||||
}
|
||||
65
common/bootcommand/config_test.go
Normal file
65
common/bootcommand/config_test.go
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package bootcommand
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
)
|
||||
|
||||
func TestConfigPrepare(t *testing.T) {
|
||||
var c *BootConfig
|
||||
|
||||
// Test a default boot_wait
|
||||
c = new(BootConfig)
|
||||
c.RawBootWait = ""
|
||||
errs := c.Prepare(&interpolate.Context{})
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("bad: %#v", errs)
|
||||
}
|
||||
if c.RawBootWait != "10s" {
|
||||
t.Fatalf("bad value: %s", c.RawBootWait)
|
||||
}
|
||||
|
||||
// Test with a bad boot_wait
|
||||
c = new(BootConfig)
|
||||
c.RawBootWait = "this is not good"
|
||||
errs = c.Prepare(&interpolate.Context{})
|
||||
if len(errs) == 0 {
|
||||
t.Fatal("should error")
|
||||
}
|
||||
|
||||
// Test with a good one
|
||||
c = new(BootConfig)
|
||||
c.RawBootWait = "5s"
|
||||
errs = c.Prepare(&interpolate.Context{})
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("bad: %#v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVNCConfigPrepare(t *testing.T) {
|
||||
var c *VNCConfig
|
||||
|
||||
// Test with a boot command
|
||||
c = new(VNCConfig)
|
||||
c.BootCommand = []string{"a", "b"}
|
||||
errs := c.Prepare(&interpolate.Context{})
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("bad: %#v", errs)
|
||||
}
|
||||
|
||||
// Test with disabled vnc
|
||||
c.DisableVNC = true
|
||||
errs = c.Prepare(&interpolate.Context{})
|
||||
if len(errs) == 0 {
|
||||
t.Fatal("should error")
|
||||
}
|
||||
|
||||
// Test no boot command with no vnc
|
||||
c = new(VNCConfig)
|
||||
c.DisableVNC = true
|
||||
errs = c.Prepare(&interpolate.Context{})
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("bad: %#v", errs)
|
||||
}
|
||||
}
|
||||
11
common/bootcommand/driver.go
Normal file
11
common/bootcommand/driver.go
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package bootcommand
|
||||
|
||||
const shiftedChars = "~!@#$%^&*()_+{}|:\"<>?"
|
||||
|
||||
// BCDriver is our access to the VM we want to type boot commands to
|
||||
type BCDriver interface {
|
||||
SendKey(key rune, action KeyAction) error
|
||||
SendSpecial(special string, action KeyAction) error
|
||||
// Flush will be called when we want to send scancodes to the VM.
|
||||
Flush() error
|
||||
}
|
||||
3
common/bootcommand/gen.go
Normal file
3
common/bootcommand/gen.go
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
package bootcommand
|
||||
|
||||
//go:generate pigeon -o boot_command.go boot_command.pigeon
|
||||
211
common/bootcommand/pc_xt_driver.go
Normal file
211
common/bootcommand/pc_xt_driver.go
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
package bootcommand
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
)
|
||||
|
||||
// SendCodeFunc will be called to send codes to the VM
|
||||
type SendCodeFunc func([]string) error
|
||||
type scMap map[string]*scancode
|
||||
|
||||
type pcXTDriver struct {
|
||||
interval time.Duration
|
||||
sendImpl SendCodeFunc
|
||||
specialMap scMap
|
||||
scancodeMap map[rune]byte
|
||||
buffer [][]string
|
||||
// TODO: set from env
|
||||
scancodeChunkSize int
|
||||
}
|
||||
|
||||
type scancode struct {
|
||||
make []string
|
||||
break_ []string
|
||||
}
|
||||
|
||||
func (sc *scancode) makeBreak() []string {
|
||||
return append(sc.make, sc.break_...)
|
||||
}
|
||||
|
||||
// NewPCXTDriver creates a new boot command driver for VMs that expect PC-XT
|
||||
// keyboard codes. `send` should send its argument to the VM. `chunkSize` should
|
||||
// be the maximum number of keyboard codes to send to `send` at one time.
|
||||
func NewPCXTDriver(send SendCodeFunc, chunkSize int) *pcXTDriver {
|
||||
// We delay (default 100ms) between each input event to allow for CPU or
|
||||
// network latency. See PackerKeyEnv for tuning.
|
||||
keyInterval := common.PackerKeyDefault
|
||||
if delay, err := time.ParseDuration(os.Getenv(common.PackerKeyEnv)); err == nil {
|
||||
keyInterval = delay
|
||||
}
|
||||
// Scancodes reference: https://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html
|
||||
// https://www.win.tue.nl/~aeb/linux/kbd/scancodes-10.html
|
||||
//
|
||||
// Scancodes are recorded here in pairs. The first entry represents
|
||||
// the key press and the second entry represents the key release and is
|
||||
// derived from the first by the addition of 0x80.
|
||||
sMap := make(scMap)
|
||||
sMap["bs"] = &scancode{[]string{"0e"}, []string{"8e"}}
|
||||
sMap["del"] = &scancode{[]string{"e0", "53"}, []string{"e0", "d3"}}
|
||||
sMap["down"] = &scancode{[]string{"e0", "50"}, []string{"e0", "d0"}}
|
||||
sMap["end"] = &scancode{[]string{"e0", "4f"}, []string{"e0", "cf"}}
|
||||
sMap["enter"] = &scancode{[]string{"1c"}, []string{"9c"}}
|
||||
sMap["esc"] = &scancode{[]string{"01"}, []string{"81"}}
|
||||
sMap["f1"] = &scancode{[]string{"3b"}, []string{"bb"}}
|
||||
sMap["f2"] = &scancode{[]string{"3c"}, []string{"bc"}}
|
||||
sMap["f3"] = &scancode{[]string{"3d"}, []string{"bd"}}
|
||||
sMap["f4"] = &scancode{[]string{"3e"}, []string{"be"}}
|
||||
sMap["f5"] = &scancode{[]string{"3f"}, []string{"bf"}}
|
||||
sMap["f6"] = &scancode{[]string{"40"}, []string{"c0"}}
|
||||
sMap["f7"] = &scancode{[]string{"41"}, []string{"c1"}}
|
||||
sMap["f8"] = &scancode{[]string{"42"}, []string{"c2"}}
|
||||
sMap["f9"] = &scancode{[]string{"43"}, []string{"c3"}}
|
||||
sMap["f10"] = &scancode{[]string{"44"}, []string{"c4"}}
|
||||
sMap["f11"] = &scancode{[]string{"57"}, []string{"d7"}}
|
||||
sMap["f12"] = &scancode{[]string{"58"}, []string{"d8"}}
|
||||
sMap["home"] = &scancode{[]string{"e0", "47"}, []string{"e0", "c7"}}
|
||||
sMap["insert"] = &scancode{[]string{"e0", "52"}, []string{"e0", "d2"}}
|
||||
sMap["left"] = &scancode{[]string{"e0", "4b"}, []string{"e0", "cb"}}
|
||||
sMap["leftalt"] = &scancode{[]string{"38"}, []string{"b8"}}
|
||||
sMap["leftctrl"] = &scancode{[]string{"1d"}, []string{"9d"}}
|
||||
sMap["leftshift"] = &scancode{[]string{"2a"}, []string{"aa"}}
|
||||
sMap["leftsuper"] = &scancode{[]string{"e0", "5b"}, []string{"e0", "db"}}
|
||||
sMap["menu"] = &scancode{[]string{"e0", "5d"}, []string{"e0", "dd"}}
|
||||
sMap["pagedown"] = &scancode{[]string{"e0", "51"}, []string{"e0", "d1"}}
|
||||
sMap["pageup"] = &scancode{[]string{"e0", "49"}, []string{"e0", "c9"}}
|
||||
sMap["return"] = &scancode{[]string{"1c"}, []string{"9c"}}
|
||||
sMap["right"] = &scancode{[]string{"e0", "4d"}, []string{"e0", "cd"}}
|
||||
sMap["rightalt"] = &scancode{[]string{"e0", "38"}, []string{"e0", "b8"}}
|
||||
sMap["rightctrl"] = &scancode{[]string{"e0", "1d"}, []string{"e0", "9d"}}
|
||||
sMap["rightshift"] = &scancode{[]string{"36"}, []string{"b6"}}
|
||||
sMap["rightsuper"] = &scancode{[]string{"e0", "5c"}, []string{"e0", "dc"}}
|
||||
sMap["spacebar"] = &scancode{[]string{"39"}, []string{"b9"}}
|
||||
sMap["tab"] = &scancode{[]string{"0f"}, []string{"8f"}}
|
||||
sMap["up"] = &scancode{[]string{"e0", "48"}, []string{"e0", "c8"}}
|
||||
|
||||
scancodeIndex := make(map[string]byte)
|
||||
scancodeIndex["1234567890-="] = 0x02
|
||||
scancodeIndex["!@#$%^&*()_+"] = 0x02
|
||||
scancodeIndex["qwertyuiop[]"] = 0x10
|
||||
scancodeIndex["QWERTYUIOP{}"] = 0x10
|
||||
scancodeIndex["asdfghjkl;'`"] = 0x1e
|
||||
scancodeIndex[`ASDFGHJKL:"~`] = 0x1e
|
||||
scancodeIndex[`\zxcvbnm,./`] = 0x2b
|
||||
scancodeIndex["|ZXCVBNM<>?"] = 0x2b
|
||||
scancodeIndex[" "] = 0x39
|
||||
|
||||
scancodeMap := make(map[rune]byte)
|
||||
for chars, start := range scancodeIndex {
|
||||
var i byte = 0
|
||||
for len(chars) > 0 {
|
||||
r, size := utf8.DecodeRuneInString(chars)
|
||||
chars = chars[size:]
|
||||
scancodeMap[r] = start + i
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
return &pcXTDriver{
|
||||
interval: keyInterval,
|
||||
sendImpl: send,
|
||||
specialMap: sMap,
|
||||
scancodeMap: scancodeMap,
|
||||
scancodeChunkSize: chunkSize,
|
||||
}
|
||||
}
|
||||
|
||||
// Flush send all scanecodes.
|
||||
func (d *pcXTDriver) Flush() error {
|
||||
defer func() {
|
||||
d.buffer = nil
|
||||
}()
|
||||
sc, err := chunkScanCodes(d.buffer, d.scancodeChunkSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, b := range sc {
|
||||
if err := d.sendImpl(b); err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(d.interval)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *pcXTDriver) SendKey(key rune, action KeyAction) error {
|
||||
keyShift := unicode.IsUpper(key) || strings.ContainsRune(shiftedChars, key)
|
||||
|
||||
var sc []string
|
||||
|
||||
if action&(KeyOn|KeyPress) != 0 {
|
||||
scInt := d.scancodeMap[key]
|
||||
if keyShift {
|
||||
sc = append(sc, "2a")
|
||||
}
|
||||
sc = append(sc, fmt.Sprintf("%02x", scInt))
|
||||
}
|
||||
|
||||
if action&(KeyOff|KeyPress) != 0 {
|
||||
scInt := d.scancodeMap[key] + 0x80
|
||||
if keyShift {
|
||||
sc = append(sc, "aa")
|
||||
}
|
||||
sc = append(sc, fmt.Sprintf("%02x", scInt))
|
||||
}
|
||||
|
||||
log.Printf("Sending char '%c', code '%s', shift %v",
|
||||
key, strings.Join(sc, ""), keyShift)
|
||||
|
||||
d.send(sc)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *pcXTDriver) SendSpecial(special string, action KeyAction) error {
|
||||
keyCode, ok := d.specialMap[special]
|
||||
if !ok {
|
||||
return fmt.Errorf("special %s not found.", special)
|
||||
}
|
||||
log.Printf("Special code '%s' '<%s>' found, replacing with: %v", action.String(), special, keyCode)
|
||||
|
||||
switch action {
|
||||
case KeyOn:
|
||||
d.send(keyCode.make)
|
||||
case KeyOff:
|
||||
d.send(keyCode.break_)
|
||||
case KeyPress:
|
||||
d.send(keyCode.makeBreak())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// send stores the codes in an internal buffer. Use Flush to send them.
|
||||
func (d *pcXTDriver) send(codes []string) {
|
||||
d.buffer = append(d.buffer, codes)
|
||||
}
|
||||
|
||||
func chunkScanCodes(sc [][]string, size int) (out [][]string, err error) {
|
||||
var running []string
|
||||
for _, codes := range sc {
|
||||
if size > 0 {
|
||||
if len(codes) > size {
|
||||
return nil, fmt.Errorf("chunkScanCodes: size cannot be smaller than sc width.")
|
||||
}
|
||||
if len(running)+len(codes) > size {
|
||||
out = append(out, running)
|
||||
running = nil
|
||||
}
|
||||
}
|
||||
running = append(running, codes...)
|
||||
}
|
||||
if running != nil {
|
||||
out = append(out, running)
|
||||
}
|
||||
return
|
||||
}
|
||||
123
common/bootcommand/pc_xt_driver_test.go
Normal file
123
common/bootcommand/pc_xt_driver_test.go
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
package bootcommand
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_chunkScanCodes(t *testing.T) {
|
||||
|
||||
var chunktests = []struct {
|
||||
size int
|
||||
in [][]string
|
||||
out [][]string
|
||||
}{
|
||||
{
|
||||
3,
|
||||
[][]string{
|
||||
{"a", "b"},
|
||||
{"c"},
|
||||
{"d"},
|
||||
{"e", "f"},
|
||||
{"g", "h"},
|
||||
{"i", "j"},
|
||||
{"k"},
|
||||
{"l", "m"},
|
||||
},
|
||||
[][]string{
|
||||
{"a", "b", "c"},
|
||||
{"d", "e", "f"},
|
||||
{"g", "h"},
|
||||
{"i", "j", "k"},
|
||||
{"l", "m"},
|
||||
},
|
||||
},
|
||||
{
|
||||
-1,
|
||||
[][]string{
|
||||
{"a", "b"},
|
||||
{"c"},
|
||||
{"d"},
|
||||
{"e", "f"},
|
||||
{"g", "h"},
|
||||
{"i", "j"},
|
||||
{"k"},
|
||||
{"l", "m"},
|
||||
},
|
||||
[][]string{
|
||||
{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range chunktests {
|
||||
out, err := chunkScanCodes(tt.in, tt.size)
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, tt.out, out, "expecting chunks of %d.", tt.size)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_chunkScanCodeError(t *testing.T) {
|
||||
// can't go from wider to thinner
|
||||
in := [][]string{
|
||||
{"a", "b", "c"},
|
||||
{"d", "e", "f"},
|
||||
{"g", "h"},
|
||||
}
|
||||
|
||||
_, err := chunkScanCodes(in, 2)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func Test_pcxtSpecialOnOff(t *testing.T) {
|
||||
in := "<rightShift><rightshiftoff><RIGHTSHIFTON>"
|
||||
expected := []string{"36", "b6", "b6", "36"}
|
||||
var codes []string
|
||||
sendCodes := func(c []string) error {
|
||||
codes = c
|
||||
return nil
|
||||
}
|
||||
d := NewPCXTDriver(sendCodes, -1)
|
||||
seq, err := GenerateExpressionSequence(in)
|
||||
assert.NoError(t, err)
|
||||
err = seq.Do(context.Background(), d)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, codes)
|
||||
}
|
||||
|
||||
func Test_pcxtSpecial(t *testing.T) {
|
||||
in := "<left>"
|
||||
expected := []string{"e0", "4b", "e0", "cb"}
|
||||
var codes []string
|
||||
sendCodes := func(c []string) error {
|
||||
codes = c
|
||||
return nil
|
||||
}
|
||||
d := NewPCXTDriver(sendCodes, -1)
|
||||
seq, err := GenerateExpressionSequence(in)
|
||||
assert.NoError(t, err)
|
||||
err = seq.Do(context.Background(), d)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, codes)
|
||||
}
|
||||
|
||||
func Test_flushes(t *testing.T) {
|
||||
in := "abc123<wait>098"
|
||||
expected := [][]string{
|
||||
{"1e", "9e", "30", "b0", "2e", "ae", "02", "82", "03", "83", "04", "84"},
|
||||
{"0b", "8b", "0a", "8a", "09", "89"},
|
||||
}
|
||||
var actual [][]string
|
||||
sendCodes := func(c []string) error {
|
||||
actual = append(actual, c)
|
||||
return nil
|
||||
}
|
||||
d := NewPCXTDriver(sendCodes, -1)
|
||||
seq, err := GenerateExpressionSequence(in)
|
||||
assert.NoError(t, err)
|
||||
err = seq.Do(context.Background(), d)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, actual)
|
||||
}
|
||||
147
common/bootcommand/vnc_driver.go
Normal file
147
common/bootcommand/vnc_driver.go
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
package bootcommand
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
)
|
||||
|
||||
const KeyLeftShift uint32 = 0xFFE1
|
||||
|
||||
type VNCKeyEvent interface {
|
||||
KeyEvent(uint32, bool) error
|
||||
}
|
||||
|
||||
type vncDriver struct {
|
||||
c VNCKeyEvent
|
||||
interval time.Duration
|
||||
specialMap map[string]uint32
|
||||
// keyEvent can set this error which will prevent it from continuing
|
||||
err error
|
||||
}
|
||||
|
||||
func NewVNCDriver(c VNCKeyEvent) *vncDriver {
|
||||
// We delay (default 100ms) between each key event to allow for CPU or
|
||||
// network latency. See PackerKeyEnv for tuning.
|
||||
keyInterval := common.PackerKeyDefault
|
||||
if delay, err := time.ParseDuration(os.Getenv(common.PackerKeyEnv)); err == nil {
|
||||
keyInterval = delay
|
||||
}
|
||||
|
||||
// Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h
|
||||
sMap := make(map[string]uint32)
|
||||
sMap["bs"] = 0xFF08
|
||||
sMap["del"] = 0xFFFF
|
||||
sMap["down"] = 0xFF54
|
||||
sMap["end"] = 0xFF57
|
||||
sMap["enter"] = 0xFF0D
|
||||
sMap["esc"] = 0xFF1B
|
||||
sMap["f1"] = 0xFFBE
|
||||
sMap["f2"] = 0xFFBF
|
||||
sMap["f3"] = 0xFFC0
|
||||
sMap["f4"] = 0xFFC1
|
||||
sMap["f5"] = 0xFFC2
|
||||
sMap["f6"] = 0xFFC3
|
||||
sMap["f7"] = 0xFFC4
|
||||
sMap["f8"] = 0xFFC5
|
||||
sMap["f9"] = 0xFFC6
|
||||
sMap["f10"] = 0xFFC7
|
||||
sMap["f11"] = 0xFFC8
|
||||
sMap["f12"] = 0xFFC9
|
||||
sMap["home"] = 0xFF50
|
||||
sMap["insert"] = 0xFF63
|
||||
sMap["left"] = 0xFF51
|
||||
sMap["leftalt"] = 0xFFE9
|
||||
sMap["leftctrl"] = 0xFFE3
|
||||
sMap["leftshift"] = 0xFFE1
|
||||
sMap["leftsuper"] = 0xFFEB
|
||||
sMap["menu"] = 0xFF67
|
||||
sMap["pagedown"] = 0xFF56
|
||||
sMap["pageup"] = 0xFF55
|
||||
sMap["return"] = 0xFF0D
|
||||
sMap["right"] = 0xFF53
|
||||
sMap["rightalt"] = 0xFFEA
|
||||
sMap["rightctrl"] = 0xFFE4
|
||||
sMap["rightshift"] = 0xFFE2
|
||||
sMap["rightsuper"] = 0xFFEC
|
||||
sMap["spacebar"] = 0x020
|
||||
sMap["tab"] = 0xFF09
|
||||
sMap["up"] = 0xFF52
|
||||
|
||||
return &vncDriver{
|
||||
c: c,
|
||||
interval: keyInterval,
|
||||
specialMap: sMap,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *vncDriver) keyEvent(k uint32, down bool) error {
|
||||
if d.err != nil {
|
||||
return nil
|
||||
}
|
||||
if err := d.c.KeyEvent(k, down); err != nil {
|
||||
d.err = err
|
||||
return err
|
||||
}
|
||||
time.Sleep(d.interval)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flush does nothing here
|
||||
func (d *vncDriver) Flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *vncDriver) SendKey(key rune, action KeyAction) error {
|
||||
keyShift := unicode.IsUpper(key) || strings.ContainsRune(shiftedChars, key)
|
||||
keyCode := uint32(key)
|
||||
log.Printf("Sending char '%c', code 0x%X, shift %v", key, keyCode, keyShift)
|
||||
|
||||
switch action {
|
||||
case KeyOn:
|
||||
if keyShift {
|
||||
d.keyEvent(KeyLeftShift, true)
|
||||
}
|
||||
d.keyEvent(keyCode, true)
|
||||
case KeyOff:
|
||||
if keyShift {
|
||||
d.keyEvent(KeyLeftShift, false)
|
||||
}
|
||||
d.keyEvent(keyCode, false)
|
||||
case KeyPress:
|
||||
if keyShift {
|
||||
d.keyEvent(KeyLeftShift, true)
|
||||
}
|
||||
d.keyEvent(keyCode, true)
|
||||
d.keyEvent(keyCode, false)
|
||||
if keyShift {
|
||||
d.keyEvent(KeyLeftShift, false)
|
||||
}
|
||||
}
|
||||
return d.err
|
||||
}
|
||||
|
||||
func (d *vncDriver) SendSpecial(special string, action KeyAction) error {
|
||||
keyCode, ok := d.specialMap[special]
|
||||
if !ok {
|
||||
return fmt.Errorf("special %s not found.", special)
|
||||
}
|
||||
log.Printf("Special code '<%s>' found, replacing with: 0x%X", special, keyCode)
|
||||
|
||||
switch action {
|
||||
case KeyOn:
|
||||
d.keyEvent(keyCode, true)
|
||||
case KeyOff:
|
||||
d.keyEvent(keyCode, false)
|
||||
case KeyPress:
|
||||
d.keyEvent(keyCode, true)
|
||||
d.keyEvent(keyCode, false)
|
||||
}
|
||||
|
||||
return d.err
|
||||
}
|
||||
39
common/bootcommand/vnc_driver_test.go
Normal file
39
common/bootcommand/vnc_driver_test.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package bootcommand
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type event struct {
|
||||
u uint32
|
||||
down bool
|
||||
}
|
||||
|
||||
type sender struct {
|
||||
e []event
|
||||
}
|
||||
|
||||
func (s *sender) KeyEvent(u uint32, down bool) error {
|
||||
s.e = append(s.e, event{u, down})
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_vncSpecialLookup(t *testing.T) {
|
||||
in := "<rightShift><rightshiftoff><RIGHTSHIFTON>"
|
||||
expected := []event{
|
||||
{0xFFE2, true},
|
||||
{0xFFE2, false},
|
||||
{0xFFE2, false},
|
||||
{0xFFE2, true},
|
||||
}
|
||||
s := &sender{}
|
||||
d := NewVNCDriver(s)
|
||||
seq, err := GenerateExpressionSequence(in)
|
||||
assert.NoError(t, err)
|
||||
err = seq.Do(context.Background(), d)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, s.e)
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
package fix
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
// FixerPowerShellEscapes removes the PowerShell escape character from user
|
||||
|
|
|
|||
1
main.go
1
main.go
|
|
@ -1,6 +1,7 @@
|
|||
// This is the main package for the `packer` application.
|
||||
|
||||
//go:generate go run ./scripts/generate-plugins.go
|
||||
//go:generate go generate ./common/bootcommand/...
|
||||
package main
|
||||
|
||||
import (
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
package vagrant
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
func TestGoogleProvider_impl(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ package vagrant
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type scalewayVagrantfileTemplate struct {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
package vsphere
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
func TestArtifact_ImplementsArtifact(t *testing.T) {
|
||||
|
|
|
|||
2
vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
2
vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
|
|
@ -1,6 +1,6 @@
|
|||
ISC License
|
||||
|
||||
Copyright (c) 2012-2013 Dave Collins <dave@davec.name>
|
||||
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
|
|
|
|||
2
vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
2
vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2015 Dave Collins <dave@davec.name>
|
||||
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
|
|
|
|||
38
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
38
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
|
|
@ -1,38 +0,0 @@
|
|||
// Copyright (c) 2015 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when the code is running on Google App Engine, compiled by GopherJS, or
|
||||
// "-tags safe" is added to the go build command line. The "disableunsafe"
|
||||
// tag is deprecated and thus should not be used.
|
||||
// +build js appengine safe disableunsafe
|
||||
|
||||
package spew
|
||||
|
||||
import "reflect"
|
||||
|
||||
const (
|
||||
// UnsafeDisabled is a build-time constant which specifies whether or
|
||||
// not access to the unsafe package is available.
|
||||
UnsafeDisabled = true
|
||||
)
|
||||
|
||||
// unsafeReflectValue typically converts the passed reflect.Value into a one
|
||||
// that bypasses the typical safety restrictions preventing access to
|
||||
// unaddressable and unexported data. However, doing this relies on access to
|
||||
// the unsafe package. This is a stub version which simply returns the passed
|
||||
// reflect.Value when the unsafe package is not available.
|
||||
func unsafeReflectValue(v reflect.Value) reflect.Value {
|
||||
return v
|
||||
}
|
||||
2
vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
2
vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
|
|
|||
11
vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
11
vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
|
@ -67,6 +67,15 @@ type ConfigState struct {
|
|||
// Google App Engine or with the "safe" build tag specified.
|
||||
DisablePointerMethods bool
|
||||
|
||||
// DisablePointerAddresses specifies whether to disable the printing of
|
||||
// pointer addresses. This is useful when diffing data structures in tests.
|
||||
DisablePointerAddresses bool
|
||||
|
||||
// DisableCapacities specifies whether to disable the printing of capacities
|
||||
// for arrays, slices, maps and channels. This is useful when diffing
|
||||
// data structures in tests.
|
||||
DisableCapacities bool
|
||||
|
||||
// ContinueOnMethod specifies whether or not recursion should continue once
|
||||
// a custom error or Stringer interface is invoked. The default, false,
|
||||
// means it will print the results of invoking the custom error or Stringer
|
||||
|
|
|
|||
11
vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
11
vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
|
@ -91,6 +91,15 @@ The following configuration options are available:
|
|||
which only accept pointer receivers from non-pointer variables.
|
||||
Pointer method invocation is enabled by default.
|
||||
|
||||
* DisablePointerAddresses
|
||||
DisablePointerAddresses specifies whether to disable the printing of
|
||||
pointer addresses. This is useful when diffing data structures in tests.
|
||||
|
||||
* DisableCapacities
|
||||
DisableCapacities specifies whether to disable the printing of
|
||||
capacities for arrays, slices, maps and channels. This is useful when
|
||||
diffing data structures in tests.
|
||||
|
||||
* ContinueOnMethod
|
||||
Enables recursion into types after invoking error and Stringer interface
|
||||
methods. Recursion after method invocation is disabled by default.
|
||||
|
|
|
|||
8
vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
8
vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
|
@ -129,7 +129,7 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
|
|||
d.w.Write(closeParenBytes)
|
||||
|
||||
// Display pointer information.
|
||||
if len(pointerChain) > 0 {
|
||||
if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 {
|
||||
d.w.Write(openParenBytes)
|
||||
for i, addr := range pointerChain {
|
||||
if i > 0 {
|
||||
|
|
@ -282,13 +282,13 @@ func (d *dumpState) dump(v reflect.Value) {
|
|||
case reflect.Map, reflect.String:
|
||||
valueLen = v.Len()
|
||||
}
|
||||
if valueLen != 0 || valueCap != 0 {
|
||||
if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 {
|
||||
d.w.Write(openParenBytes)
|
||||
if valueLen != 0 {
|
||||
d.w.Write(lenEqualsBytes)
|
||||
printInt(d.w, int64(valueLen), 10)
|
||||
}
|
||||
if valueCap != 0 {
|
||||
if !d.cs.DisableCapacities && valueCap != 0 {
|
||||
if valueLen != 0 {
|
||||
d.w.Write(spaceBytes)
|
||||
}
|
||||
|
|
|
|||
2
vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
2
vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
|
|
|||
2
vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
2
vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
|
|
|||
27
vendor/github.com/stretchr/objx/Gopkg.lock
generated
vendored
Normal file
27
vendor/github.com/stretchr/objx/Gopkg.lock
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = ["assert"]
|
||||
revision = "b91bfb9ebec76498946beb6af7c0230c7cc7ba6c"
|
||||
version = "v1.2.0"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "50e2495ec1af6e2f7ffb2f3551e4300d30357d7c7fe38ff6056469fa9cfb3673"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
3
vendor/github.com/stretchr/objx/Gopkg.toml
generated
vendored
Normal file
3
vendor/github.com/stretchr/objx/Gopkg.toml
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "~1.2.0"
|
||||
22
vendor/github.com/stretchr/objx/LICENSE
generated
vendored
Normal file
22
vendor/github.com/stretchr/objx/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License
|
||||
|
||||
Copyright (c) 2014 Stretchr, Inc.
|
||||
Copyright (c) 2017-2018 objx contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
78
vendor/github.com/stretchr/objx/README.md
generated
vendored
Normal file
78
vendor/github.com/stretchr/objx/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
# Objx
|
||||
[](https://travis-ci.org/stretchr/objx)
|
||||
[](https://goreportcard.com/report/github.com/stretchr/objx)
|
||||
[](https://sourcegraph.com/github.com/stretchr/objx)
|
||||
[](https://godoc.org/github.com/stretchr/objx)
|
||||
|
||||
Objx - Go package for dealing with maps, slices, JSON and other data.
|
||||
|
||||
Get started:
|
||||
|
||||
- Install Objx with [one line of code](#installation), or [update it with another](#staying-up-to-date)
|
||||
- Check out the API Documentation http://godoc.org/github.com/stretchr/objx
|
||||
|
||||
## Overview
|
||||
Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes a powerful `Get` method (among others) that allows you to easily and quickly get access to data within the map, without having to worry too much about type assertions, missing data, default values etc.
|
||||
|
||||
### Pattern
|
||||
Objx uses a preditable pattern to make access data from within `map[string]interface{}` easy. Call one of the `objx.` functions to create your `objx.Map` to get going:
|
||||
|
||||
m, err := objx.FromJSON(json)
|
||||
|
||||
NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, the rest will be optimistic and try to figure things out without panicking.
|
||||
|
||||
Use `Get` to access the value you're interested in. You can use dot and array
|
||||
notation too:
|
||||
|
||||
m.Get("places[0].latlng")
|
||||
|
||||
Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type.
|
||||
|
||||
if m.Get("code").IsStr() { // Your code... }
|
||||
|
||||
Or you can just assume the type, and use one of the strong type methods to extract the real value:
|
||||
|
||||
m.Get("code").Int()
|
||||
|
||||
If there's no value there (or if it's the wrong type) then a default value will be returned, or you can be explicit about the default value.
|
||||
|
||||
Get("code").Int(-1)
|
||||
|
||||
If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating, manipulating and selecting that data. You can find out more by exploring the index below.
|
||||
|
||||
### Reading data
|
||||
A simple example of how to use Objx:
|
||||
|
||||
// Use MustFromJSON to make an objx.Map from some JSON
|
||||
m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`)
|
||||
|
||||
// Get the details
|
||||
name := m.Get("name").Str()
|
||||
age := m.Get("age").Int()
|
||||
|
||||
// Get their nickname (or use their name if they don't have one)
|
||||
nickname := m.Get("nickname").Str(name)
|
||||
|
||||
### Ranging
|
||||
Since `objx.Map` is a `map[string]interface{}` you can treat it as such. For example, to `range` the data, do what you would expect:
|
||||
|
||||
m := objx.MustFromJSON(json)
|
||||
for key, value := range m {
|
||||
// Your code...
|
||||
}
|
||||
|
||||
## Installation
|
||||
To install Objx, use go get:
|
||||
|
||||
go get github.com/stretchr/objx
|
||||
|
||||
### Staying up to date
|
||||
To update Objx to the latest version, run:
|
||||
|
||||
go get -u github.com/stretchr/objx
|
||||
|
||||
### Supported go versions
|
||||
We support the lastest two major Go versions, which are 1.8 and 1.9 at the moment.
|
||||
|
||||
## Contributing
|
||||
Please feel free to submit issues, fork the repository and send pull requests!
|
||||
26
vendor/github.com/stretchr/objx/Taskfile.yml
generated
vendored
Normal file
26
vendor/github.com/stretchr/objx/Taskfile.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
default:
|
||||
deps: [test]
|
||||
|
||||
dl-deps:
|
||||
desc: Downloads cli dependencies
|
||||
cmds:
|
||||
- go get -u github.com/golang/lint/golint
|
||||
- go get -u github.com/golang/dep/cmd/dep
|
||||
|
||||
update-deps:
|
||||
desc: Updates dependencies
|
||||
cmds:
|
||||
- dep ensure
|
||||
- dep ensure -update
|
||||
- dep prune
|
||||
|
||||
lint:
|
||||
desc: Runs golint
|
||||
cmds:
|
||||
- golint $(ls *.go | grep -v "doc.go")
|
||||
silent: true
|
||||
|
||||
test:
|
||||
desc: Runs go tests
|
||||
cmds:
|
||||
- go test -race .
|
||||
171
vendor/github.com/stretchr/objx/accessors.go
generated
vendored
Normal file
171
vendor/github.com/stretchr/objx/accessors.go
generated
vendored
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
package objx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// arrayAccesRegexString is the regex used to extract the array number
|
||||
// from the access path
|
||||
const arrayAccesRegexString = `^(.+)\[([0-9]+)\]$`
|
||||
|
||||
// arrayAccesRegex is the compiled arrayAccesRegexString
|
||||
var arrayAccesRegex = regexp.MustCompile(arrayAccesRegexString)
|
||||
|
||||
// Get gets the value using the specified selector and
|
||||
// returns it inside a new Obj object.
|
||||
//
|
||||
// If it cannot find the value, Get will return a nil
|
||||
// value inside an instance of Obj.
|
||||
//
|
||||
// Get can only operate directly on map[string]interface{} and []interface.
|
||||
//
|
||||
// Example
|
||||
//
|
||||
// To access the title of the third chapter of the second book, do:
|
||||
//
|
||||
// o.Get("books[1].chapters[2].title")
|
||||
func (m Map) Get(selector string) *Value {
|
||||
rawObj := access(m, selector, nil, false, false)
|
||||
return &Value{data: rawObj}
|
||||
}
|
||||
|
||||
// Set sets the value using the specified selector and
|
||||
// returns the object on which Set was called.
|
||||
//
|
||||
// Set can only operate directly on map[string]interface{} and []interface
|
||||
//
|
||||
// Example
|
||||
//
|
||||
// To set the title of the third chapter of the second book, do:
|
||||
//
|
||||
// o.Set("books[1].chapters[2].title","Time to Go")
|
||||
func (m Map) Set(selector string, value interface{}) Map {
|
||||
access(m, selector, value, true, false)
|
||||
return m
|
||||
}
|
||||
|
||||
// access accesses the object using the selector and performs the
|
||||
// appropriate action.
|
||||
func access(current, selector, value interface{}, isSet, panics bool) interface{} {
|
||||
|
||||
switch selector.(type) {
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||
|
||||
if array, ok := current.([]interface{}); ok {
|
||||
index := intFromInterface(selector)
|
||||
|
||||
if index >= len(array) {
|
||||
if panics {
|
||||
panic(fmt.Sprintf("objx: Index %d is out of range. Slice only contains %d items.", index, len(array)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return array[index]
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
case string:
|
||||
|
||||
selStr := selector.(string)
|
||||
selSegs := strings.SplitN(selStr, PathSeparator, 2)
|
||||
thisSel := selSegs[0]
|
||||
index := -1
|
||||
var err error
|
||||
|
||||
if strings.Contains(thisSel, "[") {
|
||||
arrayMatches := arrayAccesRegex.FindStringSubmatch(thisSel)
|
||||
|
||||
if len(arrayMatches) > 0 {
|
||||
// Get the key into the map
|
||||
thisSel = arrayMatches[1]
|
||||
|
||||
// Get the index into the array at the key
|
||||
index, err = strconv.Atoi(arrayMatches[2])
|
||||
|
||||
if err != nil {
|
||||
// This should never happen. If it does, something has gone
|
||||
// seriously wrong. Panic.
|
||||
panic("objx: Array index is not an integer. Must use array[int].")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if curMap, ok := current.(Map); ok {
|
||||
current = map[string]interface{}(curMap)
|
||||
}
|
||||
|
||||
// get the object in question
|
||||
switch current.(type) {
|
||||
case map[string]interface{}:
|
||||
curMSI := current.(map[string]interface{})
|
||||
if len(selSegs) <= 1 && isSet {
|
||||
curMSI[thisSel] = value
|
||||
return nil
|
||||
}
|
||||
current = curMSI[thisSel]
|
||||
default:
|
||||
current = nil
|
||||
}
|
||||
|
||||
if current == nil && panics {
|
||||
panic(fmt.Sprintf("objx: '%v' invalid on object.", selector))
|
||||
}
|
||||
|
||||
// do we need to access the item of an array?
|
||||
if index > -1 {
|
||||
if array, ok := current.([]interface{}); ok {
|
||||
if index < len(array) {
|
||||
current = array[index]
|
||||
} else {
|
||||
if panics {
|
||||
panic(fmt.Sprintf("objx: Index %d is out of range. Slice only contains %d items.", index, len(array)))
|
||||
}
|
||||
current = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(selSegs) > 1 {
|
||||
current = access(current, selSegs[1], value, isSet, panics)
|
||||
}
|
||||
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
// intFromInterface converts an interface object to the largest
|
||||
// representation of an unsigned integer using a type switch and
|
||||
// assertions
|
||||
func intFromInterface(selector interface{}) int {
|
||||
var value int
|
||||
switch selector.(type) {
|
||||
case int:
|
||||
value = selector.(int)
|
||||
case int8:
|
||||
value = int(selector.(int8))
|
||||
case int16:
|
||||
value = int(selector.(int16))
|
||||
case int32:
|
||||
value = int(selector.(int32))
|
||||
case int64:
|
||||
value = int(selector.(int64))
|
||||
case uint:
|
||||
value = int(selector.(uint))
|
||||
case uint8:
|
||||
value = int(selector.(uint8))
|
||||
case uint16:
|
||||
value = int(selector.(uint16))
|
||||
case uint32:
|
||||
value = int(selector.(uint32))
|
||||
case uint64:
|
||||
value = int(selector.(uint64))
|
||||
default:
|
||||
panic("objx: array access argument is not an integer type (this should never happen)")
|
||||
}
|
||||
return value
|
||||
}
|
||||
13
vendor/github.com/stretchr/objx/constants.go
generated
vendored
Normal file
13
vendor/github.com/stretchr/objx/constants.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package objx
|
||||
|
||||
const (
|
||||
// PathSeparator is the character used to separate the elements
|
||||
// of the keypath.
|
||||
//
|
||||
// For example, `location.address.city`
|
||||
PathSeparator string = "."
|
||||
|
||||
// SignatureSeparator is the character that is used to
|
||||
// separate the Base64 string from the security signature.
|
||||
SignatureSeparator = "_"
|
||||
)
|
||||
108
vendor/github.com/stretchr/objx/conversions.go
generated
vendored
Normal file
108
vendor/github.com/stretchr/objx/conversions.go
generated
vendored
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
package objx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// JSON converts the contained object to a JSON string
|
||||
// representation
|
||||
func (m Map) JSON() (string, error) {
|
||||
result, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
err = errors.New("objx: JSON encode failed with: " + err.Error())
|
||||
}
|
||||
return string(result), err
|
||||
}
|
||||
|
||||
// MustJSON converts the contained object to a JSON string
|
||||
// representation and panics if there is an error
|
||||
func (m Map) MustJSON() string {
|
||||
result, err := m.JSON()
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Base64 converts the contained object to a Base64 string
|
||||
// representation of the JSON string representation
|
||||
func (m Map) Base64() (string, error) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
jsonData, err := m.JSON()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
encoder := base64.NewEncoder(base64.StdEncoding, &buf)
|
||||
_, err = encoder.Write([]byte(jsonData))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_ = encoder.Close()
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
// MustBase64 converts the contained object to a Base64 string
|
||||
// representation of the JSON string representation and panics
|
||||
// if there is an error
|
||||
func (m Map) MustBase64() string {
|
||||
result, err := m.Base64()
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// SignedBase64 converts the contained object to a Base64 string
|
||||
// representation of the JSON string representation and signs it
|
||||
// using the provided key.
|
||||
func (m Map) SignedBase64(key string) (string, error) {
|
||||
base64, err := m.Base64()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
sig := HashWithKey(base64, key)
|
||||
return base64 + SignatureSeparator + sig, nil
|
||||
}
|
||||
|
||||
// MustSignedBase64 converts the contained object to a Base64 string
|
||||
// representation of the JSON string representation and signs it
|
||||
// using the provided key and panics if there is an error
|
||||
func (m Map) MustSignedBase64(key string) string {
|
||||
result, err := m.SignedBase64(key)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/*
|
||||
URL Query
|
||||
------------------------------------------------
|
||||
*/
|
||||
|
||||
// URLValues creates a url.Values object from an Obj. This
|
||||
// function requires that the wrapped object be a map[string]interface{}
|
||||
func (m Map) URLValues() url.Values {
|
||||
vals := make(url.Values)
|
||||
for k, v := range m {
|
||||
//TODO: can this be done without sprintf?
|
||||
vals.Set(k, fmt.Sprintf("%v", v))
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
// URLQuery gets an encoded URL query representing the given
|
||||
// Obj. This function requires that the wrapped object be a
|
||||
// map[string]interface{}
|
||||
func (m Map) URLQuery() (string, error) {
|
||||
return m.URLValues().Encode(), nil
|
||||
}
|
||||
66
vendor/github.com/stretchr/objx/doc.go
generated
vendored
Normal file
66
vendor/github.com/stretchr/objx/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
Objx - Go package for dealing with maps, slices, JSON and other data.
|
||||
|
||||
Overview
|
||||
|
||||
Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes
|
||||
a powerful `Get` method (among others) that allows you to easily and quickly get
|
||||
access to data within the map, without having to worry too much about type assertions,
|
||||
missing data, default values etc.
|
||||
|
||||
Pattern
|
||||
|
||||
Objx uses a preditable pattern to make access data from within `map[string]interface{}` easy.
|
||||
Call one of the `objx.` functions to create your `objx.Map` to get going:
|
||||
|
||||
m, err := objx.FromJSON(json)
|
||||
|
||||
NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong,
|
||||
the rest will be optimistic and try to figure things out without panicking.
|
||||
|
||||
Use `Get` to access the value you're interested in. You can use dot and array
|
||||
notation too:
|
||||
|
||||
m.Get("places[0].latlng")
|
||||
|
||||
Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type.
|
||||
|
||||
if m.Get("code").IsStr() { // Your code... }
|
||||
|
||||
Or you can just assume the type, and use one of the strong type methods to extract the real value:
|
||||
|
||||
m.Get("code").Int()
|
||||
|
||||
If there's no value there (or if it's the wrong type) then a default value will be returned,
|
||||
or you can be explicit about the default value.
|
||||
|
||||
Get("code").Int(-1)
|
||||
|
||||
If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating,
|
||||
manipulating and selecting that data. You can find out more by exploring the index below.
|
||||
|
||||
Reading data
|
||||
|
||||
A simple example of how to use Objx:
|
||||
|
||||
// Use MustFromJSON to make an objx.Map from some JSON
|
||||
m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`)
|
||||
|
||||
// Get the details
|
||||
name := m.Get("name").Str()
|
||||
age := m.Get("age").Int()
|
||||
|
||||
// Get their nickname (or use their name if they don't have one)
|
||||
nickname := m.Get("nickname").Str(name)
|
||||
|
||||
Ranging
|
||||
|
||||
Since `objx.Map` is a `map[string]interface{}` you can treat it as such.
|
||||
For example, to `range` the data, do what you would expect:
|
||||
|
||||
m := objx.MustFromJSON(json)
|
||||
for key, value := range m {
|
||||
// Your code...
|
||||
}
|
||||
*/
|
||||
package objx
|
||||
193
vendor/github.com/stretchr/objx/map.go
generated
vendored
Normal file
193
vendor/github.com/stretchr/objx/map.go
generated
vendored
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
package objx
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MSIConvertable is an interface that defines methods for converting your
|
||||
// custom types to a map[string]interface{} representation.
|
||||
type MSIConvertable interface {
|
||||
// MSI gets a map[string]interface{} (msi) representing the
|
||||
// object.
|
||||
MSI() map[string]interface{}
|
||||
}
|
||||
|
||||
// Map provides extended functionality for working with
|
||||
// untyped data, in particular map[string]interface (msi).
|
||||
type Map map[string]interface{}
|
||||
|
||||
// Value returns the internal value instance
|
||||
func (m Map) Value() *Value {
|
||||
return &Value{data: m}
|
||||
}
|
||||
|
||||
// Nil represents a nil Map.
|
||||
var Nil = New(nil)
|
||||
|
||||
// New creates a new Map containing the map[string]interface{} in the data argument.
|
||||
// If the data argument is not a map[string]interface, New attempts to call the
|
||||
// MSI() method on the MSIConvertable interface to create one.
|
||||
func New(data interface{}) Map {
|
||||
if _, ok := data.(map[string]interface{}); !ok {
|
||||
if converter, ok := data.(MSIConvertable); ok {
|
||||
data = converter.MSI()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return Map(data.(map[string]interface{}))
|
||||
}
|
||||
|
||||
// MSI creates a map[string]interface{} and puts it inside a new Map.
|
||||
//
|
||||
// The arguments follow a key, value pattern.
|
||||
//
|
||||
// Panics
|
||||
//
|
||||
// Panics if any key argument is non-string or if there are an odd number of arguments.
|
||||
//
|
||||
// Example
|
||||
//
|
||||
// To easily create Maps:
|
||||
//
|
||||
// m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true))
|
||||
//
|
||||
// // creates an Map equivalent to
|
||||
// m := objx.New(map[string]interface{}{"name": "Mat", "age": 29, "subobj": map[string]interface{}{"active": true}})
|
||||
func MSI(keyAndValuePairs ...interface{}) Map {
|
||||
newMap := make(map[string]interface{})
|
||||
keyAndValuePairsLen := len(keyAndValuePairs)
|
||||
if keyAndValuePairsLen%2 != 0 {
|
||||
panic("objx: MSI must have an even number of arguments following the 'key, value' pattern.")
|
||||
}
|
||||
|
||||
for i := 0; i < keyAndValuePairsLen; i = i + 2 {
|
||||
key := keyAndValuePairs[i]
|
||||
value := keyAndValuePairs[i+1]
|
||||
|
||||
// make sure the key is a string
|
||||
keyString, keyStringOK := key.(string)
|
||||
if !keyStringOK {
|
||||
panic("objx: MSI must follow 'string, interface{}' pattern. " + keyString + " is not a valid key.")
|
||||
}
|
||||
newMap[keyString] = value
|
||||
}
|
||||
return New(newMap)
|
||||
}
|
||||
|
||||
// ****** Conversion Constructors
|
||||
|
||||
// MustFromJSON creates a new Map containing the data specified in the
|
||||
// jsonString.
|
||||
//
|
||||
// Panics if the JSON is invalid.
|
||||
func MustFromJSON(jsonString string) Map {
|
||||
o, err := FromJSON(jsonString)
|
||||
if err != nil {
|
||||
panic("objx: MustFromJSON failed with error: " + err.Error())
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// FromJSON creates a new Map containing the data specified in the
|
||||
// jsonString.
|
||||
//
|
||||
// Returns an error if the JSON is invalid.
|
||||
func FromJSON(jsonString string) (Map, error) {
|
||||
var data interface{}
|
||||
err := json.Unmarshal([]byte(jsonString), &data)
|
||||
if err != nil {
|
||||
return Nil, err
|
||||
}
|
||||
return New(data), nil
|
||||
}
|
||||
|
||||
// FromBase64 creates a new Obj containing the data specified
|
||||
// in the Base64 string.
|
||||
//
|
||||
// The string is an encoded JSON string returned by Base64
|
||||
func FromBase64(base64String string) (Map, error) {
|
||||
decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64String))
|
||||
decoded, err := ioutil.ReadAll(decoder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return FromJSON(string(decoded))
|
||||
}
|
||||
|
||||
// MustFromBase64 creates a new Obj containing the data specified
|
||||
// in the Base64 string and panics if there is an error.
|
||||
//
|
||||
// The string is an encoded JSON string returned by Base64
|
||||
func MustFromBase64(base64String string) Map {
|
||||
result, err := FromBase64(base64String)
|
||||
if err != nil {
|
||||
panic("objx: MustFromBase64 failed with error: " + err.Error())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// FromSignedBase64 creates a new Obj containing the data specified
|
||||
// in the Base64 string.
|
||||
//
|
||||
// The string is an encoded JSON string returned by SignedBase64
|
||||
func FromSignedBase64(base64String, key string) (Map, error) {
|
||||
parts := strings.Split(base64String, SignatureSeparator)
|
||||
if len(parts) != 2 {
|
||||
return nil, errors.New("objx: Signed base64 string is malformed")
|
||||
}
|
||||
|
||||
sig := HashWithKey(parts[0], key)
|
||||
if parts[1] != sig {
|
||||
return nil, errors.New("objx: Signature for base64 data does not match")
|
||||
}
|
||||
return FromBase64(parts[0])
|
||||
}
|
||||
|
||||
// MustFromSignedBase64 creates a new Obj containing the data specified
|
||||
// in the Base64 string and panics if there is an error.
|
||||
//
|
||||
// The string is an encoded JSON string returned by Base64
|
||||
func MustFromSignedBase64(base64String, key string) Map {
|
||||
result, err := FromSignedBase64(base64String, key)
|
||||
if err != nil {
|
||||
panic("objx: MustFromSignedBase64 failed with error: " + err.Error())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// FromURLQuery generates a new Obj by parsing the specified
|
||||
// query.
|
||||
//
|
||||
// For queries with multiple values, the first value is selected.
|
||||
func FromURLQuery(query string) (Map, error) {
|
||||
vals, err := url.ParseQuery(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := make(map[string]interface{})
|
||||
for k, vals := range vals {
|
||||
m[k] = vals[0]
|
||||
}
|
||||
return New(m), nil
|
||||
}
|
||||
|
||||
// MustFromURLQuery generates a new Obj by parsing the specified
|
||||
// query.
|
||||
//
|
||||
// For queries with multiple values, the first value is selected.
|
||||
//
|
||||
// Panics if it encounters an error
|
||||
func MustFromURLQuery(query string) Map {
|
||||
o, err := FromURLQuery(query)
|
||||
if err != nil {
|
||||
panic("objx: MustFromURLQuery failed with error: " + err.Error())
|
||||
}
|
||||
return o
|
||||
}
|
||||
74
vendor/github.com/stretchr/objx/mutations.go
generated
vendored
Normal file
74
vendor/github.com/stretchr/objx/mutations.go
generated
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package objx
|
||||
|
||||
// Exclude returns a new Map with the keys in the specified []string
|
||||
// excluded.
|
||||
func (m Map) Exclude(exclude []string) Map {
|
||||
excluded := make(Map)
|
||||
for k, v := range m {
|
||||
var shouldInclude = true
|
||||
for _, toExclude := range exclude {
|
||||
if k == toExclude {
|
||||
shouldInclude = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if shouldInclude {
|
||||
excluded[k] = v
|
||||
}
|
||||
}
|
||||
return excluded
|
||||
}
|
||||
|
||||
// Copy creates a shallow copy of the Obj.
|
||||
func (m Map) Copy() Map {
|
||||
copied := make(map[string]interface{})
|
||||
for k, v := range m {
|
||||
copied[k] = v
|
||||
}
|
||||
return New(copied)
|
||||
}
|
||||
|
||||
// Merge blends the specified map with a copy of this map and returns the result.
|
||||
//
|
||||
// Keys that appear in both will be selected from the specified map.
|
||||
// This method requires that the wrapped object be a map[string]interface{}
|
||||
func (m Map) Merge(merge Map) Map {
|
||||
return m.Copy().MergeHere(merge)
|
||||
}
|
||||
|
||||
// MergeHere blends the specified map with this map and returns the current map.
|
||||
//
|
||||
// Keys that appear in both will be selected from the specified map. The original map
|
||||
// will be modified. This method requires that
|
||||
// the wrapped object be a map[string]interface{}
|
||||
func (m Map) MergeHere(merge Map) Map {
|
||||
for k, v := range merge {
|
||||
m[k] = v
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Transform builds a new Obj giving the transformer a chance
|
||||
// to change the keys and values as it goes. This method requires that
|
||||
// the wrapped object be a map[string]interface{}
|
||||
func (m Map) Transform(transformer func(key string, value interface{}) (string, interface{})) Map {
|
||||
newMap := make(map[string]interface{})
|
||||
for k, v := range m {
|
||||
modifiedKey, modifiedVal := transformer(k, v)
|
||||
newMap[modifiedKey] = modifiedVal
|
||||
}
|
||||
return New(newMap)
|
||||
}
|
||||
|
||||
// TransformKeys builds a new map using the specified key mapping.
|
||||
//
|
||||
// Unspecified keys will be unaltered.
|
||||
// This method requires that the wrapped object be a map[string]interface{}
|
||||
func (m Map) TransformKeys(mapping map[string]string) Map {
|
||||
return m.Transform(func(key string, value interface{}) (string, interface{}) {
|
||||
if newKey, ok := mapping[key]; ok {
|
||||
return newKey, value
|
||||
}
|
||||
return key, value
|
||||
})
|
||||
}
|
||||
17
vendor/github.com/stretchr/objx/security.go
generated
vendored
Normal file
17
vendor/github.com/stretchr/objx/security.go
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package objx
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// HashWithKey hashes the specified string using the security
|
||||
// key.
|
||||
func HashWithKey(data, key string) string {
|
||||
hash := sha1.New()
|
||||
_, err := hash.Write([]byte(data + ":" + key))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return hex.EncodeToString(hash.Sum(nil))
|
||||
}
|
||||
17
vendor/github.com/stretchr/objx/tests.go
generated
vendored
Normal file
17
vendor/github.com/stretchr/objx/tests.go
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package objx
|
||||
|
||||
// Has gets whether there is something at the specified selector
|
||||
// or not.
|
||||
//
|
||||
// If m is nil, Has will always return false.
|
||||
func (m Map) Has(selector string) bool {
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
return !m.Get(selector).IsNil()
|
||||
}
|
||||
|
||||
// IsNil gets whether the data is nil or not.
|
||||
func (v *Value) IsNil() bool {
|
||||
return v == nil || v.data == nil
|
||||
}
|
||||
2501
vendor/github.com/stretchr/objx/type_specific_codegen.go
generated
vendored
Normal file
2501
vendor/github.com/stretchr/objx/type_specific_codegen.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
56
vendor/github.com/stretchr/objx/value.go
generated
vendored
Normal file
56
vendor/github.com/stretchr/objx/value.go
generated
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
package objx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Value provides methods for extracting interface{} data in various
|
||||
// types.
|
||||
type Value struct {
|
||||
// data contains the raw data being managed by this Value
|
||||
data interface{}
|
||||
}
|
||||
|
||||
// Data returns the raw data contained by this Value
|
||||
func (v *Value) Data() interface{} {
|
||||
return v.data
|
||||
}
|
||||
|
||||
// String returns the value always as a string
|
||||
func (v *Value) String() string {
|
||||
switch {
|
||||
case v.IsStr():
|
||||
return v.Str()
|
||||
case v.IsBool():
|
||||
return strconv.FormatBool(v.Bool())
|
||||
case v.IsFloat32():
|
||||
return strconv.FormatFloat(float64(v.Float32()), 'f', -1, 32)
|
||||
case v.IsFloat64():
|
||||
return strconv.FormatFloat(v.Float64(), 'f', -1, 64)
|
||||
case v.IsInt():
|
||||
return strconv.FormatInt(int64(v.Int()), 10)
|
||||
case v.IsInt():
|
||||
return strconv.FormatInt(int64(v.Int()), 10)
|
||||
case v.IsInt8():
|
||||
return strconv.FormatInt(int64(v.Int8()), 10)
|
||||
case v.IsInt16():
|
||||
return strconv.FormatInt(int64(v.Int16()), 10)
|
||||
case v.IsInt32():
|
||||
return strconv.FormatInt(int64(v.Int32()), 10)
|
||||
case v.IsInt64():
|
||||
return strconv.FormatInt(v.Int64(), 10)
|
||||
case v.IsUint():
|
||||
return strconv.FormatUint(uint64(v.Uint()), 10)
|
||||
case v.IsUint8():
|
||||
return strconv.FormatUint(uint64(v.Uint8()), 10)
|
||||
case v.IsUint16():
|
||||
return strconv.FormatUint(uint64(v.Uint16()), 10)
|
||||
case v.IsUint32():
|
||||
return strconv.FormatUint(uint64(v.Uint32()), 10)
|
||||
case v.IsUint64():
|
||||
return strconv.FormatUint(v.Uint64(), 10)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%#v", v.Data())
|
||||
}
|
||||
349
vendor/github.com/stretchr/testify/assert/assertion_format.go
generated
vendored
Normal file
349
vendor/github.com/stretchr/testify/assert/assertion_format.go
generated
vendored
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
/*
|
||||
* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
|
||||
* THIS FILE MUST NOT BE EDITED BY HAND
|
||||
*/
|
||||
|
||||
package assert
|
||||
|
||||
import (
|
||||
http "net/http"
|
||||
url "net/url"
|
||||
time "time"
|
||||
)
|
||||
|
||||
// Conditionf uses a Comparison to assert a complex condition.
|
||||
func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bool {
|
||||
return Condition(t, comp, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Containsf asserts that the specified string, list(array, slice...) or map contains the
|
||||
// specified substring or element.
|
||||
//
|
||||
// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted")
|
||||
// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted")
|
||||
// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted")
|
||||
func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
|
||||
return Contains(t, s, contains, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
|
||||
func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
|
||||
return DirExists(t, path, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified
|
||||
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
|
||||
// the number of appearances of each of them in both lists should match.
|
||||
//
|
||||
// assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted")
|
||||
func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) bool {
|
||||
return ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// assert.Emptyf(t, obj, "error message %s", "formatted")
|
||||
func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
return Empty(t, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Equalf asserts that two objects are equal.
|
||||
//
|
||||
// assert.Equalf(t, 123, 123, "error message %s", "formatted")
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses). Function equality
|
||||
// cannot be determined and will always fail.
|
||||
func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
return Equal(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// EqualErrorf asserts that a function returned an error (i.e. not `nil`)
|
||||
// and that it is equal to the provided error.
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted")
|
||||
func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool {
|
||||
return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// EqualValuesf asserts that two objects are equal or convertable to the same types
|
||||
// and equal.
|
||||
//
|
||||
// assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123))
|
||||
func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
return EqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Errorf asserts that a function returned an error (i.e. not `nil`).
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.Errorf(t, err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedErrorf, err)
|
||||
// }
|
||||
func Errorf(t TestingT, err error, msg string, args ...interface{}) bool {
|
||||
return Error(t, err, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Exactlyf asserts that two objects are equal in value and type.
|
||||
//
|
||||
// assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123))
|
||||
func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
return Exactly(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Failf reports a failure through
|
||||
func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) bool {
|
||||
return Fail(t, failureMessage, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// FailNowf fails test
|
||||
func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) bool {
|
||||
return FailNow(t, failureMessage, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Falsef asserts that the specified value is false.
|
||||
//
|
||||
// assert.Falsef(t, myBool, "error message %s", "formatted")
|
||||
func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool {
|
||||
return False(t, value, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
|
||||
func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
|
||||
return FileExists(t, path, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// HTTPBodyContainsf asserts that a specified handler returns a
|
||||
// body that contains a string.
|
||||
//
|
||||
// assert.HTTPBodyContainsf(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
|
||||
return HTTPBodyContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// HTTPBodyNotContainsf asserts that a specified handler returns a
|
||||
// body that does not contain a string.
|
||||
//
|
||||
// assert.HTTPBodyNotContainsf(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
|
||||
return HTTPBodyNotContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// HTTPErrorf asserts that a specified handler returns an error status code.
|
||||
//
|
||||
// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
|
||||
func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
|
||||
return HTTPError(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// HTTPRedirectf asserts that a specified handler returns a redirect status code.
|
||||
//
|
||||
// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
|
||||
func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
|
||||
return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// HTTPSuccessf asserts that a specified handler returns a success status code.
|
||||
//
|
||||
// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
|
||||
return HTTPSuccess(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Implementsf asserts that an object is implemented by the specified interface.
|
||||
//
|
||||
// assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject))
|
||||
func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
|
||||
return Implements(t, interfaceObject, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// InDeltaf asserts that the two numerals are within delta of each other.
|
||||
//
|
||||
// assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01)
|
||||
func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
|
||||
return InDelta(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
|
||||
func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
|
||||
return InDeltaMapValues(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// InDeltaSlicef is the same as InDelta, except it compares two slices.
|
||||
func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
|
||||
return InDeltaSlice(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// InEpsilonf asserts that expected and actual have a relative error less than epsilon
|
||||
func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
|
||||
return InEpsilon(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices.
|
||||
func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
|
||||
return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// IsTypef asserts that the specified objects are of the same type.
|
||||
func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool {
|
||||
return IsType(t, expectedType, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// JSONEqf asserts that two JSON strings are equivalent.
|
||||
//
|
||||
// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
|
||||
func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
|
||||
return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Lenf asserts that the specified object has specific length.
|
||||
// Lenf also fails if the object has a type that len() not accept.
|
||||
//
|
||||
// assert.Lenf(t, mySlice, 3, "error message %s", "formatted")
|
||||
func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool {
|
||||
return Len(t, object, length, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Nilf asserts that the specified object is nil.
|
||||
//
|
||||
// assert.Nilf(t, err, "error message %s", "formatted")
|
||||
func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
return Nil(t, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NoErrorf asserts that a function returned no error (i.e. `nil`).
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.NoErrorf(t, err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool {
|
||||
return NoError(t, err, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the
|
||||
// specified substring or element.
|
||||
//
|
||||
// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted")
|
||||
// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted")
|
||||
// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted")
|
||||
func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
|
||||
return NotContains(t, s, contains, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// if assert.NotEmptyf(t, obj, "error message %s", "formatted") {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
return NotEmpty(t, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotEqualf asserts that the specified values are NOT equal.
|
||||
//
|
||||
// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted")
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses).
|
||||
func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotNilf asserts that the specified object is not nil.
|
||||
//
|
||||
// assert.NotNilf(t, err, "error message %s", "formatted")
|
||||
func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
return NotNil(t, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic.
|
||||
//
|
||||
// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted")
|
||||
func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
return NotPanics(t, f, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotRegexpf asserts that a specified regexp does not match a string.
|
||||
//
|
||||
// assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
|
||||
// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted")
|
||||
func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
|
||||
return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotSubsetf asserts that the specified list(array, slice...) contains not all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
|
||||
func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
|
||||
return NotSubset(t, list, subset, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotZerof asserts that i is not the zero value for its type.
|
||||
func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
|
||||
return NotZero(t, i, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Panicsf asserts that the code inside the specified PanicTestFunc panics.
|
||||
//
|
||||
// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
return Panics(t, f, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that
|
||||
// the recovered panic value equals the expected panic value.
|
||||
//
|
||||
// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Regexpf asserts that a specified regexp matches a string.
|
||||
//
|
||||
// assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
|
||||
// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted")
|
||||
func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
|
||||
return Regexp(t, rx, str, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Subsetf asserts that the specified list(array, slice...) contains all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
|
||||
func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
|
||||
return Subset(t, list, subset, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Truef asserts that the specified value is true.
|
||||
//
|
||||
// assert.Truef(t, myBool, "error message %s", "formatted")
|
||||
func Truef(t TestingT, value bool, msg string, args ...interface{}) bool {
|
||||
return True(t, value, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// WithinDurationf asserts that the two times are within duration delta of each other.
|
||||
//
|
||||
// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted")
|
||||
func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool {
|
||||
return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Zerof asserts that i is the zero value for its type.
|
||||
func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
|
||||
return Zero(t, i, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
4
vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl
generated
vendored
Normal file
4
vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl
generated
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{{.CommentFormat}}
|
||||
func {{.DocInfo.Name}}f(t TestingT, {{.ParamsFormat}}) bool {
|
||||
return {{.DocInfo.Name}}(t, {{.ForwardedParamsFormat}})
|
||||
}
|
||||
565
vendor/github.com/stretchr/testify/assert/assertion_forward.go
generated
vendored
565
vendor/github.com/stretchr/testify/assert/assertion_forward.go
generated
vendored
|
|
@ -1,387 +1,686 @@
|
|||
/*
|
||||
* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
|
||||
* THIS FILE MUST NOT BE EDITED BY HAND
|
||||
*/
|
||||
*/
|
||||
|
||||
package assert
|
||||
|
||||
import (
|
||||
|
||||
http "net/http"
|
||||
url "net/url"
|
||||
time "time"
|
||||
)
|
||||
|
||||
|
||||
// Condition uses a Comparison to assert a complex condition.
|
||||
func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool {
|
||||
return Condition(a.t, comp, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Conditionf uses a Comparison to assert a complex condition.
|
||||
func (a *Assertions) Conditionf(comp Comparison, msg string, args ...interface{}) bool {
|
||||
return Conditionf(a.t, comp, msg, args...)
|
||||
}
|
||||
|
||||
// Contains asserts that the specified string, list(array, slice...) or map contains the
|
||||
// specified substring or element.
|
||||
//
|
||||
// a.Contains("Hello World", "World", "But 'Hello World' does contain 'World'")
|
||||
// a.Contains(["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'")
|
||||
// a.Contains({"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// a.Contains("Hello World", "World")
|
||||
// a.Contains(["Hello", "World"], "World")
|
||||
// a.Contains({"Hello": "World"}, "Hello")
|
||||
func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool {
|
||||
return Contains(a.t, s, contains, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Containsf asserts that the specified string, list(array, slice...) or map contains the
|
||||
// specified substring or element.
|
||||
//
|
||||
// a.Containsf("Hello World", "World", "error message %s", "formatted")
|
||||
// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted")
|
||||
// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted")
|
||||
func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool {
|
||||
return Containsf(a.t, s, contains, msg, args...)
|
||||
}
|
||||
|
||||
// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
|
||||
func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool {
|
||||
return DirExists(a.t, path, msgAndArgs...)
|
||||
}
|
||||
|
||||
// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
|
||||
func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bool {
|
||||
return DirExistsf(a.t, path, msg, args...)
|
||||
}
|
||||
|
||||
// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified
|
||||
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
|
||||
// the number of appearances of each of them in both lists should match.
|
||||
//
|
||||
// a.ElementsMatch([1, 3, 2, 3], [1, 3, 3, 2])
|
||||
func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) bool {
|
||||
return ElementsMatch(a.t, listA, listB, msgAndArgs...)
|
||||
}
|
||||
|
||||
// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified
|
||||
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
|
||||
// the number of appearances of each of them in both lists should match.
|
||||
//
|
||||
// a.ElementsMatchf([1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted")
|
||||
func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) bool {
|
||||
return ElementsMatchf(a.t, listA, listB, msg, args...)
|
||||
}
|
||||
|
||||
// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
//
|
||||
// a.Empty(obj)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return Empty(a.t, object, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// a.Emptyf(obj, "error message %s", "formatted")
|
||||
func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool {
|
||||
return Emptyf(a.t, object, msg, args...)
|
||||
}
|
||||
|
||||
// Equal asserts that two objects are equal.
|
||||
//
|
||||
// a.Equal(123, 123, "123 and 123 should be equal")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// a.Equal(123, 123)
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses). Function equality
|
||||
// cannot be determined and will always fail.
|
||||
func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
return Equal(a.t, expected, actual, msgAndArgs...)
|
||||
}
|
||||
|
||||
|
||||
// EqualError asserts that a function returned an error (i.e. not `nil`)
|
||||
// and that it is equal to the provided error.
|
||||
//
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.Error(t, err, "An error was expected") {
|
||||
// assert.Equal(t, err, expectedError)
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// a.EqualError(err, expectedErrorString)
|
||||
func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool {
|
||||
return EqualError(a.t, theError, errString, msgAndArgs...)
|
||||
}
|
||||
|
||||
// EqualErrorf asserts that a function returned an error (i.e. not `nil`)
|
||||
// and that it is equal to the provided error.
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted")
|
||||
func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) bool {
|
||||
return EqualErrorf(a.t, theError, errString, msg, args...)
|
||||
}
|
||||
|
||||
// EqualValues asserts that two objects are equal or convertable to the same types
|
||||
// and equal.
|
||||
//
|
||||
// a.EqualValues(uint32(123), int32(123), "123 and 123 should be equal")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// a.EqualValues(uint32(123), int32(123))
|
||||
func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
return EqualValues(a.t, expected, actual, msgAndArgs...)
|
||||
}
|
||||
|
||||
// EqualValuesf asserts that two objects are equal or convertable to the same types
|
||||
// and equal.
|
||||
//
|
||||
// a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123))
|
||||
func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
return EqualValuesf(a.t, expected, actual, msg, args...)
|
||||
}
|
||||
|
||||
// Equalf asserts that two objects are equal.
|
||||
//
|
||||
// a.Equalf(123, 123, "error message %s", "formatted")
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses). Function equality
|
||||
// cannot be determined and will always fail.
|
||||
func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
return Equalf(a.t, expected, actual, msg, args...)
|
||||
}
|
||||
|
||||
// Error asserts that a function returned an error (i.e. not `nil`).
|
||||
//
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if a.Error(err, "An error was expected") {
|
||||
// assert.Equal(t, err, expectedError)
|
||||
// if a.Error(err) {
|
||||
// assert.Equal(t, expectedError, err)
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool {
|
||||
return Error(a.t, err, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Errorf asserts that a function returned an error (i.e. not `nil`).
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if a.Errorf(err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedErrorf, err)
|
||||
// }
|
||||
func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool {
|
||||
return Errorf(a.t, err, msg, args...)
|
||||
}
|
||||
|
||||
// Exactly asserts that two objects are equal is value and type.
|
||||
//
|
||||
// a.Exactly(int32(123), int64(123), "123 and 123 should NOT be equal")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// Exactly asserts that two objects are equal in value and type.
|
||||
//
|
||||
// a.Exactly(int32(123), int64(123))
|
||||
func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
return Exactly(a.t, expected, actual, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Exactlyf asserts that two objects are equal in value and type.
|
||||
//
|
||||
// a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123))
|
||||
func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
return Exactlyf(a.t, expected, actual, msg, args...)
|
||||
}
|
||||
|
||||
// Fail reports a failure through
|
||||
func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool {
|
||||
return Fail(a.t, failureMessage, msgAndArgs...)
|
||||
}
|
||||
|
||||
|
||||
// FailNow fails test
|
||||
func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool {
|
||||
return FailNow(a.t, failureMessage, msgAndArgs...)
|
||||
}
|
||||
|
||||
// FailNowf fails test
|
||||
func (a *Assertions) FailNowf(failureMessage string, msg string, args ...interface{}) bool {
|
||||
return FailNowf(a.t, failureMessage, msg, args...)
|
||||
}
|
||||
|
||||
// Failf reports a failure through
|
||||
func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{}) bool {
|
||||
return Failf(a.t, failureMessage, msg, args...)
|
||||
}
|
||||
|
||||
// False asserts that the specified value is false.
|
||||
//
|
||||
// a.False(myBool, "myBool should be false")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// a.False(myBool)
|
||||
func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool {
|
||||
return False(a.t, value, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Falsef asserts that the specified value is false.
|
||||
//
|
||||
// a.Falsef(myBool, "error message %s", "formatted")
|
||||
func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool {
|
||||
return Falsef(a.t, value, msg, args...)
|
||||
}
|
||||
|
||||
// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
|
||||
func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool {
|
||||
return FileExists(a.t, path, msgAndArgs...)
|
||||
}
|
||||
|
||||
// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
|
||||
func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) bool {
|
||||
return FileExistsf(a.t, path, msg, args...)
|
||||
}
|
||||
|
||||
// HTTPBodyContains asserts that a specified handler returns a
|
||||
// body that contains a string.
|
||||
//
|
||||
//
|
||||
// a.HTTPBodyContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky")
|
||||
//
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool {
|
||||
return HTTPBodyContains(a.t, handler, method, url, values, str)
|
||||
func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
return HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...)
|
||||
}
|
||||
|
||||
// HTTPBodyContainsf asserts that a specified handler returns a
|
||||
// body that contains a string.
|
||||
//
|
||||
// a.HTTPBodyContainsf(myHandler, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
|
||||
return HTTPBodyContainsf(a.t, handler, method, url, values, str, msg, args...)
|
||||
}
|
||||
|
||||
// HTTPBodyNotContains asserts that a specified handler returns a
|
||||
// body that does not contain a string.
|
||||
//
|
||||
//
|
||||
// a.HTTPBodyNotContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky")
|
||||
//
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool {
|
||||
return HTTPBodyNotContains(a.t, handler, method, url, values, str)
|
||||
func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
return HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...)
|
||||
}
|
||||
|
||||
// HTTPBodyNotContainsf asserts that a specified handler returns a
|
||||
// body that does not contain a string.
|
||||
//
|
||||
// a.HTTPBodyNotContainsf(myHandler, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
|
||||
return HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...)
|
||||
}
|
||||
|
||||
// HTTPError asserts that a specified handler returns an error status code.
|
||||
//
|
||||
//
|
||||
// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values) bool {
|
||||
return HTTPError(a.t, handler, method, url, values)
|
||||
func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool {
|
||||
return HTTPError(a.t, handler, method, url, values, msgAndArgs...)
|
||||
}
|
||||
|
||||
// HTTPErrorf asserts that a specified handler returns an error status code.
|
||||
//
|
||||
// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
|
||||
func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
|
||||
return HTTPErrorf(a.t, handler, method, url, values, msg, args...)
|
||||
}
|
||||
|
||||
// HTTPRedirect asserts that a specified handler returns a redirect status code.
|
||||
//
|
||||
//
|
||||
// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values) bool {
|
||||
return HTTPRedirect(a.t, handler, method, url, values)
|
||||
func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool {
|
||||
return HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...)
|
||||
}
|
||||
|
||||
// HTTPRedirectf asserts that a specified handler returns a redirect status code.
|
||||
//
|
||||
// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
|
||||
func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
|
||||
return HTTPRedirectf(a.t, handler, method, url, values, msg, args...)
|
||||
}
|
||||
|
||||
// HTTPSuccess asserts that a specified handler returns a success status code.
|
||||
//
|
||||
//
|
||||
// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil)
|
||||
//
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values) bool {
|
||||
return HTTPSuccess(a.t, handler, method, url, values)
|
||||
func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool {
|
||||
return HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...)
|
||||
}
|
||||
|
||||
// HTTPSuccessf asserts that a specified handler returns a success status code.
|
||||
//
|
||||
// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
|
||||
return HTTPSuccessf(a.t, handler, method, url, values, msg, args...)
|
||||
}
|
||||
|
||||
// Implements asserts that an object is implemented by the specified interface.
|
||||
//
|
||||
// a.Implements((*MyInterface)(nil), new(MyObject), "MyObject")
|
||||
//
|
||||
// a.Implements((*MyInterface)(nil), new(MyObject))
|
||||
func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return Implements(a.t, interfaceObject, object, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Implementsf asserts that an object is implemented by the specified interface.
|
||||
//
|
||||
// a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject))
|
||||
func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
|
||||
return Implementsf(a.t, interfaceObject, object, msg, args...)
|
||||
}
|
||||
|
||||
// InDelta asserts that the two numerals are within delta of each other.
|
||||
//
|
||||
//
|
||||
// a.InDelta(math.Pi, (22 / 7.0), 0.01)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
||||
return InDelta(a.t, expected, actual, delta, msgAndArgs...)
|
||||
}
|
||||
|
||||
// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
|
||||
func (a *Assertions) InDeltaMapValues(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
||||
return InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...)
|
||||
}
|
||||
|
||||
// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
|
||||
func (a *Assertions) InDeltaMapValuesf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
|
||||
return InDeltaMapValuesf(a.t, expected, actual, delta, msg, args...)
|
||||
}
|
||||
|
||||
// InDeltaSlice is the same as InDelta, except it compares two slices.
|
||||
func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
||||
return InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...)
|
||||
}
|
||||
|
||||
// InDeltaSlicef is the same as InDelta, except it compares two slices.
|
||||
func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
|
||||
return InDeltaSlicef(a.t, expected, actual, delta, msg, args...)
|
||||
}
|
||||
|
||||
// InDeltaf asserts that the two numerals are within delta of each other.
|
||||
//
|
||||
// a.InDeltaf(math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01)
|
||||
func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
|
||||
return InDeltaf(a.t, expected, actual, delta, msg, args...)
|
||||
}
|
||||
|
||||
// InEpsilon asserts that expected and actual have a relative error less than epsilon
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
|
||||
return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...)
|
||||
}
|
||||
|
||||
|
||||
// InEpsilonSlice is the same as InEpsilon, except it compares two slices.
|
||||
func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
||||
return InEpsilonSlice(a.t, expected, actual, delta, msgAndArgs...)
|
||||
// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices.
|
||||
func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
|
||||
return InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...)
|
||||
}
|
||||
|
||||
// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices.
|
||||
func (a *Assertions) InEpsilonSlicef(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
|
||||
return InEpsilonSlicef(a.t, expected, actual, epsilon, msg, args...)
|
||||
}
|
||||
|
||||
// InEpsilonf asserts that expected and actual have a relative error less than epsilon
|
||||
func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
|
||||
return InEpsilonf(a.t, expected, actual, epsilon, msg, args...)
|
||||
}
|
||||
|
||||
// IsType asserts that the specified objects are of the same type.
|
||||
func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return IsType(a.t, expectedType, object, msgAndArgs...)
|
||||
}
|
||||
|
||||
// IsTypef asserts that the specified objects are of the same type.
|
||||
func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) bool {
|
||||
return IsTypef(a.t, expectedType, object, msg, args...)
|
||||
}
|
||||
|
||||
// JSONEq asserts that two JSON strings are equivalent.
|
||||
//
|
||||
//
|
||||
// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool {
|
||||
return JSONEq(a.t, expected, actual, msgAndArgs...)
|
||||
}
|
||||
|
||||
// JSONEqf asserts that two JSON strings are equivalent.
|
||||
//
|
||||
// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
|
||||
func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) bool {
|
||||
return JSONEqf(a.t, expected, actual, msg, args...)
|
||||
}
|
||||
|
||||
// Len asserts that the specified object has specific length.
|
||||
// Len also fails if the object has a type that len() not accept.
|
||||
//
|
||||
// a.Len(mySlice, 3, "The size of slice is not 3")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// a.Len(mySlice, 3)
|
||||
func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool {
|
||||
return Len(a.t, object, length, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Lenf asserts that the specified object has specific length.
|
||||
// Lenf also fails if the object has a type that len() not accept.
|
||||
//
|
||||
// a.Lenf(mySlice, 3, "error message %s", "formatted")
|
||||
func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) bool {
|
||||
return Lenf(a.t, object, length, msg, args...)
|
||||
}
|
||||
|
||||
// Nil asserts that the specified object is nil.
|
||||
//
|
||||
// a.Nil(err, "err should be nothing")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// a.Nil(err)
|
||||
func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return Nil(a.t, object, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Nilf asserts that the specified object is nil.
|
||||
//
|
||||
// a.Nilf(err, "error message %s", "formatted")
|
||||
func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) bool {
|
||||
return Nilf(a.t, object, msg, args...)
|
||||
}
|
||||
|
||||
// NoError asserts that a function returned no error (i.e. `nil`).
|
||||
//
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if a.NoError(err) {
|
||||
// assert.Equal(t, actualObj, expectedObj)
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool {
|
||||
return NoError(a.t, err, msgAndArgs...)
|
||||
}
|
||||
|
||||
// NoErrorf asserts that a function returned no error (i.e. `nil`).
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if a.NoErrorf(err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool {
|
||||
return NoErrorf(a.t, err, msg, args...)
|
||||
}
|
||||
|
||||
// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
|
||||
// specified substring or element.
|
||||
//
|
||||
// a.NotContains("Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'")
|
||||
// a.NotContains(["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'")
|
||||
// a.NotContains({"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// a.NotContains("Hello World", "Earth")
|
||||
// a.NotContains(["Hello", "World"], "Earth")
|
||||
// a.NotContains({"Hello": "World"}, "Earth")
|
||||
func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool {
|
||||
return NotContains(a.t, s, contains, msgAndArgs...)
|
||||
}
|
||||
|
||||
// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the
|
||||
// specified substring or element.
|
||||
//
|
||||
// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted")
|
||||
// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted")
|
||||
// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted")
|
||||
func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool {
|
||||
return NotContainsf(a.t, s, contains, msg, args...)
|
||||
}
|
||||
|
||||
// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
//
|
||||
// if a.NotEmpty(obj) {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return NotEmpty(a.t, object, msgAndArgs...)
|
||||
}
|
||||
|
||||
// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// if a.NotEmptyf(obj, "error message %s", "formatted") {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) bool {
|
||||
return NotEmptyf(a.t, object, msg, args...)
|
||||
}
|
||||
|
||||
// NotEqual asserts that the specified values are NOT equal.
|
||||
//
|
||||
// a.NotEqual(obj1, obj2, "two objects shouldn't be equal")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// a.NotEqual(obj1, obj2)
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses).
|
||||
func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
return NotEqual(a.t, expected, actual, msgAndArgs...)
|
||||
}
|
||||
|
||||
// NotEqualf asserts that the specified values are NOT equal.
|
||||
//
|
||||
// a.NotEqualf(obj1, obj2, "error message %s", "formatted")
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses).
|
||||
func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
return NotEqualf(a.t, expected, actual, msg, args...)
|
||||
}
|
||||
|
||||
// NotNil asserts that the specified object is not nil.
|
||||
//
|
||||
// a.NotNil(err, "err should be something")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// a.NotNil(err)
|
||||
func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return NotNil(a.t, object, msgAndArgs...)
|
||||
}
|
||||
|
||||
// NotNilf asserts that the specified object is not nil.
|
||||
//
|
||||
// a.NotNilf(err, "error message %s", "formatted")
|
||||
func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) bool {
|
||||
return NotNilf(a.t, object, msg, args...)
|
||||
}
|
||||
|
||||
// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
|
||||
//
|
||||
// a.NotPanics(func(){
|
||||
// RemainCalm()
|
||||
// }, "Calling RemainCalm() should NOT panic")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// a.NotPanics(func(){ RemainCalm() })
|
||||
func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
return NotPanics(a.t, f, msgAndArgs...)
|
||||
}
|
||||
|
||||
// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic.
|
||||
//
|
||||
// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted")
|
||||
func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
return NotPanicsf(a.t, f, msg, args...)
|
||||
}
|
||||
|
||||
// NotRegexp asserts that a specified regexp does not match a string.
|
||||
//
|
||||
//
|
||||
// a.NotRegexp(regexp.MustCompile("starts"), "it's starting")
|
||||
// a.NotRegexp("^start", "it's not starting")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
return NotRegexp(a.t, rx, str, msgAndArgs...)
|
||||
}
|
||||
|
||||
// NotRegexpf asserts that a specified regexp does not match a string.
|
||||
//
|
||||
// a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
|
||||
// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted")
|
||||
func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool {
|
||||
return NotRegexpf(a.t, rx, str, msg, args...)
|
||||
}
|
||||
|
||||
// NotZero asserts that i is not the zero value for its type and returns the truth.
|
||||
// NotSubset asserts that the specified list(array, slice...) contains not all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
|
||||
func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool {
|
||||
return NotSubset(a.t, list, subset, msgAndArgs...)
|
||||
}
|
||||
|
||||
// NotSubsetf asserts that the specified list(array, slice...) contains not all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
|
||||
func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool {
|
||||
return NotSubsetf(a.t, list, subset, msg, args...)
|
||||
}
|
||||
|
||||
// NotZero asserts that i is not the zero value for its type.
|
||||
func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool {
|
||||
return NotZero(a.t, i, msgAndArgs...)
|
||||
}
|
||||
|
||||
// NotZerof asserts that i is not the zero value for its type.
|
||||
func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bool {
|
||||
return NotZerof(a.t, i, msg, args...)
|
||||
}
|
||||
|
||||
// Panics asserts that the code inside the specified PanicTestFunc panics.
|
||||
//
|
||||
// a.Panics(func(){
|
||||
// GoCrazy()
|
||||
// }, "Calling GoCrazy() should panic")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// a.Panics(func(){ GoCrazy() })
|
||||
func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
return Panics(a.t, f, msgAndArgs...)
|
||||
}
|
||||
|
||||
// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that
|
||||
// the recovered panic value equals the expected panic value.
|
||||
//
|
||||
// a.PanicsWithValue("crazy error", func(){ GoCrazy() })
|
||||
func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
return PanicsWithValue(a.t, expected, f, msgAndArgs...)
|
||||
}
|
||||
|
||||
// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that
|
||||
// the recovered panic value equals the expected panic value.
|
||||
//
|
||||
// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
return PanicsWithValuef(a.t, expected, f, msg, args...)
|
||||
}
|
||||
|
||||
// Panicsf asserts that the code inside the specified PanicTestFunc panics.
|
||||
//
|
||||
// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
return Panicsf(a.t, f, msg, args...)
|
||||
}
|
||||
|
||||
// Regexp asserts that a specified regexp matches a string.
|
||||
//
|
||||
//
|
||||
// a.Regexp(regexp.MustCompile("start"), "it's starting")
|
||||
// a.Regexp("start...$", "it's not starting")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
return Regexp(a.t, rx, str, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Regexpf asserts that a specified regexp matches a string.
|
||||
//
|
||||
// a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
|
||||
// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted")
|
||||
func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool {
|
||||
return Regexpf(a.t, rx, str, msg, args...)
|
||||
}
|
||||
|
||||
// Subset asserts that the specified list(array, slice...) contains all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
|
||||
func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool {
|
||||
return Subset(a.t, list, subset, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Subsetf asserts that the specified list(array, slice...) contains all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
|
||||
func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool {
|
||||
return Subsetf(a.t, list, subset, msg, args...)
|
||||
}
|
||||
|
||||
// True asserts that the specified value is true.
|
||||
//
|
||||
// a.True(myBool, "myBool should be true")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// a.True(myBool)
|
||||
func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool {
|
||||
return True(a.t, value, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Truef asserts that the specified value is true.
|
||||
//
|
||||
// a.Truef(myBool, "error message %s", "formatted")
|
||||
func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool {
|
||||
return Truef(a.t, value, msg, args...)
|
||||
}
|
||||
|
||||
// WithinDuration asserts that the two times are within duration delta of each other.
|
||||
//
|
||||
// a.WithinDuration(time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
//
|
||||
// a.WithinDuration(time.Now(), time.Now(), 10*time.Second)
|
||||
func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
|
||||
return WithinDuration(a.t, expected, actual, delta, msgAndArgs...)
|
||||
}
|
||||
|
||||
// WithinDurationf asserts that the two times are within duration delta of each other.
|
||||
//
|
||||
// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted")
|
||||
func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool {
|
||||
return WithinDurationf(a.t, expected, actual, delta, msg, args...)
|
||||
}
|
||||
|
||||
// Zero asserts that i is the zero value for its type and returns the truth.
|
||||
// Zero asserts that i is the zero value for its type.
|
||||
func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool {
|
||||
return Zero(a.t, i, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Zerof asserts that i is the zero value for its type.
|
||||
func (a *Assertions) Zerof(i interface{}, msg string, args ...interface{}) bool {
|
||||
return Zerof(a.t, i, msg, args...)
|
||||
}
|
||||
|
|
|
|||
619
vendor/github.com/stretchr/testify/assert/assertions.go
generated
vendored
619
vendor/github.com/stretchr/testify/assert/assertions.go
generated
vendored
|
|
@ -4,8 +4,10 @@ import (
|
|||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
|
|
@ -18,6 +20,8 @@ import (
|
|||
"github.com/pmezard/go-difflib/difflib"
|
||||
)
|
||||
|
||||
//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_format.go.tmpl
|
||||
|
||||
// TestingT is an interface wrapper around *testing.T
|
||||
type TestingT interface {
|
||||
Errorf(format string, args ...interface{})
|
||||
|
|
@ -38,7 +42,15 @@ func ObjectsAreEqual(expected, actual interface{}) bool {
|
|||
if expected == nil || actual == nil {
|
||||
return expected == actual
|
||||
}
|
||||
|
||||
if exp, ok := expected.([]byte); ok {
|
||||
act, ok := actual.([]byte)
|
||||
if !ok {
|
||||
return false
|
||||
} else if exp == nil || act == nil {
|
||||
return exp == nil && act == nil
|
||||
}
|
||||
return bytes.Equal(exp, act)
|
||||
}
|
||||
return reflect.DeepEqual(expected, actual)
|
||||
|
||||
}
|
||||
|
|
@ -65,7 +77,7 @@ func ObjectsAreEqualValues(expected, actual interface{}) bool {
|
|||
|
||||
/* CallerInfo is necessary because the assert functions use the testing object
|
||||
internally, causing it to print the file:line of the assert method, rather than where
|
||||
the problem actually occured in calling code.*/
|
||||
the problem actually occurred in calling code.*/
|
||||
|
||||
// CallerInfo returns an array of strings containing the file and line number
|
||||
// of each stack frame leading from the current test to the assert call that
|
||||
|
|
@ -82,7 +94,9 @@ func CallerInfo() []string {
|
|||
for i := 0; ; i++ {
|
||||
pc, file, line, ok = runtime.Caller(i)
|
||||
if !ok {
|
||||
return nil
|
||||
// The breaks below failed to terminate the loop, and we ran off the
|
||||
// end of the call stack.
|
||||
break
|
||||
}
|
||||
|
||||
// This is a huge edge case, but it will panic if this is the case, see #180
|
||||
|
|
@ -90,18 +104,30 @@ func CallerInfo() []string {
|
|||
break
|
||||
}
|
||||
|
||||
parts := strings.Split(file, "/")
|
||||
dir := parts[len(parts)-2]
|
||||
file = parts[len(parts)-1]
|
||||
if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" {
|
||||
callers = append(callers, fmt.Sprintf("%s:%d", file, line))
|
||||
}
|
||||
|
||||
f := runtime.FuncForPC(pc)
|
||||
if f == nil {
|
||||
break
|
||||
}
|
||||
name = f.Name()
|
||||
|
||||
// testing.tRunner is the standard library function that calls
|
||||
// tests. Subtests are called directly by tRunner, without going through
|
||||
// the Test/Benchmark/Example function that contains the t.Run calls, so
|
||||
// with subtests we should break when we hit tRunner, without adding it
|
||||
// to the list of callers.
|
||||
if name == "testing.tRunner" {
|
||||
break
|
||||
}
|
||||
|
||||
parts := strings.Split(file, "/")
|
||||
file = parts[len(parts)-1]
|
||||
if len(parts) > 1 {
|
||||
dir := parts[len(parts)-2]
|
||||
if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" {
|
||||
callers = append(callers, fmt.Sprintf("%s:%d", file, line))
|
||||
}
|
||||
}
|
||||
|
||||
// Drop the package
|
||||
segments := strings.Split(name, ".")
|
||||
name = segments[len(segments)-1]
|
||||
|
|
@ -141,7 +167,7 @@ func getWhitespaceString() string {
|
|||
parts := strings.Split(file, "/")
|
||||
file = parts[len(parts)-1]
|
||||
|
||||
return strings.Repeat(" ", len(fmt.Sprintf("%s:%d: ", file, line)))
|
||||
return strings.Repeat(" ", len(fmt.Sprintf("%s:%d: ", file, line)))
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -158,22 +184,18 @@ func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
// Indents all lines of the message by appending a number of tabs to each line, in an output format compatible with Go's
|
||||
// test printing (see inner comment for specifics)
|
||||
func indentMessageLines(message string, tabs int) string {
|
||||
// Aligns the provided message so that all lines after the first line start at the same location as the first line.
|
||||
// Assumes that the first line starts at the correct location (after carriage return, tab, label, spacer and tab).
|
||||
// The longestLabelLen parameter specifies the length of the longest label in the output (required becaues this is the
|
||||
// basis on which the alignment occurs).
|
||||
func indentMessageLines(message string, longestLabelLen int) string {
|
||||
outBuf := new(bytes.Buffer)
|
||||
|
||||
for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ {
|
||||
// no need to align first line because it starts at the correct location (after the label)
|
||||
if i != 0 {
|
||||
outBuf.WriteRune('\n')
|
||||
}
|
||||
for ii := 0; ii < tabs; ii++ {
|
||||
outBuf.WriteRune('\t')
|
||||
// Bizarrely, all lines except the first need one fewer tabs prepended, so deliberately advance the counter
|
||||
// by 1 prematurely.
|
||||
if ii == 0 && i > 0 {
|
||||
ii++
|
||||
}
|
||||
// append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab
|
||||
outBuf.WriteString("\n\r\t" + strings.Repeat(" ", longestLabelLen+1) + "\t")
|
||||
}
|
||||
outBuf.WriteString(scanner.Text())
|
||||
}
|
||||
|
|
@ -205,42 +227,70 @@ func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool
|
|||
|
||||
// Fail reports a failure through
|
||||
func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
|
||||
content := []labeledContent{
|
||||
{"Error Trace", strings.Join(CallerInfo(), "\n\r\t\t\t")},
|
||||
{"Error", failureMessage},
|
||||
}
|
||||
|
||||
// Add test name if the Go version supports it
|
||||
if n, ok := t.(interface {
|
||||
Name() string
|
||||
}); ok {
|
||||
content = append(content, labeledContent{"Test", n.Name()})
|
||||
}
|
||||
|
||||
message := messageFromMsgAndArgs(msgAndArgs...)
|
||||
|
||||
errorTrace := strings.Join(CallerInfo(), "\n\r\t\t\t")
|
||||
if len(message) > 0 {
|
||||
t.Errorf("\r%s\r\tError Trace:\t%s\n"+
|
||||
"\r\tError:%s\n"+
|
||||
"\r\tMessages:\t%s\n\r",
|
||||
getWhitespaceString(),
|
||||
errorTrace,
|
||||
indentMessageLines(failureMessage, 2),
|
||||
message)
|
||||
} else {
|
||||
t.Errorf("\r%s\r\tError Trace:\t%s\n"+
|
||||
"\r\tError:%s\n\r",
|
||||
getWhitespaceString(),
|
||||
errorTrace,
|
||||
indentMessageLines(failureMessage, 2))
|
||||
content = append(content, labeledContent{"Messages", message})
|
||||
}
|
||||
|
||||
t.Errorf("%s", "\r"+getWhitespaceString()+labeledOutput(content...))
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type labeledContent struct {
|
||||
label string
|
||||
content string
|
||||
}
|
||||
|
||||
// labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner:
|
||||
//
|
||||
// \r\t{{label}}:{{align_spaces}}\t{{content}}\n
|
||||
//
|
||||
// The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label.
|
||||
// If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this
|
||||
// alignment is achieved, "\t{{content}}\n" is added for the output.
|
||||
//
|
||||
// If the content of the labeledOutput contains line breaks, the subsequent lines are aligned so that they start at the same location as the first line.
|
||||
func labeledOutput(content ...labeledContent) string {
|
||||
longestLabel := 0
|
||||
for _, v := range content {
|
||||
if len(v.label) > longestLabel {
|
||||
longestLabel = len(v.label)
|
||||
}
|
||||
}
|
||||
var output string
|
||||
for _, v := range content {
|
||||
output += "\r\t" + v.label + ":" + strings.Repeat(" ", longestLabel-len(v.label)) + "\t" + indentMessageLines(v.content, longestLabel) + "\n"
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
// Implements asserts that an object is implemented by the specified interface.
|
||||
//
|
||||
// assert.Implements(t, (*MyInterface)(nil), new(MyObject), "MyObject")
|
||||
// assert.Implements(t, (*MyInterface)(nil), new(MyObject))
|
||||
func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
|
||||
interfaceType := reflect.TypeOf(interfaceObject).Elem()
|
||||
|
||||
if object == nil {
|
||||
return Fail(t, fmt.Sprintf("Cannot check if nil implements %v", interfaceType), msgAndArgs...)
|
||||
}
|
||||
if !reflect.TypeOf(object).Implements(interfaceType) {
|
||||
return Fail(t, fmt.Sprintf("%T must implement %v", object, interfaceType), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
// IsType asserts that the specified objects are of the same type.
|
||||
|
|
@ -255,43 +305,66 @@ func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs
|
|||
|
||||
// Equal asserts that two objects are equal.
|
||||
//
|
||||
// assert.Equal(t, 123, 123, "123 and 123 should be equal")
|
||||
// assert.Equal(t, 123, 123)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses). Function equality
|
||||
// cannot be determined and will always fail.
|
||||
func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if err := validateEqualArgs(expected, actual); err != nil {
|
||||
return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)",
|
||||
expected, actual, err), msgAndArgs...)
|
||||
}
|
||||
|
||||
if !ObjectsAreEqual(expected, actual) {
|
||||
diff := diff(expected, actual)
|
||||
return Fail(t, fmt.Sprintf("Not equal: %#v (expected)\n"+
|
||||
" != %#v (actual)%s", expected, actual, diff), msgAndArgs...)
|
||||
expected, actual = formatUnequalValues(expected, actual)
|
||||
return Fail(t, fmt.Sprintf("Not equal: \n"+
|
||||
"expected: %s\n"+
|
||||
"actual : %s%s", expected, actual, diff), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
// formatUnequalValues takes two values of arbitrary types and returns string
|
||||
// representations appropriate to be presented to the user.
|
||||
//
|
||||
// If the values are not of like type, the returned strings will be prefixed
|
||||
// with the type name, and the value will be enclosed in parenthesis similar
|
||||
// to a type conversion in the Go grammar.
|
||||
func formatUnequalValues(expected, actual interface{}) (e string, a string) {
|
||||
if reflect.TypeOf(expected) != reflect.TypeOf(actual) {
|
||||
return fmt.Sprintf("%T(%#v)", expected, expected),
|
||||
fmt.Sprintf("%T(%#v)", actual, actual)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%#v", expected),
|
||||
fmt.Sprintf("%#v", actual)
|
||||
}
|
||||
|
||||
// EqualValues asserts that two objects are equal or convertable to the same types
|
||||
// and equal.
|
||||
//
|
||||
// assert.EqualValues(t, uint32(123), int32(123), "123 and 123 should be equal")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// assert.EqualValues(t, uint32(123), int32(123))
|
||||
func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
|
||||
if !ObjectsAreEqualValues(expected, actual) {
|
||||
return Fail(t, fmt.Sprintf("Not equal: %#v (expected)\n"+
|
||||
" != %#v (actual)", expected, actual), msgAndArgs...)
|
||||
diff := diff(expected, actual)
|
||||
expected, actual = formatUnequalValues(expected, actual)
|
||||
return Fail(t, fmt.Sprintf("Not equal: \n"+
|
||||
"expected: %s\n"+
|
||||
"actual : %s%s", expected, actual, diff), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
// Exactly asserts that two objects are equal is value and type.
|
||||
// Exactly asserts that two objects are equal in value and type.
|
||||
//
|
||||
// assert.Exactly(t, int32(123), int64(123), "123 and 123 should NOT be equal")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// assert.Exactly(t, int32(123), int64(123))
|
||||
func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
|
||||
aType := reflect.TypeOf(expected)
|
||||
|
|
@ -307,9 +380,7 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}
|
|||
|
||||
// NotNil asserts that the specified object is not nil.
|
||||
//
|
||||
// assert.NotNil(t, err, "err should be something")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// assert.NotNil(t, err)
|
||||
func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if !isNil(object) {
|
||||
return true
|
||||
|
|
@ -334,9 +405,7 @@ func isNil(object interface{}) bool {
|
|||
|
||||
// Nil asserts that the specified object is nil.
|
||||
//
|
||||
// assert.Nil(t, err, "err should be nothing")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// assert.Nil(t, err)
|
||||
func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if isNil(object) {
|
||||
return true
|
||||
|
|
@ -344,74 +413,38 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
|||
return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...)
|
||||
}
|
||||
|
||||
var numericZeros = []interface{}{
|
||||
int(0),
|
||||
int8(0),
|
||||
int16(0),
|
||||
int32(0),
|
||||
int64(0),
|
||||
uint(0),
|
||||
uint8(0),
|
||||
uint16(0),
|
||||
uint32(0),
|
||||
uint64(0),
|
||||
float32(0),
|
||||
float64(0),
|
||||
}
|
||||
|
||||
// isEmpty gets whether the specified object is considered empty or not.
|
||||
func isEmpty(object interface{}) bool {
|
||||
|
||||
// get nil case out of the way
|
||||
if object == nil {
|
||||
return true
|
||||
} else if object == "" {
|
||||
return true
|
||||
} else if object == false {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, v := range numericZeros {
|
||||
if object == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
objValue := reflect.ValueOf(object)
|
||||
|
||||
switch objValue.Kind() {
|
||||
case reflect.Map:
|
||||
fallthrough
|
||||
case reflect.Slice, reflect.Chan:
|
||||
{
|
||||
return (objValue.Len() == 0)
|
||||
}
|
||||
case reflect.Struct:
|
||||
switch object.(type) {
|
||||
case time.Time:
|
||||
return object.(time.Time).IsZero()
|
||||
}
|
||||
// collection types are empty when they have no element
|
||||
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
|
||||
return objValue.Len() == 0
|
||||
// pointers are empty if nil or if the value they point to is empty
|
||||
case reflect.Ptr:
|
||||
{
|
||||
if objValue.IsNil() {
|
||||
return true
|
||||
}
|
||||
switch object.(type) {
|
||||
case *time.Time:
|
||||
return object.(*time.Time).IsZero()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
if objValue.IsNil() {
|
||||
return true
|
||||
}
|
||||
deref := objValue.Elem().Interface()
|
||||
return isEmpty(deref)
|
||||
// for all other types, compare against the zero value
|
||||
default:
|
||||
zero := reflect.Zero(objValue.Type())
|
||||
return reflect.DeepEqual(object, zero.Interface())
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// assert.Empty(t, obj)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
|
||||
pass := isEmpty(object)
|
||||
|
|
@ -429,8 +462,6 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
|||
// if assert.NotEmpty(t, obj) {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
|
||||
pass := !isEmpty(object)
|
||||
|
|
@ -457,9 +488,7 @@ func getLen(x interface{}) (ok bool, length int) {
|
|||
// Len asserts that the specified object has specific length.
|
||||
// Len also fails if the object has a type that len() not accept.
|
||||
//
|
||||
// assert.Len(t, mySlice, 3, "The size of slice is not 3")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// assert.Len(t, mySlice, 3)
|
||||
func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool {
|
||||
ok, l := getLen(object)
|
||||
if !ok {
|
||||
|
|
@ -474,9 +503,7 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{})
|
|||
|
||||
// True asserts that the specified value is true.
|
||||
//
|
||||
// assert.True(t, myBool, "myBool should be true")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// assert.True(t, myBool)
|
||||
func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
|
||||
if value != true {
|
||||
|
|
@ -489,9 +516,7 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
|||
|
||||
// False asserts that the specified value is false.
|
||||
//
|
||||
// assert.False(t, myBool, "myBool should be false")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// assert.False(t, myBool)
|
||||
func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
|
||||
if value != false {
|
||||
|
|
@ -504,10 +529,15 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
|||
|
||||
// NotEqual asserts that the specified values are NOT equal.
|
||||
//
|
||||
// assert.NotEqual(t, obj1, obj2, "two objects shouldn't be equal")
|
||||
// assert.NotEqual(t, obj1, obj2)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses).
|
||||
func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if err := validateEqualArgs(expected, actual); err != nil {
|
||||
return Fail(t, fmt.Sprintf("Invalid operation: %#v != %#v (%s)",
|
||||
expected, actual, err), msgAndArgs...)
|
||||
}
|
||||
|
||||
if ObjectsAreEqual(expected, actual) {
|
||||
return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...)
|
||||
|
|
@ -558,11 +588,9 @@ func includeElement(list interface{}, element interface{}) (ok, found bool) {
|
|||
// Contains asserts that the specified string, list(array, slice...) or map contains the
|
||||
// specified substring or element.
|
||||
//
|
||||
// assert.Contains(t, "Hello World", "World", "But 'Hello World' does contain 'World'")
|
||||
// assert.Contains(t, ["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'")
|
||||
// assert.Contains(t, {"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// assert.Contains(t, "Hello World", "World")
|
||||
// assert.Contains(t, ["Hello", "World"], "World")
|
||||
// assert.Contains(t, {"Hello": "World"}, "Hello")
|
||||
func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
|
||||
|
||||
ok, found := includeElement(s, contains)
|
||||
|
|
@ -580,11 +608,9 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo
|
|||
// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
|
||||
// specified substring or element.
|
||||
//
|
||||
// assert.NotContains(t, "Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'")
|
||||
// assert.NotContains(t, ["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'")
|
||||
// assert.NotContains(t, {"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// assert.NotContains(t, "Hello World", "Earth")
|
||||
// assert.NotContains(t, ["Hello", "World"], "Earth")
|
||||
// assert.NotContains(t, {"Hello": "World"}, "Earth")
|
||||
func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
|
||||
|
||||
ok, found := includeElement(s, contains)
|
||||
|
|
@ -599,6 +625,142 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{})
|
|||
|
||||
}
|
||||
|
||||
// Subset asserts that the specified list(array, slice...) contains all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
|
||||
func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
|
||||
if subset == nil {
|
||||
return true // we consider nil to be equal to the nil set
|
||||
}
|
||||
|
||||
subsetValue := reflect.ValueOf(subset)
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
ok = false
|
||||
}
|
||||
}()
|
||||
|
||||
listKind := reflect.TypeOf(list).Kind()
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
|
||||
if listKind != reflect.Array && listKind != reflect.Slice {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
if subsetKind != reflect.Array && subsetKind != reflect.Slice {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
for i := 0; i < subsetValue.Len(); i++ {
|
||||
element := subsetValue.Index(i).Interface()
|
||||
ok, found := includeElement(list, element)
|
||||
if !ok {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
|
||||
}
|
||||
if !found {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, element), msgAndArgs...)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// NotSubset asserts that the specified list(array, slice...) contains not all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
|
||||
func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
|
||||
if subset == nil {
|
||||
return Fail(t, fmt.Sprintf("nil is the empty set which is a subset of every set"), msgAndArgs...)
|
||||
}
|
||||
|
||||
subsetValue := reflect.ValueOf(subset)
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
ok = false
|
||||
}
|
||||
}()
|
||||
|
||||
listKind := reflect.TypeOf(list).Kind()
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
|
||||
if listKind != reflect.Array && listKind != reflect.Slice {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
if subsetKind != reflect.Array && subsetKind != reflect.Slice {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
for i := 0; i < subsetValue.Len(); i++ {
|
||||
element := subsetValue.Index(i).Interface()
|
||||
ok, found := includeElement(list, element)
|
||||
if !ok {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
|
||||
}
|
||||
if !found {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...)
|
||||
}
|
||||
|
||||
// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified
|
||||
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
|
||||
// the number of appearances of each of them in both lists should match.
|
||||
//
|
||||
// assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2])
|
||||
func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) {
|
||||
if isEmpty(listA) && isEmpty(listB) {
|
||||
return true
|
||||
}
|
||||
|
||||
aKind := reflect.TypeOf(listA).Kind()
|
||||
bKind := reflect.TypeOf(listB).Kind()
|
||||
|
||||
if aKind != reflect.Array && aKind != reflect.Slice {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listA, aKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
if bKind != reflect.Array && bKind != reflect.Slice {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listB, bKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
aValue := reflect.ValueOf(listA)
|
||||
bValue := reflect.ValueOf(listB)
|
||||
|
||||
aLen := aValue.Len()
|
||||
bLen := bValue.Len()
|
||||
|
||||
if aLen != bLen {
|
||||
return Fail(t, fmt.Sprintf("lengths don't match: %d != %d", aLen, bLen), msgAndArgs...)
|
||||
}
|
||||
|
||||
// Mark indexes in bValue that we already used
|
||||
visited := make([]bool, bLen)
|
||||
for i := 0; i < aLen; i++ {
|
||||
element := aValue.Index(i).Interface()
|
||||
found := false
|
||||
for j := 0; j < bLen; j++ {
|
||||
if visited[j] {
|
||||
continue
|
||||
}
|
||||
if ObjectsAreEqual(bValue.Index(j).Interface(), element) {
|
||||
visited[j] = true
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return Fail(t, fmt.Sprintf("element %s appears more times in %s than in %s", element, aValue, bValue), msgAndArgs...)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Condition uses a Comparison to assert a complex condition.
|
||||
func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool {
|
||||
result := comp()
|
||||
|
|
@ -636,11 +798,7 @@ func didPanic(f PanicTestFunc) (bool, interface{}) {
|
|||
|
||||
// Panics asserts that the code inside the specified PanicTestFunc panics.
|
||||
//
|
||||
// assert.Panics(t, func(){
|
||||
// GoCrazy()
|
||||
// }, "Calling GoCrazy() should panic")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// assert.Panics(t, func(){ GoCrazy() })
|
||||
func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
|
||||
if funcDidPanic, panicValue := didPanic(f); !funcDidPanic {
|
||||
|
|
@ -650,13 +808,26 @@ func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that
|
||||
// the recovered panic value equals the expected panic value.
|
||||
//
|
||||
// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() })
|
||||
func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
|
||||
funcDidPanic, panicValue := didPanic(f)
|
||||
if !funcDidPanic {
|
||||
return Fail(t, fmt.Sprintf("func %#v should panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...)
|
||||
}
|
||||
if panicValue != expected {
|
||||
return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%v\n\r\tPanic value:\t%v", f, expected, panicValue), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
|
||||
//
|
||||
// assert.NotPanics(t, func(){
|
||||
// RemainCalm()
|
||||
// }, "Calling RemainCalm() should NOT panic")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// assert.NotPanics(t, func(){ RemainCalm() })
|
||||
func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
|
||||
if funcDidPanic, panicValue := didPanic(f); funcDidPanic {
|
||||
|
|
@ -668,9 +839,7 @@ func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
|||
|
||||
// WithinDuration asserts that the two times are within duration delta of each other.
|
||||
//
|
||||
// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second)
|
||||
func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
|
||||
|
||||
dt := expected.Sub(actual)
|
||||
|
|
@ -708,6 +877,8 @@ func toFloat(x interface{}) (float64, bool) {
|
|||
xf = float64(xn)
|
||||
case float64:
|
||||
xf = float64(xn)
|
||||
case time.Duration:
|
||||
xf = float64(xn)
|
||||
default:
|
||||
xok = false
|
||||
}
|
||||
|
|
@ -718,8 +889,6 @@ func toFloat(x interface{}) (float64, bool) {
|
|||
// InDelta asserts that the two numerals are within delta of each other.
|
||||
//
|
||||
// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
||||
|
||||
af, aok := toFloat(expected)
|
||||
|
|
@ -730,7 +899,7 @@ func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs
|
|||
}
|
||||
|
||||
if math.IsNaN(af) {
|
||||
return Fail(t, fmt.Sprintf("Actual must not be NaN"), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("Expected must not be NaN"), msgAndArgs...)
|
||||
}
|
||||
|
||||
if math.IsNaN(bf) {
|
||||
|
|
@ -757,7 +926,7 @@ func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAn
|
|||
expectedSlice := reflect.ValueOf(expected)
|
||||
|
||||
for i := 0; i < actualSlice.Len(); i++ {
|
||||
result := InDelta(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), delta)
|
||||
result := InDelta(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), delta, msgAndArgs...)
|
||||
if !result {
|
||||
return result
|
||||
}
|
||||
|
|
@ -766,6 +935,47 @@ func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAn
|
|||
return true
|
||||
}
|
||||
|
||||
// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
|
||||
func InDeltaMapValues(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
||||
if expected == nil || actual == nil ||
|
||||
reflect.TypeOf(actual).Kind() != reflect.Map ||
|
||||
reflect.TypeOf(expected).Kind() != reflect.Map {
|
||||
return Fail(t, "Arguments must be maps", msgAndArgs...)
|
||||
}
|
||||
|
||||
expectedMap := reflect.ValueOf(expected)
|
||||
actualMap := reflect.ValueOf(actual)
|
||||
|
||||
if expectedMap.Len() != actualMap.Len() {
|
||||
return Fail(t, "Arguments must have the same number of keys", msgAndArgs...)
|
||||
}
|
||||
|
||||
for _, k := range expectedMap.MapKeys() {
|
||||
ev := expectedMap.MapIndex(k)
|
||||
av := actualMap.MapIndex(k)
|
||||
|
||||
if !ev.IsValid() {
|
||||
return Fail(t, fmt.Sprintf("missing key %q in expected map", k), msgAndArgs...)
|
||||
}
|
||||
|
||||
if !av.IsValid() {
|
||||
return Fail(t, fmt.Sprintf("missing key %q in actual map", k), msgAndArgs...)
|
||||
}
|
||||
|
||||
if !InDelta(
|
||||
t,
|
||||
ev.Interface(),
|
||||
av.Interface(),
|
||||
delta,
|
||||
msgAndArgs...,
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func calcRelativeError(expected, actual interface{}) (float64, error) {
|
||||
af, aok := toFloat(expected)
|
||||
if !aok {
|
||||
|
|
@ -776,15 +986,13 @@ func calcRelativeError(expected, actual interface{}) (float64, error) {
|
|||
}
|
||||
bf, bok := toFloat(actual)
|
||||
if !bok {
|
||||
return 0, fmt.Errorf("expected value %q cannot be converted to float", actual)
|
||||
return 0, fmt.Errorf("actual value %q cannot be converted to float", actual)
|
||||
}
|
||||
|
||||
return math.Abs(af-bf) / math.Abs(af), nil
|
||||
}
|
||||
|
||||
// InEpsilon asserts that expected and actual have a relative error less than epsilon
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
|
||||
actualEpsilon, err := calcRelativeError(expected, actual)
|
||||
if err != nil {
|
||||
|
|
@ -792,7 +1000,7 @@ func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAnd
|
|||
}
|
||||
if actualEpsilon > epsilon {
|
||||
return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+
|
||||
" < %#v (actual)", actualEpsilon, epsilon), msgAndArgs...)
|
||||
" < %#v (actual)", epsilon, actualEpsilon), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
|
|
@ -827,13 +1035,11 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m
|
|||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.NoError(t, err) {
|
||||
// assert.Equal(t, actualObj, expectedObj)
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
if err != nil {
|
||||
return Fail(t, fmt.Sprintf("Received unexpected error %q", err), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
|
|
@ -842,16 +1048,13 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
|||
// Error asserts that a function returned an error (i.e. not `nil`).
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.Error(t, err, "An error was expected") {
|
||||
// assert.Equal(t, err, expectedError)
|
||||
// if assert.Error(t, err) {
|
||||
// assert.Equal(t, expectedError, err)
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
|
||||
message := messageFromMsgAndArgs(msgAndArgs...)
|
||||
if err == nil {
|
||||
return Fail(t, "An error is expected but got nil. %s", message)
|
||||
return Fail(t, "An error is expected but got nil.", msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
|
|
@ -861,20 +1064,20 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
|||
// and that it is equal to the provided error.
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.Error(t, err, "An error was expected") {
|
||||
// assert.Equal(t, err, expectedError)
|
||||
// }
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
// assert.EqualError(t, err, expectedErrorString)
|
||||
func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool {
|
||||
|
||||
message := messageFromMsgAndArgs(msgAndArgs...)
|
||||
if !NotNil(t, theError, "An error is expected but got nil. %s", message) {
|
||||
if !Error(t, theError, msgAndArgs...) {
|
||||
return false
|
||||
}
|
||||
s := "An error with value \"%s\" is expected but got \"%s\". %s"
|
||||
return Equal(t, errString, theError.Error(),
|
||||
s, errString, theError.Error(), message)
|
||||
expected := errString
|
||||
actual := theError.Error()
|
||||
// don't need to use deep equals here, we know they are both strings
|
||||
if expected != actual {
|
||||
return Fail(t, fmt.Sprintf("Error message not equal:\n"+
|
||||
"expected: %q\n"+
|
||||
"actual : %q", expected, actual), msgAndArgs...)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// matchRegexp return true if a specified regexp matches a string.
|
||||
|
|
@ -895,8 +1098,6 @@ func matchRegexp(rx interface{}, str interface{}) bool {
|
|||
//
|
||||
// assert.Regexp(t, regexp.MustCompile("start"), "it's starting")
|
||||
// assert.Regexp(t, "start...$", "it's not starting")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
|
||||
match := matchRegexp(rx, str)
|
||||
|
|
@ -912,8 +1113,6 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface
|
|||
//
|
||||
// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
|
||||
// assert.NotRegexp(t, "^start", "it's not starting")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
match := matchRegexp(rx, str)
|
||||
|
||||
|
|
@ -925,7 +1124,7 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf
|
|||
|
||||
}
|
||||
|
||||
// Zero asserts that i is the zero value for its type and returns the truth.
|
||||
// Zero asserts that i is the zero value for its type.
|
||||
func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
|
||||
if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) {
|
||||
return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...)
|
||||
|
|
@ -933,7 +1132,7 @@ func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// NotZero asserts that i is not the zero value for its type and returns the truth.
|
||||
// NotZero asserts that i is not the zero value for its type.
|
||||
func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
|
||||
if i == nil || reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) {
|
||||
return Fail(t, fmt.Sprintf("Should not be zero, but was %v", i), msgAndArgs...)
|
||||
|
|
@ -941,11 +1140,39 @@ func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
|
||||
func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
|
||||
info, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...)
|
||||
}
|
||||
return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...)
|
||||
}
|
||||
if info.IsDir() {
|
||||
return Fail(t, fmt.Sprintf("%q is a directory", path), msgAndArgs...)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
|
||||
func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
|
||||
info, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...)
|
||||
}
|
||||
return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...)
|
||||
}
|
||||
if !info.IsDir() {
|
||||
return Fail(t, fmt.Sprintf("%q is a file", path), msgAndArgs...)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// JSONEq asserts that two JSON strings are equivalent.
|
||||
//
|
||||
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
|
||||
var expectedJSONAsInterface, actualJSONAsInterface interface{}
|
||||
|
||||
|
|
@ -989,9 +1216,8 @@ func diff(expected interface{}, actual interface{}) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
spew.Config.SortKeys = true
|
||||
e := spew.Sdump(expected)
|
||||
a := spew.Sdump(actual)
|
||||
e := spewConfig.Sdump(expected)
|
||||
a := spewConfig.Sdump(actual)
|
||||
|
||||
diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
|
||||
A: difflib.SplitLines(e),
|
||||
|
|
@ -1005,3 +1231,26 @@ func diff(expected interface{}, actual interface{}) string {
|
|||
|
||||
return "\n\nDiff:\n" + diff
|
||||
}
|
||||
|
||||
// validateEqualArgs checks whether provided arguments can be safely used in the
|
||||
// Equal/NotEqual functions.
|
||||
func validateEqualArgs(expected, actual interface{}) error {
|
||||
if isFunction(expected) || isFunction(actual) {
|
||||
return errors.New("cannot take func type as argument")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isFunction(arg interface{}) bool {
|
||||
if arg == nil {
|
||||
return false
|
||||
}
|
||||
return reflect.TypeOf(arg).Kind() == reflect.Func
|
||||
}
|
||||
|
||||
var spewConfig = spew.ConfigState{
|
||||
Indent: " ",
|
||||
DisablePointerAddresses: true,
|
||||
DisableCapacities: true,
|
||||
SortKeys: true,
|
||||
}
|
||||
|
|
|
|||
2
vendor/github.com/stretchr/testify/assert/forward_assertions.go
generated
vendored
2
vendor/github.com/stretchr/testify/assert/forward_assertions.go
generated
vendored
|
|
@ -13,4 +13,4 @@ func New(t TestingT) *Assertions {
|
|||
}
|
||||
}
|
||||
|
||||
//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl
|
||||
//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs
|
||||
|
|
|
|||
61
vendor/github.com/stretchr/testify/assert/http_assertions.go
generated
vendored
61
vendor/github.com/stretchr/testify/assert/http_assertions.go
generated
vendored
|
|
@ -8,16 +8,16 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// httpCode is a helper that returns HTTP code of the response. It returns -1
|
||||
// if building a new request fails.
|
||||
func httpCode(handler http.HandlerFunc, method, url string, values url.Values) int {
|
||||
// httpCode is a helper that returns HTTP code of the response. It returns -1 and
|
||||
// an error if building a new request fails.
|
||||
func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) {
|
||||
w := httptest.NewRecorder()
|
||||
req, err := http.NewRequest(method, url+"?"+values.Encode(), nil)
|
||||
if err != nil {
|
||||
return -1
|
||||
return -1, err
|
||||
}
|
||||
handler(w, req)
|
||||
return w.Code
|
||||
return w.Code, nil
|
||||
}
|
||||
|
||||
// HTTPSuccess asserts that a specified handler returns a success status code.
|
||||
|
|
@ -25,12 +25,19 @@ func httpCode(handler http.HandlerFunc, method, url string, values url.Values) i
|
|||
// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
|
||||
code := httpCode(handler, method, url, values)
|
||||
if code == -1 {
|
||||
func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
|
||||
code, err := httpCode(handler, method, url, values)
|
||||
if err != nil {
|
||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
|
||||
return false
|
||||
}
|
||||
return code >= http.StatusOK && code <= http.StatusPartialContent
|
||||
|
||||
isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent
|
||||
if !isSuccessCode {
|
||||
Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), code))
|
||||
}
|
||||
|
||||
return isSuccessCode
|
||||
}
|
||||
|
||||
// HTTPRedirect asserts that a specified handler returns a redirect status code.
|
||||
|
|
@ -38,12 +45,19 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value
|
|||
// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
|
||||
code := httpCode(handler, method, url, values)
|
||||
if code == -1 {
|
||||
func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
|
||||
code, err := httpCode(handler, method, url, values)
|
||||
if err != nil {
|
||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
|
||||
return false
|
||||
}
|
||||
return code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect
|
||||
|
||||
isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect
|
||||
if !isRedirectCode {
|
||||
Fail(t, fmt.Sprintf("Expected HTTP redirect status code for %q but received %d", url+"?"+values.Encode(), code))
|
||||
}
|
||||
|
||||
return isRedirectCode
|
||||
}
|
||||
|
||||
// HTTPError asserts that a specified handler returns an error status code.
|
||||
|
|
@ -51,12 +65,19 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu
|
|||
// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
|
||||
code := httpCode(handler, method, url, values)
|
||||
if code == -1 {
|
||||
func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
|
||||
code, err := httpCode(handler, method, url, values)
|
||||
if err != nil {
|
||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
|
||||
return false
|
||||
}
|
||||
return code >= http.StatusBadRequest
|
||||
|
||||
isErrorCode := code >= http.StatusBadRequest
|
||||
if !isErrorCode {
|
||||
Fail(t, fmt.Sprintf("Expected HTTP error status code for %q but received %d", url+"?"+values.Encode(), code))
|
||||
}
|
||||
|
||||
return isErrorCode
|
||||
}
|
||||
|
||||
// HTTPBody is a helper that returns HTTP body of the response. It returns
|
||||
|
|
@ -77,7 +98,7 @@ func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) s
|
|||
// assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
|
||||
func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
body := HTTPBody(handler, method, url, values)
|
||||
|
||||
contains := strings.Contains(body, fmt.Sprint(str))
|
||||
|
|
@ -94,12 +115,12 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string,
|
|||
// assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
|
||||
func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
body := HTTPBody(handler, method, url, values)
|
||||
|
||||
contains := strings.Contains(body, fmt.Sprint(str))
|
||||
if contains {
|
||||
Fail(t, "Expected response body for %s to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)
|
||||
Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body))
|
||||
}
|
||||
|
||||
return !contains
|
||||
|
|
|
|||
23
vendor/vendor.json
vendored
23
vendor/vendor.json
vendored
|
|
@ -596,10 +596,13 @@
|
|||
"revisionTime": "2018-02-10T03:43:46Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Lf3uUXTkKK5DJ37BxQvxO1Fq+K8=",
|
||||
"checksumSHA1": "/5cvgU+J4l7EhMXTK76KaCAfOuU=",
|
||||
"comment": "v1.0.0-3-g6d21280",
|
||||
"path": "github.com/davecgh/go-spew/spew",
|
||||
"revision": "6d212800a42e8ab5c146b8ace3490ee17e5225f9"
|
||||
"revision": "346938d642f2ec3594ed81d874461961cd0faa76",
|
||||
"revisionTime": "2016-10-29T20:57:26Z",
|
||||
"version": "v1.1.0",
|
||||
"versionExact": "v1.1.0"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "5k4kiVJsn0CilLDx+gMjglXY6vs=",
|
||||
|
|
@ -1202,11 +1205,21 @@
|
|||
"revisionTime": "2018-03-15T01:07:03Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "iydUphwYqZRq3WhstEdGsbvBAKs=",
|
||||
"checksumSHA1": "xqtDGN726+wHn43myII/VQ4vQn8=",
|
||||
"path": "github.com/stretchr/objx",
|
||||
"revision": "facf9a85c22f48d2f52f2380e4efce1768749a89",
|
||||
"revisionTime": "2018-01-06T01:13:53Z",
|
||||
"version": "v0.1",
|
||||
"versionExact": "v0.1"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "pbq5LYckA7/rqe03l9MtXDekmRg=",
|
||||
"comment": "v1.1.4-4-g976c720",
|
||||
"path": "github.com/stretchr/testify/assert",
|
||||
"revision": "d77da356e56a7428ad25149ca77381849a6a5232",
|
||||
"revisionTime": "2016-06-15T09:26:46Z"
|
||||
"revision": "12b6f73e6084dad08a7c6e575284b177ecafbc71",
|
||||
"revisionTime": "2018-01-31T22:23:50Z",
|
||||
"version": "v1.2.1",
|
||||
"versionExact": "v1.2.1"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "GQ9bu6PuydK3Yor1JgtVKUfEJm8=",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
---
|
||||
modeline: |
|
||||
vim: set ft=pandoc:
|
||||
description: |
|
||||
The Hyper-V Packer builder is able to create Hyper-V virtual machines and
|
||||
export them.
|
||||
|
|
@ -102,7 +104,7 @@ can be configured for this builder.
|
|||
|
||||
- `skip_export` (boolean) - If true skips VM export. If you are interested only in the vhd/vhdx files, you can enable this option. This will create
|
||||
inline disks which improves the build performance. There will not be any copying of source vhds to temp directory. This defaults to false.
|
||||
|
||||
|
||||
- `enable_dynamic_memory` (boolean) - If true enable dynamic memory for virtual machine.
|
||||
This defaults to false.
|
||||
|
||||
|
|
@ -245,67 +247,9 @@ strings are all typed in sequence. It is an array only to improve readability
|
|||
within the template.
|
||||
|
||||
The boot command is "typed" character for character over the virtual keyboard
|
||||
to the machine, simulating a human actually typing the keyboard. There are
|
||||
a set of special keys available. If these are in your boot command, they
|
||||
will be replaced by the proper key:
|
||||
to the machine, simulating a human actually typing the keyboard.
|
||||
|
||||
- `<bs>` - Backspace
|
||||
|
||||
- `<del>` - Delete
|
||||
|
||||
- `<enter>` and `<return>` - Simulates an actual "enter" or "return" keypress.
|
||||
|
||||
- `<esc>` - Simulates pressing the escape key.
|
||||
|
||||
- `<tab>` - Simulates pressing the tab key.
|
||||
|
||||
- `<f1>` - `<f12>` - Simulates pressing a function key.
|
||||
|
||||
- `<up>` `<down>` `<left>` `<right>` - Simulates pressing an arrow key.
|
||||
|
||||
- `<spacebar>` - Simulates pressing the spacebar.
|
||||
|
||||
- `<insert>` - Simulates pressing the insert key.
|
||||
|
||||
- `<home>` `<end>` - Simulates pressing the home and end keys.
|
||||
|
||||
- `<pageUp>` `<pageDown>` - Simulates pressing the page up and page down keys.
|
||||
|
||||
- `<leftAlt>` `<rightAlt>` - Simulates pressing the alt key.
|
||||
|
||||
- `<leftCtrl>` `<rightCtrl>` - Simulates pressing the ctrl key.
|
||||
|
||||
- `<leftShift>` `<rightShift>` - Simulates pressing the shift key.
|
||||
|
||||
- `<leftAltOn>` `<rightAltOn>` - Simulates pressing and holding the alt key.
|
||||
|
||||
- `<leftCtrlOn>` `<rightCtrlOn>` - Simulates pressing and holding the ctrl key.
|
||||
|
||||
- `<leftShiftOn>` `<rightShiftOn>` - Simulates pressing and holding the shift key.
|
||||
|
||||
- `<leftAltOff>` `<rightAltOff>` - Simulates releasing a held alt key.
|
||||
|
||||
- `<leftCtrlOff>` `<rightCtrlOff>` - Simulates releasing a held ctrl key.
|
||||
|
||||
- `<leftShiftOff>` `<rightShiftOff>` - Simulates releasing a held shift key.
|
||||
|
||||
- `<wait>` `<wait5>` `<wait10>` - Adds a 1, 5 or 10 second pause before
|
||||
sending any additional keys. This is useful if you have to generally wait
|
||||
for the UI to update before typing more.
|
||||
|
||||
When using modifier keys `ctrl`, `alt`, `shift` ensure that you release them,
|
||||
otherwise they will be held down until the machine reboots. Use lowercase
|
||||
characters as well inside modifiers. For example: to simulate ctrl+c use
|
||||
`<leftCtrlOn>c<leftCtrlOff>`.
|
||||
|
||||
In addition to the special keys, each command to type is treated as a
|
||||
[template engine](/docs/templates/engine.html).
|
||||
The available variables are:
|
||||
|
||||
- `HTTPIP` and `HTTPPort` - The IP and port, respectively of an HTTP server
|
||||
that is started serving the directory specified by the `http_directory`
|
||||
configuration parameter. If `http_directory` isn't specified, these will
|
||||
be blank!
|
||||
<%= partial "partials/builders/boot-command" %>
|
||||
|
||||
Example boot command. This is actually a working boot command used to start
|
||||
an Ubuntu 12.04 installer:
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
---
|
||||
modeline: |
|
||||
vim: set ft=pandoc:
|
||||
description: |-
|
||||
The Hyper-V Packer builder is able to clone an existing Hyper-V virtual machine and export them.
|
||||
layout: "docs"
|
||||
|
|
@ -245,64 +247,9 @@ strings are all typed in sequence. It is an array only to improve readability
|
|||
within the template.
|
||||
|
||||
The boot command is "typed" character for character over the virtual keyboard
|
||||
to the machine, simulating a human actually typing the keyboard. There are
|
||||
a set of special keys available. If these are in your boot command, they
|
||||
will be replaced by the proper key:
|
||||
to the machine, simulating a human actually typing the keyboard.
|
||||
|
||||
- `<bs>` - Backspace
|
||||
|
||||
- `<del>` - Delete
|
||||
|
||||
- `<enter>` and `<return>` - Simulates an actual "enter" or "return" keypress.
|
||||
|
||||
- `<esc>` - Simulates pressing the escape key.
|
||||
|
||||
- `<tab>` - Simulates pressing the tab key.
|
||||
|
||||
- `<f1>` - `<f12>` - Simulates pressing a function key.
|
||||
|
||||
- `<up>` `<down>` `<left>` `<right>` - Simulates pressing an arrow key.
|
||||
|
||||
- `<spacebar>` - Simulates pressing the spacebar.
|
||||
|
||||
- `<insert>` - Simulates pressing the insert key.
|
||||
|
||||
- `<home>` `<end>` - Simulates pressing the home and end keys.
|
||||
|
||||
- `<pageUp>` `<pageDown>` - Simulates pressing the page up and page down keys.
|
||||
|
||||
- `<leftAlt>` `<rightAlt>` - Simulates pressing the alt key.
|
||||
|
||||
- `<leftCtrl>` `<rightCtrl>` - Simulates pressing the ctrl key.
|
||||
|
||||
- `<leftShift>` `<rightShift>` - Simulates pressing the shift key.
|
||||
|
||||
- `<leftAltOn>` `<rightAltOn>` - Simulates pressing and holding the alt key.
|
||||
|
||||
- `<leftCtrlOn>` `<rightCtrlOn>` - Simulates pressing and holding the ctrl key.
|
||||
|
||||
- `<leftShiftOn>` `<rightShiftOn>` - Simulates pressing and holding the shift key.
|
||||
|
||||
- `<leftAltOff>` `<rightAltOff>` - Simulates releasing a held alt key.
|
||||
|
||||
- `<leftCtrlOff>` `<rightCtrlOff>` - Simulates releasing a held ctrl key.
|
||||
|
||||
- `<leftShiftOff>` `<rightShiftOff>` - Simulates releasing a held shift key.
|
||||
|
||||
- `<wait>` `<wait5>` `<wait10>` - Adds a 1, 5 or 10 second pause before
|
||||
sending any additional keys. This is useful if you have to generally wait
|
||||
for the UI to update before typing more.
|
||||
|
||||
When using modifier keys `ctrl`, `alt`, `shift` ensure that you release them, otherwise they will be held down until the machine reboots. Use lowercase characters as well inside modifiers. For example: to simulate ctrl+c use `<leftCtrlOn>c<leftCtrlOff>`.
|
||||
|
||||
In addition to the special keys, each command to type is treated as a
|
||||
[configuration template](/docs/templates/configuration-templates.html).
|
||||
The available variables are:
|
||||
|
||||
* `HTTPIP` and `HTTPPort` - The IP and port, respectively of an HTTP server
|
||||
that is started serving the directory specified by the `http_directory`
|
||||
configuration parameter. If `http_directory` isn't specified, these will
|
||||
be blank!
|
||||
<%= partial "partials/builders/boot-command" %>
|
||||
|
||||
Example boot command. This is actually a working boot command used to start
|
||||
an Ubuntu 12.04 installer:
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
---
|
||||
modeline: |
|
||||
vim: set ft=pandoc:
|
||||
description: |
|
||||
The Parallels Packer builder is able to create Parallels Desktop for Mac
|
||||
virtual machines and export them in the PVM format, starting from an ISO
|
||||
|
|
@ -246,68 +248,9 @@ template.
|
|||
|
||||
The boot command is "typed" character for character (using the Parallels
|
||||
Virtualization SDK, see [Parallels Builder](/docs/builders/parallels.html))
|
||||
simulating a human actually typing the keyboard. There are a set of special keys
|
||||
available. If these are in your boot command, they will be replaced by the
|
||||
proper key:
|
||||
simulating a human actually typing the keyboard.
|
||||
|
||||
- `<bs>` - Backspace
|
||||
|
||||
- `<del>` - Delete
|
||||
|
||||
- `<enter>` and `<return>` - Simulates an actual "enter" or "return" keypress.
|
||||
|
||||
- `<esc>` - Simulates pressing the escape key.
|
||||
|
||||
- `<tab>` - Simulates pressing the tab key.
|
||||
|
||||
- `<f1>` - `<f12>` - Simulates pressing a function key.
|
||||
|
||||
- `<up>` `<down>` `<left>` `<right>` - Simulates pressing an arrow key.
|
||||
|
||||
- `<spacebar>` - Simulates pressing the spacebar.
|
||||
|
||||
- `<insert>` - Simulates pressing the insert key.
|
||||
|
||||
- `<home>` `<end>` - Simulates pressing the home and end keys.
|
||||
|
||||
- `<pageUp>` `<pageDown>` - Simulates pressing the page up and page down keys.
|
||||
|
||||
- `<leftAlt>` `<rightAlt>` - Simulates pressing the alt key.
|
||||
|
||||
- `<leftCtrl>` `<rightCtrl>` - Simulates pressing the ctrl key.
|
||||
|
||||
- `<leftShift>` `<rightShift>` - Simulates pressing the shift key.
|
||||
|
||||
- `<leftAltOn>` `<rightAltOn>` - Simulates pressing and holding the alt key.
|
||||
|
||||
- `<leftCtrlOn>` `<rightCtrlOn>` - Simulates pressing and holding the ctrl key.
|
||||
|
||||
- `<leftShiftOn>` `<rightShiftOn>` - Simulates pressing and holding the shift key.
|
||||
|
||||
- `<leftAltOff>` `<rightAltOff>` - Simulates releasing a held alt key.
|
||||
|
||||
- `<leftCtrlOff>` `<rightCtrlOff>` - Simulates releasing a held ctrl key.
|
||||
|
||||
- `<leftShiftOff>` `<rightShiftOff>` - Simulates releasing a held shift key.
|
||||
|
||||
- `<wait>` `<wait5>` `<wait10>` - Adds a 1, 5 or 10 second pause before
|
||||
sending any additional keys. This is useful if you have to generally wait
|
||||
for the UI to update before typing more.
|
||||
|
||||
When using modifier keys `ctrl`, `alt`, `shift` ensure that you release them,
|
||||
otherwise they will be held down until the machine reboots. Use lowercase
|
||||
characters as well inside modifiers.
|
||||
|
||||
For example: to simulate ctrl+c use `<leftCtrlOn>c<leftCtrlOff>`.
|
||||
|
||||
In addition to the special keys, each command to type is treated as a
|
||||
[template engine](/docs/templates/engine.html). The
|
||||
available variables are:
|
||||
|
||||
- `HTTPIP` and `HTTPPort` - The IP and port, respectively of an HTTP server
|
||||
that is started serving the directory specified by the `http_directory`
|
||||
configuration parameter. If `http_directory` isn't specified, these will be
|
||||
blank!
|
||||
<%= partial "partials/builders/boot-command" %>
|
||||
|
||||
Example boot command. This is actually a working boot command used to start an
|
||||
Ubuntu 12.04 installer:
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
---
|
||||
modeline: |
|
||||
vim: set ft=pandoc:
|
||||
description: |
|
||||
This Parallels builder is able to create Parallels Desktop for Mac virtual
|
||||
machines and export them in the PVM format, starting from an existing PVM
|
||||
|
|
@ -179,60 +181,9 @@ template.
|
|||
|
||||
The boot command is "typed" character for character (using the Parallels
|
||||
Virtualization SDK, see [Parallels Builder](/docs/builders/parallels.html))
|
||||
simulating a human actually typing the keyboard. There are a set of special keys
|
||||
available. If these are in your boot command, they will be replaced by the
|
||||
proper key:
|
||||
simulating a human actually typing the keyboard.
|
||||
|
||||
- `<bs>` - Backspace
|
||||
|
||||
- `<del>` - Delete
|
||||
|
||||
- `<enter>` and `<return>` - Simulates an actual "enter" or "return" keypress.
|
||||
|
||||
- `<esc>` - Simulates pressing the escape key.
|
||||
|
||||
- `<tab>` - Simulates pressing the tab key.
|
||||
|
||||
- `<f1>` - `<f12>` - Simulates pressing a function key.
|
||||
|
||||
- `<up>` `<down>` `<left>` `<right>` - Simulates pressing an arrow key.
|
||||
|
||||
- `<spacebar>` - Simulates pressing the spacebar.
|
||||
|
||||
- `<insert>` - Simulates pressing the insert key.
|
||||
|
||||
- `<home>` `<end>` - Simulates pressing the home and end keys.
|
||||
|
||||
- `<pageUp>` `<pageDown>` - Simulates pressing the page up and page down keys.
|
||||
|
||||
- `<leftAlt>` `<rightAlt>` - Simulates pressing the alt key.
|
||||
|
||||
- `<leftCtrl>` `<rightCtrl>` - Simulates pressing the ctrl key.
|
||||
|
||||
- `<leftShift>` `<rightShift>` - Simulates pressing the shift key.
|
||||
|
||||
- `<leftAltOn>` `<rightAltOn>` - Simulates pressing and holding the alt key.
|
||||
|
||||
- `<leftCtrlOn>` `<rightCtrlOn>` - Simulates pressing and holding the ctrl key.
|
||||
|
||||
- `<leftShiftOn>` `<rightShiftOn>` - Simulates pressing and holding the shift key.
|
||||
|
||||
- `<leftAltOff>` `<rightAltOff>` - Simulates releasing a held alt key.
|
||||
|
||||
- `<leftCtrlOff>` `<rightCtrlOff>` - Simulates releasing a held ctrl key.
|
||||
|
||||
- `<leftShiftOff>` `<rightShiftOff>` - Simulates releasing a held shift key.
|
||||
|
||||
- `<wait>` `<wait5>` `<wait10>` - Adds a 1, 5 or 10 second pause before
|
||||
sending any additional keys. This is useful if you have to generally wait
|
||||
for the UI to update before typing more.
|
||||
|
||||
In addition to the special keys, each command to type is treated as a
|
||||
[template engine](/docs/templates/engine.html). The
|
||||
available variables are:
|
||||
|
||||
For more examples of various boot commands, see the sample projects from our
|
||||
[community templates page](/community-tools.html#templates).
|
||||
<%= partial "partials/builders/boot-command" %>
|
||||
|
||||
## prlctl Commands
|
||||
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
---
|
||||
modeline: |
|
||||
vim: set ft=pandoc:
|
||||
description: |
|
||||
The Qemu Packer builder is able to create KVM and Xen virtual machine images.
|
||||
layout: docs
|
||||
|
|
@ -110,8 +112,8 @@ Linux server and have not enabled X11 forwarding (`ssh -X`).
|
|||
### Optional:
|
||||
|
||||
- `accelerator` (string) - The accelerator type to use when running the VM.
|
||||
This may be `none`, `kvm`, `tcg`, `hax`, or `xen`. The appropriate software
|
||||
must have already been installed on your build machine to use the accelerator
|
||||
This may be `none`, `kvm`, `tcg`, `hax`, or `xen`. The appropriate software
|
||||
must have already been installed on your build machine to use the accelerator
|
||||
you specified. When no accelerator is specified, Packer will try to use `kvm`
|
||||
if it is available but will default to `tcg` otherwise.
|
||||
|
||||
|
|
@ -370,69 +372,7 @@ default 100ms delay. The delay alleviates issues with latency and CPU
|
|||
contention. For local builds you can tune this delay by specifying
|
||||
e.g. `PACKER_KEY_INTERVAL=10ms` to speed through the boot command.
|
||||
|
||||
There are a set of special keys available. If these are in your boot
|
||||
command, they will be replaced by the proper key:
|
||||
|
||||
- `<bs>` - Backspace
|
||||
|
||||
- `<del>` - Delete
|
||||
|
||||
- `<enter>` and `<return>` - Simulates an actual "enter" or "return" keypress.
|
||||
|
||||
- `<esc>` - Simulates pressing the escape key.
|
||||
|
||||
- `<tab>` - Simulates pressing the tab key.
|
||||
|
||||
- `<f1>` - `<f12>` - Simulates pressing a function key.
|
||||
|
||||
- `<up>` `<down>` `<left>` `<right>` - Simulates pressing an arrow key.
|
||||
|
||||
- `<spacebar>` - Simulates pressing the spacebar.
|
||||
|
||||
- `<insert>` - Simulates pressing the insert key.
|
||||
|
||||
- `<home>` `<end>` - Simulates pressing the home and end keys.
|
||||
|
||||
- `<pageUp>` `<pageDown>` - Simulates pressing the page up and page down keys.
|
||||
|
||||
- `<leftAlt>` `<rightAlt>` - Simulates pressing the alt key.
|
||||
|
||||
- `<leftCtrl>` `<rightCtrl>` - Simulates pressing the ctrl key.
|
||||
|
||||
- `<leftShift>` `<rightShift>` - Simulates pressing the shift key.
|
||||
|
||||
- `<leftAltOn>` `<rightAltOn>` - Simulates pressing and holding the alt key.
|
||||
|
||||
- `<leftCtrlOn>` `<rightCtrlOn>` - Simulates pressing and holding the ctrl key.
|
||||
|
||||
- `<leftShiftOn>` `<rightShiftOn>` - Simulates pressing and holding the shift key.
|
||||
|
||||
- `<leftAltOff>` `<rightAltOff>` - Simulates releasing a held alt key.
|
||||
|
||||
- `<leftCtrlOff>` `<rightCtrlOff>` - Simulates releasing a held ctrl key.
|
||||
|
||||
- `<leftShiftOff>` `<rightShiftOff>` - Simulates releasing a held shift key.
|
||||
|
||||
- `<wait>` `<wait5>` `<wait10>` - Adds a 1, 5 or 10 second pause before
|
||||
sending any additional keys. This is useful if you have to generally wait
|
||||
for the UI to update before typing more.
|
||||
|
||||
- `<waitXX>` - Add user defined time.Duration pause before sending any
|
||||
additional keys. For example `<wait10m>` or `<wait1m20s>`
|
||||
|
||||
When using modifier keys `ctrl`, `alt`, `shift` ensure that you release them,
|
||||
otherwise they will be held down until the machine reboots. Use lowercase
|
||||
characters as well inside modifiers. For example: to simulate ctrl+c use
|
||||
`<leftCtrlOn>c<leftCtrlOff>`.
|
||||
|
||||
In addition to the special keys, each command to type is treated as a
|
||||
[template engine](/docs/templates/engine.html). The
|
||||
available variables are:
|
||||
|
||||
- `HTTPIP` and `HTTPPort` - The IP and port, respectively of an HTTP server
|
||||
that is started serving the directory specified by the `http_directory`
|
||||
configuration parameter. If `http_directory` isn't specified, these will be
|
||||
blank!
|
||||
<%= partial "partials/builders/boot-command" %>
|
||||
|
||||
Example boot command. This is actually a working boot command used to start an
|
||||
CentOS 6.4 installer:
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
---
|
||||
modeline: |
|
||||
vim: set ft=pandoc:
|
||||
description: |
|
||||
The VirtualBox Packer builder is able to create VirtualBox virtual machines
|
||||
and export them in the OVF format, starting from an ISO image.
|
||||
|
|
@ -331,71 +333,10 @@ As documented above, the `boot_command` is an array of strings. The strings are
|
|||
all typed in sequence. It is an array only to improve readability within the
|
||||
template.
|
||||
|
||||
The boot command is "typed" character for character over a VNC connection to the
|
||||
machine, simulating a human actually typing the keyboard. There are a set of
|
||||
special keys available. If these are in your boot command, they will be replaced
|
||||
by the proper key:
|
||||
The boot command is sent to the VM through the `VBoxManage` utility in as few
|
||||
invocations as possible.
|
||||
|
||||
- `<bs>` - Backspace
|
||||
|
||||
- `<del>` - Delete
|
||||
|
||||
- `<enter>` and `<return>` - Simulates an actual "enter" or "return" keypress.
|
||||
|
||||
- `<esc>` - Simulates pressing the escape key.
|
||||
|
||||
- `<tab>` - Simulates pressing the tab key.
|
||||
|
||||
- `<f1>` - `<f12>` - Simulates pressing a function key.
|
||||
|
||||
- `<up>` `<down>` `<left>` `<right>` - Simulates pressing an arrow key.
|
||||
|
||||
- `<spacebar>` - Simulates pressing the spacebar.
|
||||
|
||||
- `<insert>` - Simulates pressing the insert key.
|
||||
|
||||
- `<home>` `<end>` - Simulates pressing the home and end keys.
|
||||
|
||||
- `<pageUp>` `<pageDown>` - Simulates pressing the page up and page down keys.
|
||||
|
||||
- `<leftAlt>` `<rightAlt>` - Simulates pressing the alt key.
|
||||
|
||||
- `<leftCtrl>` `<rightCtrl>` - Simulates pressing the ctrl key.
|
||||
|
||||
- `<leftShift>` `<rightShift>` - Simulates pressing the shift key.
|
||||
|
||||
- `<leftAltOn>` `<rightAltOn>` - Simulates pressing and holding the alt key.
|
||||
|
||||
- `<leftCtrlOn>` `<rightCtrlOn>` - Simulates pressing and holding the
|
||||
ctrl key.
|
||||
|
||||
- `<leftShiftOn>` `<rightShiftOn>` - Simulates pressing and holding the
|
||||
shift key.
|
||||
|
||||
- `<leftAltOff>` `<rightAltOff>` - Simulates releasing a held alt key.
|
||||
|
||||
- `<leftCtrlOff>` `<rightCtrlOff>` - Simulates releasing a held ctrl key.
|
||||
|
||||
- `<leftShiftOff>` `<rightShiftOff>` - Simulates releasing a held shift key.
|
||||
|
||||
- `<wait>` `<wait5>` `<wait10>` - Adds a 1, 5 or 10 second pause before
|
||||
sending any additional keys. This is useful if you have to generally wait
|
||||
for the UI to update before typing more.
|
||||
|
||||
When using modifier keys `ctrl`, `alt`, `shift` ensure that you release them,
|
||||
otherwise they will be held down until the machine reboots. Use lowercase
|
||||
characters as well inside modifiers.
|
||||
|
||||
For example: to simulate ctrl+c use `<leftCtrlOn>c<leftCtrlOff>`.
|
||||
|
||||
In addition to the special keys, each command to type is treated as a
|
||||
[template engine](/docs/templates/engine.html). The
|
||||
available variables are:
|
||||
|
||||
- `HTTPIP` and `HTTPPort` - The IP and port, respectively of an HTTP server
|
||||
that is started serving the directory specified by the `http_directory`
|
||||
configuration parameter. If `http_directory` isn't specified, these will be
|
||||
blank!
|
||||
<%= partial "partials/builders/boot-command" %>
|
||||
|
||||
Example boot command. This is actually a working boot command used to start an
|
||||
Ubuntu 12.04 installer:
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
---
|
||||
modeline: |
|
||||
vim: set ft=pandoc:
|
||||
description: |
|
||||
This VirtualBox Packer builder is able to create VirtualBox virtual machines
|
||||
and export them in the OVF format, starting from an existing OVF/OVA (exported
|
||||
|
|
@ -293,65 +295,10 @@ As documented above, the `boot_command` is an array of strings. The strings are
|
|||
all typed in sequence. It is an array only to improve readability within the
|
||||
template.
|
||||
|
||||
The boot command is "typed" character for character over a VNC connection to the
|
||||
machine, simulating a human actually typing the keyboard. There are a set of
|
||||
special keys available. If these are in your boot command, they will be replaced
|
||||
by the proper key:
|
||||
The boot command is sent to the VM through the `VBoxManage` utility in as few
|
||||
invocations as possible.
|
||||
|
||||
- `<bs>` - Backspace
|
||||
|
||||
- `<del>` - Delete
|
||||
|
||||
- `<enter>` and `<return>` - Simulates an actual "enter" or "return" keypress.
|
||||
|
||||
- `<esc>` - Simulates pressing the escape key.
|
||||
|
||||
- `<tab>` - Simulates pressing the tab key.
|
||||
|
||||
- `<f1>` - `<f12>` - Simulates pressing a function key.
|
||||
|
||||
- `<up>` `<down>` `<left>` `<right>` - Simulates pressing an arrow key.
|
||||
|
||||
- `<spacebar>` - Simulates pressing the spacebar.
|
||||
|
||||
- `<insert>` - Simulates pressing the insert key.
|
||||
|
||||
- `<home>` `<end>` - Simulates pressing the home and end keys.
|
||||
|
||||
- `<pageUp>` `<pageDown>` - Simulates pressing the page up and page down keys.
|
||||
|
||||
- `<leftAlt>` `<rightAlt>` - Simulates pressing the alt key.
|
||||
|
||||
- `<leftCtrl>` `<rightCtrl>` - Simulates pressing the ctrl key.
|
||||
|
||||
- `<leftShift>` `<rightShift>` - Simulates pressing the shift key.
|
||||
|
||||
- `<leftAltOn>` `<rightAltOn>` - Simulates pressing and holding the alt key.
|
||||
|
||||
- `<leftCtrlOn>` `<rightCtrlOn>` - Simulates pressing and holding the
|
||||
ctrl key.
|
||||
|
||||
- `<leftShiftOn>` `<rightShiftOn>` - Simulates pressing and holding the
|
||||
shift key.
|
||||
|
||||
- `<leftAltOff>` `<rightAltOff>` - Simulates releasing a held alt key.
|
||||
|
||||
- `<leftCtrlOff>` `<rightCtrlOff>` - Simulates releasing a held ctrl key.
|
||||
|
||||
- `<leftShiftOff>` `<rightShiftOff>` - Simulates releasing a held shift key.
|
||||
|
||||
- `<wait>` `<wait5>` `<wait10>` - Adds a 1, 5 or 10 second pause before
|
||||
sending any additional keys. This is useful if you have to generally wait
|
||||
for the UI to update before typing more.
|
||||
|
||||
In addition to the special keys, each command to type is treated as a
|
||||
[template engine](/docs/templates/engine.html). The
|
||||
available variables are:
|
||||
|
||||
- `HTTPIP` and `HTTPPort` - The IP and port, respectively of an HTTP server
|
||||
that is started serving the directory specified by the `http_directory`
|
||||
configuration parameter. If `http_directory` isn't specified, these will be
|
||||
blank!
|
||||
<%= partial "partials/builders/boot-command" %>
|
||||
|
||||
Example boot command. This is actually a working boot command used to start an
|
||||
Ubuntu 12.04 installer:
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
---
|
||||
modeline: |
|
||||
vim: set ft=pandoc:
|
||||
description: |
|
||||
This VMware Packer builder is able to create VMware virtual machines from an
|
||||
ISO file as a source. It currently supports building virtual machines on hosts
|
||||
|
|
@ -108,7 +110,7 @@ builder.
|
|||
is full. By default this is set to 40,000 (about 40 GB).
|
||||
|
||||
- `disk_type_id` (string) - The type of VMware virtual disk to create. This
|
||||
option is for advanced usage.
|
||||
option is for advanced usage.
|
||||
|
||||
For desktop VMware clients:
|
||||
|
||||
|
|
@ -122,7 +124,7 @@ builder.
|
|||
`5` | Compressed disk optimized for streaming.
|
||||
|
||||
The default is "1".
|
||||
|
||||
|
||||
For ESXi, this defaults to "zeroedthick". The available options for ESXi
|
||||
are: `zeroedthick`, `eagerzeroedthick`, `thin`, `rdm:dev`, `rdmp:dev`,
|
||||
`2gbsparse`.
|
||||
|
|
@ -425,67 +427,7 @@ default 100ms delay. The delay alleviates issues with latency and CPU
|
|||
contention. For local builds you can tune this delay by specifying
|
||||
e.g. `PACKER_KEY_INTERVAL=10ms` to speed through the boot command.
|
||||
|
||||
There are a set of special keys available. If these are in your boot
|
||||
command, they will be replaced by the proper key:
|
||||
|
||||
- `<bs>` - Backspace
|
||||
|
||||
- `<del>` - Delete
|
||||
|
||||
- `<enter>` and `<return>` - Simulates an actual "enter" or "return" keypress.
|
||||
|
||||
- `<esc>` - Simulates pressing the escape key.
|
||||
|
||||
- `<tab>` - Simulates pressing the tab key.
|
||||
|
||||
- `<f1>` - `<f12>` - Simulates pressing a function key.
|
||||
|
||||
- `<up>` `<down>` `<left>` `<right>` - Simulates pressing an arrow key.
|
||||
|
||||
- `<spacebar>` - Simulates pressing the spacebar.
|
||||
|
||||
- `<insert>` - Simulates pressing the insert key.
|
||||
|
||||
- `<home>` `<end>` - Simulates pressing the home and end keys.
|
||||
|
||||
- `<pageUp>` `<pageDown>` - Simulates pressing the page up and page down keys.
|
||||
|
||||
- `<leftAlt>` `<rightAlt>` - Simulates pressing the alt key.
|
||||
|
||||
- `<leftCtrl>` `<rightCtrl>` - Simulates pressing the ctrl key.
|
||||
|
||||
- `<leftShift>` `<rightShift>` - Simulates pressing the shift key.
|
||||
|
||||
- `<leftAltOn>` `<rightAltOn>` - Simulates pressing and holding the alt key.
|
||||
|
||||
- `<leftCtrlOn>` `<rightCtrlOn>` - Simulates pressing and holding the ctrl key.
|
||||
|
||||
- `<leftShiftOn>` `<rightShiftOn>` - Simulates pressing and holding the shift key.
|
||||
|
||||
- `<leftAltOff>` `<rightAltOff>` - Simulates releasing a held alt key.
|
||||
|
||||
- `<leftCtrlOff>` `<rightCtrlOff>` - Simulates releasing a held ctrl key.
|
||||
|
||||
- `<leftShiftOff>` `<rightShiftOff>` - Simulates releasing a held shift key.
|
||||
|
||||
- `<wait>` `<wait5>` `<wait10>` - Adds a 1, 5 or 10 second pause before
|
||||
sending any additional keys. This is useful if you have to generally wait
|
||||
for the UI to update before typing more.
|
||||
|
||||
When using modifier keys `ctrl`, `alt`, `shift` ensure that you release them,
|
||||
otherwise they will be held down until the machine reboots. Use lowercase
|
||||
characters as well inside modifiers.
|
||||
|
||||
For example: to simulate ctrl+c use `<leftCtrlOn>c<leftCtrlOff>`.
|
||||
|
||||
In addition to the special keys, each command to type is treated as a
|
||||
[template engine](/docs/templates/engine.html). The
|
||||
available variables are:
|
||||
|
||||
- `HTTPIP` and `HTTPPort` - The IP and port, respectively of an HTTP server
|
||||
that is started serving the directory specified by the `http_directory`
|
||||
configuration parameter. If `http_directory` isn't specified, these will be
|
||||
blank!
|
||||
<%= partial "partials/builders/boot-command" %>
|
||||
|
||||
Example boot command. This is actually a working boot command used to start an
|
||||
Ubuntu 12.04 installer:
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
---
|
||||
modeline: |
|
||||
vim: set ft=pandoc:
|
||||
description: |
|
||||
This VMware Packer builder is able to create VMware virtual machines from an
|
||||
existing VMware virtual machine (a VMX file). It currently supports building
|
||||
|
|
@ -198,63 +200,7 @@ default 100ms delay. The delay alleviates issues with latency and CPU
|
|||
contention. For local builds you can tune this delay by specifying
|
||||
e.g. `PACKER_KEY_INTERVAL=10ms` to speed through the boot command.
|
||||
|
||||
There are a set of special keys available. If these are in your boot
|
||||
command, they will be replaced by the proper key:
|
||||
|
||||
- `<bs>` - Backspace
|
||||
|
||||
- `<del>` - Delete
|
||||
|
||||
- `<enter>` and `<return>` - Simulates an actual "enter" or "return" keypress.
|
||||
|
||||
- `<esc>` - Simulates pressing the escape key.
|
||||
|
||||
- `<tab>` - Simulates pressing the tab key.
|
||||
|
||||
- `<f1>` - `<f12>` - Simulates pressing a function key.
|
||||
|
||||
- `<up>` `<down>` `<left>` `<right>` - Simulates pressing an arrow key.
|
||||
|
||||
- `<spacebar>` - Simulates pressing the spacebar.
|
||||
|
||||
- `<insert>` - Simulates pressing the insert key.
|
||||
|
||||
- `<home>` `<end>` - Simulates pressing the home and end keys.
|
||||
|
||||
- `<pageUp>` `<pageDown>` - Simulates pressing the page up and page down keys.
|
||||
|
||||
- `<leftAlt>` `<rightAlt>` - Simulates pressing the alt key.
|
||||
|
||||
- `<leftCtrl>` `<rightCtrl>` - Simulates pressing the ctrl key.
|
||||
|
||||
- `<leftShift>` `<rightShift>` - Simulates pressing the shift key.
|
||||
|
||||
- `<leftAltOn>` `<rightAltOn>` - Simulates pressing and holding the alt key.
|
||||
|
||||
- `<leftCtrlOn>` `<rightCtrlOn>` - Simulates pressing and holding the ctrl
|
||||
key.
|
||||
|
||||
- `<leftShiftOn>` `<rightShiftOn>` - Simulates pressing and holding the
|
||||
shift key.
|
||||
|
||||
- `<leftAltOff>` `<rightAltOff>` - Simulates releasing a held alt key.
|
||||
|
||||
- `<leftCtrlOff>` `<rightCtrlOff>` - Simulates releasing a held ctrl key.
|
||||
|
||||
- `<leftShiftOff>` `<rightShiftOff>` - Simulates releasing a held shift key.
|
||||
|
||||
- `<wait>` `<wait5>` `<wait10>` - Adds a 1, 5 or 10 second pause before
|
||||
sending any additional keys. This is useful if you have to generally wait
|
||||
for the UI to update before typing more.
|
||||
|
||||
In addition to the special keys, each command to type is treated as a
|
||||
[template engine](/docs/templates/engine.html). The
|
||||
available variables are:
|
||||
|
||||
- `HTTPIP` and `HTTPPort` - The IP and port, respectively of an HTTP server
|
||||
that is started serving the directory specified by the `http_directory`
|
||||
configuration parameter. If `http_directory` isn't specified, these will be
|
||||
blank!
|
||||
<%= partial "partials/builders/boot-command" %>
|
||||
|
||||
Example boot command. This is actually a working boot command used to start an
|
||||
Ubuntu 12.04 installer:
|
||||
70
website/source/partials/builders/_boot-command.html.md
Normal file
70
website/source/partials/builders/_boot-command.html.md
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
There are a set of special keys available. If these are in your boot
|
||||
command, they will be replaced by the proper key:
|
||||
|
||||
- `<bs>` - Backspace
|
||||
|
||||
- `<del>` - Delete
|
||||
|
||||
- `<enter> <return>` - Simulates an actual "enter" or "return" keypress.
|
||||
|
||||
- `<esc>` - Simulates pressing the escape key.
|
||||
|
||||
- `<tab>` - Simulates pressing the tab key.
|
||||
|
||||
- `<f1> - <f12>` - Simulates pressing a function key.
|
||||
|
||||
- `<up> <down> <left> <right>` - Simulates pressing an arrow key.
|
||||
|
||||
- `<spacebar>` - Simulates pressing the spacebar.
|
||||
|
||||
- `<insert>` - Simulates pressing the insert key.
|
||||
|
||||
- `<home> <end>` - Simulates pressing the home and end keys.
|
||||
|
||||
- `<pageUp> <pageDown>` - Simulates pressing the page up and page down keys.
|
||||
|
||||
- `<menu>` - Simulates pressing the Menu key.
|
||||
|
||||
- `<leftAlt> <rightAlt>` - Simulates pressing the alt key.
|
||||
|
||||
- `<leftCtrl> <rightCtrl>` - Simulates pressing the ctrl key.
|
||||
|
||||
- `<leftShift> <rightShift>` - Simulates pressing the shift key.
|
||||
|
||||
- `<leftSuper> <rightSuper>` - Simulates pressing the ⌘ or Windows key.
|
||||
|
||||
- `<wait> <wait5> <wait10>` - Adds a 1, 5 or 10 second pause before
|
||||
sending any additional keys. This is useful if you have to generally wait
|
||||
for the UI to update before typing more.
|
||||
|
||||
- `<waitXX>` - Add an arbitrary pause before sending any additional keys. The
|
||||
format of `XX` is a sequence of positive decimal numbers, each with
|
||||
optional fraction and a unit suffix, such as `300ms`, `1.5h` or `2h45m`.
|
||||
Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. For example
|
||||
`<wait10m>` or `<wait1m20s>`
|
||||
|
||||
|
||||
### On/Off variants
|
||||
|
||||
Any printable keyboard character, and of these "special" expressions, with the
|
||||
exception of the `<wait>` types, can also be toggled on or off. For example, to
|
||||
simulate ctrl+c, use `<leftCtrlOn>c<leftCtrlOff>`. Be sure to release them,
|
||||
otherwise they will be held down until the machine reboots.
|
||||
|
||||
To hold the `c` key down, you would use `<cOn>`. Likewise, `<cOff>` to release.
|
||||
|
||||
### Templates inside boot command
|
||||
|
||||
In addition to the special keys, each command to type is treated as a
|
||||
[template engine](/docs/templates/engine.html). The
|
||||
available variables are:
|
||||
|
||||
- `HTTPIP` and `HTTPPort` - The IP and port, respectively of an HTTP server
|
||||
that is started serving the directory specified by the `http_directory`
|
||||
configuration parameter. If `http_directory` isn't specified, these will be
|
||||
blank!
|
||||
- `Name` - The name of the VM.
|
||||
|
||||
For more examples of various boot commands, see the sample projects from our
|
||||
[community templates page](/community-tools.html#templates).
|
||||
|
||||
Loading…
Reference in a new issue