Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
b2b466a
[patch] Add custom python-devops
rawa-resul Jan 11, 2025
9c13c79
[patch] Add SLS options
rawa-resul Jan 12, 2025
98488e9
[patch] Add further improvements
rawa-resul Jan 12, 2025
769250b
[patch] Fix validator
rawa-resul Jan 12, 2025
0d18cf3
[patch] Add NewNamespaceValidator
rawa-resul Jan 12, 2025
a51ceae
[patch] Update custom python-devops
rawa-resul Jan 12, 2025
16906ac
[patch] Fix invalid var referencing
rawa-resul Jan 12, 2025
55a44dc
[patch] SLS flow improvements
rawa-resul Jan 12, 2025
afb5c76
[patch] Update custom ansible-devops and python-devops
rawa-resul Jan 12, 2025
1e4dc99
[patch] Update SLS tekton definitions
rawa-resul Jan 12, 2025
52a6a4d
[patch] Add sls manual cert
rawa-resul Jan 12, 2025
0109b26
[patch] Fix validator
rawa-resul Jan 12, 2025
091e5c7
[patch] Fix bug in NewNamespaceValidator
rawa-resul Jan 12, 2025
e67632a
[patch] Add SLSInstanceSelectionValidator
rawa-resul Jan 13, 2025
4b174fb
[patch] Update SLS description
rawa-resul Jan 13, 2025
90981d4
[patch] Fix sls cert flow
rawa-resul Jan 13, 2025
285125a
[patch] Refactor sls flow
rawa-resul Jan 13, 2025
5741879
[patch] Update python-devops
rawa-resul Jan 13, 2025
f0f534f
[patch] Update ansible-devops
rawa-resul Jan 13, 2025
b21877d
[patch] Improve SLS flow
rawa-resul Jan 13, 2025
2145680
[patch] Set ImagePullPolicy to Always
rawa-resul Jan 13, 2025
98fad8b
[patch] Add image_pull_policy debug
rawa-resul Jan 13, 2025
96531e3
[patch] Fix imagePullPolicy
rawa-resul Jan 13, 2025
6693934
[patch] Check if args.image_pull_policy is not empty
rawa-resul Jan 13, 2025
ddfb73c
[patch] Update custom ansible-devops
rawa-resul Jan 14, 2025
77ec4cc
[patch] Fix SLS simple route message
rawa-resul Jan 14, 2025
3c610c6
[patch] Add SLS connection verification in external mode
rawa-resul Jan 14, 2025
dc7a805
[patch] Update custom python-devops
rawa-resul Jan 14, 2025
69979eb
[patch] Improve SLS simple flow
rawa-resul Jan 14, 2025
859046a
[patch] Make SLS License File optional for existing instance
rawa-resul Jan 14, 2025
53f90a2
[patch] Update custom python-devops
rawa-resul Jan 14, 2025
19bfd06
[patch] Update custom python-devops
rawa-resul Jan 14, 2025
8e0001d
[patch] Add slsLicenseFile function
rawa-resul Jan 14, 2025
d84b3f4
[patch] Update custom python-devops
rawa-resul Jan 14, 2025
a56a37f
[patch] Fix missing param in addFilesToSecret
rawa-resul Jan 14, 2025
4324eb2
[patch] Fix self.slsLicenseFileSecret
rawa-resul Jan 14, 2025
8998dac
[patch] Update custom ansible-devops
rawa-resul Jan 14, 2025
f493d46
Merge branch 'master' into sls-rr
rawa-resul Jan 14, 2025
38a9e08
[patch] Fix default sls namespace
rawa-resul Jan 15, 2025
ff308fd
[patch] Improve SLS validators
rawa-resul Jan 15, 2025
f601ee6
[patch] Fix message format
rawa-resul Jan 15, 2025
d0aaf6b
[patch] Fix msg
rawa-resul Jan 15, 2025
4a4c6e5
[patch] Improve sls simple route
rawa-resul Jan 15, 2025
38e6c82
[patch] Update SLS advanced flow
rawa-resul Jan 15, 2025
fefff8f
[patch] Update custom ansible-devops
rawa-resul Jan 15, 2025
5bcb840
[patch] Add debug in sls role
rawa-resul Jan 15, 2025
13344e7
[patch] Update custom ansible-devops
rawa-resul Jan 15, 2025
d627e07
[patch] Add encoding param to addFilesToSecret
rawa-resul Jan 15, 2025
ca722e2
[patch] Remove b64decode from mounted secret
rawa-resul Jan 16, 2025
eef95bb
[patch] Remove b64 env var
rawa-resul Jan 16, 2025
de8cecf
[patch] Add non-interactive support for new SLS flow
rawa-resul Jan 16, 2025
1ac32d9
[patch] Update custom python-devops
rawa-resul Jan 16, 2025
5cf1d3c
[patch] Fix arg name
rawa-resul Jan 16, 2025
f9f853b
[patch] Fix arg parser exception
rawa-resul Jan 16, 2025
23f048d
[patch] Fix argChecker
rawa-resul Jan 17, 2025
6e928dc
[patch] Cleanup
rawa-resul Jan 21, 2025
83f8961
Merge branch 'master' into sls-rr
rawa-resul Jan 21, 2025
acafe15
[patch] Update custom packages
rawa-resul Jan 21, 2025
02a22f9
[patch] Refactor SLS flow
rawa-resul Jan 23, 2025
4ba24e6
[patch] Undo some changes
rawa-resul Jan 23, 2025
2b3534e
[patch] Add --dedicated-sls arg
rawa-resul Jan 23, 2025
cec321f
[patch] Fix argBuilder.py
rawa-resul Jan 23, 2025
bd53ef2
[patch] Fix f-string error
rawa-resul Jan 23, 2025
3d4ecff
[patch] Fix SLS flow again
rawa-resul Jan 24, 2025
a789a9f
[patch] Fix conditional statement
rawa-resul Jan 24, 2025
a7a0161
[patch] Cleanup
rawa-resul Jan 24, 2025
f5b1f24
[patch] Minor improvements to SLS flow
rawa-resul Jan 24, 2025
858e681
[patch] Improve argBuilder for sls params
rawa-resul Jan 24, 2025
3a79cde
Merge branch 'master' into sls-rr
rawa-resul Jan 24, 2025
9c32266
Merge branch 'master' into sls-rr
rawa-resul Jan 27, 2025
af54cad
Merge branch 'master' into sls-rr
rawa-resul Jan 28, 2025
be36cc9
[patch] Add note
rawa-resul Jan 28, 2025
0affa7b
[patch] Update catalog output for feature channels
rawa-resul Jan 28, 2025
36c8868
[patch] Fix catalogReleases
rawa-resul Jan 28, 2025
2cfcac3
[patch] Change self.catalogReleases logic
rawa-resul Jan 28, 2025
c741536
[patch] Sort self.catalogReleases
rawa-resul Jan 28, 2025
63586c3
[patch] Fix bug in sorted()
rawa-resul Jan 28, 2025
e7d29fe
Merge branch 'master' into sls-rr
rawa-resul Jan 30, 2025
a7bd32b
[patch] Remove custom packages
rawa-resul Jan 30, 2025
ab244a9
Merge branch 'master' into sls-rr
rawa-resul Jan 30, 2025
79cc00d
[patch] Fix lint errors
rawa-resul Jan 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/reference/topology.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
- A dedicated manage database will be named `mas-{instanceId}-{workspaceId}-manage`
- Multiple `Kafka` clusters will be created using either Strimzi or Red Hat AMQ Streams operators, one for each MAS instance.
- The instance will be named `mas-{instanceId}-system`
- A single SLS `LicenseService` instance will be created, all MAS instances in the cluster will share the same pool of AppPoints from a single license file loaded into SLS. Each MAS instance is registered to SLS independently, uniquely identifying it to SLS.
- A single SLS `LicenseService` instance will be created, all MAS instances in the cluster will share the same pool of AppPoints from a single license file loaded into SLS. Each MAS instance is registered to SLS independently, uniquely identifying it to SLS. Alternatively, a dedicated SLS per MAS instance can be installed on the cluster.
- A single UDS `AnalyticsProxy` instance will be created, all MAS instaled in the cluster will be configured to report to this instance using a shared API key.

