builder/cloudstack: Support http server (#5017)
builder/cloudstack: Added docs for http server Closes hashicorp/packer#4949
This commit is contained in:
parent
7f0a404307
commit
c01f6d8708
|
@ -56,7 +56,14 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
// Build the steps.
|
// Build the steps.
|
||||||
steps := []multistep.Step{
|
steps := []multistep.Step{
|
||||||
&stepPrepareConfig{},
|
&stepPrepareConfig{},
|
||||||
&stepCreateInstance{},
|
&common.StepHTTPServer{
|
||||||
|
HTTPDir: b.config.HTTPDir,
|
||||||
|
HTTPPortMin: b.config.HTTPPortMin,
|
||||||
|
HTTPPortMax: b.config.HTTPPortMax,
|
||||||
|
},
|
||||||
|
&stepCreateInstance{
|
||||||
|
Ctx: b.config.ctx,
|
||||||
|
},
|
||||||
&stepSetupNetworking{},
|
&stepSetupNetworking{},
|
||||||
&communicator.StepConnect{
|
&communicator.StepConnect{
|
||||||
Config: &b.config.Comm,
|
Config: &b.config.Comm,
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
// Config holds all the details needed to configure the builder.
|
// Config holds all the details needed to configure the builder.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
common.PackerConfig `mapstructure:",squash"`
|
common.PackerConfig `mapstructure:",squash"`
|
||||||
|
common.HTTPConfig `mapstructure:",squash"`
|
||||||
Comm communicator.Config `mapstructure:",squash"`
|
Comm communicator.Config `mapstructure:",squash"`
|
||||||
|
|
||||||
APIURL string `mapstructure:"api_url"`
|
APIURL string `mapstructure:"api_url"`
|
||||||
|
@ -64,6 +65,11 @@ func NewConfig(raws ...interface{}) (*Config, error) {
|
||||||
err := config.Decode(c, &config.DecodeOpts{
|
err := config.Decode(c, &config.DecodeOpts{
|
||||||
Interpolate: true,
|
Interpolate: true,
|
||||||
InterpolateContext: &c.ctx,
|
InterpolateContext: &c.ctx,
|
||||||
|
InterpolateFilter: &interpolate.RenderFilter{
|
||||||
|
Exclude: []string{
|
||||||
|
"user_data",
|
||||||
|
},
|
||||||
|
},
|
||||||
}, raws...)
|
}, raws...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -2,16 +2,27 @@ package cloudstack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
"github.com/mitchellh/multistep"
|
"github.com/mitchellh/multistep"
|
||||||
"github.com/xanzy/go-cloudstack/cloudstack"
|
"github.com/xanzy/go-cloudstack/cloudstack"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// userDataTemplateData represents variables for user_data interpolation
|
||||||
|
type userDataTemplateData struct {
|
||||||
|
HTTPIP string
|
||||||
|
HTTPPort uint
|
||||||
|
}
|
||||||
|
|
||||||
// stepCreateInstance represents a Packer build step that creates CloudStack instances.
|
// stepCreateInstance represents a Packer build step that creates CloudStack instances.
|
||||||
type stepCreateInstance struct{}
|
type stepCreateInstance struct {
|
||||||
|
Ctx interpolate.Context
|
||||||
|
}
|
||||||
|
|
||||||
// Run executes the Packer build step that creates a CloudStack instance.
|
// Run executes the Packer build step that creates a CloudStack instance.
|
||||||
func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction {
|
func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
@ -65,7 +76,26 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.UserData != "" {
|
if config.UserData != "" {
|
||||||
ud, err := getUserData(config.UserData, config.HTTPGetOnly)
|
httpPort := state.Get("http_port").(uint)
|
||||||
|
hostIp, err := hostIP()
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Ctx.Data = &userDataTemplateData{
|
||||||
|
hostIp,
|
||||||
|
httpPort,
|
||||||
|
}
|
||||||
|
|
||||||
|
renderedUserData, err := interpolate.Render(config.UserData, &s.Ctx)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error rendering user_data: %s", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
ud, err := getUserData(renderedUserData, config.HTTPGetOnly)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
|
@ -159,3 +189,20 @@ func getUserData(userData string, httpGETOnly bool) (string, error) {
|
||||||
|
|
||||||
return ud, nil
|
return ud, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hostIP() (string, error) {
|
||||||
|
addrs, err := net.InterfaceAddrs()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||||||
|
if ipnet.IP.To4() != nil {
|
||||||
|
return ipnet.IP.String(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("No host IP found")
|
||||||
|
}
|
||||||
|
|
|
@ -83,10 +83,24 @@ builder.
|
||||||
- `disk_size` (int) - The size (in GB) of the root disk of the new instance.
|
- `disk_size` (int) - The size (in GB) of the root disk of the new instance.
|
||||||
This option is only available when using `source_template`.
|
This option is only available when using `source_template`.
|
||||||
|
|
||||||
|
- `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 `user_data`. This is covered in more detail below.
|
||||||
|
|
||||||
- `http_get_only` (boolean) - Some cloud providers only allow HTTP GET calls to
|
- `http_get_only` (boolean) - Some cloud providers only allow HTTP GET calls to
|
||||||
their CloudStack API. If using such a provider, you need to set this to `true`
|
their CloudStack API. If using such a provider, you need to set this to `true`
|
||||||
in order for the provider to only make GET calls and no POST calls.
|
in order for the provider to only make GET calls and no POST calls.
|
||||||
|
|
||||||
|
- `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.
|
||||||
|
|
||||||
- `hypervisor` (string) - The target hypervisor (e.g. `XenServer`, `KVM`) for
|
- `hypervisor` (string) - The target hypervisor (e.g. `XenServer`, `KVM`) for
|
||||||
the new template. This option is required when using `source_iso`.
|
the new template. This option is required when using `source_iso`.
|
||||||
|
|
||||||
|
@ -123,6 +137,15 @@ builder.
|
||||||
- `use_local_ip_address` (boolean) - Set to `true` to indicate that the
|
- `use_local_ip_address` (boolean) - Set to `true` to indicate that the
|
||||||
provisioners should connect to the local IP address of the instance.
|
provisioners should connect to the local IP address of the instance.
|
||||||
|
|
||||||
|
## User Data
|
||||||
|
|
||||||
|
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!
|
||||||
|
|
||||||
## Basic Example
|
## Basic Example
|
||||||
|
|
||||||
Here is a basic example.
|
Here is a basic example.
|
||||||
|
|
Loading…
Reference in New Issue