// servePutInstance adds an instance to a service. func (h *Handler) servePutInstance(w http.ResponseWriter, r *http.Request, params httprouter.Params) { // Read path parameter. service := params.ByName("service") // Read instance from request. inst := &discoverd.Instance{} if err := json.NewDecoder(r.Body).Decode(inst); err != nil { hh.Error(w, err) return } // Ensure instance is valid. if err := inst.Valid(); err != nil { hh.ValidationError(w, "", err.Error()) return } // Add instance to service in the store. if err := h.Store.AddInstance(service, inst); err == ErrNotLeader { h.redirectToLeader(w, r) return } else if IsNotFound(err) { hh.ObjectNotFoundError(w, err.Error()) return } else if err != nil { hh.Error(w, err) return } }
func (api *httpAPI) ServeTemplate(w http.ResponseWriter, req *http.Request, params httprouter.Params) { if req.Header.Get("Accept") == "application/json" { s, err := api.Installer.FindBaseCluster(params.ByName("id")) if err != nil { httphelper.ObjectNotFoundError(w, err.Error()) return } httphelper.JSON(w, 200, s) return } manifest, err := api.AssetManifest() if err != nil { httphelper.Error(w, err) api.logger.Debug(err.Error()) return } w.Header().Add("Content-Type", "text/html; charset=utf-8") w.Header().Add("Cache-Control", "max-age=0") err = htmlTemplate.Execute(w, &htmlTemplateData{ ApplicationJSPath: manifest.Assets["application.js"], ApplicationCSSPath: manifest.Assets["application.css"], ReactJSPath: manifest.Assets["react.js"], }) if err != nil { httphelper.Error(w, err) api.logger.Debug(err.Error()) return } }
// servePutService creates a service. func (h *Handler) servePutService(w http.ResponseWriter, r *http.Request, params httprouter.Params) { // Retrieve the path parameter. service := params.ByName("service") if err := ValidServiceName(service); err != nil { hh.ValidationError(w, "", err.Error()) return } // Read config from the request. config := &discoverd.ServiceConfig{} if err := hh.DecodeJSON(r, config); err != nil { hh.Error(w, err) return } // Add the service to the store. if err := h.Store.AddService(service, config); err == ErrNotLeader { h.redirectToLeader(w, r) return } else if IsServiceExists(err) { hh.ObjectExistsError(w, err.Error()) return } else if err != nil { hh.Error(w, err) return } }
// servePutLeader sets the leader for a service. func (h *Handler) servePutLeader(w http.ResponseWriter, r *http.Request, params httprouter.Params) { // Retrieve path parameters. service := params.ByName("service") // Check if the service allows manual leader election. config := h.Store.Config(service) if config == nil || config.LeaderType != discoverd.LeaderTypeManual { hh.ValidationError(w, "", "service leader election type is not manual") return } // Read instance from the request. inst := &discoverd.Instance{} if err := hh.DecodeJSON(r, inst); err != nil { hh.Error(w, err) return } // Manually set the leader on the service. if err := h.Store.SetServiceLeader(service, inst.ID); err == ErrNotLeader { h.redirectToLeader(w, r) return } else if err != nil { hh.Error(w, err) return } }
func (r *Runner) getBuildLog(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { id := ps.ByName("build") b := &Build{} if err := r.db.View(func(tx *bolt.Tx) error { v := tx.Bucket(dbBucket).Get([]byte(id)) if err := json.Unmarshal(v, b); err != nil { return fmt.Errorf("could not decode build %s: %s", v, err) } return nil }); err != nil { http.Error(w, err.Error(), 500) return } // if it's a V1 build, redirect to the log in S3 if b.Version == BuildVersion1 { http.Redirect(w, req, b.LogURL, http.StatusMovedPermanently) return } // if it's a browser, serve the build-log.html template if strings.Contains(req.Header.Get("Accept"), "text/html") { tpl, err := template.ParseFiles(path.Join(args.AssetsDir, "build-log.html")) if err != nil { http.Error(w, err.Error(), 500) return } w.Header().Set("Content-Type", "text/html; charset=utf-8") if err := tpl.Execute(w, b); err != nil { log.Printf("error executing build-log template: %s", err) } return } // serve the build log as either an SSE or plain text stream ch := make(chan string) stream, err := getBuildLogStream(b, ch) if err != nil { http.Error(w, err.Error(), 500) return } if cn, ok := w.(http.CloseNotifier); ok { go func() { <-cn.CloseNotify() stream.Close() }() } else { defer stream.Close() } if strings.Contains(req.Header.Get("Accept"), "text/event-stream") { sse.ServeStream(w, ch, nil) } else { servePlainStream(w, ch) } if err := stream.Err(); err != nil { log.Println("error serving build log stream:", err) } }
func (api *HTTPAPI) Pull(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { volumeID := ps.ByName("volume_id") pull := &volume.PullCoordinate{} if err := httphelper.DecodeJSON(r, &pull); err != nil { httphelper.Error(w, err) return } hostClient, err := api.cluster.Host(pull.HostID) if err != nil { httphelper.Error(w, err) return } haves, err := api.vman.ListHaves(volumeID) if err != nil { httphelper.Error(w, err) return } reader, err := hostClient.SendSnapshot(pull.SnapshotID, haves) if err != nil { httphelper.Error(w, err) return } snap, err := api.vman.ReceiveSnapshot(volumeID, reader) if err != nil { httphelper.Error(w, err) return } httphelper.JSON(w, 200, snap.Info()) }
func (api *HTTPAPI) Send(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { volumeID := ps.ByName("volume_id") if !strings.Contains(r.Header.Get("Accept"), snapshotContentType) { httphelper.ValidationError(w, "", fmt.Sprintf("must be prepared to accept a content type of %q", snapshotContentType)) return } w.Header().Set("Content-Type", snapshotContentType) var haves []json.RawMessage if err := httphelper.DecodeJSON(r, &haves); err != nil { httphelper.Error(w, err) return } err := api.vman.SendSnapshot(volumeID, haves, w) if err != nil { switch err { case volumemanager.NoSuchVolume: httphelper.ObjectNotFoundError(w, fmt.Sprintf("no volume with id %q", volumeID)) return default: httphelper.Error(w, err) return } } }
func (api *httpAPI) ServeApplicationJS(w http.ResponseWriter, req *http.Request, params httprouter.Params) { path := filepath.Join("app", "build", params.ByName("assetPath")) data, err := api.Asset(path) if err != nil { fmt.Println(err) w.WriteHeader(500) return } var jsConf bytes.Buffer jsConf.Write([]byte("window.InstallerConfig = ")) json.NewEncoder(&jsConf).Encode(installerJSConfig{ Endpoints: map[string]string{ "install": "/install", "events": "/events/:id", "prompt": "/prompt/:id", }, HasAWSEnvCredentials: api.AWSEnvCreds != nil, }) jsConf.Write([]byte(";\n")) r := ioutil.NewMultiReadSeeker(bytes.NewReader(jsConf.Bytes()), data) http.ServeContent(w, req, path, time.Now(), r) }
func (api *httpAPI) EventsHandler(w http.ResponseWriter, req *http.Request, params httprouter.Params) { api.InstallerStackMtx.Lock() s := api.InstallerStacks[params.ByName("id")] api.InstallerStackMtx.Unlock() if s == nil { httphelper.ObjectNotFoundError(w, "install instance not found") return } eventChan := make(chan *httpEvent) doneChan, errChan := s.Subscribe(eventChan) stream := sse.NewStream(w, eventChan, s.logger) stream.Serve() s.logger.Info(fmt.Sprintf("streaming events for %s", s.ID)) go func() { for { select { case err := <-errChan: s.logger.Info(err.Error()) stream.Error(err) case <-doneChan: stream.Close() return } } }() stream.Wait() }
func (api *httpAPI) DeleteCredential(w http.ResponseWriter, req *http.Request, params httprouter.Params) { if err := api.Installer.DeleteCredentials(params.ByName("id")); err != nil { httphelper.Error(w, err) return } w.WriteHeader(200) }
// serveGetLeader returns the current leader for a service. func (h *Handler) serveGetLeader(w http.ResponseWriter, r *http.Request, params httprouter.Params) { // Process as a stream if that's what the client wants. if strings.Contains(r.Header.Get("Accept"), "text/event-stream") { if !h.Store.IsLeader() { h.redirectToLeader(w, r) return } h.serveStream(w, params, discoverd.EventKindLeader) return } // Otherwise retrieve the current leader. service := params.ByName("service") leader, err := h.Store.ServiceLeader(service) if err != nil { hh.Error(w, err) return } else if leader == nil { hh.ObjectNotFoundError(w, "no leader found") return } // Write leader to the response. hh.JSON(w, 200, leader) }
func (r *Runner) getBuildLog(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { id := ps.ByName("build") b := &Build{} if err := r.db.View(func(tx *bolt.Tx) error { v := tx.Bucket(dbBucket).Get([]byte(id)) if err := json.Unmarshal(v, b); err != nil { return fmt.Errorf("could not decode build %s: %s", v, err) } return nil }); err != nil { http.Error(w, err.Error(), 500) return } if b.Finished() { if b.Version == BuildVersion1 { http.Redirect(w, req, b.LogURL, http.StatusMovedPermanently) return } if strings.Contains(req.Header.Get("Accept"), "text/event-stream") { if err := serveBuildLogStream(b, w); err != nil { http.Error(w, err.Error(), 500) } return } http.ServeFile(w, req, path.Join(args.AssetsDir, "build-log.html")) return } t, err := tail.TailFile(b.LogFile, tail.Config{Follow: true, MustExist: true}) if err != nil { http.Error(w, err.Error(), 500) return } if cn, ok := w.(http.CloseNotifier); ok { go func() { <-cn.CloseNotify() t.Stop() }() } else { defer t.Stop() } flush := func() { if fw, ok := w.(http.Flusher); ok { fw.Flush() } } w.Header().Set("Content-Type", textPlain) w.WriteHeader(http.StatusOK) flush() for line := range t.Lines { if _, err := io.WriteString(w, line.Text+"\n"); err != nil { log.Printf("serveBuildLog write error: %s\n", err) return } flush() if strings.HasPrefix(line.Text, "build finished") { return } } }
func (h *httpAPI) GetServiceMeta(w http.ResponseWriter, r *http.Request, params httprouter.Params) { meta := h.Store.GetServiceMeta(params.ByName("service")) if meta == nil { hh.ObjectNotFoundError(w, "service meta not found") return } hh.JSON(w, 200, meta) }
func (r *Runner) removeHost(c *cluster.Cluster, w http.ResponseWriter, q url.Values, ps httprouter.Params) error { hostID := ps.ByName("host") if err := c.RemoveHost(hostID); err != nil { return err } w.WriteHeader(200) return nil }
func (h *jobAPI) StopJob(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { id := ps.ByName("id") if err := h.host.StopJob(id); err != nil { httphelper.Error(w, err) return } w.WriteHeader(200) }
// serveDeleteRaftNodes removes a peer to the store cluster. func (h *Handler) serveDeleteRaftPeer(w http.ResponseWriter, r *http.Request, params httprouter.Params) { peer := params.ByName("peer") if err := h.Store.RemovePeer(peer); err == ErrNotLeader { h.redirectToLeader(w, r) return } else if err != nil { hh.Error(w, err) return } }
func (api *HTTPAPI) Inspect(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { volumeID := ps.ByName("volume_id") vol := api.vman.GetVolume(volumeID) if vol == nil { httphelper.ObjectNotFoundError(w, fmt.Sprintf("no volume with id %q", volumeID)) return } httphelper.JSON(w, 200, vol.Info()) }
func (api *httpAPI) GetCert(w http.ResponseWriter, req *http.Request, params httprouter.Params) { cluster, err := api.Installer.FindBaseCluster(params.ByName("id")) if err != nil { httphelper.ObjectNotFoundError(w, err.Error()) return } w.Header().Set("Content-Type", "application/x-x509-ca-cert") w.Header().Set("Content-Disposition", `attachment; filename="flynn-ca.cer"`) w.Write([]byte(cluster.CACert)) }
func (h *httpAPI) RemoveInstance(w http.ResponseWriter, r *http.Request, params httprouter.Params) { if err := h.Store.RemoveInstance(params.ByName("service"), params.ByName("instance_id")); err != nil { if IsNotFound(err) { hh.ObjectNotFoundError(w, err.Error()) } else { hh.Error(w, err) } return } }
func (api *HTTPAPI) Create(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { providerID := ps.ByName("provider_id") vol, err := api.vman.NewVolumeFromProvider(providerID) if err == volumemanager.NoSuchProvider { httphelper.ObjectNotFoundError(w, fmt.Sprintf("no volume provider with id %q", providerID)) return } httphelper.JSON(w, 200, vol.Info()) }
func (h *httpAPI) handleStream(w http.ResponseWriter, params httprouter.Params, kind discoverd.EventKind) { ch := make(chan *discoverd.Event, 64) // TODO: figure out how big this buffer should be stream := h.Store.Subscribe(params.ByName("service"), true, kind, ch) s := sse.NewStream(w, ch, nil) s.Serve() s.Wait() stream.Close() if err := stream.Err(); err != nil { s.CloseWithError(err) } }
func (api *httpAPI) DeleteCluster(w http.ResponseWriter, req *http.Request, params httprouter.Params) { if err := api.Installer.DeleteCluster(params.ByName("id")); err != nil { if err == ClusterNotFoundError { httphelper.ObjectNotFoundError(w, err.Error()) return } httphelper.Error(w, err) return } w.WriteHeader(200) }
func (h *jobAPI) GetJob(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { id := ps.ByName("id") if strings.Contains(r.Header.Get("Accept"), "text/event-stream") { if err := h.host.streamEvents(id, w); err != nil { httphelper.Error(w, err) } return } job := h.host.state.GetJob(id) httphelper.JSON(w, 200, job) }
func (api *httpAPI) ServeAsset(w http.ResponseWriter, req *http.Request, params httprouter.Params) { if strings.HasPrefix(params.ByName("assetPath"), "/application-") && strings.HasSuffix(params.ByName("assetPath"), ".js") { api.ServeApplicationJS(w, req, params) } else { path := filepath.Join("app", "build", params.ByName("assetPath")) data, err := api.Asset(path) if err != nil { httphelper.Error(w, err) return } http.ServeContent(w, req, path, time.Now(), data) } }
func (h *httpAPI) GetInstances(w http.ResponseWriter, r *http.Request, params httprouter.Params) { if strings.Contains(r.Header.Get("Accept"), "text/event-stream") { h.handleStream(w, params, discoverd.EventKindUp|discoverd.EventKindUpdate|discoverd.EventKindDown) return } instances := h.Store.Get(params.ByName("service")) if instances == nil { hh.ObjectNotFoundError(w, "service not found") return } hh.JSON(w, 200, instances) }
func (h *httpAPI) GetLeader(w http.ResponseWriter, r *http.Request, params httprouter.Params) { if strings.Contains(r.Header.Get("Accept"), "text/event-stream") { h.handleStream(w, params, discoverd.EventKindLeader) return } leader := h.Store.GetLeader(params.ByName("service")) if leader == nil { hh.ObjectNotFoundError(w, "no leader found") return } hh.JSON(w, 200, leader) }
// servePutRaftNodes joins a peer to the store cluster. func (h *Handler) servePutRaftPeer(w http.ResponseWriter, r *http.Request, params httprouter.Params) { peer := params.ByName("peer") if err := h.Store.AddPeer(peer); err == ErrNotLeader { h.redirectToLeader(w, r) return } else if err != nil { hh.Error(w, err) return } var targetLogIndex dt.TargetLogIndex targetLogIndex.LastIndex = h.Store.LastIndex() hh.JSON(w, 200, targetLogIndex) }
// serveGetServiceMeta returns the metadata for a service. func (h *Handler) serveGetServiceMeta(w http.ResponseWriter, r *http.Request, params httprouter.Params) { // Read path parameter. service := params.ByName("service") // Read meta from the store. meta := h.Store.ServiceMeta(service) if meta == nil { hh.ObjectNotFoundError(w, "service meta not found") return } // Write meta to the response. hh.JSON(w, 200, meta) }
func (h *httpAPI) RemoveService(w http.ResponseWriter, r *http.Request, params httprouter.Params) { service := params.ByName("service") if err := ValidServiceName(service); err != nil { hh.ValidationError(w, "", err.Error()) return } if err := h.Store.RemoveService(params.ByName("service")); err != nil { if IsNotFound(err) { hh.ObjectNotFoundError(w, err.Error()) } else { hh.Error(w, err) } return } }
func (api *HTTPAPI) Snapshot(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { volumeID := ps.ByName("volume_id") snap, err := api.vman.CreateSnapshot(volumeID) if err != nil { switch err { case volumemanager.NoSuchVolume: httphelper.ObjectNotFoundError(w, fmt.Sprintf("no volume with id %q", volumeID)) return default: httphelper.Error(w, err) return } } httphelper.JSON(w, 200, snap.Info()) }