2018-06-22 00:35:07 +00:00
|
|
|
package allocrunnerv2
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2018-06-29 00:01:05 +00:00
|
|
|
"path/filepath"
|
2018-06-22 00:35:07 +00:00
|
|
|
"sync"
|
|
|
|
|
2018-07-11 04:21:12 +00:00
|
|
|
"github.com/boltdb/bolt"
|
2018-06-22 00:35:07 +00:00
|
|
|
log "github.com/hashicorp/go-hclog"
|
|
|
|
"github.com/hashicorp/nomad/client/allocdir"
|
2018-06-29 00:01:05 +00:00
|
|
|
"github.com/hashicorp/nomad/client/allocrunner"
|
2018-06-22 00:35:07 +00:00
|
|
|
"github.com/hashicorp/nomad/client/allocrunnerv2/interfaces"
|
|
|
|
"github.com/hashicorp/nomad/client/allocrunnerv2/state"
|
|
|
|
"github.com/hashicorp/nomad/client/allocrunnerv2/taskrunner"
|
2018-06-29 00:01:05 +00:00
|
|
|
cstructs "github.com/hashicorp/nomad/client/structs"
|
2018-06-22 00:35:07 +00:00
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
|
|
)
|
|
|
|
|
|
|
|
// allocRunner is used to run all the tasks in a given allocation
|
|
|
|
type allocRunner struct {
|
|
|
|
// Logger is the logger for the alloc runner.
|
|
|
|
logger log.Logger
|
|
|
|
|
2018-07-12 23:15:33 +00:00
|
|
|
config *Config
|
2018-06-22 00:35:07 +00:00
|
|
|
|
|
|
|
// waitCh is closed when the alloc runner has transitioned to a terminal
|
|
|
|
// state
|
|
|
|
waitCh chan struct{}
|
|
|
|
|
|
|
|
// Alloc captures the allocation being run.
|
|
|
|
alloc *structs.Allocation
|
2018-06-29 00:01:05 +00:00
|
|
|
allocLock sync.RWMutex
|
2018-06-22 00:35:07 +00:00
|
|
|
|
2018-07-11 04:21:12 +00:00
|
|
|
//XXX implement for local state
|
2018-06-22 00:35:07 +00:00
|
|
|
// state captures the state of the alloc runner
|
|
|
|
state *state.State
|
|
|
|
|
2018-07-11 04:21:12 +00:00
|
|
|
stateDB *bolt.DB
|
|
|
|
|
2018-06-22 00:35:07 +00:00
|
|
|
// allocDir is used to build the allocations directory structure.
|
|
|
|
allocDir *allocdir.AllocDir
|
|
|
|
|
|
|
|
// runnerHooks are alloc runner lifecycle hooks that should be run on state
|
|
|
|
// transistions.
|
|
|
|
runnerHooks []interfaces.RunnerHook
|
|
|
|
|
|
|
|
// tasks are the set of task runners
|
2018-06-28 00:27:03 +00:00
|
|
|
tasks map[string]*taskrunner.TaskRunner
|
|
|
|
|
2018-06-29 00:01:05 +00:00
|
|
|
// updateCh receives allocation updates via the Update method
|
2018-06-28 00:27:03 +00:00
|
|
|
updateCh chan *structs.Allocation
|
2018-06-22 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewAllocRunner returns a new allocation runner.
|
2018-06-29 00:01:05 +00:00
|
|
|
func NewAllocRunner(config *Config) *allocRunner {
|
2018-06-22 00:35:07 +00:00
|
|
|
ar := &allocRunner{
|
2018-07-12 23:15:33 +00:00
|
|
|
config: config,
|
|
|
|
alloc: config.Alloc,
|
|
|
|
tasks: make(map[string]*taskrunner.TaskRunner),
|
|
|
|
waitCh: make(chan struct{}),
|
|
|
|
updateCh: make(chan *structs.Allocation),
|
|
|
|
stateDB: config.StateDB,
|
2018-06-22 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
2018-06-29 00:01:05 +00:00
|
|
|
// Create alloc dir
|
|
|
|
//XXX update AllocDir to hc log
|
|
|
|
ar.allocDir = allocdir.NewAllocDir(nil, filepath.Join(config.ClientConfig.AllocDir, config.Alloc.ID))
|
|
|
|
|
2018-06-22 00:35:07 +00:00
|
|
|
// Create the logger based on the allocation ID
|
2018-06-29 00:01:05 +00:00
|
|
|
ar.logger = config.Logger.With("alloc_id", config.Alloc.ID)
|
2018-06-22 00:35:07 +00:00
|
|
|
|
|
|
|
// Initialize the runners hooks.
|
|
|
|
ar.initRunnerHooks()
|
|
|
|
|
2018-06-29 00:01:05 +00:00
|
|
|
return ar
|
2018-06-22 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (ar *allocRunner) WaitCh() <-chan struct{} {
|
|
|
|
return ar.waitCh
|
|
|
|
}
|
|
|
|
|
|
|
|
// XXX How does alloc Restart work
|
|
|
|
// Run is the main go-routine that executes all the tasks.
|
|
|
|
func (ar *allocRunner) Run() {
|
|
|
|
// Close the wait channel
|
|
|
|
defer close(ar.waitCh)
|
|
|
|
|
2018-06-28 00:27:03 +00:00
|
|
|
var err error
|
|
|
|
var taskWaitCh <-chan struct{}
|
|
|
|
|
2018-06-22 00:35:07 +00:00
|
|
|
// Run the prerun hooks
|
|
|
|
// XXX Equivalent to TR.Prerun hook
|
|
|
|
if err := ar.prerun(); err != nil {
|
|
|
|
ar.logger.Error("prerun failed", "error", err)
|
|
|
|
goto POST
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run the runners
|
2018-06-28 00:27:03 +00:00
|
|
|
taskWaitCh, err = ar.runImpl()
|
|
|
|
if err != nil {
|
2018-06-22 00:35:07 +00:00
|
|
|
ar.logger.Error("starting tasks failed", "error", err)
|
|
|
|
}
|
|
|
|
|
2018-06-28 00:27:03 +00:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-taskWaitCh:
|
|
|
|
// TaskRunners have all exited
|
|
|
|
case updated := <-ar.updateCh:
|
|
|
|
// Updated alloc received
|
|
|
|
//XXX Update hooks
|
|
|
|
//XXX Update ar.alloc
|
|
|
|
for _, tr := range ar.tasks {
|
|
|
|
tr.Update(updated)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-22 00:35:07 +00:00
|
|
|
POST:
|
|
|
|
// Run the postrun hooks
|
|
|
|
// XXX Equivalent to TR.Poststop hook
|
|
|
|
if err := ar.postrun(); err != nil {
|
|
|
|
ar.logger.Error("postrun failed", "error", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// runImpl is used to run the runners.
|
2018-06-28 00:27:03 +00:00
|
|
|
func (ar *allocRunner) runImpl() (<-chan struct{}, error) {
|
2018-06-22 00:35:07 +00:00
|
|
|
// Grab the task group
|
2018-06-29 00:01:05 +00:00
|
|
|
alloc := ar.Alloc()
|
|
|
|
tg := alloc.Job.LookupTaskGroup(alloc.TaskGroup)
|
2018-06-22 00:35:07 +00:00
|
|
|
if tg == nil {
|
|
|
|
// XXX Fail and exit
|
2018-06-29 00:01:05 +00:00
|
|
|
ar.logger.Error("failed to lookup task group", "task_group", alloc.TaskGroup)
|
|
|
|
return nil, fmt.Errorf("failed to lookup task group %q", alloc.TaskGroup)
|
2018-06-22 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, task := range tg.Tasks {
|
2018-06-29 00:01:05 +00:00
|
|
|
if err := ar.runTask(alloc, task); err != nil {
|
2018-06-28 00:27:03 +00:00
|
|
|
return nil, err
|
2018-06-22 00:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-28 00:27:03 +00:00
|
|
|
// Return a combined WaitCh that is closed when all task runners have
|
|
|
|
// exited.
|
|
|
|
waitCh := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
defer close(waitCh)
|
|
|
|
for _, task := range ar.tasks {
|
2018-06-22 00:35:07 +00:00
|
|
|
<-task.WaitCh()
|
2018-06-28 00:27:03 +00:00
|
|
|
}
|
|
|
|
}()
|
2018-06-22 00:35:07 +00:00
|
|
|
|
2018-06-28 00:27:03 +00:00
|
|
|
return waitCh, nil
|
2018-06-22 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// runTask is used to run a task.
|
2018-06-29 00:01:05 +00:00
|
|
|
func (ar *allocRunner) runTask(alloc *structs.Allocation, task *structs.Task) error {
|
2018-06-22 00:35:07 +00:00
|
|
|
// Create the runner
|
|
|
|
config := &taskrunner.Config{
|
2018-06-29 00:01:05 +00:00
|
|
|
Alloc: alloc,
|
2018-07-12 23:15:33 +00:00
|
|
|
ClientConfig: ar.config.ClientConfig,
|
2018-06-29 00:01:05 +00:00
|
|
|
Task: task,
|
|
|
|
TaskDir: ar.allocDir.NewTaskDir(task.Name),
|
|
|
|
Logger: ar.logger,
|
2018-07-11 04:21:12 +00:00
|
|
|
StateDB: ar.stateDB,
|
2018-07-12 23:15:33 +00:00
|
|
|
VaultClient: ar.config.Vault,
|
2018-06-22 00:35:07 +00:00
|
|
|
}
|
|
|
|
tr, err := taskrunner.NewTaskRunner(config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start the runner
|
|
|
|
go tr.Run()
|
|
|
|
|
|
|
|
// Store the runner
|
|
|
|
ar.tasks[task.Name] = tr
|
|
|
|
return nil
|
|
|
|
}
|
2018-06-28 00:27:03 +00:00
|
|
|
|
2018-06-29 00:01:05 +00:00
|
|
|
// Alloc returns the current allocation being run by this runner.
|
|
|
|
//XXX how do we handle mutate the state saving stuff
|
|
|
|
func (ar *allocRunner) Alloc() *structs.Allocation {
|
|
|
|
ar.allocLock.RLock()
|
|
|
|
defer ar.allocLock.RUnlock()
|
|
|
|
return ar.alloc
|
|
|
|
}
|
|
|
|
|
|
|
|
// SaveState does all the state related stuff. Who knows. FIXME
|
|
|
|
//XXX
|
|
|
|
func (ar *allocRunner) SaveState() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-06-28 00:27:03 +00:00
|
|
|
// Update the running allocation with a new version received from the server.
|
|
|
|
//
|
|
|
|
// This method is safe for calling concurrently with Run() and does not modify
|
|
|
|
// the passed in allocation.
|
|
|
|
func (ar *allocRunner) Update(update *structs.Allocation) {
|
|
|
|
ar.updateCh <- update
|
|
|
|
}
|
2018-06-29 00:01:05 +00:00
|
|
|
|
|
|
|
// Destroy the alloc runner by stopping it if it is still running and cleaning
|
|
|
|
// up all of its resources.
|
|
|
|
//
|
|
|
|
// This method is safe for calling concurrently with Run(). Callers must
|
|
|
|
// receive on WaitCh() to block until alloc runner has stopped and been
|
|
|
|
// destroyed.
|
|
|
|
//XXX TODO
|
|
|
|
func (ar *allocRunner) Destroy() {
|
|
|
|
//TODO
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsDestroyed returns true if the alloc runner has been destroyed (stopped and
|
|
|
|
// garbage collected).
|
|
|
|
//
|
|
|
|
// This method is safe for calling concurrently with Run(). Callers must
|
|
|
|
// receive on WaitCh() to block until alloc runner has stopped and been
|
|
|
|
// destroyed.
|
|
|
|
//XXX TODO
|
|
|
|
func (ar *allocRunner) IsDestroyed() bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsWaiting returns true if the alloc runner is waiting for its previous
|
|
|
|
// allocation to terminate.
|
|
|
|
//
|
|
|
|
// This method is safe for calling concurrently with Run().
|
|
|
|
//XXX TODO
|
|
|
|
func (ar *allocRunner) IsWaiting() bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsMigrating returns true if the alloc runner is migrating data from its
|
|
|
|
// previous allocation.
|
|
|
|
//
|
|
|
|
// This method is safe for calling concurrently with Run().
|
|
|
|
//XXX TODO
|
|
|
|
func (ar *allocRunner) IsMigrating() bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// StatsReporter needs implementing
|
|
|
|
//XXX
|
|
|
|
func (ar *allocRunner) StatsReporter() allocrunner.AllocStatsReporter {
|
|
|
|
return noopStatsReporter{}
|
|
|
|
}
|
|
|
|
|
|
|
|
//FIXME implement
|
|
|
|
type noopStatsReporter struct{}
|
|
|
|
|
|
|
|
func (noopStatsReporter) LatestAllocStats(taskFilter string) (*cstructs.AllocResourceUsage, error) {
|
|
|
|
return nil, fmt.Errorf("not implemented")
|
|
|
|
}
|