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
+	}
+}