From 714c95d72f84b4838c1fc754e70800a547ee6f39 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Fri, 8 Sep 2023 15:24:29 -0700 Subject: [PATCH] Make Pod creation from a Job more correct in mock Randomize the Pod name in a similar way that Kubernetes does when the default generateName behavior is used. If an explicit name was set in the pod template in the Job, use that name; don't replace it with a generated name. Honor generateName set in the pod template in the Job. --- .../20230908_152325_rra_DM_40691_queue.md | 3 ++ src/safir/testing/kubernetes.py | 41 +++++++++---------- 2 files changed, 23 insertions(+), 21 deletions(-) create mode 100644 changelog.d/20230908_152325_rra_DM_40691_queue.md diff --git a/changelog.d/20230908_152325_rra_DM_40691_queue.md b/changelog.d/20230908_152325_rra_DM_40691_queue.md new file mode 100644 index 00000000..a48b6165 --- /dev/null +++ b/changelog.d/20230908_152325_rra_DM_40691_queue.md @@ -0,0 +1,3 @@ +### Bug fixes + +- When creating a `Pod` from a `Job` in the Kubernetes mock using `generateName`, randomize the `Pod` name like Kubernetes does rather than using a fixed name. This forces tests to scan correctly for pods associated with a job. If the `Pod` `name` or `generateName` was explicitly configured in the `Job` template, honor it. diff --git a/src/safir/testing/kubernetes.py b/src/safir/testing/kubernetes.py index e11b374c..692ed5c1 100644 --- a/src/safir/testing/kubernetes.py +++ b/src/safir/testing/kubernetes.py @@ -1221,9 +1221,12 @@ async def create_namespaced_job(self, namespace: str, body: V1Job) -> None: """Create a job object. A pod corresponding to this job will also be created. The pod will - have a label ``job-name`` set to the name of the Job object. Its - name will be the job's name prepended to ``-abcde``. If - ``initial_pod_phase`` on the mock is set to ``Running``, the + have a label ``job-name`` set to the name of the ``Job`` object, and + will honor the ``name`` or ``generateName`` metadata from the pod spec + in the ``Job``. If neither is set, it will use the job name followed + by ``-`` as the base for a generated name. + + If ``initial_pod_phase`` on the mock is set to ``Running``, the ``status.active`` field of the job will be set to 1. Parameters @@ -1244,24 +1247,20 @@ async def create_namespaced_job(self, namespace: str, body: V1Job) -> None: stream = self._event_streams[namespace]["Job"] body.metadata.resource_version = stream.next_resource_version self._store_object(namespace, "Job", name, body) - # Pretend to spawn a pod - # If there is no metadata, create a V1ObjectMeta object with just - # namespace, a "generateName" of the job name, and a "job-name" - # label, which appears to be what Kubernetes does. - template = body.spec.template - podmd = template.metadata - if podmd is None: - podmd = V1ObjectMeta( - namespace=namespace, - generate_name=f"{name}-", - ) - # In real life, the name has five random alphanumeric characters, - # but for testing we'd like to know what the spawned pod is going to - # be called. But in real life, if you set "generateName" you can't - # also set the pod name. - podmd.name = name + "-abcde" - podmd.labels["job-name"] = name - pod = V1Pod(metadata=podmd, spec=template.spec) + + # Normally, Kubernetes will immediately spawn a Pod using the + # specification in the Job. Simulate that here. + pod = V1Pod( + metadata=body.spec.template.metadata or V1ObjectMeta(), + spec=body.spec.template.spec, + ) + if not pod.metadata.name: + if not pod.metadata.generate_name: + pod.metadata.generate_name = f"{name}-" + base = pod.metadata.generate_name + pod.metadata.name = base + os.urandom(3).hex()[:5] + pod.metadata.labels["job-name"] = name + pod.metadata.namespace = namespace await self.create_namespaced_pod(namespace, pod) if pod.status.phase == "Running": body.status = V1JobStatus(active=1)