Skip to content

Commit c3cd3af

Browse files
committed
probe: merge controls from different probes
This change creates a host topology from cluster-wide probe using kubernetes nodes with cordon/un-cordon control. This also adds the logic to merge the controls coming from different probes for host topology.
1 parent ee9ffbf commit c3cd3af

File tree

5 files changed

+95
-17
lines changed

5 files changed

+95
-17
lines changed

examples/k8s/cluster-role.yaml

+10-5
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,13 @@ rules:
1515
- pods/log
1616
- replicationcontrollers
1717
- services
18-
- nodes
1918
- namespaces
2019
- persistentvolumes
2120
- persistentvolumeclaims
2221
verbs:
2322
- get
2423
- list
2524
- watch
26-
- create
27-
- update
28-
- patch
29-
- delete
3025
- apiGroups:
3126
- ""
3227
resources:
@@ -99,3 +94,13 @@ rules:
9994
verbs:
10095
- list
10196
- watch
97+
- apiGroups:
98+
- ""
99+
resources:
100+
- nodes
101+
verbs:
102+
- get
103+
- list
104+
- watch
105+
- update
106+
- patch

probe/kubernetes/client.go

+11
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ type Client interface {
6767
ScaleDown(namespaceID, id string) error
6868
// Cordon or Uncordon a node based on whether `desired` is true or false respectively.
6969
CordonNode(name string, desired bool) error
70+
// Returns a list of kubernetes nodes.
71+
GetNodes() ([]apiv1.Node, error)
7072
}
7173

7274
// ResourceMap is the mapping of resource and their GroupKind
@@ -647,3 +649,12 @@ func (c *client) CordonNode(name string, desired bool) error {
647649
}
648650
return nil
649651
}
652+
653+
func (c *client) GetNodes() ([]apiv1.Node, error) {
654+
l, err := c.client.CoreV1().Nodes().List(metav1.ListOptions{})
655+
if err != nil {
656+
return nil, err
657+
}
658+
659+
return l.Items, nil
660+
}

probe/kubernetes/reporter.go

+34-10
Original file line numberDiff line numberDiff line change
@@ -302,10 +302,6 @@ func (r *Tagger) Tag(rpt report.Report) (report.Report, error) {
302302

303303
rpt.Container.Nodes[id] = n.WithParent(report.Pod, report.MakePodNodeID(uid))
304304
}
305-
for id, n := range rpt.Host.Nodes {
306-
controls := append(n.ActiveControls(), CordonNode, UncordonNode)
307-
rpt.Host.Nodes[id] = n.WithLatestActiveControls(controls...)
308-
}
309305
return rpt, nil
310306
}
311307

@@ -364,6 +360,10 @@ func (r *Reporter) Report() (report.Report, error) {
364360
if err != nil {
365361
return result, err
366362
}
363+
hostTopology, err := r.hostTopology()
364+
if err != nil {
365+
return result, err
366+
}
367367

368368
result.Pod = result.Pod.Merge(podTopology)
369369
result.Service = result.Service.Merge(serviceTopology)
@@ -378,12 +378,7 @@ func (r *Reporter) Report() (report.Report, error) {
378378
result.VolumeSnapshot = result.VolumeSnapshot.Merge(volumeSnapshotTopology)
379379
result.VolumeSnapshotData = result.VolumeSnapshotData.Merge(volumeSnapshotDataTopology)
380380
result.Job = result.Job.Merge(jobTopology)
381-
382-
// Add buttons for Host view, with the ID of the Kubernetes probe
383-
for _, control := range CordonControl {
384-
control.ProbeID = r.probeID
385-
result.Host.Controls.AddControl(control)
386-
}
381+
result.Host = result.Host.Merge(hostTopology)
387382

388383
return result, nil
389384
}
@@ -704,3 +699,32 @@ func (r *Reporter) namespaceTopology() (report.Topology, error) {
704699
})
705700
return result, err
706701
}
702+
703+
func (r *Reporter) hostTopology() (report.Topology, error) {
704+
result := report.MakeTopology()
705+
// Add buttons for Host view, with the ID of the Kubernetes probe
706+
for _, control := range CordonControl {
707+
control.ProbeID = r.probeID
708+
result.Controls.AddControl(control)
709+
}
710+
711+
nodes, err := r.client.GetNodes()
712+
if err != nil {
713+
return result, err
714+
}
715+
716+
for _, n := range nodes {
717+
var activeControl string
718+
if n.Spec.Unschedulable {
719+
activeControl = UncordonNode
720+
} else {
721+
activeControl = CordonNode
722+
}
723+
result.AddNode(
724+
report.MakeNode(report.MakeHostNodeID(n.Name)).
725+
WithTopology("host").
726+
WithLatestActiveControls(activeControl),
727+
)
728+
}
729+
return result, nil
730+
}