## High-Level View
Expand Down
3 changes: 2 additions & 1 deletion python/src/mas/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,10 @@ def __init__(self):
# Initialize the dictionary that will hold the parameters we pass to a PipelineRun
self.params = dict()

# These dicts will hold the additional-configs, pod-templates and manual certificates secrets
# These dicts will hold the additional-configs, pod-templates, sls license file and manual certificates secrets
self.additionalConfigsSecret = None
self.podTemplatesSecret = None
self.slsLicenseFileSecret = None
self.certsSecret = None

self._isSNO = None
Expand Down
105 changes: 81 additions & 24 deletions python/src/mas/cli/install/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@

from mas.devops.ocp import createNamespace, getStorageClasses
from mas.devops.mas import getCurrentCatalog, getDefaultStorageClasses
from mas.devops.sls import findSLSByNamespace
from mas.devops.data import getCatalog
from mas.devops.tekton import (
installOpenShiftPipelines,
Expand Down Expand Up @@ -158,9 +159,6 @@ def formatCatalog(self, name: str) -> str:
year = date[:2]
return f" - {monthName} 20{year} Update\n <Orange><u>https://ibm-mas.github.io/cli/catalogs/{name}</u></Orange>"

def formatRelease(self, release: str) -> str:
return f"{release} ... {self.catalogReleases[release]['core']}"

@logMethodCall
def processCatalogChoice(self) -> list:
self.catalogDigest = self.chosenCatalog["catalog_digest"]
Expand All @@ -184,25 +182,31 @@ def processCatalogChoice(self) -> list:
"Manage": "mas_manage_version",
}

