Skip to content

Commit

Permalink
Improve support for Docker Swarm
Browse files Browse the repository at this point in the history
- Identify tags even when Docker Swarm uses a digest image
- Graph Docker Swarm tasks, services and namespaces
- Add Docker Swarm namespace as a tag
  • Loading branch information
AlexGustafsson committed Jan 12, 2025
1 parent 5f352f6 commit cdc7790
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 8 deletions.
14 changes: 10 additions & 4 deletions cmd/cupdate/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,6 @@ func main() {
edges := subgraph.Edges()
nodes := subgraph.Nodes()

// TODO: Rewrite to be more generic (to include Docker?)
var namespaceNode *platform.Node

mappedNodes := make(map[string]models.GraphNode)
Expand All @@ -316,6 +315,9 @@ func main() {
Type: string(n.Kind()),
Name: n.Name(),
}
if node.Type() == "docker/"+docker.ResourceKindSwarmNamespace {
namespaceNode = &node
}
case platform.ImageNode:
mappedNodes[node.ID()] = models.GraphNode{
Domain: "oci",
Expand All @@ -328,8 +330,8 @@ func main() {
}

tags := []string{}

// Set tags for resources
// TODO: Handle for docker as well?
if namespaceNode != nil {
children := edges[(*namespaceNode).ID()]
for childID, isParent := range children {
Expand All @@ -347,8 +349,12 @@ func main() {
}

if childNode != nil {
resource := (*childNode).(kubernetes.Resource)
tags = append(tags, kubernetes.TagName(resource.Kind()))
switch resource := (*childNode).(type) {
case kubernetes.Resource:
tags = append(tags, kubernetes.TagName(resource.Kind()))
case docker.Resource:
tags = append(tags, docker.TagName(resource.Kind()))
}
}
}
}
Expand Down
56 changes: 54 additions & 2 deletions internal/platform/docker/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,19 @@ func (p *Platform) Graph(ctx context.Context) (*graph.Graph[platform.Node], erro
continue
}

graph.InsertTree(
// Docker Swarm has a preference for digested names, even when started with
// a manifest referencing a tag, try to resolve the reference
if !reference.HasTag && reference.HasDigest {
r, _, ok := strings.Cut(container.Image, "@")
if ok {
ref, err := oci.ParseReference(r)
if err == nil {
reference = ref
}
}
}

tree := []platform.Node{
platform.ImageNode{
Reference: reference,
},
Expand All @@ -203,7 +215,46 @@ func (p *Platform) Graph(ctx context.Context) (*graph.Graph[platform.Node], erro
id: fmt.Sprintf("docker/containers/%s", container.ID),
name: container.Name(),
},
)
}

// Add graph nodes for Docker Swarm, if available
if container.Labels != nil {
if taskID, ok := container.Labels["com.docker.swarm.task.id"]; ok {
taskName, ok := container.Labels["com.docker.swarm.task.name"]
if !ok {
taskName = taskID
}

tree = append(tree, resource{
kind: ResourceKindSwarmTask,
id: fmt.Sprintf("docker/swarm/task/%s", taskID),
name: taskName,
})
}

if serviceID, ok := container.Labels["com.docker.swarm.service.id"]; ok {
serviceName, ok := container.Labels["com.docker.swarm.service.name"]
if !ok {
serviceName = serviceID
}

tree = append(tree, resource{
kind: ResourceKindSwarmService,
id: fmt.Sprintf("docker/swarm/service/%s", serviceID),
name: serviceName,
})
}

if namespace, ok := container.Labels["com.docker.stack.namespace"]; ok {
tree = append(tree, resource{
kind: ResourceKindSwarmNamespace,
id: fmt.Sprintf("docker/swarm/namespace/%s", namespace),
name: namespace,
})
}
}

graph.InsertTree(tree...)
}

return graph, nil
Expand All @@ -214,6 +265,7 @@ type Container struct {
Names []string
Image string
ImageID string
Labels map[string]string

// ... other ignored fields
}
Expand Down
11 changes: 10 additions & 1 deletion internal/platform/docker/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import (
type ResourceKind string

const (
ResourceKindContainer = "container"
ResourceKindContainer = "container"
ResourceKindSwarmTask = "swarm/task"
ResourceKindSwarmService = "swarm/service"
ResourceKindSwarmNamespace = "swarm/namespace"
)

type Resource interface {
Expand Down Expand Up @@ -49,6 +52,12 @@ func TagName(kind ResourceKind) string {
switch kind {
case ResourceKindContainer:
return "container"
case ResourceKindSwarmTask:
return "task"
case ResourceKindSwarmService:
return "service"
case ResourceKindSwarmNamespace:
return "namespace"
default:
// Panic as missing entries would be a programming issue, not runtime
// bug
Expand Down
5 changes: 4 additions & 1 deletion internal/worker/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/AlexGustafsson/cupdate/internal/httputil"
"github.com/AlexGustafsson/cupdate/internal/models"
"github.com/AlexGustafsson/cupdate/internal/oci"
"github.com/AlexGustafsson/cupdate/internal/platform/docker"
"github.com/AlexGustafsson/cupdate/internal/platform/kubernetes"
"github.com/AlexGustafsson/cupdate/internal/semver"
"github.com/AlexGustafsson/cupdate/internal/store"
Expand Down Expand Up @@ -139,7 +140,9 @@ func (w *Worker) ProcessRawImage(ctx context.Context, reference oci.Reference) e
data.InsertTag(node.Name)
}
case "docker":
// Not implemented
if node.Type == docker.ResourceKindSwarmNamespace {
data.InsertTag(node.Name)
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions web/graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ const titles: Record<string, Record<string, string | undefined> | undefined> = {
},
docker: {
container: 'Container',
'swarm/task': 'Task',
'swarm/service': 'Service',
'swarm/namespace': 'Namespace',
},
}

Expand Down

0 comments on commit cdc7790

Please sign in to comment.