7a8cacc9ec
The current implementation for the task coordinator unblocks tasks by performing destructive operations over its internal state (like closing channels and deleting maps from keys). This presents a problem in situations where we would like to revert the state of a task, such as when restarting an allocation with tasks that have already exited. With this new implementation the task coordinator behaves more like a finite state machine where task may be blocked/unblocked multiple times by performing a state transition. This initial part of the work only refactors the task coordinator and is functionally equivalent to the previous implementation. Future work will build upon this to provide bug fixes and enhancements.
57 lines
1.4 KiB
Go
57 lines
1.4 KiB
Go
package tasklifecycle
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
"github.com/hashicorp/nomad/testutil"
|
|
testing "github.com/mitchellh/go-testing-interface"
|
|
)
|
|
|
|
func RequireTaskBlocked(t testing.T, c *Coordinator, task *structs.Task) {
|
|
ch := c.StartConditionForTask(task)
|
|
requireChannelBlocking(t, ch, task.Name)
|
|
}
|
|
|
|
func RequireTaskAllowed(t testing.T, c *Coordinator, task *structs.Task) {
|
|
ch := c.StartConditionForTask(task)
|
|
requireChannelPassing(t, ch, task.Name)
|
|
}
|
|
|
|
func WaitNotInitUntil(c *Coordinator, until time.Duration, errorFunc func()) {
|
|
testutil.WaitForResultUntil(until,
|
|
func() (bool, error) {
|
|
c.currentStateLock.RLock()
|
|
defer c.currentStateLock.RUnlock()
|
|
return c.currentState != coordinatorStateInit, nil
|
|
},
|
|
func(_ error) {
|
|
errorFunc()
|
|
})
|
|
}
|
|
|
|
func requireChannelPassing(t testing.T, ch <-chan struct{}, name string) {
|
|
testutil.WaitForResult(func() (bool, error) {
|
|
return !isChannelBlocking(ch), nil
|
|
}, func(_ error) {
|
|
t.Fatalf("%s channel was blocking, should be passing", name)
|
|
})
|
|
}
|
|
|
|
func requireChannelBlocking(t testing.T, ch <-chan struct{}, name string) {
|
|
testutil.WaitForResult(func() (bool, error) {
|
|
return isChannelBlocking(ch), nil
|
|
}, func(_ error) {
|
|
t.Fatalf("%s channel was passing, should be blocking", name)
|
|
})
|
|
}
|
|
|
|
func isChannelBlocking(ch <-chan struct{}) bool {
|
|
select {
|
|
case <-ch:
|
|
return false
|
|
default:
|
|
return true
|
|
}
|
|
}
|