github.com/askholme/packer@v0.7.2-0.20140924152349-70d9566a6852/packer/provisioner.go (about) 1 package packer 2 3 import ( 4 "fmt" 5 "sync" 6 "time" 7 ) 8 9 // A provisioner is responsible for installing and configuring software 10 // on a machine prior to building the actual image. 11 type Provisioner interface { 12 // Prepare is called with a set of configurations to setup the 13 // internal state of the provisioner. The multiple configurations 14 // should be merged in some sane way. 15 Prepare(...interface{}) error 16 17 // Provision is called to actually provision the machine. A UI is 18 // given to communicate with the user, and a communicator is given that 19 // is guaranteed to be connected to some machine so that provisioning 20 // can be done. 21 Provision(Ui, Communicator) error 22 23 // Cancel is called to cancel the provisioning. This is usually called 24 // while Provision is still being called. The Provisioner should act 25 // to stop its execution as quickly as possible in a race-free way. 26 Cancel() 27 } 28 29 // A Hook implementation that runs the given provisioners. 30 type ProvisionHook struct { 31 // The provisioners to run as part of the hook. These should already 32 // be prepared (by calling Prepare) at some earlier stage. 33 Provisioners []Provisioner 34 35 lock sync.Mutex 36 runningProvisioner Provisioner 37 } 38 39 // Runs the provisioners in order. 40 func (h *ProvisionHook) Run(name string, ui Ui, comm Communicator, data interface{}) error { 41 defer func() { 42 h.lock.Lock() 43 defer h.lock.Unlock() 44 45 h.runningProvisioner = nil 46 }() 47 48 for _, p := range h.Provisioners { 49 h.lock.Lock() 50 h.runningProvisioner = p 51 h.lock.Unlock() 52 53 if err := p.Provision(ui, comm); err != nil { 54 return err 55 } 56 } 57 58 return nil 59 } 60 61 // Cancels the privisioners that are still running. 62 func (h *ProvisionHook) Cancel() { 63 h.lock.Lock() 64 defer h.lock.Unlock() 65 66 if h.runningProvisioner != nil { 67 h.runningProvisioner.Cancel() 68 } 69 } 70 71 // PausedProvisioner is a Provisioner implementation that pauses before 72 // the provisioner is actually run. 73 type PausedProvisioner struct { 74 PauseBefore time.Duration 75 Provisioner Provisioner 76 77 cancelCh chan struct{} 78 doneCh chan struct{} 79 lock sync.Mutex 80 } 81 82 func (p *PausedProvisioner) Prepare(raws ...interface{}) error { 83 return p.Provisioner.Prepare(raws...) 84 } 85 86 func (p *PausedProvisioner) Provision(ui Ui, comm Communicator) error { 87 p.lock.Lock() 88 cancelCh := make(chan struct{}) 89 p.cancelCh = cancelCh 90 91 // Setup the done channel, which is trigger when we're done 92 doneCh := make(chan struct{}) 93 defer close(doneCh) 94 p.doneCh = doneCh 95 p.lock.Unlock() 96 97 defer func() { 98 p.lock.Lock() 99 defer p.lock.Unlock() 100 if p.cancelCh == cancelCh { 101 p.cancelCh = nil 102 } 103 if p.doneCh == doneCh { 104 p.doneCh = nil 105 } 106 }() 107 108 // Use a select to determine if we get cancelled during the wait 109 ui.Say(fmt.Sprintf("Pausing %s before the next provisioner...", p.PauseBefore)) 110 select { 111 case <-time.After(p.PauseBefore): 112 case <-cancelCh: 113 return nil 114 } 115 116 provDoneCh := make(chan error, 1) 117 go p.provision(provDoneCh, ui, comm) 118 119 select { 120 case err := <-provDoneCh: 121 return err 122 case <-cancelCh: 123 p.Provisioner.Cancel() 124 return <-provDoneCh 125 } 126 } 127 128 func (p *PausedProvisioner) Cancel() { 129 var doneCh chan struct{} 130 131 p.lock.Lock() 132 if p.cancelCh != nil { 133 close(p.cancelCh) 134 p.cancelCh = nil 135 } 136 if p.doneCh != nil { 137 doneCh = p.doneCh 138 } 139 p.lock.Unlock() 140 141 <-doneCh 142 } 143 144 func (p *PausedProvisioner) provision(result chan<- error, ui Ui, comm Communicator) { 145 result <- p.Provisioner.Provision(ui, comm) 146 }