Skip to content

Commit a85d4a1

Browse files
authored
Beter labels and multi tenancy support (#348)
* metrics and logging multi-tenancy improves labeling and adds support for multi-tenancy as well as migration * create an infra panel to observe cabotage's internal infra metrics * rough-draft of container-level resource limit/req * show metrics for pods that have terminated * fix handling of migration to tenantified queries
1 parent e3a347c commit a85d4a1

8 files changed

Lines changed: 1586 additions & 23 deletions

File tree

cabotage/celery/tasks/branch_deploy.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,9 @@ def _precreate_ingresses(environment):
233233
"organization": org.slug,
234234
"project": environment.project.slug,
235235
"application": app.slug,
236+
"cabotage.io/organization": org.k8s_identifier,
237+
"cabotage.io/project": environment.project.k8s_identifier,
238+
"cabotage.io/application": app.k8s_identifier,
236239
},
237240
ingresses=app_env.ingresses,
238241
org_k8s_identifier=org.k8s_identifier,

cabotage/celery/tasks/build.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@
2727
from github.GithubException import GithubException, UnknownObjectException
2828
from github.GithubIntegration import GithubIntegration
2929

30-
from cabotage.celery.tasks.deploy import run_deploy, run_job
30+
from cabotage.celery.tasks.deploy import (
31+
_safe_labels_from_application,
32+
run_deploy,
33+
run_job,
34+
)
3135
from cabotage.celery.tasks.notify import (
3236
dispatch_autodeploy_notification,
3337
dispatch_pipeline_notification,
@@ -450,6 +454,7 @@ def build_release_buildkit(release):
450454
},
451455
)
452456
context_configmap_object = release.release_build_context_configmap
457+
safe_labels = _safe_labels_from_application(release.application)
453458
job_object = kubernetes.client.V1Job(
454459
metadata=kubernetes.client.V1ObjectMeta(
455460
name=f"releasebuild-{release.build_job_id}",
@@ -460,6 +465,7 @@ def build_release_buildkit(release):
460465
"process": "build",
461466
"build_id": release.build_job_id,
462467
"build-job.cabotage.io": "true",
468+
**safe_labels,
463469
},
464470
),
465471
spec=kubernetes.client.V1JobSpec(
@@ -477,6 +483,7 @@ def build_release_buildkit(release):
477483
"build_id": release.build_job_id,
478484
"ca-admission.cabotage.io": "true",
479485
"resident-pod.cabotage.io": "true",
486+
**safe_labels,
480487
},
481488
annotations={
482489
"container.apparmor.security.beta.kubernetes.io/build": "unconfined", # noqa: E501
@@ -960,6 +967,7 @@ def build_image_buildkit(image: Image):
960967
"buildkitd.toml": buildkitd_toml,
961968
},
962969
)
970+
safe_labels = _safe_labels_from_application(image.application)
963971
job_object = kubernetes.client.V1Job(
964972
metadata=kubernetes.client.V1ObjectMeta(
965973
name=f"imagebuild-{image.build_job_id}",
@@ -970,6 +978,7 @@ def build_image_buildkit(image: Image):
970978
"process": "build",
971979
"build_id": image.build_job_id,
972980
"build-job.cabotage.io": "true",
981+
**safe_labels,
973982
},
974983
),
975984
spec=kubernetes.client.V1JobSpec(
@@ -987,6 +996,7 @@ def build_image_buildkit(image: Image):
987996
"build_id": image.build_job_id,
988997
"ca-admission.cabotage.io": "true",
989998
"resident-pod.cabotage.io": "true",
999+
**safe_labels,
9901000
},
9911001
annotations={
9921002
"container.apparmor.security.beta.kubernetes.io/build": "unconfined", # noqa: E501
@@ -1496,6 +1506,7 @@ def build_omnibus_buildkit(image, release):
14961506
],
14971507
)
14981508