probe/kubernetes/reporter_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,10 @@ func (c *mockClient) CordonNode(name string, desired bool) error {
209209
return nil
210210
}
211211

212+
func (c *mockClient) GetNodes() ([]apiv1.Node, error) {
213+
return nil, nil
214+
}
215+
212216
type mockPipeClient map[string]xfer.Pipe
213217

214218
func (c mockPipeClient) PipeConnection(appID, id string, pipe xfer.Pipe) error {

report/node.go

+36-2
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ func (n Node) WithChild(child Node) Node {
173173
return n
174174
}
175175

176-
// Merge mergses the individual components of a node and returns a
176+
// Merge merges the individual components of a node and returns a
177177
// fresh node.
178178
func (n Node) Merge(other Node) Node {
179179
id := n.ID
@@ -186,7 +186,8 @@ func (n Node) Merge(other Node) Node {
186186
} else if other.Topology != "" && topology != other.Topology {
187187
panic("Cannot merge nodes with different topology types: " + topology + " != " + other.Topology)
188188
}
189-
return Node{
189+
190+
newNode := Node{
190191
ID: id,
191192
Topology: topology,
192193
Sets: n.Sets.Merge(other.Sets),
@@ -196,6 +197,39 @@ func (n Node) Merge(other Node) Node {
196197
Parents: n.Parents.Merge(other.Parents),
197198
Children: n.Children.Merge(other.Children),
198199
}
200+
201+
// Special case to merge controls from two different probes.
202+
if topology == "host" {
203+
controlString, _ := newNode.Latest.Lookup(NodeActiveControls)
204+
activeControls := strings.Split(controlString, ScopeDelim)
205+
// Host topology should have only two active controls.
206+
// Length equals to 2 means controls were merged properly.
207+
if len(activeControls) != 2 {
208+
// Check if host_exec control is missing.
209+
if !StringSet(activeControls).Contains("host_exec") {
210+
activeControls = append(activeControls, "host_exec")
211+
return newNode.
212+
WithLatestActiveControls(activeControls...)
213+
}
214+
// Else control kubernetes_cordon_node or kubernetes_uncordon_node is missing.
215+
// Merge controls from current node.
216+
c, _ := n.Latest.Lookup(NodeActiveControls)
217+
x := strings.Split(c, ScopeDelim)
218+
activeControls = append(activeControls, x...)
219+
220+
// Merge controls from incoming node.
221+
c, _ = other.Latest.Lookup(NodeActiveControls)
222+
x = strings.Split(c, ScopeDelim)
223+
activeControls = append(activeControls, x...)
224+
225+
// Use a StringSet to avoid duplicate controls from current and incoming node.
226+
var final StringSet
227+
return newNode.
228+
WithLatestActiveControls(final.Add(activeControls...)...)
229+
}
230+
}
231+
232+
return newNode
199233
}
200234

201235
// UnsafeUnMerge removes data from n that would be added by merging other,

0 commit comments

Comments
 (0)