func init() { cfg := config.NewConfig() http.Handle("/images", util.Chain(func(w http.ResponseWriter, r *http.Request) { params := struct { LabelFilters string ViewOnly bool }{strings.Join(cfg.LabelFilters, ","), cfg.ViewOnly} util.RenderHTML(w, []string{"images/index.tmpl"}, params, nil) })) http.Handle("/image/history/", util.Chain(func(w http.ResponseWriter, r *http.Request) { id := r.URL.Path[len("/image/history/"):] client, _ := util.RequestGetParam(r, "client") util.RenderHTML(w, []string{"images/history.tmpl"}, struct{ ID, Client string }{id, client}, nil) })) /** * Images' API * @param q string search words * @return []model.DockerImage */ http.Handle("/api/images", util.Chain(func(w http.ResponseWriter, r *http.Request) { type image struct { Client *models.DockerClient `json:"client"` Images []models.DockerImage `json:"images"` } if dockers, ok := clients(w); ok { result := []*image{} d := make(chan *image, len(dockers)) for _, docker := range dockers { go func(docker *engine.Client) { images := docker.ListImages() var words []string if q, found := util.RequestGetParam(r, "q"); found { words = util.SplittedUpperStrings(q) } d <- &image{ Client: docker.Conf, Images: models.SearchImages(images, words), } }(docker) } for i := 0; i < len(dockers); i++ { images := <-d result = append(result, images) } close(d) util.RenderJSON(w, result, nil) } })) /** * An image's API */ // inspect http.Handle("/api/image/inspect/", util.Chain(func(w http.ResponseWriter, r *http.Request) { if docker, ok := client(w, util.RequestGetParamS(r, "client", "")); ok { id := r.URL.Path[len("/api/image/inspect/"):] meta := docker.InspectImage(id) util.RenderJSON(w, meta.Image, meta.Error) } })) // history http.Handle("/api/image/history/", util.Chain(func(w http.ResponseWriter, r *http.Request) { if docker, ok := client(w, util.RequestGetParamS(r, "client", "")); ok { id := r.URL.Path[len("/api/image/history/"):] util.RenderJSON(w, docker.History(id), nil) } })) // pull http.Handle("/api/image/pull/", util.Chain(func(w http.ResponseWriter, r *http.Request) { if docker, ok := client(w, util.RequestGetParamS(r, "client", "")); ok { meta := docker.Pull(r.URL.Path[len("/api/image/pull/"):]) if meta.Error != nil { util.RenderJSON(w, meta.Error.Error(), nil) return } util.RenderJSON(w, meta.Image, nil) } })) // rmi http.Handle("/api/image/rmi/", util.Chain(func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.NotFound(w, r) return } if docker, ok := client(w, util.RequestPostParamS(r, "client", "")); ok { err := docker.Rmi(r.URL.Path[len("/api/image/rmi/"):]) message := "removed successfully." if err != nil { message = err.Error() } util.RenderJSON(w, message, nil) } })) // tag http.Handle("/api/image/tag/", util.Chain(func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.NotFound(w, r) return } if docker, ok := client(w, util.RequestPostParamS(r, "client", "")); ok { repository, _ := util.RequestPostParam(r, "repo") tag, _ := util.RequestPostParam(r, "tag") err := docker.Tag(r.URL.Path[len("/api/image/tag/"):], repository, tag) message := "tagged successfully." if err != nil { message = err.Error() } util.RenderJSON(w, message, nil) } })) }
func renderErrorJSON(w http.ResponseWriter, err error) { util.RenderJSON(w, struct { Error string `json:"error"` }{err.Error()}, nil) }
func init() { cfg := config.NewConfig() http.Handle("/clients", util.Chain(func(w http.ResponseWriter, r *http.Request) { params := struct{ ViewOnly bool }{cfg.ViewOnly} util.RenderHTML(w, []string{"clients/index.tmpl"}, params, nil) })) http.Handle("/clients/export", util.Chain(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Disposition", "attachment; filename=docker-clients.json") w.Header().Set("Content-Type", "application/force-download") http.ServeFile(w, r, models.DockerClientSavePath) })) http.Handle("/clients/import", util.Chain(func(w http.ResponseWriter, r *http.Request) { var err error if err = r.ParseMultipartForm(32 << 20); nil != err { http.Error(w, err.Error(), http.StatusInternalServerError) return } for _, headers := range r.MultipartForm.File { var in multipart.File if in, err = headers[0].Open(); nil != err { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer in.Close() var out *os.File if out, err = os.Create(models.DockerClientSavePath); nil != err { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer out.Close() if _, err = io.Copy(out, in); nil != err { http.Error(w, err.Error(), http.StatusInternalServerError) return } } w.WriteHeader(http.StatusOK) })) /** * Docker client's API */ http.Handle("/api/clients", util.Chain(func(w http.ResponseWriter, r *http.Request) { result := []cInformation{} clients, err := models.LoadDockerClients() if err != nil { renderErrorJSON(w, err) return } c := make(chan cInformation, len(clients)) for _, client := range clients { go func(client *models.DockerClient) { engine.Configure(client.Endpoint, client.CertPath) client.IsActive = true docker, err := engine.Docker() if err != nil { client.IsActive = false c <- cInformation{client, nil, nil} return } info, _ := docker.Info() version, _ := docker.Version() c <- cInformation{client, info, version} }(client) } for i := 0; i < len(clients); i++ { info := <-c info.Client.Save() result = append(result, info) } close(c) util.RenderJSON(w, result, nil) })) http.Handle("/api/client/", util.Chain(func(w http.ResponseWriter, r *http.Request) { if endpoint, found := util.RequestPostParam(r, "endpoint"); found { cert, _ := util.RequestPostParam(r, "cert") engine.Configure(endpoint, cert) engine.Save() _, err := engine.Docker() if err != nil { models.RemoveDockerClientByEndpoint(endpoint) http.Error(w, err.Error(), http.StatusInternalServerError) return } http.Redirect(w, r, "/api/client/", http.StatusFound) return } if r.Method == "DELETE" { models.RemoveDockerClient(r.URL.Path[len("/api/client/"):]) w.WriteHeader(http.StatusOK) return } docker, err := engine.Docker() if err != nil { renderErrorJSON(w, err) return } info, _ := docker.Info() version, _ := docker.Version() util.RenderJSON(w, cInformation{nil, info, version}, nil) })) }
func init() { cfg := config.NewConfig() http.Handle("/container/top/", util.Chain(func(w http.ResponseWriter, r *http.Request) { id := r.URL.Path[len("/container/top/"):] client, _ := util.RequestGetParam(r, "client") params := struct{ ID, Name, Client string }{id, _label(id, client, cfg.LabelOverrideNames), client} util.RenderHTML(w, []string{"containers/top.tmpl"}, params, nil) })) http.Handle("/container/statlog/", util.Chain(func(w http.ResponseWriter, r *http.Request) { id := r.URL.Path[len("/container/statlog/"):] client, _ := util.RequestGetParam(r, "client") params := struct { ID, Name, Client string ViewOnly bool }{id, _label(id, client, cfg.LabelOverrideNames), client, cfg.ViewOnly} util.RenderHTML(w, []string{"containers/statlog.tmpl"}, params, nil) })) http.Handle("/container/changes/", util.Chain(func(w http.ResponseWriter, r *http.Request) { id := r.URL.Path[len("/container/changes/"):] client, _ := util.RequestGetParam(r, "client") params := struct{ ID, Name, Client string }{id, _label(id, client, cfg.LabelOverrideNames), client} util.RenderHTML(w, []string{"containers/changes.tmpl"}, params, nil) })) http.Handle("/logs", util.Chain(func(w http.ResponseWriter, r *http.Request) { params := struct{ LabelFilters string }{strings.Join(cfg.LabelFilters, ",")} util.RenderHTML(w, []string{"containers/logs.tmpl"}, params, nil) })) http.Handle("/statistics", util.Chain(func(w http.ResponseWriter, r *http.Request) { clients, err := models.LoadDockerClients() if err != nil { renderErrorJSON(w, err) return } params := struct { LabelFilters string Clients int }{strings.Join(cfg.LabelFilters, ","), len(clients)} util.RenderHTML(w, []string{"containers/statistics.tmpl"}, params, nil) })) /** * Containers' API * @param limit int * @param status int (0: all, 1: created, 2: restarting, 3: running, 4: paused, 5&6: exited) * @param q string search words * @return []model.DockerContainer */ http.Handle("/api/containers", util.Chain(func(w http.ResponseWriter, r *http.Request) { type container struct { Client *models.DockerClient `json:"client"` Containers []models.DockerContainer `json:"containers"` } dockers, ok := clients(w) if !ok { return } options := models.ListContainerOption(util.RequestGetParamI(r, "status", 0)) options.Limit = util.RequestGetParamI(r, "limit", 100) d := make(chan *container, len(dockers)) result := []*container{} for _, docker := range dockers { go func(docker *engine.Client) { containers, err := docker.ListContainers(options) if err != nil { renderErrorJSON(w, err) return } var words []string if q, found := util.RequestGetParam(r, "q"); found { words = util.SplittedUpperStrings(q) } d <- &container{ Client: docker.Conf, Containers: models.SearchContainers(containers, words), } }(docker) } for i := 0; i < len(dockers); i++ { result = append(result, <-d) } close(d) util.RenderJSON(w, result, nil) })) http.Handle("/api/statistics", util.Chain(func(w http.ResponseWriter, r *http.Request) { var dockers []*engine.Client if c, found := util.RequestGetParam(r, "client"); found { if docker, ok := client(w, c); ok { dockers = []*engine.Client{docker} } } else { var ok bool dockers, ok = clients(w) if !ok { return } } type statistics struct { Client *models.DockerClient `json:"client"` Stats map[string]map[string][]*api.Stats `json:"stats"` } stats := []statistics{} d := make(chan statistics, len(dockers)) for _, docker := range dockers { go func(docker *engine.Client) { candidate, err := docker.ListContainers(models.ListContainerOption(3)) if err != nil { renderErrorJSON(w, err) return } containers := models.SearchContainers(candidate, []string{}) c := make(chan models.DockerStats, len(containers)) stats := map[string]map[string][]*api.Stats{} count := util.RequestGetParamI(r, "count", 1) for _, container := range containers { go func(container models.DockerContainer) { stat, _ := docker.Stats(container.ID, count) name := strings.Join(container.Names, ",") if !misc.ZeroOrNil(cfg.LabelOverrideNames) { if label, found := container.Labels[cfg.LabelOverrideNames]; found { name = "*" + label } } c <- models.DockerStats{ ID: container.ID, Name: name, Stats: stat, } }(container) } for i := 0; i < len(containers); i++ { ds := <-c inner := map[string][]*api.Stats{} inner[ds.Name] = ds.Stats stats[ds.ID] = inner } close(c) d <- statistics{ Client: docker.Conf, Stats: stats, } }(docker) } for i := 0; i < len(dockers); i++ { stats = append(stats, <-d) } close(d) util.RenderJSON(w, stats, nil) })) http.Handle("/api/logs", util.Chain(func(w http.ResponseWriter, r *http.Request) { count := util.RequestGetParamI(r, "count", 100) var dockers []*engine.Client if c, found := util.RequestGetParam(r, "client"); found { if docker, ok := client(w, c); ok { dockers = []*engine.Client{docker} } } else { var ok bool dockers, ok = clients(w) if !ok { return } } type stdlogs struct { ID string `json:"id"` Stdout []string `json:"stdout"` Stderr []string `json:"stderr"` } type clientlogs struct { Client *models.DockerClient `json:"client"` Logs []stdlogs `json:"logs"` } logs := []clientlogs{} d := make(chan clientlogs, len(dockers)) for _, docker := range dockers { go func(docker *engine.Client) { candidate, err := docker.ListContainers(models.ListContainerOption(3)) if err != nil { renderErrorJSON(w, err) return } containers := models.SearchContainers(candidate, []string{}) c := make(chan stdlogs, len(containers)) inner := []stdlogs{} for _, container := range containers { go func(container models.DockerContainer) { stdout, stderr, err := docker.Logs(container.ID, count, 1*time.Second) if err != nil { renderErrorJSON(w, err) return } c <- stdlogs{container.ID, stdout, stderr} }(container) } for i := 0; i < len(containers); i++ { inner = append(inner, <-c) } close(c) d <- clientlogs{ Client: docker.Conf, Logs: inner, } }(docker) } for i := 0; i < len(dockers); i++ { logs = append(logs, <-d) } close(d) util.RenderJSON(w, logs, nil) })) /** * A container's API */ // inspect http.Handle("/api/container/inspect/", util.Chain(func(w http.ResponseWriter, r *http.Request) { if docker, ok := client(w, util.RequestGetParamS(r, "client", "")); ok { id := r.URL.Path[len("/api/container/inspect/"):] meta := docker.InspectContainer(id) util.RenderJSON(w, meta.Container, meta.Error) } })) // top http.Handle("/api/container/top/", util.Chain(func(w http.ResponseWriter, r *http.Request) { if docker, ok := client(w, util.RequestGetParamS(r, "client", "")); ok { id := r.URL.Path[len("/api/container/top/"):] args := util.RequestGetParamS(r, "args", "aux") util.RenderJSON(w, docker.Top(id, args), nil) } })) // stats http.Handle("/api/container/stats/", util.Chain(func(w http.ResponseWriter, r *http.Request) { if docker, ok := client(w, util.RequestGetParamS(r, "client", "")); ok { id := r.URL.Path[len("/api/container/stats/"):] result, err := docker.Stats(id, util.RequestGetParamI(r, "count", 1)) if err != nil { renderErrorJSON(w, err) return } util.RenderJSON(w, result, nil) } })) // logs http.Handle("/api/container/logs/", util.Chain(func(w http.ResponseWriter, r *http.Request) { if docker, ok := client(w, util.RequestGetParamS(r, "client", "")); ok { id := r.URL.Path[len("/api/container/logs/"):] count := util.RequestGetParamI(r, "count", 100) stdout, stderr, err := docker.Logs(id, count, 1*time.Second) if err != nil { renderErrorJSON(w, err) return } util.RenderJSON(w, struct { Stdout []string `json:"stdout"` Stderr []string `json:"stderr"` }{ stdout, stderr, }, nil) } })) // diff http.Handle("/api/container/changes/", util.Chain(func(w http.ResponseWriter, r *http.Request) { if docker, ok := client(w, util.RequestGetParamS(r, "client", "")); ok { id := r.URL.Path[len("/api/container/changes/"):] util.RenderJSON(w, docker.Changes(id), nil) } })) // restart http.Handle("/api/container/restart/", util.Chain(func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.NotFound(w, r) return } if docker, ok := client(w, util.RequestPostParamS(r, "client", "")); ok { meta := docker.Restart(r.URL.Path[len("/api/container/restart/"):], 5) if meta.Error != nil { renderErrorJSON(w, meta.Error) return } util.RenderJSON(w, meta.Container, nil) } })) // start http.Handle("/api/container/start/", util.Chain(func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.NotFound(w, r) return } if docker, ok := client(w, util.RequestPostParamS(r, "client", "")); ok { meta := docker.Start(r.URL.Path[len("/api/container/start/"):]) if meta.Error != nil { renderErrorJSON(w, meta.Error) return } util.RenderJSON(w, meta.Container, nil) } })) // stop http.Handle("/api/container/stop/", util.Chain(func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.NotFound(w, r) return } if docker, ok := client(w, util.RequestPostParamS(r, "client", "")); ok { meta := docker.Stop(r.URL.Path[len("/api/container/stop/"):]) if meta.Error != nil { renderErrorJSON(w, meta.Error) return } util.RenderJSON(w, meta.Container, nil) } })) // kill http.Handle("/api/container/kill/", util.Chain(func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.NotFound(w, r) return } if docker, ok := client(w, util.RequestPostParamS(r, "client", "")); ok { meta := docker.Kill(r.URL.Path[len("/api/container/kill/"):], 5) if meta.Error != nil { renderErrorJSON(w, meta.Error) return } util.RenderJSON(w, meta.Container, nil) } })) // rm http.Handle("/api/container/rm/", util.Chain(func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.NotFound(w, r) return } if docker, ok := client(w, util.RequestPostParamS(r, "client", "")); ok { err := docker.Rm(r.URL.Path[len("/api/container/rm/"):]) if err != nil { renderErrorJSON(w, err) return } util.RenderJSON(w, "removed successfully.", nil) } })) // rename http.Handle("/api/container/rename/", util.Chain(func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.NotFound(w, r) return } if docker, ok := client(w, util.RequestPostParamS(r, "client", "")); ok { if name, found := util.RequestPostParam(r, "name"); found { err := docker.Rename(r.URL.Path[len("/api/container/rename/"):], name) message := "renamed successfully." if err != nil { message = err.Error() } util.RenderJSON(w, message, nil) return } } })) // commit http.Handle("/api/container/commit/", util.Chain(func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.NotFound(w, r) return } if docker, ok := client(w, util.RequestPostParamS(r, "client", "")); ok { repository, _ := util.RequestPostParam(r, "repo") tag, _ := util.RequestPostParam(r, "tag") massage, _ := util.RequestPostParam(r, "msg") author, _ := util.RequestPostParam(r, "author") meta := docker.Commit( r.URL.Path[len("/api/container/commit/"):], repository, tag, massage, author) if meta.Error != nil { renderErrorJSON(w, meta.Error) return } util.RenderJSON(w, meta.Image, nil) } })) // @see https://docs.docker.com/docker-hub/builds/#webhooks type Repository struct { Name string `json:"name"` Owner string `json:"owner"` RepoName string `json:"repo_name"` } type Webhook struct { CallbackURL string `json:"callback_url"` Repository Repository `json:"repository"` } /** * Update by DockerHub * which pull an image again & restart the container to update its service */ http.Handle("/api/container/update", util.Chain(func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.NotFound(w, r) return } // parse parameters webhook := Webhook{} err := json.NewDecoder(r.Body).Decode(&webhook) if err != nil { logs.Warn.Print(err) http.NotFound(w, r) return } repository, tag := api.ParseRepositoryTag(webhook.Repository.RepoName) tag = misc.NVL(tag, "latest") // TODO trying all docker clients!! docker, ok := client(w, util.RequestPostParamS(r, "client", "")) if !ok { return } // pull the latest image var imageRepoTag string for _, image := range docker.ListImages() { for _, repotag := range image.RepoTags { if repotag == repository+":"+tag { imageRepoTag = repotag } } } if misc.ZeroOrNil(imageRepoTag) { http.NotFound(w, r) return } if meta := docker.Pull(imageRepoTag); meta.Error != nil { util.RenderJSON(w, meta.Error.Error(), nil) return } // list running containers containers, err := docker.ListContainers(models.ListContainerOption(3)) if err != nil { util.RenderJSON(w, err.Error(), nil) return } // restart its container restarted := []string{} for _, container := range containers { if container.Image == imageRepoTag { meta := docker.InspectContainer(container.ID) if meta.Error != nil { continue } // remove the existing container if meta := docker.Stop(container.ID); meta.Error != nil { renderErrorJSON(w, meta.Error) return } if err := docker.Rm(container.ID); err != nil { renderErrorJSON(w, err) return } // create a new container using the dead container configurations. // because if we just restart the container, its image would not // reference the new one. c := meta.Container if meta := docker.Create(c.Name, c.Config, c.HostConfig); meta.Error != nil { renderErrorJSON(w, meta.Error) return } if meta := docker.Start(c.Name[1:]); meta.Error != nil { renderErrorJSON(w, meta.Error) return } restarted = append(restarted, c.Name[1:]) } } util.RenderJSON(w, restarted, nil) })) }