From 93f5bbbf45d6107c96e8950be1a52ae03058942e Mon Sep 17 00:00:00 2001 From: Marc Carmier Date: Sun, 12 Mar 2017 23:46:35 +0100 Subject: [PATCH] Add waiting for key between provisioner --- packer/core.go | 1 + packer/provisioner.go | 88 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/packer/core.go b/packer/core.go index c8ac2cfb7..e6d82aaed 100644 --- a/packer/core.go +++ b/packer/core.go @@ -152,6 +152,7 @@ func (c *Core) Build(n string) (Build, error) { Provisioner: provisioner, } } + if config.PackerDebug provisioners = append(provisioners, coreBuildProvisioner{ pType: rawP.Type, diff --git a/packer/provisioner.go b/packer/provisioner.go index 93411808d..c9af06f54 100644 --- a/packer/provisioner.go +++ b/packer/provisioner.go @@ -2,6 +2,7 @@ package packer import ( "fmt" + "log" "sync" "time" ) @@ -168,3 +169,90 @@ func (p *PausedProvisioner) Cancel() { func (p *PausedProvisioner) provision(result chan<- error, ui Ui, comm Communicator) { result <- p.Provisioner.Provision(ui, comm) } + +// DebuggedProvisioner is a Provisioner implementation that wait 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(ui Ui, comm Communicator) error { + p.lock.Lock() + cancelCh := make(chan struct{}) + p.cancelCh = cancelCh + + // Setup the done channel, which is trigger when we're done + doneCh := make(chan struct{}) + defer close(doneCh) + p.doneCh = doneCh + p.lock.Unlock() + + defer func() { + p.lock.Lock() + defer p.lock.Unlock() + if p.cancelCh == cancelCh { + p.cancelCh = nil + } + if p.doneCh == doneCh { + p.doneCh = nil + } + }() + + // 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 <-cancelCh: + return nil + } + + provDoneCh := make(chan error, 1) + go p.provision(provDoneCh, ui, comm) + + select { + case err := <-provDoneCh: + return err + case <-cancelCh: + p.Provisioner.Cancel() + return <-provDoneCh + } +} + +func (p *DebuggedProvisioner) Cancel() { + var doneCh chan struct{} + + p.lock.Lock() + if p.cancelCh != nil { + close(p.cancelCh) + p.cancelCh = nil + } + if p.doneCh != nil { + doneCh = p.doneCh + } + p.lock.Unlock() + + <-doneCh +} + +func (p *DebuggedProvisioner) provision(result chan<- error, ui Ui, comm Communicator) { + result <- p.Provisioner.Provision(ui, comm) +}