@@ -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+
274305def 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
20552093def 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 (
0 commit comments