Skip to content

Commit be0e1aa

Browse files
committed
feat(run-tests): Handle existing namespaces and permission errors more gracefully
This _does_ change behavior. In the past run-tests would fail if we can't create the namespace but now we'll proceed because it might already exist and we just can't check easily.
1 parent e62e8ac commit be0e1aa

File tree

1 file changed

+53
-16
lines changed

1 file changed

+53
-16
lines changed

template/scripts/run-tests

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -353,22 +353,7 @@ def run_tests(test: str, parallel: int, namespace: str, skip_delete: bool) -> No
353353
if namespace:
354354
kuttl_cmd.extend(["--namespace", namespace])
355355
# kuttl doesn't create the namespace so we need to do it ourselves
356-
create_ns_cmd = ["kubectl", "create", "namespace", namespace]
357-
try:
358-
logging.debug(f"Running : {create_ns_cmd}")
359-
subprocess.run(
360-
create_ns_cmd,
361-
check=True,
362-
capture_output=True,
363-
)
364-
except subprocess.CalledProcessError as e:
365-
stderr = e.stderr.decode("utf-8")
366-
# If the namespace already exists, this will fail and we ignore the
367-
# error. If it fails for any other reason, we raise an exception.
368-
if "already exists" not in stderr:
369-
logging.error(stderr)
370-
logging.error("namespace creation failed")
371-
raise TestRunnerException()
356+
ensure_namespace_exists(namespace)
372357

373358
logging.debug(f"Running : {kuttl_cmd}")
374359

@@ -382,6 +367,58 @@ def run_tests(test: str, parallel: int, namespace: str, skip_delete: bool) -> No
382367
raise TestRunnerException()
383368

384369

370+
def ensure_namespace_exists(namespace: str) -> None:
371+
"""
372+
Ensure the specified namespace exists, creating it if necessary.
373+
374+
This function handles various permission scenarios:
375+
- If the namespace already exists, it does nothing
376+
- If it doesn't exist and we have permission, it creates it
377+
- If we don't have permission to create/check namespaces, it logs a warning
378+
and assumes the namespace exists or will be created externally (useful for OpenShift)
379+
380+
Examples of (permission) errors we handle:
381+
- Error from server (Forbidden): namespaces is forbidden: User "developer" cannot create resource "namespaces" in API group "" at the cluster scope
382+
- Error from server (Forbidden): namespaces "foobar123" is forbidden: User "developer" cannot get resource "namespaces" in API group "" in the namespace "foobar123"
383+
- Error from server (AlreadyExists): namespaces "kuttl-test-finer-caiman" already exists
384+
"""
385+
# First check if the namespace already exists
386+
check_ns_cmd = ["kubectl", "get", "namespace", namespace]
387+
try:
388+
logging.debug(f"Checking if namespace exists: {check_ns_cmd}")
389+
subprocess.run(
390+
check_ns_cmd,
391+
check=True,
392+
capture_output=True,
393+
)
394+
logging.debug(f"Namespace '{namespace}' already exists")
395+
except subprocess.CalledProcessError:
396+
# Namespace doesn't exist, try to create it
397+
create_ns_cmd = ["kubectl", "create", "namespace", namespace]
398+
try:
399+
logging.debug(f"Creating namespace: {create_ns_cmd}")
400+
subprocess.run(
401+
create_ns_cmd,
402+
check=True,
403+
capture_output=True,
404+
)
405+
logging.debug(f"Successfully created namespace '{namespace}'")
406+
except subprocess.CalledProcessError as e:
407+
stderr = e.stderr.decode("utf-8")
408+
if "already exists" in stderr:
409+
logging.debug(
410+
f"Namespace '{namespace}' already exists (race condition)"
411+
)
412+
elif "forbidden" in stderr.lower():
413+
logging.warning(
414+
f"No permission to create namespace '{namespace}', assuming it exists or will be created externally"
415+
)
416+
else:
417+
logging.error(stderr)
418+
logging.error("namespace creation failed")
419+
raise TestRunnerException()
420+
421+
385422
def main(argv) -> int:
386423
ret = 0
387424
try:

0 commit comments

Comments
 (0)