From 99175c56a00cd53e17b7c49fafeadf2b0ec05225 Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Sun, 7 Apr 2019 00:49:19 -0400 Subject: [PATCH 1/2] Adhere to code style --- .../kubernetes_state/__init__.py | 5 +- .../kubernetes_state/kubernetes_state.py | 442 +++++++++--------- kubernetes_state/setup.py | 10 +- .../tests/test_kubernetes_state.py | 191 ++++---- kubernetes_state/tox.ini | 11 +- 5 files changed, 338 insertions(+), 321 deletions(-) diff --git a/kubernetes_state/datadog_checks/kubernetes_state/__init__.py b/kubernetes_state/datadog_checks/kubernetes_state/__init__.py index ceb77d96cbc2e..24c3d06d5c433 100644 --- a/kubernetes_state/datadog_checks/kubernetes_state/__init__.py +++ b/kubernetes_state/datadog_checks/kubernetes_state/__init__.py @@ -4,7 +4,4 @@ from .__about__ import __version__ from .kubernetes_state import KubernetesState -__all__ = [ - '__version__', - 'KubernetesState' -] +__all__ = ['__version__', 'KubernetesState'] diff --git a/kubernetes_state/datadog_checks/kubernetes_state/kubernetes_state.py b/kubernetes_state/datadog_checks/kubernetes_state/kubernetes_state.py index ef63782b1cd55..04b9115c65211 100644 --- a/kubernetes_state/datadog_checks/kubernetes_state/kubernetes_state.py +++ b/kubernetes_state/datadog_checks/kubernetes_state/kubernetes_state.py @@ -4,18 +4,20 @@ import re import time -from collections import defaultdict, Counter +from collections import Counter, defaultdict from copy import deepcopy + from six import iteritems -from datadog_checks.errors import CheckException from datadog_checks.checks.openmetrics import OpenMetricsBaseCheck from datadog_checks.config import is_affirmative +from datadog_checks.errors import CheckException try: # this module is only available in agent 6 from datadog_agent import get_clustername except ImportError: + def get_clustername(): return "" @@ -32,6 +34,7 @@ class KubernetesState(OpenMetricsBaseCheck): Collect kube-state-metrics metrics in the Prometheus format See https://github.com/kubernetes/kube-state-metrics """ + DEFAULT_METRIC_LIMIT = 0 def __init__(self, name, init_config, agentConfig, instances=None): @@ -42,17 +45,9 @@ def __init__(self, name, init_config, agentConfig, instances=None): generic_instances = [kubernetes_state_instance] super(KubernetesState, self).__init__(name, init_config, agentConfig, instances=generic_instances) - self.condition_to_status_positive = { - 'true': self.OK, - 'false': self.CRITICAL, - 'unknown': self.UNKNOWN - } + self.condition_to_status_positive = {'true': self.OK, 'false': self.CRITICAL, 'unknown': self.UNKNOWN} - self.condition_to_status_negative = { - 'true': self.CRITICAL, - 'false': self.OK, - 'unknown': self.UNKNOWN - } + self.condition_to_status_negative = {'true': self.CRITICAL, 'false': self.OK, 'unknown': self.UNKNOWN} # Parameters for the count_objects_by_tags method self.object_count_params = { @@ -60,10 +55,7 @@ def __init__(self, name, init_config, agentConfig, instances=None): 'metric_name': 'persistentvolumes.by_phase', 'allowed_labels': ['storageclass', 'phase'], }, - 'kube_service_spec_type': { - 'metric_name': 'service.count', - 'allowed_labels': ['namespace', 'type'], - }, + 'kube_service_spec_type': {'metric_name': 'service.count', 'allowed_labels': ['namespace', 'type']}, } self.METRIC_TRANSFORMERS = { @@ -116,157 +108,154 @@ def _create_kubernetes_state_prometheus_instance(self, instance): extra_labels = ksm_instance.get('label_joins', {}) hostname_override = is_affirmative(ksm_instance.get('hostname_override', True)) - ksm_instance.update({ - 'namespace': 'kubernetes_state', - 'metrics': [{ - 'kube_daemonset_status_current_number_scheduled': 'daemonset.scheduled', - 'kube_daemonset_status_desired_number_scheduled': 'daemonset.desired', - 'kube_daemonset_status_number_misscheduled': 'daemonset.misscheduled', - 'kube_daemonset_status_number_ready': 'daemonset.ready', - 'kube_daemonset_updated_number_scheduled': 'daemonset.updated', - 'kube_deployment_spec_paused': 'deployment.paused', - 'kube_deployment_spec_replicas': 'deployment.replicas_desired', - 'kube_deployment_spec_strategy_rollingupdate_max_unavailable': 'deployment.rollingupdate.max_unavailable', # noqa: E501 - 'kube_deployment_status_replicas': 'deployment.replicas', - 'kube_deployment_status_replicas_available': 'deployment.replicas_available', - 'kube_deployment_status_replicas_unavailable': 'deployment.replicas_unavailable', - 'kube_deployment_status_replicas_updated': 'deployment.replicas_updated', - 'kube_endpoint_address_available': 'endpoint.address_available', - 'kube_endpoint_address_not_ready': 'endpoint.address_not_ready', - 'kube_endpoint_created': 'endpoint.created', - 'kube_hpa_spec_min_replicas': 'hpa.min_replicas', - 'kube_hpa_spec_max_replicas': 'hpa.max_replicas', - 'kube_hpa_status_desired_replicas': 'hpa.desired_replicas', - 'kube_hpa_status_current_replicas': 'hpa.current_replicas', - 'kube_hpa_status_condition': 'hpa.condition', - 'kube_node_status_allocatable_cpu_cores': 'node.cpu_allocatable', - 'kube_node_status_allocatable_memory_bytes': 'node.memory_allocatable', - 'kube_node_status_allocatable_pods': 'node.pods_allocatable', - 'kube_node_status_capacity_cpu_cores': 'node.cpu_capacity', - 'kube_node_status_capacity_memory_bytes': 'node.memory_capacity', - 'kube_node_status_capacity_pods': 'node.pods_capacity', - 'kube_node_status_allocatable_nvidia_gpu_cards': 'node.gpu.cards_allocatable', - 'kube_node_status_capacity_nvidia_gpu_cards': 'node.gpu.cards_capacity', - 'kube_pod_container_status_terminated': 'container.terminated', - 'kube_pod_container_status_waiting': 'container.waiting', - 'kube_persistentvolumeclaim_status_phase': 'persistentvolumeclaim.status', - 'kube_persistentvolumeclaim_resource_requests_storage_bytes': 'persistentvolumeclaim.request_storage', - 'kube_pod_container_resource_limits_cpu_cores': 'container.cpu_limit', - 'kube_pod_container_resource_limits_memory_bytes': 'container.memory_limit', - 'kube_pod_container_resource_requests_cpu_cores': 'container.cpu_requested', - 'kube_pod_container_resource_requests_memory_bytes': 'container.memory_requested', - 'kube_pod_container_status_ready': 'container.ready', - 'kube_pod_container_status_restarts': 'container.restarts', # up to kube-state-metrics 1.1.x - 'kube_pod_container_status_restarts_total': 'container.restarts', # from kube-state-metrics 1.2.0 - 'kube_pod_container_status_running': 'container.running', - 'kube_pod_container_resource_requests_nvidia_gpu_devices': 'container.gpu.request', - 'kube_pod_container_resource_limits_nvidia_gpu_devices': 'container.gpu.limit', - 'kube_pod_status_ready': 'pod.ready', - 'kube_pod_status_scheduled': 'pod.scheduled', - 'kube_poddisruptionbudget_status_current_healthy': 'pdb.pods_healthy', - 'kube_poddisruptionbudget_status_desired_healthy': 'pdb.pods_desired', - 'kube_poddisruptionbudget_status_pod_disruptions_allowed': 'pdb.disruptions_allowed', - 'kube_poddisruptionbudget_status_expected_pods': 'pdb.pods_total', - 'kube_replicaset_spec_replicas': 'replicaset.replicas_desired', - 'kube_replicaset_status_fully_labeled_replicas': 'replicaset.fully_labeled_replicas', - 'kube_replicaset_status_ready_replicas': 'replicaset.replicas_ready', - 'kube_replicaset_status_replicas': 'replicaset.replicas', - 'kube_replicationcontroller_spec_replicas': 'replicationcontroller.replicas_desired', - 'kube_replicationcontroller_status_available_replicas': 'replicationcontroller.replicas_available', - 'kube_replicationcontroller_status_fully_labeled_replicas': 'replicationcontroller.fully_labeled_replicas', # noqa: E501 - 'kube_replicationcontroller_status_ready_replicas': 'replicationcontroller.replicas_ready', - 'kube_replicationcontroller_status_replicas': 'replicationcontroller.replicas', - 'kube_statefulset_replicas': 'statefulset.replicas_desired', - 'kube_statefulset_status_replicas': 'statefulset.replicas', - 'kube_statefulset_status_replicas_current': 'statefulset.replicas_current', - 'kube_statefulset_status_replicas_ready': 'statefulset.replicas_ready', - 'kube_statefulset_status_replicas_updated': 'statefulset.replicas_updated', - }], - 'ignore_metrics': [ - # _info, _labels and _created don't convey any metric - 'kube_cronjob_info', - 'kube_cronjob_created', - 'kube_daemonset_created', - 'kube_deployment_created', - 'kube_deployment_labels', - 'kube_job_created', - 'kube_job_info', - 'kube_limitrange_created', - 'kube_namespace_created', - 'kube_namespace_labels', - 'kube_node_created', - 'kube_node_info', - 'kube_node_labels', - 'kube_pod_created' - 'kube_pod_container_info', - 'kube_pod_info', - 'kube_pod_owner', - 'kube_pod_start_time', - 'kube_pod_labels', - 'kube_poddisruptionbudget_created', - 'kube_replicaset_created', - 'kube_replicationcontroller_created', - 'kube_resourcequota_created', - 'kube_replicaset_owner', - 'kube_service_created', - 'kube_service_info', - 'kube_service_labels', - 'kube_service_spec_external_ip', - 'kube_service_status_load_balancer_ingress', - 'kube_statefulset_labels', - 'kube_statefulset_created', - 'kube_statefulset_status_current_revision', - 'kube_statefulset_status_update_revision', - # Already provided by the kubelet integration - 'kube_pod_container_status_last_terminated_reason', - # _generation metrics are more metadata than metrics, no real use case for now - 'kube_daemonset_metadata_generation', - 'kube_deployment_metadata_generation', - 'kube_deployment_status_observed_generation', - 'kube_replicaset_metadata_generation', - 'kube_replicaset_status_observed_generation', - 'kube_replicationcontroller_metadata_generation', - 'kube_replicationcontroller_status_observed_generation', - 'kube_statefulset_metadata_generation', - 'kube_statefulset_status_observed_generation', - 'kube_hpa_metadata_generation', - # kube_node_status_phase and kube_namespace_status_phase have no use case as a service check - 'kube_namespace_status_phase', - 'kube_node_status_phase', - # These CronJob and Job metrics need use cases to determine how do implement - 'kube_cronjob_status_active', - 'kube_cronjob_status_last_schedule_time', - 'kube_cronjob_spec_suspend', - 'kube_cronjob_spec_starting_deadline_seconds', - 'kube_job_spec_active_dealine_seconds', - 'kube_job_spec_completions', - 'kube_job_spec_parallelism', - 'kube_job_status_active', - 'kube_job_status_completion_time', # We could compute the duration=completion-start as a gauge - 'kube_job_status_start_time', - ], - 'label_joins': { - 'kube_pod_info': { - 'label_to_match': 'pod', - 'labels_to_get': ['node'] - }, - 'kube_pod_status_phase': { - 'label_to_match': 'pod', - 'labels_to_get': ['phase'] + ksm_instance.update( + { + 'namespace': 'kubernetes_state', + 'metrics': [ + { + 'kube_daemonset_status_current_number_scheduled': 'daemonset.scheduled', + 'kube_daemonset_status_desired_number_scheduled': 'daemonset.desired', + 'kube_daemonset_status_number_misscheduled': 'daemonset.misscheduled', + 'kube_daemonset_status_number_ready': 'daemonset.ready', + 'kube_daemonset_updated_number_scheduled': 'daemonset.updated', + 'kube_deployment_spec_paused': 'deployment.paused', + 'kube_deployment_spec_replicas': 'deployment.replicas_desired', + 'kube_deployment_spec_strategy_rollingupdate_max_unavailable': 'deployment.rollingupdate.max_unavailable', # noqa: E501 + 'kube_deployment_status_replicas': 'deployment.replicas', + 'kube_deployment_status_replicas_available': 'deployment.replicas_available', + 'kube_deployment_status_replicas_unavailable': 'deployment.replicas_unavailable', + 'kube_deployment_status_replicas_updated': 'deployment.replicas_updated', + 'kube_endpoint_address_available': 'endpoint.address_available', + 'kube_endpoint_address_not_ready': 'endpoint.address_not_ready', + 'kube_endpoint_created': 'endpoint.created', + 'kube_hpa_spec_min_replicas': 'hpa.min_replicas', + 'kube_hpa_spec_max_replicas': 'hpa.max_replicas', + 'kube_hpa_status_desired_replicas': 'hpa.desired_replicas', + 'kube_hpa_status_current_replicas': 'hpa.current_replicas', + 'kube_hpa_status_condition': 'hpa.condition', + 'kube_node_status_allocatable_cpu_cores': 'node.cpu_allocatable', + 'kube_node_status_allocatable_memory_bytes': 'node.memory_allocatable', + 'kube_node_status_allocatable_pods': 'node.pods_allocatable', + 'kube_node_status_capacity_cpu_cores': 'node.cpu_capacity', + 'kube_node_status_capacity_memory_bytes': 'node.memory_capacity', + 'kube_node_status_capacity_pods': 'node.pods_capacity', + 'kube_node_status_allocatable_nvidia_gpu_cards': 'node.gpu.cards_allocatable', + 'kube_node_status_capacity_nvidia_gpu_cards': 'node.gpu.cards_capacity', + 'kube_pod_container_status_terminated': 'container.terminated', + 'kube_pod_container_status_waiting': 'container.waiting', + 'kube_persistentvolumeclaim_status_phase': 'persistentvolumeclaim.status', + 'kube_persistentvolumeclaim_resource_requests_storage_bytes': 'persistentvolumeclaim.request_storage', + 'kube_pod_container_resource_limits_cpu_cores': 'container.cpu_limit', + 'kube_pod_container_resource_limits_memory_bytes': 'container.memory_limit', + 'kube_pod_container_resource_requests_cpu_cores': 'container.cpu_requested', + 'kube_pod_container_resource_requests_memory_bytes': 'container.memory_requested', + 'kube_pod_container_status_ready': 'container.ready', + 'kube_pod_container_status_restarts': 'container.restarts', # up to kube-state-metrics 1.1.x + 'kube_pod_container_status_restarts_total': 'container.restarts', # from kube-state-metrics 1.2.0 + 'kube_pod_container_status_running': 'container.running', + 'kube_pod_container_resource_requests_nvidia_gpu_devices': 'container.gpu.request', + 'kube_pod_container_resource_limits_nvidia_gpu_devices': 'container.gpu.limit', + 'kube_pod_status_ready': 'pod.ready', + 'kube_pod_status_scheduled': 'pod.scheduled', + 'kube_poddisruptionbudget_status_current_healthy': 'pdb.pods_healthy', + 'kube_poddisruptionbudget_status_desired_healthy': 'pdb.pods_desired', + 'kube_poddisruptionbudget_status_pod_disruptions_allowed': 'pdb.disruptions_allowed', + 'kube_poddisruptionbudget_status_expected_pods': 'pdb.pods_total', + 'kube_replicaset_spec_replicas': 'replicaset.replicas_desired', + 'kube_replicaset_status_fully_labeled_replicas': 'replicaset.fully_labeled_replicas', + 'kube_replicaset_status_ready_replicas': 'replicaset.replicas_ready', + 'kube_replicaset_status_replicas': 'replicaset.replicas', + 'kube_replicationcontroller_spec_replicas': 'replicationcontroller.replicas_desired', + 'kube_replicationcontroller_status_available_replicas': 'replicationcontroller.replicas_available', + 'kube_replicationcontroller_status_fully_labeled_replicas': 'replicationcontroller.fully_labeled_replicas', # noqa: E501 + 'kube_replicationcontroller_status_ready_replicas': 'replicationcontroller.replicas_ready', + 'kube_replicationcontroller_status_replicas': 'replicationcontroller.replicas', + 'kube_statefulset_replicas': 'statefulset.replicas_desired', + 'kube_statefulset_status_replicas': 'statefulset.replicas', + 'kube_statefulset_status_replicas_current': 'statefulset.replicas_current', + 'kube_statefulset_status_replicas_ready': 'statefulset.replicas_ready', + 'kube_statefulset_status_replicas_updated': 'statefulset.replicas_updated', + } + ], + 'ignore_metrics': [ + # _info, _labels and _created don't convey any metric + 'kube_cronjob_info', + 'kube_cronjob_created', + 'kube_daemonset_created', + 'kube_deployment_created', + 'kube_deployment_labels', + 'kube_job_created', + 'kube_job_info', + 'kube_limitrange_created', + 'kube_namespace_created', + 'kube_namespace_labels', + 'kube_node_created', + 'kube_node_info', + 'kube_node_labels', + 'kube_pod_created' 'kube_pod_container_info', + 'kube_pod_info', + 'kube_pod_owner', + 'kube_pod_start_time', + 'kube_pod_labels', + 'kube_poddisruptionbudget_created', + 'kube_replicaset_created', + 'kube_replicationcontroller_created', + 'kube_resourcequota_created', + 'kube_replicaset_owner', + 'kube_service_created', + 'kube_service_info', + 'kube_service_labels', + 'kube_service_spec_external_ip', + 'kube_service_status_load_balancer_ingress', + 'kube_statefulset_labels', + 'kube_statefulset_created', + 'kube_statefulset_status_current_revision', + 'kube_statefulset_status_update_revision', + # Already provided by the kubelet integration + 'kube_pod_container_status_last_terminated_reason', + # _generation metrics are more metadata than metrics, no real use case for now + 'kube_daemonset_metadata_generation', + 'kube_deployment_metadata_generation', + 'kube_deployment_status_observed_generation', + 'kube_replicaset_metadata_generation', + 'kube_replicaset_status_observed_generation', + 'kube_replicationcontroller_metadata_generation', + 'kube_replicationcontroller_status_observed_generation', + 'kube_statefulset_metadata_generation', + 'kube_statefulset_status_observed_generation', + 'kube_hpa_metadata_generation', + # kube_node_status_phase and kube_namespace_status_phase have no use case as a service check + 'kube_namespace_status_phase', + 'kube_node_status_phase', + # These CronJob and Job metrics need use cases to determine how do implement + 'kube_cronjob_status_active', + 'kube_cronjob_status_last_schedule_time', + 'kube_cronjob_spec_suspend', + 'kube_cronjob_spec_starting_deadline_seconds', + 'kube_job_spec_active_dealine_seconds', + 'kube_job_spec_completions', + 'kube_job_spec_parallelism', + 'kube_job_status_active', + 'kube_job_status_completion_time', # We could compute the duration=completion-start as a gauge + 'kube_job_status_start_time', + ], + 'label_joins': { + 'kube_pod_info': {'label_to_match': 'pod', 'labels_to_get': ['node']}, + 'kube_pod_status_phase': {'label_to_match': 'pod', 'labels_to_get': ['phase']}, + 'kube_persistentvolume_info': { + 'label_to_match': 'persistentvolume', + 'labels_to_get': ['storageclass'], + }, + 'kube_persistentvolumeclaim_info': { + 'label_to_match': 'persistentvolumeclaim', + 'labels_to_get': ['storageclass'], + }, }, - 'kube_persistentvolume_info': { - 'label_to_match': 'persistentvolume', - 'labels_to_get': ['storageclass'] - }, - 'kube_persistentvolumeclaim_info': { - 'label_to_match': 'persistentvolumeclaim', - 'labels_to_get': ['storageclass'] - } - }, - # Defaults that were set when kubernetes_state was based on PrometheusCheck - 'send_monotonic_counter': ksm_instance.get('send_monotonic_counter', False), - 'health_service_check': ksm_instance.get('health_service_check', False) - }) + # Defaults that were set when kubernetes_state was based on PrometheusCheck + 'send_monotonic_counter': ksm_instance.get('send_monotonic_counter', False), + 'health_service_check': ksm_instance.get('health_service_check', False), + } + ) ksm_instance['prometheus_url'] = endpoint ksm_instance['label_joins'].update(extra_labels) @@ -304,8 +293,7 @@ def _condition_to_service_check(self, sample, sc_name, mapping, tags=None): if condition in mapping: self.service_check(sc_name, mapping[condition], tags=tags) else: - self.log.debug("Unable to handle %s - unknown condition %s" - % (sc_name, condition)) + self.log.debug("Unable to handle %s - unknown condition %s" % (sc_name, condition)) def _condition_to_tag_check(self, sample, base_sc_name, mapping, scraper_config, tags=None): """ @@ -342,29 +330,28 @@ def _condition_to_tag_check(self, sample, base_sc_name, mapping, scraper_config, def _get_metric_condition_map(self, base_sc_name, labels): if base_sc_name == 'kubernetes_state.node': switch = { - 'Ready': { - 'service_check_name': base_sc_name + '.ready', - 'mapping': self.condition_to_status_positive - }, + 'Ready': {'service_check_name': base_sc_name + '.ready', 'mapping': self.condition_to_status_positive}, 'OutOfDisk': { 'service_check_name': base_sc_name + '.out_of_disk', - 'mapping': self.condition_to_status_negative + 'mapping': self.condition_to_status_negative, }, 'DiskPressure': { 'service_check_name': base_sc_name + '.disk_pressure', - 'mapping': self.condition_to_status_negative + 'mapping': self.condition_to_status_negative, }, 'NetworkUnavailable': { 'service_check_name': base_sc_name + '.network_unavailable', - 'mapping': self.condition_to_status_negative + 'mapping': self.condition_to_status_negative, }, 'MemoryPressure': { 'service_check_name': base_sc_name + '.memory_pressure', - 'mapping': self.condition_to_status_negative - } + 'mapping': self.condition_to_status_negative, + }, } - return (labels.get('status'), switch.get(labels.get('condition'), - {'service_check_name': None, 'mapping': None})) + return ( + labels.get('status'), + switch.get(labels.get('condition'), {'service_check_name': None, 'mapping': None}), + ) def _format_tag(self, name, value, scraper_config): """ @@ -407,15 +394,16 @@ def kube_pod_status_phase(self, metric, scraper_config): # pod granularity available in the service checks tags = [ self._label_to_tag('namespace', sample[self.SAMPLE_LABELS], scraper_config), - self._label_to_tag('phase', sample[self.SAMPLE_LABELS], scraper_config) + self._label_to_tag('phase', sample[self.SAMPLE_LABELS], scraper_config), ] + scraper_config['custom_tags'] status_phase_counter[tuple(sorted(tags))] += sample[self.SAMPLE_VALUE] for tags, count in iteritems(status_phase_counter): self.gauge(metric_name, count, tags=list(tags)) - def _submit_metric_kube_pod_container_status_reason(self, metric, metric_suffix, whitelisted_status_reasons, - scraper_config): + def _submit_metric_kube_pod_container_status_reason( + self, metric, metric_suffix, whitelisted_status_reasons, scraper_config + ): metric_name = scraper_config['namespace'] + metric_suffix for sample in metric.samples: @@ -430,8 +418,9 @@ def _submit_metric_kube_pod_container_status_reason(self, metric, metric_suffix, continue if 'container' in sample[self.SAMPLE_LABELS]: - tags.append(self._format_tag('kube_container_name', sample[self.SAMPLE_LABELS]['container'], - scraper_config)) + tags.append( + self._format_tag('kube_container_name', sample[self.SAMPLE_LABELS]['container'], scraper_config) + ) if 'namespace' in sample[self.SAMPLE_LABELS]: tags.append(self._format_tag('namespace', sample[self.SAMPLE_LABELS]['namespace'], scraper_config)) @@ -439,16 +428,22 @@ def _submit_metric_kube_pod_container_status_reason(self, metric, metric_suffix, if 'pod' in sample[self.SAMPLE_LABELS]: tags.append(self._format_tag('pod', sample[self.SAMPLE_LABELS]['pod'], scraper_config)) - self.gauge(metric_name, sample[self.SAMPLE_VALUE], tags + scraper_config['custom_tags'], - hostname=self.get_hostname_for_sample(sample, scraper_config)) + self.gauge( + metric_name, + sample[self.SAMPLE_VALUE], + tags + scraper_config['custom_tags'], + hostname=self.get_hostname_for_sample(sample, scraper_config), + ) def kube_pod_container_status_waiting_reason(self, metric, scraper_config): - self._submit_metric_kube_pod_container_status_reason(metric, '.container.status_report.count.waiting', - WHITELISTED_WAITING_REASONS, scraper_config) + self._submit_metric_kube_pod_container_status_reason( + metric, '.container.status_report.count.waiting', WHITELISTED_WAITING_REASONS, scraper_config + ) def kube_pod_container_status_terminated_reason(self, metric, scraper_config): - self._submit_metric_kube_pod_container_status_reason(metric, '.container.status_report.count.terminated', - WHITELISTED_TERMINATED_REASONS, scraper_config) + self._submit_metric_kube_pod_container_status_reason( + metric, '.container.status_report.count.terminated', WHITELISTED_TERMINATED_REASONS, scraper_config + ) def kube_cronjob_next_schedule_time(self, metric, scraper_config): """ Time until the next schedule """ @@ -457,8 +452,10 @@ def kube_cronjob_next_schedule_time(self, metric, scraper_config): curr_time = int(time.time()) for sample in metric.samples: on_schedule = int(sample[self.SAMPLE_VALUE]) - curr_time - tags = [self._format_tag(label_name, label_value, scraper_config) - for label_name, label_value in iteritems(sample[self.SAMPLE_LABELS])] + tags = [ + self._format_tag(label_name, label_value, scraper_config) + for label_name, label_value in iteritems(sample[self.SAMPLE_LABELS]) + ] tags += scraper_config['custom_tags'] if on_schedule < 0: message = "The service check scheduled at {} is {} seconds late".format( @@ -522,14 +519,19 @@ def kube_node_status_condition(self, metric, scraper_config): for sample in metric.samples: node_tag = self._label_to_tag("node", sample[self.SAMPLE_LABELS], scraper_config) - self._condition_to_tag_check(sample, base_check_name, self.condition_to_status_positive, scraper_config, - tags=[node_tag] + scraper_config['custom_tags']) + self._condition_to_tag_check( + sample, + base_check_name, + self.condition_to_status_positive, + scraper_config, + tags=[node_tag] + scraper_config['custom_tags'], + ) # Counts aggregated cluster-wide to avoid no-data issues on node churn, # node granularity available in the service checks tags = [ self._label_to_tag("condition", sample[self.SAMPLE_LABELS], scraper_config), - self._label_to_tag("status", sample[self.SAMPLE_LABELS], scraper_config) + self._label_to_tag("status", sample[self.SAMPLE_LABELS], scraper_config), ] + scraper_config['custom_tags'] by_condition_counter[tuple(sorted(tags))] += sample[self.SAMPLE_VALUE] @@ -541,40 +543,60 @@ def kube_node_status_ready(self, metric, scraper_config): service_check_name = scraper_config['namespace'] + '.node.ready' for sample in metric.samples: node_tag = self._label_to_tag("node", sample[self.SAMPLE_LABELS], scraper_config) - self._condition_to_service_check(sample, service_check_name, self.condition_to_status_positive, - tags=[node_tag] + scraper_config['custom_tags']) + self._condition_to_service_check( + sample, + service_check_name, + self.condition_to_status_positive, + tags=[node_tag] + scraper_config['custom_tags'], + ) def kube_node_status_out_of_disk(self, metric, scraper_config): """ Whether the node is out of disk space (legacy)""" service_check_name = scraper_config['namespace'] + '.node.out_of_disk' for sample in metric.samples: node_tag = self._label_to_tag("node", sample[self.SAMPLE_LABELS], scraper_config) - self._condition_to_service_check(sample, service_check_name, self.condition_to_status_negative, - tags=[node_tag] + scraper_config['custom_tags']) + self._condition_to_service_check( + sample, + service_check_name, + self.condition_to_status_negative, + tags=[node_tag] + scraper_config['custom_tags'], + ) def kube_node_status_memory_pressure(self, metric, scraper_config): """ Whether the node is in a memory pressure state (legacy)""" service_check_name = scraper_config['namespace'] + '.node.memory_pressure' for sample in metric.samples: node_tag = self._label_to_tag("node", sample[self.SAMPLE_LABELS], scraper_config) - self._condition_to_service_check(sample, service_check_name, self.condition_to_status_negative, - tags=[node_tag] + scraper_config['custom_tags']) + self._condition_to_service_check( + sample, + service_check_name, + self.condition_to_status_negative, + tags=[node_tag] + scraper_config['custom_tags'], + ) def kube_node_status_disk_pressure(self, metric, scraper_config): """ Whether the node is in a disk pressure state (legacy)""" service_check_name = scraper_config['namespace'] + '.node.disk_pressure' for sample in metric.samples: node_tag = self._label_to_tag("node", sample[self.SAMPLE_LABELS], scraper_config) - self._condition_to_service_check(sample, service_check_name, self.condition_to_status_negative, - tags=[node_tag] + scraper_config['custom_tags']) + self._condition_to_service_check( + sample, + service_check_name, + self.condition_to_status_negative, + tags=[node_tag] + scraper_config['custom_tags'], + ) def kube_node_status_network_unavailable(self, metric, scraper_config): """ Whether the node is in a network unavailable state (legacy)""" service_check_name = scraper_config['namespace'] + '.node.network_unavailable' for sample in metric.samples: node_tag = self._label_to_tag("node", sample[self.SAMPLE_LABELS], scraper_config) - self._condition_to_service_check(sample, service_check_name, self.condition_to_status_negative, - tags=[node_tag] + scraper_config['custom_tags']) + self._condition_to_service_check( + sample, + service_check_name, + self.condition_to_status_negative, + tags=[node_tag] + scraper_config['custom_tags'], + ) def kube_node_spec_unschedulable(self, metric, scraper_config): """ Whether a node can schedule new pods. """ @@ -582,8 +604,10 @@ def kube_node_spec_unschedulable(self, metric, scraper_config): statuses = ('schedulable', 'unschedulable') if metric.type in METRIC_TYPES: for sample in metric.samples: - tags = [self._format_tag(label_name, label_value, scraper_config) - for label_name, label_value in iteritems(sample[self.SAMPLE_LABELS])] + tags = [ + self._format_tag(label_name, label_value, scraper_config) + for label_name, label_value in iteritems(sample[self.SAMPLE_LABELS]) + ] tags += scraper_config['custom_tags'] status = statuses[int(sample[self.SAMPLE_VALUE])] # value can be 0 or 1 tags.append(self._format_tag('status', status, scraper_config)) @@ -601,7 +625,7 @@ def kube_resourcequota(self, metric, scraper_config): resource = sample[self.SAMPLE_LABELS].get("resource") tags = [ self._label_to_tag("namespace", sample[self.SAMPLE_LABELS], scraper_config), - self._label_to_tag("resourcequota", sample[self.SAMPLE_LABELS], scraper_config) + self._label_to_tag("resourcequota", sample[self.SAMPLE_LABELS], scraper_config), ] + scraper_config['custom_tags'] self.gauge(metric_base_name.format(resource, suffixes[mtype]), sample[self.SAMPLE_VALUE], tags) else: @@ -633,7 +657,7 @@ def kube_limitrange(self, metric, scraper_config): tags = [ self._label_to_tag("namespace", sample[self.SAMPLE_LABELS], scraper_config), self._label_to_tag("limitrange", sample[self.SAMPLE_LABELS], scraper_config), - self._label_to_tag("type", sample[self.SAMPLE_LABELS], scraper_config, tag_name="consumer_type") + self._label_to_tag("type", sample[self.SAMPLE_LABELS], scraper_config, tag_name="consumer_type"), ] + scraper_config['custom_tags'] self.gauge(metric_base_name.format(resource, constraint), sample[self.SAMPLE_VALUE], tags) else: diff --git a/kubernetes_state/setup.py b/kubernetes_state/setup.py index 469ca4918c247..1d837c5d64428 100644 --- a/kubernetes_state/setup.py +++ b/kubernetes_state/setup.py @@ -1,10 +1,11 @@ # (C) Datadog, Inc. 2018 # All rights reserved # Licensed under a 3-clause BSD style license (see LICENSE) -from setuptools import setup from codecs import open # To use a consistent encoding from os import path +from setuptools import setup + HERE = path.dirname(path.abspath(__file__)) # Get version info @@ -32,17 +33,13 @@ def get_requirements(fpath): long_description=long_description, long_description_content_type='text/markdown', keywords='datadog agent kubernetes_state check', - # The project's main homepage. url='https://github.com/DataDog/integrations-core', - # Author details author='Datadog', author_email='packages@datadoghq.com', - # License license='BSD', - # See https://pypi.python.org/pypi?%3Aaction=list_classifiers classifiers=[ 'Development Status :: 5 - Production/Stable', @@ -53,13 +50,10 @@ def get_requirements(fpath): 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', ], - # The package we're going to ship packages=['datadog_checks.kubernetes_state'], - # Run-time dependencies install_requires=[CHECKS_BASE_REQ], - # Extra files to ship with the wheel package include_package_data=True, ) diff --git a/kubernetes_state/tests/test_kubernetes_state.py b/kubernetes_state/tests/test_kubernetes_state.py index 4b106cd0eb326..d898ccc829f00 100644 --- a/kubernetes_state/tests/test_kubernetes_state.py +++ b/kubernetes_state/tests/test_kubernetes_state.py @@ -9,7 +9,6 @@ from datadog_checks.kubernetes_state import KubernetesState from datadog_checks.utils.common import ensure_unicode - HERE = os.path.dirname(os.path.abspath(__file__)) FIXTURES_PATH = os.path.join(HERE, 'fixtures') NAMESPACE = 'kubernetes_state' @@ -108,76 +107,66 @@ TAGS = { NAMESPACE + '.pod.ready': ['node:minikube'], NAMESPACE + '.pod.scheduled': ['node:minikube'], - NAMESPACE + '.nodes.by_condition': [ - 'condition:MemoryPressure', 'condition:DiskPressure', - 'condition:OutOfDisk', 'condition:Ready', - 'status:true', 'status:false', 'status:unknown', + NAMESPACE + + '.nodes.by_condition': [ + 'condition:MemoryPressure', + 'condition:DiskPressure', + 'condition:OutOfDisk', + 'condition:Ready', + 'status:true', + 'status:false', + 'status:unknown', ], - NAMESPACE + '.pod.status_phase': [ - 'phase:Pending', 'phase:Running', - 'phase:Failed', 'phase:Succeeded', - 'phase:Unknown', 'namespace:default', - 'namespace:kube-system' + NAMESPACE + + '.pod.status_phase': [ + 'phase:Pending', + 'phase:Running', + 'phase:Failed', + 'phase:Succeeded', + 'phase:Unknown', + 'namespace:default', + 'namespace:kube-system', ], - NAMESPACE + '.container.status_report.count.waiting': [ + NAMESPACE + + '.container.status_report.count.waiting': [ 'reason:ContainerCreating', 'reason:CrashLoopBackoff', # Lowercase "off" 'reason:CrashLoopBackOff', # Uppercase "Off" 'reason:ErrImagePull', 'reason:ImagePullBackoff', 'pod:kube-dns-1326421443-hj4hx', - 'pod:hello-1509998340-k4f8q' - ], - NAMESPACE + '.container.status_report.count.terminated': [ - 'pod:pod2', - ], - NAMESPACE + '.persistentvolumeclaim.request_storage': [ - 'storageclass:manual' + 'pod:hello-1509998340-k4f8q', ], - NAMESPACE + '.service.count': [ + NAMESPACE + '.container.status_report.count.terminated': ['pod:pod2'], + NAMESPACE + '.persistentvolumeclaim.request_storage': ['storageclass:manual'], + NAMESPACE + + '.service.count': [ 'namespace:kube-system', 'namespace:default', 'type:ClusterIP', 'type:NodePort', 'type:LoadBalancer', ], - NAMESPACE + '.job.failed': [ - 'job:hello', - 'job_name:hello2', - ], - NAMESPACE + '.job.succeeded': [ - 'job:hello', - 'job_name:hello2', - ], - NAMESPACE + '.hpa.condition': [ - 'namespace:default', - 'hpa:myhpa', - 'condition:true', - 'status:AbleToScale', - ], + NAMESPACE + '.job.failed': ['job:hello', 'job_name:hello2'], + NAMESPACE + '.job.succeeded': ['job:hello', 'job_name:hello2'], + NAMESPACE + '.hpa.condition': ['namespace:default', 'hpa:myhpa', 'condition:true', 'status:AbleToScale'], } JOINED_METRICS = { - NAMESPACE + '.deployment.replicas': [ - 'label_addonmanager_kubernetes_io_mode:Reconcile', 'deployment:kube-dns' - ], - NAMESPACE + '.deployment.replicas_available': [ - 'label_addonmanager_kubernetes_io_mode:Reconcile', 'deployment:kube-dns' - ], - NAMESPACE + '.deployment.replicas_unavailable': [ - 'label_addonmanager_kubernetes_io_mode:Reconcile', 'deployment:kube-dns' - ], - NAMESPACE + '.deployment.replicas_updated': [ - 'label_addonmanager_kubernetes_io_mode:Reconcile', 'deployment:kube-dns' - ], - NAMESPACE + '.deployment.replicas_desired': [ - 'label_addonmanager_kubernetes_io_mode:Reconcile', 'deployment:kube-dns' - ], - NAMESPACE + '.deployment.paused': [ - 'label_addonmanager_kubernetes_io_mode:Reconcile', 'deployment:kube-dns' - ], - NAMESPACE + '.deployment.rollingupdate.max_unavailable': [ - 'label_addonmanager_kubernetes_io_mode:Reconcile', 'deployment:kube-dns' + NAMESPACE + '.deployment.replicas': ['label_addonmanager_kubernetes_io_mode:Reconcile', 'deployment:kube-dns'], + NAMESPACE + + '.deployment.replicas_available': ['label_addonmanager_kubernetes_io_mode:Reconcile', 'deployment:kube-dns'], + NAMESPACE + + '.deployment.replicas_unavailable': ['label_addonmanager_kubernetes_io_mode:Reconcile', 'deployment:kube-dns'], + NAMESPACE + + '.deployment.replicas_updated': ['label_addonmanager_kubernetes_io_mode:Reconcile', 'deployment:kube-dns'], + NAMESPACE + + '.deployment.replicas_desired': ['label_addonmanager_kubernetes_io_mode:Reconcile', 'deployment:kube-dns'], + NAMESPACE + '.deployment.paused': ['label_addonmanager_kubernetes_io_mode:Reconcile', 'deployment:kube-dns'], + NAMESPACE + + '.deployment.rollingupdate.max_unavailable': [ + 'label_addonmanager_kubernetes_io_mode:Reconcile', + 'deployment:kube-dns', ], } @@ -185,7 +174,7 @@ NAMESPACE + '.pod.ready': 'minikube', NAMESPACE + '.pod.scheduled': 'minikube', NAMESPACE + '.container.status_report.count.waiting': 'minikube', - NAMESPACE + '.container.status_report.count.terminated': 'minikube' + NAMESPACE + '.container.status_report.count.terminated': 'minikube', } ZERO_METRICS = [ @@ -206,6 +195,7 @@ class MockResponse: MockResponse is used to simulate the object requests.Response commonly returned by requests.get """ + def __init__(self, content, content_type): self.content = content self.headers = {'Content-Type': content_type} @@ -220,11 +210,7 @@ def close(self): @pytest.fixture def instance(): - return { - 'host': 'foo', - 'kube_state_url': 'http://foo', - 'tags': ['optional:tag1'] - } + return {'host': 'foo', 'kube_state_url': 'http://foo', 'tags': ['optional:tag1']} @pytest.fixture @@ -263,36 +249,59 @@ def test_update_kube_state_metrics(aggregator, instance, check): aggregator.assert_service_check(NAMESPACE + '.node.disk_pressure', check.OK) # Make sure we send counts for all statuses to avoid no-data graphing issues - aggregator.assert_metric(NAMESPACE + '.nodes.by_condition', - tags=['condition:Ready', 'status:true', 'optional:tag1'], value=1) - aggregator.assert_metric(NAMESPACE + '.nodes.by_condition', - tags=['condition:Ready', 'status:false', 'optional:tag1'], value=0) - aggregator.assert_metric(NAMESPACE + '.nodes.by_condition', - tags=['condition:Ready', 'status:unknown', 'optional:tag1'], value=0) + aggregator.assert_metric( + NAMESPACE + '.nodes.by_condition', tags=['condition:Ready', 'status:true', 'optional:tag1'], value=1 + ) + aggregator.assert_metric( + NAMESPACE + '.nodes.by_condition', tags=['condition:Ready', 'status:false', 'optional:tag1'], value=0 + ) + aggregator.assert_metric( + NAMESPACE + '.nodes.by_condition', tags=['condition:Ready', 'status:unknown', 'optional:tag1'], value=0 + ) # Make sure we send counts for all phases to avoid no-data graphing issues - aggregator.assert_metric(NAMESPACE + '.pod.status_phase', - tags=['namespace:default', 'phase:Pending', 'optional:tag1'], value=1) - aggregator.assert_metric(NAMESPACE + '.pod.status_phase', - tags=['namespace:default', 'phase:Running', 'optional:tag1'], value=3) - aggregator.assert_metric(NAMESPACE + '.pod.status_phase', - tags=['namespace:default', 'phase:Succeeded', 'optional:tag1'], value=2) - aggregator.assert_metric(NAMESPACE + '.pod.status_phase', - tags=['namespace:default', 'phase:Failed', 'optional:tag1'], value=2) - aggregator.assert_metric(NAMESPACE + '.pod.status_phase', - tags=['namespace:default', 'phase:Unknown', 'optional:tag1'], value=1) + aggregator.assert_metric( + NAMESPACE + '.pod.status_phase', tags=['namespace:default', 'phase:Pending', 'optional:tag1'], value=1 + ) + aggregator.assert_metric( + NAMESPACE + '.pod.status_phase', tags=['namespace:default', 'phase:Running', 'optional:tag1'], value=3 + ) + aggregator.assert_metric( + NAMESPACE + '.pod.status_phase', tags=['namespace:default', 'phase:Succeeded', 'optional:tag1'], value=2 + ) + aggregator.assert_metric( + NAMESPACE + '.pod.status_phase', tags=['namespace:default', 'phase:Failed', 'optional:tag1'], value=2 + ) + aggregator.assert_metric( + NAMESPACE + '.pod.status_phase', tags=['namespace:default', 'phase:Unknown', 'optional:tag1'], value=1 + ) # Persistentvolume counts - aggregator.assert_metric(NAMESPACE + '.persistentvolumes.by_phase', - tags=['storageclass:local-data', 'phase:Available', 'optional:tag1'], value=0) - aggregator.assert_metric(NAMESPACE + '.persistentvolumes.by_phase', - tags=['storageclass:local-data', 'phase:Bound', 'optional:tag1'], value=2) - aggregator.assert_metric(NAMESPACE + '.persistentvolumes.by_phase', - tags=['storageclass:local-data', 'phase:Failed', 'optional:tag1'], value=0) - aggregator.assert_metric(NAMESPACE + '.persistentvolumes.by_phase', - tags=['storageclass:local-data', 'phase:Pending', 'optional:tag1'], value=0) - aggregator.assert_metric(NAMESPACE + '.persistentvolumes.by_phase', - tags=['storageclass:local-data', 'phase:Released', 'optional:tag1'], value=0) + aggregator.assert_metric( + NAMESPACE + '.persistentvolumes.by_phase', + tags=['storageclass:local-data', 'phase:Available', 'optional:tag1'], + value=0, + ) + aggregator.assert_metric( + NAMESPACE + '.persistentvolumes.by_phase', + tags=['storageclass:local-data', 'phase:Bound', 'optional:tag1'], + value=2, + ) + aggregator.assert_metric( + NAMESPACE + '.persistentvolumes.by_phase', + tags=['storageclass:local-data', 'phase:Failed', 'optional:tag1'], + value=0, + ) + aggregator.assert_metric( + NAMESPACE + '.persistentvolumes.by_phase', + tags=['storageclass:local-data', 'phase:Pending', 'optional:tag1'], + value=0, + ) + aggregator.assert_metric( + NAMESPACE + '.persistentvolumes.by_phase', + tags=['storageclass:local-data', 'phase:Released', 'optional:tag1'], + value=0, + ) for metric in METRICS: aggregator.assert_metric(metric, hostname=HOSTNAMES.get(metric, None)) @@ -327,7 +336,7 @@ def test_join_custom_labels(aggregator, instance, check): instance['label_joins'] = { 'kube_deployment_labels': { 'label_to_match': 'deployment', - 'labels_to_get': ['label_addonmanager_kubernetes_io_mode'] + 'labels_to_get': ['label_addonmanager_kubernetes_io_mode'], } } @@ -364,7 +373,9 @@ def test_disabling_hostname_override(instance): def test_pod_phase_gauges(aggregator, instance, check): for _ in range(2): check.check(instance) - aggregator.assert_metric(NAMESPACE + '.pod.status_phase', - tags=['namespace:default', 'phase:Running', 'optional:tag1'], value=3) - aggregator.assert_metric(NAMESPACE + '.pod.status_phase', - tags=['namespace:default', 'phase:Failed', 'optional:tag1'], value=2) + aggregator.assert_metric( + NAMESPACE + '.pod.status_phase', tags=['namespace:default', 'phase:Running', 'optional:tag1'], value=3 + ) + aggregator.assert_metric( + NAMESPACE + '.pod.status_phase', tags=['namespace:default', 'phase:Failed', 'optional:tag1'], value=2 + ) diff --git a/kubernetes_state/tox.ini b/kubernetes_state/tox.ini index 2e4e46245153c..867f79d1dfda1 100644 --- a/kubernetes_state/tox.ini +++ b/kubernetes_state/tox.ini @@ -3,9 +3,9 @@ minversion = 2.0 basepython = py37 envlist = py{27,37}-unit - flake8 [testenv] +dd_check_style = true usedevelop = true platform = linux|darwin|win32 deps = @@ -14,12 +14,3 @@ deps = commands = pip install -r requirements.in pytest -v - -[testenv:flake8] -skip_install = true -deps = flake8 -commands = flake8 . - -[flake8] -exclude = .eggs,.tox,build -max-line-length = 120 From 7933450e2f76433a148e5848386ee98760bdaefd Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Sun, 7 Apr 2019 18:15:17 -0400 Subject: [PATCH 2/2] fix --- .../datadog_checks/kubernetes_state/kubernetes_state.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kubernetes_state/datadog_checks/kubernetes_state/kubernetes_state.py b/kubernetes_state/datadog_checks/kubernetes_state/kubernetes_state.py index 04b9115c65211..ebc61d871bfe4 100644 --- a/kubernetes_state/datadog_checks/kubernetes_state/kubernetes_state.py +++ b/kubernetes_state/datadog_checks/kubernetes_state/kubernetes_state.py @@ -144,14 +144,14 @@ def _create_kubernetes_state_prometheus_instance(self, instance): 'kube_pod_container_status_terminated': 'container.terminated', 'kube_pod_container_status_waiting': 'container.waiting', 'kube_persistentvolumeclaim_status_phase': 'persistentvolumeclaim.status', - 'kube_persistentvolumeclaim_resource_requests_storage_bytes': 'persistentvolumeclaim.request_storage', + 'kube_persistentvolumeclaim_resource_requests_storage_bytes': 'persistentvolumeclaim.request_storage', # noqa: E501 'kube_pod_container_resource_limits_cpu_cores': 'container.cpu_limit', 'kube_pod_container_resource_limits_memory_bytes': 'container.memory_limit', 'kube_pod_container_resource_requests_cpu_cores': 'container.cpu_requested', 'kube_pod_container_resource_requests_memory_bytes': 'container.memory_requested', 'kube_pod_container_status_ready': 'container.ready', 'kube_pod_container_status_restarts': 'container.restarts', # up to kube-state-metrics 1.1.x - 'kube_pod_container_status_restarts_total': 'container.restarts', # from kube-state-metrics 1.2.0 + 'kube_pod_container_status_restarts_total': 'container.restarts', # noqa: E501, from kube-state-metrics 1.2.0 'kube_pod_container_status_running': 'container.running', 'kube_pod_container_resource_requests_nvidia_gpu_devices': 'container.gpu.request', 'kube_pod_container_resource_limits_nvidia_gpu_devices': 'container.gpu.limit', @@ -166,7 +166,7 @@ def _create_kubernetes_state_prometheus_instance(self, instance): 'kube_replicaset_status_ready_replicas': 'replicaset.replicas_ready', 'kube_replicaset_status_replicas': 'replicaset.replicas', 'kube_replicationcontroller_spec_replicas': 'replicationcontroller.replicas_desired', - 'kube_replicationcontroller_status_available_replicas': 'replicationcontroller.replicas_available', + 'kube_replicationcontroller_status_available_replicas': 'replicationcontroller.replicas_available', # noqa: E501 'kube_replicationcontroller_status_fully_labeled_replicas': 'replicationcontroller.fully_labeled_replicas', # noqa: E501 'kube_replicationcontroller_status_ready_replicas': 'replicationcontroller.replicas_ready', 'kube_replicationcontroller_status_replicas': 'replicationcontroller.replicas',