Skip to content

Commit

Permalink
Add CNI Plugin listing and cluster version info (#1)
Browse files Browse the repository at this point in the history
* Add CNI Plugin listing and cluster version info
* Add ReplicaSet count
* Add agent restarts
* Add chart versions
* Add ConfigMap listing
  • Loading branch information
nfisher authored Feb 13, 2023
1 parent 3ccdc91 commit 25a731a
Show file tree
Hide file tree
Showing 8 changed files with 376 additions and 72 deletions.
132 changes: 111 additions & 21 deletions cluster/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,57 @@ package cluster
import (
"fmt"
"strconv"
"strings"
)

// NewIndex builds a new empty index for PodInfo.
func NewIndex() *Index {
return &Index{
Containers: make(Set),
DaemonSets: make(Set),
Deployments: make(Set),
Namespaces: make(Set),
Nodes: make(Set),
Pods: make(Set),
Running: make(Set),
StatefulSets: make(Set),
CNIPlugins: make(Counter),
Containers: make(Set),
DaemonSets: make(Set),
Deployments: make(Set),
Namespaces: make(Set),
Nodes: make(Set),
Pods: make(Set),
Running: make(Set),
StatefulSets: make(Set),
AgentRestarts: make(Counter),
AgentStatus: make(Counter),
ChartVersions: make(Counter),
ContainerRuntimes: make(Counter),
InstanceTypes: make(Counter),
KernelVersions: make(Counter),
KubeletVersions: make(Counter),
LinkedConfigMaps: make(Counter),
OSImages: make(Counter),
ProxyVersions: make(Counter),
Zones: make(Counter),
}
}

// Index provides indexes for a number of the cluster entities.
type Index struct {
Containers Set
DaemonSets Set
Deployments Set
Namespaces Set
Nodes Set
Pods Set
Running Set
StatefulSets Set
Containers Set
DaemonSets Set
Deployments Set
Namespaces Set
Nodes Set
Pods Set
Running Set
StatefulSets Set
AgentRestarts Counter
AgentStatus Counter
ChartVersions Counter
CNIPlugins Counter
ContainerRuntimes Counter
InstanceTypes Counter
KernelVersions Counter
KubeletVersions Counter
LinkedConfigMaps Counter
OSImages Counter
ProxyVersions Counter
Zones Counter
}

// Summary provides a summary overview of the number of entities in the cluster.
Expand All @@ -43,7 +68,7 @@ type Summary struct {
StatefulSets int
}

// Summary provides a summary count for all of the entities.
// Summary provides a summary pods for all the entities.
func (index *Index) Summary() Summary {
return Summary{
Containers: index.Containers.Len(),
Expand All @@ -66,8 +91,18 @@ const (
StatefulSet = "StatefulSet"
)

// Each extracts the relevant pod details and integrates it into the index.
func (index *Index) Each(pod PodInfo) {
func (index *Index) EachNode(node NodeInfo) {
index.ContainerRuntimes.Add(node.ContainerRuntime)
index.InstanceTypes.Add(node.InstanceType)
index.KernelVersions.Add(node.KernelVersion)
index.KubeletVersions.Add(node.KubeletVersion)
index.OSImages.Add(node.OSImage)
index.ProxyVersions.Add(node.ProxyVersion)
index.Zones.Add(node.Zone)
}

// EachPod extracts the relevant pod details and integrates it into the index.
func (index *Index) EachPod(pod PodInfo) {
qualifiedName := fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)
index.Pods.Add(qualifiedName)
if pod.IsRunning {
Expand All @@ -89,17 +124,72 @@ func (index *Index) Each(pod PodInfo) {
for n, t := range pod.Owners {
switch t {
case DaemonSet:
if IsCNIPlugin(n) {
index.CNIPlugins.Add(n)
}
if IsInstanaAgent(pod) {
index.AgentRestarts.Set(pod.Name, pod.Restarts)
index.AgentStatus.Add(pod.Status)
index.ChartVersions.Add(pod.ChartVersion)
for _, cm := range pod.LinkedConfigMaps {
index.LinkedConfigMaps.Add(fmt.Sprintf("%s/%s", cm.Namespace, cm.Name))
}
}
index.DaemonSets.Add(n)
break

case ReplicaSet: // hackish way to calculate deployments
index.Deployments.Add(n)
break

case StatefulSet:
index.StatefulSets.Add(n)
}
}
}

func IsInstanaAgent(pod PodInfo) bool {
for _, c := range pod.Containers {
if c.Name == "instana-agent" {
return true
}
}
return false
}

func IsCNIPlugin(n string) bool {
if n == "aws-node" {
return true
}
if strings.HasPrefix(n, "cilium") {
return true
}
if strings.HasPrefix(n, "calico") {
return true
}
if strings.HasPrefix(n, "flannel") {
return true
}
if strings.HasPrefix(n, "kube-router") {
return true
}
return false
}

type Counter map[string]int

func (c Counter) Add(item string) {
i := c[item]
i++
c[item] = i
}

func (c Counter) Set(item string, value int) {
c[item] = value
}

func (c Counter) Len() int {
return len(c)
}

// Set provides a set collection for strings.
type Set map[string]bool

Expand Down
16 changes: 8 additions & 8 deletions cluster/index_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ func Test_Each_increments_unique_namespaces(t *testing.T) {
ns2 := cluster.PodInfo{Host: "nod01", Namespace: "two", Owners: owner(cluster.ReplicaSet)}

index := cluster.NewIndex()
index.Each(ns1)
index.Each(ns2)
index.EachPod(ns1)
index.EachPod(ns2)

actual := index.Summary()
expected := cluster.Summary{
Expand All @@ -64,8 +64,8 @@ func Test_Each_container_increments_containers(t *testing.T) {
container2 := cluster.PodInfo{Host: "two", Name: "pod-2", Containers: []cluster.ContainerInfo{container("instana"), container("leader")}, Owners: owner(cluster.ReplicaSet)}

index := cluster.NewIndex()
index.Each(container1)
index.Each(container2)
index.EachPod(container1)
index.EachPod(container2)
actual := index.Summary()
expected := cluster.Summary{
Containers: 3, Deployments: 1, Namespaces: 1, Nodes: 2, Pods: 2,
Expand All @@ -89,9 +89,9 @@ func Test_Each_increments_unique_hosts(t *testing.T) {
unscheduled := cluster.PodInfo{Host: "", Name: "pod-2", Owners: owner(cluster.ReplicaSet)}

index := cluster.NewIndex()
index.Each(host1)
index.Each(host2)
index.Each(unscheduled)
index.EachPod(host1)
index.EachPod(host2)
index.EachPod(unscheduled)

actual := index.Summary()
expected := cluster.Summary{
Expand All @@ -118,7 +118,7 @@ func Test_Each_increments_by_owner_type(t *testing.T) {
tc := tc
t.Run(name, func(t *testing.T) {
index := cluster.NewIndex()
index.Each(tc.pod)
index.EachPod(tc.pod)
actual := index.Summary()
if !cmp.Equal(&actual, &tc.summary) {
t.Errorf("Summary() mismatch (-want +got)\n%s", cmp.Diff(tc.summary, actual))
Expand Down
47 changes: 30 additions & 17 deletions cluster/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,51 @@ import (
"time"
)

// PodApplyable is the interface to receive pod info from a pod collection.
type PodApplyable interface {
Each(PodInfo)
// Applyable is the interface to receive pod info from a pod collection.
type Applyable interface {
EachPod(PodInfo)
EachNode(NodeInfo)
}

// Info is a data structure for relevant cluster data.
type Info struct {
Name string
PodCount int
Pods []PodInfo
Version string
Started time.Time
Finished time.Time
Name string
NodeCount int
Nodes []NodeInfo
PodCount int
Pods []PodInfo
Version string
Started time.Time
Finished time.Time
}

// Apply iterates over each pod and yields it to the list of applyables.
func (info *Info) Apply(applyable ...PodApplyable) {
func (info *Info) Apply(applyable ...Applyable) {
for _, pod := range info.Pods {
for _, a := range applyable {
a.Each(pod)
a.EachPod(pod)
}
}

for _, node := range info.Nodes {
for _, a := range applyable {
a.EachNode(node)
}
}
}

// PodInfo is summary details for a pod.
type PodInfo struct {
Containers []ContainerInfo
Host string
IsRunning bool
Name string
Namespace string
Owners map[string]string
ChartVersion string
Containers []ContainerInfo
LinkedConfigMaps []LinkedConfigMap
Host string
IsRunning bool
Name string
Namespace string
Owners map[string]string
Restarts int
Status string
}

// ContainerInfo is summary details for a container.
Expand Down
26 changes: 18 additions & 8 deletions cluster/info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import (
func Test_Apply(t *testing.T) {
testCases := map[string]struct {
info cluster.Info
count int
pods int
nodes int
}{
"empty": {info: cluster.Info{}, count: 0},
"one pod": {info: cluster.Info{Pods: []cluster.PodInfo{{}}}, count: 1},
"empty": {info: cluster.Info{}, pods: 0},
"one pod": {info: cluster.Info{Pods: []cluster.PodInfo{{}}}, pods: 1},
"one node": {info: cluster.Info{Nodes: []cluster.NodeInfo{{}}}, nodes: 1},
}

for name, tc := range testCases {
Expand All @@ -21,17 +23,25 @@ func Test_Apply(t *testing.T) {
t.Parallel()
c := &counter{}
tc.info.Apply(c)
if c.count != tc.count {
t.Errorf("count=%v, want %v", c.count, tc.count)
if c.pods != tc.pods {
t.Errorf("pods=%v, want %v", c.pods, tc.pods)
}
if c.nodes != tc.nodes {
t.Errorf("nodes=%v, want %v", c.nodes, tc.nodes)
}
})
}
}

type counter struct {
count int
pods int
nodes int
}

func (c *counter) EachPod(_ cluster.PodInfo) {
c.pods++
}

func (c *counter) Each(_ cluster.PodInfo) {
c.count++
func (c *counter) EachNode(_ cluster.NodeInfo) {
c.nodes++
}
Loading

0 comments on commit 25a731a

Please sign in to comment.