self.catalogReleases = []
self.catalogReleases = {}
self.catalogTable = []

# Dynamically fetch the channels from the chosen catalog
# based on mas core
for channel in self.chosenCatalog["mas_core_version"]:
self.catalogReleases.append(channel)
# {"9.1-feature": "9.1.x-feature"}
self.catalogReleases.update({channel.replace('.x', ''): channel})

# Generate catalogTable
for application, key in applications.items():
self.catalogTable.append({"": application} | self.chosenCatalog[key])
# Add 9.1-feature channel based off 9.0 to those apps that have not onboarded yet
tempChosenCatalog = self.chosenCatalog[key].copy()
if '9.1.x-feature' not in tempChosenCatalog:
tempChosenCatalog.update({"9.1.x-feature": tempChosenCatalog["9.0.x"]})

self.catalogTable.append({"": application} | {key.replace(".x", ""): value for key, value in sorted(tempChosenCatalog.items(), reverse=True)})

if self.architecture == "s390x":
summary = [
"",
"<u>Catalog Details</u>",
f"Catalog Image: icr.io/cpopen/ibm-maximo-operator-catalog:{self.getParam('mas_catalog_version')}",
f"Catalog Digest: {self.catalogDigest}",
f"MAS Releases: {', '.join(self.catalogReleases)}",
f"MAS Releases: {', '.join(sorted(self.catalogReleases, reverse=True))}",
f"MongoDb: {self.catalogMongoDbVersion}",
]
else:
Expand All @@ -211,7 +215,7 @@ def processCatalogChoice(self) -> list:
"<u>Catalog Details</u>",
f"Catalog Image: icr.io/cpopen/ibm-maximo-operator-catalog:{self.getParam('mas_catalog_version')}",
f"Catalog Digest: {self.catalogDigest}",
f"MAS Releases: {', '.join(self.catalogReleases)}",
f"MAS Releases: {', '.join(sorted(self.catalogReleases, reverse=True))}",
f"Cloud Pak for Data: {self.catalogCp4dVersion}",
f"MongoDb: {self.catalogMongoDbVersion}",
]
Expand Down Expand Up @@ -254,28 +258,64 @@ def configCatalog(self):
self.printDescription(catalogSummary)
self.printDescription([
"",
"Multiple releases of Maximo Application Suite are available, each is supported under IBM's standard 3+1+3 support model.",
"Choose the release of IBM Maximo Application Suite that you want to use for this installation from the table below:",
"Two types of release are available:",
" - GA releases of Maximo Application Suite are supported under IBM's standard 3+1+3 support lifecycle policy.",
" - 'Feature' releases allow early access to new features for evaluation in non-production environments and are only supported through to the next GA release.",
""
])

