Merge pull request #2724 from weaveworks/mike/k8s/jobs-and-petsets

kubernetes: Add StatefulSets and CronJobs
This commit is contained in:
Mike Lang
2017-07-19 16:27:53 -07:00
committed by GitHub
12 changed files with 360 additions and 18 deletions
+65 -10
View File
@@ -11,9 +11,13 @@ import (
log "github.com/Sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
apiv1 "k8s.io/client-go/pkg/api/v1"
apiv1beta1 "k8s.io/client-go/pkg/apis/extensions/v1beta1"
apiappsv1beta1 "k8s.io/client-go/pkg/apis/apps/v1beta1"
apibatchv1 "k8s.io/client-go/pkg/apis/batch/v1"
apibatchv2alpha1 "k8s.io/client-go/pkg/apis/batch/v2alpha1"
apiextensionsv1beta1 "k8s.io/client-go/pkg/apis/extensions/v1beta1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/clientcmd"
@@ -28,6 +32,8 @@ type Client interface {
WalkDeployments(f func(Deployment) error) error
WalkReplicaSets(f func(ReplicaSet) error) error
WalkDaemonSets(f func(DaemonSet) error) error
WalkStatefulSets(f func(StatefulSet) error) error
WalkCronJobs(f func(CronJob) error) error
WalkReplicationControllers(f func(ReplicationController) error) error
WalkNodes(f func(*apiv1.Node) error) error
@@ -48,6 +54,9 @@ type client struct {
deploymentStore cache.Store
replicaSetStore cache.Store
daemonSetStore cache.Store
statefulSetStore cache.Store
jobStore cache.Store
cronJobStore cache.Store
replicationControllerStore cache.Store
nodeStore cache.Store
@@ -157,9 +166,21 @@ func NewClient(config ClientConfig) (Client, error) {
if _, err := c.Extensions().Deployments(metav1.NamespaceAll).List(metav1.ListOptions{}); err != nil {
log.Infof("Deployments, ReplicaSets and DaemonSets are not supported by this Kubernetes version: %v", err)
} else {
result.deploymentStore = result.setupStore(c.ExtensionsV1beta1Client.RESTClient(), "deployments", &apiv1beta1.Deployment{}, nil)
result.replicaSetStore = result.setupStore(c.ExtensionsV1beta1Client.RESTClient(), "replicasets", &apiv1beta1.ReplicaSet{}, nil)
result.daemonSetStore = result.setupStore(c.ExtensionsV1beta1Client.RESTClient(), "daemonsets", &apiv1beta1.DaemonSet{}, nil)
result.deploymentStore = result.setupStore(c.ExtensionsV1beta1Client.RESTClient(), "deployments", &apiextensionsv1beta1.Deployment{}, nil)
result.replicaSetStore = result.setupStore(c.ExtensionsV1beta1Client.RESTClient(), "replicasets", &apiextensionsv1beta1.ReplicaSet{}, nil)
result.daemonSetStore = result.setupStore(c.ExtensionsV1beta1Client.RESTClient(), "daemonsets", &apiextensionsv1beta1.DaemonSet{}, nil)
}
// CronJobs and StatefulSets were introduced later. Easiest to use the same technique.
if _, err := c.BatchV2alpha1().CronJobs(metav1.NamespaceAll).List(metav1.ListOptions{}); err != nil {
log.Infof("CronJobs are not supported by this Kubernetes version: %v", err)
} else {
result.jobStore = result.setupStore(c.BatchV1Client.RESTClient(), "jobs", &apibatchv1.Job{}, nil)
result.cronJobStore = result.setupStore(c.BatchV2alpha1Client.RESTClient(), "cronjobs", &apibatchv2alpha1.CronJob{}, nil)
}
if _, err := c.Apps().StatefulSets(metav1.NamespaceAll).List(metav1.ListOptions{}); err != nil {
log.Infof("StatefulSets are not supported by this Kubernetes version: %v", err)
} else {
result.statefulSetStore = result.setupStore(c.AppsV1beta1Client.RESTClient(), "statefulsets", &apiappsv1beta1.StatefulSet{}, nil)
}
return result, nil
@@ -214,7 +235,7 @@ func (c *client) WalkDeployments(f func(Deployment) error) error {
return nil
}
for _, m := range c.deploymentStore.List() {
d := m.(*apiv1beta1.Deployment)
d := m.(*apiextensionsv1beta1.Deployment)
if err := f(NewDeployment(d)); err != nil {
return err
}
@@ -228,7 +249,7 @@ func (c *client) WalkReplicaSets(f func(ReplicaSet) error) error {
return nil
}
for _, m := range c.replicaSetStore.List() {
rs := m.(*apiv1beta1.ReplicaSet)
rs := m.(*apiextensionsv1beta1.ReplicaSet)
if err := f(NewReplicaSet(rs)); err != nil {
return err
}
@@ -254,7 +275,7 @@ func (c *client) WalkDaemonSets(f func(DaemonSet) error) error {
return nil
}
for _, m := range c.daemonSetStore.List() {
ds := m.(*apiv1beta1.DaemonSet)
ds := m.(*apiextensionsv1beta1.DaemonSet)
if err := f(NewDaemonSet(ds)); err != nil {
return err
}
@@ -262,6 +283,40 @@ func (c *client) WalkDaemonSets(f func(DaemonSet) error) error {
return nil
}
// WalkStatefulSets calls f for each statefulset
func (c *client) WalkStatefulSets(f func(StatefulSet) error) error {
if c.statefulSetStore == nil {
return nil
}
for _, m := range c.statefulSetStore.List() {
s := m.(*apiappsv1beta1.StatefulSet)
if err := f(NewStatefulSet(s)); err != nil {
return err
}
}
return nil
}
// WalkCronJobs calls f for each cronjob
func (c *client) WalkCronJobs(f func(CronJob) error) error {
if c.cronJobStore == nil {
return nil
}
// We index jobs by id to make lookup for each cronjob more efficient
jobs := map[types.UID]*apibatchv1.Job{}
for _, m := range c.jobStore.List() {
j := m.(*apibatchv1.Job)
jobs[j.UID] = j
}
for _, m := range c.cronJobStore.List() {
cj := m.(*apibatchv2alpha1.CronJob)
if err := f(NewCronJob(cj, jobs)); err != nil {
return err
}
}
return nil
}
func (c *client) WalkNodes(f func(*apiv1.Node) error) error {
for _, m := range c.nodeStore.List() {
node := m.(*apiv1.Node)
@@ -288,18 +343,18 @@ func (c *client) DeletePod(namespaceID, podID string) error {
}
func (c *client) ScaleUp(resource, namespaceID, id string) error {
return c.modifyScale(resource, namespaceID, id, func(scale *apiv1beta1.Scale) {
return c.modifyScale(resource, namespaceID, id, func(scale *apiextensionsv1beta1.Scale) {
scale.Spec.Replicas++
})
}
func (c *client) ScaleDown(resource, namespaceID, id string) error {
return c.modifyScale(resource, namespaceID, id, func(scale *apiv1beta1.Scale) {
return c.modifyScale(resource, namespaceID, id, func(scale *apiextensionsv1beta1.Scale) {
scale.Spec.Replicas--
})
}
func (c *client) modifyScale(resource, namespace, id string, f func(*apiv1beta1.Scale)) error {
func (c *client) modifyScale(resource, namespace, id string, f func(*apiextensionsv1beta1.Scale)) error {
scaler := c.client.Extensions().Scales(namespace)
scale, err := scaler.Get(resource, id)
if err != nil {
+73
View File
@@ -0,0 +1,73 @@
package kubernetes
import (
"fmt"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
batchv1 "k8s.io/client-go/pkg/apis/batch/v1"
batchv2alpha1 "k8s.io/client-go/pkg/apis/batch/v2alpha1"
"github.com/weaveworks/scope/report"
)
// These constants are keys used in node metadata
const (
Schedule = "kubernetes_schedule"
Suspended = "kubernetes_suspended"
LastScheduled = "kubernetes_last_scheduled"
ActiveJobs = "kubernetes_active_jobs"
)
// CronJob represents a Kubernetes cron job
type CronJob interface {
Meta
Selectors() ([]labels.Selector, error)
GetNode() report.Node
}
type cronJob struct {
*batchv2alpha1.CronJob
Meta
jobs []*batchv1.Job
}
// NewCronJob creates a new cron job. jobs should be all jobs, which will be filtered
// for those matching this cron job.
func NewCronJob(cj *batchv2alpha1.CronJob, jobs map[types.UID]*batchv1.Job) CronJob {
myJobs := []*batchv1.Job{}
for _, o := range cj.Status.Active {
if j, ok := jobs[o.UID]; ok {
myJobs = append(myJobs, j)
}
}
return &cronJob{
CronJob: cj,
Meta: meta{cj.ObjectMeta},
jobs: myJobs,
}
}
func (cj *cronJob) Selectors() ([]labels.Selector, error) {
selectors := []labels.Selector{}
for _, j := range cj.jobs {
selector, err := metav1.LabelSelectorAsSelector(j.Spec.Selector)
if err != nil {
return nil, err
}
selectors = append(selectors, selector)
}
return selectors, nil
}
func (cj *cronJob) GetNode() report.Node {
return cj.MetaNode(report.MakeCronJobNodeID(cj.UID())).WithLatests(map[string]string{
NodeType: "Cron Job",
Schedule: cj.Spec.Schedule,
Suspended: fmt.Sprint(cj.Spec.Suspend != nil && *cj.Spec.Suspend), // nil -> false
LastScheduled: cj.Status.LastScheduleTime.Format(time.RFC3339Nano),
ActiveJobs: fmt.Sprint(len(cj.jobs)),
})
}
+6 -1
View File
@@ -46,9 +46,14 @@ func (d *deployment) Selector() (labels.Selector, error) {
}
func (d *deployment) GetNode(probeID string) report.Node {
// Spec.Replicas can be omitted, and the pointer will be nil. It defaults to 1.
desiredReplicas := 1
if d.Spec.Replicas != nil {
desiredReplicas = int(*d.Spec.Replicas)
}
return d.MetaNode(report.MakeDeploymentNodeID(d.UID())).WithLatests(map[string]string{
ObservedGeneration: fmt.Sprint(d.Status.ObservedGeneration),
DesiredReplicas: fmt.Sprint(d.Spec.Replicas),
DesiredReplicas: fmt.Sprint(desiredReplicas),
Replicas: fmt.Sprint(d.Status.Replicas),
UpdatedReplicas: fmt.Sprint(d.Status.UpdatedReplicas),
AvailableReplicas: fmt.Sprint(d.Status.AvailableReplicas),
+90 -2
View File
@@ -78,6 +78,30 @@ var (
DaemonSetMetricTemplates = PodMetricTemplates
StatefulSetMetadataTemplates = report.MetadataTemplates{
NodeType: {ID: NodeType, Label: "Type", From: report.FromLatest, Priority: 1},
Namespace: {ID: Namespace, Label: "Namespace", From: report.FromLatest, Priority: 2},
Created: {ID: Created, Label: "Created", From: report.FromLatest, Datatype: "datetime", Priority: 3},
ObservedGeneration: {ID: ObservedGeneration, Label: "Observed Gen.", From: report.FromLatest, Datatype: "number", Priority: 4},
DesiredReplicas: {ID: DesiredReplicas, Label: "Desired Replicas", From: report.FromLatest, Datatype: "number", Priority: 5},
report.Pod: {ID: report.Pod, Label: "# Pods", From: report.FromCounters, Datatype: "number", Priority: 6},
}
StatefulSetMetricTemplates = PodMetricTemplates
CronJobMetadataTemplates = report.MetadataTemplates{
NodeType: {ID: NodeType, Label: "Type", From: report.FromLatest, Priority: 1},
Namespace: {ID: Namespace, Label: "Namespace", From: report.FromLatest, Priority: 2},
Created: {ID: Created, Label: "Created", From: report.FromLatest, Datatype: "datetime", Priority: 3},
Schedule: {ID: Schedule, Label: "Schedule", From: report.FromLatest, Priority: 4},
LastScheduled: {ID: LastScheduled, Label: "Last Scheduled", From: report.FromLatest, Datatype: "datetime", Priority: 5},
Suspended: {ID: Suspended, Label: "Suspended", From: report.FromLatest, Priority: 6},
ActiveJobs: {ID: ActiveJobs, Label: "# Jobs", From: report.FromLatest, Datatype: "number", Priority: 7},
report.Pod: {ID: report.Pod, Label: "# Pods", From: report.FromCounters, Datatype: "number", Priority: 8},
}
CronJobMetricTemplates = PodMetricTemplates
TableTemplates = report.TableTemplates{
LabelPrefix: {
ID: LabelPrefix,
@@ -222,6 +246,14 @@ func (r *Reporter) Report() (report.Report, error) {
if err != nil {
return result, err
}
statefulSetTopology, statefulSets, err := r.statefulSetTopology()
if err != nil {
return result, err
}
cronJobTopology, cronJobs, err := r.cronJobTopology()
if err != nil {
return result, err
}
deploymentTopology, deployments, err := r.deploymentTopology(r.probeID)
if err != nil {
return result, err
@@ -230,7 +262,7 @@ func (r *Reporter) Report() (report.Report, error) {
if err != nil {
return result, err
}
podTopology, err := r.podTopology(services, replicaSets, daemonSets)
podTopology, err := r.podTopology(services, replicaSets, daemonSets, statefulSets, cronJobs)
if err != nil {
return result, err
}
@@ -238,6 +270,8 @@ func (r *Reporter) Report() (report.Report, error) {
result.Service = result.Service.Merge(serviceTopology)
result.Host = result.Host.Merge(hostTopology)
result.DaemonSet = result.DaemonSet.Merge(daemonSetTopology)
result.StatefulSet = result.StatefulSet.Merge(statefulSetTopology)
result.CronJob = result.CronJob.Merge(cronJobTopology)
result.Deployment = result.Deployment.Merge(deploymentTopology)
result.ReplicaSet = result.ReplicaSet.Merge(replicaSetTopology)
return result, nil
@@ -310,6 +344,34 @@ func (r *Reporter) daemonSetTopology() (report.Topology, []DaemonSet, error) {
return result, daemonSets, err
}
func (r *Reporter) statefulSetTopology() (report.Topology, []StatefulSet, error) {
statefulSets := []StatefulSet{}
result := report.MakeTopology().
WithMetadataTemplates(StatefulSetMetadataTemplates).
WithMetricTemplates(StatefulSetMetricTemplates).
WithTableTemplates(TableTemplates)
err := r.client.WalkStatefulSets(func(s StatefulSet) error {
result = result.AddNode(s.GetNode())
statefulSets = append(statefulSets, s)
return nil
})
return result, statefulSets, err
}
func (r *Reporter) cronJobTopology() (report.Topology, []CronJob, error) {
cronJobs := []CronJob{}
result := report.MakeTopology().
WithMetadataTemplates(CronJobMetadataTemplates).
WithMetricTemplates(CronJobMetricTemplates).
WithTableTemplates(TableTemplates)
err := r.client.WalkCronJobs(func(c CronJob) error {
result = result.AddNode(c.GetNode())
cronJobs = append(cronJobs, c)
return nil
})
return result, cronJobs, err
}
func (r *Reporter) replicaSetTopology(probeID string, deployments []Deployment) (report.Topology, []ReplicaSet, error) {
var (
result = report.MakeTopology().
@@ -373,7 +435,7 @@ func match(namespace string, selector labels.Selector, topology, id string) func
}
}
func (r *Reporter) podTopology(services []Service, replicaSets []ReplicaSet, daemonSets []DaemonSet) (report.Topology, error) {
func (r *Reporter) podTopology(services []Service, replicaSets []ReplicaSet, daemonSets []DaemonSet, statefulSets []StatefulSet, cronJobs []CronJob) (report.Topology, error) {
var (
pods = report.MakeTopology().
WithMetadataTemplates(PodMetadataTemplates).
@@ -425,6 +487,32 @@ func (r *Reporter) podTopology(services []Service, replicaSets []ReplicaSet, dae
report.MakeDaemonSetNodeID(daemonSet.UID()),
))
}
for _, statefulSet := range statefulSets {
selector, err := statefulSet.Selector()
if err != nil {
return pods, err
}
selectors = append(selectors, match(
statefulSet.Namespace(),
selector,
report.StatefulSet,
report.MakeStatefulSetNodeID(statefulSet.UID()),
))
}
for _, cronJob := range cronJobs {
cronJobSelectors, err := cronJob.Selectors()
if err != nil {
return pods, err
}
for _, selector := range cronJobSelectors {
selectors = append(selectors, match(
cronJob.Namespace(),
selector,
report.CronJob,
report.MakeCronJobNodeID(cronJob.UID()),
))
}
}
var localPodUIDs map[string]struct{}
if r.nodeName == "" {
+6
View File
@@ -136,6 +136,12 @@ func (c *mockClient) WalkServices(f func(kubernetes.Service) error) error {
func (c *mockClient) WalkDaemonSets(f func(kubernetes.DaemonSet) error) error {
return nil
}
func (c *mockClient) WalkStatefulSets(f func(kubernetes.StatefulSet) error) error {
return nil
}
func (c *mockClient) WalkCronJobs(f func(kubernetes.CronJob) error) error {
return nil
}
func (c *mockClient) WalkDeployments(f func(kubernetes.Deployment) error) error {
return nil
}
+55
View File
@@ -0,0 +1,55 @@
package kubernetes
import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/pkg/apis/apps/v1beta1"
"github.com/weaveworks/scope/report"
)
// StatefulSet represents a Kubernetes statefulset
type StatefulSet interface {
Meta
Selector() (labels.Selector, error)
GetNode() report.Node
}
type statefulSet struct {
*v1beta1.StatefulSet
Meta
}
// NewStatefulSet creates a new statefulset
func NewStatefulSet(s *v1beta1.StatefulSet) StatefulSet {
return &statefulSet{
StatefulSet: s,
Meta: meta{s.ObjectMeta},
}
}
func (s *statefulSet) Selector() (labels.Selector, error) {
selector, err := metav1.LabelSelectorAsSelector(s.Spec.Selector)
if err != nil {
return nil, err
}
return selector, nil
}
func (s *statefulSet) GetNode() report.Node {
desiredReplicas := 1
if s.Spec.Replicas != nil {
desiredReplicas = int(*s.Spec.Replicas)
}
latests := map[string]string{
NodeType: "Stateful Set",
DesiredReplicas: fmt.Sprint(desiredReplicas),
Replicas: fmt.Sprint(s.Status.Replicas),
}
if s.Status.ObservedGeneration != nil {
latests[ObservedGeneration] = fmt.Sprint(*s.Status.ObservedGeneration)
}
return s.MetaNode(report.MakeStatefulSetNodeID(s.UID())).WithLatests(latests)
}
+2
View File
@@ -25,6 +25,8 @@ var (
report.Pod: kubernetesParentLabel,
report.Deployment: kubernetesParentLabel,
report.DaemonSet: kubernetesParentLabel,
report.StatefulSet: kubernetesParentLabel,
report.CronJob: kubernetesParentLabel,
report.Service: kubernetesParentLabel,
report.ECSTask: latestLookup(awsecs.TaskFamily),
report.ECSService: ecsServiceParentLabel,
+8 -2
View File
@@ -68,6 +68,8 @@ var renderers = map[string]func(NodeSummary, report.Node) (NodeSummary, bool){
report.Service: podGroupNodeSummary,
report.Deployment: podGroupNodeSummary,
report.DaemonSet: podGroupNodeSummary,
report.StatefulSet: podGroupNodeSummary,
report.CronJob: podGroupNodeSummary,
report.ECSTask: ecsTaskNodeSummary,
report.ECSService: ecsServiceNodeSummary,
report.SwarmService: swarmServiceNodeSummary,
@@ -90,6 +92,8 @@ var primaryAPITopology = map[string]string{
report.Pod: "pods",
report.Deployment: "kube-controllers",
report.DaemonSet: "kube-controllers",
report.StatefulSet: "kube-controllers",
report.CronJob: "kube-controllers",
report.Service: "services",
report.ECSTask: "ecs-tasks",
report.ECSService: "ecs-services",
@@ -256,8 +260,10 @@ func podNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
}
var podGroupNodeTypeName = map[string]string{
report.Deployment: "Deployment",
report.DaemonSet: "Daemon Set",
report.Deployment: "Deployment",
report.DaemonSet: "Daemon Set",
report.StatefulSet: "Stateful Set",
report.CronJob: "Cron Job",
}
func podGroupNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
+16 -2
View File
@@ -18,7 +18,21 @@ const (
var UnmanagedIDPrefix = MakePseudoNodeID(UnmanagedID)
func renderKubernetesTopologies(rpt report.Report) bool {
return len(rpt.Pod.Nodes)+len(rpt.Service.Nodes)+len(rpt.Deployment.Nodes)+len(rpt.DaemonSet.Nodes) >= 1
// Render if any k8s topology has any nodes
topologies := []*report.Topology{
&rpt.Pod,
&rpt.Service,
&rpt.Deployment,
&rpt.DaemonSet,
&rpt.StatefulSet,
&rpt.CronJob,
}
for _, t := range topologies {
if len(t.Nodes) > 0 {
return true
}
}
return false
}
func isPauseContainer(n report.Node) bool {
@@ -68,7 +82,7 @@ var PodServiceRenderer = ConditionalRenderer(renderKubernetesTopologies,
// have connections to each other.
var KubeControllerRenderer = ConditionalRenderer(renderKubernetesTopologies,
renderParents(
report.Pod, []string{report.Deployment, report.DaemonSet}, UnmanagedID,
report.Pod, []string{report.Deployment, report.DaemonSet, report.StatefulSet, report.CronJob}, UnmanagedID,
PodRenderer,
),
)
+2
View File
@@ -31,6 +31,8 @@ var (
SelectService = TopologySelector(report.Service)
SelectDeployment = TopologySelector(report.Deployment)
SelectDaemonSet = TopologySelector(report.DaemonSet)
SelectStatefulSet = TopologySelector(report.StatefulSet)
SelectCronJob = TopologySelector(report.CronJob)
SelectECSTask = TopologySelector(report.ECSTask)
SelectECSService = TopologySelector(report.ECSService)
SelectSwarmService = TopologySelector(report.SwarmService)
+12
View File
@@ -131,6 +131,18 @@ var (
// ParseDaemonSetNodeID parses a daemon set node ID
ParseDaemonSetNodeID = parseSingleComponentID("daemonset")
// MakeStatefulSetNodeID produces a replica set node ID from its composite parts.
MakeStatefulSetNodeID = makeSingleComponentID("statefulset")
// ParseStatefulSetNodeID parses a daemon set node ID
ParseStatefulSetNodeID = parseSingleComponentID("statefulset")
// MakeCronJobNodeID produces a replica set node ID from its composite parts.
MakeCronJobNodeID = makeSingleComponentID("cronjob")
// ParseCronJobNodeID parses a daemon set node ID
ParseCronJobNodeID = parseSingleComponentID("cronjob")
// MakeECSTaskNodeID produces a replica set node ID from its composite parts.
MakeECSTaskNodeID = makeSingleComponentID("ecs_task")
+25 -1
View File
@@ -20,6 +20,8 @@ const (
Deployment = "deployment"
ReplicaSet = "replica_set"
DaemonSet = "daemon_set"
StatefulSet = "stateful_set"
CronJob = "cron_job"
ContainerImage = "container_image"
Host = "host"
Overlay = "overlay"
@@ -83,6 +85,16 @@ type Report struct {
// present.
DaemonSet Topology
// StatefulSet nodes represent all Kubernetes Stateful Sets running on hosts running probes.
// Metadata includes things like Stateful Set id, name, etc. Edges are not
// present.
StatefulSet Topology
// CronJob nodes represent all Kubernetes Cron Jobs running on hosts running probes.
// Metadata includes things like Cron Job id, name, etc. Edges are not
// present.
CronJob Topology
// ContainerImages nodes represent all Docker containers images on
// hosts running probes. Metadata includes things like image id, name etc.
// Edges are not present.
@@ -166,7 +178,7 @@ func MakeReport() Report {
WithLabel("service", "services"),
Deployment: MakeTopology().
WithShape(Octagon).
WithShape(Heptagon).
WithLabel("deployment", "deployments"),
ReplicaSet: MakeTopology().
@@ -177,6 +189,14 @@ func MakeReport() Report {
WithShape(Pentagon).
WithLabel("daemonset", "daemonsets"),
StatefulSet: MakeTopology().
WithShape(Octagon).
WithLabel("stateful set", "stateful sets"),
CronJob: MakeTopology().
WithShape(Triangle).
WithLabel("cron job", "cron jobs"),
Overlay: MakeTopology().
WithShape(Circle).
WithLabel("peer", "peers"),
@@ -212,6 +232,8 @@ func (r *Report) TopologyMap() map[string]*Topology {
Deployment: &r.Deployment,
ReplicaSet: &r.ReplicaSet,
DaemonSet: &r.DaemonSet,
StatefulSet: &r.StatefulSet,
CronJob: &r.CronJob,
Host: &r.Host,
Overlay: &r.Overlay,
ECSTask: &r.ECSTask,
@@ -275,6 +297,8 @@ func (r *Report) WalkPairedTopologies(o *Report, f func(*Topology, *Topology)) {
f(&r.Deployment, &o.Deployment)
f(&r.ReplicaSet, &o.ReplicaSet)
f(&r.DaemonSet, &o.DaemonSet)
f(&r.StatefulSet, &o.StatefulSet)
f(&r.CronJob, &o.CronJob)
f(&r.Host, &o.Host)
f(&r.Overlay, &o.Overlay)
f(&r.ECSTask, &o.ECSTask)