github.com/netdata/go.d.plugin@v0.58.1/modules/k8s_state/charts.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package k8s_state
     4  
     5  import (
     6  	"fmt"
     7  	"regexp"
     8  	"strings"
     9  
    10  	"github.com/netdata/go.d.plugin/agent/module"
    11  )
    12  
    13  // NETDATA_CHART_PRIO_CGROUPS_CONTAINERS 40000
    14  const prioDiscoveryDiscovererState = 50999
    15  
    16  const (
    17  	prioNodeAllocatableCPURequestsUtil = 50100 + iota
    18  	prioNodeAllocatableCPURequestsUsed
    19  	prioNodeAllocatableCPULimitsUtil
    20  	prioNodeAllocatableCPULimitsUsed
    21  	prioNodeAllocatableMemRequestsUtil
    22  	prioNodeAllocatableMemRequestsUsed
    23  	prioNodeAllocatableMemLimitsUtil
    24  	prioNodeAllocatableMemLimitsUsed
    25  	prioNodeAllocatablePodsUtil
    26  	prioNodeAllocatablePodsUsage
    27  	prioNodeConditions
    28  	prioNodeSchedulability
    29  	prioNodePodsReadiness
    30  	prioNodePodsReadinessState
    31  	prioNodePodsCondition
    32  	prioNodePodsPhase
    33  	prioNodeContainersCount
    34  	prioNodeContainersState
    35  	prioNodeInitContainersState
    36  	prioNodeAge
    37  )
    38  
    39  const (
    40  	prioPodCPURequestsUsed = 50300 + iota
    41  	prioPodCPULimitsUsed
    42  	prioPodMemRequestsUsed
    43  	prioPodMemLimitsUsed
    44  	prioPodCondition
    45  	prioPodPhase
    46  	prioPodAge
    47  	prioPodContainersCount
    48  	prioPodContainersState
    49  	prioPodInitContainersState
    50  	prioPodContainerReadinessState
    51  	prioPodContainerRestarts
    52  	prioPodContainerState
    53  	prioPodContainerWaitingStateReason
    54  	prioPodContainerTerminatedStateReason
    55  )
    56  
    57  const (
    58  	labelKeyPrefix = "k8s_"
    59  	//labelKeyLabelPrefix      = labelKeyPrefix + "label_"
    60  	//labelKeyAnnotationPrefix = labelKeyPrefix + "annotation_"
    61  	labelKeyClusterID      = labelKeyPrefix + "cluster_id"
    62  	labelKeyClusterName    = labelKeyPrefix + "cluster_name"
    63  	labelKeyNamespace      = labelKeyPrefix + "namespace"
    64  	labelKeyKind           = labelKeyPrefix + "kind"
    65  	labelKeyPodName        = labelKeyPrefix + "pod_name"
    66  	labelKeyNodeName       = labelKeyPrefix + "node_name"
    67  	labelKeyPodUID         = labelKeyPrefix + "pod_uid"
    68  	labelKeyControllerKind = labelKeyPrefix + "controller_kind"
    69  	labelKeyControllerName = labelKeyPrefix + "controller_name"
    70  	labelKeyContainerName  = labelKeyPrefix + "container_name"
    71  	labelKeyContainerID    = labelKeyPrefix + "container_id"
    72  	labelKeyQoSClass       = labelKeyPrefix + "qos_class"
    73  )
    74  
    75  var baseCharts = module.Charts{
    76  	discoveryStatusChart.Copy(),
    77  }
    78  
    79  var nodeChartsTmpl = module.Charts{
    80  	nodeAllocatableCPURequestsUtilChartTmpl.Copy(),
    81  	nodeAllocatableCPURequestsUsedChartTmpl.Copy(),
    82  	nodeAllocatableCPULimitsUtilChartTmpl.Copy(),
    83  	nodeAllocatableCPULimitsUsedChartTmpl.Copy(),
    84  	nodeAllocatableMemRequestsUtilChartTmpl.Copy(),
    85  	nodeAllocatableMemRequestsUsedChartTmpl.Copy(),
    86  	nodeAllocatableMemLimitsUtilChartTmpl.Copy(),
    87  	nodeAllocatableMemLimitsUsedChartTmpl.Copy(),
    88  	nodeAllocatablePodsUtilizationChartTmpl.Copy(),
    89  	nodeAllocatablePodsUsageChartTmpl.Copy(),
    90  	nodeConditionsChartTmpl.Copy(),
    91  	nodeSchedulabilityChartTmpl.Copy(),
    92  	nodePodsReadinessChartTmpl.Copy(),
    93  	nodePodsReadinessStateChartTmpl.Copy(),
    94  	nodePodsConditionChartTmpl.Copy(),
    95  	nodePodsPhaseChartTmpl.Copy(),
    96  	nodeContainersChartTmpl.Copy(),
    97  	nodeContainersStateChartTmpl.Copy(),
    98  	nodeInitContainersStateChartTmpl.Copy(),
    99  	nodeAgeChartTmpl.Copy(),
   100  }
   101  
   102  var podChartsTmpl = module.Charts{
   103  	podCPURequestsUsedChartTmpl.Copy(),
   104  	podCPULimitsUsedChartTmpl.Copy(),
   105  	podMemRequestsUsedChartTmpl.Copy(),
   106  	podMemLimitsUsedChartTmpl.Copy(),
   107  	podConditionChartTmpl.Copy(),
   108  	podPhaseChartTmpl.Copy(),
   109  	podAgeChartTmpl.Copy(),
   110  	podContainersCountChartTmpl.Copy(),
   111  	podContainersStateChartTmpl.Copy(),
   112  	podInitContainersStateChartTmpl.Copy(),
   113  }
   114  
   115  var containerChartsTmpl = module.Charts{
   116  	containerReadinessStateChartTmpl.Copy(),
   117  	containerRestartsChartTmpl.Copy(),
   118  	containersStateChartTmpl.Copy(),
   119  	containersStateWaitingChartTmpl.Copy(),
   120  	containersStateTerminatedChartTmpl.Copy(),
   121  }
   122  
   123  var (
   124  	// CPU resource
   125  	nodeAllocatableCPURequestsUtilChartTmpl = module.Chart{
   126  		IDSep:    true,
   127  		ID:       "node_%s.allocatable_cpu_requests_utilization",
   128  		Title:    "CPU requests utilization",
   129  		Units:    "%",
   130  		Fam:      "node cpu resource",
   131  		Ctx:      "k8s_state.node_allocatable_cpu_requests_utilization",
   132  		Priority: prioNodeAllocatableCPURequestsUtil,
   133  		Dims: module.Dims{
   134  			{ID: "node_%s_alloc_cpu_requests_util", Name: "requests", Div: precision},
   135  		},
   136  	}
   137  	nodeAllocatableCPURequestsUsedChartTmpl = module.Chart{
   138  		IDSep:    true,
   139  		ID:       "node_%s.allocatable_cpu_requests_used",
   140  		Title:    "CPU requests used",
   141  		Units:    "millicpu",
   142  		Fam:      "node cpu resource",
   143  		Ctx:      "k8s_state.node_allocatable_cpu_requests_used",
   144  		Priority: prioNodeAllocatableCPURequestsUsed,
   145  		Dims: module.Dims{
   146  			{ID: "node_%s_alloc_cpu_requests_used", Name: "requests"},
   147  		},
   148  	}
   149  	nodeAllocatableCPULimitsUtilChartTmpl = module.Chart{
   150  		IDSep:    true,
   151  		ID:       "node_%s.allocatable_cpu_limits_utilization",
   152  		Title:    "CPU limits utilization",
   153  		Units:    "%",
   154  		Fam:      "node cpu resource",
   155  		Ctx:      "k8s_state.node_allocatable_cpu_limits_utilization",
   156  		Priority: prioNodeAllocatableCPULimitsUtil,
   157  		Dims: module.Dims{
   158  			{ID: "node_%s_alloc_cpu_limits_util", Name: "limits", Div: precision},
   159  		},
   160  	}
   161  	nodeAllocatableCPULimitsUsedChartTmpl = module.Chart{
   162  		IDSep:    true,
   163  		ID:       "node_%s.allocatable_cpu_limits_used",
   164  		Title:    "CPU limits used",
   165  		Units:    "millicpu",
   166  		Fam:      "node cpu resource",
   167  		Ctx:      "k8s_state.node_allocatable_cpu_limits_used",
   168  		Priority: prioNodeAllocatableCPULimitsUsed,
   169  		Dims: module.Dims{
   170  			{ID: "node_%s_alloc_cpu_limits_used", Name: "limits"},
   171  		},
   172  	}
   173  	// memory resource
   174  	nodeAllocatableMemRequestsUtilChartTmpl = module.Chart{
   175  		IDSep:    true,
   176  		ID:       "node_%s.allocatable_mem_requests_utilization",
   177  		Title:    "Memory requests utilization",
   178  		Units:    "%",
   179  		Fam:      "node mem resource",
   180  		Ctx:      "k8s_state.node_allocatable_mem_requests_utilization",
   181  		Priority: prioNodeAllocatableMemRequestsUtil,
   182  		Dims: module.Dims{
   183  			{ID: "node_%s_alloc_mem_requests_util", Name: "requests", Div: precision},
   184  		},
   185  	}
   186  	nodeAllocatableMemRequestsUsedChartTmpl = module.Chart{
   187  		IDSep:    true,
   188  		ID:       "node_%s.allocatable_mem_requests_used",
   189  		Title:    "Memory requests used",
   190  		Units:    "bytes",
   191  		Fam:      "node mem resource",
   192  		Ctx:      "k8s_state.node_allocatable_mem_requests_used",
   193  		Priority: prioNodeAllocatableMemRequestsUsed,
   194  		Dims: module.Dims{
   195  			{ID: "node_%s_alloc_mem_requests_used", Name: "requests"},
   196  		},
   197  	}
   198  	nodeAllocatableMemLimitsUtilChartTmpl = module.Chart{
   199  		IDSep:    true,
   200  		ID:       "node_%s.allocatable_mem_limits_utilization",
   201  		Title:    "Memory limits utilization",
   202  		Units:    "%",
   203  		Fam:      "node mem resource",
   204  		Ctx:      "k8s_state.node_allocatable_mem_limits_utilization",
   205  		Priority: prioNodeAllocatableMemLimitsUtil,
   206  		Dims: module.Dims{
   207  			{ID: "node_%s_alloc_mem_limits_util", Name: "limits", Div: precision},
   208  		},
   209  	}
   210  	nodeAllocatableMemLimitsUsedChartTmpl = module.Chart{
   211  		IDSep:    true,
   212  		ID:       "node_%s.allocatable_mem_limits_used",
   213  		Title:    "Memory limits used",
   214  		Units:    "bytes",
   215  		Fam:      "node mem resource",
   216  		Ctx:      "k8s_state.node_allocatable_mem_limits_used",
   217  		Priority: prioNodeAllocatableMemLimitsUsed,
   218  		Dims: module.Dims{
   219  			{ID: "node_%s_alloc_mem_limits_used", Name: "limits"},
   220  		},
   221  	}
   222  	// pods resource
   223  	nodeAllocatablePodsUtilizationChartTmpl = module.Chart{
   224  		IDSep:    true,
   225  		ID:       "node_%s.allocatable_pods_utilization",
   226  		Title:    "Pods resource utilization",
   227  		Units:    "%",
   228  		Fam:      "node pods resource",
   229  		Ctx:      "k8s_state.node_allocatable_pods_utilization",
   230  		Priority: prioNodeAllocatablePodsUtil,
   231  		Dims: module.Dims{
   232  			{ID: "node_%s_alloc_pods_util", Name: "allocated", Div: precision},
   233  		},
   234  	}
   235  	nodeAllocatablePodsUsageChartTmpl = module.Chart{
   236  		IDSep:    true,
   237  		ID:       "node_%s.allocated_pods_usage",
   238  		Title:    "Pods resource usage",
   239  		Units:    "pods",
   240  		Fam:      "node pods resource",
   241  		Ctx:      "k8s_state.node_allocatable_pods_usage",
   242  		Type:     module.Stacked,
   243  		Priority: prioNodeAllocatablePodsUsage,
   244  		Dims: module.Dims{
   245  			{ID: "node_%s_alloc_pods_available", Name: "available"},
   246  			{ID: "node_%s_alloc_pods_allocated", Name: "allocated"},
   247  		},
   248  	}
   249  	// condition
   250  	nodeConditionsChartTmpl = module.Chart{
   251  		IDSep:    true,
   252  		ID:       "node_%s.condition_status",
   253  		Title:    "Condition status",
   254  		Units:    "status",
   255  		Fam:      "node condition",
   256  		Ctx:      "k8s_state.node_condition",
   257  		Priority: prioNodeConditions,
   258  	}
   259  	nodeSchedulabilityChartTmpl = module.Chart{
   260  		IDSep:    true,
   261  		ID:       "node_%s.schedulability",
   262  		Title:    "Schedulability",
   263  		Units:    "state",
   264  		Fam:      "node schedulability",
   265  		Ctx:      "k8s_state.node_schedulability",
   266  		Priority: prioNodeSchedulability,
   267  		Dims: module.Dims{
   268  			{ID: "node_%s_schedulability_schedulable", Name: "schedulable"},
   269  			{ID: "node_%s_schedulability_unschedulable", Name: "unschedulable"},
   270  		},
   271  	}
   272  	// pods readiness
   273  	nodePodsReadinessChartTmpl = module.Chart{
   274  		IDSep:    true,
   275  		ID:       "node_%s.pods_readiness",
   276  		Title:    "Pods readiness",
   277  		Units:    "%",
   278  		Fam:      "node pods readiness",
   279  		Ctx:      "k8s_state.node_pods_readiness",
   280  		Priority: prioNodePodsReadiness,
   281  		Dims: module.Dims{
   282  			{ID: "node_%s_pods_readiness", Name: "ready", Div: precision},
   283  		},
   284  	}
   285  	nodePodsReadinessStateChartTmpl = module.Chart{
   286  		IDSep:    true,
   287  		ID:       "node_%s.pods_readiness_state",
   288  		Title:    "Pods readiness state",
   289  		Units:    "pods",
   290  		Fam:      "node pods readiness",
   291  		Ctx:      "k8s_state.node_pods_readiness_state",
   292  		Type:     module.Stacked,
   293  		Priority: prioNodePodsReadinessState,
   294  		Dims: module.Dims{
   295  			{ID: "node_%s_pods_readiness_ready", Name: "ready"},
   296  			{ID: "node_%s_pods_readiness_unready", Name: "unready"},
   297  		},
   298  	}
   299  	// pods condition
   300  	nodePodsConditionChartTmpl = module.Chart{
   301  		IDSep:    true,
   302  		ID:       "node_%s.pods_condition",
   303  		Title:    "Pods condition",
   304  		Units:    "pods",
   305  		Fam:      "node pods condition",
   306  		Ctx:      "k8s_state.node_pods_condition",
   307  		Priority: prioNodePodsCondition,
   308  		Dims: module.Dims{
   309  			{ID: "node_%s_pods_cond_podready", Name: "pod_ready"},
   310  			{ID: "node_%s_pods_cond_podscheduled", Name: "pod_scheduled"},
   311  			{ID: "node_%s_pods_cond_podinitialized", Name: "pod_initialized"},
   312  			{ID: "node_%s_pods_cond_containersready", Name: "containers_ready"},
   313  		},
   314  	}
   315  	// pods phase
   316  	nodePodsPhaseChartTmpl = module.Chart{
   317  		IDSep:    true,
   318  		ID:       "node_%s.pods_phase",
   319  		Title:    "Pods phase",
   320  		Units:    "pods",
   321  		Fam:      "node pods phase",
   322  		Ctx:      "k8s_state.node_pods_phase",
   323  		Type:     module.Stacked,
   324  		Priority: prioNodePodsPhase,
   325  		Dims: module.Dims{
   326  			{ID: "node_%s_pods_phase_running", Name: "running"},
   327  			{ID: "node_%s_pods_phase_failed", Name: "failed"},
   328  			{ID: "node_%s_pods_phase_succeeded", Name: "succeeded"},
   329  			{ID: "node_%s_pods_phase_pending", Name: "pending"},
   330  		},
   331  	}
   332  	// containers
   333  	nodeContainersChartTmpl = module.Chart{
   334  		IDSep:    true,
   335  		ID:       "node_%s.containers",
   336  		Title:    "Containers",
   337  		Units:    "containers",
   338  		Fam:      "node containers",
   339  		Ctx:      "k8s_state.node_containers",
   340  		Priority: prioNodeContainersCount,
   341  		Dims: module.Dims{
   342  			{ID: "node_%s_containers", Name: "containers"},
   343  			{ID: "node_%s_init_containers", Name: "init_containers"},
   344  		},
   345  	}
   346  	nodeContainersStateChartTmpl = module.Chart{
   347  		IDSep:    true,
   348  		ID:       "node_%s.containers_state",
   349  		Title:    "Containers state",
   350  		Units:    "containers",
   351  		Fam:      "node containers",
   352  		Ctx:      "k8s_state.node_containers_state",
   353  		Type:     module.Stacked,
   354  		Priority: prioNodeContainersState,
   355  		Dims: module.Dims{
   356  			{ID: "node_%s_containers_state_running", Name: "running"},
   357  			{ID: "node_%s_containers_state_waiting", Name: "waiting"},
   358  			{ID: "node_%s_containers_state_terminated", Name: "terminated"},
   359  		},
   360  	}
   361  	nodeInitContainersStateChartTmpl = module.Chart{
   362  		IDSep:    true,
   363  		ID:       "node_%s.init_containers_state",
   364  		Title:    "Init containers state",
   365  		Units:    "containers",
   366  		Fam:      "node containers",
   367  		Ctx:      "k8s_state.node_init_containers_state",
   368  		Type:     module.Stacked,
   369  		Priority: prioNodeInitContainersState,
   370  		Dims: module.Dims{
   371  			{ID: "node_%s_init_containers_state_running", Name: "running"},
   372  			{ID: "node_%s_init_containers_state_waiting", Name: "waiting"},
   373  			{ID: "node_%s_init_containers_state_terminated", Name: "terminated"},
   374  		},
   375  	}
   376  	// age
   377  	nodeAgeChartTmpl = module.Chart{
   378  		IDSep:    true,
   379  		ID:       "node_%s.age",
   380  		Title:    "Age",
   381  		Units:    "seconds",
   382  		Fam:      "node age",
   383  		Ctx:      "k8s_state.node_age",
   384  		Priority: prioNodeAge,
   385  		Dims: module.Dims{
   386  			{ID: "node_%s_age", Name: "age"},
   387  		},
   388  	}
   389  )
   390  
   391  func (ks *KubeState) newNodeCharts(ns *nodeState) *module.Charts {
   392  	cs := nodeChartsTmpl.Copy()
   393  	for _, c := range *cs {
   394  		c.ID = fmt.Sprintf(c.ID, replaceDots(ns.id()))
   395  		c.Labels = ks.newNodeChartLabels(ns)
   396  		for _, d := range c.Dims {
   397  			d.ID = fmt.Sprintf(d.ID, ns.id())
   398  		}
   399  	}
   400  	return cs
   401  }
   402  
   403  func (ks *KubeState) newNodeChartLabels(ns *nodeState) []module.Label {
   404  	labels := []module.Label{
   405  		{Key: labelKeyNodeName, Value: ns.name, Source: module.LabelSourceK8s},
   406  		{Key: labelKeyClusterID, Value: ks.kubeClusterID, Source: module.LabelSourceK8s},
   407  		{Key: labelKeyClusterName, Value: ks.kubeClusterName, Source: module.LabelSourceK8s},
   408  	}
   409  	return labels
   410  }
   411  
   412  func (ks *KubeState) addNodeCharts(ns *nodeState) {
   413  	cs := ks.newNodeCharts(ns)
   414  	if err := ks.Charts().Add(*cs...); err != nil {
   415  		ks.Warning(err)
   416  	}
   417  }
   418  
   419  func (ks *KubeState) removeNodeCharts(ns *nodeState) {
   420  	prefix := fmt.Sprintf("node_%s", replaceDots(ns.id()))
   421  	for _, c := range *ks.Charts() {
   422  		if strings.HasPrefix(c.ID, prefix) {
   423  			c.MarkRemove()
   424  			c.MarkNotCreated()
   425  		}
   426  	}
   427  }
   428  
   429  func (ks *KubeState) addNodeConditionToCharts(ns *nodeState, cond string) {
   430  	id := fmt.Sprintf(nodeConditionsChartTmpl.ID, replaceDots(ns.id()))
   431  	c := ks.Charts().Get(id)
   432  	if c == nil {
   433  		ks.Warningf("chart '%s' does not exist", id)
   434  		return
   435  	}
   436  	dim := &module.Dim{
   437  		ID:   fmt.Sprintf("node_%s_cond_%s", ns.id(), strings.ToLower(cond)),
   438  		Name: cond,
   439  	}
   440  	if err := c.AddDim(dim); err != nil {
   441  		ks.Warning(err)
   442  		return
   443  	}
   444  	c.MarkNotCreated()
   445  }
   446  
   447  var (
   448  	podCPURequestsUsedChartTmpl = module.Chart{
   449  		IDSep:    true,
   450  		ID:       "pod_%s.cpu_requests_used",
   451  		Title:    "CPU requests used",
   452  		Units:    "millicpu",
   453  		Fam:      "pod allocated cpu",
   454  		Ctx:      "k8s_state.pod_cpu_requests_used",
   455  		Priority: prioPodCPURequestsUsed,
   456  		Dims: module.Dims{
   457  			{ID: "pod_%s_cpu_requests_used", Name: "requests"},
   458  		},
   459  	}
   460  	podCPULimitsUsedChartTmpl = module.Chart{
   461  		IDSep:    true,
   462  		ID:       "pod_%s.cpu_limits_used",
   463  		Title:    "CPU limits used",
   464  		Units:    "millicpu",
   465  		Fam:      "pod allocated cpu",
   466  		Ctx:      "k8s_state.pod_cpu_limits_used",
   467  		Priority: prioPodCPULimitsUsed,
   468  		Dims: module.Dims{
   469  			{ID: "pod_%s_cpu_limits_used", Name: "limits"},
   470  		},
   471  	}
   472  	podMemRequestsUsedChartTmpl = module.Chart{
   473  		IDSep:    true,
   474  		ID:       "pod_%s.mem_requests_used",
   475  		Title:    "Memory requests used",
   476  		Units:    "bytes",
   477  		Fam:      "pod allocated mem",
   478  		Ctx:      "k8s_state.pod_mem_requests_used",
   479  		Priority: prioPodMemRequestsUsed,
   480  		Dims: module.Dims{
   481  			{ID: "pod_%s_mem_requests_used", Name: "requests"},
   482  		},
   483  	}
   484  	podMemLimitsUsedChartTmpl = module.Chart{
   485  		IDSep:    true,
   486  		ID:       "pod_%s.mem_limits_used",
   487  		Title:    "Memory limits used",
   488  		Units:    "bytes",
   489  		Fam:      "pod allocated mem",
   490  		Ctx:      "k8s_state.pod_mem_limits_used",
   491  		Priority: prioPodMemLimitsUsed,
   492  		Dims: module.Dims{
   493  			{ID: "pod_%s_mem_limits_used", Name: "limits"},
   494  		},
   495  	}
   496  	podConditionChartTmpl = module.Chart{
   497  		IDSep:    true,
   498  		ID:       "pod_%s.condition",
   499  		Title:    "Condition",
   500  		Units:    "state",
   501  		Fam:      "pod condition",
   502  		Ctx:      "k8s_state.pod_condition",
   503  		Priority: prioPodCondition,
   504  		Dims: module.Dims{
   505  			{ID: "pod_%s_cond_podready", Name: "pod_ready"},
   506  			{ID: "pod_%s_cond_podscheduled", Name: "pod_scheduled"},
   507  			{ID: "pod_%s_cond_podinitialized", Name: "pod_initialized"},
   508  			{ID: "pod_%s_cond_containersready", Name: "containers_ready"},
   509  		},
   510  	}
   511  	podPhaseChartTmpl = module.Chart{
   512  		IDSep:    true,
   513  		ID:       "pod_%s.phase",
   514  		Title:    "Phase",
   515  		Units:    "state",
   516  		Fam:      "pod phase",
   517  		Ctx:      "k8s_state.pod_phase",
   518  		Priority: prioPodPhase,
   519  		Dims: module.Dims{
   520  			{ID: "pod_%s_phase_running", Name: "running"},
   521  			{ID: "pod_%s_phase_failed", Name: "failed"},
   522  			{ID: "pod_%s_phase_succeeded", Name: "succeeded"},
   523  			{ID: "pod_%s_phase_pending", Name: "pending"},
   524  		},
   525  	}
   526  	podAgeChartTmpl = module.Chart{
   527  		IDSep:    true,
   528  		ID:       "pod_%s.age",
   529  		Title:    "Age",
   530  		Units:    "seconds",
   531  		Fam:      "pod age",
   532  		Ctx:      "k8s_state.pod_age",
   533  		Priority: prioPodAge,
   534  		Dims: module.Dims{
   535  			{ID: "pod_%s_age", Name: "age"},
   536  		},
   537  	}
   538  	podContainersCountChartTmpl = module.Chart{
   539  		IDSep:    true,
   540  		ID:       "pod_%s.containers_count",
   541  		Title:    "Containers",
   542  		Units:    "containers",
   543  		Fam:      "pod containers",
   544  		Ctx:      "k8s_state.pod_containers",
   545  		Priority: prioPodContainersCount,
   546  		Dims: module.Dims{
   547  			{ID: "pod_%s_containers", Name: "containers"},
   548  			{ID: "pod_%s_init_containers", Name: "init_containers"},
   549  		},
   550  	}
   551  	podContainersStateChartTmpl = module.Chart{
   552  		IDSep:    true,
   553  		ID:       "pod_%s.containers_state",
   554  		Title:    "Containers state",
   555  		Units:    "containers",
   556  		Fam:      "pod containers",
   557  		Ctx:      "k8s_state.pod_containers_state",
   558  		Type:     module.Stacked,
   559  		Priority: prioPodContainersState,
   560  		Dims: module.Dims{
   561  			{ID: "pod_%s_containers_state_running", Name: "running"},
   562  			{ID: "pod_%s_containers_state_waiting", Name: "waiting"},
   563  			{ID: "pod_%s_containers_state_terminated", Name: "terminated"},
   564  		},
   565  	}
   566  	podInitContainersStateChartTmpl = module.Chart{
   567  		IDSep:    true,
   568  		ID:       "pod_%s.init_containers_state",
   569  		Title:    "Init containers state",
   570  		Units:    "containers",
   571  		Fam:      "pod containers",
   572  		Ctx:      "k8s_state.pod_init_containers_state",
   573  		Type:     module.Stacked,
   574  		Priority: prioPodInitContainersState,
   575  		Dims: module.Dims{
   576  			{ID: "pod_%s_init_containers_state_running", Name: "running"},
   577  			{ID: "pod_%s_init_containers_state_waiting", Name: "waiting"},
   578  			{ID: "pod_%s_init_containers_state_terminated", Name: "terminated"},
   579  		},
   580  	}
   581  )
   582  
   583  func (ks *KubeState) newPodCharts(ps *podState) *module.Charts {
   584  	charts := podChartsTmpl.Copy()
   585  	for _, c := range *charts {
   586  		c.ID = fmt.Sprintf(c.ID, replaceDots(ps.id()))
   587  		c.Labels = ks.newPodChartLabels(ps)
   588  		for _, d := range c.Dims {
   589  			d.ID = fmt.Sprintf(d.ID, ps.id())
   590  		}
   591  	}
   592  	return charts
   593  }
   594  
   595  func (ks *KubeState) newPodChartLabels(ps *podState) []module.Label {
   596  	labels := []module.Label{
   597  		{Key: labelKeyNamespace, Value: ps.namespace, Source: module.LabelSourceK8s},
   598  		{Key: labelKeyPodName, Value: ps.name, Source: module.LabelSourceK8s},
   599  		{Key: labelKeyNodeName, Value: ps.nodeName, Source: module.LabelSourceK8s},
   600  		{Key: labelKeyQoSClass, Value: ps.qosClass, Source: module.LabelSourceK8s},
   601  		{Key: labelKeyControllerKind, Value: ps.controllerKind, Source: module.LabelSourceK8s},
   602  		{Key: labelKeyControllerName, Value: ps.controllerName, Source: module.LabelSourceK8s},
   603  		{Key: labelKeyClusterID, Value: ks.kubeClusterID, Source: module.LabelSourceK8s},
   604  		{Key: labelKeyClusterName, Value: ks.kubeClusterName, Source: module.LabelSourceK8s},
   605  	}
   606  	return labels
   607  }
   608  
   609  func (ks *KubeState) addPodCharts(ps *podState) {
   610  	charts := ks.newPodCharts(ps)
   611  	if err := ks.Charts().Add(*charts...); err != nil {
   612  		ks.Warning(err)
   613  	}
   614  }
   615  
   616  func (ks *KubeState) updatePodChartsNodeLabel(ps *podState) {
   617  	prefix := fmt.Sprintf("pod_%s", replaceDots(ps.id()))
   618  	for _, c := range *ks.Charts() {
   619  		if strings.HasPrefix(c.ID, prefix) {
   620  			updateNodeLabel(c, ps.nodeName)
   621  			c.MarkNotCreated()
   622  		}
   623  	}
   624  }
   625  
   626  func updateNodeLabel(c *module.Chart, nodeName string) {
   627  	for i, l := range c.Labels {
   628  		if l.Key == labelKeyNodeName {
   629  			c.Labels[i].Value = nodeName
   630  			break
   631  		}
   632  	}
   633  }
   634  
   635  func (ks *KubeState) removePodCharts(ps *podState) {
   636  	prefix := fmt.Sprintf("pod_%s", replaceDots(ps.id()))
   637  	for _, c := range *ks.Charts() {
   638  		if strings.HasPrefix(c.ID, prefix) {
   639  			c.MarkRemove()
   640  			c.MarkNotCreated()
   641  		}
   642  	}
   643  }
   644  
   645  var (
   646  	containerReadinessStateChartTmpl = module.Chart{
   647  		IDSep:    true,
   648  		ID:       "pod_%s_container_%s.readiness_state",
   649  		Title:    "Readiness state",
   650  		Units:    "state",
   651  		Fam:      "container readiness",
   652  		Ctx:      "k8s_state.pod_container_readiness_state",
   653  		Priority: prioPodContainerReadinessState,
   654  		Dims: module.Dims{
   655  			{ID: "pod_%s_container_%s_readiness", Name: "ready"},
   656  		},
   657  	}
   658  	containerRestartsChartTmpl = module.Chart{
   659  		IDSep:    true,
   660  		ID:       "pod_%s_container_%s.restarts",
   661  		Title:    "Restarts",
   662  		Units:    "restarts",
   663  		Fam:      "container restarts",
   664  		Ctx:      "k8s_state.pod_container_restarts",
   665  		Priority: prioPodContainerRestarts,
   666  		Dims: module.Dims{
   667  			{ID: "pod_%s_container_%s_restarts", Name: "restarts"},
   668  		},
   669  	}
   670  	containersStateChartTmpl = module.Chart{
   671  		IDSep:    true,
   672  		ID:       "pod_%s_container_%s.state",
   673  		Title:    "Container state",
   674  		Units:    "state",
   675  		Fam:      "container state",
   676  		Ctx:      "k8s_state.pod_container_state",
   677  		Priority: prioPodContainerState,
   678  		Dims: module.Dims{
   679  			{ID: "pod_%s_container_%s_state_running", Name: "running"},
   680  			{ID: "pod_%s_container_%s_state_waiting", Name: "waiting"},
   681  			{ID: "pod_%s_container_%s_state_terminated", Name: "terminated"},
   682  		},
   683  	}
   684  	containersStateWaitingChartTmpl = module.Chart{
   685  		IDSep:    true,
   686  		ID:       "pod_%s_container_%s.state_waiting_reason",
   687  		Title:    "Container waiting state reason",
   688  		Units:    "state",
   689  		Fam:      "container waiting reason",
   690  		Ctx:      "k8s_state.pod_container_waiting_state_reason",
   691  		Priority: prioPodContainerWaitingStateReason,
   692  	}
   693  	containersStateTerminatedChartTmpl = module.Chart{
   694  		IDSep:    true,
   695  		ID:       "pod_%s_container_%s.state_terminated_reason",
   696  		Title:    "Container terminated state reason",
   697  		Units:    "state",
   698  		Fam:      "container terminated reason",
   699  		Ctx:      "k8s_state.pod_container_terminated_state_reason",
   700  		Priority: prioPodContainerTerminatedStateReason,
   701  	}
   702  )
   703  
   704  func (ks *KubeState) newContainerCharts(ps *podState, cs *containerState) *module.Charts {
   705  	charts := containerChartsTmpl.Copy()
   706  	for _, c := range *charts {
   707  		c.ID = fmt.Sprintf(c.ID, replaceDots(ps.id()), cs.name)
   708  		c.Labels = ks.newContainerChartLabels(ps, cs)
   709  		for _, d := range c.Dims {
   710  			d.ID = fmt.Sprintf(d.ID, ps.id(), cs.name)
   711  		}
   712  	}
   713  	return charts
   714  }
   715  
   716  func (ks *KubeState) newContainerChartLabels(ps *podState, cs *containerState) []module.Label {
   717  	labels := ks.newPodChartLabels(ps)
   718  	labels = append(
   719  		labels, module.Label{Key: labelKeyContainerName, Value: cs.name, Source: module.LabelSourceK8s},
   720  	)
   721  	return labels
   722  }
   723  
   724  func (ks *KubeState) addContainerCharts(ps *podState, cs *containerState) {
   725  	charts := ks.newContainerCharts(ps, cs)
   726  	if err := ks.Charts().Add(*charts...); err != nil {
   727  		ks.Warning(err)
   728  	}
   729  }
   730  
   731  func (ks *KubeState) addContainerWaitingStateReasonToChart(ps *podState, cs *containerState, reason string) {
   732  	id := fmt.Sprintf(containersStateWaitingChartTmpl.ID, replaceDots(ps.id()), cs.name)
   733  	c := ks.Charts().Get(id)
   734  	if c == nil {
   735  		ks.Warningf("chart '%s' does not exist", id)
   736  		return
   737  	}
   738  	dim := &module.Dim{
   739  		ID:   fmt.Sprintf("pod_%s_container_%s_state_waiting_reason_%s", ps.id(), cs.name, reason),
   740  		Name: reason,
   741  	}
   742  	if err := c.AddDim(dim); err != nil {
   743  		ks.Warning(err)
   744  		return
   745  	}
   746  	c.MarkNotCreated()
   747  }
   748  
   749  func (ks *KubeState) addContainerTerminatedStateReasonToChart(ps *podState, cs *containerState, reason string) {
   750  	id := fmt.Sprintf(containersStateTerminatedChartTmpl.ID, replaceDots(ps.id()), cs.name)
   751  	c := ks.Charts().Get(id)
   752  	if c == nil {
   753  		ks.Warningf("chart '%s' does not exist", id)
   754  		return
   755  	}
   756  	dim := &module.Dim{
   757  		ID:   fmt.Sprintf("pod_%s_container_%s_state_terminated_reason_%s", ps.id(), cs.name, reason),
   758  		Name: reason,
   759  	}
   760  	if err := c.AddDim(dim); err != nil {
   761  		ks.Warning(err)
   762  		return
   763  	}
   764  	c.MarkNotCreated()
   765  }
   766  
   767  var discoveryStatusChart = module.Chart{
   768  	ID:       "discovery_discoverers_state",
   769  	Title:    "Running discoverers state",
   770  	Units:    "state",
   771  	Fam:      "discovery",
   772  	Ctx:      "k8s_state.discovery_discoverers_state",
   773  	Priority: prioDiscoveryDiscovererState,
   774  	Opts:     module.Opts{Hidden: true},
   775  	Dims: module.Dims{
   776  		{ID: "discovery_node_discoverer_state", Name: "node"},
   777  		{ID: "discovery_pod_discoverer_state", Name: "pod"},
   778  	},
   779  }
   780  
   781  var reDots = regexp.MustCompile(`\.`)
   782  
   783  func replaceDots(v string) string {
   784  	return reDots.ReplaceAllString(v, "-")
   785  }