From eda84cb2d38dc0edc53b705661e9f680d104273e Mon Sep 17 00:00:00 2001 From: Jake Champlin Date: Thu, 21 Jan 2016 18:19:11 -0500 Subject: [PATCH 1/3] Prevalidate Hardware Specs on Linux Prevalidates hardware resources on Linux platforms for Virtualbox and VMware builders. This is currently only available on Linux, as enabling for both Darwin and Windows platforms, relies on cgo bindings that would prevent effective cross-compilation. Packer will now fail to build and validate templates if the template is requesting that the VM to be created would allocate more system resources than the host system has available. This _however_ doesn't catch parallel builds that overflow the hosts resources, will probably still need a better error message for VM's failing to boot in that case. Example Outputs: ``` $ $GOPATH/bin/packer build -debug ./vmware-iso.json Debug mode enabled. Builds will not be parallelized. vmware-iso output will be in this color. 2 error(s) occurred: * Unavailable Resources: RAM - Requested - 204800000MB - Available 21721MB * Unavailable Resources: Disk - Requested - 4000000000MB - Available 76701MB ``` ``` $ $GOPATH/bin/packer build -debug ./vbox-iso.json Debug mode enabled. Builds will not be parallelized. virtualbox-iso output will be in this color. 2 error(s) occurred: * Unavailable Resources: RAM - Requested - 10240000MB - Available 21721MB * Unavailable Resources: Disk - Requested - 1000000000MB - Available 76701MB ``` --- .../virtualbox/common/vboxmanage_config.go | 24 +++++++++++- builder/virtualbox/iso/builder.go | 6 +++ builder/vmware/common/vmx_config.go | 23 +++++++++++- builder/vmware/iso/builder.go | 6 +++ common/resources_linux.go | 37 +++++++++++++++++++ common/resources_universal.go | 11 ++++++ 6 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 common/resources_linux.go create mode 100644 common/resources_universal.go diff --git a/builder/virtualbox/common/vboxmanage_config.go b/builder/virtualbox/common/vboxmanage_config.go index 1670aee0d..b243400a1 100644 --- a/builder/virtualbox/common/vboxmanage_config.go +++ b/builder/virtualbox/common/vboxmanage_config.go @@ -1,6 +1,10 @@ package common import ( + "fmt" + "strconv" + + "github.com/mitchellh/packer/common" "github.com/mitchellh/packer/template/interpolate" ) @@ -11,7 +15,25 @@ type VBoxManageConfig struct { func (c *VBoxManageConfig) Prepare(ctx *interpolate.Context) []error { if c.VBoxManage == nil { c.VBoxManage = make([][]string, 0) + return nil + } + + var errs []error + var err error + var desiredMem uint64 + + for _, cmd := range c.VBoxManage { + if cmd[2] == "--memory" { + desiredMem, err = strconv.ParseUint(cmd[3], 10, 64) + if err != nil { + errs = append(errs, fmt.Errorf("Error parsing string: %s", err)) + } + } + } + + if err = common.AvailableMem(desiredMem); err != nil { + errs = append(errs, fmt.Errorf("Unavailable Resources: %s", err)) } - return nil + return errs } diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index b8b08959a..c30fd83ee 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -152,6 +152,12 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.GuestAdditionsSHA256 = strings.ToLower(b.config.GuestAdditionsSHA256) } + // Determine if DiskSize is able to be allocated + if err = common.AvailableDisk(uint64(b.config.DiskSize)); err != nil { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf("Unavailable Resources: %s", err)) + } + // Warnings if b.config.ShutdownCommand == "" { warnings = append(warnings, diff --git a/builder/vmware/common/vmx_config.go b/builder/vmware/common/vmx_config.go index aac16d1e0..6a707abe0 100644 --- a/builder/vmware/common/vmx_config.go +++ b/builder/vmware/common/vmx_config.go @@ -1,6 +1,10 @@ package common import ( + "fmt" + "strconv" + + "github.com/mitchellh/packer/common" "github.com/mitchellh/packer/template/interpolate" ) @@ -10,5 +14,22 @@ type VMXConfig struct { } func (c *VMXConfig) Prepare(ctx *interpolate.Context) []error { - return nil + var errs []error + var err error + var desiredMem uint64 + + for k, v := range c.VMXData { + if k == "memsize" { + desiredMem, err = strconv.ParseUint(v, 10, 64) + if err != nil { + errs = append(errs, fmt.Errorf("Error parsing string: %s", err)) + } + } + } + + if err = common.AvailableMem(desiredMem); err != nil { + errs = append(errs, fmt.Errorf("Unavailable Resources: %s", err)) + } + + return errs } diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 4d428745c..ea6f88b5a 100755 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -171,6 +171,12 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } } + // Determine if DiskSize is able to be allocated + if err = common.AvailableDisk(uint64(b.config.DiskSize)); err != nil { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf("Unavailable Resources: %s", err)) + } + // Warnings if b.config.ShutdownCommand == "" { warnings = append(warnings, diff --git a/common/resources_linux.go b/common/resources_linux.go new file mode 100644 index 000000000..fd9b0cf16 --- /dev/null +++ b/common/resources_linux.go @@ -0,0 +1,37 @@ +package common + +import ( + "fmt" + "os" + + sigar "github.com/cloudfoundry/gosigar" +) + +func AvailableMem(desired uint64) error { + free := freeMem() + if desired > free { + return fmt.Errorf("RAM - Requested - %dMB - Available %dMB", desired, free) + } + return nil +} + +func freeMem() uint64 { + mem := sigar.Mem{} + mem.Get() + return (mem.Free / 1024 / 1024) +} + +func AvailableDisk(desired uint64) error { + free := freeDisk() + if desired > free { + return fmt.Errorf("Disk - Requested - %dMB - Available %dMB", desired, free) + } + return nil +} + +func freeDisk() uint64 { + disk := sigar.FileSystemUsage{} + workingDirectory, _ := os.Getwd() + disk.Get(workingDirectory) + return (disk.Avail / 1024) +} diff --git a/common/resources_universal.go b/common/resources_universal.go new file mode 100644 index 000000000..3ecc7d2e8 --- /dev/null +++ b/common/resources_universal.go @@ -0,0 +1,11 @@ +// +build !linux + +package common + +func AvailableMem(desired uint64) error { + return nil +} + +func AvailableDisk(desired uint64) error { + return nil +} From f01578c91e140f67a0b56773be2b3f7d08d8faa9 Mon Sep 17 00:00:00 2001 From: Jake Champlin Date: Mon, 25 Jan 2016 12:01:56 -0500 Subject: [PATCH 2/3] Only validate vmware resources on local hosts Disable resource validation when `remote_type` is specified --- builder/vmware/common/vmx_config.go | 15 +++++++++------ builder/vmware/iso/builder.go | 12 +++++++----- builder/vmware/vmx/config.go | 2 +- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/builder/vmware/common/vmx_config.go b/builder/vmware/common/vmx_config.go index 6a707abe0..9b3162382 100644 --- a/builder/vmware/common/vmx_config.go +++ b/builder/vmware/common/vmx_config.go @@ -13,16 +13,19 @@ type VMXConfig struct { VMXDataPost map[string]string `mapstructure:"vmx_data_post"` } -func (c *VMXConfig) Prepare(ctx *interpolate.Context) []error { +func (c *VMXConfig) Prepare(ctx *interpolate.Context, remoteType string) []error { var errs []error var err error var desiredMem uint64 - for k, v := range c.VMXData { - if k == "memsize" { - desiredMem, err = strconv.ParseUint(v, 10, 64) - if err != nil { - errs = append(errs, fmt.Errorf("Error parsing string: %s", err)) + // Validate memory resources, only on local hosts + if remoteType == "" { + for k, v := range c.VMXData { + if k == "memsize" { + desiredMem, err = strconv.ParseUint(v, 10, 64) + if err != nil { + errs = append(errs, fmt.Errorf("Error parsing string: %s", err)) + } } } } diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index ea6f88b5a..9f6ad62ab 100755 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -93,7 +93,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { 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.VMXConfig.Prepare(&b.config.ctx)...) + errs = packer.MultiErrorAppend(errs, b.config.VMXConfig.Prepare(&b.config.ctx, b.config.RemoteType)...) if b.config.DiskName == "" { b.config.DiskName = "disk" @@ -171,10 +171,12 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } } - // Determine if DiskSize is able to be allocated - if err = common.AvailableDisk(uint64(b.config.DiskSize)); err != nil { - errs = packer.MultiErrorAppend(errs, - fmt.Errorf("Unavailable Resources: %s", err)) + // Determine if DiskSize is able to be allocated, only when running locally + if b.config.RemoteType == "" { + if err = common.AvailableDisk(uint64(b.config.DiskSize)); err != nil { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf("Unavailable Resources: %s", err)) + } } // Warnings diff --git a/builder/vmware/vmx/config.go b/builder/vmware/vmx/config.go index 4f93db9f3..87aad7d59 100644 --- a/builder/vmware/vmx/config.go +++ b/builder/vmware/vmx/config.go @@ -63,7 +63,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { 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)...) - errs = packer.MultiErrorAppend(errs, c.VMXConfig.Prepare(&c.ctx)...) + errs = packer.MultiErrorAppend(errs, c.VMXConfig.Prepare(&c.ctx, c.RemoteType)...) if c.SourcePath == "" { errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is blank, but is required")) From 6e8bfd3ce99a272be59589ab204831a9addb3f67 Mon Sep 17 00:00:00 2001 From: Jake Champlin Date: Mon, 25 Jan 2016 12:54:15 -0500 Subject: [PATCH 3/3] Correctly call prepare function in test --- builder/vmware/common/vmx_config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/vmware/common/vmx_config_test.go b/builder/vmware/common/vmx_config_test.go index 4b8172cb0..0278ae74c 100644 --- a/builder/vmware/common/vmx_config_test.go +++ b/builder/vmware/common/vmx_config_test.go @@ -11,7 +11,7 @@ func TestVMXConfigPrepare(t *testing.T) { "two": "bar", } - errs := c.Prepare(testConfigTemplate(t)) + errs := c.Prepare(testConfigTemplate(t), "") if len(errs) > 0 { t.Fatalf("bad: %#v", errs) }