|
19 | 19 | from jinja2 import Environment, FileSystemLoader, select_autoescape
|
20 | 20 |
|
21 | 21 | import pykube
|
22 |
| -from pykube import Namespace, Pod, Node, Ingress |
| 22 | +from pykube import Namespace, Pod, Node, Ingress, Service, ObjectDoesNotExist |
23 | 23 | from pykube.objects import APIObject, NamespacedAPIObject
|
24 | 24 |
|
25 | 25 | from kube_resource_report import cluster_discovery, pricing, filters, __version__
|
@@ -161,6 +161,38 @@ def get_pod_usage(cluster, pods: dict):
|
161 | 161 | logger.exception("Failed to get pod usage metrics")
|
162 | 162 |
|
163 | 163 |
|
| 164 | +def find_backend_application(client, ingress, rule): |
| 165 | + ''' |
| 166 | + The Ingress object might not have a "application" label, so let's try to find the application by looking at the backend service and its pods |
| 167 | + ''' |
| 168 | + paths = rule.get('http', {}).get('paths', []) |
| 169 | + selectors = [] |
| 170 | + for path in paths: |
| 171 | + service_name = path.get('backend', {}).get('serviceName') |
| 172 | + if service_name: |
| 173 | + try: |
| 174 | + service = Service.objects(client, namespace=ingress.namespace).get(name=service_name) |
| 175 | + except ObjectDoesNotExist: |
| 176 | + logger.debug(f'Referenced service does not exist: {ingress.namespace}/{service_name}') |
| 177 | + else: |
| 178 | + selector = service.obj['spec'].get('selector', {}) |
| 179 | + selectors.append(selector) |
| 180 | + application = get_application_from_labels(selector) |
| 181 | + if application: |
| 182 | + return application |
| 183 | + # we still haven't found the application, let's look up pods by label selectors |
| 184 | + for selector in selectors: |
| 185 | + application_candidates = set() |
| 186 | + for pod in Pod.objects(client).filter(namespace=ingress.namespace, selector=selector): |
| 187 | + application = get_application_from_labels(pod.labels) |
| 188 | + if application: |
| 189 | + application_candidates.add(application) |
| 190 | + |
| 191 | + if len(application_candidates) == 1: |
| 192 | + return application_candidates.pop() |
| 193 | + return '' |
| 194 | + |
| 195 | + |
164 | 196 | def query_cluster(
|
165 | 197 | cluster, executor, system_namespaces, additional_cost_per_cluster, no_ingress_status, node_label
|
166 | 198 | ):
|
@@ -308,7 +340,12 @@ def query_cluster(
|
308 | 340 | application = get_application_from_labels(_ingress.labels)
|
309 | 341 | for rule in _ingress.obj["spec"].get("rules", []):
|
310 | 342 | host = rule.get('host', '')
|
311 |
| - ingress = [_ingress.namespace, _ingress.name, application, host, 0] |
| 343 | + if not application: |
| 344 | + # find the application by getting labels from pods |
| 345 | + backend_application = find_backend_application(cluster.client, _ingress, rule) |
| 346 | + else: |
| 347 | + backend_application = None |
| 348 | + ingress = [_ingress.namespace, _ingress.name, application or backend_application, host, 0] |
312 | 349 | if host and not no_ingress_status:
|
313 | 350 | try:
|
314 | 351 | future = futures_by_host[host]
|
|
0 commit comments