2222
2323from jinja2 import Environment , FileSystemLoader
2424
25- from .ocp import getConsoleURL , waitForCRD , waitForDeployment , crdExists , waitForPVC
25+ from .ocp import getConsoleURL , waitForCRD , waitForDeployment , crdExists , waitForPVC , getStorageClasses
2626
2727logger = logging .getLogger (__name__ )
2828
@@ -105,27 +105,61 @@ def installOpenShiftPipelines(dynClient: DynamicClient, customStorageClassName:
105105 # due to these resources not coming up, the MAS pre-install check in the pipeline times out checking the health of this statefulSet,
106106 # causing failure in pipeline.
107107 # Refer https://github.com/ibm-mas/cli/issues/1511
108- logger .debug ("Waiting for postgredb-tekton-results-postgres-0 PVC to be ready" )
109- foundReadyPVC = waitForPVC (dynClient , namespace = "openshift-pipelines" , pvcName = "postgredb-tekton-results-postgres-0" )
110- if foundReadyPVC :
111- logger .info ("OpenShift Pipelines postgres is installed and ready" )
108+ logger .debug ("Checking postgredb-tekton-results-postgres-0 PVC status" )
109+
110+ pvcAPI = dynClient .resources .get (api_version = "v1" , kind = "PersistentVolumeClaim" )
111+ pvcName = "postgredb-tekton-results-postgres-0"
112+ pvcNamespace = "openshift-pipelines"
113+
114+ # Wait briefly for PVC to be created (max 30 seconds)
115+ maxInitialRetries = 6
116+ pvc = None
117+ for retry in range (maxInitialRetries ):
118+ try :
119+ pvc = pvcAPI .get (name = pvcName , namespace = pvcNamespace )
120+ break
121+ except NotFoundError :
122+ if retry < maxInitialRetries - 1 :
123+ logger .debug (f"Waiting 5s for PVC { pvcName } to be created (attempt { retry + 1 } /{ maxInitialRetries } )..." )
124+ sleep (5 )
125+
126+ if pvc is None :
127+ logger .error (f"PVC { pvcName } was not created after { maxInitialRetries * 5 } seconds" )
128+ return False
129+
130+ # Check if PVC is already bound
131+ if pvc .status .phase == "Bound" :
132+ logger .info ("OpenShift Pipelines postgres PVC is already bound and ready" )
112133 return True
113- else :
134+
135+ # Check if PVC is pending without a storage class - needs immediate patching
136+ if pvc .status .phase == "Pending" and pvc .spec .storageClassName is None :
137+ logger .info ("PVC is pending without storage class, attempting to patch immediately..." )
114138 tektonPVCisReady = addMissingStorageClassToTektonPVC (
115139 dynClient = dynClient ,
116- namespace = "openshift-pipelines" ,
117- pvcName = "postgredb-tekton-results-postgres-0" ,
140+ namespace = pvcNamespace ,
141+ pvcName = pvcName ,
118142 storageClassName = customStorageClassName
119143 )
120144 if tektonPVCisReady :
121145 logger .info ("OpenShift Pipelines postgres is installed and ready" )
122146 return True
123147 else :
124- logger .error ("OpenShift Pipelines postgres PVC is NOT ready" )
148+ logger .error ("OpenShift Pipelines postgres PVC is NOT ready after patching " )
125149 return False
126150
151+ # PVC exists with storage class but not bound yet - wait for it to bind
152+ logger .debug (f"PVC has storage class '{ pvc .spec .storageClassName } ', waiting for it to be bound..." )
153+ foundReadyPVC = waitForPVC (dynClient , namespace = pvcNamespace , pvcName = pvcName )
154+ if foundReadyPVC :
155+ logger .info ("OpenShift Pipelines postgres is installed and ready" )
156+ return True
157+ else :
158+ logger .error ("OpenShift Pipelines postgres PVC is NOT ready" )
159+ return False
160+
127161
128- def addMissingStorageClassToTektonPVC (dynClient : DynamicClient , namespace : str , pvcName : str , storageClassName : str ) -> bool :
162+ def addMissingStorageClassToTektonPVC (dynClient : DynamicClient , namespace : str , pvcName : str , storageClassName : str = None ) -> bool :
129163 """
130164 OpenShift Pipelines has a problem when there is no default storage class defined in a cluster, this function
131165 patches the PVC used to store pipeline results to add a specific storage class into the PVC spec and waits for the
@@ -137,18 +171,49 @@ def addMissingStorageClassToTektonPVC(dynClient: DynamicClient, namespace: str,
137171 :type namespace: str
138172 :param pvcName: Name of the PVC that we want to fix
139173 :type pvcName: str
140- :param storageClassName: Name of the storage class that we want to update the PVC to reference
174+ :param storageClassName: Name of the storage class that we want to update the PVC to reference (optional, will auto-select if not provided)
141175 :type storageClassName: str
142- :return: Description
176+ :return: True if PVC is successfully patched and bound, False otherwise
143177 :rtype: bool
144178 """
145179 pvcAPI = dynClient .resources .get (api_version = "v1" , kind = "PersistentVolumeClaim" )
180+ storageClassAPI = dynClient .resources .get (api_version = "storage.k8s.io/v1" , kind = "StorageClass" )
181+
146182 try :
147183 pvc = pvcAPI .get (name = pvcName , namespace = namespace )
184+
185+ # Check if PVC is pending and has no storage class
148186 if pvc .status .phase == "Pending" and pvc .spec .storageClassName is None :
149- pvc .spec .storageClassName = storageClassName
187+ # Determine which storage class to use
188+ targetStorageClass = None
189+
190+ if storageClassName is not None :
191+ # Verify the provided storage class exists
192+ try :
193+ storageClassAPI .get (name = storageClassName )
194+ targetStorageClass = storageClassName
195+ logger .info (f"Using provided storage class '{ storageClassName } ' for PVC { pvcName } " )
196+ except NotFoundError :
197+ logger .warning (f"Provided storage class '{ storageClassName } ' not found, will try to detect available storage class" )
198+
199+ # If no valid custom storage class, try to detect one
200+ if targetStorageClass is None :
201+ logger .warning ("No storage class provided or provided storage class not found, attempting to use first available storage class" )
202+ storageClasses = getStorageClasses (dynClient )
203+ if len (storageClasses ) > 0 :
204+ # Use the first available storage class
205+ targetStorageClass = storageClasses [0 ].metadata .name
206+ logger .info (f"Using first available storage class '{ targetStorageClass } ' for PVC { pvcName } " )
207+ else :
208+ logger .error (f"Unable to set storageClassName in PVC { pvcName } . No storage classes available in the cluster." )
209+ return False
210+
211+ # Patch the PVC with the storage class
212+ pvc .spec .storageClassName = targetStorageClass
213+ logger .info (f"Patching PVC { pvcName } with storageClassName: { targetStorageClass } " )
150214 pvcAPI .patch (body = pvc , namespace = namespace )
151215
216+ # Wait for the PVC to be bound
152217 maxRetries = 60
153218 foundReadyPVC = False
154219 retries = 0
@@ -158,6 +223,7 @@ def addMissingStorageClassToTektonPVC(dynClient: DynamicClient, namespace: str,
158223 patchedPVC = pvcAPI .get (name = pvcName , namespace = namespace )
159224 if patchedPVC .status .phase == "Bound" :
160225 foundReadyPVC = True
226+ logger .info (f"PVC { pvcName } is now bound" )
161227 else :
162228 logger .debug (f"Waiting 5s for PVC { pvcName } to be bound before checking again ..." )
163229 sleep (5 )
@@ -166,6 +232,9 @@ def addMissingStorageClassToTektonPVC(dynClient: DynamicClient, namespace: str,
166232 return False
167233
168234 return foundReadyPVC
235+ else :
236+ logger .warning (f"PVC { pvcName } is not in Pending state or already has a storageClassName" )
237+ return pvc .status .phase == "Bound"
169238
170239 except NotFoundError :
171240 logger .error (f"PVC { pvcName } does not exist" )
0 commit comments