mirror of
https://github.com/weaveworks/scope.git
synced 2026-08-18 03:46:45 +00:00
Merge pull request #1412 from weaveworks/1395-pause-container
Index Pods by UID and join with containers based on this.
This commit is contained in:
@@ -51,19 +51,30 @@ func (r *Reporter) deletePod(req xfer.Request, namespaceID, podID string) xfer.R
|
||||
}
|
||||
|
||||
// CapturePod is exported for testing
|
||||
func CapturePod(f func(xfer.Request, string, string) xfer.Response) func(xfer.Request) xfer.Response {
|
||||
func (r *Reporter) CapturePod(f func(xfer.Request, string, string) xfer.Response) func(xfer.Request) xfer.Response {
|
||||
return func(req xfer.Request) xfer.Response {
|
||||
namespaceID, podID, ok := report.ParsePodNodeID(req.NodeID)
|
||||
uid, ok := report.ParsePodNodeID(req.NodeID)
|
||||
if !ok {
|
||||
return xfer.ResponseErrorf("Invalid ID: %s", req.NodeID)
|
||||
}
|
||||
return f(req, namespaceID, podID)
|
||||
// find pod by UID
|
||||
var pod Pod
|
||||
r.client.WalkPods(func(p Pod) error {
|
||||
if p.UID() == uid {
|
||||
pod = p
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if pod == nil {
|
||||
return xfer.ResponseErrorf("Pod not found: %s", uid)
|
||||
}
|
||||
return f(req, pod.Namespace(), pod.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reporter) registerControls() {
|
||||
controls.Register(GetLogs, CapturePod(r.GetLogs))
|
||||
controls.Register(DeletePod, CapturePod(r.deletePod))
|
||||
controls.Register(GetLogs, r.CapturePod(r.GetLogs))
|
||||
controls.Register(DeletePod, r.CapturePod(r.deletePod))
|
||||
}
|
||||
|
||||
func (r *Reporter) deregisterControls() {
|
||||
|
||||
+23
-25
@@ -11,24 +11,23 @@ import (
|
||||
|
||||
// These constants are keys used in node metadata
|
||||
const (
|
||||
PodID = "kubernetes_pod_id"
|
||||
PodName = "kubernetes_pod_name"
|
||||
PodCreated = "kubernetes_pod_created"
|
||||
PodContainerIDs = "kubernetes_pod_container_ids"
|
||||
PodState = "kubernetes_pod_state"
|
||||
PodLabelPrefix = "kubernetes_pod_labels_"
|
||||
PodIP = "kubernetes_pod_ip"
|
||||
ServiceIDs = "kubernetes_service_ids"
|
||||
PodID = "kubernetes_pod_id"
|
||||
PodName = "kubernetes_pod_name"
|
||||
PodCreated = "kubernetes_pod_created"
|
||||
PodState = "kubernetes_pod_state"
|
||||
PodLabelPrefix = "kubernetes_pod_labels_"
|
||||
PodIP = "kubernetes_pod_ip"
|
||||
ServiceIDs = "kubernetes_service_ids"
|
||||
|
||||
StateDeleted = "deleted"
|
||||
)
|
||||
|
||||
// Pod represents a Kubernetes pod
|
||||
type Pod interface {
|
||||
UID() string
|
||||
ID() string
|
||||
Name() string
|
||||
Namespace() string
|
||||
ContainerIDs() []string
|
||||
Created() string
|
||||
AddServiceID(id string)
|
||||
Labels() labels.Labels
|
||||
@@ -47,6 +46,14 @@ func NewPod(p *api.Pod) Pod {
|
||||
return &pod{Pod: p}
|
||||
}
|
||||
|
||||
func (p *pod) UID() string {
|
||||
// Work around for master pod not reporting the right UID.
|
||||
if hash, ok := p.ObjectMeta.Annotations["kubernetes.io/config.hash"]; ok {
|
||||
return hash
|
||||
}
|
||||
return string(p.ObjectMeta.UID)
|
||||
}
|
||||
|
||||
func (p *pod) ID() string {
|
||||
return p.ObjectMeta.Namespace + "/" + p.ObjectMeta.Name
|
||||
}
|
||||
@@ -63,14 +70,6 @@ func (p *pod) Created() string {
|
||||
return p.ObjectMeta.CreationTimestamp.Format(time.RFC822)
|
||||
}
|
||||
|
||||
func (p *pod) ContainerIDs() []string {
|
||||
ids := []string{}
|
||||
for _, container := range p.Status.ContainerStatuses {
|
||||
ids = append(ids, strings.TrimPrefix(container.ContainerID, "docker://"))
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func (p *pod) Labels() labels.Labels {
|
||||
return labels.Set(p.ObjectMeta.Labels)
|
||||
}
|
||||
@@ -88,14 +87,13 @@ func (p *pod) NodeName() string {
|
||||
}
|
||||
|
||||
func (p *pod) GetNode(probeID string) report.Node {
|
||||
n := report.MakeNodeWith(report.MakePodNodeID(p.Namespace(), p.Name()), map[string]string{
|
||||
PodID: p.ID(),
|
||||
PodName: p.Name(),
|
||||
Namespace: p.Namespace(),
|
||||
PodCreated: p.Created(),
|
||||
PodContainerIDs: strings.Join(p.ContainerIDs(), " "),
|
||||
PodState: p.State(),
|
||||
PodIP: p.Status.PodIP,
|
||||
n := report.MakeNodeWith(report.MakePodNodeID(p.UID()), map[string]string{
|
||||
PodID: p.ID(),
|
||||
PodName: p.Name(),
|
||||
Namespace: p.Namespace(),
|
||||
PodCreated: p.Created(),
|
||||
PodState: p.State(),
|
||||
PodIP: p.Status.PodIP,
|
||||
report.ControlProbeID: probeID,
|
||||
})
|
||||
if len(p.serviceIDs) > 0 {
|
||||
|
||||
@@ -82,7 +82,7 @@ func (r *Reporter) podEvent(e Event, pod Pod) {
|
||||
rpt.Shortcut = true
|
||||
rpt.Pod.AddNode(
|
||||
report.MakeNodeWith(
|
||||
report.MakePodNodeID(pod.Namespace(), pod.Name()),
|
||||
report.MakePodNodeID(pod.UID()),
|
||||
map[string]string{PodState: StateDeleted},
|
||||
),
|
||||
)
|
||||
@@ -90,6 +90,21 @@ func (r *Reporter) podEvent(e Event, pod Pod) {
|
||||
}
|
||||
}
|
||||
|
||||
// Tag adds pod parents to container nodes.
|
||||
func (r *Reporter) Tag(rpt report.Report) (report.Report, error) {
|
||||
for id, n := range rpt.Container.Nodes {
|
||||
uid, ok := n.Latest.Lookup(docker.LabelPrefix + "io.kubernetes.pod.uid")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
rpt.Container.Nodes[id] = n.WithParents(report.EmptySets.Add(
|
||||
report.Pod,
|
||||
report.EmptyStringSet.Add(report.MakePodNodeID(uid)),
|
||||
))
|
||||
}
|
||||
return rpt, nil
|
||||
}
|
||||
|
||||
// Report generates a Report containing Container and ContainerImage topologies
|
||||
func (r *Reporter) Report() (report.Report, error) {
|
||||
result := report.MakeReport()
|
||||
@@ -97,13 +112,12 @@ func (r *Reporter) Report() (report.Report, error) {
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
podTopology, containerTopology, err := r.podTopology(services)
|
||||
podTopology, err := r.podTopology(services)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result.Service = result.Service.Merge(serviceTopology)
|
||||
result.Pod = result.Pod.Merge(podTopology)
|
||||
result.Container = result.Container.Merge(containerTopology)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -143,13 +157,12 @@ var GetNodeName = func(r *Reporter) (string, error) {
|
||||
return nodeName, err
|
||||
}
|
||||
|
||||
func (r *Reporter) podTopology(services []Service) (report.Topology, report.Topology, error) {
|
||||
func (r *Reporter) podTopology(services []Service) (report.Topology, error) {
|
||||
var (
|
||||
pods = report.MakeTopology().
|
||||
WithMetadataTemplates(PodMetadataTemplates).
|
||||
WithTableTemplates(PodTableTemplates)
|
||||
containers = report.MakeTopology()
|
||||
selectors = map[string]labels.Selector{}
|
||||
selectors = map[string]labels.Selector{}
|
||||
)
|
||||
pods.Controls.AddControl(report.Control{
|
||||
ID: GetLogs,
|
||||
@@ -169,7 +182,7 @@ func (r *Reporter) podTopology(services []Service) (report.Topology, report.Topo
|
||||
|
||||
thisNodeName, err := GetNodeName(r)
|
||||
if err != nil {
|
||||
return pods, containers, err
|
||||
return pods, err
|
||||
}
|
||||
err = r.client.WalkPods(func(p Pod) error {
|
||||
if p.NodeName() != thisNodeName {
|
||||
@@ -180,18 +193,8 @@ func (r *Reporter) podTopology(services []Service) (report.Topology, report.Topo
|
||||
p.AddServiceID(serviceID)
|
||||
}
|
||||
}
|
||||
nodeID := report.MakePodNodeID(p.Namespace(), p.Name())
|
||||
pods = pods.AddNode(p.GetNode(r.probeID))
|
||||
|
||||
for _, containerID := range p.ContainerIDs() {
|
||||
container := report.MakeNodeWith(report.MakeContainerNodeID(containerID), map[string]string{
|
||||
PodID: p.ID(),
|
||||
Namespace: p.Namespace(),
|
||||
docker.ContainerID: containerID,
|
||||
}).WithParents(report.EmptySets.Add(report.Pod, report.MakeStringSet(nodeID)))
|
||||
containers.AddNode(container)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return pods, containers, err
|
||||
return pods, err
|
||||
}
|
||||
|
||||
@@ -9,14 +9,19 @@ import (
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
|
||||
"github.com/weaveworks/scope/common/xfer"
|
||||
"github.com/weaveworks/scope/probe/docker"
|
||||
"github.com/weaveworks/scope/probe/kubernetes"
|
||||
"github.com/weaveworks/scope/report"
|
||||
"github.com/weaveworks/scope/test/reflect"
|
||||
)
|
||||
|
||||
var (
|
||||
nodeName = "nodename"
|
||||
pod1UID = "a1b2c3d4e5"
|
||||
pod2UID = "f6g7h8i9j0"
|
||||
podTypeMeta = unversioned.TypeMeta{
|
||||
Kind: "Pod",
|
||||
APIVersion: "v1",
|
||||
@@ -25,6 +30,7 @@ var (
|
||||
TypeMeta: podTypeMeta,
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "pong-a",
|
||||
UID: types.UID(pod1UID),
|
||||
Namespace: "ping",
|
||||
CreationTimestamp: unversioned.Now(),
|
||||
Labels: map[string]string{"ponger": "true"},
|
||||
@@ -44,6 +50,7 @@ var (
|
||||
TypeMeta: podTypeMeta,
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "pong-b",
|
||||
UID: types.UID(pod2UID),
|
||||
Namespace: "ping",
|
||||
CreationTimestamp: unversioned.Now(),
|
||||
Labels: map[string]string{"ponger": "true"},
|
||||
@@ -127,7 +134,7 @@ func (*mockClient) WalkNodes(f func(*api.Node) error) error {
|
||||
}
|
||||
func (*mockClient) WatchPods(func(kubernetes.Event, kubernetes.Pod)) {}
|
||||
func (c *mockClient) GetLogs(namespaceID, podName string) (io.ReadCloser, error) {
|
||||
r, ok := c.logs[report.MakePodNodeID(namespaceID, podName)]
|
||||
r, ok := c.logs[namespaceID+";"+podName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Not found")
|
||||
}
|
||||
@@ -157,8 +164,8 @@ func TestReporter(t *testing.T) {
|
||||
return nodeName, nil
|
||||
}
|
||||
|
||||
pod1ID := report.MakePodNodeID("ping", "pong-a")
|
||||
pod2ID := report.MakePodNodeID("ping", "pong-b")
|
||||
pod1ID := report.MakePodNodeID(pod1UID)
|
||||
pod2ID := report.MakePodNodeID(pod2UID)
|
||||
serviceID := report.MakeServiceNodeID("ping", "pongservice")
|
||||
rpt, _ := kubernetes.NewReporter(newMockClient(), nil, "", nil).Report()
|
||||
|
||||
@@ -169,20 +176,18 @@ func TestReporter(t *testing.T) {
|
||||
latest map[string]string
|
||||
}{
|
||||
{pod1ID, serviceID, map[string]string{
|
||||
kubernetes.PodID: "ping/pong-a",
|
||||
kubernetes.PodName: "pong-a",
|
||||
kubernetes.Namespace: "ping",
|
||||
kubernetes.PodCreated: pod1.Created(),
|
||||
kubernetes.PodContainerIDs: "container1 container2",
|
||||
kubernetes.ServiceIDs: "ping/pongservice",
|
||||
kubernetes.PodID: "ping/pong-a",
|
||||
kubernetes.PodName: "pong-a",
|
||||
kubernetes.Namespace: "ping",
|
||||
kubernetes.PodCreated: pod1.Created(),
|
||||
kubernetes.ServiceIDs: "ping/pongservice",
|
||||
}},
|
||||
{pod2ID, serviceID, map[string]string{
|
||||
kubernetes.PodID: "ping/pong-b",
|
||||
kubernetes.PodName: "pong-b",
|
||||
kubernetes.Namespace: "ping",
|
||||
kubernetes.PodCreated: pod1.Created(),
|
||||
kubernetes.PodContainerIDs: "container3 container4",
|
||||
kubernetes.ServiceIDs: "ping/pongservice",
|
||||
kubernetes.PodID: "ping/pong-b",
|
||||
kubernetes.PodName: "pong-b",
|
||||
kubernetes.Namespace: "ping",
|
||||
kubernetes.PodCreated: pod1.Created(),
|
||||
kubernetes.ServiceIDs: "ping/pongservice",
|
||||
}},
|
||||
} {
|
||||
node, ok := rpt.Pod.Nodes[pod.id]
|
||||
@@ -219,33 +224,23 @@ func TestReporter(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reporter should have tagged the containers
|
||||
for _, pod := range []struct {
|
||||
id, nodeID string
|
||||
containers []string
|
||||
}{
|
||||
{"ping/pong-a", pod1ID, []string{"container1", "container2"}},
|
||||
{"ping/pong-b", pod2ID, []string{"container3", "container4"}},
|
||||
} {
|
||||
for _, containerID := range pod.containers {
|
||||
node, ok := rpt.Container.Nodes[report.MakeContainerNodeID(containerID)]
|
||||
if !ok {
|
||||
t.Errorf("Expected report to have container %q, but not found", containerID)
|
||||
}
|
||||
// container should have pod id
|
||||
if have, ok := node.Latest.Lookup(kubernetes.PodID); !ok || have != pod.id {
|
||||
t.Errorf("Expected container %s latest %q: %q, got %q", containerID, kubernetes.PodID, pod.id, have)
|
||||
}
|
||||
// container should have namespace
|
||||
if have, ok := node.Latest.Lookup(kubernetes.Namespace); !ok || have != "ping" {
|
||||
t.Errorf("Expected container %s latest %q: %q, got %q", containerID, kubernetes.Namespace, "ping", have)
|
||||
}
|
||||
// container should have pod parent
|
||||
if parents, ok := node.Parents.Lookup(report.Pod); !ok || !parents.Contains(pod.nodeID) {
|
||||
t.Errorf("Expected container %s to have parent service %q, got %q", containerID, pod.nodeID, parents)
|
||||
}
|
||||
}
|
||||
func TestTagger(t *testing.T) {
|
||||
rpt := report.MakeReport()
|
||||
rpt.Container.AddNode(report.MakeNodeWith("container1", map[string]string{
|
||||
docker.LabelPrefix + "io.kubernetes.pod.uid": "123456",
|
||||
}))
|
||||
|
||||
rpt, err := kubernetes.NewReporter(newMockClient(), nil, "", nil).Tag(rpt)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
have, ok := rpt.Container.Nodes["container1"].Parents.Lookup(report.Pod)
|
||||
want := report.EmptyStringSet.Add(report.MakePodNodeID("123456"))
|
||||
if !ok || !reflect.DeepEqual(have, want) {
|
||||
t.Errorf("Expected container to have pod parent %v %v", have, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +264,7 @@ func TestReporterGetLogs(t *testing.T) {
|
||||
|
||||
// Should error on invalid IDs
|
||||
{
|
||||
resp := kubernetes.CapturePod(reporter.GetLogs)(xfer.Request{
|
||||
resp := reporter.CapturePod(reporter.GetLogs)(xfer.Request{
|
||||
NodeID: "invalidID",
|
||||
Control: kubernetes.GetLogs,
|
||||
})
|
||||
@@ -280,39 +275,39 @@ func TestReporterGetLogs(t *testing.T) {
|
||||
|
||||
// Should pass through errors from k8s (e.g if pod does not exist)
|
||||
{
|
||||
resp := kubernetes.CapturePod(reporter.GetLogs)(xfer.Request{
|
||||
resp := reporter.CapturePod(reporter.GetLogs)(xfer.Request{
|
||||
AppID: "appID",
|
||||
NodeID: report.MakePodNodeID("not", "found"),
|
||||
NodeID: report.MakePodNodeID("notfound"),
|
||||
Control: kubernetes.GetLogs,
|
||||
})
|
||||
if want := "Not found"; resp.Error != want {
|
||||
if want := "Pod not found: notfound"; resp.Error != want {
|
||||
t.Errorf("Expected error on invalid ID: %q, got %q", want, resp.Error)
|
||||
}
|
||||
}
|
||||
|
||||
pod1ID := report.MakePodNodeID("ping", "pong-a")
|
||||
podNamespaceAndID := "ping;pong-a"
|
||||
pod1Request := xfer.Request{
|
||||
AppID: "appID",
|
||||
NodeID: pod1ID,
|
||||
NodeID: report.MakePodNodeID(pod1UID),
|
||||
Control: kubernetes.GetLogs,
|
||||
}
|
||||
|
||||
// Inject our logs content, and watch for it to be closed
|
||||
closed := false
|
||||
wantContents := "logs: ping/pong-a"
|
||||
client.logs[pod1ID] = &callbackReadCloser{Reader: strings.NewReader(wantContents), close: func() error {
|
||||
client.logs[podNamespaceAndID] = &callbackReadCloser{Reader: strings.NewReader(wantContents), close: func() error {
|
||||
closed = true
|
||||
return nil
|
||||
}}
|
||||
|
||||
// Should create a new pipe for the stream
|
||||
resp := kubernetes.CapturePod(reporter.GetLogs)(pod1Request)
|
||||
resp := reporter.CapturePod(reporter.GetLogs)(pod1Request)
|
||||
if resp.Pipe == "" {
|
||||
t.Errorf("Expected pipe id to be returned, but got %#v", resp)
|
||||
}
|
||||
pipe, ok := pipes[resp.Pipe]
|
||||
if !ok {
|
||||
t.Errorf("Expected pipe %q to have been created, but wasn't", resp.Pipe)
|
||||
t.Fatalf("Expected pipe %q to have been created, but wasn't", resp.Pipe)
|
||||
}
|
||||
|
||||
// Should push logs from k8s client into the pipe
|
||||
|
||||
@@ -20,6 +20,7 @@ const (
|
||||
|
||||
// Service represents a Kubernetes service
|
||||
type Service interface {
|
||||
UID() string
|
||||
ID() string
|
||||
Name() string
|
||||
Namespace() string
|
||||
@@ -36,6 +37,10 @@ func NewService(s *api.Service) Service {
|
||||
return &service{Service: s}
|
||||
}
|
||||
|
||||
func (s *service) UID() string {
|
||||
return string(s.ObjectMeta.UID)
|
||||
}
|
||||
|
||||
func (s *service) ID() string {
|
||||
return s.ObjectMeta.Namespace + "/" + s.ObjectMeta.Name
|
||||
}
|
||||
|
||||
@@ -152,6 +152,7 @@ func probeMain(flags probeFlags) {
|
||||
reporter := kubernetes.NewReporter(client, clients, probeID, p)
|
||||
defer reporter.Stop()
|
||||
p.AddReporter(reporter)
|
||||
p.AddTagger(reporter)
|
||||
} else {
|
||||
log.Errorf("Kubernetes: failed to start client: %v", err)
|
||||
log.Errorf("Kubernetes: make sure to run Scope inside a POD with a service account or provide a valid kubernetes.api url")
|
||||
|
||||
@@ -236,6 +236,11 @@ func TestMakeDetailedContainerNode(t *testing.T) {
|
||||
Label: fixture.ServerHostName,
|
||||
TopologyID: "hosts",
|
||||
},
|
||||
{
|
||||
ID: fixture.ServerPodNodeID,
|
||||
Label: "pong-b",
|
||||
TopologyID: "pods",
|
||||
},
|
||||
},
|
||||
Connections: []detailed.ConnectionsSummary{
|
||||
{
|
||||
@@ -310,12 +315,13 @@ func TestMakeDetailedPodNode(t *testing.T) {
|
||||
serverProcessNodeSummary.Linkable = true // Temporary workaround for: https://github.com/weaveworks/scope/issues/1295
|
||||
want := detailed.Node{
|
||||
NodeSummary: detailed.NodeSummary{
|
||||
ID: id,
|
||||
Label: "pong-b",
|
||||
Rank: "ping/pong-b",
|
||||
Shape: "heptagon",
|
||||
Linkable: true,
|
||||
Pseudo: false,
|
||||
ID: id,
|
||||
Label: "pong-b",
|
||||
LabelMinor: "1 container",
|
||||
Rank: "ping/pong-b",
|
||||
Shape: "heptagon",
|
||||
Linkable: true,
|
||||
Pseudo: false,
|
||||
Metadata: []report.MetadataRow{
|
||||
{ID: "kubernetes_pod_id", Label: "ID", Value: "ping/pong-b", Priority: 1},
|
||||
{ID: "kubernetes_pod_state", Label: "State", Value: "running", Priority: 2},
|
||||
@@ -358,24 +364,6 @@ func TestMakeDetailedPodNode(t *testing.T) {
|
||||
Label: "Inbound",
|
||||
Columns: detailed.NormalColumns,
|
||||
Connections: []detailed.Connection{
|
||||
{
|
||||
ID: fmt.Sprintf("%s:%s-%s:%s-%d", render.IncomingInternetID, "", fixture.ServerPodNodeID, "", 80),
|
||||
NodeID: render.IncomingInternetID,
|
||||
Label: render.InboundMajor,
|
||||
Linkable: true,
|
||||
Metadata: []report.MetadataRow{
|
||||
{
|
||||
ID: "port",
|
||||
Value: "80",
|
||||
Datatype: "number",
|
||||
},
|
||||
{
|
||||
ID: "count",
|
||||
Value: "1",
|
||||
Datatype: "number",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: fmt.Sprintf("%s:%s-%s:%s-%d", fixture.ClientPodNodeID, "", fixture.ServerPodNodeID, "", 80),
|
||||
NodeID: fixture.ClientPodNodeID,
|
||||
@@ -394,6 +382,24 @@ func TestMakeDetailedPodNode(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: fmt.Sprintf("%s:%s-%s:%s-%d", render.IncomingInternetID, "", fixture.ServerPodNodeID, "", 80),
|
||||
NodeID: render.IncomingInternetID,
|
||||
Label: render.InboundMajor,
|
||||
Linkable: true,
|
||||
Metadata: []report.MetadataRow{
|
||||
{
|
||||
ID: "port",
|
||||
Value: "80",
|
||||
Datatype: "number",
|
||||
},
|
||||
{
|
||||
ID: "count",
|
||||
Value: "1",
|
||||
Datatype: "number",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -40,6 +40,7 @@ func TestParents(t *testing.T) {
|
||||
want: []detailed.Parent{
|
||||
{ID: fixture.ClientContainerImageNodeID, Label: fixture.ClientContainerImageName, TopologyID: "containers-by-image"},
|
||||
{ID: fixture.ClientHostNodeID, Label: fixture.ClientHostName, TopologyID: "hosts"},
|
||||
{ID: fixture.ClientPodNodeID, Label: "pong-a", TopologyID: "pods"},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
+18
-26
@@ -3,6 +3,7 @@ package render
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/weaveworks/scope/probe/docker"
|
||||
"github.com/weaveworks/scope/probe/kubernetes"
|
||||
"github.com/weaveworks/scope/report"
|
||||
)
|
||||
@@ -61,7 +62,7 @@ var PodServiceRenderer = FilterEmpty(report.Pod,
|
||||
// It does not have enough info to do that, and the resulting graph
|
||||
// must be merged with a container graph to get that info.
|
||||
func MapContainer2Pod(n report.Node, _ report.Networks) report.Nodes {
|
||||
// Uncontainerd becomes unmanaged in the pods view
|
||||
// Uncontained becomes unmanaged in the pods view
|
||||
if strings.HasPrefix(n.ID, MakePseudoNodeID(UncontainedID)) {
|
||||
id := MakePseudoNodeID(UnmanagedID, report.ExtractHostID(n))
|
||||
node := NewDerivedPseudoNode(id, n)
|
||||
@@ -73,34 +74,25 @@ func MapContainer2Pod(n report.Node, _ report.Networks) report.Nodes {
|
||||
return report.Nodes{n.ID: n}
|
||||
}
|
||||
|
||||
// Otherwise, if some some reason the container doesn't have a pod_id (maybe
|
||||
// slightly out of sync reports, or its not in a pod), just drop it
|
||||
namespace, ok := n.Latest.Lookup(kubernetes.Namespace)
|
||||
if !ok {
|
||||
id := MakePseudoNodeID(UnmanagedID, report.ExtractHostID(n))
|
||||
node := NewDerivedPseudoNode(id, n)
|
||||
return report.Nodes{id: node}
|
||||
// Ignore non-running containers
|
||||
if state, ok := n.Latest.Lookup(docker.ContainerState); ok && state != docker.StateRunning {
|
||||
return report.Nodes{}
|
||||
}
|
||||
podID, ok := n.Latest.Lookup(kubernetes.PodID)
|
||||
if !ok {
|
||||
id := MakePseudoNodeID(UnmanagedID, report.ExtractHostID(n))
|
||||
node := NewDerivedPseudoNode(id, n)
|
||||
return report.Nodes{id: node}
|
||||
}
|
||||
podName := strings.TrimPrefix(podID, namespace+"/")
|
||||
id := report.MakePodNodeID(namespace, podName)
|
||||
|
||||
// Due to a bug in kubernetes, addon pods on the master node are not returned
|
||||
// from the API. Adding the namespace and pod name is a workaround until
|
||||
// https://github.com/kubernetes/kubernetes/issues/14738 is fixed.
|
||||
return report.Nodes{
|
||||
id: NewDerivedNode(id, n).
|
||||
WithTopology(report.Pod).
|
||||
WithLatests(map[string]string{
|
||||
kubernetes.Namespace: namespace,
|
||||
kubernetes.PodName: podName,
|
||||
}),
|
||||
// Otherwise, if some some reason the container doesn't have a pod uid (maybe
|
||||
// slightly out of sync reports, or its not in a pod), make it part of unmanaged.
|
||||
uid, ok := n.Latest.Lookup(docker.LabelPrefix + "io.kubernetes.pod.uid")
|
||||
if !ok {
|
||||
id := MakePseudoNodeID(UnmanagedID, report.ExtractHostID(n))
|
||||
node := NewDerivedPseudoNode(id, n)
|
||||
return report.Nodes{id: node}
|
||||
}
|
||||
|
||||
id := report.MakePodNodeID(uid)
|
||||
node := NewDerivedNode(id, n).
|
||||
WithTopology(report.Pod)
|
||||
node.Counters = node.Counters.Add(n.Topology, 1)
|
||||
return report.Nodes{id: node}
|
||||
}
|
||||
|
||||
// MapPod2Service maps pod Nodes to service Nodes.
|
||||
|
||||
+6
-6
@@ -112,8 +112,8 @@ func MakeContainerImageNodeID(containerImageID string) string {
|
||||
}
|
||||
|
||||
// MakePodNodeID produces a pod node ID from its composite parts.
|
||||
func MakePodNodeID(namespaceID, podID string) string {
|
||||
return namespaceID + ScopeDelim + podID
|
||||
func MakePodNodeID(uid string) string {
|
||||
return uid + ScopeDelim + "<pod>"
|
||||
}
|
||||
|
||||
// MakeServiceNodeID produces a service node ID from its composite parts.
|
||||
@@ -167,12 +167,12 @@ func ParseAddressNodeID(addressNodeID string) (hostID, address string, ok bool)
|
||||
}
|
||||
|
||||
// ParsePodNodeID produces the namespace ID and pod ID from an pod node ID.
|
||||
func ParsePodNodeID(podNodeID string) (namespaceID, podID string, ok bool) {
|
||||
func ParsePodNodeID(podNodeID string) (uid string, ok bool) {
|
||||
fields := strings.SplitN(podNodeID, ScopeDelim, 2)
|
||||
if len(fields) != 2 {
|
||||
return "", "", false
|
||||
if len(fields) != 2 || fields[1] != "<pod>" {
|
||||
return "", false
|
||||
}
|
||||
return fields[0], fields[1], true
|
||||
return fields[0], true
|
||||
}
|
||||
|
||||
// ExtractHostID extracts the host id from Node
|
||||
|
||||
@@ -92,8 +92,10 @@ var (
|
||||
KubernetesNamespace = "ping"
|
||||
ClientPodID = "ping/pong-a"
|
||||
ServerPodID = "ping/pong-b"
|
||||
ClientPodNodeID = report.MakePodNodeID(KubernetesNamespace, "pong-a")
|
||||
ServerPodNodeID = report.MakePodNodeID(KubernetesNamespace, "pong-b")
|
||||
ClientPodUID = "5d4c3b2a1"
|
||||
ServerPodUID = "i9h8g7f6e"
|
||||
ClientPodNodeID = report.MakePodNodeID(ClientPodUID)
|
||||
ServerPodNodeID = report.MakePodNodeID(ServerPodUID)
|
||||
ServiceID = "ping/pongservice"
|
||||
ServiceNodeID = report.MakeServiceNodeID(KubernetesNamespace, "pongservice")
|
||||
|
||||
@@ -258,21 +260,20 @@ var (
|
||||
ClientContainerNodeID: report.MakeNodeWith(
|
||||
|
||||
ClientContainerNodeID, map[string]string{
|
||||
docker.ContainerID: ClientContainerID,
|
||||
docker.ContainerName: ClientContainerName,
|
||||
docker.ContainerHostname: ClientContainerHostname,
|
||||
docker.ImageID: ClientContainerImageID,
|
||||
report.HostNodeID: ClientHostNodeID,
|
||||
docker.LabelPrefix + "io.kubernetes.pod.name": ClientPodID,
|
||||
kubernetes.PodID: ClientPodID,
|
||||
kubernetes.Namespace: KubernetesNamespace,
|
||||
docker.ContainerState: docker.StateRunning,
|
||||
docker.ContainerStateHuman: docker.StateRunning,
|
||||
docker.ContainerID: ClientContainerID,
|
||||
docker.ContainerName: ClientContainerName,
|
||||
docker.ContainerHostname: ClientContainerHostname,
|
||||
docker.ImageID: ClientContainerImageID,
|
||||
report.HostNodeID: ClientHostNodeID,
|
||||
docker.LabelPrefix + "io.kubernetes.pod.uid": ClientPodUID,
|
||||
kubernetes.Namespace: KubernetesNamespace,
|
||||
docker.ContainerState: docker.StateRunning,
|
||||
docker.ContainerStateHuman: docker.StateRunning,
|
||||
}).
|
||||
WithTopology(report.Container).WithParents(report.EmptySets.
|
||||
Add("host", report.MakeStringSet(ClientHostNodeID)).
|
||||
Add("container_image", report.MakeStringSet(ClientContainerImageNodeID)).
|
||||
Add("pod", report.MakeStringSet(ClientPodID)),
|
||||
Add("pod", report.MakeStringSet(ClientPodNodeID)),
|
||||
).WithMetrics(report.Metrics{
|
||||
docker.CPUTotalUsage: ClientContainerCPUMetric,
|
||||
docker.MemoryUsage: ClientContainerMemoryMetric,
|
||||
@@ -290,14 +291,13 @@ var (
|
||||
docker.LabelPrefix + detailed.AmazonECSContainerNameLabel: "server",
|
||||
docker.LabelPrefix + "foo1": "bar1",
|
||||
docker.LabelPrefix + "foo2": "bar2",
|
||||
docker.LabelPrefix + "io.kubernetes.pod.name": ServerPodID,
|
||||
kubernetes.PodID: ServerPodID,
|
||||
docker.LabelPrefix + "io.kubernetes.pod.uid": ServerPodUID,
|
||||
kubernetes.Namespace: KubernetesNamespace,
|
||||
}).
|
||||
WithTopology(report.Container).WithParents(report.EmptySets.
|
||||
Add("host", report.MakeStringSet(ServerHostNodeID)).
|
||||
Add("container_image", report.MakeStringSet(ServerContainerImageNodeID)).
|
||||
Add("pod", report.MakeStringSet(ServerPodID)),
|
||||
Add("pod", report.MakeStringSet(ServerPodNodeID)),
|
||||
).WithMetrics(report.Metrics{
|
||||
docker.CPUTotalUsage: ServerContainerCPUMetric,
|
||||
docker.MemoryUsage: ServerContainerMemoryMetric,
|
||||
@@ -366,29 +366,25 @@ var (
|
||||
Pod: report.Topology{
|
||||
Nodes: report.Nodes{
|
||||
ClientPodNodeID: report.MakeNodeWith(
|
||||
|
||||
ClientPodNodeID, map[string]string{
|
||||
kubernetes.PodID: ClientPodID,
|
||||
kubernetes.PodName: "pong-a",
|
||||
kubernetes.Namespace: KubernetesNamespace,
|
||||
kubernetes.PodContainerIDs: ClientContainerID,
|
||||
kubernetes.ServiceIDs: ServiceID,
|
||||
report.HostNodeID: ClientHostNodeID,
|
||||
kubernetes.PodID: ClientPodID,
|
||||
kubernetes.PodName: "pong-a",
|
||||
kubernetes.Namespace: KubernetesNamespace,
|
||||
kubernetes.ServiceIDs: ServiceID,
|
||||
report.HostNodeID: ClientHostNodeID,
|
||||
}).
|
||||
WithTopology(report.Pod).WithParents(report.EmptySets.
|
||||
Add("host", report.MakeStringSet(ClientHostNodeID)).
|
||||
Add("service", report.MakeStringSet(ServiceID)),
|
||||
),
|
||||
ServerPodNodeID: report.MakeNodeWith(
|
||||
|
||||
ServerPodNodeID, map[string]string{
|
||||
kubernetes.PodID: ServerPodID,
|
||||
kubernetes.PodName: "pong-b",
|
||||
kubernetes.Namespace: KubernetesNamespace,
|
||||
kubernetes.PodState: "running",
|
||||
kubernetes.PodContainerIDs: ServerContainerID,
|
||||
kubernetes.ServiceIDs: ServiceID,
|
||||
report.HostNodeID: ServerHostNodeID,
|
||||
kubernetes.PodID: ServerPodID,
|
||||
kubernetes.PodName: "pong-b",
|
||||
kubernetes.Namespace: KubernetesNamespace,
|
||||
kubernetes.PodState: "running",
|
||||
kubernetes.ServiceIDs: ServiceID,
|
||||
report.HostNodeID: ServerHostNodeID,
|
||||
}).
|
||||
WithTopology(report.Pod).WithParents(report.EmptySets.
|
||||
Add("host", report.MakeStringSet(ServerHostNodeID)).
|
||||
|
||||
Reference in New Issue
Block a user