diff --git a/image/cli/mascli/functions/internal/save_config b/image/cli/mascli/functions/internal/save_config index 67955e2bfe2..b6996e6e786 100644 --- a/image/cli/mascli/functions/internal/save_config +++ b/image/cli/mascli/functions/internal/save_config @@ -38,6 +38,7 @@ export MAS_DOMAIN=$MAS_DOMAIN export CLUSTER_ISSUER_SELECTION=$CLUSTER_ISSUER_SELECTION export MAS_CLUSTER_ISSUER=$MAS_CLUSTER_ISSUER +export MAS_ROUTING_MODE=$MAS_ROUTING_MODE export MAS_TRUST_DEFAULT_CAS=$MAS_TRUST_DEFAULT_CAS export OCP_INGRESS_TLS_SECRET_NAME=$OCP_INGRESS_TLS_SECRET_NAME diff --git a/python/src/mas/cli/displayMixins.py b/python/src/mas/cli/displayMixins.py index c8c2c9cd300..772718eaec2 100644 --- a/python/src/mas/cli/displayMixins.py +++ b/python/src/mas/cli/displayMixins.py @@ -13,7 +13,7 @@ from prompt_toolkit.completion import WordCompleter from prompt_toolkit.validation import Validator -from .validators import YesNoValidator, FileExistsValidator, DirectoryExistsValidator +from .validators import YesNoValidator, IntValidator, FileExistsValidator, DirectoryExistsValidator import logging logger = logging.getLogger(__name__) @@ -100,20 +100,20 @@ def promptForString(self, message: str, param: str = None, default: str = "", is self.params[param] = response return response - def promptForInt(self, message: str, param: str = None, default: int = None) -> int: + def promptForInt(self, message: str, param: str = None, default: int = None, min=None, max=None) -> int: if param is not None and default is None: default = getenv(param.upper(), default=None) if default is None: - response = int(prompt(message=masPromptValue(message))) + response = int(prompt(message=masPromptValue(message), validator=IntValidator(min, max))) else: - response = int(prompt(message=masPromptValue(message), default=str(default))) + response = int(prompt(message=masPromptValue(message), validator=IntValidator(min, max), default=str(default))) if param is not None: self.params[param] = str(response) return response def promptForListSelect(self, message: str, options: list, param: str = None, default: int = None) -> str: - selection = self.promptForInt(message=message, default=default) + selection = self.promptForInt(message=message, default=default, min=1, max=len(options)) # List indices are 0 origin, so we need to subtract 1 from the selection made to arrive at the correct value self.setParam(param, options[selection - 1]) diff --git a/python/src/mas/cli/install/app.py b/python/src/mas/cli/install/app.py index f67408d156d..814d8e397e8 100644 --- a/python/src/mas/cli/install/app.py +++ b/python/src/mas/cli/install/app.py @@ -299,7 +299,7 @@ def configSLS(self) -> None: " 2. Install MAS with Dedicated License (AppPoints)", ] ) - self.slsMode = self.promptForInt("SLS Mode", default=1) + self.slsMode = self.promptForInt("SLS Mode", default=1, min=1, max=2) if self.slsMode not in [1, 2]: self.fatalError(f"Invalid selection: {self.slsMode}") @@ -441,6 +441,7 @@ def configMAS(self): self.setParam("sls_namespace", f"mas-{self.getParam('mas_instance_id')}-sls") self.configOperationMode() + self.configRoutingMode() self.configCATrust() self.configDNSAndCerts() self.configSSOProperties() @@ -469,7 +470,21 @@ def configOperationMode(self): " 1. Production", " 2. Non-Production" ]) - self.operationalMode = self.promptForInt("Operational Mode", default=1) + self.operationalMode = self.promptForInt("Operational Mode", default=1, min=1, max=2) + + @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") + self.printDescription([ + "Maximo Application Suite can be installed so it can be accessed with single domain URLs (path mode) or multi-domain URLs (subdomain mode):", + "", + " 1. Path (single domain)", + " 2. Subdomain (multi domain)" + ]) + routingModeInt = self.promptForInt("Routing Mode", default=1, min=1, max=2) + routingModeOptions = ["path", "subdomain"] + self.setParam("mas_routing_mode", routingModeOptions[routingModeInt - 1]) @logMethodCall def configAnnotations(self): @@ -508,7 +523,7 @@ def configDNSAndCerts(self): " 4. None (I will set up DNS myself)" ]) - dnsProvider = self.promptForInt("DNS Provider") + dnsProvider = self.promptForInt("DNS Provider", min=1, max=4) if dnsProvider == 1: self.configDNSAndCertsCloudflare() @@ -555,7 +570,7 @@ def configDNSAndCertsCloudflare(self): " 2. LetsEncrypt (Staging)", " 3. Self-Signed" ]) - certIssuer = self.promptForInt("Certificate issuer") + certIssuer = self.promptForInt("Certificate issuer", min=1, max=3) certIssuerOptions = [ f"{self.getParam('mas_instance_id')}-cloudflare-le-prod", f"{self.getParam('mas_instance_id')}-cloudflare-le-stg", @@ -577,7 +592,7 @@ def configDNSAndCertsCIS(self): " 2. LetsEncrypt (Staging)", " 3. Self-Signed" ]) - certIssuer = self.promptForInt("Certificate issuer") + certIssuer = self.promptForInt("Certificate issuer", min=1, max=3) certIssuerOptions = [ f"{self.getParam('mas_instance_id')}-cis-le-prod", f"{self.getParam('mas_instance_id')}-cis-le-stg", @@ -851,7 +866,7 @@ def chooseInstallFlavour(self) -> None: " - Configure whether to trust well-known certificate authorities by default (defaults to enabled)", " - Configure whether the Guided Tour feature is enabled (defaults to enabled)", " - Configure whether special characters are allowed in usernames and userids (defaults to disabled)", - " - Configure a custom domain, DNS integrations, and manual certificates", + " - Configure a custom domain, DNS integrations, routing mode and manual certificates", " - Customize Maximo Manage database settings (schema, tablespace, indexspace)", " - Customize Maximo Manage server bundle configuration (defaults to \"all\" configuration)", " - Enable optional Maximo Manage integration Cognos Analytics and Watson Studio Local", diff --git a/python/src/mas/cli/install/argBuilder.py b/python/src/mas/cli/install/argBuilder.py index 3b6533b5342..bd824a2009f 100644 --- a/python/src/mas/cli/install/argBuilder.py +++ b/python/src/mas/cli/install/argBuilder.py @@ -98,6 +98,9 @@ def buildCommand(self) -> str: if self.getParam('mas_manual_cert_mgmt') is True: command += f" --manual-certificates \"{self.manualCertsDir}\"{newline}" + if self.getParam('mas_routing_mode') != "": + command += f" --routing \"{self.getParam('mas_routing_mode')}\"{newline}" + if self.getParam('mas_domain') != "": command += f" --domain \"{self.getParam('mas_domain')}\"{newline}" diff --git a/python/src/mas/cli/install/argParser.py b/python/src/mas/cli/install/argParser.py index 116f14bc82c..39a291d1c0f 100644 --- a/python/src/mas/cli/install/argParser.py +++ b/python/src/mas/cli/install/argParser.py @@ -159,6 +159,13 @@ def isValidFile(parser, arg) -> str: action="store_const", const="false" ) +masAdvancedArgGroup.add_argument( + "--routing", + dest="mas_routing_mode", + required=False, + help="Configure MAS with path or subdomain routing", + choices=["path", "subdomain"] +) masAdvancedArgGroup.add_argument( "--manual-certificates", required=False, diff --git a/python/src/mas/cli/install/params.py b/python/src/mas/cli/install/params.py index 89cc9ff07fb..4a57ae3fa64 100644 --- a/python/src/mas/cli/install/params.py +++ b/python/src/mas/cli/install/params.py @@ -37,6 +37,7 @@ "mas_superuser_username", "mas_superuser_password", "mas_trust_default_cas", + "mas_routing_mode", "mas_app_settings_server_bundles_size", "mas_app_settings_default_jms", "mas_app_settings_persistent_volumes_flag", diff --git a/python/src/mas/cli/install/summarizer.py b/python/src/mas/cli/install/summarizer.py index 0fd50b6e7b5..03b27809629 100644 --- a/python/src/mas/cli/install/summarizer.py +++ b/python/src/mas/cli/install/summarizer.py @@ -75,6 +75,9 @@ def masSummary(self) -> None: elif self.getParam('dns_provider') == "": pass + print() + self.printParamSummary("Network Routing Mode", "mas_routing_mode") + print() self.printParamSummary("Configure Suite to run in IPV6", "enable_ipv6") diff --git a/python/src/mas/cli/validators.py b/python/src/mas/cli/validators.py index e9f8b54f6dd..426d2d82f27 100644 --- a/python/src/mas/cli/validators.py +++ b/python/src/mas/cli/validators.py @@ -123,6 +123,26 @@ def validate(self, document): raise ValidationError(message='Enter a valid response: y(es), n(o)', cursor_position=len(response)) +class IntValidator(Validator): + def __init__(self, min, max): + self.min = min + self.max = max + + def validate(self, document): + """ + Validate that a response is understandable as a yes/no response + """ + response = document.text + if not str.isdigit(response): + raise ValidationError(message='Enter a valid number', cursor_position=len(response)) + + if self.min and int(response) < self.min: + raise ValidationError(message=f'Enter a number not less than {self.min}', cursor_position=len(response)) + + if self.max and int(response) > self.max: + raise ValidationError(message=f'Enter a number not more than {self.max}', cursor_position=len(response)) + + class FileExistsValidator(Validator): def validate(self, document): """ diff --git a/tekton/src/params/install.yml.j2 b/tekton/src/params/install.yml.j2 index c241cdb9d51..d24422f5a44 100644 --- a/tekton/src/params/install.yml.j2 +++ b/tekton/src/params/install.yml.j2 @@ -439,6 +439,9 @@ - name: mas_annotations type: string default: "" +- name: mas_routing_mode + type: string + default: "" - name: mas_trust_default_cas type: string default: "" diff --git a/tekton/src/pipelines/taskdefs/core/suite-install.yml.j2 b/tekton/src/pipelines/taskdefs/core/suite-install.yml.j2 index ac28b43d697..5aedd68daff 100644 --- a/tekton/src/pipelines/taskdefs/core/suite-install.yml.j2 +++ b/tekton/src/pipelines/taskdefs/core/suite-install.yml.j2 @@ -54,6 +54,8 @@ value: $(params.disable_ldap_cookie) - name: allow_custom_cache_key value: $(params.allow_custom_cache_key) + - name: mas_routing_mode + value: $(params.mas_routing_mode) - name: mas_manual_cert_mgmt value: $(params.mas_manual_cert_mgmt) - name: mas_trust_default_cas diff --git a/tekton/src/tasks/suite-install.yml.j2 b/tekton/src/tasks/suite-install.yml.j2 index 489ef9aa93e..cc700cd8f79 100644 --- a/tekton/src/tasks/suite-install.yml.j2 +++ b/tekton/src/tasks/suite-install.yml.j2 @@ -43,6 +43,10 @@ spec: type: string description: Optional boolean parameter that when set to True, indicates that manually created certificates will be used to certify MAS and application routes default: "" + - name: mas_routing_mode + type: string + description: Optional string parameter, either path or subdomain, defines the network routing mode used for the suite. + default: "" - name: mas_trust_default_cas type: string description: Optional boolean parameter that when set to False, disables the normal trust of well known public certificate authorities @@ -150,6 +154,8 @@ spec: value: $(params.mas_domain) - name: MAS_CLUSTER_ISSUER value: $(params.mas_cluster_issuer) + - name: MAS_ROUTING_MODE + value: $(params.mas_routing_mode) - name: MAS_MANUAL_CERT_MGMT value: $(params.mas_manual_cert_mgmt) - name: MAS_TRUST_DEFAULT_CAS