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
```
This commit is contained in:
Jake Champlin 2016-01-21 18:19:11 -05:00
parent 1f8ebab4de
commit eda84cb2d3
6 changed files with 105 additions and 2 deletions

View File

@ -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
}
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 errs
}

View File

@ -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,

View File

@ -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
}

View File

@ -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,

37
common/resources_linux.go Normal file
View File

@ -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)
}

View File

@ -0,0 +1,11 @@
// +build !linux
package common
func AvailableMem(desired uint64) error {
return nil
}
func AvailableDisk(desired uint64) error {
return nil
}