feat(inventory): Use OCP projects if available

If no namespaces were specified in the inventory config try to get all
available namespaces by trying to list OCP projects first. If the resource
was not found (no OCP cluster) fall back to regular namespaces.

Signed-off-by: Felix Matouschek <fmatouschek@redhat.com>
This commit is contained in:
Felix Matouschek
2025-04-28 15:41:31 +02:00
parent 0b542ddced
commit 4a8b4ead2f
3 changed files with 54 additions and 2 deletions

View File

@@ -141,7 +141,7 @@ from typing import (
# Set HAS_K8S_MODULE_HELPER and k8s_import exception accordingly to
# potentially print a warning to the user if the client is missing.
try:
from kubernetes.dynamic.exceptions import DynamicApiError
from kubernetes.dynamic.exceptions import DynamicApiError, ResourceNotFoundError
HAS_K8S_MODULE_HELPER = True
K8S_IMPORT_EXCEPTION = None
@@ -152,6 +152,11 @@ except ImportError as e:
Dummy class, mainly used for ansible-test sanity.
"""
class ResourceNotFoundError(Exception):
"""
Dummy class, mainly used for ansible-test sanity.
"""
HAS_K8S_MODULE_HELPER = False
K8S_IMPORT_EXCEPTION = e
@@ -575,9 +580,18 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
_get_available_namespaces lists all namespaces accessible with the
configured credentials and returns them.
"""
namespaces = []
try:
namespaces = self._get_resources(
client, "project.openshift.io/v1", "Project"
)
except ResourceNotFoundError:
namespaces = self._get_resources(client, "v1", "Namespace")
return [
namespace["metadata"]["name"]
for namespace in self._get_resources(client, "v1", "Namespace")
for namespace in namespaces
if "metadata" in namespace and "name" in namespace["metadata"]
]

View File

@@ -8,6 +8,7 @@ __metaclass__ = type
import pytest
from kubernetes.dynamic.exceptions import ResourceNotFoundError
from kubernetes.dynamic.resource import ResourceField
from ansible.template import Templar
@@ -113,6 +114,9 @@ def client(mocker, request):
dns_obj = ResourceField({"spec": {"baseDomain": base_domain}})
dns.items = [dns_obj]
projects = mocker.Mock()
projects.items = [ResourceField(item) for item in param.get("projects", [])]
namespace_client = mocker.Mock()
namespace_client.get = mocker.Mock(return_value=namespaces)
vm_client = mocker.Mock()
@@ -130,6 +134,14 @@ def client(mocker, request):
dns_client = mocker.Mock()
dns_client.get = dns_client_get
def project_client_get():
if not projects.items:
raise ResourceNotFoundError
return projects
project_client = mocker.Mock()
project_client.get = project_client_get
def resources_get(api_version="", kind=""):
if api_version.lower() == "v1":
if kind.lower() == "namespace":
@@ -138,6 +150,11 @@ def client(mocker, request):
return service_client
elif api_version.lower() == "config.openshift.io/v1" and kind.lower() == "dns":
return dns_client
elif (
api_version.lower() == "project.openshift.io/v1"
and kind.lower() == "project"
):
return project_client
elif "kubevirt.io/" in api_version.lower():
if kind.lower() == "virtualmachine":
return vm_client

View File

@@ -63,6 +63,27 @@ def test_get_resources(inventory, client):
},
[DEFAULT_NAMESPACE, "test"],
),
(
{
"projects": [
{"metadata": {"name": DEFAULT_NAMESPACE}},
{"metadata": {"name": "testproject"}},
]
},
[DEFAULT_NAMESPACE, "testproject"],
),
(
{
"namespaces": [
{"metadata": {"name": DEFAULT_NAMESPACE}},
{"metadata": {"name": "test"}},
],
"projects": [
{"metadata": {"name": "testproject"}},
],
},
["testproject"],
),
],
indirect=["client"],
)