mas-cli 11.6.0__py3-none-any.whl → 11.7.0__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 +1 -1
- mas/cli/cli.py +41 -4
- mas/cli/gencfg.py +32 -8
- mas/cli/install/app.py +204 -383
- mas/cli/install/argBuilder.py +2 -0
- mas/cli/install/argParser.py +19 -0
- mas/cli/install/catalogs.py +141 -0
- mas/cli/install/params.py +168 -0
- mas/cli/install/settings/__init__.py +2 -1
- mas/cli/install/settings/additionalConfigs.py +1 -1
- mas/cli/install/settings/db2Settings.py +75 -45
- mas/cli/install/settings/kafkaSettings.py +14 -10
- mas/cli/install/settings/manageSettings.py +47 -41
- mas/cli/install/settings/mongodbSettings.py +42 -0
- mas/cli/install/settings/turbonomicSettings.py +14 -13
- mas/cli/install/summarizer.py +9 -9
- mas/cli/templates/ibm-mas-tekton.yaml +174 -105
- mas/cli/templates/suite_mongocfg.yml.j2 +55 -0
- mas/cli/update/app.py +20 -15
- {mas_cli-11.6.0.dist-info → mas_cli-11.7.0.dist-info}/METADATA +2 -2
- {mas_cli-11.6.0.dist-info → mas_cli-11.7.0.dist-info}/RECORD +24 -20
- {mas_cli-11.6.0.dist-info → mas_cli-11.7.0.dist-info}/WHEEL +1 -1
- {mas_cli-11.6.0.data → mas_cli-11.7.0.data}/scripts/mas-cli +0 -0
- {mas_cli-11.6.0.dist-info → mas_cli-11.7.0.dist-info}/top_level.txt +0 -0
mas/cli/__init__.py
CHANGED
mas/cli/cli.py
CHANGED
|
@@ -28,7 +28,7 @@ from openshift.dynamic.exceptions import NotFoundError
|
|
|
28
28
|
from prompt_toolkit import prompt, print_formatted_text, HTML
|
|
29
29
|
|
|
30
30
|
from mas.devops.mas import isAirgapInstall
|
|
31
|
-
from mas.devops.ocp import connect, isSNO
|
|
31
|
+
from mas.devops.ocp import connect, isSNO, getNodes
|
|
32
32
|
|
|
33
33
|
from .displayMixins import PrintMixin, PromptMixin
|
|
34
34
|
|
|
@@ -50,7 +50,7 @@ def getHelpFormatter(formatter=RawTextHelpFormatter, w=160, h=50):
|
|
|
50
50
|
formatter(None, **kwargs)
|
|
51
51
|
return lambda prog: formatter(prog, **kwargs)
|
|
52
52
|
except TypeError:
|
|
53
|
-
logger.
|
|
53
|
+
logger.warning("argparse help formatter failed, falling back.")
|
|
54
54
|
return formatter
|
|
55
55
|
|
|
56
56
|
|
|
@@ -89,6 +89,15 @@ def runCmd(cmdArray, timeout=630):
|
|
|
89
89
|
return RunCmdResult(127, 'TimeoutExpired', str(e))
|
|
90
90
|
|
|
91
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
|
+
|
|
92
101
|
class BaseApp(PrintMixin, PromptMixin):
|
|
93
102
|
def __init__(self):
|
|
94
103
|
# Set up a log formatter
|
|
@@ -105,9 +114,10 @@ class BaseApp(PrintMixin, PromptMixin):
|
|
|
105
114
|
rootLogger = logging.getLogger()
|
|
106
115
|
rootLogger.addHandler(ch)
|
|
107
116
|
rootLogger.setLevel(logging.DEBUG)
|
|
117
|
+
logging.getLogger('asyncio').setLevel(logging.INFO)
|
|
108
118
|
|
|
109
119
|
# Supports extended semver, unlike mas.cli.__version__
|
|
110
|
-
self.version = "11.
|
|
120
|
+
self.version = "11.7.0"
|
|
111
121
|
self.h1count = 0
|
|
112
122
|
self.h2count = 0
|
|
113
123
|
|
|
@@ -129,6 +139,9 @@ class BaseApp(PrintMixin, PromptMixin):
|
|
|
129
139
|
|
|
130
140
|
self._isSNO = None
|
|
131
141
|
|
|
142
|
+
# Until we connect to the cluster we don't know what architecture it's worker nodes are
|
|
143
|
+
self.architecture = None
|
|
144
|
+
|
|
132
145
|
self.compatibilityMatrix = {
|
|
133
146
|
"9.0.x": {
|
|
134
147
|
"assist": ["9.0.x", "8.8.x"],
|
|
@@ -175,6 +188,7 @@ class BaseApp(PrintMixin, PromptMixin):
|
|
|
175
188
|
if which("kubectl") is None:
|
|
176
189
|
self.fatalError("Could not find kubectl on the path, see <DarkGoldenRod><u>https://kubernetes.io/docs/tasks/tools/#kubectl</u></DarkGoldenRod> for installation instructions")
|
|
177
190
|
|
|
191
|
+
@logMethodCall
|
|
178
192
|
def createTektonFileWithDigest(self) -> None:
|
|
179
193
|
if path.exists(self.tektonDefsWithDigestPath):
|
|
180
194
|
logger.debug(f"We have already generated {self.tektonDefsWithDigestPath}")
|
|
@@ -217,12 +231,14 @@ class BaseApp(PrintMixin, PromptMixin):
|
|
|
217
231
|
|
|
218
232
|
self.tektonDefsPath = self.tektonDefsWithDigestPath
|
|
219
233
|
|
|
234
|
+
@logMethodCall
|
|
220
235
|
def getCompatibleVersions(self, coreChannel: str, appId: str) -> list:
|
|
221
236
|
if coreChannel in self.compatibilityMatrix:
|
|
222
237
|
return self.compatibilityMatrix[coreChannel][appId]
|
|
223
238
|
else:
|
|
224
239
|
return []
|
|
225
240
|
|
|
241
|
+
@logMethodCall
|
|
226
242
|
def fatalError(self, message: str, exception: Exception = None) -> None:
|
|
227
243
|
if exception is not None:
|
|
228
244
|
logger.error(message)
|
|
@@ -233,6 +249,7 @@ class BaseApp(PrintMixin, PromptMixin):
|
|
|
233
249
|
print_formatted_text(HTML(f"<Red>Fatal Error: {message.replace(' & ', ' & ')}</Red>\n"))
|
|
234
250
|
exit(1)
|
|
235
251
|
|
|
252
|
+
@logMethodCall
|
|
236
253
|
def isSNO(self):
|
|
237
254
|
if self._isSNO is None:
|
|
238
255
|
self._isSNO = isSNO(self.dynamicClient)
|
|
@@ -257,6 +274,7 @@ class BaseApp(PrintMixin, PromptMixin):
|
|
|
257
274
|
else:
|
|
258
275
|
return self.reloadDynamicClient()
|
|
259
276
|
|
|
277
|
+
@logMethodCall
|
|
260
278
|
def reloadDynamicClient(self):
|
|
261
279
|
"""
|
|
262
280
|
Configure the Kubernetes API Client using the active context in kubeconfig
|
|
@@ -278,6 +296,7 @@ class BaseApp(PrintMixin, PromptMixin):
|
|
|
278
296
|
logger.exception(e, stack_info=True)
|
|
279
297
|
return None
|
|
280
298
|
|
|
299
|
+
@logMethodCall
|
|
281
300
|
def connect(self):
|
|
282
301
|
promptForNewServer = False
|
|
283
302
|
self.reloadDynamicClient()
|
|
@@ -289,7 +308,7 @@ class BaseApp(PrintMixin, PromptMixin):
|
|
|
289
308
|
print()
|
|
290
309
|
if not self.noConfirm:
|
|
291
310
|
# We are already connected to a cluster, but prompt the user if they want to use this connection
|
|
292
|
-
promptForNewServer = not self.yesOrNo("Proceed with this cluster
|
|
311
|
+
promptForNewServer = not self.yesOrNo("Proceed with this cluster")
|
|
293
312
|
except Exception as e:
|
|
294
313
|
# We are already connected to a cluster, but the connection is not valid so prompt for connection details
|
|
295
314
|
logger.debug("Failed looking up OpenShift Console route to verify connection")
|
|
@@ -310,6 +329,24 @@ class BaseApp(PrintMixin, PromptMixin):
|
|
|
310
329
|
print_formatted_text(HTML("<Red>Unable to connect to cluster. See log file for details</Red>"))
|
|
311
330
|
exit(1)
|
|
312
331
|
|
|
332
|
+
# Now that we are connected, inspect the architecture of the OpenShift cluster
|
|
333
|
+
self.lookupTargetArchitecture()
|
|
334
|
+
|
|
335
|
+
@logMethodCall
|
|
336
|
+
def lookupTargetArchitecture(self, architecture: str = None) -> None:
|
|
337
|
+
logger.debug("Looking up worker node architecture")
|
|
338
|
+
if architecture is not None:
|
|
339
|
+
self.architecture = architecture
|
|
340
|
+
logger.debug(f"Target architecture (overridden): {self.architecture}")
|
|
341
|
+
else:
|
|
342
|
+
nodes = getNodes(self.dynamicClient)
|
|
343
|
+
self.architecture = nodes[0]["status"]["nodeInfo"]["architecture"]
|
|
344
|
+
logger.debug(f"Target architecture: {self.architecture}")
|
|
345
|
+
|
|
346
|
+
if self.architecture not in ["amd64", "s390x"]:
|
|
347
|
+
self.fatalError(f"Unsupported worker node architecture: {self.architecture}")
|
|
348
|
+
|
|
349
|
+
@logMethodCall
|
|
313
350
|
def initializeApprovalConfigMap(self, namespace: str, id: str, key: str = None, maxRetries: int = 100, delay: int = 300, ignoreFailure: bool = True) -> None:
|
|
314
351
|
"""
|
|
315
352
|
Set key = None if you don't want approval workflow enabled
|
mas/cli/gencfg.py
CHANGED
|
@@ -10,17 +10,11 @@
|
|
|
10
10
|
|
|
11
11
|
from os import path
|
|
12
12
|
from jinja2 import Template
|
|
13
|
+
from base64 import b64encode
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class ConfigGeneratorMixin():
|
|
16
|
-
def generateJDBCCfg(
|
|
17
|
-
self,
|
|
18
|
-
instanceId: str,
|
|
19
|
-
scope: str,
|
|
20
|
-
destination: str,
|
|
21
|
-
appId: str = "",
|
|
22
|
-
workspaceId: str = "") -> None:
|
|
23
|
-
|
|
17
|
+
def generateJDBCCfg(self, instanceId: str, scope: str, destination: str, appId: str = "", workspaceId: str = "") -> None:
|
|
24
18
|
templateFile = path.join(self.templatesDir, "jdbccfg.yml.j2")
|
|
25
19
|
with open(templateFile) as tFile:
|
|
26
20
|
template = Template(tFile.read())
|
|
@@ -64,3 +58,33 @@ class ConfigGeneratorMixin():
|
|
|
64
58
|
with open(destination, 'w') as f:
|
|
65
59
|
f.write(cfg)
|
|
66
60
|
f.write('\n')
|
|
61
|
+
|
|
62
|
+
def generateMongoCfg(self, instanceId: str, destination: str) -> None:
|
|
63
|
+
templateFile = path.join(self.templatesDir, "suite_mongocfg.yml.j2")
|
|
64
|
+
|
|
65
|
+
with open(templateFile) as tFile:
|
|
66
|
+
template = Template(tFile.read())
|
|
67
|
+
|
|
68
|
+
name = self.promptForString("Configuration Display Name")
|
|
69
|
+
hosts = self.promptForString("MongoDb Hosts (comma-separated list)")
|
|
70
|
+
|
|
71
|
+
username = self.promptForString("MongoDb Username")
|
|
72
|
+
password = self.promptForString("MongoDb Password", isPassword=True)
|
|
73
|
+
encoded_username = b64encode(username.encode('ascii')).decode("ascii")
|
|
74
|
+
encoded_password = b64encode(password.encode('ascii')).decode("ascii")
|
|
75
|
+
sslCertFile = self.promptForFile("Path to certificate file")
|
|
76
|
+
with open(sslCertFile) as cFile:
|
|
77
|
+
certLocalFileContent = cFile.read()
|
|
78
|
+
|
|
79
|
+
cfg = template.render(
|
|
80
|
+
mas_instance_id=instanceId,
|
|
81
|
+
cfg_display_name=name,
|
|
82
|
+
mongodb_hosts=hosts,
|
|
83
|
+
mongodb_admin_username=encoded_username,
|
|
84
|
+
mongodb_admin_password=encoded_password,
|
|
85
|
+
mongodb_ca_pem_local_file=certLocalFileContent
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
with open(destination, 'w') as f:
|
|
89
|
+
f.write(cfg)
|
|
90
|
+
f.write('\n')
|