1509+
safe_labels = _safe_labels_from_application(image.application)
14991510
job_object = kubernetes.client.V1Job(
15001511
metadata=kubernetes.client.V1ObjectMeta(
15011512
name=f"omnibusbuild-{image.build_job_id}",
@@ -1506,6 +1517,7 @@ def build_omnibus_buildkit(image, release):
15061517
"process": "build",
15071518
"build_id": image.build_job_id,
15081519
"build-job.cabotage.io": "true",
1520+
**safe_labels,
15091521
},
15101522
),
15111523
spec=kubernetes.client.V1JobSpec(
@@ -1523,6 +1535,7 @@ def build_omnibus_buildkit(image, release):
15231535
"build_id": image.build_job_id,
15241536
"ca-admission.cabotage.io": "true",
15251537
"resident-pod.cabotage.io": "true",
1538+
**safe_labels,
15261539
},
15271540
annotations={
15281541
"container.apparmor.security.beta.kubernetes.io/image-build": "unconfined", # noqa: E501

cabotage/celery/tasks/deploy.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,37 @@ def k8s_label_value(release):
271271
return compact_k8s_name(*pairs)
272272

273273

274+
def _safe_labels_from_release(release):
275+
"""Build cabotage.io/-prefixed labels using k8s_identifiers.
276+
277+
These are collision-safe labels that sit alongside the legacy
278+
slug-based labels.
279+
"""
280+
org = release.application.project.organization
281+
project = release.application.project
282+
app = release.application
283+
app_env = release.application_environment
284+
labels = {
285+
"cabotage.io/organization": org.k8s_identifier,
286+
"cabotage.io/project": project.k8s_identifier,
287+
"cabotage.io/application": app.k8s_identifier,
288+
}
289+
if app_env.k8s_identifier is not None:
290+
labels["cabotage.io/environment"] = app_env.environment.k8s_identifier
291+
return labels
292+
293+
294+
def _safe_labels_from_application(application):
295+
"""Build cabotage.io/-prefixed labels from an Application (for builds)."""
296+
org = application.project.organization
297+
project = application.project
298+
return {
299+
"cabotage.io/organization": org.k8s_identifier,
300+
"cabotage.io/project": project.k8s_identifier,
301+
"cabotage.io/application": application.k8s_identifier,
302+
}
303+
304+
274305
def render_namespace(release):
275306
namespace_object = kubernetes.client.V1Namespace(
276307
metadata=kubernetes.client.V1ObjectMeta(
@@ -808,13 +839,15 @@ def render_service(release, process_name):
808839
resource_prefix = k8s_resource_prefix(release)
809840
service_name = f"{resource_prefix}-{process_name}"
810841
label_value = k8s_label_value(release)
842+
safe_labels = _safe_labels_from_release(release)
811843
service_object = kubernetes.client.V1Service(
812844
metadata=kubernetes.client.V1ObjectMeta(
813845
name=service_name,
814846
labels={
815847
"resident-service.cabotage.io": "true",
816848
"app": resource_prefix,
817849
"process": process_name,
850+
**safe_labels,
818851
},
819852
),
820853
spec=kubernetes.client.V1ServiceSpec(
@@ -874,6 +907,7 @@ def render_ingress(release, ingress):
874907
875908
Convenience wrapper that extracts naming context from a Release.
876909
"""
910+
safe_labels = _safe_labels_from_release(release)
877911
return render_ingress_object(
878912
ingress=ingress,
879913
resource_prefix=k8s_resource_prefix(release),
@@ -882,6 +916,7 @@ def render_ingress(release, ingress):
882916
"project": release.application.project.slug,
883917
"application": release.application.slug,
884918
"app": k8s_label_value(release),
919+
**safe_labels,
885920
},
886921
org_k8s_identifier=release.application.project.organization.k8s_identifier,
887922
process_names=list(release.processes) if release.processes else [],
@@ -1841,6 +1876,7 @@ def render_deployment(
18411876
app_env = release.application_environment
18421877
env_slug = app_env.environment.slug if app_env.environment else ""
18431878
process_counts = app_env.process_counts or {}
1879+
safe_labels = _safe_labels_from_release(release)
18441880
pod_labels = {
18451881
"organization": release.application.project.organization.slug,
18461882
"project": release.application.project.slug,
@@ -1852,6 +1888,7 @@ def render_deployment(
18521888
"deployment": str(deployment_id),
18531889
"ca-admission.cabotage.io": "true",
18541890
"resident-pod.cabotage.io": "true",
1891+
**safe_labels,
18551892
}
18561893
deployment_object = kubernetes.client.V1Deployment(
18571894
metadata=kubernetes.client.V1ObjectMeta(
@@ -1863,6 +1900,7 @@ def render_deployment(
18631900
"process": process_name,
18641901
"app": label_value,
18651902
"resident-deployment.cabotage.io": "true",
1903+
**safe_labels,
18661904
},
18671905
),
18681906
spec=kubernetes.client.V1DeploymentSpec(
@@ -2054,6 +2092,7 @@ def resize_deployment(namespace, release, process_name, pod_class_name):
20542092

20552093
def render_job(namespace, release, service_account_name, process_name, job_id):
20562094
label_value = k8s_label_value(release)
2095+
safe_labels = _safe_labels_from_release(release)
20572096
job_object = kubernetes.client.V1Job(
20582097
metadata=kubernetes.client.V1ObjectMeta(
20592098
name=f"deployment-{job_id}",
@@ -2066,6 +2105,7 @@ def render_job(namespace, release, service_account_name, process_name, job_id):
20662105
"release": str(release.version),
20672106
"deployment": job_id,
20682107
"resident-job.cabotage.io": "true",
2108+
**safe_labels,
20692109
},
20702110
),
20712111
spec=kubernetes.client.V1JobSpec(
@@ -2084,6 +2124,7 @@ def render_job(namespace, release, service_account_name, process_name, job_id):
20842124
"deployment": job_id,
20852125
"ca-admission.cabotage.io": "true",
20862126
"resident-pod.cabotage.io": "true",
2127+
**safe_labels,
20872128
}
20882129
),
20892130
spec=render_podspec(release, process_name, service_account_name),
@@ -2130,6 +2171,7 @@ def render_cronjob(
21302171
)
21312172
process_counts = app_env.process_counts or {}
21322173
suspended = process_counts.get(process_name, 0) == 0
2174+
safe_labels = _safe_labels_from_release(release)
21332175
common_labels = {
21342176
"organization": release.application.project.organization.slug,
21352177
"project": release.application.project.slug,
@@ -2139,6 +2181,7 @@ def render_cronjob(
21392181
"environment": env_slug,
21402182
"release": str(release.version),
21412183
"deployment": str(deployment_id),
2184+
**safe_labels,
21422185
}
21432186
job_labels = {
21442187
**common_labels,
@@ -2160,6 +2203,7 @@ def render_cronjob(
21602203
"process": process_name,
21612204
"app": label_value,
21622205
"resident-cronjob.cabotage.io": "true",
2206+
**safe_labels,
21632207
},
21642208
),
21652209
spec=kubernetes.client.V1CronJobSpec(

cabotage/client/templates/_base.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,10 @@
259259
class="btn btn-ghost btn-sm text-base-content/60 hover:text-base-content text-xs font-medium">Projects</a>
260260
<a href="{{ url_for('user.guide') }}"
261261
class="btn btn-ghost btn-sm text-base-content/60 hover:text-base-content text-xs font-medium">Guide</a>
262+
{% if current_user.admin %}
263+
<a href="{{ url_for('user.infra_observe') }}"
264+
class="btn btn-ghost btn-sm text-base-content/60 hover:text-base-content text-xs font-medium">Infra</a>
265+
{% endif %}
262266
</nav>
263267
<div class="dropdown dropdown-hover dropdown-end theme-toggle-wrap">
264268
<div tabindex="0" role="button" id="theme-toggle"
@@ -611,6 +615,10 @@
611615
class="btn btn-ghost btn-sm justify-start text-sm">Projects</a>
612616
<a href="{{ url_for('user.guide') }}"
613617
class="btn btn-ghost btn-sm justify-start text-sm">Guide</a>
618+
{% if current_user.admin %}
619+
<a href="{{ url_for('user.infra_observe') }}"
620+
class="btn btn-ghost btn-sm justify-start text-sm">Infrastructure</a>
621+
{% endif %}
614622
</nav>
615623
{% endif %}
616624

0 commit comments

Comments
 (0)