avmesos-cli 1.0.0__tar.gz

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.
Files changed (38) hide show
  1. avmesos-cli-1.0.0/.gitignore +8 -0
  2. avmesos-cli-1.0.0/Makefile +37 -0
  3. avmesos-cli-1.0.0/PKG-INFO +84 -0
  4. avmesos-cli-1.0.0/README.md +73 -0
  5. avmesos-cli-1.0.0/avmesos/__init__.py +26 -0
  6. avmesos-cli-1.0.0/avmesos/cli/__init__.py +26 -0
  7. avmesos-cli-1.0.0/avmesos/cli/config.py +239 -0
  8. avmesos-cli-1.0.0/avmesos/cli/constants.py +25 -0
  9. avmesos-cli-1.0.0/avmesos/cli/docopt.py +89 -0
  10. avmesos-cli-1.0.0/avmesos/cli/exceptions.py +24 -0
  11. avmesos-cli-1.0.0/avmesos/cli/http.py +83 -0
  12. avmesos-cli-1.0.0/avmesos/cli/mesos-cli.py +155 -0
  13. avmesos-cli-1.0.0/avmesos/cli/mesos.py +891 -0
  14. avmesos-cli-1.0.0/avmesos/cli/plugins/__init__.py +22 -0
  15. avmesos-cli-1.0.0/avmesos/cli/plugins/agent/__init__.py +22 -0
  16. avmesos-cli-1.0.0/avmesos/cli/plugins/agent/main.py +82 -0
  17. avmesos-cli-1.0.0/avmesos/cli/plugins/base.py +179 -0
  18. avmesos-cli-1.0.0/avmesos/cli/plugins/config/__init__.py +22 -0
  19. avmesos-cli-1.0.0/avmesos/cli/plugins/config/main.py +97 -0
  20. avmesos-cli-1.0.0/avmesos/cli/plugins/framework/__init__.py +23 -0
  21. avmesos-cli-1.0.0/avmesos/cli/plugins/framework/main.py +110 -0
  22. avmesos-cli-1.0.0/avmesos/cli/plugins/task/__init__.py +22 -0
  23. avmesos-cli-1.0.0/avmesos/cli/plugins/task/main.py +167 -0
  24. avmesos-cli-1.0.0/avmesos/cli/settings.py +50 -0
  25. avmesos-cli-1.0.0/avmesos/cli/util.py +392 -0
  26. avmesos-cli-1.0.0/avmesos/exceptions.py +110 -0
  27. avmesos-cli-1.0.0/avmesos/http.py +397 -0
  28. avmesos-cli-1.0.0/avmesos/recordio.py +175 -0
  29. avmesos-cli-1.0.0/avmesos_cli.egg-info/PKG-INFO +84 -0
  30. avmesos-cli-1.0.0/avmesos_cli.egg-info/SOURCES.txt +36 -0
  31. avmesos-cli-1.0.0/avmesos_cli.egg-info/dependency_links.txt +1 -0
  32. avmesos-cli-1.0.0/avmesos_cli.egg-info/requires.txt +15 -0
  33. avmesos-cli-1.0.0/avmesos_cli.egg-info/top_level.txt +1 -0
  34. avmesos-cli-1.0.0/bin/mesos-cli +4 -0
  35. avmesos-cli-1.0.0/pip-requirements.txt +16 -0
  36. avmesos-cli-1.0.0/setup.cfg +4 -0
  37. avmesos-cli-1.0.0/setup.py +25 -0
  38. avmesos-cli-1.0.0/shell.nix +25 -0
