From 02978cbe6bdb51825eadaaa5e2147d3b7ca88fc9 Mon Sep 17 00:00:00 2001 From: Anil Prajapati Date: Sun, 23 Nov 2025 11:52:11 +0530 Subject: [PATCH 1/9] [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/9] [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/9] 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/9] [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/9] [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 From e1a344e5caac49c45063da06df2f89c69499e03a Mon Sep 17 00:00:00 2001 From: Anil Prajapati Date: Wed, 7 Jan 2026 20:40:02 +0530 Subject: [PATCH 6/9] [patch] add few logger info --- src/mas/devops/tekton.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/mas/devops/tekton.py b/src/mas/devops/tekton.py index 01e7864d..8791a384 100644 --- a/src/mas/devops/tekton.py +++ b/src/mas/devops/tekton.py @@ -251,13 +251,18 @@ def preparePipelinesNamespace(dynClient: DynamicClient, instanceId: str = None, if waitForBind: logger.info(f"Storage class {storageClass} uses volumeBindingMode={volumeBindingMode}, waiting for PVC to bind") pvcIsBound = False + logger.info(msg=f"pvcIsBound={pvcIsBound}") # need to remove while not pvcIsBound: configPVC = pvcAPI.get(name="config-pvc", namespace=namespace) + logger.info(f"configPVC: {configPVC}") # need to remove + logger.info(f"configPVC.status.phase: {configPVC.status.phase}") # need to remove if configPVC.status.phase == "Bound": + logger.info(f"pvcIsBound: {pvcIsBound}") # need to remove pvcIsBound = True else: logger.debug("Waiting 15s before checking status of PVC again") logger.debug(configPVC) + logger.info(f"Else part 265: {configPVC}") # need to remove sleep(15) else: logger.info(f"Storage class {storageClass} uses volumeBindingMode={volumeBindingMode}, skipping PVC bind wait") From 4f7cf175db15591a8c4ba6d3fb5713f0c56d0bb5 Mon Sep 17 00:00:00 2001 From: Anil Prajapati Date: Thu, 8 Jan 2026 12:37:40 +0530 Subject: [PATCH 7/9] [patch] fix lint error --- src/mas/devops/tekton.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mas/devops/tekton.py b/src/mas/devops/tekton.py index 8791a384..f11be73a 100644 --- a/src/mas/devops/tekton.py +++ b/src/mas/devops/tekton.py @@ -251,18 +251,18 @@ def preparePipelinesNamespace(dynClient: DynamicClient, instanceId: str = None, if waitForBind: logger.info(f"Storage class {storageClass} uses volumeBindingMode={volumeBindingMode}, waiting for PVC to bind") pvcIsBound = False - logger.info(msg=f"pvcIsBound={pvcIsBound}") # need to remove + logger.info(msg=f"pvcIsBound={pvcIsBound}") # need to remove while not pvcIsBound: configPVC = pvcAPI.get(name="config-pvc", namespace=namespace) - logger.info(f"configPVC: {configPVC}") # need to remove + logger.info(f"configPVC: {configPVC}") # need to remove logger.info(f"configPVC.status.phase: {configPVC.status.phase}") # need to remove if configPVC.status.phase == "Bound": - logger.info(f"pvcIsBound: {pvcIsBound}") # need to remove + logger.info(f"pvcIsBound: {pvcIsBound}") # need to remove pvcIsBound = True else: logger.debug("Waiting 15s before checking status of PVC again") logger.debug(configPVC) - logger.info(f"Else part 265: {configPVC}") # need to remove + logger.info(f"Else part 265: {configPVC}") # need to remove sleep(15) else: logger.info(f"Storage class {storageClass} uses volumeBindingMode={volumeBindingMode}, skipping PVC bind wait") From fd5a751e17b8c9a8f7ea7f230bb4a8fc8a094226 Mon Sep 17 00:00:00 2001 From: Anil Prajapati Date: Thu, 8 Jan 2026 12:39:38 +0530 Subject: [PATCH 8/9] [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 f11be73a..442b28da 100644 --- a/src/mas/devops/tekton.py +++ b/src/mas/devops/tekton.py @@ -255,7 +255,7 @@ def preparePipelinesNamespace(dynClient: DynamicClient, instanceId: str = None, while not pvcIsBound: configPVC = pvcAPI.get(name="config-pvc", namespace=namespace) logger.info(f"configPVC: {configPVC}") # need to remove - logger.info(f"configPVC.status.phase: {configPVC.status.phase}") # need to remove + logger.info(f"configPVC.status.phase: {configPVC.status.phase}") # need to remove if configPVC.status.phase == "Bound": logger.info(f"pvcIsBound: {pvcIsBound}") # need to remove pvcIsBound = True From 80fd0ab8b8db715eb549fc256e47b66a8bbdc7ef Mon Sep 17 00:00:00 2001 From: Anil Prajapati Date: Thu, 8 Jan 2026 16:07:24 +0530 Subject: [PATCH 9/9] [patch] remove extra loggers --- src/mas/devops/tekton.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/mas/devops/tekton.py b/src/mas/devops/tekton.py index 6a4db29a..58042db0 100644 --- a/src/mas/devops/tekton.py +++ b/src/mas/devops/tekton.py @@ -320,18 +320,13 @@ def preparePipelinesNamespace(dynClient: DynamicClient, instanceId: str = None, if waitForBind: logger.info(f"Storage class {storageClass} uses volumeBindingMode={volumeBindingMode}, waiting for PVC to bind") pvcIsBound = False - logger.info(msg=f"pvcIsBound={pvcIsBound}") # need to remove while not pvcIsBound: configPVC = pvcAPI.get(name="config-pvc", namespace=namespace) - logger.info(f"configPVC: {configPVC}") # need to remove - logger.info(f"configPVC.status.phase: {configPVC.status.phase}") # need to remove if configPVC.status.phase == "Bound": - logger.info(f"pvcIsBound: {pvcIsBound}") # need to remove pvcIsBound = True else: logger.debug("Waiting 15s before checking status of PVC again") logger.debug(configPVC) - logger.info(f"Else part 265: {configPVC}") # need to remove sleep(15) else: logger.info(f"Storage class {storageClass} uses volumeBindingMode={volumeBindingMode}, skipping PVC bind wait")