mas-cli 5.1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mas-cli might be problematic. Click here for more details.
- mas/cli/__init__.py +11 -0
- mas/cli/aiservice/install/__init__.py +11 -0
- mas/cli/aiservice/install/app.py +894 -0
- mas/cli/aiservice/install/argBuilder.py +180 -0
- mas/cli/aiservice/install/argParser.py +507 -0
- mas/cli/aiservice/install/params.py +100 -0
- mas/cli/aiservice/install/summarizer.py +134 -0
- mas/cli/cli.py +432 -0
- mas/cli/displayMixins.py +132 -0
- mas/cli/gencfg.py +113 -0
- mas/cli/install/__init__.py +11 -0
- mas/cli/install/app.py +1316 -0
- mas/cli/install/argBuilder.py +465 -0
- mas/cli/install/argParser.py +1176 -0
- mas/cli/install/catalogs.py +27 -0
- mas/cli/install/params.py +172 -0
- mas/cli/install/settings/__init__.py +23 -0
- mas/cli/install/settings/additionalConfigs.py +227 -0
- mas/cli/install/settings/db2Settings.py +252 -0
- mas/cli/install/settings/kafkaSettings.py +103 -0
- mas/cli/install/settings/manageSettings.py +273 -0
- mas/cli/install/settings/mongodbSettings.py +46 -0
- mas/cli/install/settings/turbonomicSettings.py +29 -0
- mas/cli/install/summarizer.py +398 -0
- mas/cli/templates/facilities-configs.yml.j2 +25 -0
- mas/cli/templates/ibm-mas-tekton.yaml +49772 -0
- mas/cli/templates/jdbccfg.yml.j2 +52 -0
- mas/cli/templates/pod-templates/best-effort/ibm-data-dictionary-assetdatadictionary.yml +26 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-bascfg.yml +56 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-coreidp.yml +21 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-actions.yml +28 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-auth.yml +32 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-datapower.yml +12 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-devops.yml +14 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-dm.yml +22 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-dsc.yml +40 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-edgeconfig.yml +10 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-fpl.yml +24 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-guardian.yml +20 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-iot.yml +10 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-mbgx.yml +18 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-mfgx.yml +14 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-monitor.yml +18 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-orgmgmt.yml +48 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-provision.yml +28 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-registry.yml +26 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-state.yml +40 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-webui.yml +22 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-manage-healthextaccelerator.yml +13 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-manage-healthextworkspace.yml +10 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-manage-imagestitching.yml +10 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-manage-manageaccelerators.yml +10 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-manage-manageapp.yml +46 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-manage-manageworkspace.yml +48 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-manage-slackproxy.yml +10 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-pushnotificationcfg.yml +13 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-scimcfg.yml +14 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-slscfg.yml +10 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-smtpcfg.yml +10 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-suite.yml +136 -0
- mas/cli/templates/pod-templates/best-effort/ibm-mas-visualinspection.yml +34 -0
- mas/cli/templates/pod-templates/best-effort/ibm-sls-licenseservice.yml +10 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-data-dictionary-assetdatadictionary.yml +56 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-bascfg.yml +140 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-coreidp.yml +45 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-actions.yml +70 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-auth.yml +80 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-datapower.yml +24 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-devops.yml +26 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-dm.yml +52 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-dsc.yml +106 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-edgeconfig.yml +16 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-fpl.yml +62 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-guardian.yml +44 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-iot.yml +16 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-mbgx.yml +42 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-mfgx.yml +32 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-monitor.yml +42 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-orgmgmt.yml +126 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-provision.yml +70 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-registry.yml +62 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-state.yml +106 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-webui.yml +52 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-manage-healthextaccelerator.yml +28 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-manage-healthextworkspace.yml +18 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-manage-imagestitching.yml +16 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-manage-manageaccelerators.yml +16 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-manage-manageapp.yml +106 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-manage-manageworkspace.yml +126 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-manage-slackproxy.yml +16 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-pushnotificationcfg.yml +25 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-scimcfg.yml +26 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-slscfg.yml +16 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-smtpcfg.yml +16 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-suite.yml +340 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-mas-visualinspection.yml +76 -0
- mas/cli/templates/pod-templates/guaranteed/ibm-sls-licenseservice.yml +16 -0
- mas/cli/templates/suite_mongocfg.yml.j2 +55 -0
- mas/cli/uninstall/__init__.py +11 -0
- mas/cli/uninstall/app.py +197 -0
- mas/cli/uninstall/argParser.py +115 -0
- mas/cli/update/__init__.py +11 -0
- mas/cli/update/app.py +673 -0
- mas/cli/update/argParser.py +156 -0
- mas/cli/upgrade/__init__.py +11 -0
- mas/cli/upgrade/app.py +164 -0
- mas/cli/upgrade/argParser.py +68 -0
- mas/cli/upgrade/settings/__init__.py +19 -0
- mas/cli/validators.py +151 -0
- mas_cli-5.1.4.data/scripts/mas-cli +87 -0
- mas_cli-5.1.4.dist-info/METADATA +73 -0
- mas_cli-5.1.4.dist-info/RECORD +114 -0
- mas_cli-5.1.4.dist-info/WHEEL +5 -0
- mas_cli-5.1.4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# *****************************************************************************
|
|
2
|
+
# Copyright (c) 2024, 2025 IBM Corporation and other Contributors.
|
|
3
|
+
#
|
|
4
|
+
# All rights reserved. This program and the accompanying materials
|
|
5
|
+
# are made available under the terms of the Eclipse Public License v1.0
|
|
6
|
+
# which accompanies this distribution, and is available at
|
|
7
|
+
# http://www.eclipse.org/legal/epl-v10.html
|
|
8
|
+
#
|
|
9
|
+
# *****************************************************************************
|
|
10
|
+
|
|
11
|
+
requiredParams = [
|
|
12
|
+
# MAS
|
|
13
|
+
"mas_catalog_version",
|
|
14
|
+
# Storage classes
|
|
15
|
+
"storage_class_rwo",
|
|
16
|
+
"storage_class_rwx",
|
|
17
|
+
# Entitlement
|
|
18
|
+
"ibm_entitlement_key",
|
|
19
|
+
# DRO
|
|
20
|
+
"uds_contact_email",
|
|
21
|
+
"uds_contact_firstname",
|
|
22
|
+
"uds_contact_lastname"
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
optionalParams = [
|
|
26
|
+
# Pipeline
|
|
27
|
+
"image_pull_policy",
|
|
28
|
+
"service_account_name",
|
|
29
|
+
# Catalogue
|
|
30
|
+
"mas_catalog_digest",
|
|
31
|
+
# SLS
|
|
32
|
+
"sls_namespace",
|
|
33
|
+
# DRO
|
|
34
|
+
"dro_namespace",
|
|
35
|
+
# Db2
|
|
36
|
+
"db2_action_system",
|
|
37
|
+
"db2_action_manage",
|
|
38
|
+
"db2_action_facilities",
|
|
39
|
+
"db2_type",
|
|
40
|
+
"db2_timezone",
|
|
41
|
+
"db2_namespace",
|
|
42
|
+
"db2_channel",
|
|
43
|
+
"db2_affinity_key",
|
|
44
|
+
"db2_affinity_value",
|
|
45
|
+
"db2_tolerate_key",
|
|
46
|
+
"db2_tolerate_value",
|
|
47
|
+
"db2_tolerate_effect",
|
|
48
|
+
"db2_cpu_requests",
|
|
49
|
+
"db2_cpu_limits",
|
|
50
|
+
"db2_memory_requests",
|
|
51
|
+
"db2_memory_limits",
|
|
52
|
+
"db2_backup_storage_size",
|
|
53
|
+
"db2_data_storage_size",
|
|
54
|
+
"db2_logs_storage_size",
|
|
55
|
+
"db2_meta_storage_size",
|
|
56
|
+
"db2_temp_storage_size",
|
|
57
|
+
# Dev Mode
|
|
58
|
+
"artifactory_username",
|
|
59
|
+
"artifactory_token",
|
|
60
|
+
# Aibroker
|
|
61
|
+
"aiservice_storage_provider",
|
|
62
|
+
"aiservice_storage_accesskey",
|
|
63
|
+
"aiservice_storage_secretkey",
|
|
64
|
+
"aiservice_storage_host",
|
|
65
|
+
"aiservice_storage_port",
|
|
66
|
+
"aiservice_storage_ssl",
|
|
67
|
+
"aiservice_storage_region",
|
|
68
|
+
"aiservice_storage_pipelines_bucket",
|
|
69
|
+
"aiservice_storage_tenants_bucket",
|
|
70
|
+
"aiservice_storage_templates_bucket",
|
|
71
|
+
|
|
72
|
+
"aiservice_watsonxai_apikey",
|
|
73
|
+
"aiservice_watsonxai_url",
|
|
74
|
+
"aiservice_watsonxai_project_id",
|
|
75
|
+
"aiservice_watsonx_action",
|
|
76
|
+
|
|
77
|
+
"aiservice_instance_id",
|
|
78
|
+
|
|
79
|
+
"minio_root_user",
|
|
80
|
+
"minio_root_password",
|
|
81
|
+
|
|
82
|
+
"tenant_entitlement_type",
|
|
83
|
+
"tenant_entitlement_start_date",
|
|
84
|
+
"tenant_entitlement_end_date",
|
|
85
|
+
|
|
86
|
+
"aiservice_s3_bucket_prefix",
|
|
87
|
+
"aiservice_s3_region",
|
|
88
|
+
"aiservice_s3_endpoint_url",
|
|
89
|
+
"aiservice_tenant_s3_bucket_prefix",
|
|
90
|
+
"aiservice_tenant_s3_region",
|
|
91
|
+
"aiservice_tenant_s3_endpoint_url",
|
|
92
|
+
"aiservice_tenant_s3_access_key",
|
|
93
|
+
"aiservice_tenant_s3_secret_key",
|
|
94
|
+
|
|
95
|
+
"rsl_url",
|
|
96
|
+
"rsl_org_id",
|
|
97
|
+
"rsl_token",
|
|
98
|
+
|
|
99
|
+
"environment_type",
|
|
100
|
+
]
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# *****************************************************************************
|
|
2
|
+
# Copyright (c) 2024, 2025 IBM Corporation and other Contributors.
|
|
3
|
+
#
|
|
4
|
+
# All rights reserved. This program and the accompanying materials
|
|
5
|
+
# are made available under the terms of the Eclipse Public License v1.0
|
|
6
|
+
# which accompanies this distribution, and is available at
|
|
7
|
+
# http://www.eclipse.org/legal/epl-v10.html
|
|
8
|
+
#
|
|
9
|
+
# *****************************************************************************
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
import yaml
|
|
13
|
+
from mas.devops.ocp import getConsoleURL
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class aiServiceInstallSummarizerMixin():
|
|
19
|
+
def ocpSummary(self) -> None:
|
|
20
|
+
self.printH2("Pipeline Configuration")
|
|
21
|
+
self.printParamSummary("Service Account", "service_account_name")
|
|
22
|
+
self.printParamSummary("Image Pull Policy", "image_pull_policy")
|
|
23
|
+
self.printSummary("Skip Pre-Install Healthcheck", "Yes" if self.getParam('skip_pre_check') == "true" else "No")
|
|
24
|
+
|
|
25
|
+
self.printH2("OpenShift Container Platform")
|
|
26
|
+
self.printSummary("Worker Node Architecture", self.architecture)
|
|
27
|
+
self.printSummary("Storage Class Provider", self.storageClassProvider)
|
|
28
|
+
self.printParamSummary("ReadWriteOnce Storage Class", "storage_class_rwo")
|
|
29
|
+
self.printParamSummary("ReadWriteMany Storage Class", "storage_class_rwx")
|
|
30
|
+
|
|
31
|
+
self.printParamSummary("Certificate Manager", "cert_manager_provider")
|
|
32
|
+
self.printParamSummary("Cluster Ingress Certificate Secret", "ocp_ingress_tls_secret_name")
|
|
33
|
+
|
|
34
|
+
def aiServiceSummary(self) -> None:
|
|
35
|
+
self.printH2("Maximo Operator Catalog")
|
|
36
|
+
self.printParamSummary("Catalog Version", "mas_catalog_version")
|
|
37
|
+
# We only list the digest if it's specified (primary use case is when running development builds in airgap environments)
|
|
38
|
+
if self.getParam("mas_catalog_digest" != ""):
|
|
39
|
+
self.printParamSummary("Catalog Digest", "mas_catalog_digest")
|
|
40
|
+
|
|
41
|
+
self.printH2("IBM Container Registry")
|
|
42
|
+
self.printParamSummary("IBM Entitled Registry", "mas_icr_cp")
|
|
43
|
+
self.printParamSummary("IBM Open Registry", "mas_icr_cpopen")
|
|
44
|
+
|
|
45
|
+
self.printH2("AI Service")
|
|
46
|
+
self.printParamSummary("Release", "aiservice_channel")
|
|
47
|
+
self.printParamSummary("Instance ID", "aiservice_instance_id")
|
|
48
|
+
self.printParamSummary("Environment type", "environment_type")
|
|
49
|
+
|
|
50
|
+
self.printH2("S3 Configuration")
|
|
51
|
+
self.printParamSummary("Storage provider", "aiservice_storage_provider")
|
|
52
|
+
if self.getParam("aiservice_storage_provider") == "minio":
|
|
53
|
+
self.printParamSummary("minio root username", "minio_root_user")
|
|
54
|
+
print()
|
|
55
|
+
self.printParamSummary("Storage access key", "aiservice_storage_accesskey")
|
|
56
|
+
self.printParamSummary("Storage host", "aiservice_storage_host")
|
|
57
|
+
self.printParamSummary("Storage port", "aiservice_storage_port")
|
|
58
|
+
self.printParamSummary("Storage ssl", "aiservice_storage_ssl")
|
|
59
|
+
self.printParamSummary("Storage region", "aiservice_storage_region")
|
|
60
|
+
self.printParamSummary("Storage pipelines bucket", "aiservice_storage_pipelines_bucket")
|
|
61
|
+
self.printParamSummary("Storage tenants bucket", "aiservice_storage_tenants_bucket")
|
|
62
|
+
self.printParamSummary("Storage templates bucket", "aiservice_storage_templates_bucket")
|
|
63
|
+
print()
|
|
64
|
+
self.printParamSummary("S3 bucket prefix", "aiservice_s3_bucket_prefix")
|
|
65
|
+
self.printParamSummary("S3 endpoint url", "aiservice_s3_endpoint_url")
|
|
66
|
+
self.printParamSummary("S3 bucket prefix (tenant level)", "aiservice_tenant_s3_bucket_prefix")
|
|
67
|
+
self.printParamSummary("S3 region (tenant level)", "aiservice_tenant_s3_region")
|
|
68
|
+
self.printParamSummary("S3 endpoint url (tenant level)", "aiservice_tenant_s3_endpoint_url")
|
|
69
|
+
|
|
70
|
+
self.printH2("IBM WatsonX")
|
|
71
|
+
self.printParamSummary("Watsonxai machine learning url", "aiservice_watsonxai_url")
|
|
72
|
+
self.printParamSummary("Watsonxai project id", "aiservice_watsonxai_project_id")
|
|
73
|
+
|
|
74
|
+
self.printH2("AI Service Tenant")
|
|
75
|
+
self.printParamSummary("Tenant entitlement type", "tenant_entitlement_type")
|
|
76
|
+
self.printParamSummary("Tenant start date", "tenant_entitlement_start_date")
|
|
77
|
+
self.printParamSummary("Tenant end date", "tenant_entitlement_end_date")
|
|
78
|
+
|
|
79
|
+
self.printH2("RSL")
|
|
80
|
+
self.printParamSummary("RSL url", "rsl_url")
|
|
81
|
+
self.printParamSummary("ORG Id of RSL", "rsl_org_id")
|
|
82
|
+
self.printParamSummary("Token for RSL", "rsl_token")
|
|
83
|
+
|
|
84
|
+
def db2Summary(self) -> None:
|
|
85
|
+
self.printH2("IBM Db2 Univeral Operator Configuration")
|
|
86
|
+
self.printParamSummary("Action", "db2_action_aiservice")
|
|
87
|
+
self.printParamSummary("Install Namespace", "db2_namespace")
|
|
88
|
+
self.printParamSummary("Subscription Channel", "db2_channel")
|
|
89
|
+
|
|
90
|
+
def droSummary(self) -> None:
|
|
91
|
+
self.printH2("IBM Data Reporter Operator (DRO) Configuration")
|
|
92
|
+
self.printParamSummary("Contact e-mail", "uds_contact_email")
|
|
93
|
+
self.printParamSummary("First name", "uds_contact_firstname")
|
|
94
|
+
self.printParamSummary("Last name", "uds_contact_lastname")
|
|
95
|
+
self.printParamSummary("Install Namespace", "dro_namespace")
|
|
96
|
+
|
|
97
|
+
def slsSummary(self) -> None:
|
|
98
|
+
self.printH2("IBM Suite License Service")
|
|
99
|
+
self.printParamSummary("Namespace", "sls_namespace")
|
|
100
|
+
if self.getParam("sls_action") == "install":
|
|
101
|
+
self.printSummary("Subscription Channel", "3.x")
|
|
102
|
+
self.printParamSummary("IBM Open Registry", "sls_icr_cpopen")
|
|
103
|
+
if self.slsLicenseFileLocal:
|
|
104
|
+
self.printSummary("License File", self.slsLicenseFileLocal)
|
|
105
|
+
|
|
106
|
+
def mongoSummary(self) -> None:
|
|
107
|
+
self.printH2("MongoDb")
|
|
108
|
+
if self.getParam("mongodb_action") == "install":
|
|
109
|
+
self.printSummary("Type", "MongoCE Operator")
|
|
110
|
+
self.printParamSummary("Install Namespace", "mongodb_namespace")
|
|
111
|
+
elif self.getParam("mongodb_action") == "byo":
|
|
112
|
+
self.printSummary("Type", "BYO (mongodb-system.yaml)")
|
|
113
|
+
else:
|
|
114
|
+
self.fatalError(f"Unexpected value for mongodb_action parameter: {self.getParam('mongodb_action')}")
|
|
115
|
+
|
|
116
|
+
def displayInstallSummary(self) -> None:
|
|
117
|
+
self.printH1("Review Settings")
|
|
118
|
+
self.printDescription([
|
|
119
|
+
"Connected to:",
|
|
120
|
+
f" - <u>{getConsoleURL(self.dynamicClient)}</u>"
|
|
121
|
+
])
|
|
122
|
+
|
|
123
|
+
logger.debug("PipelineRun parameters:")
|
|
124
|
+
logger.debug(yaml.dump(self.params, default_flow_style=False))
|
|
125
|
+
|
|
126
|
+
# Cluster Config & AI Service
|
|
127
|
+
self.ocpSummary()
|
|
128
|
+
self.aiServiceSummary()
|
|
129
|
+
|
|
130
|
+
# Dependencies
|
|
131
|
+
self.droSummary()
|
|
132
|
+
self.slsSummary()
|
|
133
|
+
self.mongoSummary()
|
|
134
|
+
self.db2Summary()
|
mas/cli/cli.py
ADDED
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
# *****************************************************************************
|
|
2
|
+
# Copyright (c) 2024 IBM Corporation and other Contributors.
|
|
3
|
+
#
|
|
4
|
+
# All rights reserved. This program and the accompanying materials
|
|
5
|
+
# are made available under the terms of the Eclipse Public License v1.0
|
|
6
|
+
# which accompanies this distribution, and is available at
|
|
7
|
+
# http://www.eclipse.org/legal/epl-v10.html
|
|
8
|
+
#
|
|
9
|
+
# *****************************************************************************
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
import urllib3
|
|
13
|
+
|
|
14
|
+
from argparse import RawTextHelpFormatter
|
|
15
|
+
from shutil import which
|
|
16
|
+
from os import path, environ
|
|
17
|
+
from sys import exit
|
|
18
|
+
from subprocess import PIPE, Popen, TimeoutExpired
|
|
19
|
+
import threading
|
|
20
|
+
import json
|
|
21
|
+
|
|
22
|
+
# Use of the openshift client rather than the kubernetes client allows us access to "apply"
|
|
23
|
+
from kubernetes import config
|
|
24
|
+
from kubernetes.client import api_client, Configuration
|
|
25
|
+
from openshift.dynamic import DynamicClient
|
|
26
|
+
from openshift.dynamic.exceptions import NotFoundError
|
|
27
|
+
|
|
28
|
+
from prompt_toolkit import prompt, print_formatted_text, HTML
|
|
29
|
+
|
|
30
|
+
from mas.devops.mas import isAirgapInstall
|
|
31
|
+
from mas.devops.ocp import connect, isSNO, getNodes
|
|
32
|
+
|
|
33
|
+
from .displayMixins import PrintMixin, PromptMixin
|
|
34
|
+
|
|
35
|
+
# Configure the logger
|
|
36
|
+
logger = logging.getLogger(__name__)
|
|
37
|
+
|
|
38
|
+
# Disable warnings when users are connecting to OCP clusters with self-signed certificates
|
|
39
|
+
urllib3.disable_warnings()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def getHelpFormatter(formatter=RawTextHelpFormatter, w=160, h=50):
|
|
43
|
+
"""
|
|
44
|
+
Return a wider HelpFormatter, if possible.
|
|
45
|
+
|
|
46
|
+
https://stackoverflow.com/a/57655311
|
|
47
|
+
"""
|
|
48
|
+
try:
|
|
49
|
+
kwargs = {'width': w, 'max_help_position': h}
|
|
50
|
+
formatter(None, **kwargs)
|
|
51
|
+
return lambda prog: formatter(prog, **kwargs)
|
|
52
|
+
except TypeError:
|
|
53
|
+
logger.warning("argparse help formatter failed, falling back.")
|
|
54
|
+
return formatter
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class RunCmdResult(object):
|
|
58
|
+
def __init__(self, returnCode, output, error):
|
|
59
|
+
self.rc = returnCode
|
|
60
|
+
self.out = output
|
|
61
|
+
self.err = error
|
|
62
|
+
|
|
63
|
+
def successful(self):
|
|
64
|
+
return self.rc == 0
|
|
65
|
+
|
|
66
|
+
def failed(self):
|
|
67
|
+
return self.rc != 0
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def runCmd(cmdArray, timeout=630):
|
|
71
|
+
"""
|
|
72
|
+
Run a command on the local host. This drives all the helm operations,
|
|
73
|
+
as there is no python Helm client available.
|
|
74
|
+
# Parameters
|
|
75
|
+
cmdArray (list<string>): Command to execute
|
|
76
|
+
timeout (int): How long to allow for the command to complete
|
|
77
|
+
# Returns
|
|
78
|
+
[int, string, string]: `returnCode`, `stdOut`, `stdErr`
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
lock = threading.Lock()
|
|
82
|
+
|
|
83
|
+
with lock:
|
|
84
|
+
p = Popen(cmdArray, stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1)
|
|
85
|
+
try:
|
|
86
|
+
output, error = p.communicate(timeout=timeout)
|
|
87
|
+
return RunCmdResult(p.returncode, output, error)
|
|
88
|
+
except TimeoutExpired as e:
|
|
89
|
+
return RunCmdResult(127, 'TimeoutExpired', str(e))
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def logMethodCall(func):
|
|
93
|
+
def wrapper(self, *args, **kwargs):
|
|
94
|
+
logger.debug(f">>> BaseApp.{func.__name__}")
|
|
95
|
+
result = func(self, *args, **kwargs)
|
|
96
|
+
logger.debug(f"<<< BaseApp.{func.__name__}")
|
|
97
|
+
return result
|
|
98
|
+
return wrapper
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class BaseApp(PrintMixin, PromptMixin):
|
|
102
|
+
def __init__(self):
|
|
103
|
+
# Set up a log formatter
|
|
104
|
+
chFormatter = logging.Formatter('%(asctime)-25s' + ' %(levelname)-8s %(message)s')
|
|
105
|
+
|
|
106
|
+
# Set up a log handler (5mb rotating log file)
|
|
107
|
+
ch = logging.handlers.RotatingFileHandler(
|
|
108
|
+
"mas.log", maxBytes=(1048576 * 5), backupCount=2
|
|
109
|
+
)
|
|
110
|
+
ch.setLevel(logging.DEBUG)
|
|
111
|
+
ch.setFormatter(chFormatter)
|
|
112
|
+
|
|
113
|
+
# Configure the root logger
|
|
114
|
+
rootLogger = logging.getLogger()
|
|
115
|
+
rootLogger.addHandler(ch)
|
|
116
|
+
rootLogger.setLevel(logging.DEBUG)
|
|
117
|
+
logging.getLogger('asyncio').setLevel(logging.INFO)
|
|
118
|
+
|
|
119
|
+
# Supports extended semver, unlike mas.cli.__version__
|
|
120
|
+
self.version = "5.1.4"
|
|
121
|
+
self.h1count = 0
|
|
122
|
+
self.h2count = 0
|
|
123
|
+
|
|
124
|
+
self.localConfigDir = None
|
|
125
|
+
self.templatesDir = path.join(path.abspath(path.dirname(__file__)), "templates")
|
|
126
|
+
self.tektonDefsWithoutDigestPath = path.join(self.templatesDir, "ibm-mas-tekton.yaml")
|
|
127
|
+
self.tektonDefsWithDigestPath = path.join(self.templatesDir, "ibm-mas-tekton-with-digest.yaml")
|
|
128
|
+
|
|
129
|
+
# Default to using the tekton definitions without image digests
|
|
130
|
+
self.tektonDefsPath = self.tektonDefsWithoutDigestPath
|
|
131
|
+
|
|
132
|
+
# Initialize the dictionary that will hold the parameters we pass to a PipelineRun
|
|
133
|
+
self.params = dict()
|
|
134
|
+
|
|
135
|
+
# These dicts will hold the additional-configs, pod-templates, sls license file and manual certificates secrets
|
|
136
|
+
self.additionalConfigsSecret = None
|
|
137
|
+
self.podTemplatesSecret = None
|
|
138
|
+
self.slsLicenseFileSecret = None
|
|
139
|
+
self.certsSecret = None
|
|
140
|
+
|
|
141
|
+
self._isSNO = None
|
|
142
|
+
self._isAirgap = None
|
|
143
|
+
|
|
144
|
+
# Until we connect to the cluster we don't know what architecture it's worker nodes are
|
|
145
|
+
self.architecture = None
|
|
146
|
+
|
|
147
|
+
self.compatibilityMatrix = {
|
|
148
|
+
"9.1.x": {
|
|
149
|
+
"facilities": ["9.1.x"],
|
|
150
|
+
"assist": ["9.1.x", "9.0.x"],
|
|
151
|
+
"iot": ["9.1.x", "9.0.x"],
|
|
152
|
+
"manage": ["9.1.x", "9.0.x"],
|
|
153
|
+
"monitor": ["9.1.x", "9.0.x"],
|
|
154
|
+
"optimizer": ["9.1.x", "9.0.x"],
|
|
155
|
+
"predict": ["9.1.x", "9.0.x"],
|
|
156
|
+
"visualinspection": ["9.1.x", "9.0.x"],
|
|
157
|
+
"aibroker": ["9.1.x", "9.0.x"],
|
|
158
|
+
|
|
159
|
+
},
|
|
160
|
+
"9.1.x-feature": {
|
|
161
|
+
"assist": ["9.0.x"],
|
|
162
|
+
"iot": ["9.0.x"],
|
|
163
|
+
"manage": ["9.1.x-feature", "9.0.x"],
|
|
164
|
+
"monitor": ["9.0.x"],
|
|
165
|
+
"optimizer": ["9.1.x-feature", "9.0.x"],
|
|
166
|
+
"predict": ["9.0.x"],
|
|
167
|
+
"visualinspection": ["9.1.x-feature", "9.0.x"],
|
|
168
|
+
"aibroker": ["9.0.x"],
|
|
169
|
+
},
|
|
170
|
+
"9.0.x": {
|
|
171
|
+
"assist": ["9.0.x", "8.8.x"],
|
|
172
|
+
"iot": ["9.0.x", "8.8.x"],
|
|
173
|
+
"manage": ["9.0.x", "8.7.x"],
|
|
174
|
+
"monitor": ["9.0.x", "8.11.x"],
|
|
175
|
+
"optimizer": ["9.0.x", "8.5.x"],
|
|
176
|
+
"predict": ["9.0.x", "8.9.x"],
|
|
177
|
+
"visualinspection": ["9.0.x", "8.9.x"],
|
|
178
|
+
"aibroker": ["9.0.x"],
|
|
179
|
+
},
|
|
180
|
+
"8.11.x": {
|
|
181
|
+
"assist": ["8.8.x", "8.7.x"],
|
|
182
|
+
"iot": ["8.8.x", "8.7.x"],
|
|
183
|
+
"manage": ["8.7.x", "8.6.x"],
|
|
184
|
+
"monitor": ["8.11.x", "8.10.x"],
|
|
185
|
+
"optimizer": ["8.5.x", "8.4.x"],
|
|
186
|
+
"predict": ["8.9.x", "8.8.x"],
|
|
187
|
+
"visualinspection": ["8.9.x", "8.8.x"],
|
|
188
|
+
},
|
|
189
|
+
"8.10.x": {
|
|
190
|
+
"assist": ["8.7.x", "8.6.x"],
|
|
191
|
+
"hputilities": ["8.6.x", "8.5.x"],
|
|
192
|
+
"iot": ["8.7.x", "8.6.x"],
|
|
193
|
+
"manage": ["8.6.x", "8.5.x"],
|
|
194
|
+
"monitor": ["8.10.x", "8.9.x"],
|
|
195
|
+
"optimizer": ["8.4.x", "8.3.x"],
|
|
196
|
+
"predict": ["8.8.x", "8.7.x"],
|
|
197
|
+
"visualinspection": ["8.8.x", "8.7.x"],
|
|
198
|
+
},
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
self.licenses = {
|
|
202
|
+
"8.9.x": " - <u>https://ibm.biz/MAS89-License</u>",
|
|
203
|
+
"8.10.x": " - <u>https://ibm.biz/MAS810-License</u>",
|
|
204
|
+
"8.11.x": " - <u>https://ibm.biz/MAS811-License</u>\n - <u>https://ibm.biz/MAXIT81-License</u>",
|
|
205
|
+
"9.0.x": " - <u>https://ibm.biz/MAS90-License</u>\n - <u>https://ibm.biz/MaximoIT90-License</u>\n - <u>https://ibm.biz/MAXArcGIS90-License</u>",
|
|
206
|
+
"9.1.x-feature": " - <u>https://ibm.biz/MAS90-License</u>\n - <u>https://ibm.biz/MaximoIT90-License</u>\n - <u>https://ibm.biz/MAXArcGIS90-License</u>\n\nBe aware, this channel subscription is supported for non-production use only. \nIt allows early access to new features for evaluation in non-production environments. \nThis subscription is offered alongside and in parallel with our normal maintained streams. \nWhen using this subscription, IBM Support will only accept cases for the latest available bundle deployed in a non-production environment. \nSeverity must be either 3 or 4 and cases cannot be escalated. \nPlease refer to IBM documentation for more details.\n",
|
|
207
|
+
"9.1.x": " - <u>https://ibm.biz/MAS91-License</u>\n - <u>https://ibm.biz/MAXIT91-License</u>\n - <u>https://ibm.biz/MAXESRI91-License</u>",
|
|
208
|
+
"aibroker-9.1.x": " - <u>https://ibm.biz/MAS91-License</u>",
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
self.upgrade_path = {
|
|
212
|
+
"9.1.x": "9.1.x",
|
|
213
|
+
"9.1.x-feature": "9.1.x",
|
|
214
|
+
"9.0.x": "9.1.x",
|
|
215
|
+
"8.11.x": "9.0.x",
|
|
216
|
+
"8.10.x": "8.11.x",
|
|
217
|
+
"8.9.x": "8.10.x",
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
self.spinner = {
|
|
221
|
+
"interval": 80,
|
|
222
|
+
"frames": [" ⠋", " ⠙", " ⠹", " ⠸", " ⠼", " ⠴", " ⠦", " ⠧", " ⠇", " ⠏"]
|
|
223
|
+
}
|
|
224
|
+
self.successIcon = "✅️"
|
|
225
|
+
self.failureIcon = "❌"
|
|
226
|
+
|
|
227
|
+
self._dynClient = None
|
|
228
|
+
|
|
229
|
+
self.printTitle(f"\nIBM Maximo Application Suite Admin CLI v{self.version}")
|
|
230
|
+
print_formatted_text(HTML("Powered by <Orange><u>https://github.com/ibm-mas/ansible-devops/</u></Orange> and <Orange><u>https://tekton.dev/</u></Orange>\n"))
|
|
231
|
+
if which("kubectl") is None:
|
|
232
|
+
self.fatalError("Could not find kubectl on the path, see <Orange><u>https://kubernetes.io/docs/tasks/tools/#kubectl</u></Orange> for installation instructions")
|
|
233
|
+
|
|
234
|
+
@logMethodCall
|
|
235
|
+
def createTektonFileWithDigest(self) -> None:
|
|
236
|
+
if path.exists(self.tektonDefsWithDigestPath):
|
|
237
|
+
logger.debug(f"We have already generated {self.tektonDefsWithDigestPath}")
|
|
238
|
+
elif isAirgapInstall(self.dynamicClient):
|
|
239
|
+
# We need to modify the tekton definitions to
|
|
240
|
+
imageWithoutDigest = f"quay.io/ibmmas/cli:{self.version}"
|
|
241
|
+
self.printH1("Disconnected OpenShift Preparation")
|
|
242
|
+
self.printDescription([
|
|
243
|
+
f"Unless the {imageWithoutDigest} image is accessible from your cluster the MAS CLI container image must be present in your mirror registry"
|
|
244
|
+
])
|
|
245
|
+
cmdArray = ["skopeo", "inspect", f"docker://{imageWithoutDigest}"]
|
|
246
|
+
logger.info(f"Skopeo inspect command: {' '.join(cmdArray)}")
|
|
247
|
+
skopeoResult = runCmd(cmdArray)
|
|
248
|
+
if skopeoResult.successful():
|
|
249
|
+
skopeoData = json.loads(skopeoResult.out)
|
|
250
|
+
logger.info(f"Skopeo Data for {imageWithoutDigest}: {skopeoData}")
|
|
251
|
+
if "Digest" not in skopeoData:
|
|
252
|
+
self.fatalError("Recieved bad data inspecting CLI manifest to determine digest")
|
|
253
|
+
cliImageDigest = skopeoData["Digest"]
|
|
254
|
+
else:
|
|
255
|
+
warning = f"Unable to retrieve image digest for {imageWithoutDigest} ({skopeoResult.rc})"
|
|
256
|
+
self.printWarning(warning)
|
|
257
|
+
logger.warning(warning)
|
|
258
|
+
logger.warning(skopeoResult.err)
|
|
259
|
+
if self.noConfirm:
|
|
260
|
+
self.fatalError("Unable to automatically determine CLI image digest and --no-confirm flag has been set")
|
|
261
|
+
else:
|
|
262
|
+
cliImageDigest = self.promptForString(f"Enter {imageWithoutDigest} image digest")
|
|
263
|
+
|
|
264
|
+
# Overwrite the tekton definitions with one that uses the looked up image digest
|
|
265
|
+
imageWithDigest = f"quay.io/ibmmas/cli@{cliImageDigest}"
|
|
266
|
+
self.printHighlight(f"\nConverting Tekton definitions to use {imageWithDigest}")
|
|
267
|
+
with open(self.tektonDefsPath, 'r') as file:
|
|
268
|
+
tektonDefsWithoutDigest = file.read()
|
|
269
|
+
|
|
270
|
+
tektonDefsWithDigest = tektonDefsWithoutDigest.replace(imageWithoutDigest, imageWithDigest)
|
|
271
|
+
|
|
272
|
+
with open(self.tektonDefsWithDigestPath, 'w') as file:
|
|
273
|
+
file.write(tektonDefsWithDigest)
|
|
274
|
+
|
|
275
|
+
self.tektonDefsPath = self.tektonDefsWithDigestPath
|
|
276
|
+
|
|
277
|
+
@logMethodCall
|
|
278
|
+
def getCompatibleVersions(self, coreChannel: str, appId: str) -> list:
|
|
279
|
+
if coreChannel in self.compatibilityMatrix:
|
|
280
|
+
return self.compatibilityMatrix[coreChannel][appId]
|
|
281
|
+
else:
|
|
282
|
+
return []
|
|
283
|
+
|
|
284
|
+
@logMethodCall
|
|
285
|
+
def fatalError(self, message: str, exception: Exception = None) -> None:
|
|
286
|
+
if exception is not None:
|
|
287
|
+
logger.error(message)
|
|
288
|
+
logger.exception(exception, stack_info=True)
|
|
289
|
+
print_formatted_text(HTML(f"<Red>Fatal Exception: {message.replace(' & ', ' & ')}: {exception}</Red>\n"))
|
|
290
|
+
else:
|
|
291
|
+
logger.error(message)
|
|
292
|
+
print_formatted_text(HTML(f"<Red>Fatal Error: {message.replace(' & ', ' & ')}</Red>\n"))
|
|
293
|
+
exit(1)
|
|
294
|
+
|
|
295
|
+
@logMethodCall
|
|
296
|
+
def isSNO(self):
|
|
297
|
+
if self._isSNO is None:
|
|
298
|
+
self._isSNO = isSNO(self.dynamicClient)
|
|
299
|
+
return self._isSNO
|
|
300
|
+
|
|
301
|
+
@logMethodCall
|
|
302
|
+
def isAirgap(self):
|
|
303
|
+
if self._isAirgap is None:
|
|
304
|
+
# First check if the legacy ICSP is installed. If it is raise an error and instruct the user to re-run configure-airgap to
|
|
305
|
+
# migrate the cluster from ICSP to IDMS
|
|
306
|
+
if isAirgapInstall(self.dynamicClient, checkICSP=True):
|
|
307
|
+
self.fatalError("Deprecated Maximo Application Suite ImageContentSourcePolicy detected on the target cluster. Run 'mas configure-airgap' to migrate to the replacement ImageDigestMirrorSet beofre proceeding.")
|
|
308
|
+
self._isAirgap = isAirgapInstall(self.dynamicClient)
|
|
309
|
+
return self._isAirgap
|
|
310
|
+
|
|
311
|
+
def setParam(self, param: str, value: str):
|
|
312
|
+
self.params[param] = value
|
|
313
|
+
|
|
314
|
+
def getParam(self, param: str):
|
|
315
|
+
"""
|
|
316
|
+
Returns the value of a parameter, or an empty string is the parameter has not set at all or is set to None
|
|
317
|
+
"""
|
|
318
|
+
if param in self.params and self.params[param] is not None:
|
|
319
|
+
return self.params[param]
|
|
320
|
+
else:
|
|
321
|
+
return ""
|
|
322
|
+
|
|
323
|
+
@property
|
|
324
|
+
def dynamicClient(self):
|
|
325
|
+
if self._dynClient is not None:
|
|
326
|
+
return self._dynClient
|
|
327
|
+
else:
|
|
328
|
+
return self.reloadDynamicClient()
|
|
329
|
+
|
|
330
|
+
@logMethodCall
|
|
331
|
+
def reloadDynamicClient(self):
|
|
332
|
+
"""
|
|
333
|
+
Configure the Kubernetes API Client using the active context in kubeconfig
|
|
334
|
+
"""
|
|
335
|
+
logger.debug("Reloading Kubernetes Client Configuration")
|
|
336
|
+
try:
|
|
337
|
+
if "KUBERNETES_SERVICE_HOST" in environ:
|
|
338
|
+
config.load_incluster_config()
|
|
339
|
+
k8s_config = Configuration.get_default_copy()
|
|
340
|
+
self._apiClient = api_client.ApiClient(configuration=k8s_config)
|
|
341
|
+
self._dynClient = DynamicClient(self._apiClient)
|
|
342
|
+
else:
|
|
343
|
+
config.load_kube_config()
|
|
344
|
+
self._apiClient = api_client.ApiClient()
|
|
345
|
+
self._dynClient = DynamicClient(self._apiClient)
|
|
346
|
+
return self._dynClient
|
|
347
|
+
except Exception as e:
|
|
348
|
+
logger.warning(f"Error: Unable to connect to OpenShift Container Platform: {e}")
|
|
349
|
+
logger.exception(e, stack_info=True)
|
|
350
|
+
return None
|
|
351
|
+
|
|
352
|
+
@logMethodCall
|
|
353
|
+
def connect(self):
|
|
354
|
+
promptForNewServer = False
|
|
355
|
+
self.reloadDynamicClient()
|
|
356
|
+
if self._dynClient is not None:
|
|
357
|
+
try:
|
|
358
|
+
routesAPI = self._dynClient.resources.get(api_version="route.openshift.io/v1", kind="Route")
|
|
359
|
+
consoleRoute = routesAPI.get(name="console", namespace="openshift-console")
|
|
360
|
+
print_formatted_text(HTML(f"Already connected to OCP Cluster:\n <u><Orange>https://{consoleRoute.spec.host}</Orange></u>"))
|
|
361
|
+
print()
|
|
362
|
+
if not self.noConfirm:
|
|
363
|
+
# We are already connected to a cluster, but prompt the user if they want to use this connection
|
|
364
|
+
promptForNewServer = not self.yesOrNo("Proceed with this cluster")
|
|
365
|
+
except Exception as e:
|
|
366
|
+
# We are already connected to a cluster, but the connection is not valid so prompt for connection details
|
|
367
|
+
logger.debug("Failed looking up OpenShift Console route to verify connection")
|
|
368
|
+
logger.exception(e, stack_info=True)
|
|
369
|
+
promptForNewServer = True
|
|
370
|
+
else:
|
|
371
|
+
# We are not already connected to any cluster, so prompt for connection details
|
|
372
|
+
promptForNewServer = True
|
|
373
|
+
|
|
374
|
+
if promptForNewServer:
|
|
375
|
+
# Prompt for new connection properties
|
|
376
|
+
server = prompt(HTML('<Yellow>Server URL:</Yellow> '), placeholder="https://...")
|
|
377
|
+
token = prompt(HTML('<Yellow>Login Token:</Yellow> '), is_password=True, placeholder="sha256~...")
|
|
378
|
+
skipVerify = self.yesOrNo('Disable TLS Verify')
|
|
379
|
+
connect(server, token, skipVerify)
|
|
380
|
+
self.reloadDynamicClient()
|
|
381
|
+
if self._dynClient is None:
|
|
382
|
+
print_formatted_text(HTML("<Red>Unable to connect to cluster. See log file for details</Red>"))
|
|
383
|
+
exit(1)
|
|
384
|
+
|
|
385
|
+
# Now that we are connected, inspect the architecture of the OpenShift cluster
|
|
386
|
+
self.lookupTargetArchitecture()
|
|
387
|
+
|
|
388
|
+
@logMethodCall
|
|
389
|
+
def lookupTargetArchitecture(self, architecture: str = None) -> None:
|
|
390
|
+
logger.debug("Looking up worker node architecture")
|
|
391
|
+
if architecture is not None:
|
|
392
|
+
self.architecture = architecture
|
|
393
|
+
logger.debug(f"Target architecture (overridden): {self.architecture}")
|
|
394
|
+
else:
|
|
395
|
+
nodes = getNodes(self.dynamicClient)
|
|
396
|
+
self.architecture = nodes[0]["status"]["nodeInfo"]["architecture"]
|
|
397
|
+
logger.debug(f"Target architecture: {self.architecture}")
|
|
398
|
+
|
|
399
|
+
if self.architecture not in ["amd64", "s390x", "ppc64le"]:
|
|
400
|
+
self.fatalError(f"Unsupported worker node architecture: {self.architecture}")
|
|
401
|
+
|
|
402
|
+
@logMethodCall
|
|
403
|
+
def initializeApprovalConfigMap(self, namespace: str, id: str, enabled: bool, maxRetries: int = 100, delay: int = 300, ignoreFailure: bool = True) -> None:
|
|
404
|
+
"""
|
|
405
|
+
Set key = None if you don't want approval workflow enabled
|
|
406
|
+
"""
|
|
407
|
+
cmAPI = self.dynamicClient.resources.get(api_version="v1", kind="ConfigMap")
|
|
408
|
+
configMap = {
|
|
409
|
+
"apiVersion": "v1",
|
|
410
|
+
"kind": "ConfigMap",
|
|
411
|
+
"metadata": {
|
|
412
|
+
"name": f"approval-{id}",
|
|
413
|
+
"namespace": namespace
|
|
414
|
+
},
|
|
415
|
+
"data": {
|
|
416
|
+
"MAX_RETRIES": str(maxRetries),
|
|
417
|
+
"DELAY": str(delay),
|
|
418
|
+
"IGNORE_FAILURE": str(ignoreFailure),
|
|
419
|
+
"STATUS": ""
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
# Delete any existing configmap and create a new one
|
|
424
|
+
try:
|
|
425
|
+
logger.debug(f"Deleting any existing approval workflow configmap for {id}")
|
|
426
|
+
cmAPI.delete(name=f"approval-{id}", namespace=namespace)
|
|
427
|
+
except NotFoundError:
|
|
428
|
+
pass
|
|
429
|
+
|
|
430
|
+
if enabled:
|
|
431
|
+
logger.debug(f"Enabling approval workflow for {id} with {maxRetries} max retries on a {delay}s delay ({'ignoring failures' if ignoreFailure else 'abort on failure'})")
|
|
432
|
+
cmAPI.create(body=configMap, namespace=namespace)
|