print(tabulate(self.catalogTable, headers="keys", tablefmt="simple_grid"))

releaseCompleter = WordCompleter(self.catalogReleases)
releaseCompleter = WordCompleter(sorted(self.catalogReleases, reverse=True))
releaseSelection = self.promptForString("Select release", completer=releaseCompleter)

self.setParam("mas_channel", releaseSelection)
self.setParam("mas_channel", self.catalogReleases[releaseSelection])

@logMethodCall
def configSLS(self) -> None:
self.printH1("Configure Product License")
self.printH1("Configure AppPoint Licensing")
self.printDescription(
[
"By default the MAS instance will be configured to use a cluster-shared License, this provides a shared pool of AppPoints available to all MAS instances on the cluster.",
"",
]
)

self.slsMode = 1
self.slsLicenseFileLocal = None

if self.showAdvancedOptions:
self.printDescription(
[
"Alternatively you may choose to install using a dedicated license only available to this MAS instance.",
" 1. Install MAS with Cluster-Shared License (AppPoints)",
" 2. Install MAS with Dedicated License (AppPoints)",
]
)
self.slsMode = self.promptForInt("SLS Mode", default=1)

if self.slsMode not in [1, 2]:
self.fatalError(f"Invalid selection: {self.slsMode}")

if not (self.slsMode == 2 and not self.getParam("sls_namespace")):
sls_namespace = "ibm-sls" if self.slsMode == 1 else self.getParam("sls_namespace")
if findSLSByNamespace(sls_namespace, dynClient=self.dynamicClient):
print_formatted_text(HTML(f"<MediumSeaGreen>SLS auto-detected: {sls_namespace}</MediumSeaGreen>"))
print()
if not self.yesOrNo("Upload/Replace the license file"):
self.setParam("sls_action", "gencfg")
return

self.slsLicenseFileLocal = self.promptForFile("License file", mustExist=True, envVar="SLS_LICENSE_FILE_LOCAL")
self.setParam("sls_action", "install")

@logMethodCall
def configDRO(self) -> None:
self.promptForString("Contact e-mail address", "uds_contact_email")
self.promptForString("Contact first name", "uds_contact_firstname")
self.promptForString("Contact last name", "uds_contact_lastname")

if self.showAdvancedOptions:
self.promptForString("IBM Suite License Services (SLS) Namespace", "sls_namespace", default="ibm-sls")
self.promptForString("IBM Data Reporter Operator (DRO) Namespace", "dro_namespace", default="redhat-marketplace")

@logMethodCall
Expand Down Expand Up @@ -396,6 +436,9 @@ def configMAS(self):
])
self.promptForString("Workspace name", "mas_workspace_name", validator=WorkspaceNameFormatValidator())

if self.slsMode == 2 and not self.getParam("sls_namespace"):
self.setParam("sls_namespace", f"mas-{self.getParam('mas_instance_id')}-sls")

self.configOperationMode()
self.configCATrust()
self.configDNSAndCerts()
Expand Down Expand Up @@ -747,6 +790,7 @@ def interactiveMode(self, simplified: bool, advanced: bool) -> None:

