terraform: Context.Stop() calls Stop on providers if running
This commit is contained in:
parent
d338a1ef88
commit
a61b7227f5
|
@ -105,6 +105,7 @@ type Context struct {
|
||||||
parallelSem Semaphore
|
parallelSem Semaphore
|
||||||
providerInputConfig map[string]map[string]interface{}
|
providerInputConfig map[string]map[string]interface{}
|
||||||
runCh <-chan struct{}
|
runCh <-chan struct{}
|
||||||
|
stopCh chan struct{}
|
||||||
shadowErr error
|
shadowErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -587,6 +588,9 @@ func (c *Context) Stop() {
|
||||||
// Tell the hook we want to stop
|
// Tell the hook we want to stop
|
||||||
c.sh.Stop()
|
c.sh.Stop()
|
||||||
|
|
||||||
|
// Close the stop channel
|
||||||
|
close(c.stopCh)
|
||||||
|
|
||||||
// Wait for us to stop
|
// Wait for us to stop
|
||||||
c.l.Unlock()
|
c.l.Unlock()
|
||||||
<-ch
|
<-ch
|
||||||
|
@ -672,6 +676,9 @@ func (c *Context) acquireRun() chan<- struct{} {
|
||||||
ch := make(chan struct{})
|
ch := make(chan struct{})
|
||||||
c.runCh = ch
|
c.runCh = ch
|
||||||
|
|
||||||
|
// Reset the stop channel so we can watch that
|
||||||
|
c.stopCh = make(chan struct{})
|
||||||
|
|
||||||
// Reset the stop hook so we're not stopped
|
// Reset the stop hook so we're not stopped
|
||||||
c.sh.Reset()
|
c.sh.Reset()
|
||||||
|
|
||||||
|
@ -687,6 +694,7 @@ func (c *Context) releaseRun(ch chan<- struct{}) {
|
||||||
|
|
||||||
close(ch)
|
close(ch)
|
||||||
c.runCh = nil
|
c.runCh = nil
|
||||||
|
c.stopCh = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) walk(
|
func (c *Context) walk(
|
||||||
|
@ -714,9 +722,16 @@ func (c *Context) walk(
|
||||||
log.Printf("[DEBUG] Starting graph walk: %s", operation.String())
|
log.Printf("[DEBUG] Starting graph walk: %s", operation.String())
|
||||||
walker := &ContextGraphWalker{Context: realCtx, Operation: operation}
|
walker := &ContextGraphWalker{Context: realCtx, Operation: operation}
|
||||||
|
|
||||||
|
// Watch for a stop so we can call the provider Stop() API.
|
||||||
|
doneCh := make(chan struct{})
|
||||||
|
go c.watchStop(walker, c.stopCh, doneCh)
|
||||||
|
|
||||||
// Walk the real graph, this will block until it completes
|
// Walk the real graph, this will block until it completes
|
||||||
realErr := graph.Walk(walker)
|
realErr := graph.Walk(walker)
|
||||||
|
|
||||||
|
// Close the done channel so the watcher stops
|
||||||
|
close(doneCh)
|
||||||
|
|
||||||
// If we have a shadow graph and we interrupted the real graph, then
|
// If we have a shadow graph and we interrupted the real graph, then
|
||||||
// we just close the shadow and never verify it. It is non-trivial to
|
// we just close the shadow and never verify it. It is non-trivial to
|
||||||
// recreate the exact execution state up until an interruption so this
|
// recreate the exact execution state up until an interruption so this
|
||||||
|
@ -796,6 +811,35 @@ func (c *Context) walk(
|
||||||
return walker, realErr
|
return walker, realErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Context) watchStop(walker *ContextGraphWalker, stopCh, doneCh <-chan struct{}) {
|
||||||
|
// Wait for a stop or completion
|
||||||
|
select {
|
||||||
|
case <-stopCh:
|
||||||
|
// Stop was triggered. Fall out of the select
|
||||||
|
case <-doneCh:
|
||||||
|
// Done, just exit completely
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're here, we're stopped, trigger the call.
|
||||||
|
|
||||||
|
// Copy the providers so that a misbehaved blocking Stop doesn't
|
||||||
|
// completely hang Terraform.
|
||||||
|
walker.providerLock.Lock()
|
||||||
|
ps := make([]ResourceProvider, 0, len(walker.providerCache))
|
||||||
|
for _, p := range walker.providerCache {
|
||||||
|
ps = append(ps, p)
|
||||||
|
}
|
||||||
|
defer walker.providerLock.Unlock()
|
||||||
|
|
||||||
|
for _, p := range ps {
|
||||||
|
// We ignore the error for now since there isn't any reasonable
|
||||||
|
// action to take if there is an error here, since the stop is still
|
||||||
|
// advisory: Terraform will exit once the graph node completes.
|
||||||
|
p.Stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// parseVariableAsHCL parses the value of a single variable as would have been specified
|
// parseVariableAsHCL parses the value of a single variable as would have been specified
|
||||||
// on the command line via -var or in an environment variable named TF_VAR_x, where x is
|
// on the command line via -var or in an environment variable named TF_VAR_x, where x is
|
||||||
// the name of the variable. In order to get around the restriction of HCL requiring a
|
// the name of the variable. In order to get around the restriction of HCL requiring a
|
||||||
|
|
|
@ -1043,6 +1043,10 @@ func TestContext2Apply_cancel(t *testing.T) {
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
t.Fatalf("bad: \n%s", actual)
|
t.Fatalf("bad: \n%s", actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !p.StopCalled {
|
||||||
|
t.Fatal("stop should be called")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContext2Apply_compute(t *testing.T) {
|
func TestContext2Apply_compute(t *testing.T) {
|
||||||
|
|
Loading…
Reference in New Issue