Skip to content

Commit ae566c5

Browse files
authored
[patch] fix for postgres db pvc bound issue (#165)
1 parent 0ab0147 commit ae566c5

1 file changed

Lines changed: 82 additions & 13 deletions

File tree

src/mas/devops/tekton.py

Lines changed: 82 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
from 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

2727
logger = 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

Comments
 (0)