@@ -0,0 +1,8 @@
1
+ *.pyc
2
+ .virtualenv
3
+ .coverage
4
+ .tox
5
+ build/
6
+ dist/
7
+ avmesos_cli.egg-info/
8
+ avmesos.egg-info
@@ -0,0 +1,37 @@
1
+ #Dockerfile vars
2
+
3
+ #vars
4
+
5
+ .PHONY: help build bootstrap all
6
+
7
+ help:
8
+ @echo "Makefile arguments:"
9
+ @echo ""
10
+ @echo "Makefile commands:"
11
+ @echo "build"
12
+ @echo "all"
13
+ @echo "publish"
14
+ @echo ${TAG}
15
+
16
+ .DEFAULT_GOAL := all
17
+
18
+ build:
19
+ @echo ">>>> Build python module"
20
+ @python3 setup.py sdist bdist_wheel
21
+
22
+ upload:
23
+ @python3 -m twine upload --repository pypi dist/*
24
+
25
+ install:
26
+ @echo ">>>> Install python module"
27
+ @pip3 install .
28
+
29
+ install-dev:
30
+ @echo ">>>> Install python module development"
31
+ @pip3 install -e .
32
+
33
+ docs:
34
+ @echo ">>>> Build docs"
35
+ $(MAKE) -C $@
36
+
37
+ all: build
@@ -0,0 +1,84 @@
1
+ Metadata-Version: 2.1
2
+ Name: avmesos-cli
3
+ Version: 1.0.0
4
+ Summary: Apache Mesos CLI
5
+ Home-page: https://www.aventer.biz/
6
+ Author: AVENTER UG (haftungsbeschraenkt)
7
+ Author-email: support@aventer.biz
8
+ License: Apache License 2.0
9
+ Requires-Python: >=3.6
10
+ Description-Content-Type: text/markdown
11
+
12
+ # Mesos CLI
13
+
14
+ ## Prerequisites
15
+
16
+ Make sure you have python 3.6 or newer installed
17
+ on your system before you begin.
18
+
19
+ ## How to install
20
+
21
+ ```bash
22
+
23
+ pip install avmesos-cli
24
+
25
+ ```
26
+
27
+ ## Setting up your configuration
28
+
29
+ In order to use this tool, you will need to create a
30
+ configuration file in your home directory under
31
+ `~/.mesos/config.toml`. A template for this config can be
32
+ seen below:
33
+
34
+ ```
35
+ # The `plugins` is an array listing the absolute paths of the
36
+ # plugins you want to add to the CLI.
37
+ plugins = [
38
+ "</absolute/path/to/plugin-1/directory>",
39
+ "</absolute/path/to/plugin-2/directory>"
40
+ ]
41
+
42
+ # The `master` is a field that has to be composed of an
43
+ # `address` or `zookeeper` field, but not both. For example:
44
+ [master]
45
+ address = "10.10.0.30:5050"
46
+ principal = "username"
47
+ secret = "password"
48
+
49
+ [agent]
50
+ ssl = true
51
+ ssl_verify = false
52
+ principal = "username"
53
+ secret = "password"
54
+ timeout = 5
55
+ ```
56
+
57
+ You can override the location of this configuration file using
58
+ the environment variable `MESOS_CLI_CONFIG`.
59
+
60
+ ## How to use it
61
+
62
+ ```bash
63
+ $ mesos-cli
64
+
65
+ Mesos CLI
66
+
67
+ Usage:
68
+ mesos (-h | --help)
69
+ mesos --version
70
+ mesos <command> [<args>...]
71
+
72
+ Options:
73
+ -h --help Show this screen.
74
+ --version Show version info.
75
+
76
+ Commands:
77
+ agent Interacts with the Mesos agents
78
+ config Interacts with the Mesos CLI configuration file
79
+ framework Interacts with the Mesos Frameworks
80
+ task Interacts with the tasks running in a Mesos cluster
81
+
82
+ See 'mesos help <command>' for more information on a specific command.
83
+
84
+ ```
@@ -0,0 +1,73 @@
1
+ # Mesos CLI
2
+
3
+ ## Prerequisites
4
+
5
+ Make sure you have python 3.6 or newer installed
6
+ on your system before you begin.
7
+
8
+ ## How to install
9
+
10
+ ```bash
11
+
12
+ pip install avmesos-cli
13
+
14
+ ```
15
+
16
+ ## Setting up your configuration
17
+
18
+ In order to use this tool, you will need to create a
19
+ configuration file in your home directory under
20
+ `~/.mesos/config.toml`. A template for this config can be
21
+ seen below:
22
+
23
+ ```
24
+ # The `plugins` is an array listing the absolute paths of the
25
+ # plugins you want to add to the CLI.
26
+ plugins = [
27
+ "</absolute/path/to/plugin-1/directory>",
28
+ "</absolute/path/to/plugin-2/directory>"
29
+ ]
30
+
31
+ # The `master` is a field that has to be composed of an
32
+ # `address` or `zookeeper` field, but not both. For example:
33
+ [master]
34
+ address = "10.10.0.30:5050"
35
+ principal = "username"
36
+ secret = "password"
37
+
38
+ [agent]
39
+ ssl = true
40
+ ssl_verify = false
41
+ principal = "username"
42
+ secret = "password"
43
+ timeout = 5
44
+ ```
45
+
46
+ You can override the location of this configuration file using
47
+ the environment variable `MESOS_CLI_CONFIG`.
48
+
49
+ ## How to use it
50
+
51
+ ```bash
52
+ $ mesos-cli
53
+
54
+ Mesos CLI
55
+
56
+ Usage:
57
+ mesos (-h | --help)
58
+ mesos --version
59
+ mesos <command> [<args>...]
60
+
61
+ Options:
62
+ -h --help Show this screen.
63
+ --version Show version info.
64
+
65
+ Commands:
66
+ agent Interacts with the Mesos agents
67
+ config Interacts with the Mesos CLI configuration file
68
+ framework Interacts with the Mesos Frameworks
69
+ task Interacts with the tasks running in a Mesos cluster
70
+
71
+ See 'mesos help <command>' for more information on a specific command.
72
+
73
+ ```
@@ -0,0 +1,26 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ """
18
+ Client library for the Mesos HTTP ReST API
19
+ """
20
+
21
+ __version__ = '0.0.0.dev'
22
+
23
+ __all__ = ['exceptions', 'http', 'recordio']
24
+ from . import exceptions
25
+ from . import http
26
+ from . import recordio
@@ -0,0 +1,26 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ """
18
+ Mesos CLI Module
19
+ """
20
+
21
+ # pylint: disable=cyclic-import
22
+ from . import config
23
+ from . import plugins
24
+
25
+ from . import exceptions
26
+ from . import util
@@ -0,0 +1,239 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ """
18
+ Config class to manage the configuration file.
19
+ """
20
+
21
+ import os
22
+ import toml
23
+
24
+ import requests
25
+
26
+ from avmesos import cli
27
+ from avmesos.cli.constants import DEFAULT_MASTER_IP
28
+ from avmesos.cli.constants import DEFAULT_MASTER_PORT
29
+ from avmesos.cli.exceptions import CLIException
30
+
31
+
32
+ class Config():
33
+ """
34
+ The Config class loads the configuration file on initialization and has
35
+ one method for each element that can be specified in the config file.
36
+ """
37
+
38
+ def __init__(self, settings):
39
+ # Load the configuration file path for the CLI.
40
+ if os.environ.get("MESOS_CLI_CONFIG"):
41
+ self.path = os.environ["MESOS_CLI_CONFIG"]
42
+ else:
43
+ self.path = settings.DEFAULT_MESOS_CLI_CONFIG
44
+
45
+ # Load the configuration file as a TOML file.
46
+ try:
47
+ self.data = toml.load(self.path)
48
+ except Exception as exception:
49
+ raise CLIException("Error loading config file as TOML: {error}"
50
+ .format(error=exception))
51
+
52
+ def master(self):
53
+ """
54
+ Parse the master info in the configuration file and return
55
+ its IP address and the port where Mesos is running.
56
+ """
57
+ master = "{ip}:{port}".format(ip=DEFAULT_MASTER_IP,
58
+ port=DEFAULT_MASTER_PORT)
59
+
60
+ if "master" in self.data:
61
+ if not isinstance(self.data["master"], dict):
62
+ raise CLIException("The 'master' field must be a dictionary")
63
+
64
+ if ("address" not in self.data["master"] and
65
+ "zookeeper" not in self.data["master"]):
66
+ raise CLIException("The 'master' field must either"
67
+ " contain an 'address' field or"
68
+ " a 'zookeeper' dictionary")
69
+ if ("address" in self.data["master"] and
70
+ "zookeeper" in self.data["master"]):
71
+ raise CLIException("The 'master' field should only contain "
72
+ " an 'address' field or a 'zookeeper'"
73
+ " dictionary but not both")
74
+
75
+ if "address" in self.data["master"]:
76
+ master_address = self.data["master"]["address"]
77
+ try:
78
+ master = cli.util.sanitize_address(master_address)
79
+ except Exception as exception:
80
+ raise CLIException("The 'master' address {address} is"
81
+ " formatted incorrectly: {error}"
82
+ .format(address=master_address,
83
+ error=exception))
84
+
85
+ if "zookeeper" in self.data["master"]:
86
+ zk_field = self.data["master"]["zookeeper"]
87
+
88
+ if ("addresses" not in zk_field or
89
+ not isinstance(zk_field["addresses"], list)):
90
+ raise CLIException("The 'zookeeper' field must contain"
91
+ " an 'addresses' list")
92
+
93
+ if ("path" not in zk_field or
94
+ not isinstance(zk_field["path"], str)):
95
+ raise CLIException("The 'zookeeper' field must contain"
96
+ " a 'path' field")
97
+
98
+ if not zk_field["path"].startswith("/"):
99
+ raise CLIException("The 'zookeeper' field 'path'"
100
+ " must start with a '/'")
101
+ if len(zk_field["path"]) == 1:
102
+ raise CLIException("The 'zookeeper' field 'path' should"
103
+ " be nested ('/' is not supported)")
104
+
105
+ for address in zk_field["addresses"]:
106
+ try:
107
+ cli.util.sanitize_address(address)
108
+ except Exception as exception:
109
+ raise CLIException("The 'zookeeper' address {address}"
110
+ " is formatted incorrectly: {error}"
111
+ .format(address=address,
112
+ error=exception))
113
+ try:
114
+ master = cli.util.zookeeper_resolve_leader(
115
+ zk_field["addresses"], zk_field["path"])
116
+ except Exception as exception:
117
+ raise CLIException("Could not resolve the"
118
+ " leading master: {error}"
119
+ .format(error=exception))
120
+
121
+ return master
122
+
123
+ def principal(self):
124
+ """
125
+ Return the principal in the configuration file
126
+ """
127
+ return self.data["master"].get("principal")
128
+
129
+ def secret(self):
130
+ """
131
+ Return the secret in the configuration file
132
+ """
133
+ return self.data["master"].get("secret")
134
+
135
+ def agent_ssl(self, default=False):
136
+ """
137
+ Return if the agent support ssl
138
+ """
139
+ if "agent" in self.data:
140
+ agent_ssl = self.data["agent"].get("ssl", default)
141
+ if not isinstance(agent_ssl, bool):
142
+ raise CLIException("The 'agent->ssl' field"
143
+ " must be True/False")
144
+
145
+ return agent_ssl
146
+
147
+ return default
148
+
149
+ def agent_ssl_verify(self, default=False):
150
+ """
151
+ Return if the ssl certificate should be verified
152
+ """
153
+ if "agent" in self.data:
154
+ ssl_verify = self.data["agent"].get("ssl_verify", default)
155
+ if not isinstance(ssl_verify, bool):
156
+ raise CLIException("The 'agent->ssl_verify' field"
157
+ " must be True/False")
158
+
159
+ return ssl_verify
160
+
161
+ return default
162
+
163
+ def ssl_verify(self, default=False):
164
+ """
165
+ Return if the ssl certificate should be verified
166
+ """
167
+ if "master" in self.data:
168
+ ssl_verify = self.data["master"].get("ssl_verify", default)
169
+ if not isinstance(ssl_verify, bool):
170
+ raise CLIException("The 'master->ssl_verify' field"
171
+ " must be True/False")
172
+
173
+ return ssl_verify
174
+
175
+ return default
176
+
177
+ def agent_timeout(self, default=5):
178
+ """
179
+ Return the connection timeout of the agent
180
+ """
181
+ if "agent" in self.data:
182
+ timeout = self.data["agent"].get("timeout", default)
183
+ if not isinstance(timeout, int):
184
+ raise CLIException("The 'agent->timeout' field"
185
+ " must be a number in seconds")
186
+
187
+ return timeout
188
+
189
+ return default
190
+
191
+
192
+ def agent_principal(self):
193
+ """
194
+ Return the principal in the configuration file
195
+ """
196
+ if "agent" in self.data:
197
+ return self.data["agent"].get("principal")
198
+
199
+ return None
200
+
201
+ def agent_secret(self):
202
+ """
203
+ Return the secret in the configuration file
204
+ """
205
+ if "agent" in self.data:
206
+ return self.data["agent"].get("secret")
207
+
208
+ return None
209
+
210
+ def plugins(self):
211
+ """
212
+ Parse the plugins listed in the configuration file and return them.
213
+ """
214
+ # Allow extra plugins to be pulled in from the configuration file.
215
+ if "plugins" in self.data:
216
+ if not isinstance(self.data["plugins"], list):
217
+ raise CLIException("Unable to parse config file '{path}':"
218
+ " 'plugins' field must be a list"
219
+ .format(path=self.path))
220
+
221
+ for plugin in self.data["plugins"]:
222
+ if not os.path.exists(plugin):
223
+ raise CLIException("Plugin path not found: {path}"
224
+ .format(path=plugin))
225
+ return self.data["plugins"]
226
+
227
+ return []
228
+
229
+ def authentication_header(self):
230
+ """
231
+ Return the BasicAuth authentication header
232
+ """
233
+ if (self.agent_principal() is not None
234
+ and self.agent_secret() is not None):
235
+ return requests.auth.HTTPBasicAuth(
236
+ self.agent_principal(),
237
+ self.agent_secret()
238
+ )
239
+ return None
@@ -0,0 +1,25 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ """
18
+ A collection of constants useful for the CLI.
19
+ """
20
+
21
+ DEFAULT_MASTER_IP = "127.0.0.1"
22
+ DEFAULT_MASTER_PORT = "5050"
23
+
24
+ DEFAULT_AGENT_IP = "127.0.0.1"
25
+ DEFAULT_AGENT_PORT = "5051"
@@ -0,0 +1,89 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ """
18
+ Unfortunately, docopt doesn't support multi-word commands. This is
19
+ important for supporting things like:
20
+
21
+ mesos cluster ps <args>...
22
+
23
+ However, it looks like some plans are in place for supporting it in the
24
+ future: https://github.com/docopt/docopt/issues/41
25
+
26
+ The proposal is to add a "program" keyword argument to docopt to specify the
27
+ full set of words used to represent the command. Since this is not yet
28
+ supported officially, we include the hack below to make it work for us. We
29
+ essentially intercept the call to docopt and make it work with such a "program"
30
+ argument.
31
+
32
+ To make it work, we inspect the value of "program" and search and replace all
33
+ instances of it in the usage string with a transformed version of it to make it
34
+ a single word (i.e. we replace all spaces with dashes: echo $program | s/
35
+ /-/g). This essentially turns all multi-word commands in the usage string into
36
+ dash-separated single words (e.g., s/mesos cluster ps/mesos-cluster-ps/g). With
37
+ this in place, we then pass this usage string to the original docopt for
38
+ parsing.
39
+
40
+ Unfortunately, doing things this way means that docopt (by default) will print
41
+ the usage string containing the dashes. To avoid this, we intercept all paths
42
+ where docopt does the printing itself, and transform the usage string back to
43
+ its original form.
44
+
45
+ Hopefully we can remove this brutal hack at some point in the future
46
+ once docopt supports the "program" argument natively.
47
+ """
48
+
49
+ import os
50
+ import sys
51
+
52
+ # pylint: disable=import-error
53
+ from docopt import docopt as real_docopt, DocoptExit
54
+
55
+
56
+ def docopt(usage, **keywords):
57
+ """ A wrapper around the real docopt parser. """
58
+ new_usage = usage
59
+
60
+ if "program" in keywords:
61
+ program = keywords.pop("program")
62
+ new_usage = usage.replace(program, program.replace(" ", "-"))
63
+
64
+ try:
65
+ stdout = sys.stdout
66
+
67
+ with open(os.devnull, 'w') as nullfile:
68
+ sys.stdout = nullfile
69
+ arguments = real_docopt(new_usage, **keywords)
70
+ sys.stdout = stdout
71
+
72
+ return arguments
73
+
74
+ except DocoptExit:
75
+ sys.stdout = stdout
76
+ print(usage.strip(), file=sys.stderr)
77
+ sys.exit(1)
78
+
79
+ except SystemExit:
80
+ sys.stdout = stdout
81
+
82
+ if "argv" in keywords and any(h in ("-h", "--help")
83
+ for h in keywords["argv"]):
84
+ print(usage.strip())
85
+ elif "version" in keywords and any(v in ("--version")
86
+ for v in keywords["argv"]):
87
+ print(keywords["version"].strip())
88
+
89
+ sys.exit()
@@ -0,0 +1,24 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ """
18
+ CLIException Class
19
+ """
20
+
21
+ class CLIException(Exception):
22
+ """
23
+ Exceptions class to handle all CLI errors.
24
+ """