Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3375054
[patch] apply RABC for target version during upgrade
May 19, 2026
c83bebd
[patch] check permissionMode during upgrade and test with 91x to 92x-…
May 21, 2026
d9cc9ec
[patch] add ansible-devops and python-devops changes
May 21, 2026
72deccb
[patch] fix import getPermissionMode issue
May 21, 2026
a80e82a
Merge branch 'master' into upgrade-fix
May 21, 2026
f03bc44
update ansible-devops and python devops to merge latest changes
May 21, 2026
5441dfd
update combilitiy matrix for testing
May 21, 2026
b675e93
[patch] fix tests
May 21, 2026
cfc1c31
[patch] Update getpermissionMode and fix issues with getting apps lis…
May 21, 2026
588c0bb
[patch] Update error msg for minimal mode and fix syntax issue with s…
May 22, 2026
63c4be5
Merge branch 'master' into upgrade-fix
May 22, 2026
94216be
[patch] remove upgrade path that was added for testing 9.1.x to 9.2.x…
May 22, 2026
c293ab8
[patch] delete zip files for the ansible devops and python devops
May 22, 2026
efdfc4f
Merge branch 'master' into upgrade-fix
May 22, 2026
92ded53
[patch] add the 91x to 92x-feature back to the upgrade path
May 22, 2026
7018f83
Merge branch 'master' into upgrade-fix
May 26, 2026
768880d
Merge branch 'master' into upgrade-fix
May 27, 2026
5949796
[patch] Update the update and upgrade pipeline to use same functions …
May 27, 2026
2fa1e4f
[patch] comment out the prerelease version for target version
May 29, 2026
6b011cc
Merge branch 'master' into upgrade-fix
May 29, 2026
5682b8b
[patch] fix version formats for validation
May 29, 2026
c7bfebf
[patch] Update methods to reuse same pattern across update, upgrade a…
May 29, 2026
cb25a1c
[patch] Update evaluatePreInstallRBACAccess function
May 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 36 additions & 35 deletions python/src/mas/cli/install/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
testCLI,
launchInstallPipeline,
)
from mas.devops.pre_install import applyPreInstallMASRBAC, permissionCheckForRBAC
from mas.devops.pre_install import applyPreInstallMASRBAC, requiresPreInstallRBAC

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -118,51 +118,52 @@ def getSelectedApps(self) -> list[str]:
return selectedApps

def evaluatePreInstallRBACAccess(self) -> None:
"""
Evaluate if pre-install RBAC should be applied using requiresPreInstallRBAC().
Sets self.applyPreInstallMASRBAC flag based on the result.
"""
self.applyPreInstallMASRBAC = False

if not isVersionEqualOrAfter("9.2.0", self.getParam("mas_channel")):
return

if self.mas_permission_mode == "minimal":
return

if self.skip_preinstall_rbac:
return

permissionResults = permissionCheckForRBAC(self.dynamicClient)
hasPreInstallRBACAccess = all(result["allowed"] for result in permissionResults)

if hasPreInstallRBACAccess:
self.applyPreInstallMASRBAC = True
return

if self.isInteractiveMode:
self.printDescription(
[
"",
f"You selected the '{self.mas_permission_mode}' permission mode.",
"The pre-install RBAC required for this permission mode has not been applied by your current cluster login.",
"This step must be completed by an OpenShift cluster administrator before MAS installation can continue.",
"Ask your OpenShift administrator to run 'mas pre-install' for this MAS instance, MAS channel, permission mode, and selected apps.",
"If that has already been done, you can continue the installation without applying it again.",
]
)
try:
# Use centralized function to check if RBAC should be applied
if requiresPreInstallRBAC(self.dynamicClient, self.getParam("mas_channel"), self.mas_permission_mode, self.skip_preinstall_rbac):
self.applyPreInstallMASRBAC = True
logger.info("Pre-install RBAC will be applied during installation")
else:
logger.info("Pre-install RBAC will be skipped during installation")

