diff --git a/docs/commands/pre-install.md b/docs/commands/pre-install.md index 7d55db46e0d..b9398ef33a0 100644 --- a/docs/commands/pre-install.md +++ b/docs/commands/pre-install.md @@ -7,7 +7,7 @@ Usage information can be obtained using `mas pre-install --help` ``` usage: mas pre-install [-i MAS_INSTANCE_ID] [--mas-channel MAS_CHANNEL] - [--permission-mode {cluster,namespaced}] + [--admin-mode {cluster,namespaced}] [--apps APPS] [--no-confirm] [-h] IBM Maximo Application Suite Admin CLI v21.3.0 @@ -24,8 +24,8 @@ Specify the target cluster and MAS instance for which pre-install RBAC should be The MAS instance ID for which pre-install RBAC will be set up --mas-channel MAS_CHANNEL The MAS channel used to select pre-install RBAC manifests, for example 9.2.x - --permission-mode {cluster,namespaced} - The permission mode used to determine which pre-install RBAC manifests are set up + --admin-mode {cluster,namespaced} + The admin mode used to determine which pre-install RBAC manifests are set up --apps APPS Comma-separated list of apps used to filter which pre-install RBAC manifests are set up (required for namespaced mode), for example core,manage,iot More: @@ -46,25 +46,25 @@ mas pre-install ``` ### Non-Interactive Pre-Install for Namespaced Mode -Set up pre-install RBAC for a MAS instance using namespaced permission mode (apps are required): +Set up pre-install RBAC for a MAS instance using namespaced admin mode (apps are required): ```bash mas pre-install \ --mas-instance-id prod1 \ --mas-channel 9.2.x \ - --permission-mode namespaced \ + --admin-mode namespaced \ --apps core,manage,iot \ --no-confirm ``` ### Pre-Install for Cluster Mode -Set up pre-install RBAC for cluster permission mode (apps are not required - automatically uses ibm-mas operator): +Set up pre-install RBAC for cluster admin mode (apps are not required - automatically uses ibm-mas operator): ```bash mas pre-install \ --mas-instance-id test1 \ --mas-channel 9.2.x \ - --permission-mode cluster \ + --admin-mode cluster \ --no-confirm ``` @@ -75,43 +75,48 @@ Set up pre-install RBAC for multiple MAS applications: mas pre-install \ --mas-instance-id prod1 \ --mas-channel 9.2.x \ - --permission-mode namespaced \ + --admin-mode namespaced \ --apps core,manage,monitor,iot,predict,visualinspection \ --no-confirm ``` -Notes + +Understanding MAS 9.2 Permission Model Changes ------------------------------------------------------------------------------- -### Pre-Install Process -The pre-install command performs the following operations: +Starting with MAS 9.2, there are significant changes to the permissions model used by Maximo Application Suite related to the ability of MAS to act as a delegated OpenShift administrator. -1. **Validates cluster administrator permissions** - Ensures you have the required permissions to create RBAC resources -2. **Validates MAS channel** - Confirms the channel is 9.2.x or later -3. **Validates application selection** - Ensures all specified applications are supported (only in namespaced mode) -4. **Applies RBAC manifests** - Creates ClusterRoles, ClusterRoleBindings, Roles and RoleBindings based on permission mode and selected applications +**By default, MAS operator packages operate in minimal permissions mode.** Compared to previous MAS releases, this means MAS has: -### When to Use Pre-Install +- **No ClusterRoles** - No cluster-wide permissions to access resources across all namespaces +- **No permissions to create namespaces** - Cannot create new application namespaces +- **No permissions in openshift-marketplace** - Cannot list PackageManifests, CatalogSources, or manage Subscriptions +- **Limited cross-namespace access** - Only essential namespace-scoped roles for specific operations (e.g., reading Secrets and ConfigMaps in application namespaces for binding) -The `mas pre-install` command is used to grant necessary RBAC permissions. +In this minimal permission mode, when you install Maximo Application Suite applications, each application operator will grant MAS only the **essential namespace-scoped permissions** required for basic operation (such as binding configuration). This means that the **non-essential** capabilities (installing new applications, managing application lifecycle, and viewing application status outside the core namespace) of the MAS administrative API and UI that were previously enabled by default are now **disabled by default**. -**If you have cluster administrator permissions:** -- Run `mas install` directly - it will automatically handle the pre-install RBAC setup +**To enable these administrative capabilities**, you must explicitly configure one of the elevated admin modes (**cluster** or **namespaced**). If you have cluster administrator permissions, you can specify `--admin-mode cluster` or `--admin-mode namespaced` when running `mas install` and the necessary RBAC will be applied automatically. If you do not have cluster administrator permissions, a cluster administrator must first run `mas pre-install` to set up the required RBAC, then you can proceed with `mas install`. -**If you do NOT have cluster administrator permissions:** -- A cluster administrator must run `mas pre-install` to grant the necessary permissions -- Then you can run `mas install --skip-preinstall-rbac` to proceed with installation -### Permission Modes +### Admin Modes -The following permission modes are supported with `mas pre-install`: +MAS 9.2 and later supports three admin modes that control the level of permissions granted to the MAS: -| Permission Mode | Description | Apps Required | +| Admin Mode | Description | Apps Required | |----------------|-------------|---------------| | **cluster** | MAS has cluster-level access to manage its applications and resources across the cluster. ClusterRoles are installed for the ibm-mas operator only. | No - automatically uses ibm-mas operator | | **namespaced** | No ClusterRoles are installed. MAS can manage resources only in namespaces prepared by the OpenShift admin. Roles are created for selected applications. | Yes - must specify apps | -**Note:** In minimal permission mode, essential roles are installed by each operator during installation, so `mas pre-install` is not required. +**Note:** If the cluster is intended to run in minimal mode, `mas pre-install` is not required as essential roles are installed by each operator during installation. + +### When to Use Pre-Install + +The `mas pre-install` command sets up RBAC (Role-Based Access Control) permissions for your MAS instance by creating ClusterRoles, Roles, and RoleBindings based on the selected admin mode and applications. This is required when you do not have cluster administrator permissions and want to use **cluster** or **namespaced** mode. + +When you run `mas install` with **cluster** or **namespaced** mode and you don't have cluster administrator permissions, `mas install` cannot create the required RBAC for these modes. A cluster administrator must first run `mas pre-install` to set up these permissions, then you can proceed with `mas install`. + +If you have cluster administrator permissions, you can skip `mas pre-install` and run `mas install` directly - it will automatically handle the RBAC setup for **cluster** or **namespaced** mode. For **minimal** mode, `mas pre-install` is not required as essential roles are installed by operators during installation. + ### Supported Applications @@ -130,39 +135,6 @@ The following applications are supported for pre-install RBAC setup: You can specify multiple applications as a comma-separated list (e.g., `core,manage,iot`). -### Integration with MAS Install - -After running `mas pre-install`, proceed with the MAS installation using the `--skip-preinstall-rbac` flag to avoid re-applying the RBAC resources. Use the same permission mode that was used during pre-install for issuerKind selection: - -```bash -mas install \ - --mas-instance-id prod1 \ - --permission-mode namespaced \ - --skip-preinstall-rbac \ - ... other install parameters ... -``` - -### Workflow Example - -**Step 1: Cluster Administrator runs `mas pre-install`** -```bash -mas pre-install \ - --mas-instance-id prod1 \ - --mas-channel 9.2.x \ - --permission-mode namespaced \ - --apps core,manage,iot \ - --no-confirm -``` - -**Step 2: MAS Installer (with limited permissions) runs `mas install`** -```bash -mas install \ - --mas-instance-id prod1 \ - --permission-mode namespaced \ - --skip-preinstall-rbac \ - ... other parameters ... -``` - ### Requirements - OpenShift cluster administrator permissions @@ -170,23 +142,4 @@ mas install \ - Access to the target OpenShift cluster !!! warning "Administrator Permissions Required" - The `mas pre-install` command requires cluster administrator permissions. If you do not have these permissions, the command will fail with an error message. Only a cluster administrator can set up pre-install RBAC for MAS. - -### Interactive Mode - -When running without all required options, the command enters interactive mode and will prompt for: - -1. Target OpenShift cluster connection -2. MAS instance ID -3. MAS channel (for example 9.2.x) -4. Permission mode (cluster or namespaced) -5. Applications to include (comma-separated list) - **only for namespaced mode** - -The command will display a summary of your selections and ask for confirmation before applying the RBAC resources. - -**Note:** In cluster mode, applications are not prompted as the ibm-mas operator is automatically used. - -### Version Compatibility - -!!! important - This command is only supported for MAS version 9.2 and later. If you specify an earlier mas version, the command will fail with an error message. \ No newline at end of file + The `mas pre-install` command requires cluster administrator permissions. If you do not have these permissions, the command will fail with an error message. \ No newline at end of file diff --git a/image/cli/mascli/functions/internal/save_config b/image/cli/mascli/functions/internal/save_config index d7a85931e52..a7e139af160 100644 --- a/image/cli/mascli/functions/internal/save_config +++ b/image/cli/mascli/functions/internal/save_config @@ -42,7 +42,7 @@ export MAS_DOMAIN=$MAS_DOMAIN export CLUSTER_ISSUER_SELECTION=$CLUSTER_ISSUER_SELECTION export MAS_CLUSTER_ISSUER=$MAS_CLUSTER_ISSUER -export MAS_PERMISSION_MODE=$MAS_PERMISSION_MODE +export MAS_ADMIN_MODE=$MAS_ADMIN_MODE export MAS_ROUTING_MODE=$MAS_ROUTING_MODE export MAS_INGRESS_CONTROLLER_NAME=$MAS_INGRESS_CONTROLLER_NAME diff --git a/python/src/mas/cli/aiservice/install/app.py b/python/src/mas/cli/aiservice/install/app.py index 4bfbeaee56e..3c4c5db10a2 100644 --- a/python/src/mas/cli/aiservice/install/app.py +++ b/python/src/mas/cli/aiservice/install/app.py @@ -77,7 +77,7 @@ def evaluatePreInstallRBACAccess(self) -> None: if not isVersionEqualOrAfter("9.2.0", self.getParam("aiservice_channel")): return - if self.skip_preinstall_rbac: + if self.admin_mode == "minimal": return permissionResults = permissionCheckForRBAC(self.dynamicClient) @@ -87,37 +87,42 @@ def evaluatePreInstallRBACAccess(self) -> None: self.applyPreInstallMASRBAC = True return - if self.isInteractiveMode: + self.printH2("Pre-Install RBAC Configuration") + # User does not have permissions to apply RBAC + self.printDescription( + [ + f"Admin mod: '{self.admin_mode}'.", + "Pre-install RBAC could not be applied automatically (insufficient permissions).", + ] + ) + + if self.noConfirm: + self.printDescription( + [ + f"Installation will continue with the selected '{self.admin_mode}' admin mode.", + "The current user does not have sufficient permissions to apply the pre-install RBAC automatically.", + "With the --no-confirm flag, the installation assumes the required RBAC has already been applied by your OpenShift administrator.", + "If it has not been applied, ensure your OpenShift administrator runs 'mas pre-install' with the same admin mode before the installation proceeds.", + ] + ) + else: self.printDescription( [ "", - f"You selected the '{self.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 AI Service installation can continue.", - "Ask your OpenShift administrator to run 'mas pre-install' for this AI Service instance.", - "If that has already been done, you can continue the installation without applying it again.", + "Ask your OpenShift administrator to run 'mas pre-install' for this AI Service instance, channel, admin mode, and selected apps.", + "If that has already been done, you can continue the installation.", ] ) if not self.yesOrNo("Has your OpenShift administrator already run 'mas pre-install' for this AI Service installation"): self.fatalError( - "Installation aborted. Ask your OpenShift administrator to run 'mas pre-install' for this AI Service installation and then run 'mas aiservice-install' again with --skip-preinstall-rbac." + "Installation aborted. Ask your OpenShift administrator to run 'mas pre-install' for this AI Service installation and then run 'mas aiservice-install' again using the same admin mode." ) - else: - self.fatalError( - "\n".join( - [ - f"You selected the '{self.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 AI Service installation can continue.", - "Ask your OpenShift administrator to run 'mas pre-install' for this installation and then rerun 'mas aiservice-install' with --skip-preinstall-rbac.", - ] - ) - ) - def configPermissionMode(self) -> None: + def configAdminMode(self) -> None: if self.showAdvancedOptions: - self.printH1("Configure Permission Mode") + self.printH1("Configure Admin Mode") self.printDescription( [ "Choose how AI Service should be installed with respect to permissions:", @@ -138,11 +143,11 @@ def configPermissionMode(self) -> None: ] ) - permissionModeInt = self.promptForInt("Permission Mode", default=1, min=1, max=3) - permissionModeMap = {1: "cluster", 2: "namespaced", 3: "minimal"} - self.permission_mode = permissionModeMap[permissionModeInt] - elif self.permission_mode == "": - self.permission_mode = "cluster" + adminModeInt = self.promptForInt("Admin Mode", default=1, min=1, max=3) + adminModeMap = {1: "cluster", 2: "namespaced", 3: "minimal"} + self.admin_mode = adminModeMap[adminModeInt] + elif self.admin_mode == "": + self.admin_mode = "cluster" @logMethodCall def processCatalogChoice(self) -> list: @@ -254,9 +259,8 @@ def interactiveMode(self, simplified: bool, advanced: bool) -> None: ["Db2 Universal Operator for v12 onwards requires to add a License activation key", "If you don't have a license press enter to continue."] ) self.db2LicenseFileLocal = self.promptForFile("Db2 License file", envVar="DB2_LICENSE_FILE", default="", mustExist=False) - # Permission mode prompt (especially in dev mode) if isVersionEqualOrAfter("9.2.0", self.getParam("aiservice_channel")): - self.configPermissionMode() + self.configAdminMode() @logMethodCall def nonInteractiveMode(self) -> None: @@ -424,7 +428,6 @@ def nonInteractiveMode(self) -> None: "accept_license", "dev_mode", "skip_pre_check", - "skip_preinstall_rbac", "skip_grafana_install", "no_confirm", "help", @@ -466,12 +469,16 @@ def nonInteractiveMode(self) -> None: self.validateCatalogSource() self.licensePrompt() - if self.permission_mode != "" and not isVersionEqualOrAfter("9.2.0", self.getParam("aiservice_channel")): - self.fatalError("--permission-mode is supported only for AI Service releases aligned to MAS 9.2.0 and later") - - # Set default permission_mode for 9.2.0+ if not provided - if isVersionEqualOrAfter("9.2.0", self.getParam("aiservice_channel")) and self.permission_mode == "": - self.permission_mode = "cluster" + # Validate admin mode based on AI Service version + if isVersionEqualOrAfter("9.2.0", self.getParam("aiservice_channel")): + # AI Service 9.2+: --admin-mode is REQUIRED + if self.admin_mode == "": + self.fatalError( + f"--admin-mode is required for MAS version 9.2 or higher (selected channel: {self.getParam('aiservice_channel')}). Valid options: cluster, namespaced, minimal" + ) + else: + if self.admin_mode != "": + self.fatalError(f"--admin-mode is not supported for MAS version 9.1 and earlier (selected channel: {self.getParam('aiservice_channel')})") @logMethodCall def install(self, argv): @@ -487,7 +494,7 @@ def install(self, argv): self.noConfirm = args.no_confirm self.licenseAccepted = args.accept_license self.devMode = args.dev_mode - self.permission_mode = args.permission_mode if args.permission_mode else "" + self.admin_mode = args.admin_mode if args.admin_mode else "" self.printDescription( [ @@ -515,8 +522,6 @@ def install(self, argv): if args.skip_pre_check: self.setParam("skip_pre_check", "true") - self.skip_preinstall_rbac = hasattr(args, "skip_preinstall_rbac") and args.skip_preinstall_rbac - if instanceId is None: self.printH1("Set Target OpenShift Cluster") # Connect to the target cluster @@ -625,7 +630,7 @@ def install(self, argv): dynClient=self.dynamicClient, masVersion=".".join(self.getParam("aiservice_channel").split(".")[:2]), masInstanceId=self.getParam("aiservice_instance_id"), - permissionMode=self.permission_mode, + adminMode=self.admin_mode, selectedApps=["aiservice"], ) h.stop_and_persist(symbol=self.successIcon, text=f"Pre-install RBAC for AI Service is ready for {self.getParam('aiservice_instance_id')}") diff --git a/python/src/mas/cli/aiservice/install/argBuilder.py b/python/src/mas/cli/aiservice/install/argBuilder.py index f71c7fae4a9..7404790045c 100644 --- a/python/src/mas/cli/aiservice/install/argBuilder.py +++ b/python/src/mas/cli/aiservice/install/argBuilder.py @@ -96,10 +96,8 @@ def buildCommand(self) -> str: command += f" --dev-mode{newline}" if self.getParam("skip_pre_check") is True: command += f" --skip-pre-check{newline}" - if self.permission_mode != "": - command += f' --permission-mode "{self.permission_mode}"{newline}' - if self.skip_preinstall_rbac: - command += f" --skip-preinstall-rbac{newline}" + if self.admin_mode != "": + command += f' --admin-mode "{self.admin_mode}"{newline}' if self.getParam("image_pull_policy") != "": command += f" --image-pull-policy {self.getParam('image_pull_policy')}{newline}" if self.getParam("service_account_name") != "": diff --git a/python/src/mas/cli/aiservice/install/argParser.py b/python/src/mas/cli/aiservice/install/argParser.py index 9346386466e..9c668dfa1fd 100644 --- a/python/src/mas/cli/aiservice/install/argParser.py +++ b/python/src/mas/cli/aiservice/install/argParser.py @@ -207,14 +207,11 @@ def isValidFile(parser, arg) -> str: help="Provide the name of the Issuer to configure AI Service to issue certificates", ) aiserviceAdvancedArgGroup.add_argument( - "--permission-mode", - dest="permission_mode", + "--admin-mode", + dest="admin_mode", required=False, choices=["cluster", "namespaced", "minimal"], - help="The permission mode used to determine which pre-install RBAC manifests are applied for AI Service (MAS 9.2+ advanced option)", -) -aiserviceAdvancedArgGroup.add_argument( - "--skip-preinstall-rbac", dest="skip_preinstall_rbac", required=False, action="store_true", help="Skip pre-install RBAC setup (non-interactive mode only)" + help="The admin mode used to determine which pre-install RBAC manifests are applied for AI Service (MAS 9.2+)", ) aiserviceAdvancedArgGroup.add_argument( "--enable-ipv6", diff --git a/python/src/mas/cli/aiservice/install/params.py b/python/src/mas/cli/aiservice/install/params.py index 7625c418c9a..7b090363777 100644 --- a/python/src/mas/cli/aiservice/install/params.py +++ b/python/src/mas/cli/aiservice/install/params.py @@ -97,8 +97,8 @@ "configure_aiassistant", # Certificate Issuer "aiservice_certificate_issuer", - # permission mode - "permission_mode", + # admin mode + "admin_mode", # Enable IPv6 networking "enable_ipv6", # Slack diff --git a/python/src/mas/cli/aiservice/install/summarizer.py b/python/src/mas/cli/aiservice/install/summarizer.py index 8888d46cb6c..9d7d6809685 100644 --- a/python/src/mas/cli/aiservice/install/summarizer.py +++ b/python/src/mas/cli/aiservice/install/summarizer.py @@ -46,9 +46,12 @@ def aiServiceSummary(self) -> None: self.printParamSummary("Release", "aiservice_channel") self.printParamSummary("Instance ID", "aiservice_instance_id") self.printParamSummary("Environment Type", "environment_type") - if self.permission_mode not in [None, ""]: - self.printSummary("Permission Mode", self.permission_mode) - self.printSummary("Skip Pre-Install RBAC", "Yes" if self.skip_preinstall_rbac else "No") + if self.admin_mode not in [None, ""]: + self.printSummary("Admin Mode", self.admin_mode) + self.printSummary( + "Apply Pre-Install RBAC", + "Yes" if self.applyPreInstallMASRBAC else "No", + ) if "aiservice_certificate_issuer" in self.params: self.printParamSummary("Certificate Issuer", "aiservice_certificate_issuer") diff --git a/python/src/mas/cli/install/app.py b/python/src/mas/cli/install/app.py index a9433f55fa0..dec3a882ade 100644 --- a/python/src/mas/cli/install/app.py +++ b/python/src/mas/cli/install/app.py @@ -123,46 +123,47 @@ def evaluatePreInstallRBACAccess(self) -> None: if not isVersionEqualOrAfter("9.2.0", self.getParam("mas_channel")): return - if self.mas_permission_mode == "minimal": - return - - if self.skip_preinstall_rbac: + if self.mas_admin_mode == "minimal": return permissionResults = permissionCheckForRBAC(self.dynamicClient) hasPreInstallRBACAccess = all(result["allowed"] for result in permissionResults) - if hasPreInstallRBACAccess: self.applyPreInstallMASRBAC = True return - if self.isInteractiveMode: + self.printH1("Pre-Install RBAC Configuration") + # User does not have permissions to apply RBAC + self.printDescription( + [ + f"Admin mode: '{self.mas_admin_mode}'", + "Pre-install RBAC could not be applied automatically (insufficient permissions).", + ] + ) + + if self.noConfirm: + self.printDescription( + [ + f"Installation will continue with the selected '{self.mas_admin_mode}' admin mode.", + "The current user does not have sufficient permissions to apply the pre-install RBAC automatically.", + "With the --no-confirm flag, the installation assumes the required RBAC has already been applied by your OpenShift administrator.", + "If it has not been applied, ensure your OpenShift administrator runs 'mas pre-install' with the same admin mode before the installation proceeds.", + ] + ) + else: 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.", + "Ask your OpenShift administrator to run 'mas pre-install' for this MAS instance, MAS channel, admin mode, and selected apps.", + "If that has already been done, you can continue the installation.", ] ) if not self.yesOrNo("Has your OpenShift administrator already run 'mas pre-install' for this installation"): 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'." + "Installation aborted. Ask your OpenShift administrator to run 'mas pre-install' for this installation and then run 'mas install' again using the same admin mode." ) - else: - self.fatalError( - "\n".join( - [ - 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'.", - ] - ) - ) @logMethodCall def validateCatalogSource(self): @@ -746,10 +747,10 @@ def configOperationMode(self): self.setParam("rhoai", "false") @logMethodCall - def configPermissionMode(self): + def configAdminMode(self): if isVersionEqualOrAfter("9.2.0", self.getParam("mas_channel")): if self.showAdvancedOptions: - self.printH1("Configure Permission Mode") + self.printH1("Configure Mas Admin Mode") self.printDescription( [ "Choose how MAS should be installed with respect to permissions:", @@ -772,11 +773,11 @@ def configPermissionMode(self): ] ) - permissionModeInt = self.promptForInt("Permission Mode", default=1, min=1, max=3) - permissionModeMap = {1: "cluster", 2: "namespaced", 3: "minimal"} - self.mas_permission_mode = permissionModeMap[permissionModeInt] + adminModeInt = self.promptForInt("Mas Admin Mode", default=1, min=1, max=3) + adminModeMap = {1: "cluster", 2: "namespaced", 3: "minimal"} + self.mas_admin_mode = adminModeMap[adminModeInt] - if self.mas_permission_mode in ["namespaced", "minimal"]: + if self.mas_admin_mode in ["namespaced", "minimal"]: self.setParam("mas_issuer_kind", "Issuer") else: self.printDescription( @@ -793,18 +794,18 @@ def configPermissionMode(self): ) issuerKindChoice = self.promptForInt("Certificate issuer kind", min=1, max=2, default=2) self.setParam("mas_issuer_kind", "ClusterIssuer" if issuerKindChoice == 2 else "Issuer") - elif self.mas_permission_mode == "": - self.mas_permission_mode = "cluster" + elif self.mas_admin_mode == "": + self.mas_admin_mode = "cluster" self.setParam("mas_issuer_kind", "ClusterIssuer") def _handleDNSIntegrationRestriction(self): if not isVersionEqualOrAfter("9.2.0", self.getParam("mas_channel")): return False - if self.mas_permission_mode in ["namespaced", "minimal"]: + if self.mas_admin_mode in ["namespaced", "minimal"]: self.printDescription( [ - f"You are using the {self.mas_permission_mode} permission mode.", + f"You are using the {self.mas_admin_mode} admin mode.", "DNS integration is not available in this mode.", "If you use a custom domain, you need to configure DNS manually.", ] @@ -1934,7 +1935,7 @@ def interactiveMode(self, simplified: bool, advanced: bool) -> None: self.configICRCredentials() # MAS Core - self.configPermissionMode() + self.configAdminMode() self.evaluatePreInstallRBACAccess() self.configCertManager() self.configMAS() @@ -2217,7 +2218,6 @@ def nonInteractiveMode(self) -> None: "accept_license", "dev_mode", "skip_pre_check", - "skip_preinstall_rbac", "skip_grafana_install", "no_confirm", "help", @@ -2339,55 +2339,58 @@ def nonInteractiveMode(self) -> None: if self.getParam("mas_issuer_kind") != "" and not isVersionEqualOrAfter("9.2.0", self.getParam("mas_channel")): self.fatalError(f"--mas-issuer-kind is only supported for MAS 9.2+ (selected channel: {self.getParam('mas_channel')})") - if self.mas_permission_mode != "": - if not isVersionEqualOrAfter("9.2.0", self.getParam("mas_channel")): - self.fatalError(f"--permission-mode is only supported for MAS 9.2+ (selected channel: {self.getParam('mas_channel')})") - else: - if self.getParam("mas_issuer_kind") == "": - if self.mas_permission_mode == "cluster": - self.setParam("mas_issuer_kind", "ClusterIssuer") - else: - self.setParam("mas_issuer_kind", "Issuer") + # Validate admin mode based on MAS version + if isVersionEqualOrAfter("9.2.0", self.getParam("mas_channel")): + # MAS 9.2+: --admin-mode is REQUIRED + if self.mas_admin_mode == "": + self.fatalError( + f"--admin-mode is required for MAS version 9.2 or higher (selected channel: {self.getParam('mas_channel')}). Valid options: cluster, namespaced, minimal" + ) - if self.getParam("mas_issuer_kind") == "ClusterIssuer" and self.mas_permission_mode != "cluster": + # Set issuer kind based on admin mode if not explicitly set + if self.getParam("mas_issuer_kind") == "": + if self.mas_admin_mode == "cluster": + self.setParam("mas_issuer_kind", "ClusterIssuer") + else: + self.setParam("mas_issuer_kind", "Issuer") + + # Validate ClusterIssuer requires cluster mode + if self.getParam("mas_issuer_kind") == "ClusterIssuer" and self.mas_admin_mode != "cluster": + self.fatalError( + "\n".join( + [ + "Invalid configuration for certificate issuer kind 'ClusterIssuer'", + "ClusterIssuer can only be used when --admin-mode cluster is selected.", + ] + ) + ) + + # Validate DNS integration restrictions + if self.getParam("dns_provider") != "": + if self.mas_admin_mode in ["namespaced", "minimal"]: self.fatalError( "\n".join( [ - "Invalid configuration for certificate issuer kind 'ClusterIssuer'", - "ClusterIssuer can only be used when --permission-mode cluster is selected.", + f"Invalid configuration for admin mode '{self.mas_admin_mode}'", + "DNS integration is not available in this mode.", + "Remove DNS integration option --dns-provider, or switch to --admin-mode cluster and use --mas-issuer-kind ClusterIssuer.", ] ) ) - if self.getParam("dns_provider") != "": - if self.mas_permission_mode in [ - "namespaced", - "minimal", - ]: - self.fatalError( - "\n".join( - [ - f"Invalid configuration for permission mode '{self.mas_permission_mode}'", - "DNS integration is not available in this mode.", - "Remove DNS integration option --dns-provider, or switch to --permission-mode cluster and use --mas-issuer-kind ClusterIssuer.", - ] - ) - ) - - if self.mas_permission_mode == "cluster" and self.getParam("mas_issuer_kind") == "Issuer": - self.fatalError( - "\n".join( - [ - "Invalid configuration for certificate issuer kind 'Issuer'", - "DNS integration is not available when --mas-issuer-kind Issuer is selected.", - "Remove DNS integration option --dns-provider, or use --mas-issuer-kind ClusterIssuer.", - ] - ) + if self.mas_admin_mode == "cluster" and self.getParam("mas_issuer_kind") == "Issuer": + self.fatalError( + "\n".join( + [ + "Invalid configuration for certificate issuer kind 'Issuer'", + "DNS integration is not available when --mas-issuer-kind Issuer is selected.", + "Remove DNS integration option --dns-provider, or use --mas-issuer-kind ClusterIssuer.", + ] ) - elif isVersionEqualOrAfter("9.2.0", self.getParam("mas_channel")): - self.mas_permission_mode = "cluster" - if self.getParam("mas_issuer_kind") == "": - self.setParam("mas_issuer_kind", "ClusterIssuer") + ) + else: + if self.mas_admin_mode != "": + self.fatalError(f"--admin-mode is not supported for MAS version 9.1 and earlier (selected channel: {self.getParam('mas_channel')})") self.evaluatePreInstallRBACAccess() self.setDB2DefaultChannel() @@ -2463,7 +2466,7 @@ def install(self, argv): self.licenseAccepted = args.accept_license self.devMode = args.dev_mode self.skipGrafanaInstall = args.skip_grafana_install - self.mas_permission_mode = args.mas_permission_mode if args.mas_permission_mode else "" + self.mas_admin_mode = args.mas_admin_mode if args.mas_admin_mode else "" # Set image_pull_policy of the CLI in interactive mode if args.image_pull_policy and args.image_pull_policy != "": @@ -2478,8 +2481,6 @@ def install(self, argv): if args.skip_pre_check: self.setParam("skip_pre_check", "true") - self.skip_preinstall_rbac = hasattr(args, "skip_preinstall_rbac") and args.skip_preinstall_rbac - # Handle --use-cli-digest parameter which can be: # - False (not provided) # - True (provided without value, auto-lookup digest) @@ -2708,7 +2709,7 @@ def install(self, argv): dynClient=self.dynamicClient, masVersion=".".join(self.getParam("mas_channel").split(".")[:2]), masInstanceId=self.getParam("mas_instance_id"), - permissionMode=self.mas_permission_mode, + adminMode=self.mas_admin_mode, selectedApps=self.getSelectedApps(), ) h.stop_and_persist(symbol=self.successIcon, text="Pre-install MAS RBAC applied") diff --git a/python/src/mas/cli/install/argBuilder.py b/python/src/mas/cli/install/argBuilder.py index 7ec22a5e1b9..708ed8a11d3 100644 --- a/python/src/mas/cli/install/argBuilder.py +++ b/python/src/mas/cli/install/argBuilder.py @@ -92,8 +92,8 @@ def buildCommand(self) -> str: if self.operationalMode == 2: command += f" --non-prod{newline}" - if self.mas_permission_mode != "": - command += f" --permission-mode {self.mas_permission_mode}{newline}" + if self.mas_admin_mode != "": + command += f" --admin-mode {self.mas_admin_mode}{newline}" if self.getParam("mas_trust_default_cas").lower() == "false": command += f" --disable-ca-trust{newline}" @@ -608,8 +608,6 @@ def buildCommand(self) -> str: command += f" --dev-mode{newline}" if self.getParam("skip_pre_check") is True: command += f" --skip-pre-check{newline}" - if self.skip_preinstall_rbac: - command += f" --skip-preinstall-rbac{newline}" if self.getParam("image_pull_policy") != "": command += f" --image-pull-policy {self.getParam('image_pull_policy')}{newline}" if self.getParam("service_account_name") != "": diff --git a/python/src/mas/cli/install/argParser.py b/python/src/mas/cli/install/argParser.py index 7f7bbac8bc9..9c0799bb40c 100644 --- a/python/src/mas/cli/install/argParser.py +++ b/python/src/mas/cli/install/argParser.py @@ -260,10 +260,10 @@ def isValidFile(parser: argparse.ArgumentParser, arg: str) -> str: ) masAdvancedArgGroup.add_argument( - "--permission-mode", - dest="mas_permission_mode", + "--admin-mode", + dest="mas_admin_mode", required=False, - help="Permission mode for MAS installation: 'cluster' (with ClusterRoles, default), 'namespaced' (without ClusterRoles, limited to pre-created namespaces), 'minimal' (essential roles only, no app lifecycle management)", + help="Admin mode for MAS installation: 'cluster' (with ClusterRoles, default), 'namespaced' (without ClusterRoles, limited to pre-created namespaces), 'minimal' (essential roles only, no app lifecycle management)", choices=["cluster", "namespaced", "minimal"], default=None, ) @@ -1542,14 +1542,6 @@ def isValidFile(parser: argparse.ArgumentParser, arg: str) -> str: action="store_true", help="Disable the 'pre-install-check' at the start of the install 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( "--no-confirm", required=False, diff --git a/python/src/mas/cli/install/params.py b/python/src/mas/cli/install/params.py index 5de5836b309..b9ca498464d 100644 --- a/python/src/mas/cli/install/params.py +++ b/python/src/mas/cli/install/params.py @@ -45,7 +45,7 @@ "mas_app_settings_default_jms", "mas_app_settings_persistent_volumes_flag", "mas_app_settings_demodata", - "mas_permission_mode", + "mas_admin_mode", "mas_issuer_kind", "mas_app_settings_customization_archive_name", "mas_app_settings_customization_archive_url", diff --git a/python/src/mas/cli/install/summarizer.py b/python/src/mas/cli/install/summarizer.py index bfb20372b5a..f63beff8220 100644 --- a/python/src/mas/cli/install/summarizer.py +++ b/python/src/mas/cli/install/summarizer.py @@ -47,6 +47,7 @@ class InstallSummarizerMixin: installAIService: bool installArcgis: bool dynamicClient: DynamicClient + applyPreInstallMASRBAC: bool # Methods from BaseApp def getParam(self, param: str) -> str: ... @@ -107,12 +108,11 @@ def masSummary(self) -> None: print() self.printSummary("Operational Mode", operationalModeNames[self.operationalMode]) - if self.mas_permission_mode != "": - self.printSummary("Permission Mode", self.mas_permission_mode) - # Only show "Apply Pre-Install MAS RBAC" when permission mode is defined + if self.mas_admin_mode != "": + self.printSummary("MAS Admin Mode", self.mas_admin_mode) self.printSummary( "Apply Pre-Install MAS RBAC", - "No" if self.skip_preinstall_rbac else "Yes", + "Yes" if self.applyPreInstallMASRBAC else "No", ) if self.getParam("mas_issuer_kind") != "": self.printParamSummary("Mas Certificate Issuer Kind", "mas_issuer_kind") diff --git a/python/src/mas/cli/pre_install/app.py b/python/src/mas/cli/pre_install/app.py index e948851207e..6aaa89ab7e0 100644 --- a/python/src/mas/cli/pre_install/app.py +++ b/python/src/mas/cli/pre_install/app.py @@ -36,19 +36,19 @@ def promptForMASChannel(self) -> None: self.printDescription(["Enter the MAS channel", "For example: 9.2.x"]) self.mas_channel = self.promptForString("MAS Channel", default="9.2.x") - def promptForPermissionMode(self) -> None: - self.printH2("Permission Mode") + def promptForAdminMode(self) -> None: + self.printH2("Admin Mode") self.printDescription( [ - "Choose the permission mode for which pre-install RBAC should be set up:", + "Choose the admin mode for which pre-install RBAC should be set up:", "", " 1. cluster", " 2. namespaced", ] ) - permissionModeInt = self.promptForInt("Permission Mode", default=1, min=1, max=2) - permissionModeMap = {1: "cluster", 2: "namespaced"} - self.permission_mode = permissionModeMap[permissionModeInt] + adminModeInt = self.promptForInt("Admin Mode", default=1, min=1, max=2) + adminModeMap = {1: "cluster", 2: "namespaced"} + self.admin_mode = adminModeMap[adminModeInt] def promptForApps(self) -> None: self.printH2("MAS Applications") @@ -69,7 +69,7 @@ def setupPreinstallRBAC(self, argv): """ self.args = setupPreinstallRBACArgParser.parse_args(args=argv) self.noConfirm = self.args.no_confirm - self.interactive_mode = not all([self.args.mas_instance_id, self.args.mas_channel, self.args.permission_mode]) + self.interactive_mode = not all([self.args.mas_instance_id, self.args.mas_channel, self.args.admin_mode]) self.printH1("Set Target OpenShift Cluster") self.connect() @@ -86,23 +86,22 @@ def setupPreinstallRBAC(self, argv): else: self.promptForMASChannel() - if self.args.permission_mode is not None: - self.permission_mode = self.args.permission_mode.strip() + if self.args.admin_mode is not None: + self.admin_mode = self.args.admin_mode.strip() else: - self.promptForPermissionMode() + self.promptForAdminMode() else: # Non-interactive mode - validate required parameters if not self.args.mas_instance_id or self.args.mas_instance_id.strip() == "": self.fatalError("mas_instance_id must be set") if not self.args.mas_channel or self.args.mas_channel.strip() == "": self.fatalError("mas_channel must be set") - if not self.args.permission_mode or self.args.permission_mode.strip() == "": - self.fatalError("permission_mode must be set") + if not self.args.admin_mode or self.args.admin_mode.strip() == "": + self.fatalError("admin_mode must be set") self.mas_instance_id = self.args.mas_instance_id.strip() self.mas_channel = self.args.mas_channel.strip() - self.permission_mode = self.args.permission_mode.strip() - + self.admin_mode = self.args.admin_mode.strip() # Extract major.minor version from channel # Channel can be in formats like: 9.2.x, 9.2.0, 9.2.x-pre, etc. masVersion = ".".join(self.mas_channel.split(".")[:2]) @@ -113,7 +112,7 @@ def setupPreinstallRBAC(self, argv): self.fatalError("mas pre-install is supported only for MAS channel 9.2.x and later") # Only prompt for apps in namespaced mode - if self.permission_mode == "namespaced": + if self.admin_mode == "namespaced": if self.interactive_mode: if self.args.apps is not None: self.apps = self.args.apps.strip() @@ -122,7 +121,7 @@ def setupPreinstallRBAC(self, argv): else: # In non-interactive mode, apps is required for namespaced mode if not self.args.apps or self.args.apps.strip() == "": - self.fatalError("apps must be set for namespaced permission mode") + self.fatalError("apps must be set for namespaced admin mode") self.apps = self.args.apps.strip() selectedApps = [app.strip().lower() for app in self.apps.split(",") if app.strip()] @@ -146,13 +145,13 @@ def setupPreinstallRBAC(self, argv): "This will set up pre-install RBAC for MAS.", "", "This command is supported only for MAS version 9.2 and later.", - "The RBAC that is applied is determined by the selected permission mode and apps.", + "The RBAC that is applied is determined by the selected Admin mode and apps.", ] ) self.printSummary("Instance ID", self.mas_instance_id) self.printSummary("MAS Channel", self.mas_channel) - self.printSummary("Permission Mode", self.permission_mode) - if self.permission_mode == "namespaced": + self.printSummary("Admin Mode", self.admin_mode) + if self.admin_mode == "namespaced": self.printSummary("Selected Apps", ", ".join(selectedApps)) continueWithSetup = True @@ -169,7 +168,7 @@ def setupPreinstallRBAC(self, argv): dynClient=self.dynamicClient, masVersion=masVersion, masInstanceId=self.mas_instance_id, - permissionMode=self.permission_mode, + adminMode=self.admin_mode, selectedApps=selectedApps, ) h.stop_and_persist(symbol=self.successIcon, text=f"Pre-install RBAC for MAS is ready for {self.mas_instance_id}") diff --git a/python/src/mas/cli/pre_install/argParser.py b/python/src/mas/cli/pre_install/argParser.py index 383f6d68adf..106cea87c87 100644 --- a/python/src/mas/cli/pre_install/argParser.py +++ b/python/src/mas/cli/pre_install/argParser.py @@ -40,11 +40,11 @@ ) targetArgGroup.add_argument( - "--permission-mode", - dest="permission_mode", + "--admin-mode", + dest="admin_mode", required=False, choices=["cluster", "namespaced"], - help="The permission mode used to determine which pre-install RBAC manifests are set up", + help="The admin mode used to determine which pre-install RBAC manifests are set up", ) targetArgGroup.add_argument( diff --git a/python/test/install/test_dev_mode.py b/python/test/install/test_dev_mode.py index 7ba76d66916..d59d3d54ff6 100644 --- a/python/test/install/test_dev_mode.py +++ b/python/test/install/test_dev_mode.py @@ -279,7 +279,7 @@ def test_install_master_dev_mode_with_path_routing(tmpdir): # 10. Operational mode ".*Operational Mode.*": lambda msg: "1", # 11. Permission mode - ".*Permission Mode.*": lambda msg: "1", + ".*Mas Admin Mode.*": lambda msg: "1", # 12. Internal certificate issuer kind (appears when Permission Mode is cluster) ".*Certificate issuer kind.*": lambda msg: "2", # Select ClusterIssuer # 13. Certificate Authority Trust @@ -407,6 +407,8 @@ def test_install_master_dev_mode_non_interactive(tmpdir): "MAS_SUPERUSER_PASSWORD", "--mas-channel", "9.2.x-dev", + "--admin-mode", + "cluster", "--iot-channel", "9.2.x-dev", "--db2-system", @@ -546,6 +548,8 @@ def test_install_master_dev_mode_non_interactive_with_path_routing(tmpdir): "MAS_SUPERUSER_PASSWORD", "--mas-channel", "9.2.x-dev", + "--admin-mode", + "cluster", "--routing", "path", "--ingress-controller-name", @@ -688,6 +692,8 @@ def test_install_master_dev_mode_non_interactive_with_slack(tmpdir): "MAS_SUPERUSER_PASSWORD", "--mas-channel", "9.2.x-dev", + "--admin-mode", + "cluster", "--iot-channel", "9.2.x-dev", "--db2-system", diff --git a/python/test/install/test_routing_mode.py b/python/test/install/test_routing_mode.py index 39b22365a72..83595aa2d0f 100644 --- a/python/test/install/test_routing_mode.py +++ b/python/test/install/test_routing_mode.py @@ -393,6 +393,8 @@ def test_noninteractive_path_mode_with_configure_flag_success(self): "testws", "--mas-channel", "9.2.0", + "--admin-mode", + "cluster", "--routing", "path", "--ingress-controller-name", @@ -452,6 +454,8 @@ def test_noninteractive_path_mode_custom_controller_name(self): "testws", "--mas-channel", "9.2.0", + "--admin-mode", + "cluster", "--routing", "path", "--ingress-controller-name", @@ -492,6 +496,8 @@ def test_noninteractive_path_mode_missing_controller_name_defaults_to_default(se "testws", "--mas-channel", "9.2.0", + "--admin-mode", + "cluster", "--routing", "path", "--accept-license", @@ -525,6 +531,8 @@ def test_noninteractive_path_mode_no_permissions_fails_gracefully(self): "testws", "--mas-channel", "9.2.0", + "--admin-mode", + "cluster", "--routing", "path", "--ingress-controller-name", @@ -560,6 +568,8 @@ def test_noninteractive_path_mode_with_configure_flag_no_permissions(self): "testws", "--mas-channel", "9.2.0", + "--admin-mode", + "cluster", "--routing", "path", "--ingress-controller-name", @@ -600,6 +610,8 @@ def test_noninteractive_path_mode_controller_not_configured_without_flag(self): "testws", "--mas-channel", "9.2.0", + "--admin-mode", + "cluster", "--routing", "path", "--ingress-controller-name", @@ -639,6 +651,8 @@ def test_noninteractive_path_mode_all_flags_with_permissions(self): "testws", "--mas-channel", "9.2.0", + "--admin-mode", + "cluster", "--routing", "path", "--ingress-controller-name", @@ -689,6 +703,8 @@ def test_noninteractive_path_mode_controller_already_configured(self): "testws", "--mas-channel", "9.2.0", + "--admin-mode", + "cluster", "--routing", "path", "--ingress-controller-name", @@ -736,6 +752,8 @@ def test_noninteractive_subdomain_mode_basic(self): "testws", "--mas-channel", "9.2.0", + "--admin-mode", + "cluster", "--routing", "subdomain", "--accept-license", @@ -763,6 +781,8 @@ def test_noninteractive_subdomain_mode_ignores_ingress_flags(self): "testws", "--mas-channel", "9.2.0", + "--admin-mode", + "cluster", "--routing", "subdomain", # Even if these are provided, they should be ignored for subdomain mode