From 93f5bbbf45d6107c96e8950be1a52ae03058942e Mon Sep 17 00:00:00 2001 From: Marc Carmier Date: Sun, 12 Mar 2017 23:46:35 +0100 Subject: [PATCH 1/3] 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) +} From a5f802ec01e75dcf49db495794a2c8ac71d4f6a7 Mon Sep 17 00:00:00 2001 From: Marc Carmier Date: Thu, 16 Mar 2017 23:03:30 +0100 Subject: [PATCH 2/3] Add unit tests for DebuggedProvisionner --- packer/provisioner_test.go | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/packer/provisioner_test.go b/packer/provisioner_test.go index 22d34806f..4d370ef39 100644 --- a/packer/provisioner_test.go +++ b/packer/provisioner_test.go @@ -197,3 +197,67 @@ func TestPausedProvisionerCancel(t *testing.T) { t.Fatal("cancel should be called") } } + +func TestDebuggedProvisioner_impl(t *testing.T) { + var _ Provisioner = new(DebuggedProvisioner) +} + +func TestDebuggedProvisionerPrepare(t *testing.T) { + mock := new(MockProvisioner) + prov := &DebuggedProvisioner{ + Provisioner: mock, + } + + prov.Prepare(42) + if !mock.PrepCalled { + t.Fatal("prepare should be called") + } + if mock.PrepConfigs[0] != 42 { + t.Fatal("should have proper configs") + } +} + +func TestDebuggedProvisionerProvision(t *testing.T) { + mock := new(MockProvisioner) + prov := &DebuggedProvisioner{ + Provisioner: mock, + } + + ui := testUi() + comm := new(MockCommunicator) + writeReader(ui, "\n") + prov.Provision(ui, comm) + if !mock.ProvCalled { + t.Fatal("prov should be called") + } + if mock.ProvUi != ui { + t.Fatal("should have proper ui") + } + if mock.ProvCommunicator != comm { + t.Fatal("should have proper comm") + } +} + +func TestDebuggedProvisionerCancel(t *testing.T) { + mock := new(MockProvisioner) + prov := &DebuggedProvisioner{ + Provisioner: mock, + } + + provCh := make(chan struct{}) + mock.ProvFunc = func() error { + close(provCh) + time.Sleep(10 * time.Millisecond) + return nil + } + + // Start provisioning and wait for it to start + go prov.Provision(testUi(), new(MockCommunicator)) + <-provCh + + // Cancel it + prov.Cancel() + if !mock.CancelCalled { + t.Fatal("cancel should be called") + } +} From 91aa5f8bbbdbe0ac75ffa012e572d9ca19ee4416 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 7 Jun 2018 15:35:36 -0700 Subject: [PATCH 3/3] resolve conflicts; fix to work with hookedprovisioner which has been added since PR was made --- packer/build.go | 16 ++++++++++++---- packer/core.go | 1 - packer/provisioner.go | 4 ++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packer/build.go b/packer/build.go index 1187e49d7..bb441b522 100644 --- a/packer/build.go +++ b/packer/build.go @@ -200,10 +200,18 @@ func (b *coreBuild) Run(originalUi Ui, cache Cache) ([]Artifact, error) { if len(p.config) > 0 { pConfig = p.config[0] } - hookedProvisioners[i] = &HookedProvisioner{ - p.provisioner, - pConfig, - p.pType, + if b.debug { + hookedProvisioners[i] = &HookedProvisioner{ + &DebuggedProvisioner{Provisioner: p.provisioner}, + pConfig, + p.pType, + } + } else { + hookedProvisioners[i] = &HookedProvisioner{ + p.provisioner, + pConfig, + p.pType, + } } } diff --git a/packer/core.go b/packer/core.go index e6d82aaed..c8ac2cfb7 100644 --- a/packer/core.go +++ b/packer/core.go @@ -152,7 +152,6 @@ 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 c9af06f54..a565c30e2 100644 --- a/packer/provisioner.go +++ b/packer/provisioner.go @@ -170,8 +170,8 @@ func (p *PausedProvisioner) provision(result chan<- error, ui Ui, comm Communica result <- p.Provisioner.Provision(ui, comm) } -// DebuggedProvisioner is a Provisioner implementation that wait key press before -// the provisioner is actually run. +// DebuggedProvisioner is a Provisioner implementation that waits until a key +// press before the provisioner is actually run. type DebuggedProvisioner struct { Provisioner Provisioner