diff --git a/cmd/sourced/cmd/web.go b/cmd/sourced/cmd/web.go index aaef589..d2cc56b 100644 --- a/cmd/sourced/cmd/web.go +++ b/cmd/sourced/cmd/web.go @@ -6,6 +6,7 @@ import ( "fmt" "net/http" "os" + "regexp" "runtime" "strings" "time" @@ -15,6 +16,7 @@ import ( "github.com/src-d/sourced-ce/cmd/sourced/compose" "github.com/src-d/sourced-ce/cmd/sourced/compose/workdir" "github.com/src-d/sourced-ce/cmd/sourced/dir" + "github.com/src-d/sourced-ce/cmd/sourced/docker" ) // The service name used in docker-compose.yml for the srcd/sourced-ui image @@ -78,6 +80,67 @@ func waitForContainer(stdout *bytes.Buffer) { } } +var newLineFormatter = regexp.MustCompile(`(\r\n|\r|\n)`) + +func normalizeNewLine(s string) string { + return newLineFormatter.ReplaceAllString(s, "\n") +} + +func runMonitor(ch chan<- error) { + go func() { + client, err := docker.GetClient() + if err != nil { + ch <- errors.Wrap(err, "cannot get docker client") + return + } + + var stdout bytes.Buffer + if err := compose.RunWithIO(context.Background(), + os.Stdin, &stdout, nil, "ps", "-q"); err != nil { + ch <- errors.Wrap(err, "cannot get services' container id") + return + } + + containerIds := strings.Split( + normalizeNewLine(strings.TrimSpace(stdout.String())), + "\n", + ) + + for { + for _, contID := range containerIds { + cont, err := docker.InfoWithClient(client, contID) + if err != nil { + ch <- errors.Wrapf(err, "unable to get info for container with id '%s'", contID) + return + } + + service := cont.Labels["com.docker.compose.service"] + + if cont.State == "exited" { + if service != "ghsync" && service != "gitcollector" { + ch <- fmt.Errorf("service '%s' is in state '%s'", service, cont.State) + return + } + + if !strings.HasPrefix(cont.Status, "Exited (0)") { + ch <- fmt.Errorf("service '%s' exited with status: %s", service, cont.Status) + return + } + + continue + } + + if cont.State != "created" && cont.State != "running" { + ch <- fmt.Errorf("service '%s' is in state '%s'", service, cont.State) + return + } + } + + time.Sleep(1 * time.Second) + } + }() +} + // OpenUI opens the browser with the UI. func OpenUI(timeout time.Duration) error { var stdout bytes.Buffer @@ -89,6 +152,8 @@ func OpenUI(timeout time.Duration) error { ch := make(chan error) containerReady := err == nil + runMonitor(ch) + go func() { if !containerReady { waitForContainer(&stdout)