def generate_metrics(namespaces): metrics = [] if not namespaces: print("No namespaces specified, watching all namespaces\n") v1_namespaces = dyn_client.resources.get(api_version='v1', kind='Namespace') namespaces = [namespace.metadata.name for namespace in v1_namespaces.get( label_selector=pelorus.get_prod_label()).items] else: print("Watching namespaces: %s\n" % (namespaces)) for namespace in namespaces: v1_replicationcontrollers = dyn_client.resources.get(api_version='v1', kind='ReplicationController') replicationcontrollers = v1_replicationcontrollers.get(namespace=namespace, label_selector=pelorus.get_app_label()) for rc in replicationcontrollers.items: images = [image_sha(c.image) for c in rc.spec.template.spec.containers] # Since a commit will be built into a particular image and there could be multiple # containers (images) per pod, we will push one metric per image/container in the # pod template for i in images: if i is not None: metric = DeployTimeMetric(rc.metadata.name, namespace) metric.labels = rc.metadata.labels metric.deploy_time = rc.metadata.creationTimestamp metric.image_sha = i metrics.append(metric) return metrics
def generate_metrics(self) -> Iterable[CommitMetric]: """Method called by the collect to create a list of metrics to publish""" # This will loop and look at OCP builds (calls get_git_commit_time) if not self._namespaces: logging.info("No namespaces specified, watching all namespaces") v1_namespaces = self._kube_client.resources.get( api_version="v1", kind="Namespace" ) self._namespaces = [ namespace.metadata.name for namespace in v1_namespaces.get().items ] else: logging.info("Watching namespaces: %s" % (self._namespaces)) # Initialize metrics list metrics = [] for namespace in self._namespaces: # Initialized variables builds = [] apps = [] builds_by_app = {} app_label = pelorus.get_app_label() logging.debug( "Searching for builds with label: %s in namespace: %s" % (app_label, namespace) ) v1_builds = self._kube_client.resources.get( api_version="build.openshift.io/v1", kind="Build" ) # only use builds that have the app label builds = v1_builds.get(namespace=namespace, label_selector=app_label) # use a jsonpath expression to find all values for the app label jsonpath_str = ( "$['items'][*]['metadata']['labels']['" + str(app_label) + "']" ) jsonpath_expr = parse(jsonpath_str) found = jsonpath_expr.find(builds) apps = [match.value for match in found] if not apps: continue # remove duplicates apps = list(dict.fromkeys(apps)) builds_by_app = {} for app in apps: builds_by_app[app] = list( filter(lambda b: b.metadata.labels[app_label] == app, builds.items) ) metrics += self.get_metrics_from_apps(builds_by_app, namespace) return metrics
def generate_commit_metrics_list(self, namespaces): if not namespaces: logging.info("No namespaces specified, watching all namespaces") v1_namespaces = dyn_client.resources.get(api_version='v1', kind='Namespace') namespaces = [ namespace.metadata.name for namespace in v1_namespaces.get().items ] else: logging.info("Watching namespaces: %s" % (namespaces)) # Initialize metrics list metrics = [] for namespace in namespaces: # Initialized variables builds = [] apps = [] builds_by_app = {} app_label = pelorus.get_app_label() logging.info( "Searching for builds with label: %s in namespace: %s" % (app_label, namespace)) v1_builds = dyn_client.resources.get( api_version='build.openshift.io/v1', kind='Build') # only use builds that have the app label builds = v1_builds.get(namespace=namespace, label_selector=app_label) # use a jsonpath expression to find all values for the app label jsonpath_str = "$['items'][*]['metadata']['labels']['" + str( app_label) + "']" jsonpath_expr = parse(jsonpath_str) found = jsonpath_expr.find(builds) apps = [match.value for match in found] if not apps: continue # remove duplicates apps = list(dict.fromkeys(apps)) builds_by_app = {} for app in apps: builds_by_app[app] = list( filter(lambda b: b.metadata.labels[app_label] == app, builds.items)) metrics += self.get_metrics_from_apps(builds_by_app, namespace) return metrics
def get_replicas( dyn_client: DynamicClient, apiVersion: str, objectName: str ) -> dict[str, object]: """Process Replicas for given Api Version and Object type (ReplicaSet or ReplicationController)""" try: apiResource = dyn_client.resources.get(api_version=apiVersion, kind=objectName) replicationobjects = apiResource.get(label_selector=pelorus.get_app_label()) return { f"{replica.metadata.namespace}/{replica.metadata.name}": replica for replica in replicationobjects.items } except ResourceNotFoundError: logging.debug( "API Object not found for version: %s object: %s", apiVersion, objectName ) return {}
def get_replicas(apiVersion, objectName): # Process Replicas for given Api Version and Object type (ReplicaSet or ReplicationController) replicas = {} try: apiResource = dyn_client.resources.get(api_version=apiVersion, kind=objectName) replicationobjects = apiResource.get( label_selector=pelorus.get_app_label()) for replica in replicationobjects.items: replicas[replica.metadata.namespace + "/" + replica.metadata.name] = replica except ResourceNotFoundError: logging.debug("API Object not found for version: %s object: %s", apiVersion, objectName) pass return replicas
def generate_metrics( namespaces: NamespaceSpec, dyn_client: DynamicClient ) -> Iterable[DeployTimeMetric]: visited_replicas = set() def already_seen(full_path: str) -> bool: return full_path in visited_replicas def mark_as_seen(full_path: str): visited_replicas.add(full_path) logging.info("generate_metrics: start") v1_pods = dyn_client.resources.get(api_version="v1", kind="Pod") log_namespaces(namespaces) def in_namespace(namespace: str) -> bool: return (not namespaces) or namespace in namespaces pods = v1_pods.get( label_selector=pelorus.get_app_label(), field_selector="status.phase=Running" ).items replicas_dict = ( get_replicas(dyn_client, "v1", "ReplicationController") | get_replicas(dyn_client, "apps/v1", "ReplicaSet") | get_replicas(dyn_client, "extensions/v1beta1", "ReplicaSet") ) for pod in pods: namespace = pod.metadata.namespace owner_refs = pod.metadata.ownerReferences if not in_namespace(namespace) or not owner_refs: continue logging.debug( "Getting Replicas for pod: %s in namespace: %s", pod.metadata.name, pod.metadata.namespace, ) # Get deploytime from the owning controller of the pod. # We track all already-visited controllers to not duplicate metrics per-pod. for ref in owner_refs: full_path = f"{namespace}/{ref.name}" if ref.kind not in supported_replica_objects or already_seen(full_path): continue logging.debug( "Getting replica: %s, kind: %s, namespace: %s", ref.name, ref.kind, namespace, ) if not (rc := replicas_dict.get(full_path)): continue mark_as_seen(full_path) images = (sha for c in pod.spec.containers if (sha := image_sha(c.image)))
ref.kind, namespace, ) if not (rc := replicas_dict.get(full_path)): continue mark_as_seen(full_path) images = (sha for c in pod.spec.containers if (sha := image_sha(c.image))) # Since a commit will be built into a particular image and there could be multiple # containers (images) per pod, we will push one metric per image/container in the # pod template for sha in images: metric = DeployTimeMetric( name=rc.metadata.labels[pelorus.get_app_label()], namespace=namespace, labels=rc.metadata.labels, deploy_time=rc.metadata.creationTimestamp, image_sha=sha, ) yield metric def get_replicas( dyn_client: DynamicClient, apiVersion: str, objectName: str ) -> dict[str, object]: """Process Replicas for given Api Version and Object type (ReplicaSet or ReplicationController)""" try: apiResource = dyn_client.resources.get(api_version=apiVersion, kind=objectName) replicationobjects = apiResource.get(label_selector=pelorus.get_app_label())
def get_app_name(self, issue): app_label = pelorus.get_app_label() for label in issue.fields.labels: if label.startswith("%s=" % app_label): return label.replace("%s=" % app_label, "") return "unknown"
from random import randrange from typing import Sequence from unittest.mock import NonCallableMock import pytest from deploytime.app import DeployTimeMetric, generate_metrics, image_sha # type: ignore from openshift.dynamic import DynamicClient # type: ignore from openshift.dynamic.discovery import Discoverer # type: ignore from tests.openshift_mocks import * import pelorus # pylava:ignore=W0401 # region test constants APP_LABEL = pelorus.get_app_label() FOO_NS = "foo_ns" BAR_NS = "bar_ns" BAZ_NS = "baz_ns" FOO_APP = "foo_app" BAR_APP = "bar_app" FOO_LABEL = "foo_label" FOO_LABEL_VALUE = "test" REPLICA_SET = "ReplicaSet" REP_CONTROLLER = "ReplicationController" UNKNOWN_OWNER_KIND = "UnknownOwnerKind"
def generate_metrics(namespaces): metrics = [] pods = [] pod_replica_dict = {} logging.info("generate_metrics: start") v1_pods = dyn_client.resources.get(api_version='v1', kind='Pod') if not namespaces: logging.info("No namespaces specified, watching all namespaces") else: logging.info("Watching namespaces %s", namespaces) pods = v1_pods.get(label_selector=pelorus.get_app_label(), field_selector='status.phase=Running').items replicas_dict = {} # Process ReplicationControllers for DeploymentConfigs replicas_dict = get_replicas('v1', 'ReplicationController') # Process ReplicaSets from apps/v1 api version for Deployments replicas_dict.update(get_replicas('apps/v1', 'ReplicaSet')) # Process ReplicaSets from extentions/v1beta1 api version for Deployments replicas_dict.update(get_replicas('extensions/v1beta1', 'ReplicaSet')) for pod in pods: if (not namespaces or (pod.metadata.namespace in namespaces)) and pod.metadata.ownerReferences: logging.debug("Getting Replicas for pod: %s in namespace: %s", pod.metadata.name, pod.metadata.namespace) ownerRefs = pod.metadata.ownerReferences namespace = pod.metadata.namespace # use the replica controller/replicasets to get deploy timestame. The ownerRef of pod is used to get # replicaiton controller. A dictionary is used to handle dups when multiple pods are running. for ownerRef in ownerRefs: if (ownerRef.kind in supported_replica_objects and not pod_replica_dict.get(namespace + "/" + ownerRef.name)): logging.debug( "Getting replica: %s, kind: %s, namespace: %s", ownerRef.name, ownerRef.kind, namespace) if replicas_dict.get(namespace + "/" + ownerRef.name): rc = replicas_dict.get(namespace + "/" + ownerRef.name) pod_replica_dict[namespace + "/" + ownerRef.name] = "DONE" images = [ image_sha(c.image) for c in pod.spec.containers ] # Since a commit will be built into a particular image and there could be multiple # containers (images) per pod, we will push one metric per image/container in the # pod template for i in images: if i is not None: metric = DeployTimeMetric( rc.metadata.labels[ pelorus.get_app_label()], namespace) metric.labels = rc.metadata.labels metric.deploy_time = rc.metadata.creationTimestamp metric.image_sha = i metric.namespace = namespace metrics.append(metric) return metrics
def test_get_app_label(): assert pelorus.get_app_label() == pelorus.DEFAULT_APP_LABEL os.environ["APP_LABEL"] = "changed" assert pelorus.get_app_label() == "changed" os.unsetenv("APP_LABEL")