+func (boot *Booter) wait(ctx context.Context, tasks ...bootTask) error {
+ for _, task := range tasks {
+ ch, ok := boot.tasksReady[task.String()]
+ if !ok {
+ return fmt.Errorf("no such task: %s", task)
+ }
+ boot.logger.WithField("task", task.String()).Info("waiting")
+ select {
+ case <-ch:
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+ }
+ return nil
+}
+
+func (boot *Booter) Stop() {
+ boot.cancel()
+ <-boot.done
+}
+
+func (boot *Booter) WaitReady() (*arvados.URL, bool) {
+ for waiting := true; waiting; {
+ time.Sleep(time.Second)
+ if boot.ctx.Err() != nil {
+ return nil, false
+ }
+ if boot.healthChecker == nil {
+ // not set up yet
+ continue
+ }
+ resp := boot.healthChecker.ClusterHealth()
+ // The overall health check (resp.Health=="OK") might
+ // never pass due to missing components (like
+ // arvados-dispatch-cloud in a test cluster), so
+ // instead we wait for all configured components to
+ // pass.
+ waiting = false
+ for target, check := range resp.Checks {
+ if check.Health != "OK" {
+ waiting = true
+ boot.logger.WithField("target", target).Debug("waiting")
+ }
+ }
+ }
+ u := boot.cluster.Services.Controller.ExternalURL
+ return &u, true
+}
+
+func (boot *Booter) prependEnv(key, prepend string) {
+ for i, s := range boot.environ {
+ if strings.HasPrefix(s, key+"=") {
+ boot.environ[i] = key + "=" + prepend + s[len(key)+1:]
+ return
+ }
+ }
+ boot.environ = append(boot.environ, key+"="+prepend)
+}
+
+var cleanEnvPrefixes = []string{
+ "GEM_HOME=",
+ "GEM_PATH=",
+ "ARVADOS_",
+}
+
+func (boot *Booter) cleanEnv() {
+ var cleaned []string
+ for _, s := range boot.environ {
+ drop := false
+ for _, p := range cleanEnvPrefixes {
+ if strings.HasPrefix(s, p) {
+ drop = true
+ break
+ }
+ }
+ if !drop {
+ cleaned = append(cleaned, s)
+ }
+ }
+ boot.environ = cleaned
+}
+
+func (boot *Booter) setEnv(key, val string) {
+ for i, s := range boot.environ {
+ if strings.HasPrefix(s, key+"=") {
+ boot.environ[i] = key + "=" + val
+ return
+ }
+ }
+ boot.environ = append(boot.environ, key+"="+val)
+}
+
+func (boot *Booter) installGoProgram(ctx context.Context, srcpath string) error {