github.com/yoctocloud/packer@v0.6.2-0.20160520224004-e11a0a18423f/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 // Shortcut 42 if len(h.Provisioners) == 0 { 43 return nil 44 } 45 46 if comm == nil { 47 return fmt.Errorf( 48 "No communicator found for provisioners! This is usually because the\n" + 49 "`communicator` config was set to \"none\". If you have any provisioners\n" + 50 "then a communicator is required. Please fix this to continue.") 51 } 52 53 defer func() { 54 h.lock.Lock() 55 defer h.lock.Unlock() 56 57 h.runningProvisioner = nil 58 }() 59 60 for _, p := range h.Provisioners { 61 h.lock.Lock() 62 h.runningProvisioner = p 63 h.lock.Unlock() 64 65 if err := p.Provision(ui, comm); err != nil { 66 return err 67 } 68 } 69 70 return nil 71 } 72 73 // Cancels the privisioners that are still running. 74 func (h *ProvisionHook) Cancel() { 75 h.lock.Lock() 76 defer h.lock.Unlock() 77 78 if h.runningProvisioner != nil { 79 h.runningProvisioner.Cancel() 80 } 81 } 82 83 // PausedProvisioner is a Provisioner implementation that pauses before 84 // the provisioner is actually run. 85 type PausedProvisioner struct { 86 PauseBefore time.Duration 87 Provisioner Provisioner 88 89 cancelCh chan struct{} 90 doneCh chan struct{} 91 lock sync.Mutex 92 } 93 94 func (p *PausedProvisioner) Prepare(raws ...interface{}) error { 95 return p.Provisioner.Prepare(raws...) 96 } 97 98 func (p *PausedProvisioner) Provision(ui Ui, comm Communicator) error { 99 p.lock.Lock() 100 cancelCh := make(chan struct{}) 101 p.cancelCh = cancelCh 102 103 // Setup the done channel, which is trigger when we're done 104 doneCh := make(chan struct{}) 105 defer close(doneCh) 106 p.doneCh = doneCh 107 p.lock.Unlock() 108 109 defer func() { 110 p.lock.Lock() 111 defer p.lock.Unlock() 112 if p.cancelCh == cancelCh { 113 p.cancelCh = nil 114 } 115 if p.doneCh == doneCh { 116 p.doneCh = nil 117 } 118 }() 119 120 // Use a select to determine if we get cancelled during the wait 121 ui.Say(fmt.Sprintf("Pausing %s before the next provisioner...", p.PauseBefore)) 122 select { 123 case <-time.After(p.PauseBefore): 124 case <-cancelCh: 125 return nil 126 } 127 128 provDoneCh := make(chan error, 1) 129 go p.provision(provDoneCh, ui, comm) 130 131 select { 132 case err := <-provDoneCh: 133 return err 134 case <-cancelCh: 135 p.Provisioner.Cancel() 136 return <-provDoneCh 137 } 138 } 139 140 func (p *PausedProvisioner) Cancel() { 141 var doneCh chan struct{} 142 143 p.lock.Lock() 144 if p.cancelCh != nil { 145 close(p.cancelCh) 146 p.cancelCh = nil 147 } 148 if p.doneCh != nil { 149 doneCh = p.doneCh 150 } 151 p.lock.Unlock() 152 153 <-doneCh 154 } 155 156 func (p *PausedProvisioner) provision(result chan<- error, ui Ui, comm Communicator) { 157 result <- p.Provisioner.Provision(ui, comm) 158 }