# Licensing (SLS and DRO)
self.configSLS()
self.configDRO()
self.configICRCredentials()

# MAS Core
Expand Down Expand Up @@ -795,6 +839,7 @@ def nonInteractiveMode(self) -> None:
self.deployCP4D = False
self.db2SetAffinity = False
self.db2SetTolerations = False
self.slsLicenseFileLocal = None

self.approvals = {
"approval_core": {"id": "suite-verify"}, # After Core Platform verification has completed
Expand Down Expand Up @@ -903,6 +948,14 @@ def nonInteractiveMode(self) -> None:
self.setParam(key, value)
if value in ["jms", "snojms"]:
self.setParam("mas_app_settings_persistent_volumes_flag", "true")
# SLS
elif key == "license_file":
if value is not None and value != "":
self.slsLicenseFileLocal = value
self.setParam("sls_action", "install")
elif key == "dedicated_sls":
if value:
self.setParam("sls_namespace", f"mas-{self.args.mas_instance_id}-sls")

# These settings are used by the CLI rather than passed to the PipelineRun
elif key == "storage_accessmode":
Expand All @@ -913,10 +966,6 @@ def nonInteractiveMode(self) -> None:
if value is None:
self.fatalError(f"{key} must be set")
self.pipelineStorageClass = value
elif key == "license_file":
if value is None:
self.fatalError(f"{key} must be set")
self.slsLicenseFileLocal = value

elif key.startswith("approval_"):
if key not in self.approvals:
Expand Down Expand Up @@ -954,6 +1003,13 @@ def nonInteractiveMode(self) -> None:
# Load the catalog information
self.chosenCatalog = getCatalog(self.getParam("mas_catalog_version"))

# License file is only optional for existing SLS instance
if self.slsLicenseFileLocal is None:
if findSLSByNamespace(self.getParam("sls_namespace"), dynClient=self.dynamicClient):
self.setParam("sls_action", "gencfg")
else:
self.fatalError("--license-file must be set for new SLS install")

# Once we've processed the inputs, we should validate the catalog source & prompt to accept the license terms
if not self.devMode:
self.validateCatalogSource()
Expand All @@ -977,6 +1033,10 @@ def install(self, argv):
self.devMode = args.dev_mode
self.skipGrafanaInstall = args.skip_grafana_install

# Set image_pull_policy of the CLI in interactive mode
if args.image_pull_policy and args.image_pull_policy != "":
self.setParam("image_pull_policy", args.image_pull_policy)

self.approvals = {}

# Store all args
Expand Down Expand Up @@ -1024,13 +1084,10 @@ def install(self, argv):
if self.deployCP4D:
self.configCP4D()

# The entitlement file for SLS is mounted as a secret in /workspace/entitlement
entitlementFileBaseName = path.basename(self.slsLicenseFileLocal)
self.setParam("sls_entitlement_file", f"/workspace/entitlement/{entitlementFileBaseName}")

# Set up the secrets for additional configs, podtemplates and manual certificates
# Set up the secrets for additional configs, podtemplates, sls license file and manual certificates
self.additionalConfigs()
self.podTemplates()
self.slsLicenseFile()
self.manualCertificates()

# Show a summary of the installation configuration
Expand Down Expand Up @@ -1082,7 +1139,7 @@ def install(self, argv):
prepareInstallSecrets(
dynClient=self.dynamicClient,
instanceId=self.getParam("mas_instance_id"),
slsLicenseFile=self.slsLicenseFileLocal,
slsLicenseFile=self.slsLicenseFileSecret,
additionalConfigs=self.additionalConfigsSecret,
podTemplates=self.podTemplatesSecret,
certs=self.certsSecret
Expand Down
12 changes: 9 additions & 3 deletions python/src/mas/cli/install/argBuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,15 @@ def buildCommand(self) -> str:

# IBM Suite License Service
# -----------------------------------------------------------------------------
command += f" --license-file \"{self.slsLicenseFileLocal}\"{newline}"
if self.getParam("sls_namespace") != "ibm-sls":
command += f" --sls-namespace \"{self.getParam('sls_namespace')}\"{newline}"
if self.getParam("sls_namespace") and self.getParam("sls_namespace") != "ibm-sls":
if self.getParam("mas_instance_id") and self.getParam("sls_namespace") == f"mas-{self.getParam('mas_instance_id')}-sls":
command += " --dedicated-sls"
else:
command += f" --sls-namespace \"{self.getParam('sls_namespace')}\""
if self.slsLicenseFileLocal:
command += f" --license-file \"{self.slsLicenseFileLocal}\""
if self.getParam("sls_namespace") and self.getParam("sls_namespace") != "ibm-sls" or self.slsLicenseFileLocal:
command += newline

# IBM Data Reporting Operator (DRO)
# -----------------------------------------------------------------------------
Expand Down
6 changes: 6 additions & 0 deletions python/src/mas/cli/install/argParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,12 @@ def isValidFile(parser, arg) -> str:
help="Customize the SLS install namespace",
default="ibm-sls"
)
slsArgGroup.add_argument(
"--dedicated-sls",
action="store_true",
default=False,
help="Set the SLS namespace to mas-<instanceid>-sls"
)

# IBM Data Reporting Operator (DRO)
# -----------------------------------------------------------------------------
Expand Down
13 changes: 13 additions & 0 deletions python/src/mas/cli/install/settings/additionalConfigs.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,19 @@ def manualCertificates(self) -> None:

self.certsSecret = certsSecret

def slsLicenseFile(self) -> None:
if self.slsLicenseFileLocal:
slsLicenseFileSecret = {
"apiVersion": "v1",
"kind": "Secret",
"type": "Opaque",
"metadata": {
"name": "pipeline-sls-entitlement"
}
}
self.setParam("sls_entitlement_file", f"/workspace/entitlement/{path.basename(self.slsLicenseFileLocal)}")
self.slsLicenseFileSecret = self.addFilesToSecret(slsLicenseFileSecret, self.slsLicenseFileLocal, '')

def addFilesToSecret(self, secretDict: dict, configPath: str, extension: str, keyPrefix: str = '') -> dict:
"""
Add file (or files) to pipeline-additional-configs
Expand Down
7 changes: 5 additions & 2 deletions python/src/mas/cli/install/summarizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,9 +270,12 @@ def droSummary(self) -> None:

def slsSummary(self) -> None:
self.printH2("IBM Suite License Service")
self.printSummary("License File", self.slsLicenseFileLocal)
self.printParamSummary("IBM Open Registry", "sls_icr_cpopen")
self.printParamSummary("Namespace", "sls_namespace")
if self.getParam("sls_action") == "install":
self.printSummary("Subscription Channel", "3.x")
self.printParamSummary("IBM Open Registry", "sls_icr_cpopen")
if self.slsLicenseFileLocal:
self.printSummary("License File", self.slsLicenseFileLocal)

def cosSummary(self) -> None:
self.printH2("Cloud Object Storage")
Expand Down
5 changes: 4 additions & 1 deletion tekton/src/params/install.yml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
default: ""
- name: sls_entitlement_file
type: string
default: "/workspace/entitlement/entitlement.lic"
default: ""
- name: sls_mongodb_cfg_file
type: string
# The default value works for the default in-cluster install, it will need
Expand All @@ -42,6 +42,9 @@
- name: sls_icr_cpopen
type: string
default: ""
- name: sls_action
type: string
default: ""

# Dependencies - MongoDb
# -----------------------------------------------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions tekton/src/pipelines/taskdefs/dependencies/sls.yml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
value: $(params.sls_channel)
- name: sls_icr_cpopen
value: $(params.sls_icr_cpopen)
- name: sls_action
value: $(params.sls_action)

# New way of bootstrapping license file
- name: sls_entitlement_file
Expand Down