From 02978cbe6bdb51825eadaaa5e2147d3b7ca88fc9 Mon Sep 17 00:00:00 2001 From: Anil Prajapati Date: Sun, 23 Nov 2025 11:52:11 +0530 Subject: [PATCH 1/5] [patch] auto check binding mode and wait based on the type --- src/mas/devops/ocp.py | 24 +++++++++++++++++++++++ src/mas/devops/tekton.py | 42 ++++++++++++++++++++++++++-------------- 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/src/mas/devops/ocp.py b/src/mas/devops/ocp.py index b5988010..f67aa930 100644 --- a/src/mas/devops/ocp.py +++ b/src/mas/devops/ocp.py @@ -228,6 +228,30 @@ def getStorageClasses(dynClient: DynamicClient) -> list: return storageClasses +def getStorageClassVolumeBindingMode(dynClient: DynamicClient, storageClassName: str) -> str: + """ + Get the volumeBindingMode for a storage class. + + Args: + dynClient: OpenShift dynamic client + storageClassName: Name of the storage class + + Returns: + str: "Immediate" or "WaitForFirstConsumer" (defaults to "Immediate" if not found) + """ + try: + storageClass = getStorageClass(dynClient, storageClassName) + if storageClass and hasattr(storageClass, 'volumeBindingMode'): + return storageClass.volumeBindingMode + # Default to Immediate if not specified (Kubernetes default) + logger.debug(f"Storage class {storageClassName} does not have volumeBindingMode set, defaulting to 'Immediate'") + return "Immediate" + except Exception as e: + logger.warning(f"Unable to determine volumeBindingMode for storage class {storageClassName}: {e}") + # Default to Immediate to maintain backward compatibility + return "Immediate" + + def isSNO(dynClient: DynamicClient) -> bool: return len(getNodes(dynClient)) == 1 diff --git a/src/mas/devops/tekton.py b/src/mas/devops/tekton.py index e408e141..348d5400 100644 --- a/src/mas/devops/tekton.py +++ b/src/mas/devops/tekton.py @@ -124,7 +124,7 @@ def updateTektonDefinitions(namespace: str, yamlFile: str) -> None: logger.debug(line) -def preparePipelinesNamespace(dynClient: DynamicClient, instanceId: str = None, storageClass: str = None, accessMode: str = None, waitForBind: bool = True, configureRBAC: bool = True): +def preparePipelinesNamespace(dynClient: DynamicClient, instanceId: str = None, storageClass: str = None, accessMode: str = None, configureRBAC: bool = True): templateDir = path.join(path.abspath(path.dirname(__file__)), "templates") env = Environment( loader=FileSystemLoader(searchpath=templateDir) @@ -157,20 +157,27 @@ def preparePipelinesNamespace(dynClient: DynamicClient, instanceId: str = None, pvcAPI = dynClient.resources.get(api_version="v1", kind="PersistentVolumeClaim") pvcAPI.apply(body=pvc, namespace=namespace) - if instanceId is not None and waitForBind: - logger.debug("Waiting for PVC to be bound") - pvcIsBound = False - while not pvcIsBound: - configPVC = pvcAPI.get(name="config-pvc", namespace=namespace) - if configPVC.status.phase == "Bound": - pvcIsBound = True - else: - logger.debug("Waiting 15s before checking status of PVC again") - logger.debug(configPVC) - sleep(15) + # Automatically determine if we should wait for PVC binding based on storage class + from .ocp import getStorageClassVolumeBindingMode + volumeBindingMode = getStorageClassVolumeBindingMode(dynClient, storageClass) + waitForBind = (volumeBindingMode == "Immediate") + + if waitForBind: + logger.info(f"Storage class {storageClass} uses volumeBindingMode={volumeBindingMode}, waiting for PVC to bind") + pvcIsBound = False + while not pvcIsBound: + configPVC = pvcAPI.get(name="config-pvc", namespace=namespace) + if configPVC.status.phase == "Bound": + pvcIsBound = True + else: + logger.debug("Waiting 15s before checking status of PVC again") + logger.debug(configPVC) + sleep(15) + else: + logger.info(f"Storage class {storageClass} uses volumeBindingMode={volumeBindingMode}, skipping PVC bind wait") -def prepareAiServicePipelinesNamespace(dynClient: DynamicClient, instanceId: str = None, storageClass: str = None, accessMode: str = None, waitForBind: bool = True, configureRBAC: bool = True): +def prepareAiServicePipelinesNamespace(dynClient: DynamicClient, instanceId: str = None, storageClass: str = None, accessMode: str = None, configureRBAC: bool = True): templateDir = path.join(path.abspath(path.dirname(__file__)), "templates") env = Environment( loader=FileSystemLoader(searchpath=templateDir) @@ -197,8 +204,13 @@ def prepareAiServicePipelinesNamespace(dynClient: DynamicClient, instanceId: str pvcAPI = dynClient.resources.get(api_version="v1", kind="PersistentVolumeClaim") pvcAPI.apply(body=pvc, namespace=namespace) + # Automatically determine if we should wait for PVC binding based on storage class + from .ocp import getStorageClassVolumeBindingMode + volumeBindingMode = getStorageClassVolumeBindingMode(dynClient, storageClass) + waitForBind = (volumeBindingMode == "Immediate") + if waitForBind: - logger.debug("Waiting for PVC to be bound") + logger.info(f"Storage class {storageClass} uses volumeBindingMode={volumeBindingMode}, waiting for PVC to bind") pvcIsBound = False while not pvcIsBound: configPVC = pvcAPI.get(name="config-pvc", namespace=namespace) @@ -208,6 +220,8 @@ def prepareAiServicePipelinesNamespace(dynClient: DynamicClient, instanceId: str logger.debug("Waiting 15s before checking status of PVC again") logger.debug(configPVC) sleep(15) + else: + logger.info(f"Storage class {storageClass} uses volumeBindingMode={volumeBindingMode}, skipping PVC bind wait") def prepareInstallSecrets(dynClient: DynamicClient, namespace: str, slsLicenseFile: str = None, additionalConfigs: dict = None, certs: str = None, podTemplates: str = None) -> None: From 53048ced512d229457059568ffe4159f71af992d Mon Sep 17 00:00:00 2001 From: Anil Prajapati Date: Mon, 1 Dec 2025 15:51:59 +0530 Subject: [PATCH 2/5] [patch] fix import --- src/mas/devops/tekton.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/mas/devops/tekton.py b/src/mas/devops/tekton.py index 348d5400..c907deb6 100644 --- a/src/mas/devops/tekton.py +++ b/src/mas/devops/tekton.py @@ -22,7 +22,7 @@ from jinja2 import Environment, FileSystemLoader -from .ocp import getConsoleURL, waitForCRD, waitForDeployment, crdExists +from .ocp import getConsoleURL, waitForCRD, waitForDeployment, crdExists, getStorageClassVolumeBindingMode from .mas import waitForPVC, patchPendingPVC logger = logging.getLogger(__name__) @@ -158,7 +158,6 @@ def preparePipelinesNamespace(dynClient: DynamicClient, instanceId: str = None, pvcAPI.apply(body=pvc, namespace=namespace) # Automatically determine if we should wait for PVC binding based on storage class - from .ocp import getStorageClassVolumeBindingMode volumeBindingMode = getStorageClassVolumeBindingMode(dynClient, storageClass) waitForBind = (volumeBindingMode == "Immediate") @@ -205,7 +204,6 @@ def prepareAiServicePipelinesNamespace(dynClient: DynamicClient, instanceId: str pvcAPI.apply(body=pvc, namespace=namespace) # Automatically determine if we should wait for PVC binding based on storage class - from .ocp import getStorageClassVolumeBindingMode volumeBindingMode = getStorageClassVolumeBindingMode(dynClient, storageClass) waitForBind = (volumeBindingMode == "Immediate") From 37d6f61bf89ea249d3183d3b5756043ee330bc54 Mon Sep 17 00:00:00 2001 From: Anil Prajapati Date: Mon, 1 Dec 2025 16:08:20 +0530 Subject: [PATCH 3/5] fix lint error --- src/mas/devops/ocp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mas/devops/ocp.py b/src/mas/devops/ocp.py index f67aa930..4606b213 100644 --- a/src/mas/devops/ocp.py +++ b/src/mas/devops/ocp.py @@ -231,11 +231,11 @@ def getStorageClasses(dynClient: DynamicClient) -> list: def getStorageClassVolumeBindingMode(dynClient: DynamicClient, storageClassName: str) -> str: """ Get the volumeBindingMode for a storage class. - + Args: dynClient: OpenShift dynamic client storageClassName: Name of the storage class - + Returns: str: "Immediate" or "WaitForFirstConsumer" (defaults to "Immediate" if not found) """ From e4c034baab2da8e0e756e60489e5928ac61b2cf5 Mon Sep 17 00:00:00 2001 From: Anil Prajapati Date: Mon, 1 Dec 2025 17:40:11 +0530 Subject: [PATCH 4/5] [patch] fix lint error --- src/mas/devops/tekton.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mas/devops/tekton.py b/src/mas/devops/tekton.py index c907deb6..a78a351c 100644 --- a/src/mas/devops/tekton.py +++ b/src/mas/devops/tekton.py @@ -206,7 +206,7 @@ def prepareAiServicePipelinesNamespace(dynClient: DynamicClient, instanceId: str # Automatically determine if we should wait for PVC binding based on storage class volumeBindingMode = getStorageClassVolumeBindingMode(dynClient, storageClass) waitForBind = (volumeBindingMode == "Immediate") - + if waitForBind: logger.info(f"Storage class {storageClass} uses volumeBindingMode={volumeBindingMode}, waiting for PVC to bind") pvcIsBound = False From 4f6156d8def297662a55066ee3471d4f21fa1d5b Mon Sep 17 00:00:00 2001 From: Anil Prajapati Date: Mon, 1 Dec 2025 17:46:23 +0530 Subject: [PATCH 5/5] [patch] remove extra spaces --- src/mas/devops/tekton.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mas/devops/tekton.py b/src/mas/devops/tekton.py index a78a351c..82e98cae 100644 --- a/src/mas/devops/tekton.py +++ b/src/mas/devops/tekton.py @@ -156,11 +156,9 @@ def preparePipelinesNamespace(dynClient: DynamicClient, instanceId: str = None, pvc = yaml.safe_load(renderedTemplate) pvcAPI = dynClient.resources.get(api_version="v1", kind="PersistentVolumeClaim") pvcAPI.apply(body=pvc, namespace=namespace) - # Automatically determine if we should wait for PVC binding based on storage class volumeBindingMode = getStorageClassVolumeBindingMode(dynClient, storageClass) waitForBind = (volumeBindingMode == "Immediate") - if waitForBind: logger.info(f"Storage class {storageClass} uses volumeBindingMode={volumeBindingMode}, waiting for PVC to bind") pvcIsBound = False