packer-cn/packer/provisioner.go

152 lines
4.3 KiB
Go
Raw Normal View History

2013-05-22 16:25:03 -04:00
package packer
import (
"context"
"fmt"
"log"
"sync"
2013-12-21 00:36:41 -05:00
"time"
"github.com/hashicorp/packer/helper/config"
)
2013-05-22 16:25:03 -04:00
// A provisioner is responsible for installing and configuring software
// on a machine prior to building the actual image.
type Provisioner interface {
2013-06-06 20:02:02 -04:00
// Prepare is called with a set of configurations to setup the
// internal state of the provisioner. The multiple configurations
// should be merged in some sane way.
2013-06-06 20:07:42 -04:00
Prepare(...interface{}) error
2013-05-22 16:25:03 -04:00
// Provision is called to actually provision the machine. A context is
// given for cancellation, a UI is given to communicate with the user, and
// a communicator is given that is guaranteed to be connected to some
// machine so that provisioning can be done.
Provision(context.Context, Ui, Communicator, *ProvisionHookData) error
2013-05-22 16:25:03 -04:00
}
2013-05-24 00:13:18 -04:00
// A HookedProvisioner represents a provisioner and information describing it
type HookedProvisioner struct {
Provisioner Provisioner
Config interface{}
TypeName string
}
2013-05-24 00:13:18 -04:00
// A Hook implementation that runs the given provisioners.
type ProvisionHook struct {
// The provisioners to run as part of the hook. These should already
// be prepared (by calling Prepare) at some earlier stage.
Provisioners []*HookedProvisioner
2013-05-24 00:13:18 -04:00
}
type ProvisionHookData struct {
WinRMPassword string
}
func NewProvisionHookData() ProvisionHookData {
// this is the function we use to create the provisionhookdata, and as a
// bonus the initialized state applies defaults that act as passthroughs so
// that when prepare is called the first time, we can save interpolating
// these values until provisioner run time.
hookData := ProvisionHookData{
WinRMPassword: `{{.WinRMPassword}}`,
}
return hookData
}
2013-05-24 00:13:18 -04:00
// Runs the provisioners in order.
func (h *ProvisionHook) Run(ctx context.Context, name string, ui Ui, comm Communicator, data interface{}) error {
// Shortcut
if len(h.Provisioners) == 0 {
return nil
}
generatedData := NewProvisionHookData()
err := config.Decode(&generatedData, &config.DecodeOpts{Interpolate: false}, data)
if err != nil {
log.Printf("Failed to decode provisioner generated data: %s", err)
}
log.Printf("Megan decoded data: %#v", generatedData)
if comm == nil {
return fmt.Errorf(
"No communicator found for provisioners! This is usually because the\n" +
"`communicator` config was set to \"none\". If you have any provisioners\n" +
"then a communicator is required. Please fix this to continue.")
}
2017-11-03 02:31:32 -04:00
for _, p := range h.Provisioners {
ts := CheckpointReporter.AddSpan(p.TypeName, "provisioner", p.Config)
2017-11-03 02:31:32 -04:00
err := p.Provisioner.Provision(ctx, ui, comm, &generatedData)
2017-11-03 02:31:32 -04:00
ts.End(err)
if err != nil {
return err
}
2013-05-24 00:13:18 -04:00
}
return nil
2013-05-24 00:13:18 -04:00
}
2013-08-30 20:03:55 -04:00
2013-12-21 00:36:41 -05:00
// PausedProvisioner is a Provisioner implementation that pauses before
// the provisioner is actually run.
type PausedProvisioner struct {
PauseBefore time.Duration
Provisioner Provisioner
}
func (p *PausedProvisioner) Prepare(raws ...interface{}) error {
return p.Provisioner.Prepare(raws...)
}
func (p *PausedProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData *ProvisionHookData) error {
2013-12-21 00:36:41 -05:00
// Use a select to determine if we get cancelled during the wait
ui.Say(fmt.Sprintf("Pausing %s before the next provisioner...", p.PauseBefore))
2013-12-21 00:36:41 -05:00
select {
case <-time.After(p.PauseBefore):
case <-ctx.Done():
return ctx.Err()
2013-12-21 00:36:41 -05:00
}
return p.Provisioner.Provision(ctx, ui, comm, generatedData)
2013-12-21 00:36:41 -05:00
}
// DebuggedProvisioner is a Provisioner implementation that waits until a key
// press before the provisioner is actually run.
type DebuggedProvisioner struct {
Provisioner Provisioner
cancelCh chan struct{}
doneCh chan struct{}
lock sync.Mutex
}
func (p *DebuggedProvisioner) Prepare(raws ...interface{}) error {
return p.Provisioner.Prepare(raws...)
}
func (p *DebuggedProvisioner) Provision(ctx context.Context, ui Ui, comm Communicator, generatedData *ProvisionHookData) error {
// Use a select to determine if we get cancelled during the wait
message := "Pausing before the next provisioner . Press enter to continue."
result := make(chan string, 1)
go func() {
line, err := ui.Ask(message)
if err != nil {
log.Printf("Error asking for input: %s", err)
}
result <- line
}()
select {
case <-result:
case <-ctx.Done():
return ctx.Err()
}
return p.Provisioner.Provision(ctx, ui, comm, generatedData)
}