From ed744ee60df18d4d30b495b020dfaf76e4457a17 Mon Sep 17 00:00:00 2001 From: Dixit Sathwara Date: Tue, 26 May 2026 21:33:27 +0530 Subject: [PATCH 1/6] [patch] add the default mode as the path in cli for 9.2+ --- python/src/mas/cli/install/app.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/python/src/mas/cli/install/app.py b/python/src/mas/cli/install/app.py index 2dc485fe56..79ad679376 100644 --- a/python/src/mas/cli/install/app.py +++ b/python/src/mas/cli/install/app.py @@ -2516,6 +2516,14 @@ def install(self, argv): else: self.nonInteractiveMode() + # Set default routing mode to 'path' for MAS 9.2+ if not explicitly configured + # This applies to both interactive and non-interactive modes + if isVersionEqualOrAfter("9.2.0", self.getParam("mas_channel")) and self.getParam("mas_channel") != "9.2.x-feature": + if self.getParam("mas_routing_mode") == "": + self.setParam("mas_routing_mode", "path") + # Set default ingress controller for path-based routing if not already set + if self.getParam("mas_ingress_controller_name") == "": + self.setParam("mas_ingress_controller_name", "default") # After we've configured the basic inputs, we can calculate these ones self.setIoTStorageClasses() self.setMonitorInstallOrder() From 0fd8e8dae631bef1a034b834a08e2de95e63aa7c Mon Sep 17 00:00:00 2001 From: Dixit Sathwara Date: Wed, 27 May 2026 09:43:53 +0530 Subject: [PATCH 2/6] [patch] update the interactive mode for routingMode selection options --- python/src/mas/cli/install/app.py | 69 +++++++++++++++++++------------ 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/python/src/mas/cli/install/app.py b/python/src/mas/cli/install/app.py index 79ad679376..02873acd0b 100644 --- a/python/src/mas/cli/install/app.py +++ b/python/src/mas/cli/install/app.py @@ -878,34 +878,52 @@ def _promptForIngressController(self): @logMethodCall def configRoutingMode(self): - if self.showAdvancedOptions and isVersionEqualOrAfter("9.2.0", self.getParam("mas_channel")) and self.getParam("mas_channel") != "9.2.x-feature": - self.printH1("Configure Routing Mode") - + if isVersionEqualOrAfter("9.2.0", self.getParam("mas_channel")) and self.getParam("mas_channel") != "9.2.x-feature": masDomain = self._getMasDomainForDisplay() + + # Determine routing mode based on whether we're in advanced mode + if self.showAdvancedOptions: + # Advanced mode: Show full prompt and let user choose + self.printH1("Configure Routing Mode") - self.printDescription( - [ - "Maximo Application Suite can be configured in one of two ways:", - "", - " 1. Single domain with path-based routing across the suite", - f" Example: https://{masDomain}/admin", - "", - " 2. Multi domain with subdomain-based routing across the suite", - f" Example: https://admin.{masDomain}", - "", - "Path-based routing requires the IngressController to have the routeAdmission policy", - "set to 'InterNamespaceAllowed'. This allows routes to claim the same hostname across", - "different namespaces, which is necessary for path-based routing to function correctly.", - "", - "For more information refer to:", - "https://docs.redhat.com/en/documentation/openshift_container_platform/4.20/html/ingress_and_load_balancing/routes#nw-route-admission-policy_configuring-routes", - ] - ) + self.printDescription( + [ + "Maximo Application Suite can be configured in one of two ways:", + "", + " 1. Single domain with path-based routing across the suite", + f" Example: https://{masDomain}/admin", + "", + " 2. Multi domain with subdomain-based routing across the suite", + f" Example: https://admin.{masDomain}", + "", + "Path-based routing requires the IngressController to have the routeAdmission policy", + "set to 'InterNamespaceAllowed'. This allows routes to claim the same hostname across", + "different namespaces, which is necessary for path-based routing to function correctly.", + "", + "For more information refer to:", + "https://docs.redhat.com/en/documentation/openshift_container_platform/4.20/html/ingress_and_load_balancing/routes#nw-route-admission-policy_configuring-routes", + ] + ) - routingModeInt = self.promptForInt("Routing Mode", default=1, min=1, max=2) - routingModeOptions = ["path", "subdomain"] - selectedMode = routingModeOptions[routingModeInt - 1] + routingModeInt = self.promptForInt("Routing Mode", default=1, min=1, max=2) + routingModeOptions = ["path", "subdomain"] + selectedMode = routingModeOptions[routingModeInt - 1] + else: + # Simplified mode: Default to path and inform user + selectedMode = "path" + self.printDescription( + [ + "", + f"Routing mode defaulting to 'path' for MAS 9.2+", + f" Example: https://{masDomain}/admin", + "", + "Note: Routing mode selection is available in advanced installation mode.", + "For MAS 9.2+ installations, path-based routing is the default if not explicitly configured.", + "", + ] + ) + # Now validate the selected/defaulted mode if selectedMode == "path": canConfigure = self._checkIngressControllerPermissions() if not canConfigure: @@ -2516,8 +2534,7 @@ def install(self, argv): else: self.nonInteractiveMode() - # Set default routing mode to 'path' for MAS 9.2+ if not explicitly configured - # This applies to both interactive and non-interactive modes + # Set default routing mode to 'path' for MAS 9.2+ in non-interactive mode if not explicitly configured if isVersionEqualOrAfter("9.2.0", self.getParam("mas_channel")) and self.getParam("mas_channel") != "9.2.x-feature": if self.getParam("mas_routing_mode") == "": self.setParam("mas_routing_mode", "path") From 581debe1402dca47d894bfbe1a37f718a998acc7 Mon Sep 17 00:00:00 2001 From: Dixit Sathwara Date: Wed, 27 May 2026 11:16:44 +0530 Subject: [PATCH 3/6] fix the test cases and pre-commit failure --- python/src/mas/cli/install/app.py | 148 ++++++++++++++++------- python/test/install/test_dev_mode.py | 10 ++ python/test/install/test_routing_mode.py | 4 +- 3 files changed, 113 insertions(+), 49 deletions(-) diff --git a/python/src/mas/cli/install/app.py b/python/src/mas/cli/install/app.py index 02873acd0b..16795ccc5a 100644 --- a/python/src/mas/cli/install/app.py +++ b/python/src/mas/cli/install/app.py @@ -878,9 +878,16 @@ def _promptForIngressController(self): @logMethodCall def configRoutingMode(self): - if isVersionEqualOrAfter("9.2.0", self.getParam("mas_channel")) and self.getParam("mas_channel") != "9.2.x-feature": + if isVersionEqualOrAfter("9.2.0", self.getParam("mas_channel")): + # For 9.2.x-feature channel, explicitly set subdomain routing mode + if self.getParam("mas_channel") == "9.2.x-feature": + if self.getParam("mas_routing_mode") == "": + self.setParam("mas_routing_mode", "subdomain") + logger.info("Routing mode set to 'subdomain' for 9.2.x-feature channel") + return + masDomain = self._getMasDomainForDisplay() - + # Determine routing mode based on whether we're in advanced mode if self.showAdvancedOptions: # Advanced mode: Show full prompt and let user choose @@ -914,7 +921,7 @@ def configRoutingMode(self): self.printDescription( [ "", - f"Routing mode defaulting to 'path' for MAS 9.2+", + "Routing mode defaulting to 'path' for MAS 9.2+", f" Example: https://{masDomain}/admin", "", "Note: Routing mode selection is available in advanced installation mode.", @@ -930,14 +937,10 @@ def configRoutingMode(self): self.printDescription( [ "", - "Your cluster ingress currently does not support path-based routing", - "", - "If you wish to configure MAS with path-based routing, contact your OpenShift", - "administrator to apply the following configuration:", + "Insufficient permissions to validate IngressController configuration", "", - " spec:", - " routeAdmission:", - " namespaceOwnership: InterNamespaceAllowed", + "Path-based routing requires IngressController permissions to validate the configuration.", + "Contact your OpenShift administrator to grant the necessary permissions.", "", "MAS will be configured to use subdomain-based routing.", ] @@ -2406,6 +2409,21 @@ def nonInteractiveMode(self) -> None: if self.getParam("mas_issuer_kind") == "": self.setParam("mas_issuer_kind", "ClusterIssuer") + # Set default routing mode for MAS 9.2+ in non-interactive mode if not explicitly configured + if isVersionEqualOrAfter("9.2.0", self.getParam("mas_channel")): + if self.getParam("mas_routing_mode") == "": + # For 9.2.x-feature channel, default to subdomain routing + if self.getParam("mas_channel") == "9.2.x-feature": + self.setParam("mas_routing_mode", "subdomain") + logger.info("Routing mode defaulting to 'subdomain' for 9.2.x-feature channel in non-interactive mode") + else: + # For other 9.2+ channels, default to path routing + self.setParam("mas_routing_mode", "path") + logger.info("Routing mode defaulting to 'path' for MAS 9.2+ in non-interactive mode") + # Set default ingress controller for path-based routing if not already set + if self.getParam("mas_ingress_controller_name") == "": + self.setParam("mas_ingress_controller_name", "default") + self.evaluatePreInstallRBACAccess() self.setDB2DefaultChannel() @@ -2534,13 +2552,6 @@ def install(self, argv): else: self.nonInteractiveMode() - # Set default routing mode to 'path' for MAS 9.2+ in non-interactive mode if not explicitly configured - if isVersionEqualOrAfter("9.2.0", self.getParam("mas_channel")) and self.getParam("mas_channel") != "9.2.x-feature": - if self.getParam("mas_routing_mode") == "": - self.setParam("mas_routing_mode", "path") - # Set default ingress controller for path-based routing if not already set - if self.getParam("mas_ingress_controller_name") == "": - self.setParam("mas_ingress_controller_name", "default") # After we've configured the basic inputs, we can calculate these ones self.setIoTStorageClasses() self.setMonitorInstallOrder() @@ -2587,17 +2598,13 @@ def install(self, argv): self.fatalError( "\n".join( [ - "IngressController Configuration Requires Administrator Permissions", + "Insufficient Permissions to Validate IngressController Configuration", "========================================================================", - "You do not have sufficient permissions to check or configure the", - f"IngressController '{ingressControllerName}'.", + "You do not have sufficient permissions to validate the IngressController", + f"configuration for path-based routing (IngressController: '{ingressControllerName}').", "", - "If you wish to configure MAS with path-based routing, contact your OpenShift", - "administrator to apply the following configuration:", - "", - " spec:", - " routeAdmission:", - " namespaceOwnership: InterNamespaceAllowed", + "Path-based routing requires IngressController permissions to validate the configuration.", + "Contact your OpenShift administrator to grant the necessary permissions.", "", "Alternatively, you can use subdomain routing mode:", " mas install --routing subdomain ...", @@ -2638,36 +2645,83 @@ def install(self, argv): logger.info(f"IngressController '{ingressControllerName}' will be configured for path-based routing before MAS installation") self.setParam("mas_configure_ingress", "true") else: - self.fatalError( - "\n".join( + # If --no-confirm is NOT used, prompt user to configure ingress + if not self.noConfirm: + self.printDescription( [ - "IngressController Not Configured for Path-Based Routing", "", - "========================================================================", + "IngressController is not configured for path-based routing", + "", f"IngressController '{ingressControllerName}' exists but is not properly configured", "for path-based routing.", "", - "Required Configuration:", - " spec:", - " routeAdmission:", - " namespaceOwnership: InterNamespaceAllowed", - "", - "To fix this issue, you have two options:", - "", - "1. Add the --configure-ingress flag to configure it during installation:", - f" (Optionally, you can provide your custom IngressController name instead of {ingressControllerName} )", - f" mas install --routing path --ingress-controller-name {ingressControllerName} --configure-ingress ...", + "The following setting needs to be applied to the IngressController:", "", - "2. Manually configure it before installation by running:", - f" oc patch ingresscontroller {ingressControllerName} -n openshift-ingress-operator \\", - " --type=merge \\", - ' --patch=\'{"spec":{"routeAdmission":{"namespaceOwnership":"InterNamespaceAllowed"}}}\'', + " spec:", + " routeAdmission:", + " namespaceOwnership: InterNamespaceAllowed", "", - "Alternatively, you can use subdomain routing mode:", - " mas install --routing subdomain ...", ] ) - ) + + if self.yesOrNo("Configure ingress namespace ownership policy to enable path-based routing for MAS"): + self.setParam("mas_configure_ingress", "true") + logger.info(f"IngressController '{ingressControllerName}' will be configured for path-based routing before MAS installation") + else: + self.fatalError( + "\n".join( + [ + "IngressController Configuration Required", + "", + "========================================================================", + "Path-based routing requires IngressController configuration.", + "", + "To proceed, you have the following options:", + "", + "1. Add the --configure-ingress flag to configure it during installation:", + f" mas install --routing path --ingress-controller-name {ingressControllerName} --configure-ingress ...", + "", + "2. Manually configure it before installation by running:", + f" oc patch ingresscontroller {ingressControllerName} -n openshift-ingress-operator \\", + " --type=merge \\", + ' --patch=\'{"spec":{"routeAdmission":{"namespaceOwnership":"InterNamespaceAllowed"}}}\'', + "", + "3. Use subdomain routing mode instead:", + " mas install --routing subdomain ...", + ] + ) + ) + else: + # --no-confirm is used, fail immediately with instructions + self.fatalError( + "\n".join( + [ + "IngressController Not Configured for Path-Based Routing", + "", + "========================================================================", + f"IngressController '{ingressControllerName}' exists but is not properly configured", + "for path-based routing.", + "", + "Required Configuration:", + " spec:", + " routeAdmission:", + " namespaceOwnership: InterNamespaceAllowed", + "", + "To fix this issue, you have two options:", + "", + "1. Add the --configure-ingress flag to configure it during installation:", + f" mas install --routing path --ingress-controller-name {ingressControllerName} --configure-ingress --no-confirm ...", + "", + "2. Manually configure it before installation by running:", + f" oc patch ingresscontroller {ingressControllerName} -n openshift-ingress-operator \\", + " --type=merge \\", + ' --patch=\'{"spec":{"routeAdmission":{"namespaceOwnership":"InterNamespaceAllowed"}}}\'', + "", + "Alternatively, you can use subdomain routing mode:", + " mas install --routing subdomain ...", + ] + ) + ) else: logger.info(f"IngressController '{ingressControllerName}' is already configured for path-based routing") diff --git a/python/test/install/test_dev_mode.py b/python/test/install/test_dev_mode.py index 072c815391..2801912336 100644 --- a/python/test/install/test_dev_mode.py +++ b/python/test/install/test_dev_mode.py @@ -409,6 +409,11 @@ def test_install_master_dev_mode_non_interactive(tmpdir): "MAS_SUPERUSER_PASSWORD", "--mas-channel", "9.2.x-dev", + "--routing", + "path", + "--ingress-controller-name", + "default", + "--configure-ingress", "--iot-channel", "9.2.x-dev", "--db2-system", @@ -690,6 +695,11 @@ def test_install_master_dev_mode_non_interactive_with_slack(tmpdir): "MAS_SUPERUSER_PASSWORD", "--mas-channel", "9.2.x-dev", + "--routing", + "path", + "--ingress-controller-name", + "default", + "--configure-ingress", "--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 39b22365a7..0cf56bf140 100644 --- a/python/test/install/test_routing_mode.py +++ b/python/test/install/test_routing_mode.py @@ -988,8 +988,8 @@ def test_complete_end_to_end_flow_advanced_options_disabled_skips_routing(self): app.configRoutingMode() # Routing mode should not be set (method should exit early) - # Default is subdomain if not explicitly set - assert app.getParam("mas_routing_mode") == "" + # Default is path if not explicitly set for 9.2+ + assert app.getParam("mas_routing_mode") == "path" # promptForInt should not be called (no routing mode prompt) assert app.promptForInt.call_count == 0 From 25de3f46496fddb295880f80615c2875651f44fe Mon Sep 17 00:00:00 2001 From: Dixit Sathwara Date: Wed, 27 May 2026 11:52:44 +0530 Subject: [PATCH 4/6] [patch] reuse the functions --- python/src/mas/cli/install/app.py | 336 ++++++++++++++++-------------- 1 file changed, 175 insertions(+), 161 deletions(-) diff --git a/python/src/mas/cli/install/app.py b/python/src/mas/cli/install/app.py index 16795ccc5a..b03d2252f6 100644 --- a/python/src/mas/cli/install/app.py +++ b/python/src/mas/cli/install/app.py @@ -958,21 +958,7 @@ def configRoutingMode(self): self.setParam("mas_routing_mode", "path") self.printDescription([f"IngressController '{selectedController}' is configured for path-based routing."]) else: - self.printDescription( - [ - "", - "Your cluster ingress currently does not support path-based routing", - "", - "The following setting needs to be applied to the IngressController:", - "", - " spec:", - " routeAdmission:", - " namespaceOwnership: InterNamespaceAllowed", - "", - ] - ) - - if self.yesOrNo("Configure ingress namespace ownership policy to enable path-based routing for MAS"): + if self._promptForIngressConfiguration(selectedController): self.setParam("mas_routing_mode", "path") self.setParam("mas_configure_ingress", "true") self.printDescription( @@ -1035,6 +1021,179 @@ def _checkIngressControllerPermissions(self, controllerName="default"): logger.warning(f"User may not have permissions to configure IngressController '{controllerName}': {e}") return False + def _promptForIngressConfiguration(self, ingressControllerName): + """ + Display IngressController configuration prompt and handle user response. + + Args: + ingressControllerName: Name of the IngressController + + Returns: + bool: True if user wants to configure, False otherwise + """ + self.printDescription( + [ + "", + "IngressController is not configured for path-based routing", + "", + f"IngressController '{ingressControllerName}' exists but is not properly configured", + "for path-based routing.", + "", + "The following setting needs to be applied to the IngressController:", + "", + " spec:", + " routeAdmission:", + " namespaceOwnership: InterNamespaceAllowed", + "", + ] + ) + + return self.yesOrNo("Configure ingress namespace ownership policy to enable path-based routing for MAS") + + def _validateAndConfigureIngressControllerForPathRouting(self): + """ + Validate and configure IngressController for path-based routing in non-interactive mode. + + This function: + 1. Determines the IngressController name (from CLI flag, parameter, or default) + 2. Checks user permissions + 3. Validates IngressController existence and configuration + 4. Prompts user to configure if needed (respects --no-confirm flag) + 5. Sets mas_configure_ingress parameter if user agrees + + Raises: + SystemExit: If validation fails or user declines configuration + """ + # Determine IngressController name + ingressControllerName = None + if hasattr(self.args, "mas_ingress_controller_name") and self.args.mas_ingress_controller_name: + ingressControllerName = self.args.mas_ingress_controller_name + logger.info(f"Using IngressController '{ingressControllerName}' from CLI flag") + elif self.getParam("mas_ingress_controller_name"): + ingressControllerName = self.getParam("mas_ingress_controller_name") + logger.info(f"Using IngressController '{ingressControllerName}' from existing parameter") + else: + ingressControllerName = "default" + logger.info("No IngressController specified, defaulting to 'default'") + + self.setParam("mas_ingress_controller_name", ingressControllerName) + + # Check permissions BEFORE attempting to check the IngressController + canConfigure = self._checkIngressControllerPermissions() + if not canConfigure: + self.fatalError( + "\n".join( + [ + "Insufficient Permissions to Validate IngressController Configuration", + "========================================================================", + "You do not have sufficient permissions to validate the IngressController", + f"configuration for path-based routing (IngressController: '{ingressControllerName}').", + "", + "Path-based routing requires IngressController permissions to validate the configuration.", + "Contact your OpenShift administrator to grant the necessary permissions.", + "", + "Alternatively, you can use subdomain routing mode:", + " mas install --routing subdomain ...", + ] + ) + ) + + exists, isConfigured = self._checkIngressControllerForPathRouting(ingressControllerName) + + if not exists: + self.fatalError( + "\n".join( + [ + "IngressController Not Found", + "", + "========================================================================", + f"You selected IngressController '{ingressControllerName}', but it does not exist", + "in the openshift-ingress-operator namespace.", + "", + "To fix this issue:", + "", + "1. List available IngressControllers:", + " oc get ingresscontroller -n openshift-ingress-operator", + "", + "2. Use an existing controller name with --ingress-controller-name flag:", + " mas install --routing path --ingress-controller-name [existing-controller] ...", + "", + "3. Or use the default controller (usually named 'default'):", + " mas install --routing path --ingress-controller-name default ...", + "", + "Alternatively, you can use subdomain routing mode:", + " mas install --routing subdomain ...", + ] + ) + ) + elif not isConfigured: + if hasattr(self.args, "mas_configure_ingress") and self.args.mas_configure_ingress: + logger.info(f"IngressController '{ingressControllerName}' will be configured for path-based routing before MAS installation") + self.setParam("mas_configure_ingress", "true") + else: + # If --no-confirm is NOT used, prompt user to configure ingress + if not self.noConfirm: + if self._promptForIngressConfiguration(ingressControllerName): + self.setParam("mas_configure_ingress", "true") + logger.info(f"IngressController '{ingressControllerName}' will be configured for path-based routing before MAS installation") + else: + self.fatalError( + "\n".join( + [ + "IngressController Configuration Required", + "", + "========================================================================", + "Path-based routing requires IngressController configuration.", + "", + "To proceed, you have the following options:", + "", + "1. Add the --configure-ingress flag to configure it during installation:", + f" mas install --routing path --ingress-controller-name {ingressControllerName} --configure-ingress ...", + "", + "2. Manually configure it before installation by running:", + f" oc patch ingresscontroller {ingressControllerName} -n openshift-ingress-operator \\", + " --type=merge \\", + ' --patch=\'{"spec":{"routeAdmission":{"namespaceOwnership":"InterNamespaceAllowed"}}}\'', + "", + "3. Use subdomain routing mode instead:", + " mas install --routing subdomain ...", + ] + ) + ) + else: + # --no-confirm is used, fail immediately with instructions + self.fatalError( + "\n".join( + [ + "IngressController Not Configured for Path-Based Routing", + "", + "========================================================================", + f"IngressController '{ingressControllerName}' exists but is not properly configured", + "for path-based routing.", + "", + "Required Configuration:", + " spec:", + " routeAdmission:", + " namespaceOwnership: InterNamespaceAllowed", + "", + "To fix this issue, you have two options:", + "", + "1. Add the --configure-ingress flag to configure it during installation:", + f" mas install --routing path --ingress-controller-name {ingressControllerName} --configure-ingress --no-confirm ...", + "", + "2. Manually configure it before installation by running:", + f" oc patch ingresscontroller {ingressControllerName} -n openshift-ingress-operator \\", + " --type=merge \\", + ' --patch=\'{"spec":{"routeAdmission":{"namespaceOwnership":"InterNamespaceAllowed"}}}\'', + "", + "Alternatively, you can use subdomain routing mode:", + " mas install --routing subdomain ...", + ] + ) + ) + else: + logger.info(f"IngressController '{ingressControllerName}' is already configured for path-based routing") + @logMethodCall def configManualRoutesMgmt(self) -> None: if self.showAdvancedOptions: @@ -2578,152 +2737,7 @@ def install(self, argv): # Validate IngressController configuration for path-based routing (non-interactive mode only) if not self.isInteractiveMode and self.getParam("mas_routing_mode") == "path": - ingressControllerName = None - if hasattr(self.args, "mas_ingress_controller_name") and self.args.mas_ingress_controller_name: - ingressControllerName = self.args.mas_ingress_controller_name - logger.info(f"Using IngressController '{ingressControllerName}' from CLI flag") - elif self.getParam("mas_ingress_controller_name"): - ingressControllerName = self.getParam("mas_ingress_controller_name") - logger.info(f"Using IngressController '{ingressControllerName}' from existing parameter") - else: - ingressControllerName = "default" - logger.info("No IngressController specified, defaulting to 'default'") - - self.setParam("mas_ingress_controller_name", ingressControllerName) - - # Check permissions BEFORE attempting to check the IngressController - canConfigure = self._checkIngressControllerPermissions() - if not canConfigure: - - self.fatalError( - "\n".join( - [ - "Insufficient Permissions to Validate IngressController Configuration", - "========================================================================", - "You do not have sufficient permissions to validate the IngressController", - f"configuration for path-based routing (IngressController: '{ingressControllerName}').", - "", - "Path-based routing requires IngressController permissions to validate the configuration.", - "Contact your OpenShift administrator to grant the necessary permissions.", - "", - "Alternatively, you can use subdomain routing mode:", - " mas install --routing subdomain ...", - ] - ) - ) - - exists, isConfigured = self._checkIngressControllerForPathRouting(ingressControllerName) - - if not exists: - self.fatalError( - "\n".join( - [ - "IngressController Not Found", - "", - "========================================================================", - f"You selected IngressController '{ingressControllerName}', but it does not exist", - "in the openshift-ingress-operator namespace.", - "", - "To fix this issue:", - "", - "1. List available IngressControllers:", - " oc get ingresscontroller -n openshift-ingress-operator", - "", - "2. Use an existing controller name with --ingress-controller-name flag:", - " mas install --routing path --ingress-controller-name [existing-controller] ...", - "", - "3. Or use the default controller (usually named 'default'):", - " mas install --routing path --ingress-controller-name default ...", - "", - "Alternatively, you can use subdomain routing mode:", - " mas install --routing subdomain ...", - ] - ) - ) - elif not isConfigured: - if hasattr(self.args, "mas_configure_ingress") and self.args.mas_configure_ingress: - logger.info(f"IngressController '{ingressControllerName}' will be configured for path-based routing before MAS installation") - self.setParam("mas_configure_ingress", "true") - else: - # If --no-confirm is NOT used, prompt user to configure ingress - if not self.noConfirm: - self.printDescription( - [ - "", - "IngressController is not configured for path-based routing", - "", - f"IngressController '{ingressControllerName}' exists but is not properly configured", - "for path-based routing.", - "", - "The following setting needs to be applied to the IngressController:", - "", - " spec:", - " routeAdmission:", - " namespaceOwnership: InterNamespaceAllowed", - "", - ] - ) - - if self.yesOrNo("Configure ingress namespace ownership policy to enable path-based routing for MAS"): - self.setParam("mas_configure_ingress", "true") - logger.info(f"IngressController '{ingressControllerName}' will be configured for path-based routing before MAS installation") - else: - self.fatalError( - "\n".join( - [ - "IngressController Configuration Required", - "", - "========================================================================", - "Path-based routing requires IngressController configuration.", - "", - "To proceed, you have the following options:", - "", - "1. Add the --configure-ingress flag to configure it during installation:", - f" mas install --routing path --ingress-controller-name {ingressControllerName} --configure-ingress ...", - "", - "2. Manually configure it before installation by running:", - f" oc patch ingresscontroller {ingressControllerName} -n openshift-ingress-operator \\", - " --type=merge \\", - ' --patch=\'{"spec":{"routeAdmission":{"namespaceOwnership":"InterNamespaceAllowed"}}}\'', - "", - "3. Use subdomain routing mode instead:", - " mas install --routing subdomain ...", - ] - ) - ) - else: - # --no-confirm is used, fail immediately with instructions - self.fatalError( - "\n".join( - [ - "IngressController Not Configured for Path-Based Routing", - "", - "========================================================================", - f"IngressController '{ingressControllerName}' exists but is not properly configured", - "for path-based routing.", - "", - "Required Configuration:", - " spec:", - " routeAdmission:", - " namespaceOwnership: InterNamespaceAllowed", - "", - "To fix this issue, you have two options:", - "", - "1. Add the --configure-ingress flag to configure it during installation:", - f" mas install --routing path --ingress-controller-name {ingressControllerName} --configure-ingress --no-confirm ...", - "", - "2. Manually configure it before installation by running:", - f" oc patch ingresscontroller {ingressControllerName} -n openshift-ingress-operator \\", - " --type=merge \\", - ' --patch=\'{"spec":{"routeAdmission":{"namespaceOwnership":"InterNamespaceAllowed"}}}\'', - "", - "Alternatively, you can use subdomain routing mode:", - " mas install --routing subdomain ...", - ] - ) - ) - else: - logger.info(f"IngressController '{ingressControllerName}' is already configured for path-based routing") + self._validateAndConfigureIngressControllerForPathRouting() # Based on the parameters set the annotations correctly self.configAnnotations() From 0c2b9f1f613ece58067e94f1c3941d2b1d56ea04 Mon Sep 17 00:00:00 2001 From: Dixit Sathwara Date: Wed, 27 May 2026 12:13:27 +0530 Subject: [PATCH 5/6] [patch] fail with same error in interactive and non interactive mode --- python/src/mas/cli/install/app.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/python/src/mas/cli/install/app.py b/python/src/mas/cli/install/app.py index b03d2252f6..224126dcda 100644 --- a/python/src/mas/cli/install/app.py +++ b/python/src/mas/cli/install/app.py @@ -965,15 +965,27 @@ def configRoutingMode(self): [f"IngressController '{selectedController}' will be configured before MAS installation begins."] ) else: - self.printDescription( - [ - "", - "Path-based routing requires IngressController configuration.", - "MAS will be configured to use subdomain-based routing.", - ] + self.fatalError( + "\n".join( + [ + "IngressController Configuration Required", + "", + "========================================================================", + "Path-based routing requires IngressController configuration.", + "", + "To proceed, you have the following options:", + "", + "1. Re-run the installation and agree to configure the IngressController", + "", + "2. Manually configure it before installation by running:", + f" oc patch ingresscontroller {selectedController} -n openshift-ingress-operator \\", + " --type=merge \\", + ' --patch=\'{"spec":{"routeAdmission":{"namespaceOwnership":"InterNamespaceAllowed"}}}\'', + "", + "3. Use subdomain routing mode instead path" + ] + ) ) - self.setParam("mas_routing_mode", "subdomain") - self.setParam("mas_ingress_controller_name", "") else: self.setParam("mas_routing_mode", "subdomain") From fbfd51426505581b7401979c2e86add1173afd9f Mon Sep 17 00:00:00 2001 From: Dixit Sathwara Date: Wed, 27 May 2026 12:58:46 +0530 Subject: [PATCH 6/6] fix the test cases failure --- python/src/mas/cli/install/app.py | 12 ++++++------ python/test/install/test_routing_mode.py | 21 ++++++++++++--------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/python/src/mas/cli/install/app.py b/python/src/mas/cli/install/app.py index 224126dcda..d21b29c3f0 100644 --- a/python/src/mas/cli/install/app.py +++ b/python/src/mas/cli/install/app.py @@ -982,7 +982,7 @@ def configRoutingMode(self): " --type=merge \\", ' --patch=\'{"spec":{"routeAdmission":{"namespaceOwnership":"InterNamespaceAllowed"}}}\'', "", - "3. Use subdomain routing mode instead path" + "3. Use subdomain routing mode instead path", ] ) ) @@ -1036,10 +1036,10 @@ def _checkIngressControllerPermissions(self, controllerName="default"): def _promptForIngressConfiguration(self, ingressControllerName): """ Display IngressController configuration prompt and handle user response. - + Args: ingressControllerName: Name of the IngressController - + Returns: bool: True if user wants to configure, False otherwise """ @@ -1059,20 +1059,20 @@ def _promptForIngressConfiguration(self, ingressControllerName): "", ] ) - + return self.yesOrNo("Configure ingress namespace ownership policy to enable path-based routing for MAS") def _validateAndConfigureIngressControllerForPathRouting(self): """ Validate and configure IngressController for path-based routing in non-interactive mode. - + This function: 1. Determines the IngressController name (from CLI flag, parameter, or default) 2. Checks user permissions 3. Validates IngressController existence and configuration 4. Prompts user to configure if needed (respects --no-confirm flag) 5. Sets mas_configure_ingress parameter if user agrees - + Raises: SystemExit: If validation fails or user declines configuration """ diff --git a/python/test/install/test_routing_mode.py b/python/test/install/test_routing_mode.py index 0cf56bf140..2567872060 100644 --- a/python/test/install/test_routing_mode.py +++ b/python/test/install/test_routing_mode.py @@ -50,8 +50,10 @@ def create_mock_app(): app._checkIngressControllerPermissions = InstallApp._checkIngressControllerPermissions.__get__(app, InstallApp) app._checkIngressControllerForPathRouting = InstallApp._checkIngressControllerForPathRouting.__get__(app, InstallApp) app._promptForIngressController = InstallApp._promptForIngressController.__get__(app, InstallApp) + app._promptForIngressConfiguration = InstallApp._promptForIngressConfiguration.__get__(app, InstallApp) app._getMasDomainForDisplay = InstallApp._getMasDomainForDisplay.__get__(app, InstallApp) app.configRoutingMode = InstallApp.configRoutingMode.__get__(app, InstallApp) + app.fatalError = MagicMock(side_effect=SystemExit(1)) return app @@ -183,16 +185,17 @@ def get_side_effect(api_version, kind): assert app.getParam("mas_ingress_controller_name") == "default" assert app.getParam("mas_configure_ingress") == "true" - def test_interactive_path_routing_user_declines_falls_back_to_subdomain(self): - """Test fallback to subdomain when user declines to configure IngressController.""" + def test_interactive_path_routing_user_declines_fails_with_error(self): + """Test that installation fails with error when user declines to configure IngressController.""" app = create_mock_app() app.isInteractiveMode = True app.showAdvancedOptions = True app.setParam("mas_channel", "9.2.0") app.setParam("mas_instance_id", "test-inst") - # User declines to configure ingress - app.yesOrNo.return_value = False + # User selects path mode (option 1) and declines to configure ingress + app.promptForInt.return_value = 1 # Select path mode + app.yesOrNo.return_value = False # Decline configuration # Mock IngressController not configured controller = create_ingress_controller_mock(namespace_ownership="Strict") @@ -223,11 +226,9 @@ def get_side_effect(api_version, kind): # Mock isVersionEqualOrAfter to return True for version check with patch("mas.cli.install.app.isVersionEqualOrAfter", return_value=True): - app.configRoutingMode() - - # Verify fallback to subdomain - assert app.getParam("mas_routing_mode") == "subdomain" - assert app.getParam("mas_ingress_controller_name") == "" + # Should raise SystemExit when user declines configuration + with pytest.raises(SystemExit): + app.configRoutingMode() def test_interactive_path_routing_patch_fails_gracefully(self): """Test graceful failure when IngressController patch operation fails.""" @@ -879,6 +880,8 @@ def get_side_effect(api_version, kind): with patch("mas.cli.install.app.isVersionEqualOrAfter", return_value=True): # Set up promptForInt to return different values for routing mode and controller selection app.promptForInt.side_effect = [1, 1] # Path mode, first controller + # User agrees to configure IngressController + app.yesOrNo.return_value = True app.configRoutingMode() # Verify all parameters are set correctly