if not self.yesOrNo("Has your OpenShift administrator already run 'mas pre-install' for this installation"):
except PermissionError as e:
# User doesn't have RBAC permissions - prompt or fail
logger.warning(f"Permission error when checking RBAC requirements: {e}")
if not self.isInteractiveMode:
# Non-interactive mode - fail with clear message
self.fatalError(
"Installation aborted. Ask your OpenShift administrator to run 'mas pre-install' for this installation and then run 'mas install' again with --skip-preinstall-rbac using the same permission mode that was used in 'mas pre-install'."
f"You selected the '{self.mas_permission_mode}' permission mode.\n"
"The pre-install RBAC required for this permission mode has not been applied by your current cluster login.\n"
"This step must be completed by an OpenShift cluster administrator before MAS installation can continue.\n"
f"Ask your OpenShift administrator to run 'mas pre-install --mas-instance-id {self.getParam('mas_instance_id')} --mas-channel {self.getParam('mas_channel')}' "
"and then rerun 'mas install' with --skip-preinstall-rbac using the same permission mode that was used in 'mas pre-install'."
)
else:
self.fatalError(
"\n".join(
else:
# Interactive mode - ask if admin already applied RBAC
self.printDescription(
[
"",
f"You selected the '{self.mas_permission_mode}' permission mode.",
"The pre-install RBAC required for this permission mode has not been applied by your current cluster login.",
"This step must be completed by an OpenShift cluster administrator before MAS installation can continue.",
"Ask your OpenShift administrator to run 'mas pre-install' for this installation and then rerun 'mas install' with --skip-preinstall-rbac using the same permission mode that was used in 'mas pre-install'.",
f"Ask your OpenShift administrator to run 'mas pre-install --mas-instance-id {self.getParam('mas_instance_id')} --mas-channel {self.getParam('mas_channel')}'.",
"If that has already been done, you can continue the installation without applying it again.",
]
)
)

if not self.yesOrNo("Has your OpenShift administrator already run 'mas pre-install' for this installation"):
self.fatalError(
f"Installation aborted. Ask your OpenShift administrator to run 'mas pre-install --mas-instance-id {self.getParam('mas_instance_id')} --mas-channel {self.getParam('mas_channel')}' "
"and then run 'mas install' again with --skip-preinstall-rbac using the same permission mode that was used in 'mas pre-install'."
)
# User confirmed RBAC was already applied, continue with installation
logger.info("User confirmed pre-install RBAC was already applied by administrator, continuing with installation")

@logMethodCall
def validateCatalogSource(self):
Expand Down
169 changes: 167 additions & 2 deletions python/src/mas/cli/update/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,74 @@
from .argParser import updateArgParser
from mas.devops.data import getCatalog, getNewestCatalogTag
from mas.devops.ocp import createNamespace, getConsoleURL, getClusterVersion, isClusterVersionInRange
from mas.devops.mas import listMasInstances, getCurrentCatalog
from mas.devops.mas import listMasInstances, getCurrentCatalog, getMasChannel, getInstalledAppsForRBAC
from mas.devops.aiservice import listAiServiceInstances
from mas.devops.tekton import preparePipelinesNamespace, installOpenShiftPipelines, updateTektonDefinitions, launchUpdatePipeline, prepareUpdateSecrets
from mas.devops.pre_install import applyPreInstallMASRBAC, requiresPreInstallRBAC
from mas.devops.utils import isVersionEqualOrAfter, extractBaseVersion, isPreReleaseVersion
from ..install.settings import AdditionalConfigsMixin

logger = logging.getLogger(__name__)


class UpdateApp(BaseApp, AdditionalConfigsMixin):

def shouldApplyRBACForInstance(self, instanceId, currentVersion, targetCatalog) -> bool:
"""
Determine if RBAC should be applied for a specific MAS instance during update.
Returns True if the instance is transitioning from pre-release to GA version.

This method checks:
1. Current version is a pre-release (e.g., "9.2.0-pre.stable+21734")
2. Target catalog contains a GA version (e.g., "9.2.0") for the instance's channel
3. The GA version is >= 9.2.0 (when RBAC was introduced)

Args:
instanceId: The MAS instance ID
currentVersion: The current reconciled version (e.g., "9.2.0-pre.stable+21734")
targetCatalog: The target catalog dictionary containing version mappings

Returns:
bool: True if RBAC should be applied for pre-release → GA transition, False otherwise
"""
if not currentVersion or not targetCatalog:
return False

# Check if current version is a pre-release
if not isPreReleaseVersion(currentVersion):
return False

# Get the channel this instance is subscribed to
channel = getMasChannel(self.dynamicClient, instanceId)
if not channel:
logger.warning(f"Could not determine channel for instance {instanceId}")
return False

# Get the target version from the catalog for this channel
targetVersion = targetCatalog.get("mas_core_version", {}).get(channel)
if not targetVersion:
logger.warning(f"No target version found in catalog for channel {channel}")
return False

# Check if target version is GA (not pre-release)
# COMMENTED OUT FOR TESTING: Force RBAC application even for pre-release targets
# if isPreReleaseVersion(targetVersion):
# logger.info(f"Instance {instanceId} will stay on pre-release (current: {currentVersion}, target: {targetVersion}). Skipping RBAC.")
# return False
logger.info(f"TEST MODE: Forcing RBAC application (current: {currentVersion}, target: {targetVersion})")

# Extract base version from target version
baseVersion = extractBaseVersion(targetVersion)
fullVersion = f"{baseVersion}.0"

# Only apply RBAC if target GA version is >= 9.2.0
if isVersionEqualOrAfter("9.2.0", fullVersion):
logger.info(f"Instance {instanceId} will transition from pre-release to GA (current: {currentVersion}, target: {targetVersion}).")
return True

logger.info(f"Instance {instanceId} target version {targetVersion} is < 9.2.0. Skipping RBAC.")
return False

def update(self, argv):
"""
Update MAS instance
Expand All @@ -40,6 +98,8 @@ def update(self, argv):
self.noConfirm = self.args.no_confirm
self.devMode = self.args.dev_mode
self.db2LicenseFileLocal = None
self.skipPreinstallRbac = self.args.skip_preinstall_rbac if hasattr(self.args, "skip_preinstall_rbac") else False
self.noConfirm = self.args.no_confirm if hasattr(self.args, "no_confirm") else False

if self.args.mas_catalog_version:
# Non-interactive mode
Expand Down Expand Up @@ -81,7 +141,7 @@ def update(self, argv):
self.setParam(key, value)

# Arguments that we don't need to do anything with
elif key in ["no_confirm", "help"]:
elif key in ["no_confirm", "help", "skip_preinstall_rbac"]:
pass

# Db2 License file has special handling
Expand Down Expand Up @@ -213,6 +273,111 @@ def update(self, argv):
h.stop_and_persist(symbol=self.successIcon, text="OpenShift Pipelines Operator installation failed")
self.fatalError("Installation failed")

# Apply pre-install RBAC for instances transitioning from pre-release to GA
# This handles the 9.2.0-pre.stable → 9.2.0 transition
try:
masInstances = listMasInstances(self.dynamicClient)
instancesNeedingRBAC = []

for instance in masInstances:
instanceId = instance["metadata"]["name"]
currentVersion = instance.get("status", {}).get("versions", {}).get("reconciled", "")

if self.shouldApplyRBACForInstance(instanceId, currentVersion, self.chosenCatalog):
channel = getMasChannel(self.dynamicClient, instanceId)
targetVersion = self.chosenCatalog.get("mas_core_version", {}).get(channel, "")
instancesNeedingRBAC.append({"id": instanceId, "currentVersion": currentVersion, "targetVersion": targetVersion, "channel": channel})

if instancesNeedingRBAC:
print()
print_formatted_text(
HTML(f"<Yellow>Detected {len(instancesNeedingRBAC)} MAS instance(s) on pre-release versions that will transition to GA.</Yellow>")
)
print_formatted_text(HTML("<Yellow>Applying RBAC for target GA version before catalog update...</Yellow>"))
print()

for instanceInfo in instancesNeedingRBAC:
instanceId = instanceInfo["id"]
currentVersion = instanceInfo["currentVersion"]
targetVersion = instanceInfo["targetVersion"]

# Extract base version from TARGET version for RBAC
baseVersion = extractBaseVersion(targetVersion)

# Ensure version has patch number for semver validation (9.2 -> 9.2.0)
fullVersion = f"{baseVersion}.0" if baseVersion.count(".") == 1 else baseVersion

# For pre-release to 9.2 GA transition, default to cluster mode
permissionMode = "cluster"
logger.info(
f"Using cluster mode for pre-release to GA transition for instance {instanceId} (current: {currentVersion}, target: {targetVersion})"
)

# Check permissions and apply RBAC
# requiresPreInstallRBAC checks permissions and raises PermissionError if user lacks access
try:
if requiresPreInstallRBAC(self.dynamicClient, fullVersion, permissionMode, self.skipPreinstallRbac):
# Get installed apps for this instance
selectedApps = getInstalledAppsForRBAC(self.dynamicClient, instanceId)

with Halo(
text=f"Applying pre-install RBAC for {instanceId} (v{baseVersion}, mode: {permissionMode})", spinner=self.spinner
) as h:
applyPreInstallMASRBAC(
dynClient=self.dynamicClient,
masVersion=baseVersion,
masInstanceId=instanceId,
permissionMode=permissionMode,
selectedApps=selectedApps,
)
h.stop_and_persist(
symbol=self.successIcon, text=f"Pre-install RBAC applied for {instanceId} (v{baseVersion}, mode: {permissionMode})"
)
except PermissionError as e:
# User doesn't have RBAC permissions - prompt or fail
logger.warning(f"Permission error when checking RBAC requirements: {e}")
if self.noConfirm:
# Non-interactive mode - fail with clear message
self.fatalError(
f"Instance {instanceId} is transitioning from pre-release to GA version {baseVersion}.\n"
f"The pre-install RBAC required for '{permissionMode}' permission mode has not been applied by your current cluster login.\n"
"This step must be completed by an OpenShift cluster administrator before MAS update can continue.\n"
f"Ask your OpenShift administrator to run 'mas pre-install --mas-instance-id {instanceId} --mas-channel {baseVersion}' "
"and then rerun 'mas update' with --skip-preinstall-rbac."
)
else:
# Interactive mode - ask if admin already applied RBAC
self.printDescription(
[
"",
f"Instance {instanceId} is transitioning from pre-release to GA version {baseVersion}.",
f"The pre-install RBAC required for '{permissionMode}' permission mode has not been applied by your current cluster login.",
"This step must be completed by an OpenShift cluster administrator before MAS update can continue.",
f"Ask your OpenShift administrator to run 'mas pre-install --mas-instance-id {instanceId} --mas-channel {baseVersion}'.",
"If that has already been done, you can continue the update without applying it again.",
]
)

if not self.yesOrNo(f"Has your OpenShift administrator already run 'mas pre-install' for instance {instanceId}"):
self.fatalError(
f"Update aborted for instance {instanceId}. Ask your OpenShift administrator to run 'mas pre-install --mas-instance-id {instanceId} --mas-channel {baseVersion}' "
"and then run 'mas update' again with --skip-preinstall-rbac."
)
# User confirmed RBAC was already applied, continue with update
logger.info(
f"User confirmed pre-install RBAC was already applied by administrator for instance {instanceId}, continuing with update"
)

print()
print_formatted_text(HTML("<Green>✓ Pre-install RBAC applied successfully for all instances transitioning to GA</Green>"))
print()
else:
logger.info("No MAS instances require RBAC update (not transitioning from pre-release to GA)")

except Exception as e:
logger.error(f"Error while applying pre-install RBAC: {e}")
self.printWarning(f"Failed to apply pre-install RBAC: {e}\n" f"Continuing with update, but RBAC may need to be applied manually.")

with Halo(text=f"Preparing namespace ({pipelinesNamespace})", spinner=self.spinner) as h:
createNamespace(self.dynamicClient, pipelinesNamespace)
preparePipelinesNamespace(dynClient=self.dynamicClient)
Expand Down
7 changes: 7 additions & 0 deletions python/src/mas/cli/update/argParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,13 @@ def parse_args(self, args=None, namespace=None): # type: ignore[override]
default=False,
help="Skips the 'pre-update-check' and 'post-update-verify' tasks in the update pipeline",
)
otherArgGroup.add_argument(
"--skip-preinstall-rbac",
required=False,
action="store_true",
default=False,
help="Skip CLI application of pre-install MAS RBAC. Use this when an OpenShift administrator has already applied the required RBAC.",
)
otherArgGroup.add_argument("--slack-token", required=False, help="Slack bot token for sending pipeline status notifications")
otherArgGroup.add_argument("--slack-channel", required=False, help="Slack channel(s) for pipeline notifications (comma-separated for multiple channels)")
otherArgGroup.add_argument(
Expand Down
Loading
Loading