diff --git a/cli/api.go b/cli/api.go index 766e990..89ff78d 100644 --- a/cli/api.go +++ b/cli/api.go @@ -45,6 +45,10 @@ func newAbraHandler() *abraHandler { h := &abraHandler{ mux: http.NewServeMux(), } + h.mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + log.Printf("API Path not found") + http.Error(w, "API Path not found", http.StatusMethodNotAllowed) + }) h.mux.HandleFunc("/api/apps", func(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: @@ -63,6 +67,8 @@ func newAbraHandler() *abraHandler { switch r.Method{ case http.MethodPost: h.handleDeployApp(w, r, r.PathValue("appId")) + case http.MethodGet: + h.getDeployLogs(w, r, r.PathValue("appId")) default: http.Error(w, "Method not implemented", http.StatusMethodNotAllowed) } diff --git a/cli/catalogue.go b/cli/catalogue.go index 706e18a..e03dc7f 100644 --- a/cli/catalogue.go +++ b/cli/catalogue.go @@ -24,8 +24,8 @@ func (h *abraHandler) handleListCatalogue(w http.ResponseWriter, r *http.Request http.Error(w, fmt.Sprintf("Error: %s\n", err), http.StatusInternalServerError) return } - w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) w.Write(jsonBytes) } diff --git a/cli/deploy.go b/cli/deploy.go index ab11790..f2b6595 100644 --- a/cli/deploy.go +++ b/cli/deploy.go @@ -5,7 +5,7 @@ import ( "coop-cloud-backend/internal" "coop-cloud-backend/cli/status" "net/http" - //"encoding/json" + "encoding/json" "fmt" "context" @@ -36,6 +36,11 @@ func (h *abraHandler) handleDeployApp(w http.ResponseWriter, r *http.Request, ap InternalServerErrorHandler(w, r) return } + log.Printf("Finishing app deploy!") + w.WriteHeader(http.StatusOK) +} +func (h *abraHandler) getDeployLogs(w http.ResponseWriter, r *http.Request, appName string) { + log.Printf("Get deploy logs!") app, err := appPkg.Get(appName) if err != nil { log.Printf("Error: %s\n", err) @@ -80,10 +85,43 @@ func (h *abraHandler) handleDeployApp(w http.ResponseWriter, r *http.Request, ap ID: service.ID, }) } - log.Printf("Waiting on service...") - status.WaitOnServices(context.Background(), cl, serviceIDs, f) + ctx := r.Context() + + flusher, ok := w.(http.Flusher) + if !ok { + http.Error(w, "Streaming unsupported", http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "text/event-stream") + w.Header().Set("Cache-Control", "no-cache") + w.Header().Set("Connection", "keep-alive") + log.Printf("Waiting on service...") + + stream := make (chan status.StreamEvent, 50) + go status.WaitOnServices(ctx, cl, serviceIDs, f, stream) + for { + select{ + case <- ctx.Done(): + log.Printf("deploy cancelled or done") + return + case msg, ok := <- stream: + if !ok { + return + } + test, err := json.MarshalIndent(msg, "", " ") + fmt.Printf(string(test), err); + b, err := json.Marshal(msg) + if err != nil { + // TODO: send error through onerror handler + log.Printf("error?: %s", err) + continue + } + fmt.Fprintf(w, "data: %s\n\n", b) + + flusher.Flush() + } + } - w.WriteHeader(http.StatusOK) } diff --git a/cli/list.go b/cli/list.go index 135e647..a606c88 100644 --- a/cli/list.go +++ b/cli/list.go @@ -156,8 +156,8 @@ import ( InternalServerErrorHandler(w, r) return } - w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) w.Write(jsonBytes) } func (h *abraHandler) handleListServers (w http.ResponseWriter, r *http.Request) { @@ -168,8 +168,8 @@ func (h *abraHandler) handleListServers (w http.ResponseWriter, r *http.Request) InternalServerErrorHandler(w, r) return } - w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) w.Write(abraServers) } diff --git a/cli/new.go b/cli/new.go index 62992ec..8a34457 100644 --- a/cli/new.go +++ b/cli/new.go @@ -16,7 +16,7 @@ func (h *abraHandler) handleNewApp(w http.ResponseWriter, r *http.Request, appNa Domain *string `json:"domain"` Server *string `json:"server"` Chaos *bool `json:"chaos"` - CreateSecrets *bool `json:"createSecrets"` + Secrets *bool `json:"secrets"` }{} err := d.Decode(&body) @@ -39,7 +39,7 @@ func (h *abraHandler) handleNewApp(w http.ResponseWriter, r *http.Request, appNa if body.Chaos != nil && *body.Chaos == true { args = append(args, "-C") } - if body.CreateSecrets != nil && *body.CreateSecrets == true { + if body.Secrets != nil && *body.Secrets == true { args = append(args, "--secrets") } log.Printf("%v", args) diff --git a/cli/ping.go b/cli/ping.go index a287ced..bbd8fa7 100644 --- a/cli/ping.go +++ b/cli/ping.go @@ -42,7 +42,7 @@ func (h *abraHandler) handleGetAppServices (w http.ResponseWriter, r *http.Reque InternalServerErrorHandler(w, r) return } - w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) w.Write(jsonBytes) } \ No newline at end of file diff --git a/cli/secret.go b/cli/secret.go index bba9b2d..f51ce48 100644 --- a/cli/secret.go +++ b/cli/secret.go @@ -13,8 +13,8 @@ func (h *abraHandler) handleGetAppSecrets(w http.ResponseWriter, r *http.Request InternalServerErrorHandler(w, r) return } - w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) w.Write(secrets) } diff --git a/cli/status/events.go b/cli/status/events.go index 454a003..e062d8e 100644 --- a/cli/status/events.go +++ b/cli/status/events.go @@ -42,6 +42,10 @@ type StatusEvent struct { } func (e StatusEvent) ServiceID() string { return e.Service.Id } +type StreamEvent struct { + Type string `json:"type"` // "service" | "done" + Data interface{} `json:"data"` +} type ServiceState struct { Name string `json:"name"` @@ -55,10 +59,10 @@ type ServiceState struct { } type DeployState struct { - count int - total int - failed bool - quit bool + Count int `json:"count"` + Total int `json:"total"` + Failed bool `json:"failed"` + Quit bool `json:"quit"` } type DeployMsg int @@ -69,7 +73,7 @@ const ( ) func (ds DeployState) complete() bool { - return ds.count == ds.total + return ds.Count == ds.Total } func progressProducer(ctx context.Context, cl *dockerClient.Client, s ServiceState, w *io.PipeWriter, ch chan<- Event) { @@ -164,7 +168,7 @@ func healthProducer(ctx context.Context, cl *dockerClient.Client, s ServiceState }() } -func processEvent(ctx context.Context, events <- chan Event, info chan <- DeployMsg, s ServiceState) error { +func processEvent(ctx context.Context, events <- chan Event, info chan <- DeployMsg, s ServiceState, stream chan <- StreamEvent) error { for { select { case event := <- events: @@ -237,31 +241,30 @@ func processEvent(ctx context.Context, events <- chan Event, info chan <- Deploy } s.Health = h } - + stream <- StreamEvent{Type: "service", Data: s} case <- ctx.Done(): - log.Printf("context is done???") return ctx.Err() } } } -func processService(ctx context.Context, info chan <- DeployMsg, cl *dockerClient.Client, s ServiceState, decoder *json.Decoder, writer *io.PipeWriter) { +func processService(ctx context.Context, info chan <- DeployMsg, cl *dockerClient.Client, s ServiceState, decoder *json.Decoder, writer *io.PipeWriter, stream chan <- StreamEvent) { events := make (chan Event, 50) progressProducer(ctx, cl, s, writer, events) statusProducer(ctx, decoder, s, events) healthProducer(ctx, cl, s, events) - go processEvent(ctx, events, info, s) + go processEvent(ctx, events, info, s, stream) } -func WaitOnServices(pctx context.Context, cl *dockerClient.Client, services []ui.ServiceMeta, filters filters.Args) { +func WaitOnServices(pctx context.Context, cl *dockerClient.Client, services []ui.ServiceMeta, filters filters.Args, stream chan <- StreamEvent) { ctx, cancel := context.WithCancel(pctx) defer cancel() - + log.Printf("What???") ds := DeployState{ - count: 0, - total: len(services), - failed: false, - quit: false, + Count: 0, + Total: len(services), + Failed: false, + Quit: false, } info := make (chan DeployMsg, 50) for _, service := range services { @@ -274,25 +277,27 @@ func WaitOnServices(pctx context.Context, cl *dockerClient.Client, services []ui Health: "?", } log.Printf("Processing Service: %s", s.Id) - processService(ctx, info, cl, s, d, w) + processService(ctx, info, cl, s, d, w, stream) } for { select { case msg := <-info: switch msg { case FailMsg: - ds.failed = true + ds.Failed = true case CompleteMsg: - ds.count += 1 + ds.Count += 1 if ds.complete() { log.Printf("deploy completed") cancel() } case QuitMsg: - ds.quit = true + ds.Quit = true cancel() } case <-ctx.Done(): + log.Printf("%v", ds) + stream <- StreamEvent{Type: "done", Data: ds} log.Printf("Context finished because: %s", ctx.Err()) return }