Commit bfdc6ea6 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

builder/virtualbox: support http server for ovf [GH-1169]

parent 118e0b8a
package common package common
import ( import (
"errors"
"fmt" "fmt"
"github.com/mitchellh/packer/packer"
"time" "time"
"github.com/mitchellh/packer/packer"
) )
type RunConfig struct { type RunConfig struct {
Headless bool `mapstructure:"headless"` Headless bool `mapstructure:"headless"`
RawBootWait string `mapstructure:"boot_wait"` RawBootWait string `mapstructure:"boot_wait"`
HTTPDir string `mapstructure:"http_directory"`
HTTPPortMin uint `mapstructure:"http_port_min"`
HTTPPortMax uint `mapstructure:"http_port_max"`
BootWait time.Duration `` BootWait time.Duration ``
} }
...@@ -18,8 +24,17 @@ func (c *RunConfig) Prepare(t *packer.ConfigTemplate) []error { ...@@ -18,8 +24,17 @@ func (c *RunConfig) Prepare(t *packer.ConfigTemplate) []error {
c.RawBootWait = "10s" c.RawBootWait = "10s"
} }
if c.HTTPPortMin == 0 {
c.HTTPPortMin = 8000
}
if c.HTTPPortMax == 0 {
c.HTTPPortMax = 9000
}
templates := map[string]*string{ templates := map[string]*string{
"boot_wait": &c.RawBootWait, "boot_wait": &c.RawBootWait,
"http_directory": &c.HTTPDir,
} }
errs := make([]error, 0) errs := make([]error, 0)
...@@ -37,5 +52,10 @@ func (c *RunConfig) Prepare(t *packer.ConfigTemplate) []error { ...@@ -37,5 +52,10 @@ func (c *RunConfig) Prepare(t *packer.ConfigTemplate) []error {
errs = append(errs, fmt.Errorf("Failed parsing boot_wait: %s", err)) errs = append(errs, fmt.Errorf("Failed parsing boot_wait: %s", err))
} }
if c.HTTPPortMin > c.HTTPPortMax {
errs = append(errs,
errors.New("http_port_min must be less than http_port_max"))
}
return errs return errs
} }
package common
import (
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"math/rand"
"net"
"net/http"
)
// This step creates and runs the HTTP server that is serving files from the
// directory specified by the 'http_directory` configuration parameter in the
// template.
//
// Uses:
// ui packer.Ui
//
// Produces:
// http_port int - The port the HTTP server started on.
type StepHTTPServer struct {
HTTPDir string
HTTPPortMin uint
HTTPPortMax uint
l net.Listener
}
func (s *StepHTTPServer) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
var httpPort uint = 0
if s.HTTPDir == "" {
state.Put("http_port", httpPort)
return multistep.ActionContinue
}
// Find an available TCP port for our HTTP server
var httpAddr string
portRange := int(s.HTTPPortMax - s.HTTPPortMin)
for {
var err error
var offset uint = 0
if portRange > 0 {
// Intn will panic if portRange == 0, so we do a check.
offset = uint(rand.Intn(portRange))
}
httpPort = offset + s.HTTPPortMin
httpAddr = fmt.Sprintf(":%d", httpPort)
log.Printf("Trying port: %d", httpPort)
s.l, err = net.Listen("tcp", httpAddr)
if err == nil {
break
}
}
ui.Say(fmt.Sprintf("Starting HTTP server on port %d", httpPort))
// Start the HTTP server and run it in the background
fileServer := http.FileServer(http.Dir(s.HTTPDir))
server := &http.Server{Addr: httpAddr, Handler: fileServer}
go server.Serve(s.l)
// Save the address into the state so it can be accessed in the future
state.Put("http_port", httpPort)
return multistep.ActionContinue
}
func (s *StepHTTPServer) Cleanup(multistep.StateBag) {
if s.l != nil {
// Close the listener so that the HTTP server stops
s.l.Close()
}
}
...@@ -42,9 +42,6 @@ type config struct { ...@@ -42,9 +42,6 @@ type config struct {
GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"` GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"`
GuestOSType string `mapstructure:"guest_os_type"` GuestOSType string `mapstructure:"guest_os_type"`
HardDriveInterface string `mapstructure:"hard_drive_interface"` HardDriveInterface string `mapstructure:"hard_drive_interface"`
HTTPDir string `mapstructure:"http_directory"`
HTTPPortMin uint `mapstructure:"http_port_min"`
HTTPPortMax uint `mapstructure:"http_port_max"`
ISOChecksum string `mapstructure:"iso_checksum"` ISOChecksum string `mapstructure:"iso_checksum"`
ISOChecksumType string `mapstructure:"iso_checksum_type"` ISOChecksumType string `mapstructure:"iso_checksum_type"`
ISOInterface string `mapstructure:"iso_interface"` ISOInterface string `mapstructure:"iso_interface"`
...@@ -103,14 +100,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { ...@@ -103,14 +100,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
b.config.GuestOSType = "Other" b.config.GuestOSType = "Other"
} }
if b.config.HTTPPortMin == 0 {
b.config.HTTPPortMin = 8000
}
if b.config.HTTPPortMax == 0 {
b.config.HTTPPortMax = 9000
}
if b.config.ISOInterface == "" { if b.config.ISOInterface == "" {
b.config.ISOInterface = "ide" b.config.ISOInterface = "ide"
} }
...@@ -125,7 +114,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { ...@@ -125,7 +114,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
"guest_additions_sha256": &b.config.GuestAdditionsSHA256, "guest_additions_sha256": &b.config.GuestAdditionsSHA256,
"guest_os_type": &b.config.GuestOSType, "guest_os_type": &b.config.GuestOSType,
"hard_drive_interface": &b.config.HardDriveInterface, "hard_drive_interface": &b.config.HardDriveInterface,
"http_directory": &b.config.HTTPDir,
"iso_checksum": &b.config.ISOChecksum, "iso_checksum": &b.config.ISOChecksum,
"iso_checksum_type": &b.config.ISOChecksumType, "iso_checksum_type": &b.config.ISOChecksumType,
"iso_interface": &b.config.ISOInterface, "iso_interface": &b.config.ISOInterface,
...@@ -175,11 +163,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { ...@@ -175,11 +163,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
errs, errors.New("hard_drive_interface can only be ide or sata")) errs, errors.New("hard_drive_interface can only be ide or sata"))
} }
if b.config.HTTPPortMin > b.config.HTTPPortMax {
errs = packer.MultiErrorAppend(
errs, errors.New("http_port_min must be less than http_port_max"))
}
if b.config.ISOChecksumType == "" { if b.config.ISOChecksumType == "" {
errs = packer.MultiErrorAppend( errs = packer.MultiErrorAppend(
errs, errors.New("The iso_checksum_type must be specified.")) errs, errors.New("The iso_checksum_type must be specified."))
...@@ -298,7 +281,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe ...@@ -298,7 +281,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&common.StepCreateFloppy{ &common.StepCreateFloppy{
Files: b.config.FloppyFiles, Files: b.config.FloppyFiles,
}, },
new(stepHTTPServer), &vboxcommon.StepHTTPServer{
HTTPDir: b.config.HTTPDir,
HTTPPortMin: b.config.HTTPPortMin,
HTTPPortMax: b.config.HTTPPortMax,
},
new(vboxcommon.StepSuppressMessages), new(vboxcommon.StepSuppressMessages),
new(stepCreateVM), new(stepCreateVM),
new(stepCreateDisk), new(stepCreateDisk),
......
...@@ -61,6 +61,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe ...@@ -61,6 +61,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&common.StepCreateFloppy{ &common.StepCreateFloppy{
Files: b.config.FloppyFiles, Files: b.config.FloppyFiles,
}, },
&vboxcommon.StepHTTPServer{
HTTPDir: b.config.HTTPDir,
HTTPPortMin: b.config.HTTPPortMin,
HTTPPortMax: b.config.HTTPPortMax,
},
&vboxcommon.StepDownloadGuestAdditions{ &vboxcommon.StepDownloadGuestAdditions{
GuestAdditionsMode: b.config.GuestAdditionsMode, GuestAdditionsMode: b.config.GuestAdditionsMode,
GuestAdditionsURL: b.config.GuestAdditionsURL, GuestAdditionsURL: b.config.GuestAdditionsURL,
......
...@@ -109,6 +109,21 @@ each category, the available options are alphabetized and described. ...@@ -109,6 +109,21 @@ each category, the available options are alphabetized and described.
machine being built. When this value is set to true, the machine will machine being built. When this value is set to true, the machine will
start without a console. start without a console.
* `http_directory` (string) - Path to a directory to serve using an HTTP
server. The files in this directory will be available over HTTP that will
be requestable from the virtual machine. This is useful for hosting
kickstart files and so on. By default this is "", which means no HTTP
server will be started. The address and port of the HTTP server will be
available as variables in `boot_command`. This is covered in more detail
below.
* `http_port_min` and `http_port_max` (integer) - These are the minimum and
maximum port to use for the HTTP server started to serve the `http_directory`.
Because Packer often runs in parallel, Packer will choose a randomly available
port in this range to run the HTTP server. If you want to force the HTTP
server to be on one port, make this minimum and maximum port the same.
By default the values are 8000 and 9000, respectively.
* `import_flags` (array of strings) - Additional flags to pass to * `import_flags` (array of strings) - Additional flags to pass to
`VBoxManage import`. This can be used to add additional command-line flags `VBoxManage import`. This can be used to add additional command-line flags
such as `--eula-accept` to accept a EULA in the OVF. such as `--eula-accept` to accept a EULA in the OVF.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment