installer: split up new env creation into chain of tasks
diff --git a/core/installer/tasks/tasks.go b/core/installer/tasks/tasks.go
new file mode 100644
index 0000000..4eb834d
--- /dev/null
+++ b/core/installer/tasks/tasks.go
@@ -0,0 +1,106 @@
+package tasks
+
+import (
+ "fmt"
+)
+
+type Status int
+
+const (
+ StatusPending Status = 0
+ StatusRunning = 1
+ StatusFailed = 2
+ StatusDone = 3
+)
+
+type TaskDoneListener func(err error)
+
+type Task interface {
+ Title() string
+ Start()
+ Status() Status
+ Err() error
+ Subtasks() []Task
+ AddSubtask(t Task) error
+ FinalizeSubtasks()
+ OnDone(l TaskDoneListener)
+}
+
+type basicTask struct {
+ title string
+ status Status
+ err error
+ subtasks []Task
+ done []bool
+ finalized bool
+ listeners []TaskDoneListener
+}
+
+func (b *basicTask) Title() string {
+ return b.title
+}
+
+func (b *basicTask) Status() Status {
+ return b.status
+}
+
+func (b *basicTask) Err() error {
+ return b.err
+}
+
+func (b *basicTask) Subtasks() []Task {
+ return b.subtasks
+}
+
+func (b *basicTask) AddSubtask(t Task) error {
+ if b.finalized {
+ return fmt.Errorf("already finalized")
+ }
+ i := len(b.subtasks)
+ b.subtasks = append(b.subtasks, t)
+ b.done = append(b.done, false)
+ t.OnDone(func(err error) {
+ if b.done[i] {
+ panic(fmt.Sprintf("already done: %s", b.subtasks[i].Title()))
+ }
+ b.done[i] = true
+ if err != nil {
+ b.callDoneListeners(err)
+ }
+ if !b.finalized {
+ return
+ }
+ done := 0
+ for _, d := range b.done {
+ if d {
+ done++
+ } else {
+ break
+ }
+ }
+ if done == len(b.subtasks) {
+ b.callDoneListeners(nil)
+ }
+ })
+ return nil
+}
+
+func (b *basicTask) FinalizeSubtasks() {
+ b.finalized = true
+}
+
+func (b *basicTask) OnDone(l TaskDoneListener) {
+ b.listeners = append(b.listeners, l)
+}
+
+func (b *basicTask) callDoneListeners(err error) {
+ for _, l := range b.listeners {
+ go l(err)
+ }
+ if err == nil {
+ b.status = StatusDone
+ } else {
+ b.status = StatusFailed
+ b.err = err
+ }
+}