locust-cloud 1.16.1__py3-none-any.whl → 1.17.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.
- locust_cloud/__init__.py +18 -0
- locust_cloud/args.py +64 -87
- locust_cloud/cloud.py +16 -23
- {locust_cloud-1.16.1.dist-info → locust_cloud-1.17.0.dist-info}/METADATA +5 -2
- locust_cloud-1.17.0.dist-info/RECORD +13 -0
- locust_cloud-1.17.0.dist-info/licenses/LICENSE +21 -0
- locust_cloud-1.16.1.dist-info/RECORD +0 -11
- {locust_cloud-1.16.1.dist-info → locust_cloud-1.17.0.dist-info}/WHEEL +0 -0
- {locust_cloud-1.16.1.dist-info → locust_cloud-1.17.0.dist-info}/entry_points.txt +0 -0
locust_cloud/__init__.py
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
from locust import events
|
2
|
+
from locust_cloud.args import cloud_parser
|
3
|
+
from locust_cloud.cloud import main
|
4
|
+
|
5
|
+
__all__ = ["main"]
|
6
|
+
|
7
|
+
|
8
|
+
@events.init_command_line_parser.add_listener
|
9
|
+
def _(parser):
|
10
|
+
cloud_group = parser.add_argument_group(
|
11
|
+
"Locust Cloud",
|
12
|
+
"""Launches a distributed Locust run on locust.cloud infrastructure.
|
13
|
+
|
14
|
+
Example: locust --cloud -f my_locustfile.py --users 1000 ...""",
|
15
|
+
)
|
16
|
+
|
17
|
+
for action in cloud_parser._actions:
|
18
|
+
cloud_group._add_action(action)
|
locust_cloud/args.py
CHANGED
@@ -4,8 +4,14 @@ import gzip
|
|
4
4
|
import io
|
5
5
|
import os
|
6
6
|
import pathlib
|
7
|
-
import
|
8
|
-
|
7
|
+
import sys
|
8
|
+
|
9
|
+
if sys.version_info >= (3, 11):
|
10
|
+
import tomllib
|
11
|
+
else:
|
12
|
+
import tomli as tomllib
|
13
|
+
|
14
|
+
from argparse import ArgumentTypeError
|
9
15
|
from collections import OrderedDict
|
10
16
|
from collections.abc import Callable, Generator
|
11
17
|
from typing import IO, Any, cast
|
@@ -103,116 +109,54 @@ class MergeToTransferEncodedZip(argparse.Action):
|
|
103
109
|
setattr(namespace, self.dest, value)
|
104
110
|
|
105
111
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
"locust.conf",
|
110
|
-
"pyproject.toml",
|
111
|
-
"~/.cloud.conf",
|
112
|
-
"cloud.conf",
|
113
|
-
],
|
114
|
-
auto_env_var_prefix="LOCUSTCLOUD_",
|
115
|
-
formatter_class=configargparse.RawTextHelpFormatter,
|
116
|
-
config_file_parser_class=configargparse.CompositeConfigParser(
|
117
|
-
[
|
118
|
-
LocustTomlConfigParser(["tool.locust"]),
|
119
|
-
configargparse.DefaultConfigFileParser,
|
120
|
-
]
|
121
|
-
),
|
122
|
-
description="""Launches a distributed Locust runs on locust.cloud infrastructure.
|
123
|
-
|
124
|
-
Example: locust-cloud -f my_locustfile.py --users 1000 ...""",
|
125
|
-
epilog="""Any parameters not listed here are forwarded to locust master unmodified, so go ahead and use things like --users, --host, --run-time, ...
|
126
|
-
Locust config can also be set using config file (~/.locust.conf, locust.conf, pyproject.toml, ~/.cloud.conf or cloud.conf).
|
127
|
-
Parameters specified on command line override env vars, which in turn override config files.""",
|
128
|
-
add_config_file_help=False,
|
129
|
-
add_env_var_help=False,
|
130
|
-
add_help=False,
|
131
|
-
)
|
132
|
-
parser.add_argument(
|
133
|
-
"-h",
|
134
|
-
"--help",
|
135
|
-
action="help",
|
136
|
-
help=configargparse.SUPPRESS,
|
137
|
-
)
|
138
|
-
parser.add_argument(
|
139
|
-
"-V",
|
140
|
-
"--version",
|
112
|
+
cloud_parser = configargparse.ArgumentParser(add_help=False)
|
113
|
+
cloud_parser.add_argument(
|
114
|
+
"--cloud",
|
141
115
|
action="store_true",
|
142
|
-
help=
|
143
|
-
)
|
144
|
-
parser.add_argument(
|
145
|
-
"-f",
|
146
|
-
"--locustfile",
|
147
|
-
metavar="<filename>",
|
148
|
-
default="locustfile.py",
|
149
|
-
help="The Python file that contains your test. Defaults to 'locustfile.py'.",
|
150
|
-
env_var="LOCUST_LOCUSTFILE",
|
151
|
-
type=transfer_encoded_file,
|
116
|
+
help="Run Locust in cloud mode.",
|
152
117
|
)
|
153
|
-
|
154
|
-
"
|
155
|
-
"
|
156
|
-
|
157
|
-
default=1,
|
158
|
-
help="Number of users to launch. This is the same as the regular Locust argument, but also affects how many workers to launch.",
|
159
|
-
env_var="LOCUST_USERS",
|
118
|
+
cloud_parser.add_argument(
|
119
|
+
"--login",
|
120
|
+
action="store_true",
|
121
|
+
help="Launch an interactive session to authenticate your user.\nOnce completed your credentials will be stored and automatically refreshed for quite a long time.\nOnce those expires you will be prompted to perform another login.",
|
160
122
|
)
|
161
|
-
|
162
|
-
|
163
|
-
"
|
164
|
-
"-
|
165
|
-
type=str.upper,
|
166
|
-
help="Set --loglevel DEBUG for extra info.",
|
167
|
-
choices=["DEBUG", "INFO", "WARNING", "ERROR"],
|
168
|
-
default="INFO",
|
123
|
+
cloud_parser.add_argument(
|
124
|
+
"--delete",
|
125
|
+
action="store_true",
|
126
|
+
help="Delete a running cluster. Useful if locust-cloud was killed/disconnected or if there was an error.",
|
169
127
|
)
|
170
|
-
|
128
|
+
cloud_parser.add_argument(
|
171
129
|
"--requirements",
|
130
|
+
metavar="<filename>",
|
172
131
|
type=transfer_encoded_file,
|
173
132
|
help="Optional requirements.txt file that contains your external libraries.",
|
174
133
|
)
|
175
|
-
|
176
|
-
"--login",
|
177
|
-
action="store_true",
|
178
|
-
default=False,
|
179
|
-
help="Launch an interactive session to authenticate your user.\nOnce completed your credentials will be stored and automatically refreshed for quite a long time.\nOnce those expires you will be prompted to perform another login.",
|
180
|
-
)
|
181
|
-
advanced.add_argument(
|
134
|
+
cloud_parser.add_argument(
|
182
135
|
"--non-interactive",
|
183
136
|
action="store_true",
|
184
137
|
default=False,
|
185
138
|
help="This can be set when, for example, running in a CI/CD environment to ensure no interactive steps while executing.\nRequires that LOCUSTCLOUD_USERNAME, LOCUSTCLOUD_PASSWORD and LOCUSTCLOUD_REGION environment variables are set.",
|
186
139
|
)
|
187
|
-
|
140
|
+
cloud_parser.add_argument(
|
188
141
|
"--workers",
|
142
|
+
metavar="<int>",
|
189
143
|
type=int,
|
190
144
|
help="Number of workers to use for the deployment. Defaults to number of users divided by 500, but the default may be customized for your account.",
|
191
145
|
default=None,
|
192
146
|
)
|
193
|
-
|
194
|
-
"--delete",
|
195
|
-
action="store_true",
|
196
|
-
help="Delete a running cluster. Useful if locust-cloud was killed/disconnected or if there was an error.",
|
197
|
-
)
|
198
|
-
parser.add_argument(
|
147
|
+
cloud_parser.add_argument(
|
199
148
|
"--image-tag",
|
200
149
|
type=str,
|
201
150
|
default=None,
|
202
151
|
help=configargparse.SUPPRESS, # overrides the locust-cloud docker image tag. for internal use
|
203
152
|
)
|
204
|
-
|
153
|
+
cloud_parser.add_argument(
|
205
154
|
"--mock-server",
|
206
155
|
action="store_true",
|
207
156
|
default=False,
|
208
157
|
help="Start a demo mock service and set --host parameter to point Locust towards it",
|
209
158
|
)
|
210
|
-
|
211
|
-
"--profile",
|
212
|
-
type=str,
|
213
|
-
help="Set a profile to group the testruns together",
|
214
|
-
)
|
215
|
-
parser.add_argument(
|
159
|
+
cloud_parser.add_argument(
|
216
160
|
"--extra-files",
|
217
161
|
action=MergeToTransferEncodedZip,
|
218
162
|
nargs="*",
|
@@ -220,6 +164,39 @@ parser.add_argument(
|
|
220
164
|
help="A list of extra files or directories to upload. Space-separated, e.g. --extra-files testdata.csv *.py my-directory/",
|
221
165
|
)
|
222
166
|
|
223
|
-
|
224
|
-
|
225
|
-
|
167
|
+
combined_cloud_parser = configargparse.ArgumentParser(
|
168
|
+
default_config_files=[
|
169
|
+
"~/.cloud.conf",
|
170
|
+
"cloud.conf",
|
171
|
+
],
|
172
|
+
auto_env_var_prefix="LOCUSTCLOUD_",
|
173
|
+
formatter_class=configargparse.RawTextHelpFormatter,
|
174
|
+
config_file_parser_class=configargparse.CompositeConfigParser(
|
175
|
+
[
|
176
|
+
LocustTomlConfigParser(["tool.locust"]),
|
177
|
+
configargparse.DefaultConfigFileParser,
|
178
|
+
]
|
179
|
+
),
|
180
|
+
parents=[cloud_parser],
|
181
|
+
)
|
182
|
+
combined_cloud_parser.add_argument(
|
183
|
+
"-f",
|
184
|
+
"--locustfile",
|
185
|
+
default="locustfile.py",
|
186
|
+
env_var="LOCUST_LOCUSTFILE",
|
187
|
+
type=transfer_encoded_file,
|
188
|
+
)
|
189
|
+
combined_cloud_parser.add_argument(
|
190
|
+
"-u",
|
191
|
+
"--users",
|
192
|
+
type=int,
|
193
|
+
default=1,
|
194
|
+
env_var="LOCUST_USERS",
|
195
|
+
)
|
196
|
+
combined_cloud_parser.add_argument(
|
197
|
+
"--loglevel",
|
198
|
+
"-L",
|
199
|
+
type=str.upper,
|
200
|
+
choices=["DEBUG", "INFO", "WARNING", "ERROR"],
|
201
|
+
default="INFO",
|
202
|
+
)
|
locust_cloud/cloud.py
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
import logging
|
2
2
|
import os
|
3
|
-
import sys
|
4
3
|
import time
|
5
4
|
import webbrowser
|
6
5
|
from threading import Thread
|
7
6
|
|
8
7
|
import requests
|
9
8
|
from locust_cloud.apisession import ApiSession
|
10
|
-
from locust_cloud.args import
|
9
|
+
from locust_cloud.args import combined_cloud_parser
|
11
10
|
from locust_cloud.common import __version__
|
12
11
|
from locust_cloud.input_events import input_listener
|
13
12
|
from locust_cloud.web_login import web_login
|
@@ -28,30 +27,28 @@ def configure_logging(loglevel: str) -> None:
|
|
28
27
|
logging.getLogger("urllib3").setLevel(logging.INFO)
|
29
28
|
|
30
29
|
|
31
|
-
def main()
|
32
|
-
options, locust_options = parse_known_args()
|
30
|
+
def main():
|
31
|
+
options, locust_options = combined_cloud_parser.parse_known_args()
|
32
|
+
|
33
33
|
configure_logging(options.loglevel)
|
34
34
|
|
35
|
-
if options.version:
|
36
|
-
print(f"locust-cloud version {__version__}")
|
37
|
-
sys.exit(0)
|
38
35
|
if not options.locustfile:
|
39
36
|
logger.error("A locustfile is required to run a test.")
|
40
|
-
|
37
|
+
return 1
|
41
38
|
|
42
39
|
if options.login:
|
43
40
|
try:
|
44
41
|
web_login()
|
45
42
|
except KeyboardInterrupt:
|
46
43
|
pass
|
47
|
-
|
44
|
+
return
|
48
45
|
|
49
46
|
session = ApiSession(options.non_interactive)
|
50
47
|
websocket = Websocket()
|
51
48
|
|
52
49
|
if options.delete:
|
53
50
|
delete(session)
|
54
|
-
|
51
|
+
return
|
55
52
|
|
56
53
|
try:
|
57
54
|
logger.info(f"Deploying ({session.region}, {__version__})")
|
@@ -67,12 +64,12 @@ def main() -> None:
|
|
67
64
|
"LOCUST_SKIP_MONKEY_PATCH",
|
68
65
|
]
|
69
66
|
]
|
67
|
+
|
70
68
|
payload = {
|
71
69
|
"locust_args": [
|
72
70
|
{"name": "LOCUST_USERS", "value": str(options.users)},
|
73
71
|
{"name": "LOCUST_FLAGS", "value": " ".join(locust_options)},
|
74
72
|
{"name": "LOCUSTCLOUD_DEPLOYER_URL", "value": session.api_url},
|
75
|
-
{"name": "LOCUSTCLOUD_PROFILE", "value": options.profile},
|
76
73
|
*locust_env_variables,
|
77
74
|
],
|
78
75
|
"locustfile": options.locustfile,
|
@@ -107,10 +104,10 @@ def main() -> None:
|
|
107
104
|
time.sleep(2)
|
108
105
|
except requests.exceptions.RequestException as e:
|
109
106
|
logger.error(f"Failed to deploy the load generators: {e}")
|
110
|
-
|
107
|
+
return 1
|
111
108
|
else:
|
112
|
-
logger.error("Your Locust instance is still running, run locust
|
113
|
-
|
109
|
+
logger.error("Your Locust instance is still running, run locust --cloud --delete")
|
110
|
+
return 1
|
114
111
|
|
115
112
|
if response.status_code != 200:
|
116
113
|
try:
|
@@ -119,7 +116,7 @@ def main() -> None:
|
|
119
116
|
logger.error(
|
120
117
|
f"HTTP {response.status_code}/{response.reason} - Response: {response.text} - URL: {response.request.url}"
|
121
118
|
)
|
122
|
-
|
119
|
+
return 1
|
123
120
|
|
124
121
|
log_ws_url = js["log_ws_url"]
|
125
122
|
session_id = js["session_id"]
|
@@ -146,19 +143,19 @@ def main() -> None:
|
|
146
143
|
websocket.wait(timeout=True)
|
147
144
|
except (WebsocketTimeout, SessionMismatchError) as e:
|
148
145
|
logger.error(str(e))
|
149
|
-
|
146
|
+
return 1
|
150
147
|
except WebsocketTimeout as e:
|
151
148
|
logger.error(str(e))
|
152
149
|
delete(session)
|
153
|
-
|
150
|
+
return 1
|
154
151
|
except SessionMismatchError as e:
|
155
152
|
# In this case we do not trigger the teardown since the running instance is not ours
|
156
153
|
logger.error(str(e))
|
157
|
-
|
154
|
+
return 1
|
158
155
|
except Exception as e:
|
159
156
|
logger.exception(e)
|
160
157
|
delete(session)
|
161
|
-
|
158
|
+
return 1
|
162
159
|
else:
|
163
160
|
delete(session)
|
164
161
|
finally:
|
@@ -183,7 +180,3 @@ def delete(session):
|
|
183
180
|
pass # don't show nasty callstack
|
184
181
|
except Exception as e:
|
185
182
|
logger.error(f"Could not automatically tear down Locust Cloud: {e.__class__.__name__}:{e}")
|
186
|
-
|
187
|
-
|
188
|
-
if __name__ == "__main__":
|
189
|
-
main()
|
@@ -1,15 +1,18 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: locust-cloud
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.17.0
|
4
4
|
Summary: Locust Cloud
|
5
5
|
Project-URL: Homepage, https://locust.cloud
|
6
|
-
|
6
|
+
License-File: LICENSE
|
7
|
+
Requires-Python: >=3.10
|
7
8
|
Requires-Dist: configargparse==1.7
|
8
9
|
Requires-Dist: gevent>=24.11.1
|
10
|
+
Requires-Dist: locust>=2.34.0
|
9
11
|
Requires-Dist: platformdirs>=4.3.6
|
10
12
|
Requires-Dist: pyjwt>=2.0
|
11
13
|
Requires-Dist: python-socketio[client]==5.11.4
|
12
14
|
Requires-Dist: requests==2.32.3
|
15
|
+
Requires-Dist: tomli>=1.1.0; python_version < '3.11'
|
13
16
|
Description-Content-Type: text/markdown
|
14
17
|
|
15
18
|
# Locust Cloud
|
@@ -0,0 +1,13 @@
|
|
1
|
+
locust_cloud/__init__.py,sha256=NPggKqjx13UKmmgoG6dpoXOyA1liGBAUP8pz6OSpBvI,488
|
2
|
+
locust_cloud/apisession.py,sha256=kCr271_l0IeMGw0L563mOecqPJj4OD9h2J3vxCM5zYQ,4015
|
3
|
+
locust_cloud/args.py,sha256=oyl6Uv5CV3mTMLos_-3ChVwyHt6ElxLAaanlbQn5SaU,6056
|
4
|
+
locust_cloud/cloud.py,sha256=RbW7p4pQWIcvaYe_80BwNiBC8YHmrX-1tE5X8Bi0dH0,5912
|
5
|
+
locust_cloud/common.py,sha256=cFrDVKpi9OEmH6giOuj9HoIUFSBArixNtNHzZIgDvPE,992
|
6
|
+
locust_cloud/input_events.py,sha256=MyxccgboHByICuK6VpQCCJhZQqTZAacNmkSpw-gxBEw,3420
|
7
|
+
locust_cloud/web_login.py,sha256=1j2AQoEM6XVSDtE1q0Ryrs4jFEx07r9IQfZCoFAQXJg,2400
|
8
|
+
locust_cloud/websocket.py,sha256=9Q7nTFuAwVhgW74DlJNcHTZXOQ1drsXi8hX9ciZhWlQ,8998
|
9
|
+
locust_cloud-1.17.0.dist-info/METADATA,sha256=HTj3BaXSF4pqcuJB9Y83M0F2rgonl3sXMzCzYm8HmXs,633
|
10
|
+
locust_cloud-1.17.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
11
|
+
locust_cloud-1.17.0.dist-info/entry_points.txt,sha256=PGyAb4e3aTsGS3N3VGShDl6VzJaXy7QwsEgsLOC7V00,57
|
12
|
+
locust_cloud-1.17.0.dist-info/licenses/LICENSE,sha256=Ow6fY6ta4KIjdlWalmxGvRP8yLmetvkbkl-SdHMjPIs,1093
|
13
|
+
locust_cloud-1.17.0.dist-info/RECORD,,
|
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2024-present, Locust Technologies Inc
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
@@ -1,11 +0,0 @@
|
|
1
|
-
locust_cloud/apisession.py,sha256=kCr271_l0IeMGw0L563mOecqPJj4OD9h2J3vxCM5zYQ,4015
|
2
|
-
locust_cloud/args.py,sha256=4U_bZaEH1UJR8C7_bH_sX1DRB8m6WBcJh-ewhilFQX0,7118
|
3
|
-
locust_cloud/cloud.py,sha256=kih2Qtddry0dxmGylxXQdrw4SPTwx6k6YN0ur3u47Qc,6144
|
4
|
-
locust_cloud/common.py,sha256=cFrDVKpi9OEmH6giOuj9HoIUFSBArixNtNHzZIgDvPE,992
|
5
|
-
locust_cloud/input_events.py,sha256=MyxccgboHByICuK6VpQCCJhZQqTZAacNmkSpw-gxBEw,3420
|
6
|
-
locust_cloud/web_login.py,sha256=1j2AQoEM6XVSDtE1q0Ryrs4jFEx07r9IQfZCoFAQXJg,2400
|
7
|
-
locust_cloud/websocket.py,sha256=9Q7nTFuAwVhgW74DlJNcHTZXOQ1drsXi8hX9ciZhWlQ,8998
|
8
|
-
locust_cloud-1.16.1.dist-info/METADATA,sha256=4EHCv-Hpo9NR-IThvTMiJR9t5EWfdjARKBt2V2QApBQ,528
|
9
|
-
locust_cloud-1.16.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
10
|
-
locust_cloud-1.16.1.dist-info/entry_points.txt,sha256=PGyAb4e3aTsGS3N3VGShDl6VzJaXy7QwsEgsLOC7V00,57
|
11
|
-
locust_cloud-1.16.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|