Просмотр исходного кода

Consolidate each metric emission based on kubernetes type into single collector, separate collectors and metrics into separate source file by type. Added new node metrics, job metrics, and pvc metrics.

Matt Bolt 4 лет назад
Родитель
Сommit
5ba1f17cee

+ 19 - 1
pkg/clustercache/clustercache.go

@@ -7,6 +7,7 @@ import (
 	"k8s.io/klog"
 
 	appsv1 "k8s.io/api/apps/v1"
+	batchv1 "k8s.io/api/batch/v1"
 	v1 "k8s.io/api/core/v1"
 	stv1 "k8s.io/api/storage/v1"
 	"k8s.io/apimachinery/pkg/fields"
@@ -59,6 +60,9 @@ type ClusterCache interface {
 	// GetAllStorageClasses returns all the cached storage classes
 	GetAllStorageClasses() []*stv1.StorageClass
 
+	// GetAllJobs returns all the cached jobs
+	GetAllJobs() []*batchv1.Job
+
 	// SetConfigMapUpdateFunc sets the configmap update function
 	SetConfigMapUpdateFunc(func(interface{}))
 }
@@ -79,6 +83,7 @@ type KubernetesClusterCache struct {
 	pvWatch                WatchController
 	pvcWatch               WatchController
 	storageClassWatch      WatchController
+	jobsWatch              WatchController
 	stop                   chan struct{}
 }
 
@@ -91,6 +96,7 @@ func NewKubernetesClusterCache(client kubernetes.Interface) ClusterCache {
 	coreRestClient := client.CoreV1().RESTClient()
 	appsRestClient := client.AppsV1().RESTClient()
 	storageRestClient := client.StorageV1().RESTClient()
+	batchClient := client.BatchV1().RESTClient()
 
 	kubecostNamespace := env.GetKubecostNamespace()
 	klog.Infof("NAMESPACE: %s", kubecostNamespace)
@@ -109,11 +115,12 @@ func NewKubernetesClusterCache(client kubernetes.Interface) ClusterCache {
 		pvWatch:                NewCachingWatcher(coreRestClient, "persistentvolumes", &v1.PersistentVolume{}, "", fields.Everything()),
 		pvcWatch:               NewCachingWatcher(coreRestClient, "persistentvolumeclaims", &v1.PersistentVolumeClaim{}, "", fields.Everything()),
 		storageClassWatch:      NewCachingWatcher(storageRestClient, "storageclasses", &stv1.StorageClass{}, "", fields.Everything()),
+		jobsWatch:              NewCachingWatcher(batchClient, "jobs", &batchv1.Job{}, "", fields.Everything()),
 	}
 
 	// Wait for each caching watcher to initialize
 	var wg sync.WaitGroup
-	wg.Add(12)
+	wg.Add(13)
 
 	cancel := make(chan struct{})
 
@@ -129,6 +136,7 @@ func NewKubernetesClusterCache(client kubernetes.Interface) ClusterCache {
 	go initializeCache(kcc.pvWatch, &wg, cancel)
 	go initializeCache(kcc.pvcWatch, &wg, cancel)
 	go initializeCache(kcc.storageClassWatch, &wg, cancel)
+	go initializeCache(kcc.jobsWatch, &wg, cancel)
 
 	wg.Wait()
 
@@ -153,6 +161,7 @@ func (kcc *KubernetesClusterCache) Run() {
 	go kcc.pvWatch.Run(1, stopCh)
 	go kcc.pvcWatch.Run(1, stopCh)
 	go kcc.storageClassWatch.Run(1, stopCh)
+	go kcc.jobsWatch.Run(1, stopCh)
 
 	kcc.stop = stopCh
 }
@@ -269,6 +278,15 @@ func (kcc *KubernetesClusterCache) GetAllStorageClasses() []*stv1.StorageClass {
 	return storageClasses
 }
 
+func (kcc *KubernetesClusterCache) GetAllJobs() []*batchv1.Job {
+	var jobs []*batchv1.Job
+	items := kcc.jobsWatch.GetAll()
+	for _, job := range items {
+		jobs = append(jobs, job.(*batchv1.Job))
+	}
+	return jobs
+}
+
 func (kcc *KubernetesClusterCache) SetConfigMapUpdateFunc(f func(interface{})) {
 	kcc.kubecostConfigMapWatch.SetUpdateHandler(f)
 }

+ 125 - 0
pkg/metrics/deploymentmetrics.go

@@ -0,0 +1,125 @@
+package metrics
+
+import (
+	"github.com/kubecost/cost-model/pkg/clustercache"
+	"github.com/kubecost/cost-model/pkg/prom"
+
+	"github.com/prometheus/client_golang/prometheus"
+	dto "github.com/prometheus/client_model/go"
+)
+
+//--------------------------------------------------------------------------
+//  KubecostDeploymentCollector
+//--------------------------------------------------------------------------
+
+// KubecostDeploymentCollector is a prometheus collector that generates kubecost
+// specific deployment metrics.
+type KubecostDeploymentCollector struct {
+	KubeClusterCache clustercache.ClusterCache
+}
+
+// Describe sends the super-set of all possible descriptors of metrics
+// collected by this Collector.
+func (kdc KubecostDeploymentCollector) Describe(ch chan<- *prometheus.Desc) {
+	ch <- prometheus.NewDesc("deployment_match_labels", "deployment match labels", []string{}, nil)
+}
+
+// Collect is called by the Prometheus registry when collecting metrics.
+func (kdc KubecostDeploymentCollector) Collect(ch chan<- prometheus.Metric) {
+	ds := kdc.KubeClusterCache.GetAllDeployments()
+
+	for _, deployment := range ds {
+		deploymentName := deployment.GetName()
+		deploymentNS := deployment.GetNamespace()
+
+		labels, values := prom.KubeLabelsToLabels(deployment.Spec.Selector.MatchLabels)
+		if len(labels) > 0 {
+			m := newDeploymentMatchLabelsMetric(deploymentName, deploymentNS, "deployment_match_labels", labels, values)
+			ch <- m
+		}
+	}
+}
+
+//--------------------------------------------------------------------------
+//  DeploymentMatchLabelsMetric
+//--------------------------------------------------------------------------
+
+// DeploymentMatchLabelsMetric is a prometheus.Metric used to encode deployment match labels
+type DeploymentMatchLabelsMetric struct {
+	fqName         string
+	help           string
+	labelNames     []string
+	labelValues    []string
+	deploymentName string
+	namespace      string
+}
+
+// Creates a new DeploymentMatchLabelsMetric, implementation of prometheus.Metric
+func newDeploymentMatchLabelsMetric(name, namespace, fqname string, labelNames, labelvalues []string) DeploymentMatchLabelsMetric {
+	return DeploymentMatchLabelsMetric{
+		fqName:         fqname,
+		labelNames:     labelNames,
+		labelValues:    labelvalues,
+		help:           "deployment_match_labels Deployment Match Labels",
+		deploymentName: name,
+		namespace:      namespace,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (dmlm DeploymentMatchLabelsMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{
+		"deployment": dmlm.deploymentName,
+		"namespace":  dmlm.namespace,
+	}
+	return prometheus.NewDesc(dmlm.fqName, dmlm.help, dmlm.labelNames, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data
+// transmission object.
+func (dmlm DeploymentMatchLabelsMetric) Write(m *dto.Metric) error {
+	h := float64(1)
+	m.Gauge = &dto.Gauge{
+		Value: &h,
+	}
+	var labels []*dto.LabelPair
+	for i := range dmlm.labelNames {
+		labels = append(labels, &dto.LabelPair{
+			Name:  &dmlm.labelNames[i],
+			Value: &dmlm.labelValues[i],
+		})
+	}
+	labels = append(labels, &dto.LabelPair{
+		Name:  toStringPtr("namespace"),
+		Value: &dmlm.namespace,
+	})
+	labels = append(labels, &dto.LabelPair{
+		Name:  toStringPtr("deployment"),
+		Value: &dmlm.deploymentName,
+	})
+	m.Label = labels
+	return nil
+}
+
+//--------------------------------------------------------------------------
+//  KubeDeploymentCollector
+//--------------------------------------------------------------------------
+
+// KubeDeploymentCollector is a prometheus collector that generates
+type KubeDeploymentCollector struct {
+	KubeClusterCache clustercache.ClusterCache
+}
+
+// Describe sends the super-set of all possible descriptors of metrics
+// collected by this Collector.
+func (kdc KubeDeploymentCollector) Describe(ch chan<- *prometheus.Desc) {
+	// kube_deployment_status_replicas_available
+	// kube_deployment_spec_replicas
+	// kube_deployment_status_replicas_available
+}
+
+// Collect is called by the Prometheus registry when collecting metrics.
+func (kdc KubeDeploymentCollector) Collect(ch chan<- prometheus.Metric) {
+
+}

+ 116 - 0
pkg/metrics/jobmetrics.go

@@ -0,0 +1,116 @@
+package metrics
+
+import (
+	"github.com/kubecost/cost-model/pkg/clustercache"
+	"github.com/prometheus/client_golang/prometheus"
+	dto "github.com/prometheus/client_model/go"
+	batchv1 "k8s.io/api/batch/v1"
+)
+
+var (
+	jobFailureReasons = []string{"BackoffLimitExceeded", "DeadLineExceeded", "Evicted"}
+)
+
+//--------------------------------------------------------------------------
+//  KubeJobCollector
+//--------------------------------------------------------------------------
+
+// KubeJobCollector is a prometheus collector that generates job sourced metrics.
+type KubeJobCollector struct {
+	KubeClusterCache clustercache.ClusterCache
+}
+
+// Describe sends the super-set of all possible descriptors of metrics
+// collected by this Collector.
+func (kjc KubeJobCollector) Describe(ch chan<- *prometheus.Desc) {
+	ch <- prometheus.NewDesc("kube_job_status_failed", "The number of pods which reached Phase Failed and the reason for failure.", []string{}, nil)
+}
+
+// Collect is called by the Prometheus registry when collecting metrics.
+func (kjc KubeJobCollector) Collect(ch chan<- prometheus.Metric) {
+	jobs := kjc.KubeClusterCache.GetAllJobs()
+	for _, job := range jobs {
+		jobName := job.GetName()
+		jobNS := job.GetNamespace()
+
+		if job.Status.Failed == 0 {
+			ch <- newKubeJobStatusFailedMetric(jobName, jobNS, "kube_job_status_failed", "", 0)
+		} else {
+			for _, condition := range job.Status.Conditions {
+				if condition.Type == batchv1.JobFailed {
+					reasonKnown := false
+					for _, reason := range jobFailureReasons {
+						reasonKnown = reasonKnown || failureReason(&condition, reason)
+
+						ch <- newKubeJobStatusFailedMetric(jobName, jobNS, "kube_job_status_failed", reason, boolFloat64(failureReason(&condition, reason)))
+					}
+
+					// for unknown reasons
+					if !reasonKnown {
+						ch <- newKubeJobStatusFailedMetric(jobName, jobNS, "kube_job_status_failed", "", float64(job.Status.Failed))
+					}
+				}
+			}
+		}
+	}
+}
+
+//--------------------------------------------------------------------------
+//  KubeJobStatusFailedMetric
+//--------------------------------------------------------------------------
+
+// KubeJobStatusFailedMetric
+type KubeJobStatusFailedMetric struct {
+	fqName    string
+	help      string
+	job       string
+	namespace string
+	reason    string
+	value     float64
+}
+
+// Creates a new KubeJobStatusFailedMetric, implementation of prometheus.Metric
+func newKubeJobStatusFailedMetric(job, namespace, fqName, reason string, value float64) KubeJobStatusFailedMetric {
+	return KubeJobStatusFailedMetric{
+		fqName:    fqName,
+		help:      "kube_job_status_failed Failed job",
+		job:       job,
+		namespace: namespace,
+		reason:    reason,
+		value:     value,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (kjsf KubeJobStatusFailedMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{
+		"job_name":  kjsf.job,
+		"namespace": kjsf.namespace,
+		"reason":    kjsf.reason,
+	}
+	return prometheus.NewDesc(kjsf.fqName, kjsf.help, []string{}, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data
+// transmission object.
+func (kjsf KubeJobStatusFailedMetric) Write(m *dto.Metric) error {
+	m.Gauge = &dto.Gauge{
+		Value: &kjsf.value,
+	}
+	m.Label = []*dto.LabelPair{
+		{
+			Name:  toStringPtr("job_name"),
+			Value: &kjsf.job,
+		},
+		{
+			Name:  toStringPtr("namespace"),
+			Value: &kjsf.namespace,
+		},
+		{
+			Name:  toStringPtr("reason"),
+			Value: &kjsf.reason,
+		},
+	}
+	return nil
+}

Разница между файлами не показана из-за своего большого размера
+ 57 - 1667
pkg/metrics/kubemetrics.go


+ 92 - 0
pkg/metrics/namespacemetrics.go

@@ -0,0 +1,92 @@
+package metrics
+
+import (
+	"github.com/kubecost/cost-model/pkg/clustercache"
+	"github.com/kubecost/cost-model/pkg/prom"
+
+	"github.com/prometheus/client_golang/prometheus"
+	dto "github.com/prometheus/client_model/go"
+)
+
+//--------------------------------------------------------------------------
+//  KubeNamespaceCollector
+//--------------------------------------------------------------------------
+
+// KubeNamespaceCollector is a prometheus collector that generates namespace sourced metrics
+type KubeNamespaceCollector struct {
+	KubeClusterCache clustercache.ClusterCache
+}
+
+// Describe sends the super-set of all possible descriptors of metrics
+// collected by this Collector.
+func (nsac KubeNamespaceCollector) Describe(ch chan<- *prometheus.Desc) {
+	ch <- prometheus.NewDesc("kube_namespace_annotations", "namespace annotations", []string{}, nil)
+}
+
+// Collect is called by the Prometheus registry when collecting metrics.
+func (nsac KubeNamespaceCollector) Collect(ch chan<- prometheus.Metric) {
+	namespaces := nsac.KubeClusterCache.GetAllNamespaces()
+	for _, namespace := range namespaces {
+		nsName := namespace.GetName()
+
+		labels, values := prom.KubeAnnotationsToLabels(namespace.Annotations)
+		if len(labels) > 0 {
+			m := newNamespaceAnnotationsMetric(nsName, "kube_namespace_annotations", labels, values)
+			ch <- m
+		}
+	}
+}
+
+//--------------------------------------------------------------------------
+//  NamespaceAnnotationsMetric
+//--------------------------------------------------------------------------
+
+// NamespaceAnnotationsMetric is a prometheus.Metric used to encode namespace annotations
+type NamespaceAnnotationsMetric struct {
+	fqName      string
+	help        string
+	labelNames  []string
+	labelValues []string
+	namespace   string
+}
+
+// Creates a new NamespaceAnnotationsMetric, implementation of prometheus.Metric
+func newNamespaceAnnotationsMetric(namespace, fqname string, labelNames []string, labelValues []string) NamespaceAnnotationsMetric {
+	return NamespaceAnnotationsMetric{
+		namespace:   namespace,
+		fqName:      fqname,
+		labelNames:  labelNames,
+		labelValues: labelValues,
+		help:        "kube_namespace_annotations Namespace Annotations",
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (nam NamespaceAnnotationsMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{"namespace": nam.namespace}
+	return prometheus.NewDesc(nam.fqName, nam.help, nam.labelNames, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data
+// transmission object.
+func (nam NamespaceAnnotationsMetric) Write(m *dto.Metric) error {
+	h := float64(1)
+	m.Gauge = &dto.Gauge{
+		Value: &h,
+	}
+
+	var labels []*dto.LabelPair
+	for i := range nam.labelNames {
+		labels = append(labels, &dto.LabelPair{
+			Name:  &nam.labelNames[i],
+			Value: &nam.labelValues[i],
+		})
+	}
+	labels = append(labels, &dto.LabelPair{
+		Name:  toStringPtr("namespace"),
+		Value: &nam.namespace,
+	})
+	m.Label = labels
+	return nil
+}

+ 405 - 0
pkg/metrics/nodemetrics.go

@@ -0,0 +1,405 @@
+package metrics
+
+import (
+	"strings"
+
+	"github.com/kubecost/cost-model/pkg/clustercache"
+	"github.com/kubecost/cost-model/pkg/log"
+	"github.com/kubecost/cost-model/pkg/prom"
+	"github.com/prometheus/client_golang/prometheus"
+	dto "github.com/prometheus/client_model/go"
+	v1 "k8s.io/api/core/v1"
+)
+
+var (
+	conditionStatuses = []v1.ConditionStatus{v1.ConditionTrue, v1.ConditionFalse, v1.ConditionUnknown}
+)
+
+//--------------------------------------------------------------------------
+//  KubeNodeCollector
+//--------------------------------------------------------------------------
+
+// KubeNodeCollector is a prometheus collector that generates node sourced metrics.
+type KubeNodeCollector struct {
+	KubeClusterCache clustercache.ClusterCache
+}
+
+// Describe sends the super-set of all possible descriptors of metrics
+// collected by this Collector.
+func (nsac KubeNodeCollector) Describe(ch chan<- *prometheus.Desc) {
+	ch <- prometheus.NewDesc("kube_node_status_capacity_memory_bytes", "node capacity memory bytes", []string{}, nil)
+	ch <- prometheus.NewDesc("kube_node_status_capacity_cpu_cores", "node capacity cpu cores", []string{}, nil)
+	ch <- prometheus.NewDesc("kube_node_labels", "all labels for each node prefixed with label_", []string{}, nil)
+	ch <- prometheus.NewDesc("kube_node_status_condition", "The condition of a cluster node.", []string{}, nil)
+	ch <- prometheus.NewDesc("kube_node_status_allocatable", "The allocatable for different resources of a node that are available for scheduling.", []string{}, nil)
+}
+
+// Collect is called by the Prometheus registry when collecting metrics.
+func (nsac KubeNodeCollector) Collect(ch chan<- prometheus.Metric) {
+	nodes := nsac.KubeClusterCache.GetAllNodes()
+	for _, node := range nodes {
+		nodeName := node.GetName()
+
+		// k8s.io/apimachinery/pkg/api/resource/amount.go and
+		// k8s.io/apimachinery/pkg/api/resource/quantity.go for
+		// details on the "amount" API. See
+		// https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#resource-types
+		// for the units of memory and CPU.
+		memoryBytes := node.Status.Capacity.Memory().Value()
+		ch <- newKubeNodeStatusCapacityMemoryBytesMetric(nodeName, memoryBytes, "kube_node_status_capacity_memory_bytes", nil, nil)
+
+		cpuCores := float64(node.Status.Capacity.Cpu().MilliValue()) / 1000
+		ch <- newKubeNodeStatusCapacityCPUCoresMetric(nodeName, cpuCores, "kube_node_status_capacity_cpu_cores", nil, nil)
+
+		// allocatable resources
+		for resourceName, quantity := range node.Status.Allocatable {
+			resource, unit, value := toResourceUnitValue(resourceName, quantity)
+
+			// failed to parse the resource type
+			if resource == "" {
+				log.DedupedWarningf(5, "Failed to parse resource units and quantity for resource: %s", resourceName)
+				continue
+			}
+
+			ch <- newKubeNodeStatusAllocatableMetric("kube_node_status_allocatable", nodeName, resource, unit, value)
+		}
+
+		// node labels
+		labelNames, labelValues := prom.KubePrependQualifierToLabels(node.GetLabels(), "label_")
+		ch <- newKubeNodeLabelsMetric(nodeName, "kube_node_labels", labelNames, labelValues)
+
+		// kube_node_status_condition
+		// Collect node conditions and while default to false.
+		for _, c := range node.Status.Conditions {
+			conditions := getConditions(c.Status)
+
+			for _, cond := range conditions {
+				ch <- newKubeNodeStatusConditionMetric(nodeName, "kube_node_status_condition", string(c.Type), cond.status, cond.value)
+			}
+		}
+
+	}
+}
+
+//--------------------------------------------------------------------------
+//  KubeNodeStatusCapacityMemoryBytesMetric
+//--------------------------------------------------------------------------
+
+// KubeNodeStatusCapacityMemoryBytesMetric is a prometheus.Metric used to encode
+// a duplicate of the deprecated kube-state-metrics metric
+// kube_node_status_capacity_memory_bytes
+type KubeNodeStatusCapacityMemoryBytesMetric struct {
+	fqName      string
+	help        string
+	labelNames  []string
+	labelValues []string
+	bytes       int64
+	node        string
+}
+
+// Creates a new KubeNodeStatusCapacityMemoryBytesMetric, implementation of prometheus.Metric
+func newKubeNodeStatusCapacityMemoryBytesMetric(node string, bytes int64, fqname string, labelNames []string, labelValues []string) KubeNodeStatusCapacityMemoryBytesMetric {
+	return KubeNodeStatusCapacityMemoryBytesMetric{
+		fqName:      fqname,
+		labelNames:  labelNames,
+		labelValues: labelValues,
+		help:        "kube_node_status_capacity_memory_bytes Node Capacity Memory Bytes",
+		bytes:       bytes,
+		node:        node,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (nam KubeNodeStatusCapacityMemoryBytesMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{"node": nam.node}
+	return prometheus.NewDesc(nam.fqName, nam.help, nam.labelNames, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data
+// transmission object.
+func (nam KubeNodeStatusCapacityMemoryBytesMetric) Write(m *dto.Metric) error {
+	h := float64(nam.bytes)
+	m.Gauge = &dto.Gauge{
+		Value: &h,
+	}
+
+	var labels []*dto.LabelPair
+	for i := range nam.labelNames {
+		labels = append(labels, &dto.LabelPair{
+			Name:  &nam.labelNames[i],
+			Value: &nam.labelValues[i],
+		})
+	}
+	n := "node"
+	labels = append(labels, &dto.LabelPair{
+		Name:  &n,
+		Value: &nam.node,
+	})
+	m.Label = labels
+	return nil
+}
+
+//--------------------------------------------------------------------------
+//  KubeNodeStatusCapacityCPUCoresMetric
+//--------------------------------------------------------------------------
+
+// KubeNodeStatusCapacityCPUCoresMetric is a prometheus.Metric used to encode
+// a duplicate of the deprecated kube-state-metrics metric
+// kube_node_status_capacity_memory_bytes
+type KubeNodeStatusCapacityCPUCoresMetric struct {
+	fqName      string
+	help        string
+	labelNames  []string
+	labelValues []string
+	cores       float64
+	node        string
+}
+
+// Creates a new KubeNodeStatusCapacityCPUCoresMetric, implementation of prometheus.Metric
+func newKubeNodeStatusCapacityCPUCoresMetric(node string, cores float64, fqname string, labelNames []string, labelValues []string) KubeNodeStatusCapacityCPUCoresMetric {
+	return KubeNodeStatusCapacityCPUCoresMetric{
+		fqName:      fqname,
+		labelNames:  labelNames,
+		labelValues: labelValues,
+		help:        "kube_node_status_capacity_cpu_cores Node Capacity CPU Cores",
+		cores:       cores,
+		node:        node,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (nam KubeNodeStatusCapacityCPUCoresMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{"node": nam.node}
+	return prometheus.NewDesc(nam.fqName, nam.help, nam.labelNames, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data
+// transmission object.
+func (nam KubeNodeStatusCapacityCPUCoresMetric) Write(m *dto.Metric) error {
+	h := nam.cores
+	m.Gauge = &dto.Gauge{
+		Value: &h,
+	}
+
+	var labels []*dto.LabelPair
+	for i := range nam.labelNames {
+		labels = append(labels, &dto.LabelPair{
+			Name:  &nam.labelNames[i],
+			Value: &nam.labelValues[i],
+		})
+	}
+	n := "node"
+	labels = append(labels, &dto.LabelPair{
+		Name:  &n,
+		Value: &nam.node,
+	})
+	m.Label = labels
+	return nil
+}
+
+//--------------------------------------------------------------------------
+//  KubeNodeLabelsCollector
+//--------------------------------------------------------------------------
+//
+// We use this to emit kube_node_labels with all of a node's labels, regardless
+// of the whitelist setting introduced in KSM v2. See
+// https://github.com/kubernetes/kube-state-metrics/issues/1270#issuecomment-712986441
+
+//--------------------------------------------------------------------------
+//  KubeNodeLabelsMetric
+//--------------------------------------------------------------------------
+
+// KubeNodeLabelsMetric is a prometheus.Metric used to encode
+// a duplicate of the deprecated kube-state-metrics metric
+// kube_node_labels
+type KubeNodeLabelsMetric struct {
+	fqName      string
+	help        string
+	labelNames  []string
+	labelValues []string
+	node        string
+}
+
+// Creates a new KubeNodeLabelsMetric, implementation of prometheus.Metric
+func newKubeNodeLabelsMetric(node string, fqname string, labelNames []string, labelValues []string) KubeNodeLabelsMetric {
+	return KubeNodeLabelsMetric{
+		fqName:      fqname,
+		labelNames:  labelNames,
+		labelValues: labelValues,
+		help:        "kube_node_labels all labels for each node prefixed with label_",
+		node:        node,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (nam KubeNodeLabelsMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{
+		"node": nam.node,
+	}
+	return prometheus.NewDesc(nam.fqName, nam.help, nam.labelNames, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data
+// transmission object.
+func (nam KubeNodeLabelsMetric) Write(m *dto.Metric) error {
+	h := float64(1)
+	m.Gauge = &dto.Gauge{
+		Value: &h,
+	}
+
+	var labels []*dto.LabelPair
+	for i := range nam.labelNames {
+		labels = append(labels, &dto.LabelPair{
+			Name:  &nam.labelNames[i],
+			Value: &nam.labelValues[i],
+		})
+	}
+
+	nodeString := "node"
+	labels = append(labels, &dto.LabelPair{Name: &nodeString, Value: &nam.node})
+	m.Label = labels
+	return nil
+}
+
+//--------------------------------------------------------------------------
+//  KubeNodeStatusConditionMetric
+//--------------------------------------------------------------------------
+
+// KubeNodeStatusConditionMetric
+type KubeNodeStatusConditionMetric struct {
+	fqName    string
+	help      string
+	node      string
+	condition string
+	status    string
+	value     float64
+}
+
+// Creates a new KubeNodeStatusConditionMetric, implementation of prometheus.Metric
+func newKubeNodeStatusConditionMetric(node, fqname, condition, status string, value float64) KubeNodeStatusConditionMetric {
+	return KubeNodeStatusConditionMetric{
+		fqName:    fqname,
+		help:      "kube_node_status_condition condition status for nodes",
+		node:      node,
+		condition: condition,
+		status:    status,
+		value:     value,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (nam KubeNodeStatusConditionMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{
+		"node":      nam.node,
+		"condition": nam.condition,
+		"status":    nam.status,
+	}
+	return prometheus.NewDesc(nam.fqName, nam.help, []string{}, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data
+// transmission object.
+func (nam KubeNodeStatusConditionMetric) Write(m *dto.Metric) error {
+	m.Gauge = &dto.Gauge{
+		Value: &nam.value,
+	}
+	m.Label = []*dto.LabelPair{
+		{
+			Name:  toStringPtr("node"),
+			Value: &nam.node,
+		},
+		{
+			Name:  toStringPtr("condition"),
+			Value: &nam.condition,
+		},
+		{
+			Name:  toStringPtr("status"),
+			Value: &nam.status,
+		},
+	}
+	return nil
+}
+
+// helper type for status condition reporting and metric rollup
+type statusCondition struct {
+	status string
+	value  float64
+}
+
+// retrieves the total status conditions and the comparison to the provided condition
+func getConditions(cs v1.ConditionStatus) []*statusCondition {
+	ms := make([]*statusCondition, len(conditionStatuses))
+
+	for i, status := range conditionStatuses {
+		ms[i] = &statusCondition{
+			status: strings.ToLower(string(status)),
+			value:  boolFloat64(cs == status),
+		}
+	}
+
+	return ms
+}
+
+//--------------------------------------------------------------------------
+//  KubeNodeStatusAllocatableMetric
+//--------------------------------------------------------------------------
+
+// KubeNodeStatusAllocatableMetric is a prometheus.Metric
+type KubeNodeStatusAllocatableMetric struct {
+	fqName   string
+	help     string
+	resource string
+	unit     string
+	node     string
+	value    float64
+}
+
+// Creates a new KubeNodeStatusAllocatableMetric, implementation of prometheus.Metric
+func newKubeNodeStatusAllocatableMetric(fqname, node, resource, unit string, value float64) KubeNodeStatusAllocatableMetric {
+	return KubeNodeStatusAllocatableMetric{
+		fqName:   fqname,
+		help:     "kube_node_status_allocatable node allocatable",
+		node:     node,
+		resource: resource,
+		unit:     unit,
+		value:    value,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (kpcrr KubeNodeStatusAllocatableMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{
+		"node":     kpcrr.node,
+		"resource": kpcrr.resource,
+		"unit":     kpcrr.unit,
+	}
+	return prometheus.NewDesc(kpcrr.fqName, kpcrr.help, []string{}, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data transmission object.
+func (kpcrr KubeNodeStatusAllocatableMetric) Write(m *dto.Metric) error {
+	m.Gauge = &dto.Gauge{
+		Value: &kpcrr.value,
+	}
+
+	m.Label = []*dto.LabelPair{
+		{
+			Name:  toStringPtr("node"),
+			Value: &kpcrr.node,
+		},
+		{
+			Name:  toStringPtr("resource"),
+			Value: &kpcrr.resource,
+		},
+		{
+			Name:  toStringPtr("unit"),
+			Value: &kpcrr.unit,
+		},
+	}
+	return nil
+}

+ 825 - 0
pkg/metrics/podmetrics.go

@@ -0,0 +1,825 @@
+package metrics
+
+import (
+	"fmt"
+
+	"github.com/kubecost/cost-model/pkg/clustercache"
+	"github.com/kubecost/cost-model/pkg/log"
+	"github.com/kubecost/cost-model/pkg/prom"
+	"github.com/prometheus/client_golang/prometheus"
+	dto "github.com/prometheus/client_model/go"
+	v1 "k8s.io/api/core/v1"
+)
+
+//--------------------------------------------------------------------------
+//  KubePodMetricCollector
+//--------------------------------------------------------------------------
+
+// KubePodMetricCollector is a prometheus collector that emits pod metrics
+type KubePodMetricCollector struct {
+	KubeClusterCache   clustercache.ClusterCache
+	emitPodAnnotations bool
+}
+
+// Describe sends the super-set of all possible descriptors of metrics
+// collected by this Collector.
+func (kpmc KubePodMetricCollector) Describe(ch chan<- *prometheus.Desc) {
+	ch <- prometheus.NewDesc("kube_pod_labels", "All labels for each pod prefixed with label_", []string{}, nil)
+	if kpmc.emitPodAnnotations {
+		ch <- prometheus.NewDesc("kube_pod_annotations", "All annotations for each pod prefix with annotation_", []string{}, nil)
+	}
+	ch <- prometheus.NewDesc("kube_pod_owner", "Information about the Pod's owner", []string{}, nil)
+	ch <- prometheus.NewDesc("kube_pod_container_status_running", "Describes whether the container is currently in running state", []string{}, nil)
+	ch <- prometheus.NewDesc("kube_pod_container_status_terminated_reason", "Describes the reason the container is currently in terminated state.", []string{}, nil)
+	ch <- prometheus.NewDesc("kube_pod_container_status_restarts_total", "The number of container restarts per container.", []string{}, nil)
+	ch <- prometheus.NewDesc("kube_pod_container_resource_requests", "The number of requested resource by a container", []string{}, nil)
+	ch <- prometheus.NewDesc("kube_pod_container_resource_limits", "The number of requested limit resource by a container.", []string{}, nil)
+	ch <- prometheus.NewDesc("kube_pod_status_phase", "The pods current phase.", []string{}, nil)
+}
+
+// Collect is called by the Prometheus registry when collecting metrics.
+func (kpmc KubePodMetricCollector) Collect(ch chan<- prometheus.Metric) {
+	pods := kpmc.KubeClusterCache.GetAllPods()
+	for _, pod := range pods {
+		podName := pod.GetName()
+		podNS := pod.GetNamespace()
+		podUID := string(pod.GetUID())
+		node := pod.Spec.NodeName
+		phase := pod.Status.Phase
+
+		// Pod Status Phase
+		if phase != "" {
+			phases := []struct {
+				v bool
+				n string
+			}{
+				{phase == v1.PodPending, string(v1.PodPending)},
+				{phase == v1.PodSucceeded, string(v1.PodSucceeded)},
+				{phase == v1.PodFailed, string(v1.PodFailed)},
+				{phase == v1.PodUnknown, string(v1.PodUnknown)},
+				{phase == v1.PodRunning, string(v1.PodRunning)},
+			}
+
+			for _, p := range phases {
+				ch <- newKubePodStatusPhaseMetric("kube_pod_status_phase", podName, podNS, podUID, p.n, boolFloat64(p.v))
+			}
+		}
+
+		// Pod Labels
+		labelNames, labelValues := prom.KubePrependQualifierToLabels(pod.GetLabels(), "label_")
+		ch <- newKubePodLabelsMetric(podName, podNS, podUID, "kube_pod_labels", labelNames, labelValues)
+
+		// Pod Annotations
+		if kpmc.emitPodAnnotations {
+			labels, values := prom.KubeAnnotationsToLabels(pod.Annotations)
+
+			if len(labels) > 0 {
+				ch <- newPodAnnotationMetric(podNS, podName, "kube_pod_annotations", labels, values)
+			}
+		}
+
+		// Owner References
+		for _, owner := range pod.OwnerReferences {
+			ch <- newKubePodOwnerMetric("kube_pod_owner", podNS, podName, owner.Name, owner.Kind, owner.Controller != nil)
+		}
+
+		// Container Status
+		for _, status := range pod.Status.ContainerStatuses {
+			ch <- newKubePodContainerStatusRestartsTotalMetric("kube_pod_container_status_restarts_total", podName, podNS, podUID, status.Name, float64(status.RestartCount))
+			if status.State.Running != nil {
+				ch <- newKubePodContainerStatusRunningMetric("kube_pod_container_status_running", podName, podNS, podUID, status.Name)
+			}
+
+			if status.State.Terminated != nil {
+				ch <- newKubePodContainerStatusTerminatedReasonMetric(
+					"kube_pod_container_status_terminated_reason",
+					podName,
+					podNS,
+					podUID,
+					status.Name,
+					status.State.Terminated.Reason)
+			}
+		}
+
+		for _, container := range pod.Spec.Containers {
+			// Requests
+			for resourceName, quantity := range container.Resources.Requests {
+				resource, unit, value := toResourceUnitValue(resourceName, quantity)
+
+				// failed to parse the resource type
+				if resource == "" {
+					log.DedupedWarningf(5, "Failed to parse resource units and quantity for resource: %s", resourceName)
+					continue
+				}
+
+				ch <- newKubePodContainerResourceRequestsMetric(
+					"kube_pod_container_resource_requests",
+					podName,
+					podNS,
+					podUID,
+					container.Name,
+					node,
+					resource,
+					unit,
+					value)
+			}
+
+			// Limits
+			for resourceName, quantity := range container.Resources.Limits {
+				resource, unit, value := toResourceUnitValue(resourceName, quantity)
+
+				// failed to parse the resource type
+				if resource == "" {
+					log.DedupedWarningf(5, "Failed to parse resource units and quantity for resource: %s", resourceName)
+					continue
+				}
+
+				ch <- newKubePodContainerResourceLimitsMetric(
+					"kube_pod_container_resource_limits",
+					podName,
+					podNS,
+					podUID,
+					container.Name,
+					node,
+					resource,
+					unit,
+					value)
+			}
+		}
+	}
+}
+
+//--------------------------------------------------------------------------
+//  PodAnnotationsMetric
+//--------------------------------------------------------------------------
+
+// PodAnnotationsMetric is a prometheus.Metric used to encode namespace annotations
+type PodAnnotationsMetric struct {
+	name        string
+	fqName      string
+	help        string
+	labelNames  []string
+	labelValues []string
+	namespace   string
+}
+
+// Creates a new PodAnnotationsMetric, implementation of prometheus.Metric
+func newPodAnnotationMetric(namespace, name, fqname string, labelNames []string, labelValues []string) PodAnnotationsMetric {
+	return PodAnnotationsMetric{
+		namespace:   namespace,
+		name:        name,
+		fqName:      fqname,
+		labelNames:  labelNames,
+		labelValues: labelValues,
+		help:        "kube_pod_annotations Pod Annotations",
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (pam PodAnnotationsMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{"namespace": pam.namespace, "pod": pam.name}
+	return prometheus.NewDesc(pam.fqName, pam.help, pam.labelNames, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data
+// transmission object.
+func (pam PodAnnotationsMetric) Write(m *dto.Metric) error {
+	h := float64(1)
+	m.Gauge = &dto.Gauge{
+		Value: &h,
+	}
+
+	var labels []*dto.LabelPair
+	for i := range pam.labelNames {
+		labels = append(labels, &dto.LabelPair{
+			Name:  &pam.labelNames[i],
+			Value: &pam.labelValues[i],
+		})
+	}
+	n := "namespace"
+	labels = append(labels, &dto.LabelPair{
+		Name:  &n,
+		Value: &pam.namespace,
+	})
+	r := "pod"
+	labels = append(labels, &dto.LabelPair{
+		Name:  &r,
+		Value: &pam.name,
+	})
+	m.Label = labels
+	return nil
+}
+
+//--------------------------------------------------------------------------
+//  KubePodLabelsMetric
+//--------------------------------------------------------------------------
+
+// KubePodLabelsMetric is a prometheus.Metric used to encode
+// a duplicate of the deprecated kube-state-metrics metric
+// kube_pod_labels
+type KubePodLabelsMetric struct {
+	fqName      string
+	help        string
+	labelNames  []string
+	labelValues []string
+	pod         string
+	namespace   string
+	uid         string
+}
+
+// Creates a new KubePodLabelsMetric, implementation of prometheus.Metric
+func newKubePodLabelsMetric(pod string, namespace string, uid string, fqname string, labelNames []string, labelValues []string) KubePodLabelsMetric {
+	return KubePodLabelsMetric{
+		fqName:      fqname,
+		labelNames:  labelNames,
+		labelValues: labelValues,
+		help:        "kube_pod_labels all labels for each pod prefixed with label_",
+		pod:         pod,
+		namespace:   namespace,
+		uid:         uid,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (nam KubePodLabelsMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{
+		"pod":       nam.pod,
+		"namespace": nam.namespace,
+		"uid":       nam.uid,
+	}
+	return prometheus.NewDesc(nam.fqName, nam.help, nam.labelNames, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data
+// transmission object.
+func (nam KubePodLabelsMetric) Write(m *dto.Metric) error {
+	h := float64(1)
+	m.Gauge = &dto.Gauge{
+		Value: &h,
+	}
+
+	var labels []*dto.LabelPair
+	for i := range nam.labelNames {
+		labels = append(labels, &dto.LabelPair{
+			Name:  &nam.labelNames[i],
+			Value: &nam.labelValues[i],
+		})
+	}
+
+	podString := "pod"
+	namespaceString := "namespace"
+	uidString := "uid"
+	labels = append(labels,
+		&dto.LabelPair{
+			Name:  &podString,
+			Value: &nam.pod,
+		},
+		&dto.LabelPair{
+			Name:  &namespaceString,
+			Value: &nam.namespace,
+		}, &dto.LabelPair{
+			Name:  &uidString,
+			Value: &nam.uid,
+		},
+	)
+	m.Label = labels
+	return nil
+}
+
+//--------------------------------------------------------------------------
+//  KubePodContainerStatusRestartsTotalMetric
+//--------------------------------------------------------------------------
+
+// KubePodContainerStatusRestartsTotalMetric is a prometheus.Metric emitting container restarts metrics.
+type KubePodContainerStatusRestartsTotalMetric struct {
+	fqName    string
+	help      string
+	pod       string
+	namespace string
+	container string
+	uid       string
+	value     float64
+}
+
+// Creates a new KubePodContainerStatusRestartsTotalMetric, implementation of prometheus.Metric
+func newKubePodContainerStatusRestartsTotalMetric(fqname, pod, namespace, uid, container string, value float64) KubePodContainerStatusRestartsTotalMetric {
+	return KubePodContainerStatusRestartsTotalMetric{
+		fqName:    fqname,
+		help:      "kube_pod_container_status_restarts_total total container restarts",
+		pod:       pod,
+		namespace: namespace,
+		uid:       uid,
+		container: container,
+		value:     value,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (kpcs KubePodContainerStatusRestartsTotalMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{
+		"pod":       kpcs.pod,
+		"namespace": kpcs.namespace,
+		"uid":       kpcs.uid,
+		"container": kpcs.container,
+	}
+	return prometheus.NewDesc(kpcs.fqName, kpcs.help, []string{}, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data transmission object.
+func (kpcs KubePodContainerStatusRestartsTotalMetric) Write(m *dto.Metric) error {
+	m.Counter = &dto.Counter{
+		Value: &kpcs.value,
+	}
+
+	var labels []*dto.LabelPair
+	labels = append(labels,
+		&dto.LabelPair{
+			Name:  toStringPtr("pod"),
+			Value: &kpcs.pod,
+		},
+		&dto.LabelPair{
+			Name:  toStringPtr("namespace"),
+			Value: &kpcs.namespace,
+		},
+		&dto.LabelPair{
+			Name:  toStringPtr("container"),
+			Value: &kpcs.container,
+		},
+		&dto.LabelPair{
+			Name:  toStringPtr("uid"),
+			Value: &kpcs.uid,
+		},
+	)
+	m.Label = labels
+	return nil
+}
+
+//--------------------------------------------------------------------------
+//  KubePodContainerStatusTerminatedReasonMetric
+//--------------------------------------------------------------------------
+
+// KubePodContainerStatusTerminatedReasonMetric is a prometheus.Metric emitting container termination reasons.
+type KubePodContainerStatusTerminatedReasonMetric struct {
+	fqName    string
+	help      string
+	pod       string
+	namespace string
+	container string
+	uid       string
+	reason    string
+}
+
+// Creates a new KubePodContainerStatusRestartsTotalMetric, implementation of prometheus.Metric
+func newKubePodContainerStatusTerminatedReasonMetric(fqname, pod, namespace, uid, container, reason string) KubePodContainerStatusTerminatedReasonMetric {
+	return KubePodContainerStatusTerminatedReasonMetric{
+		fqName:    fqname,
+		help:      "kube_pod_container_status_terminated_reason Describes the reason the container is currently in terminated state.",
+		pod:       pod,
+		namespace: namespace,
+		uid:       uid,
+		container: container,
+		reason:    reason,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (kpcs KubePodContainerStatusTerminatedReasonMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{
+		"pod":       kpcs.pod,
+		"namespace": kpcs.namespace,
+		"uid":       kpcs.uid,
+		"container": kpcs.container,
+		"reason":    kpcs.reason,
+	}
+	return prometheus.NewDesc(kpcs.fqName, kpcs.help, []string{}, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data transmission object.
+func (kpcs KubePodContainerStatusTerminatedReasonMetric) Write(m *dto.Metric) error {
+	h := float64(1)
+	m.Gauge = &dto.Gauge{
+		Value: &h,
+	}
+
+	var labels []*dto.LabelPair
+	labels = append(labels,
+		&dto.LabelPair{
+			Name:  toStringPtr("pod"),
+			Value: &kpcs.pod,
+		},
+		&dto.LabelPair{
+			Name:  toStringPtr("namespace"),
+			Value: &kpcs.namespace,
+		},
+		&dto.LabelPair{
+			Name:  toStringPtr("container"),
+			Value: &kpcs.container,
+		},
+		&dto.LabelPair{
+			Name:  toStringPtr("uid"),
+			Value: &kpcs.uid,
+		},
+		&dto.LabelPair{
+			Name:  toStringPtr("reason"),
+			Value: &kpcs.reason,
+		},
+	)
+	m.Label = labels
+	return nil
+}
+
+//--------------------------------------------------------------------------
+//  KubePodStatusPhaseMetric
+//--------------------------------------------------------------------------
+
+// KubePodStatusPhaseMetric is a prometheus.Metric emitting all phases for a pod
+type KubePodStatusPhaseMetric struct {
+	fqName    string
+	help      string
+	pod       string
+	namespace string
+	uid       string
+	phase     string
+	value     float64
+}
+
+// Creates a new KubePodContainerStatusRestartsTotalMetric, implementation of prometheus.Metric
+func newKubePodStatusPhaseMetric(fqname, pod, namespace, uid, phase string, value float64) KubePodStatusPhaseMetric {
+	return KubePodStatusPhaseMetric{
+		fqName:    fqname,
+		help:      "kube_pod_container_status_terminated_reason Describes the reason the container is currently in terminated state.",
+		pod:       pod,
+		namespace: namespace,
+		uid:       uid,
+		phase:     phase,
+		value:     value,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (kpcs KubePodStatusPhaseMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{
+		"pod":       kpcs.pod,
+		"namespace": kpcs.namespace,
+		"uid":       kpcs.uid,
+		"phase":     kpcs.phase,
+	}
+	return prometheus.NewDesc(kpcs.fqName, kpcs.help, []string{}, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data transmission object.
+func (kpcs KubePodStatusPhaseMetric) Write(m *dto.Metric) error {
+	m.Gauge = &dto.Gauge{
+		Value: &kpcs.value,
+	}
+
+	var labels []*dto.LabelPair
+	labels = append(labels,
+		&dto.LabelPair{
+			Name:  toStringPtr("pod"),
+			Value: &kpcs.pod,
+		},
+		&dto.LabelPair{
+			Name:  toStringPtr("namespace"),
+			Value: &kpcs.namespace,
+		},
+		&dto.LabelPair{
+			Name:  toStringPtr("uid"),
+			Value: &kpcs.uid,
+		},
+		&dto.LabelPair{
+			Name:  toStringPtr("phase"),
+			Value: &kpcs.phase,
+		},
+	)
+	m.Label = labels
+	return nil
+}
+
+//--------------------------------------------------------------------------
+//  KubePodContainerStatusRunningMetric
+//--------------------------------------------------------------------------
+
+// KubePodLabelsMetric is a prometheus.Metric used to encode
+// a duplicate of the deprecated kube-state-metrics metric
+// kube_pod_labels
+type KubePodContainerStatusRunningMetric struct {
+	fqName    string
+	help      string
+	pod       string
+	namespace string
+	container string
+	uid       string
+}
+
+// Creates a new KubePodContainerStatusRunningMetric, implementation of prometheus.Metric
+func newKubePodContainerStatusRunningMetric(fqname string, pod string, namespace string, uid string, container string) KubePodContainerStatusRunningMetric {
+	return KubePodContainerStatusRunningMetric{
+		fqName:    fqname,
+		help:      "kube_pod_container_status_running pods container status",
+		pod:       pod,
+		namespace: namespace,
+		uid:       uid,
+		container: container,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (kpcs KubePodContainerStatusRunningMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{
+		"pod":       kpcs.pod,
+		"namespace": kpcs.namespace,
+		"uid":       kpcs.uid,
+		"container": kpcs.container,
+	}
+	return prometheus.NewDesc(kpcs.fqName, kpcs.help, []string{}, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data
+// transmission object.
+func (kpcs KubePodContainerStatusRunningMetric) Write(m *dto.Metric) error {
+	h := float64(1)
+	m.Gauge = &dto.Gauge{
+		Value: &h,
+	}
+
+	var labels []*dto.LabelPair
+	labels = append(labels,
+		&dto.LabelPair{
+			Name:  toStringPtr("pod"),
+			Value: &kpcs.pod,
+		},
+		&dto.LabelPair{
+			Name:  toStringPtr("namespace"),
+			Value: &kpcs.namespace,
+		},
+		&dto.LabelPair{
+			Name:  toStringPtr("container"),
+			Value: &kpcs.container,
+		},
+		&dto.LabelPair{
+			Name:  toStringPtr("uid"),
+			Value: &kpcs.uid,
+		},
+	)
+	m.Label = labels
+	return nil
+}
+
+//--------------------------------------------------------------------------
+//  KubePodContainerResourceRequestMetric
+//--------------------------------------------------------------------------
+
+// KubePodContainerResourceRequestsMetric is a prometheus.Metric
+type KubePodContainerResourceRequestsMetric struct {
+	fqName    string
+	help      string
+	pod       string
+	namespace string
+	container string
+	uid       string
+	resource  string
+	unit      string
+	node      string
+	value     float64
+}
+
+// Creates a new newKubePodContainerResourceRequestsMetric, implementation of prometheus.Metric
+func newKubePodContainerResourceRequestsMetric(fqname, pod, namespace, uid, container, node, resource, unit string, value float64) KubePodContainerResourceRequestsMetric {
+	return KubePodContainerResourceRequestsMetric{
+		fqName:    fqname,
+		help:      "kube_pod_container_resource_requests pods container resource requests",
+		pod:       pod,
+		namespace: namespace,
+		uid:       uid,
+		container: container,
+		node:      node,
+		resource:  resource,
+		unit:      unit,
+		value:     value,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (kpcrr KubePodContainerResourceRequestsMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{
+		"pod":       kpcrr.pod,
+		"namespace": kpcrr.namespace,
+		"uid":       kpcrr.uid,
+		"container": kpcrr.container,
+		"node":      kpcrr.node,
+		"resource":  kpcrr.resource,
+		"unit":      kpcrr.unit,
+	}
+	return prometheus.NewDesc(kpcrr.fqName, kpcrr.help, []string{}, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data
+// transmission object.
+func (kpcrr KubePodContainerResourceRequestsMetric) Write(m *dto.Metric) error {
+	m.Gauge = &dto.Gauge{
+		Value: &kpcrr.value,
+	}
+
+	m.Label = []*dto.LabelPair{
+		{
+			Name:  toStringPtr("pod"),
+			Value: &kpcrr.pod,
+		},
+		{
+			Name:  toStringPtr("namespace"),
+			Value: &kpcrr.namespace,
+		},
+		{
+			Name:  toStringPtr("container"),
+			Value: &kpcrr.container,
+		},
+		{
+			Name:  toStringPtr("uid"),
+			Value: &kpcrr.uid,
+		},
+		{
+			Name:  toStringPtr("node"),
+			Value: &kpcrr.node,
+		},
+		{
+			Name:  toStringPtr("resource"),
+			Value: &kpcrr.resource,
+		},
+		{
+			Name:  toStringPtr("unit"),
+			Value: &kpcrr.unit,
+		},
+	}
+	return nil
+}
+
+//--------------------------------------------------------------------------
+//  KubePodContainerResourceLimitsMetric
+//--------------------------------------------------------------------------
+
+// KubePodContainerResourceLimitsMetric is a prometheus.Metric
+type KubePodContainerResourceLimitsMetric struct {
+	fqName    string
+	help      string
+	pod       string
+	namespace string
+	container string
+	uid       string
+	resource  string
+	unit      string
+	node      string
+	value     float64
+}
+
+// Creates a new KubePodContainerResourceLimitsMetric, implementation of prometheus.Metric
+func newKubePodContainerResourceLimitsMetric(fqname, pod, namespace, uid, container, node, resource, unit string, value float64) KubePodContainerResourceLimitsMetric {
+	return KubePodContainerResourceLimitsMetric{
+		fqName:    fqname,
+		help:      "kube_pod_container_resource_limits pods container resource limits",
+		pod:       pod,
+		namespace: namespace,
+		uid:       uid,
+		container: container,
+		node:      node,
+		resource:  resource,
+		unit:      unit,
+		value:     value,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (kpcrr KubePodContainerResourceLimitsMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{
+		"pod":       kpcrr.pod,
+		"namespace": kpcrr.namespace,
+		"uid":       kpcrr.uid,
+		"container": kpcrr.container,
+		"node":      kpcrr.node,
+		"resource":  kpcrr.resource,
+		"unit":      kpcrr.unit,
+	}
+	return prometheus.NewDesc(kpcrr.fqName, kpcrr.help, []string{}, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data
+// transmission object.
+func (kpcrr KubePodContainerResourceLimitsMetric) Write(m *dto.Metric) error {
+	m.Gauge = &dto.Gauge{
+		Value: &kpcrr.value,
+	}
+
+	m.Label = []*dto.LabelPair{
+		{
+			Name:  toStringPtr("pod"),
+			Value: &kpcrr.pod,
+		},
+		{
+			Name:  toStringPtr("namespace"),
+			Value: &kpcrr.namespace,
+		},
+		{
+			Name:  toStringPtr("container"),
+			Value: &kpcrr.container,
+		},
+		{
+			Name:  toStringPtr("uid"),
+			Value: &kpcrr.uid,
+		},
+		{
+			Name:  toStringPtr("node"),
+			Value: &kpcrr.node,
+		},
+		{
+			Name:  toStringPtr("resource"),
+			Value: &kpcrr.resource,
+		},
+		{
+			Name:  toStringPtr("unit"),
+			Value: &kpcrr.unit,
+		},
+	}
+	return nil
+}
+
+//--------------------------------------------------------------------------
+//  KubePodOwnerMetric
+//--------------------------------------------------------------------------
+
+// KubePodOwnerMetric is a prometheus.Metric
+type KubePodOwnerMetric struct {
+	fqName            string
+	help              string
+	namespace         string
+	pod               string
+	ownerIsController bool
+	ownerName         string
+	ownerKind         string
+}
+
+// Creates a new KubePodOwnerMetric, implementation of prometheus.Metric
+func newKubePodOwnerMetric(fqname, namespace, pod, ownerName, ownerKind string, ownerIsController bool) KubePodOwnerMetric {
+	return KubePodOwnerMetric{
+		fqName:            fqname,
+		help:              "kube_pod_owner Information about the Pod's owner",
+		namespace:         namespace,
+		pod:               pod,
+		ownerName:         ownerName,
+		ownerKind:         ownerKind,
+		ownerIsController: ownerIsController,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (kpo KubePodOwnerMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{
+		"namespace":           kpo.namespace,
+		"pod":                 kpo.pod,
+		"owner_name":          kpo.ownerName,
+		"owner_kind":          kpo.ownerKind,
+		"owner_is_controller": fmt.Sprintf("%t", kpo.ownerIsController),
+	}
+	return prometheus.NewDesc(kpo.fqName, kpo.help, []string{}, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data
+// transmission object.
+func (kpo KubePodOwnerMetric) Write(m *dto.Metric) error {
+	v := float64(1.0)
+	m.Gauge = &dto.Gauge{
+		Value: &v,
+	}
+
+	m.Label = []*dto.LabelPair{
+		{
+			Name:  toStringPtr("namespace"),
+			Value: &kpo.namespace,
+		},
+		{
+			Name:  toStringPtr("pod"),
+			Value: &kpo.pod,
+		},
+		{
+			Name:  toStringPtr("owner_name"),
+			Value: &kpo.ownerName,
+		},
+		{
+			Name:  toStringPtr("owner_kind"),
+			Value: &kpo.ownerKind,
+		},
+		{
+			Name:  toStringPtr("owner_is_controller"),
+			Value: toStringPtr(fmt.Sprintf("%t", kpo.ownerIsController)),
+		},
+	}
+	return nil
+}

+ 159 - 0
pkg/metrics/pvcmetrics.go

@@ -0,0 +1,159 @@
+package metrics
+
+import (
+	"github.com/kubecost/cost-model/pkg/clustercache"
+	"github.com/prometheus/client_golang/prometheus"
+	dto "github.com/prometheus/client_model/go"
+	v1 "k8s.io/api/core/v1"
+)
+
+//--------------------------------------------------------------------------
+//  KubePVCCollector
+//--------------------------------------------------------------------------
+
+// KubePVCCollector is a prometheus collector that generates pvc sourced metrics
+type KubePVCCollector struct {
+	KubeClusterCache clustercache.ClusterCache
+}
+
+// Describe sends the super-set of all possible descriptors of metrics collected by this Collector.
+func (kpvc KubePVCCollector) Describe(ch chan<- *prometheus.Desc) {
+	ch <- prometheus.NewDesc("kube_persistentvolumeclaim_resource_requests_storage_bytes", "The pvc storage resource requests in bytes", []string{}, nil)
+	ch <- prometheus.NewDesc("kube_persistentvolumeclaim_info", "The pvc storage resource requests in bytes", []string{}, nil)
+}
+
+// Collect is called by the Prometheus registry when collecting metrics.
+func (kpvc KubePVCCollector) Collect(ch chan<- prometheus.Metric) {
+	pvcs := kpvc.KubeClusterCache.GetAllPersistentVolumeClaims()
+	for _, pvc := range pvcs {
+		storageClass := getPersistentVolumeClaimClass(pvc)
+		volume := pvc.Spec.VolumeName
+
+		ch <- newKubePVCInfoMetric("kube_persistentvolumeclaim_info", pvc.Name, pvc.Namespace, volume, storageClass)
+
+		if storage, ok := pvc.Spec.Resources.Requests[v1.ResourceStorage]; ok {
+			ch <- newKubePVCResourceRequestsStorageBytesMetric("kube_persistentvolumeclaim_resource_requests_storage_bytes", pvc.Name, pvc.Namespace, float64(storage.Value()))
+		}
+	}
+}
+
+//--------------------------------------------------------------------------
+//  KubePVCResourceRequestsStorageBytesMetric
+//--------------------------------------------------------------------------
+
+// KubePVCResourceRequestsStorageBytesMetric is a prometheus.Metric
+type KubePVCResourceRequestsStorageBytesMetric struct {
+	fqName    string
+	help      string
+	namespace string
+	pvc       string
+	value     float64
+}
+
+// Creates a new KubePVCResourceRequestsStorageBytesMetric, implementation of prometheus.Metric
+func newKubePVCResourceRequestsStorageBytesMetric(fqname, pvc, namespace string, value float64) KubePVCResourceRequestsStorageBytesMetric {
+	return KubePVCResourceRequestsStorageBytesMetric{
+		fqName:    fqname,
+		help:      "kube_persistentvolumeclaim_resource_requests_storage_bytes pvc storage resource requests in bytes",
+		pvc:       pvc,
+		namespace: namespace,
+		value:     value,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (kpvcrr KubePVCResourceRequestsStorageBytesMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{
+		"persistentvolumeclaim": kpvcrr.pvc,
+		"namespace":             kpvcrr.namespace,
+	}
+	return prometheus.NewDesc(kpvcrr.fqName, kpvcrr.help, []string{}, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data
+// transmission object.
+func (kpvcrr KubePVCResourceRequestsStorageBytesMetric) Write(m *dto.Metric) error {
+	m.Gauge = &dto.Gauge{
+		Value: &kpvcrr.value,
+	}
+
+	m.Label = []*dto.LabelPair{
+		{
+			Name:  toStringPtr("persistentvolumeclaim"),
+			Value: &kpvcrr.pvc,
+		},
+		{
+			Name:  toStringPtr("namespace"),
+			Value: &kpvcrr.namespace,
+		},
+	}
+	return nil
+}
+
+//--------------------------------------------------------------------------
+//  KubePVCInfoMetric
+//--------------------------------------------------------------------------
+
+// KubePVCInfoMetric is a prometheus.Metric
+type KubePVCInfoMetric struct {
+	fqName       string
+	help         string
+	namespace    string
+	pvc          string
+	storageclass string
+	volume       string
+}
+
+// Creates a new KubePVCInfoMetric, implementation of prometheus.Metric
+func newKubePVCInfoMetric(fqname, pvc, namespace, storageclass, volume string) KubePVCInfoMetric {
+	return KubePVCInfoMetric{
+		fqName:       fqname,
+		help:         "kube_persistentvolumeclaim_info pvc storage resource requests in bytes",
+		pvc:          pvc,
+		namespace:    namespace,
+		storageclass: storageclass,
+		volume:       volume,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (kpvcrr KubePVCInfoMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{
+		"persistentvolumeclaim": kpvcrr.pvc,
+		"namespace":             kpvcrr.namespace,
+		"storageclass":          kpvcrr.storageclass,
+		"volumename":            kpvcrr.volume,
+	}
+	return prometheus.NewDesc(kpvcrr.fqName, kpvcrr.help, []string{}, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data
+// transmission object.
+func (kpvci KubePVCInfoMetric) Write(m *dto.Metric) error {
+	v := float64(1.0)
+	m.Gauge = &dto.Gauge{
+		Value: &v,
+	}
+
+	m.Label = []*dto.LabelPair{
+		{
+			Name:  toStringPtr("namespace"),
+			Value: &kpvci.namespace,
+		},
+		{
+			Name:  toStringPtr("persistentvolumeclaim"),
+			Value: &kpvci.pvc,
+		},
+		{
+			Name:  toStringPtr("storageclass"),
+			Value: &kpvci.storageclass,
+		},
+		{
+			Name:  toStringPtr("volumename"),
+			Value: &kpvci.volume,
+		},
+	}
+	return nil
+}

+ 154 - 0
pkg/metrics/pvmetrics.go

@@ -0,0 +1,154 @@
+package metrics
+
+import (
+	"github.com/kubecost/cost-model/pkg/clustercache"
+	"github.com/prometheus/client_golang/prometheus"
+	dto "github.com/prometheus/client_model/go"
+	v1 "k8s.io/api/core/v1"
+)
+
+//--------------------------------------------------------------------------
+//  KubePVCollector
+//--------------------------------------------------------------------------
+
+// KubePVCollector is a prometheus collector that generates PV metrics
+type KubePVCollector struct {
+	KubeClusterCache clustercache.ClusterCache
+}
+
+// Describe sends the super-set of all possible descriptors of metrics
+// collected by this Collector.
+func (kpvcb KubePVCollector) Describe(ch chan<- *prometheus.Desc) {
+	ch <- prometheus.NewDesc("kube_persistentvolume_capacity_bytes", "The pv storage capacity in bytes", []string{}, nil)
+	ch <- prometheus.NewDesc("kube_persistentvolume_status_phase", "The phase indicates if a volume is available, bound to a claim, or released by a claim.", []string{}, nil)
+}
+
+// Collect is called by the Prometheus registry when collecting metrics.
+func (kpvcb KubePVCollector) Collect(ch chan<- prometheus.Metric) {
+	pvs := kpvcb.KubeClusterCache.GetAllPersistentVolumes()
+	for _, pv := range pvs {
+		phase := pv.Status.Phase
+		if phase != "" {
+			phases := []struct {
+				v bool
+				n string
+			}{
+				{phase == v1.VolumePending, string(v1.VolumePending)},
+				{phase == v1.VolumeAvailable, string(v1.VolumeAvailable)},
+				{phase == v1.VolumeBound, string(v1.VolumeBound)},
+				{phase == v1.VolumeReleased, string(v1.VolumeReleased)},
+				{phase == v1.VolumeFailed, string(v1.VolumeFailed)},
+			}
+
+			for _, p := range phases {
+				ch <- newKubePVStatusPhaseMetric("kube_persistentvolume_status_phase", pv.Name, p.n, boolFloat64(p.v))
+			}
+		}
+
+		storage := pv.Spec.Capacity[v1.ResourceStorage]
+		m := newKubePVCapacityBytesMetric("kube_persistentvolume_capacity_bytes", pv.Name, float64(storage.Value()))
+
+		ch <- m
+	}
+}
+
+//--------------------------------------------------------------------------
+//  KubePVCapacityBytesMetric
+//--------------------------------------------------------------------------
+
+// KubePVCapacityBytesMetric is a prometheus.Metric
+type KubePVCapacityBytesMetric struct {
+	fqName string
+	help   string
+	pv     string
+	value  float64
+}
+
+// Creates a new KubePVCapacityBytesMetric, implementation of prometheus.Metric
+func newKubePVCapacityBytesMetric(fqname, pv string, value float64) KubePVCapacityBytesMetric {
+	return KubePVCapacityBytesMetric{
+		fqName: fqname,
+		help:   "kube_persistentvolume_capacity_bytes pv storage capacity in bytes",
+		pv:     pv,
+		value:  value,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (kpcrr KubePVCapacityBytesMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{
+		"persistentvolume": kpcrr.pv,
+	}
+	return prometheus.NewDesc(kpcrr.fqName, kpcrr.help, []string{}, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data
+// transmission object.
+func (kpcrr KubePVCapacityBytesMetric) Write(m *dto.Metric) error {
+	m.Gauge = &dto.Gauge{
+		Value: &kpcrr.value,
+	}
+
+	m.Label = []*dto.LabelPair{
+		{
+			Name:  toStringPtr("persistentvolume"),
+			Value: &kpcrr.pv,
+		},
+	}
+	return nil
+}
+
+//--------------------------------------------------------------------------
+//  KubePVStatusPhaseMetric
+//--------------------------------------------------------------------------
+
+// KubePVStatusPhaseMetric is a prometheus.Metric
+type KubePVStatusPhaseMetric struct {
+	fqName string
+	help   string
+	pv     string
+	phase  string
+	value  float64
+}
+
+// Creates a new KubePVCapacityBytesMetric, implementation of prometheus.Metric
+func newKubePVStatusPhaseMetric(fqname, pv, phase string, value float64) KubePVStatusPhaseMetric {
+	return KubePVStatusPhaseMetric{
+		fqName: fqname,
+		help:   "kube_persistentvolume_status_phase pv status phase",
+		pv:     pv,
+		phase:  phase,
+		value:  value,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (kpcrr KubePVStatusPhaseMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{
+		"persistentvolume": kpcrr.pv,
+		"phase":            kpcrr.phase,
+	}
+	return prometheus.NewDesc(kpcrr.fqName, kpcrr.help, []string{}, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data
+// transmission object.
+func (kpcrr KubePVStatusPhaseMetric) Write(m *dto.Metric) error {
+	m.Gauge = &dto.Gauge{
+		Value: &kpcrr.value,
+	}
+
+	m.Label = []*dto.LabelPair{
+		{
+			Name:  toStringPtr("persistentvolume"),
+			Value: &kpcrr.pv,
+		},
+		{
+			Name:  toStringPtr("phase"),
+			Value: &kpcrr.phase,
+		},
+	}
+	return nil
+}

+ 101 - 0
pkg/metrics/servicemetrics.go

@@ -0,0 +1,101 @@
+package metrics
+
+import (
+	"github.com/kubecost/cost-model/pkg/clustercache"
+	"github.com/kubecost/cost-model/pkg/prom"
+
+	"github.com/prometheus/client_golang/prometheus"
+	dto "github.com/prometheus/client_model/go"
+)
+
+//--------------------------------------------------------------------------
+//  KubecostServiceCollector
+//--------------------------------------------------------------------------
+
+// KubecostServiceCollector is a prometheus collector that generates service sourced metrics.
+type KubecostServiceCollector struct {
+	KubeClusterCache clustercache.ClusterCache
+}
+
+// Describe sends the super-set of all possible descriptors of metrics
+// collected by this Collector.
+func (sc KubecostServiceCollector) Describe(ch chan<- *prometheus.Desc) {
+	ch <- prometheus.NewDesc("service_selector_labels", "service selector labels", []string{}, nil)
+}
+
+// Collect is called by the Prometheus registry when collecting metrics.
+func (sc KubecostServiceCollector) Collect(ch chan<- prometheus.Metric) {
+	svcs := sc.KubeClusterCache.GetAllServices()
+	for _, svc := range svcs {
+		serviceName := svc.GetName()
+		serviceNS := svc.GetNamespace()
+
+		labels, values := prom.KubeLabelsToLabels(svc.Spec.Selector)
+		if len(labels) > 0 {
+			m := newServiceSelectorLabelsMetric(serviceName, serviceNS, "service_selector_labels", labels, values)
+			ch <- m
+		}
+	}
+}
+
+//--------------------------------------------------------------------------
+//  ServiceSelectorLabelsMetric
+//--------------------------------------------------------------------------
+
+// ServiceSelectorLabelsMetric is a prometheus.Metric used to encode service selector labels
+type ServiceSelectorLabelsMetric struct {
+	fqName      string
+	help        string
+	labelNames  []string
+	labelValues []string
+	serviceName string
+	namespace   string
+}
+
+// Creates a new ServiceMetric, implementation of prometheus.Metric
+func newServiceSelectorLabelsMetric(name, namespace, fqname string, labelNames, labelvalues []string) ServiceSelectorLabelsMetric {
+	return ServiceSelectorLabelsMetric{
+		fqName:      fqname,
+		labelNames:  labelNames,
+		labelValues: labelvalues,
+		help:        "service_selector_labels Service Selector Labels",
+		serviceName: name,
+		namespace:   namespace,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (s ServiceSelectorLabelsMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{
+		"service":   s.serviceName,
+		"namespace": s.namespace,
+	}
+	return prometheus.NewDesc(s.fqName, s.help, s.labelNames, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data
+// transmission object.
+func (s ServiceSelectorLabelsMetric) Write(m *dto.Metric) error {
+	h := float64(1)
+	m.Gauge = &dto.Gauge{
+		Value: &h,
+	}
+	var labels []*dto.LabelPair
+	for i := range s.labelNames {
+		labels = append(labels, &dto.LabelPair{
+			Name:  &s.labelNames[i],
+			Value: &s.labelValues[i],
+		})
+	}
+	labels = append(labels, &dto.LabelPair{
+		Name:  toStringPtr("namespace"),
+		Value: &s.namespace,
+	})
+	labels = append(labels, &dto.LabelPair{
+		Name:  toStringPtr("service"),
+		Value: &s.serviceName,
+	})
+	m.Label = labels
+	return nil
+}

+ 101 - 0
pkg/metrics/statefulsetmetrics.go

@@ -0,0 +1,101 @@
+package metrics
+
+import (
+	"github.com/kubecost/cost-model/pkg/clustercache"
+	"github.com/kubecost/cost-model/pkg/prom"
+
+	"github.com/prometheus/client_golang/prometheus"
+	dto "github.com/prometheus/client_model/go"
+)
+
+//--------------------------------------------------------------------------
+//  KubecostStatefulsetCollector
+//--------------------------------------------------------------------------
+
+// StatefulsetCollector is a prometheus collector that generates StatefulsetMetrics
+type KubecostStatefulsetCollector struct {
+	KubeClusterCache clustercache.ClusterCache
+}
+
+// Describe sends the super-set of all possible descriptors of metrics
+// collected by this Collector.
+func (sc KubecostStatefulsetCollector) Describe(ch chan<- *prometheus.Desc) {
+	ch <- prometheus.NewDesc("statefulSet_match_labels", "statfulSet match labels", []string{}, nil)
+}
+
+// Collect is called by the Prometheus registry when collecting metrics.
+func (sc KubecostStatefulsetCollector) Collect(ch chan<- prometheus.Metric) {
+	ds := sc.KubeClusterCache.GetAllStatefulSets()
+	for _, statefulset := range ds {
+		statefulsetName := statefulset.GetName()
+		statefulsetNS := statefulset.GetNamespace()
+
+		labels, values := prom.KubeLabelsToLabels(statefulset.Spec.Selector.MatchLabels)
+		if len(labels) > 0 {
+			m := newStatefulsetMatchLabelsMetric(statefulsetName, statefulsetNS, "statefulSet_match_labels", labels, values)
+			ch <- m
+		}
+	}
+}
+
+//--------------------------------------------------------------------------
+//  StatefulsetMatchLabelsMetric
+//--------------------------------------------------------------------------
+
+// StatefulsetMetric is a prometheus.Metric used to encode statefulset match labels
+type StatefulsetMatchLabelsMetric struct {
+	fqName          string
+	help            string
+	labelNames      []string
+	labelValues     []string
+	statefulsetName string
+	namespace       string
+}
+
+// Creates a new StatefulsetMetric, implementation of prometheus.Metric
+func newStatefulsetMatchLabelsMetric(name, namespace, fqname string, labelNames, labelvalues []string) StatefulsetMatchLabelsMetric {
+	return StatefulsetMatchLabelsMetric{
+		fqName:          fqname,
+		labelNames:      labelNames,
+		labelValues:     labelvalues,
+		help:            "statefulSet_match_labels StatefulSet Match Labels",
+		statefulsetName: name,
+		namespace:       namespace,
+	}
+}
+
+// Desc returns the descriptor for the Metric. This method idempotently
+// returns the same descriptor throughout the lifetime of the Metric.
+func (s StatefulsetMatchLabelsMetric) Desc() *prometheus.Desc {
+	l := prometheus.Labels{
+		"statefulSet": s.statefulsetName,
+		"namespace":   s.namespace,
+	}
+	return prometheus.NewDesc(s.fqName, s.help, s.labelNames, l)
+}
+
+// Write encodes the Metric into a "Metric" Protocol Buffer data
+// transmission object.
+func (s StatefulsetMatchLabelsMetric) Write(m *dto.Metric) error {
+	h := float64(1)
+	m.Gauge = &dto.Gauge{
+		Value: &h,
+	}
+	var labels []*dto.LabelPair
+	for i := range s.labelNames {
+		labels = append(labels, &dto.LabelPair{
+			Name:  &s.labelNames[i],
+			Value: &s.labelValues[i],
+		})
+	}
+	labels = append(labels, &dto.LabelPair{
+		Name:  toStringPtr("namespace"),
+		Value: &s.namespace,
+	})
+	labels = append(labels, &dto.LabelPair{
+		Name:  toStringPtr("statefulSet"),
+		Value: &s.statefulsetName,
+	})
+	m.Label = labels
+	return nil
+}

Некоторые файлы не были показаны из-за большого количества измененных файлов