mirror of
https://github.com/glanceapp/glance.git
synced 2025-06-21 18:31:24 +02:00
Docker containers: remote socket, category
and running-only
This commit is contained in:
parent
77fb199cb3
commit
18436e91e0
@ -1886,12 +1886,66 @@ If any of the child containers are down, their status will propagate up to the p
|
|||||||
| ---- | ---- | -------- | ------- |
|
| ---- | ---- | -------- | ------- |
|
||||||
| hide-by-default | boolean | no | false |
|
| hide-by-default | boolean | no | false |
|
||||||
| sock-path | string | no | /var/run/docker.sock |
|
| sock-path | string | no | /var/run/docker.sock |
|
||||||
|
| category | string | no | |
|
||||||
|
| running-only | boolean | no | false |
|
||||||
|
|
||||||
##### `hide-by-default`
|
##### `hide-by-default`
|
||||||
Whether to hide the containers by default. If set to `true` you'll have to manually add a `glance.hide: false` label to each container you want to display. By default all containers will be shown and if you want to hide a specific container you can add a `glance.hide: true` label.
|
Whether to hide the containers by default. If set to `true` you'll have to manually add a `glance.hide: false` label to each container you want to display. By default all containers will be shown and if you want to hide a specific container you can add a `glance.hide: true` label.
|
||||||
|
|
||||||
##### `sock-path`
|
##### `sock-path`
|
||||||
The path to the Docker socket.
|
The path to the Docker socket. This can also be a [remote socket](https://docs.docker.com/engine/daemon/remote-access/) or proxied socket using something like [docker-socket-proxy](https://github.com/Tecnativa/docker-socket-proxy).
|
||||||
|
|
||||||
|
###### `category`
|
||||||
|
Filter to only the containers which have this category specified via the `glance.category` label. Useful if you want to have multiple containers widgets, each showing a different set of containers.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>View example</summary>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
jellyfin:
|
||||||
|
image: jellyfin/jellyfin:latest
|
||||||
|
labels:
|
||||||
|
glance.name: Jellyfin
|
||||||
|
glance.icon: si:jellyfin
|
||||||
|
glance.url: https://jellyfin.domain.com
|
||||||
|
glance.category: media
|
||||||
|
|
||||||
|
gitea:
|
||||||
|
image: gitea/gitea:latest
|
||||||
|
labels:
|
||||||
|
glance.name: Gitea
|
||||||
|
glance.icon: si:gitea
|
||||||
|
glance.url: https://gitea.domain.com
|
||||||
|
glance.category: dev-tools
|
||||||
|
|
||||||
|
vaultwarden:
|
||||||
|
image: vaultwarden/server:latest
|
||||||
|
labels:
|
||||||
|
glance.name: Vaultwarden
|
||||||
|
glance.icon: si:vaultwarden
|
||||||
|
glance.url: https://vaultwarden.domain.com
|
||||||
|
glance.category: dev-tools
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can use the `category` property to filter the containers:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- type: docker-containers
|
||||||
|
title: Dev tool containers
|
||||||
|
category: dev-tools
|
||||||
|
|
||||||
|
- type: docker-containers
|
||||||
|
title: Media containers
|
||||||
|
category: media
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
##### `running-only`
|
||||||
|
Whether to only show running containers. If set to `true` only containers that are currently running will be displayed. If set to `false` all containers will be displayed regardless of their state.
|
||||||
|
|
||||||
#### Labels
|
#### Labels
|
||||||
| Name | Description |
|
| Name | Description |
|
||||||
@ -1904,6 +1958,7 @@ The path to the Docker socket.
|
|||||||
| glance.hide | Whether to hide the container. If set to `true` the container will not be displayed. Defaults to `false`. |
|
| glance.hide | Whether to hide the container. If set to `true` the container will not be displayed. Defaults to `false`. |
|
||||||
| glance.id | The custom ID of the container. Used to group containers under a single parent. |
|
| glance.id | The custom ID of the container. Used to group containers under a single parent. |
|
||||||
| glance.parent | The ID of the parent container. Used to group containers under a single parent. |
|
| glance.parent | The ID of the parent container. Used to group containers under a single parent. |
|
||||||
|
| glance.category | The category of the container. Used to filter containers by category. |
|
||||||
|
|
||||||
### DNS Stats
|
### DNS Stats
|
||||||
Display statistics from a self-hosted ad-blocking DNS resolver such as AdGuard Home, Pi-hole, or Technitium.
|
Display statistics from a self-hosted ad-blocking DNS resolver such as AdGuard Home, Pi-hole, or Technitium.
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"html/template"
|
"html/template"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -17,6 +18,8 @@ var dockerContainersWidgetTemplate = mustParseTemplate("docker-containers.html",
|
|||||||
type dockerContainersWidget struct {
|
type dockerContainersWidget struct {
|
||||||
widgetBase `yaml:",inline"`
|
widgetBase `yaml:",inline"`
|
||||||
HideByDefault bool `yaml:"hide-by-default"`
|
HideByDefault bool `yaml:"hide-by-default"`
|
||||||
|
RunningOnly bool `yaml:"running-only"`
|
||||||
|
Category string `yaml:"category"`
|
||||||
SockPath string `yaml:"sock-path"`
|
SockPath string `yaml:"sock-path"`
|
||||||
Containers dockerContainerList `yaml:"-"`
|
Containers dockerContainerList `yaml:"-"`
|
||||||
}
|
}
|
||||||
@ -32,7 +35,7 @@ func (widget *dockerContainersWidget) initialize() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (widget *dockerContainersWidget) update(ctx context.Context) {
|
func (widget *dockerContainersWidget) update(ctx context.Context) {
|
||||||
containers, err := fetchDockerContainers(widget.SockPath, widget.HideByDefault)
|
containers, err := fetchDockerContainers(widget.SockPath, widget.HideByDefault, widget.Category, widget.RunningOnly)
|
||||||
if !widget.canContinueUpdateAfterHandlingErr(err) {
|
if !widget.canContinueUpdateAfterHandlingErr(err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -54,6 +57,7 @@ const (
|
|||||||
dockerContainerLabelIcon = "glance.icon"
|
dockerContainerLabelIcon = "glance.icon"
|
||||||
dockerContainerLabelID = "glance.id"
|
dockerContainerLabelID = "glance.id"
|
||||||
dockerContainerLabelParent = "glance.parent"
|
dockerContainerLabelParent = "glance.parent"
|
||||||
|
dockerContainerLabelCategory = "glance.category"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -137,8 +141,8 @@ func dockerContainerStateToStateIcon(state string) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchDockerContainers(socketPath string, hideByDefault bool) (dockerContainerList, error) {
|
func fetchDockerContainers(socketPath string, hideByDefault bool, category string, runningOnly bool) (dockerContainerList, error) {
|
||||||
containers, err := fetchAllDockerContainersFromSock(socketPath)
|
containers, err := fetchDockerContainersFromSource(socketPath, category, runningOnly)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("fetching containers: %w", err)
|
return nil, fmt.Errorf("fetching containers: %w", err)
|
||||||
}
|
}
|
||||||
@ -239,17 +243,48 @@ func isDockerContainerHidden(container *dockerContainerJsonResponse, hideByDefau
|
|||||||
return hideByDefault
|
return hideByDefault
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchAllDockerContainersFromSock(socketPath string) ([]dockerContainerJsonResponse, error) {
|
func fetchDockerContainersFromSource(source string, category string, runningOnly bool) ([]dockerContainerJsonResponse, error) {
|
||||||
client := &http.Client{
|
var hostname string
|
||||||
Timeout: 5 * time.Second,
|
|
||||||
|
var client *http.Client
|
||||||
|
if strings.HasPrefix(source, "tcp://") || strings.HasPrefix(source, "http://") {
|
||||||
|
client = &http.Client{}
|
||||||
|
parsed, err := url.Parse(source)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing URL: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
port := parsed.Port()
|
||||||
|
if port == "" {
|
||||||
|
port = "80"
|
||||||
|
}
|
||||||
|
|
||||||
|
hostname = parsed.Hostname() + ":" + port
|
||||||
|
} else {
|
||||||
|
hostname = "docker"
|
||||||
|
client = &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
|
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
|
||||||
return net.Dial("unix", socketPath)
|
return net.Dial("unix", source)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
request, err := http.NewRequest("GET", "http://docker/containers/json?all=true", nil)
|
query := url.Values{}
|
||||||
|
query.Set("all", ternary(runningOnly, "false", "true"))
|
||||||
|
|
||||||
|
if category != "" {
|
||||||
|
query.Set(
|
||||||
|
"filters",
|
||||||
|
fmt.Sprintf(`{"label": ["%s=%s"]}`, dockerContainerLabelCategory, category),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
request, err := http.NewRequestWithContext(ctx, "GET", "http://"+hostname+"/containers/json?"+query.Encode(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("creating request: %w", err)
|
return nil, fmt.Errorf("creating request: %w", err)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user