mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-04 10:41:14 +00:00
Merge pull request #2444 from weaveworks/mike/docker-swarm/services-topology
Add very basic Docker Swarm service topology
This commit is contained in:
@@ -33,6 +33,7 @@ const (
|
||||
weaveID = "weave"
|
||||
ecsTasksID = "ecs-tasks"
|
||||
ecsServicesID = "ecs-services"
|
||||
swarmServicesID = "swarm-services"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -252,6 +253,14 @@ func MakeRegistry() *Registry {
|
||||
Options: []APITopologyOptionGroup{unmanagedFilter},
|
||||
HideIfEmpty: true,
|
||||
},
|
||||
APITopologyDesc{
|
||||
id: swarmServicesID,
|
||||
renderer: render.SwarmServiceRenderer,
|
||||
Name: "services",
|
||||
Rank: 3,
|
||||
Options: []APITopologyOptionGroup{unmanagedFilter},
|
||||
HideIfEmpty: true,
|
||||
},
|
||||
APITopologyDesc{
|
||||
id: hostsID,
|
||||
renderer: render.HostRenderer,
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestAPITopology(t *testing.T) {
|
||||
if err := decoder.Decode(&topologies); err != nil {
|
||||
t.Fatalf("JSON parse error: %s", err)
|
||||
}
|
||||
equals(t, 5, len(topologies))
|
||||
equals(t, 6, len(topologies))
|
||||
|
||||
for _, topology := range topologies {
|
||||
is200(t, ts, topology.URL)
|
||||
@@ -50,7 +50,7 @@ func TestAPITopology(t *testing.T) {
|
||||
}
|
||||
|
||||
// TODO: add ECS nodes in report fixture
|
||||
if topology.Name == "Tasks" {
|
||||
if topology.Name == "Tasks" || topology.Name == "services" {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ func TestAPITopologyAddsKubernetes(t *testing.T) {
|
||||
if err := decoder.Decode(&topologies); err != nil {
|
||||
t.Fatalf("JSON parse error: %s", err)
|
||||
}
|
||||
equals(t, 5, len(topologies))
|
||||
equals(t, 6, len(topologies))
|
||||
|
||||
// Enable the kubernetes topologies
|
||||
rpt := report.MakeReport()
|
||||
@@ -234,7 +234,7 @@ func TestAPITopologyAddsKubernetes(t *testing.T) {
|
||||
if err := decoder.Decode(&topologies); err != nil {
|
||||
t.Fatalf("JSON parse error: %s", err)
|
||||
}
|
||||
equals(t, 5, len(topologies))
|
||||
equals(t, 6, len(topologies))
|
||||
|
||||
found := false
|
||||
for _, topology := range topologies {
|
||||
|
||||
@@ -21,6 +21,7 @@ const (
|
||||
ImageLabelPrefix = "docker_image_label_"
|
||||
IsInHostNetwork = "docker_is_in_host_network"
|
||||
ImageTableID = "image_table"
|
||||
ServiceName = "service_name"
|
||||
)
|
||||
|
||||
// Exposed for testing
|
||||
@@ -132,6 +133,10 @@ var (
|
||||
Rank: 8,
|
||||
},
|
||||
}
|
||||
|
||||
SwarmServiceMetadataTemplates = report.MetadataTemplates{
|
||||
ServiceName: {ID: ServiceName, Label: "Service Name", From: report.FromLatest, Priority: 0},
|
||||
}
|
||||
)
|
||||
|
||||
// Reporter generate Reports containing Container and ContainerImage topologies
|
||||
@@ -177,6 +182,7 @@ func (r *Reporter) Report() (report.Report, error) {
|
||||
result.Container = result.Container.Merge(r.containerTopology(localAddrs))
|
||||
result.ContainerImage = result.ContainerImage.Merge(r.containerImageTopology())
|
||||
result.Overlay = result.Overlay.Merge(r.overlayTopology())
|
||||
result.SwarmService = result.SwarmService.Merge(r.swarmServiceTopology())
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -298,6 +304,10 @@ func (r *Reporter) overlayTopology() report.Topology {
|
||||
return report.MakeTopology().AddNode(node)
|
||||
}
|
||||
|
||||
func (r *Reporter) swarmServiceTopology() report.Topology {
|
||||
return report.MakeTopology().WithMetadataTemplates(SwarmServiceMetadataTemplates)
|
||||
}
|
||||
|
||||
// Docker sometimes prefixes ids with a "type" annotation, but it renders a bit
|
||||
// ugly and isn't necessary, so we should strip it off
|
||||
func trimImageID(id string) string {
|
||||
|
||||
@@ -2,6 +2,7 @@ package docker
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/weaveworks/scope/probe/process"
|
||||
"github.com/weaveworks/scope/report"
|
||||
@@ -21,6 +22,7 @@ var (
|
||||
|
||||
// Tagger is a tagger that tags Docker container information to process
|
||||
// nodes that have a PID.
|
||||
// It also populates the SwarmService topology if any of the associated docker labels are present.
|
||||
type Tagger struct {
|
||||
registry Registry
|
||||
procWalker process.Walker
|
||||
@@ -44,6 +46,31 @@ func (t *Tagger) Tag(r report.Report) (report.Report, error) {
|
||||
return report.MakeReport(), err
|
||||
}
|
||||
t.tag(tree, &r.Process)
|
||||
|
||||
// Scan for Swarm service info
|
||||
for containerID, container := range r.Container.Nodes {
|
||||
serviceID, ok := container.Latest.Lookup(LabelPrefix + "com.docker.swarm.service.id")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
serviceName, ok := container.Latest.Lookup(LabelPrefix + "com.docker.swarm.service.name")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(serviceName, "dockerswarm_") {
|
||||
serviceName = serviceName[len("dockerswarm_"):]
|
||||
}
|
||||
|
||||
nodeID := report.MakeSwarmServiceNodeID(serviceID)
|
||||
node := report.MakeNodeWith(nodeID, map[string]string{
|
||||
ServiceName: serviceName,
|
||||
})
|
||||
r.SwarmService = r.SwarmService.AddNode(node)
|
||||
|
||||
r.Container.Nodes[containerID] = container.WithParents(container.Parents.Add(report.SwarmService, report.MakeStringSet(nodeID)))
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"sort"
|
||||
|
||||
"github.com/weaveworks/scope/probe/awsecs"
|
||||
"github.com/weaveworks/scope/probe/docker"
|
||||
"github.com/weaveworks/scope/probe/host"
|
||||
"github.com/weaveworks/scope/probe/kubernetes"
|
||||
"github.com/weaveworks/scope/report"
|
||||
@@ -27,6 +28,7 @@ var (
|
||||
report.Service: kubernetesParentLabel,
|
||||
report.ECSTask: latestLookup(awsecs.TaskFamily),
|
||||
report.ECSService: ecsServiceParentLabel,
|
||||
report.SwarmService: latestLookup(docker.ServiceName),
|
||||
report.ContainerImage: containerImageParentLabel,
|
||||
report.Host: latestLookup(host.HostName),
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ var renderers = map[string]func(NodeSummary, report.Node) (NodeSummary, bool){
|
||||
report.ReplicaSet: podGroupNodeSummary,
|
||||
report.ECSTask: ecsTaskNodeSummary,
|
||||
report.ECSService: ecsServiceNodeSummary,
|
||||
report.SwarmService: swarmServiceNodeSummary,
|
||||
report.Host: hostNodeSummary,
|
||||
report.Overlay: weaveNodeSummary,
|
||||
report.Endpoint: nil, // Do not render
|
||||
@@ -93,6 +94,7 @@ var primaryAPITopology = map[string]string{
|
||||
report.Service: "services",
|
||||
report.ECSTask: "ecs-tasks",
|
||||
report.ECSService: "ecs-services",
|
||||
report.SwarmService: "swarm-services",
|
||||
report.Host: "hosts",
|
||||
}
|
||||
|
||||
@@ -276,6 +278,11 @@ func ecsServiceNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool)
|
||||
return base, true
|
||||
}
|
||||
|
||||
func swarmServiceNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
|
||||
base.Label, _ = n.Latest.Lookup(docker.ServiceName)
|
||||
return base, true
|
||||
}
|
||||
|
||||
func hostNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
|
||||
var (
|
||||
hostname, _ = n.Latest.Lookup(host.HostName)
|
||||
|
||||
@@ -33,5 +33,6 @@ var (
|
||||
SelectReplicaSet = TopologySelector(report.ReplicaSet)
|
||||
SelectECSTask = TopologySelector(report.ECSTask)
|
||||
SelectECSService = TopologySelector(report.ECSService)
|
||||
SelectSwarmService = TopologySelector(report.SwarmService)
|
||||
SelectOverlay = TopologySelector(report.Overlay)
|
||||
)
|
||||
|
||||
26
render/swarm.go
Normal file
26
render/swarm.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package render
|
||||
|
||||
import (
|
||||
"github.com/weaveworks/scope/report"
|
||||
)
|
||||
|
||||
// SwarmServiceRenderer is a Renderer for Docker Swarm services
|
||||
var SwarmServiceRenderer = ConditionalRenderer(renderSwarmTopologies,
|
||||
MakeMap(
|
||||
PropagateSingleMetrics(report.Container),
|
||||
MakeReduce(
|
||||
MakeMap(
|
||||
Map2Parent(report.SwarmService, UnmanagedID, nil),
|
||||
MakeFilter(
|
||||
IsRunning,
|
||||
ContainerWithImageNameRenderer,
|
||||
),
|
||||
),
|
||||
SelectSwarmService,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
func renderSwarmTopologies(rpt report.Report) bool {
|
||||
return len(rpt.SwarmService.Nodes) >= 1
|
||||
}
|
||||
@@ -130,6 +130,12 @@ var (
|
||||
|
||||
// ParseECSTaskNodeID parses a replica set node ID
|
||||
ParseECSTaskNodeID = parseSingleComponentID("ecs_task")
|
||||
|
||||
// MakeSwarmServiceNodeID produces a replica set node ID from its composite parts.
|
||||
MakeSwarmServiceNodeID = makeSingleComponentID("swarm_service")
|
||||
|
||||
// ParseSwarmServiceNodeID parses a replica set node ID
|
||||
ParseSwarmServiceNodeID = parseSingleComponentID("swarm_service")
|
||||
)
|
||||
|
||||
// makeSingleComponentID makes a single-component node id encoder
|
||||
|
||||
@@ -24,6 +24,7 @@ const (
|
||||
Overlay = "overlay"
|
||||
ECSService = "ecs_service"
|
||||
ECSTask = "ecs_task"
|
||||
SwarmService = "swarm_service"
|
||||
|
||||
// Shapes used for different nodes
|
||||
Circle = "circle"
|
||||
@@ -92,6 +93,11 @@ type Report struct {
|
||||
// Metadata is limited for now, more to come later. Edges are not present.
|
||||
ECSService Topology
|
||||
|
||||
// Swarm Service nodes are Docker Swarm services, which represent a specification for a
|
||||
// group of tasks (either one per host, or a desired count).
|
||||
// Edges are not present.
|
||||
SwarmService Topology
|
||||
|
||||
// Overlay nodes are active peers in any software-defined network that's
|
||||
// overlaid on the infrastructure. The information is scraped by polling
|
||||
// their status endpoints. Edges could be present, but aren't currently.
|
||||
@@ -170,6 +176,10 @@ func MakeReport() Report {
|
||||
WithShape(Heptagon).
|
||||
WithLabel("service", "services"),
|
||||
|
||||
SwarmService: MakeTopology().
|
||||
WithShape(Heptagon).
|
||||
WithLabel("service", "services"),
|
||||
|
||||
Sampling: Sampling{},
|
||||
Window: 0,
|
||||
Plugins: xfer.MakePluginSpecs(),
|
||||
@@ -192,6 +202,7 @@ func (r *Report) TopologyMap() map[string]*Topology {
|
||||
Overlay: &r.Overlay,
|
||||
ECSTask: &r.ECSTask,
|
||||
ECSService: &r.ECSService,
|
||||
SwarmService: &r.SwarmService,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,6 +264,7 @@ func (r *Report) WalkPairedTopologies(o *Report, f func(*Topology, *Topology)) {
|
||||
f(&r.Overlay, &o.Overlay)
|
||||
f(&r.ECSTask, &o.ECSTask)
|
||||
f(&r.ECSService, &o.ECSService)
|
||||
f(&r.SwarmService, &o.SwarmService)
|
||||
}
|
||||
|
||||
// Topology gets a topology by name
|
||||
|
||||
Reference in New Issue
Block a user