mirror of https://github.com/glanceapp/glance.git
Added docker widget with documentation ✨
parent
c8570d07ef
commit
eacbb14279
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
@ -0,0 +1,44 @@
|
|||||||
|
{{ template "widget-base.html" . }}
|
||||||
|
|
||||||
|
{{ define "widget-content" }}
|
||||||
|
<div class="dynamic-columns list-gap-20 list-with-separator">
|
||||||
|
{{ range .Containers }}
|
||||||
|
<div class="docker-container flex items-center gap-15">
|
||||||
|
{{ template "container" . }}
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "container" }}
|
||||||
|
{{ if .Icon.URL }}
|
||||||
|
<img class="monitor-site-icon{{ if .Icon.IsFlatIcon }} flat-icon{{ end }}" src="{{ .Icon.URL }}" alt="" loading="lazy">
|
||||||
|
{{ end }}
|
||||||
|
<div class="min-width-0">
|
||||||
|
<a class="size-h3 color-highlight text-truncate block" href="{{ .URL }}" target="_blank" rel="noreferrer" title="{{ .Title }}">{{ .Title }}</a>
|
||||||
|
<div class="text-truncate" title="{{ .Image }}">{{ .Image }}</div>
|
||||||
|
<ul class="size-h6 color-subdue list-horizontal-text">
|
||||||
|
<li>{{ .StatusShort }}</li>
|
||||||
|
<li>{{ .StatusFull }}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{ if eq .StatusStyle "success" }}
|
||||||
|
<div class="docker-container-status-icon">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="var(--color-positive)">
|
||||||
|
<path fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
{{ else if eq .StatusStyle "warning" }}
|
||||||
|
<div class="docker-container-status-icon">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="var(--color-positive)">
|
||||||
|
<path fill-rule="evenodd" d="M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003ZM12 8.25a.75.75 0 0 1 .75.75v3.75a.75.75 0 0 1-1.5 0V9a.75.75 0 0 1 .75-.75Zm0 8.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
{{ else }}
|
||||||
|
<div class="docker-container-status-icon">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="var(--color-negative)">
|
||||||
|
<path fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12ZM12 8.25a.75.75 0 0 1 .75.75v3.75a.75.75 0 0 1-1.5 0V9a.75.75 0 0 1 .75-.75Zm0 8.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
package feed
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/docker/docker/client"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dockerAPIVersion = "1.24"
|
||||||
|
dockerGlanceEnable = "glance.enable"
|
||||||
|
dockerGlanceTitle = "glance.title"
|
||||||
|
dockerGlanceUrl = "glance.url"
|
||||||
|
dockerGlanceIconUrl = "glance.iconUrl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DockerContainer struct {
|
||||||
|
Id string
|
||||||
|
Image string
|
||||||
|
Title string
|
||||||
|
URL string
|
||||||
|
IconURL string
|
||||||
|
Status string
|
||||||
|
State string
|
||||||
|
}
|
||||||
|
|
||||||
|
func FetchDockerContainers(ctx context.Context) ([]DockerContainer, error) {
|
||||||
|
apiClient, err := client.NewClientWithOpts(client.WithVersion(dockerAPIVersion), client.FromEnv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer apiClient.Close()
|
||||||
|
|
||||||
|
containers, err := apiClient.ContainerList(ctx, container.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []DockerContainer
|
||||||
|
|
||||||
|
for _, c := range containers {
|
||||||
|
isGlanceEnabled := getLabelValue(c.Labels, dockerGlanceEnable, "true")
|
||||||
|
|
||||||
|
if isGlanceEnabled != "true" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
results = append(results, DockerContainer{
|
||||||
|
Id: c.ID,
|
||||||
|
Image: c.Image,
|
||||||
|
Title: getLabelValue(c.Labels, dockerGlanceTitle, strings.Join(c.Names, "")),
|
||||||
|
URL: getLabelValue(c.Labels, dockerGlanceUrl, ""),
|
||||||
|
IconURL: getLabelValue(c.Labels, dockerGlanceIconUrl, "si:docker"),
|
||||||
|
Status: c.Status,
|
||||||
|
State: c.State,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLabelValue get string value associated to a label.
|
||||||
|
func getLabelValue(labels map[string]string, labelName, defaultValue string) string {
|
||||||
|
if value, ok := labels[labelName]; ok && len(value) > 0 {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
package widget
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"golang.org/x/text/cases"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
"html/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/glanceapp/glance/internal/assets"
|
||||||
|
"github.com/glanceapp/glance/internal/feed"
|
||||||
|
)
|
||||||
|
|
||||||
|
type containerData struct {
|
||||||
|
Id string
|
||||||
|
Image string
|
||||||
|
URL string
|
||||||
|
Title string
|
||||||
|
Icon CustomIcon
|
||||||
|
StatusShort string
|
||||||
|
StatusFull string
|
||||||
|
StatusStyle string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Docker struct {
|
||||||
|
widgetBase `yaml:",inline"`
|
||||||
|
Containers []containerData `yaml:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (widget *Docker) Initialize() error {
|
||||||
|
widget.withTitle("Docker").withCacheDuration(1 * time.Minute)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (widget *Docker) Update(ctx context.Context) {
|
||||||
|
containers, err := feed.FetchDockerContainers(ctx)
|
||||||
|
|
||||||
|
if !widget.canContinueUpdateAfterHandlingErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var items []containerData
|
||||||
|
for _, container := range containers {
|
||||||
|
var item containerData
|
||||||
|
item.Id = container.Id
|
||||||
|
item.Image = container.Image
|
||||||
|
item.URL = container.URL
|
||||||
|
item.Title = container.Title
|
||||||
|
|
||||||
|
_ = item.Icon.FromURL(container.IconURL)
|
||||||
|
|
||||||
|
switch container.State {
|
||||||
|
case "paused":
|
||||||
|
case "starting":
|
||||||
|
case "unhealthy":
|
||||||
|
item.StatusStyle = "warning"
|
||||||
|
break
|
||||||
|
case "stopped":
|
||||||
|
case "dead":
|
||||||
|
case "exited":
|
||||||
|
item.StatusStyle = "error"
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
item.StatusStyle = "success"
|
||||||
|
}
|
||||||
|
|
||||||
|
item.StatusFull = container.Status
|
||||||
|
item.StatusShort = cases.Title(language.English, cases.Compact).String(container.State)
|
||||||
|
|
||||||
|
items = append(items, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
widget.Containers = items
|
||||||
|
}
|
||||||
|
|
||||||
|
func (widget *Docker) Render() template.HTML {
|
||||||
|
return widget.render(widget, assets.DockerTemplate)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue