env: status page

Updates page asynchronously every 5 seconds.
Introduces beforeStart and afterStart trigger points to update setup status information.

Change-Id: Ic2f6a9bb7a0fefeefc4d6a1a7338d506a4f99e80
diff --git a/core/installer/tasks/env.go b/core/installer/tasks/env.go
index 80289c4..1d1eaba 100644
--- a/core/installer/tasks/env.go
+++ b/core/installer/tasks/env.go
@@ -12,6 +12,7 @@
 )
 
 type state struct {
+	infoListener   EnvInfoListener
 	publicIPs      []net.IP
 	nsCreator      installer.NamespaceCreator
 	repo           installer.RepoIO
@@ -33,6 +34,8 @@
 	AdminPublicKey string
 }
 
+type EnvInfoListener func(string)
+
 type DNSZoneRef struct {
 	Name      string
 	Namespace string
@@ -44,11 +47,13 @@
 	startIP net.IP,
 	nsCreator installer.NamespaceCreator,
 	repo installer.RepoIO,
+	infoListener EnvInfoListener,
 ) (Task, DNSZoneRef) {
 	st := state{
-		publicIPs: publicIPs,
-		nsCreator: nsCreator,
-		repo:      repo,
+		infoListener: infoListener,
+		publicIPs:    publicIPs,
+		nsCreator:    nsCreator,
+		repo:         repo,
 	}
 	t := newSequentialParentTask(
 		"Create env",
@@ -57,6 +62,9 @@
 		SetupZoneTask(env, startIP, &st),
 		SetupInfra(env, startIP, &st),
 	)
+	t.afterDone = func() {
+		infoListener(fmt.Sprintf("dodo environment for %s has been provisioned successfully. Visit [https://welcome.%s](https://welcome.%s) to create administrative account and log into the system.", env.Domain, env.Domain, env.Domain))
+	}
 	rctx, done := context.WithCancel(context.Background())
 	t.OnDone(func(_ error) {
 		done()