From 14fd9f0a0638bd5090ad293a50f4e3b535ddf20b Mon Sep 17 00:00:00 2001 From: Aneri Thakkar Date: Fri, 22 May 2026 12:43:40 +0530 Subject: [PATCH] [patch] add detect permission mode function for pre-upgrade checks --- src/mas/devops/mas/__init__.py | 1 + src/mas/devops/mas/suite.py | 90 ++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/src/mas/devops/mas/__init__.py b/src/mas/devops/mas/__init__.py index 333f109f..6e83a342 100644 --- a/src/mas/devops/mas/__init__.py +++ b/src/mas/devops/mas/__init__.py @@ -14,4 +14,5 @@ getMasChannel, updateIBMEntitlementKey, getMasPublicClusterIssuer, + getPermissionMode, ) diff --git a/src/mas/devops/mas/suite.py b/src/mas/devops/mas/suite.py index 01020be8..0042257a 100644 --- a/src/mas/devops/mas/suite.py +++ b/src/mas/devops/mas/suite.py @@ -357,3 +357,93 @@ def getMasPublicClusterIssuer(dynClient: DynamicClient, instanceId: str) -> str except UnauthorizedError as e: logger.error(f"Error: Unable to retrieve MAS instance due to failed authorization: {e}") return None + + +def getPermissionMode(dynClient: DynamicClient, instanceId: str) -> str | None: + """ + Detect the current RBAC permission mode for a MAS instance. + + This function determines whether MAS is installed with cluster-level permissions, + namespace-scoped permissions (essential + non-essential), or minimal essential-only + permissions by checking for the existence of RBAC resources in the cluster. + + RBAC Resource Distribution: + - Cluster mode: ClusterRoles + Essential Roles + - Namespaced mode: Essential Roles + Non-essential Roles + - Minimal mode: Essential Roles ONLY + + Detection Logic: + 1. Check for ClusterRoles → cluster mode + 2. Check for non-essential openshift-marketplace Role → namespaced mode + 3. No ClusterRole and no openshift-marketplace Role → minimal mode + + Args: + dynClient (DynamicClient): OpenShift dynamic client for cluster API interactions. + instanceId (str): The MAS instance identifier. + + Returns: + str: Permission mode - "cluster", "namespaced", or "minimal" + Returns None if unable to determine (e.g., no RBAC resources found) + """ + try: + # Step 1: Check for ClusterRoles (indicates cluster mode) + clusterRoleAPI = dynClient.resources.get(api_version="rbac.authorization.k8s.io/v1", kind="ClusterRole") + + # Look for MAS ClusterRoles with the instance ID pattern + clusterRoleName = f"mas:{instanceId}:core:coreapi" + try: + clusterRoleAPI.get(name=clusterRoleName) + logger.info(f"Found ClusterRole '{clusterRoleName}' - permission mode is 'cluster'") + return "cluster" + except NotFoundError: + logger.debug(f"ClusterRole '{clusterRoleName}' not found, checking for non-essential Roles") + + # Step 2: Check for non-essential openshift-marketplace Role (only exists in namespaced mode) + roleAPI = dynClient.resources.get(api_version="rbac.authorization.k8s.io/v1", kind="Role") + + # This role only exists in namespaced mode (applied via role-non-essential-core-coreapi-openshift-marketplace.yaml) + marketplaceRoleName = f"mas:{instanceId}:core:coreapi:openshift-marketplace" + marketplaceNamespace = "openshift-marketplace" + + try: + roleAPI.get(name=marketplaceRoleName, namespace=marketplaceNamespace) + logger.info(f"Found non-essential Role '{marketplaceRoleName}' in namespace '{marketplaceNamespace}' - permission mode is 'namespaced'") + return "namespaced" + except NotFoundError: + logger.debug("Non-essential openshift-marketplace Role not found, checking for essential roles") + + # Step 3: Verify minimal mode by checking for essential roles in mas-{instanceId}-core namespace + # Essential roles have pattern: mas:{instanceId}:core:suite:{app}:essential + coreNamespace = f"mas-{instanceId}-core" + + # Try to find at least one essential role to confirm minimal mode + # Check common apps that might be installed + essentialRolePatterns = [ + f"mas:{instanceId}:core:suite:manage:essential", + f"mas:{instanceId}:core:suite:iot:essential", + f"mas:{instanceId}:core:suite:monitor:essential", + f"mas:{instanceId}:core:suite:predict:essential", + f"mas:{instanceId}:core:suite:arcgis:essential", + f"mas:{instanceId}:core:suite:facilities:essential", + f"mas:{instanceId}:core:suite:optimizer:essential", + f"mas:{instanceId}:core:suite:visualinspection:essential" + ] + + for essentialRoleName in essentialRolePatterns: + try: + roleAPI.get(name=essentialRoleName, namespace=coreNamespace) + logger.info(f"Found essential Role '{essentialRoleName}' in namespace '{coreNamespace}' with no non-essential roles - permission mode is 'minimal'") + return "minimal" + except NotFoundError: + continue + + # If we couldn't find any RBAC resources, return None + logger.warning(f"Unable to determine permission mode for instance '{instanceId}' ") + return None + + except ResourceNotFoundError: + logger.warning("Required API resources not found in the cluster") + return None + except UnauthorizedError as e: + logger.error(f"Error: Unable to check permissions due to failed authorization: {e}") + return None