示例#1
0
func fileConfig() Config {
	path := misc.NVL(os.Getenv("CONFIG_FILE_PATH"), "/etc/docker-webui/config.json")
	file, err := os.Open(path)
	if err != nil {
		return Config{}
	}
	data, err := ioutil.ReadAll(file)
	if err != nil {
		log.Fatal("Unable to read config file", "err:", err)
		return Config{}
	}
	if strings.TrimSpace(string(data)) == "" {
		return Config{}
	}
	config := Config{}
	err = json.Unmarshal(data, &config)
	if err != nil {
		log.Fatal("Error reading config json data. [message] ", err)
	}
	return config
}
示例#2
0
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)
	}))
}
示例#3
0
func (c *Client) pull(image string) ImageMetadata {
	reader, writer := io.Pipe()
	defer writer.Close()

	repository, tag := api.ParseRepositoryTag(image)
	tag = misc.NVL(tag, "latest")
	opts := api.PullImageOptions{
		Repository:   repository + ":" + tag,
		OutputStream: writer,
	}

	// check output goroutine
	began := make(chan bool, 1)
	once := sync.Once{}

	go func() {
		reader := bufio.NewReader(reader)
		var line string
		var err error
		for err == nil {
			line, err = reader.ReadString('\n')
			if err != nil {
				break
			}
			once.Do(func() {
				began <- true
			})
			if strings.Contains(line, "already being pulled by another client. Waiting.") {
				logs.Error.Printf("Image 'pull' status marked as already being pulled. image: %v, status: %v", opts.Repository, line)
			}
		}
		if err != nil && err != io.EOF {
			logs.Warn.Printf("Error reading pull image status. image: %v, err: %v", opts.Repository, err)
		}
	}()

	// pull the image
	timeout := time.After(cfg.DockerPullBeginTimeout)
	finished := make(chan error, 1)
	go func() {
		finished <- c.PullImage(opts, api.AuthConfiguration{})
		logs.Debug.Printf("Pull completed for image: %v", opts.Repository)
	}()

	// wait for the pulling to begin
	select {
	case <-began:
		break
	case err := <-finished:
		if err != nil {
			return ImageMetadata{
				Image: &api.Image{ID: ""},
				Error: CannotXContainerError{"Pull", err.Error()},
			}
		}
		return c.InspectImage(opts.Repository)
	case <-timeout:
		return ImageMetadata{
			Image: &api.Image{ID: ""},
			Error: &DockerTimeoutError{cfg.DockerPullBeginTimeout, "pullBegin"},
		}
	}

	// wait for the completion
	err := <-finished
	if err != nil {
		return ImageMetadata{
			Image: &api.Image{ID: ""},
			Error: CannotXContainerError{"Pull", err.Error()},
		}
	}
	return c.InspectImage(opts.Repository)
}