twc-cli 2.11.0__tar.gz → 2.13.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.

Potentially problematic release.


This version of twc-cli might be problematic. Click here for more details.

Files changed (35) hide show
  1. {twc_cli-2.11.0 → twc_cli-2.13.0}/CHANGELOG.md +17 -0
  2. {twc_cli-2.11.0 → twc_cli-2.13.0}/PKG-INFO +4 -4
  3. {twc_cli-2.11.0 → twc_cli-2.13.0}/pyproject.toml +2 -2
  4. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/__main__.py +2 -0
  5. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/__version__.py +1 -1
  6. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/api/client.py +101 -2
  7. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/commands/__init__.py +1 -0
  8. twc_cli-2.13.0/twc/commands/apps.py +337 -0
  9. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/commands/balancer.py +42 -0
  10. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/commands/storage.py +1 -1
  11. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/vars.py +1 -1
  12. {twc_cli-2.11.0 → twc_cli-2.13.0}/COPYING +0 -0
  13. {twc_cli-2.11.0 → twc_cli-2.13.0}/README.md +0 -0
  14. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/__init__.py +0 -0
  15. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/api/__init__.py +0 -0
  16. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/api/base.py +0 -0
  17. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/api/exceptions.py +0 -0
  18. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/api/types.py +0 -0
  19. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/apiwrap.py +0 -0
  20. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/commands/account.py +0 -0
  21. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/commands/common.py +0 -0
  22. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/commands/config.py +0 -0
  23. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/commands/database.py +0 -0
  24. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/commands/domain.py +0 -0
  25. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/commands/firewall.py +0 -0
  26. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/commands/floating_ip.py +0 -0
  27. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/commands/image.py +0 -0
  28. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/commands/kubernetes.py +0 -0
  29. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/commands/project.py +0 -0
  30. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/commands/server.py +0 -0
  31. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/commands/ssh_key.py +0 -0
  32. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/commands/vpc.py +0 -0
  33. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/fmt.py +0 -0
  34. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/typerx.py +0 -0
  35. {twc_cli-2.11.0 → twc_cli-2.13.0}/twc/utils.py +0 -0
@@ -2,6 +2,23 @@
2
2
 
3
3
  В этом файле описаны все значимые изменения в Timeweb Cloud CLI. В выпусках мы придерживается правил [семантического версионирования](https://semver.org/lang/ru/).
4
4
 
5
+ # Версия 2.13.0 (2025.06.17)
6
+
7
+ ## Добавлено
8
+
9
+ - Добавлена новая команда `twc apps` для управления сервисом Cloud Apps.
10
+
11
+ # Версия 2.12.0 (2025.04.11)
12
+
13
+ ## Добавлено
14
+
15
+ - Добавлены опции `--max-connections`, `--connect-timeout`, `--client-timeout`, `--server-timeout` в команды `twc balancer create`, `twc balancer set`.
16
+ - В регионе `kz-1` для облачных серверов теперь доступен IPv6.
17
+
18
+ ## Изменено
19
+
20
+ - Стандартный ендпоинт объектного хранилища изменён с `s3.timeweb.cloud` на `s3.twcstorage.ru`.
21
+
5
22
  # Версия 2.11.0 (2025.04.01)
6
23
 
7
24
  ## Добавлено
@@ -1,8 +1,7 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: twc-cli
3
- Version: 2.11.0
3
+ Version: 2.13.0
4
4
  Summary: Timeweb Cloud Command Line Interface.
5
- Home-page: https://github.com/timeweb-cloud/twc
6
5
  License: MIT
7
6
  Author: ge
8
7
  Author-email: dev@timeweb.cloud
@@ -17,10 +16,11 @@ Classifier: Programming Language :: Python :: 3.13
17
16
  Requires-Dist: colorama (>=0.4.6,<0.5.0)
18
17
  Requires-Dist: pygments (>=2.18.0,<3.0.0)
19
18
  Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
20
- Requires-Dist: requests (>=2.32.3,<3.0.0)
19
+ Requires-Dist: requests (>=2.32.4,<3.0.0)
21
20
  Requires-Dist: shellingham (>=1.5.4,<2.0.0)
22
21
  Requires-Dist: toml (>=0.10.2,<0.11.0)
23
22
  Requires-Dist: typer-slim (>=0.12.3,<0.13.0)
23
+ Project-URL: Homepage, https://github.com/timeweb-cloud/twc
24
24
  Project-URL: Repository, https://github.com/timeweb-cloud/twc
25
25
  Description-Content-Type: text/markdown
26
26
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "twc-cli"
3
- version = "2.11.0"
3
+ version = "2.13.0"
4
4
  description = "Timeweb Cloud Command Line Interface."
5
5
  authors = ["ge <dev@timeweb.cloud>"]
6
6
  homepage = "https://github.com/timeweb-cloud/twc"
@@ -12,7 +12,7 @@ packages = [{ include = "twc", from = "." }]
12
12
 
13
13
  [tool.poetry.dependencies]
14
14
  python = "^3.8.19"
15
- requests = "^2.32.3"
15
+ requests = "^2.32.4"
16
16
  typer-slim = "^0.12.3"
17
17
  shellingham = "^1.5.4"
18
18
  colorama = "^0.4.6"
@@ -22,6 +22,7 @@ from .commands import (
22
22
  firewall,
23
23
  floating_ip,
24
24
  whoami,
25
+ apps,
25
26
  )
26
27
  from .commands.common import version_callback, version_option, verbose_option
27
28
 
@@ -33,6 +34,7 @@ cli = TyperAlias(
33
34
  )
34
35
  cli.add_typer(config, name="config")
35
36
  cli.add_typer(account, name="account")
37
+ cli.add_typer(apps, name="apps")
36
38
  cli.add_typer(server, name="server", aliases=["servers", "s"])
37
39
  cli.add_typer(ssh_key, name="ssh-key", aliases=["ssh-keys", "k"])
38
40
  cli.add_typer(image, name="image", aliases=["images", "i"])
@@ -12,5 +12,5 @@
12
12
  import sys
13
13
 
14
14
 
15
- __version__ = "2.11.0"
15
+ __version__ = "2.13.0"
16
16
  __pyversion__ = sys.version.replace("\n", "")
@@ -2,10 +2,12 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from ipaddress import IPv4Address, IPv6Address, IPv4Network, IPv6Network
5
6
  from typing import Optional, Union, List
6
- from uuid import UUID
7
7
  from pathlib import Path
8
- from ipaddress import IPv4Address, IPv6Address, IPv4Network, IPv6Network
8
+ from uuid import UUID
9
+
10
+ import yaml
9
11
 
10
12
  from .base import TimewebCloudBase
11
13
  from .types import (
@@ -34,6 +36,43 @@ from .types import (
34
36
  class TimewebCloud(TimewebCloudBase):
35
37
  """Timeweb Cloud API client class."""
36
38
 
39
+ # -----------------------------------------------------------------------
40
+ # Apps
41
+
42
+ def get_apps(self):
43
+ """Return Timeweb Cloud apps list."""
44
+ return self._request("GET", f"{self.api_url}/apps")
45
+
46
+ def create_app(self, yaml_config_path: str):
47
+ """Create Timeweb Cloud app."""
48
+
49
+ with open(yaml_config_path, "r", encoding="utf-8") as f:
50
+ payload = yaml.safe_load(f)
51
+ payload = payload["app"]
52
+ return self._request("POST", f"{self.api_url}/apps", json=payload)
53
+
54
+ def get_app(self, app_id: int):
55
+ """Return Timeweb Cloud app."""
56
+ return self._request("GET", f"{self.api_url}/apps/{app_id}")
57
+
58
+ def get_vcs_providers(self):
59
+ """Return Timeweb Cloud vcs providers."""
60
+ return self._request("GET", f"{self.api_url}/vcs-provider")
61
+
62
+ def get_repositories(self, vcs_provider_id: str):
63
+ """Return Timeweb Cloud user vcs repositories."""
64
+ return self._request(
65
+ "GET", f"{self.api_url}/vcs-provider/{vcs_provider_id}"
66
+ )
67
+
68
+ def get_apps_tarifs(self):
69
+ """Return Timeweb Cloud Apps tarifs"""
70
+ return self._request("GET", f"{self.api_url}/presets/apps")
71
+
72
+ def delete_app(self, app_id: int):
73
+ """Delete Timeweb Cloud app."""
74
+ return self._request("DELETE", f"{self.api_url}/apps/{app_id}")
75
+
37
76
  # -----------------------------------------------------------------------
38
77
  # Account
39
78
 
@@ -1162,6 +1201,11 @@ class TimewebCloud(TimewebCloudBase):
1162
1201
  comment: Optional[str] = None,
1163
1202
  project_id: Optional[int] = None,
1164
1203
  certificates: Optional[dict] = None,
1204
+ max_connections: Optional[int] = None,
1205
+ connect_timeout: Optional[int] = None,
1206
+ client_timeout: Optional[int] = None,
1207
+ server_timeout: Optional[int] = None,
1208
+ http_timeout: Optional[int] = None,
1165
1209
  ):
1166
1210
  """Create load balancer."""
1167
1211
  payload = {
@@ -1183,6 +1227,31 @@ class TimewebCloud(TimewebCloudBase):
1183
1227
  **({"comment": comment} if comment else {}),
1184
1228
  **({"project_id": project_id} if project_id else {}),
1185
1229
  **({"certificates": certificates} if certificates else {}),
1230
+ **(
1231
+ {"maxconn": max_connections}
1232
+ if max_connections is not None
1233
+ else {}
1234
+ ),
1235
+ **(
1236
+ {"connect_timeout": connect_timeout}
1237
+ if connect_timeout is not None
1238
+ else {}
1239
+ ),
1240
+ **(
1241
+ {"client_timeout": client_timeout}
1242
+ if client_timeout is not None
1243
+ else {}
1244
+ ),
1245
+ **(
1246
+ {"server_timeout": server_timeout}
1247
+ if server_timeout is not None
1248
+ else {}
1249
+ ),
1250
+ **(
1251
+ {"httprequest_timeout": http_timeout}
1252
+ if http_timeout is not None
1253
+ else {}
1254
+ ),
1186
1255
  }
1187
1256
  return self._request("POST", f"{self.api_url}/balancers", json=payload)
1188
1257
 
@@ -1203,6 +1272,11 @@ class TimewebCloud(TimewebCloudBase):
1203
1272
  proxy_protocol: Optional[bool] = None,
1204
1273
  force_https: Optional[bool] = None,
1205
1274
  backend_keepalive: Optional[bool] = None,
1275
+ max_connections: Optional[int] = None,
1276
+ connect_timeout: Optional[int] = None,
1277
+ client_timeout: Optional[int] = None,
1278
+ server_timeout: Optional[int] = None,
1279
+ http_timeout: Optional[int] = None,
1206
1280
  ):
1207
1281
  """Update load balancer settings."""
1208
1282
  payload = {
@@ -1228,6 +1302,31 @@ class TimewebCloud(TimewebCloudBase):
1228
1302
  if backend_keepalive is not None
1229
1303
  else {}
1230
1304
  ),
1305
+ **(
1306
+ {"maxconn": max_connections}
1307
+ if max_connections is not None
1308
+ else {}
1309
+ ),
1310
+ **(
1311
+ {"connect_timeout": connect_timeout}
1312
+ if connect_timeout is not None
1313
+ else {}
1314
+ ),
1315
+ **(
1316
+ {"client_timeout": client_timeout}
1317
+ if client_timeout is not None
1318
+ else {}
1319
+ ),
1320
+ **(
1321
+ {"server_timeout": server_timeout}
1322
+ if server_timeout is not None
1323
+ else {}
1324
+ ),
1325
+ **(
1326
+ {"httprequest_timeout": http_timeout}
1327
+ if http_timeout is not None
1328
+ else {}
1329
+ ),
1231
1330
  }
1232
1331
  return self._request(
1233
1332
  "PATCH", f"{self.api_url}/balancers/{balancer_id}", json=payload
@@ -1,6 +1,7 @@
1
1
  """Commands."""
2
2
 
3
3
  from .account import account, whoami
4
+ from .apps import apps
4
5
  from .config import config
5
6
  from .server import server
6
7
  from .ssh_key import ssh_key
@@ -0,0 +1,337 @@
1
+ """Manage apps."""
2
+
3
+ from typing import Optional
4
+ from pathlib import Path
5
+
6
+ import typer
7
+ from requests import Response
8
+
9
+ from twc import fmt
10
+ from twc.typerx import TyperAlias
11
+ from twc.apiwrap import create_client
12
+ from .common import (
13
+ verbose_option,
14
+ config_option,
15
+ profile_option,
16
+ filter_option,
17
+ output_format_option,
18
+ )
19
+
20
+
21
+ apps = TyperAlias(help=__doc__)
22
+
23
+
24
+ # ------------------------------------------------------------- #
25
+ # $ twc apps list #
26
+ # ------------------------------------------------------------- #
27
+
28
+
29
+ def print_apps(response: Response, filters: Optional[str]):
30
+ """Print table with apps list."""
31
+ # pylint: disable=invalid-name
32
+ _apps = response.json()["apps"]
33
+ if filters:
34
+ _apps = fmt.filter_list(_apps, filters)
35
+ table = fmt.Table()
36
+ table.header(
37
+ [
38
+ "ID",
39
+ "NAME",
40
+ "STATUS",
41
+ "TYPE",
42
+ "IPV4",
43
+ ]
44
+ )
45
+ for app in _apps:
46
+ table.row(
47
+ [
48
+ app["id"],
49
+ app["name"],
50
+ app["status"],
51
+ app["type"],
52
+ app["ip"],
53
+ ]
54
+ )
55
+ table.print()
56
+
57
+
58
+ @apps.command("list", "ls")
59
+ def apps_list(
60
+ verbose: Optional[bool] = verbose_option,
61
+ config: Optional[Path] = config_option,
62
+ profile: Optional[str] = profile_option,
63
+ output_format: Optional[str] = output_format_option,
64
+ filters: Optional[str] = filter_option,
65
+ ):
66
+ """List apps."""
67
+ client = create_client(config, profile)
68
+ response = client.get_apps()
69
+ fmt.printer(
70
+ response,
71
+ output_format=output_format,
72
+ filters=filters,
73
+ func=print_apps,
74
+ )
75
+
76
+
77
+ @apps.command("create")
78
+ def app_create(
79
+ yml_config_path: str,
80
+ verbose: Optional[bool] = verbose_option,
81
+ config: Optional[Path] = config_option,
82
+ profile: Optional[str] = profile_option,
83
+ output_format: Optional[str] = output_format_option,
84
+ status: Optional[bool] = typer.Option(
85
+ False,
86
+ "--status",
87
+ help="Display status and exit with 0 if status is 'started'.",
88
+ ),
89
+ ):
90
+ """Create app"""
91
+ client = create_client(config, profile)
92
+ response = client.create_app(yml_config_path)
93
+ fmt.printer(
94
+ response,
95
+ output_format=output_format,
96
+ func=lambda response: print(response.json()["app"]["id"]),
97
+ )
98
+
99
+
100
+ def print_vcs_providers(response: Response):
101
+ """Print table with vcs list."""
102
+ # pylint: disable=invalid-name
103
+ providers = response.json()["providers"]
104
+ table = fmt.Table()
105
+ table.header(
106
+ [
107
+ "LOGIN",
108
+ "PROVIDER",
109
+ "PROVIDER_ID",
110
+ ]
111
+ )
112
+ for provider in providers:
113
+ table.row(
114
+ [
115
+ provider["login"],
116
+ provider["provider"],
117
+ provider["provider_id"],
118
+ ]
119
+ )
120
+ table.print()
121
+
122
+
123
+ @apps.command("get-vcs-providers")
124
+ def app_get_vcs_providers(
125
+ verbose: Optional[bool] = verbose_option,
126
+ config: Optional[Path] = config_option,
127
+ profile: Optional[str] = profile_option,
128
+ output_format: Optional[str] = output_format_option,
129
+ ):
130
+ """Get VCS providers."""
131
+ client = create_client(config, profile)
132
+ response = client.get_vcs_providers()
133
+ fmt.printer(
134
+ response, output_format=output_format, func=print_vcs_providers
135
+ )
136
+
137
+
138
+ def print_vcs_repositories(response: Response):
139
+ """Print table with vcs list."""
140
+ # pylint: disable=invalid-name
141
+ providers = response.json()["repositories"]
142
+ table = fmt.Table()
143
+ table.header(["FULL NAME", "ID", "NAME", "URL", "IS PRIVATE"])
144
+ for provider in providers:
145
+ table.row(
146
+ [
147
+ provider["full_name"],
148
+ provider["id"],
149
+ provider["name"],
150
+ provider["url"],
151
+ provider["is_private"],
152
+ ]
153
+ )
154
+ table.print()
155
+
156
+
157
+ @apps.command("get-repositories")
158
+ def app_get_repositories(
159
+ vcs_provider_id: str,
160
+ verbose: Optional[bool] = verbose_option,
161
+ config: Optional[Path] = config_option,
162
+ profile: Optional[str] = profile_option,
163
+ output_format: Optional[str] = output_format_option,
164
+ ):
165
+ """Get repositories."""
166
+ client = create_client(config, profile)
167
+ response = client.get_repositories(vcs_provider_id)
168
+ fmt.printer(
169
+ response, output_format=output_format, func=print_vcs_repositories
170
+ )
171
+
172
+
173
+ def print_apps_tarifs(response: Response, typ: str):
174
+ """Print Timeweb Cloud Apps tarifs."""
175
+ # pylint: disable=invalid-name
176
+ if typ == "backend":
177
+ key = "backend_presets"
178
+ elif typ == "frontend":
179
+ key = "frontend_presets"
180
+ else:
181
+ raise KeyError("Tarifs can be only backend or frontend")
182
+
183
+ providers = response.json()[key]
184
+ table = fmt.Table()
185
+ if key == "backend_presets":
186
+ table.header(
187
+ [
188
+ "CPU",
189
+ "CPU FREQUENCY",
190
+ "DESCRIPTION",
191
+ "DESCRIPTION SHORT",
192
+ "DISK",
193
+ "ID",
194
+ "LOCATION",
195
+ "PRICE",
196
+ "RAM",
197
+ ]
198
+ )
199
+ for provider in providers:
200
+ table.row(
201
+ [
202
+ provider["cpu"],
203
+ provider["cpu_frequency"],
204
+ provider["description"],
205
+ provider["description_short"],
206
+ provider["disk"],
207
+ provider["id"],
208
+ provider["location"],
209
+ provider["price"],
210
+ provider["ram"],
211
+ ]
212
+ )
213
+ elif key == "frontend_presets":
214
+ table.header(
215
+ [
216
+ "DESCRIPTION",
217
+ "DESCRIPTION_SHORT",
218
+ "DISK",
219
+ "ID",
220
+ "LOCATION",
221
+ "PRICE",
222
+ "REQUESTS",
223
+ ]
224
+ )
225
+ for provider in providers:
226
+ table.row(
227
+ [
228
+ provider["description"],
229
+ provider["description_short"],
230
+ provider["disk"],
231
+ provider["id"],
232
+ provider["location"],
233
+ provider["price"],
234
+ provider["requests"],
235
+ ]
236
+ )
237
+ table.print()
238
+
239
+
240
+ @apps.command("list-presets")
241
+ def get_apps_tarifs(
242
+ _type: str = typer.Argument(..., metavar="TYPE"),
243
+ verbose: Optional[bool] = verbose_option,
244
+ config: Optional[Path] = config_option,
245
+ profile: Optional[str] = profile_option,
246
+ output_format: Optional[str] = output_format_option,
247
+ ):
248
+ """Get tarifs; backend or frontend"""
249
+ client = create_client(config, profile)
250
+ response = client.get_apps_tarifs()
251
+ fmt.printer(
252
+ response,
253
+ output_format=output_format,
254
+ typ=_type,
255
+ func=print_apps_tarifs,
256
+ )
257
+
258
+
259
+ def print_app_delete_response(response: Response, app_id: int):
260
+ """Print table with apps list."""
261
+ # pylint: disable=invalid-name
262
+ table = fmt.Table()
263
+ table.header(
264
+ [
265
+ "ID",
266
+ ]
267
+ )
268
+ table.row(
269
+ [
270
+ app_id,
271
+ ]
272
+ )
273
+ table.print()
274
+
275
+
276
+ @apps.command("delete")
277
+ def delete_app(
278
+ app_id: int,
279
+ verbose: Optional[bool] = verbose_option,
280
+ config: Optional[Path] = config_option,
281
+ profile: Optional[str] = profile_option,
282
+ output_format: Optional[str] = output_format_option,
283
+ ):
284
+ """Delete apps."""
285
+ client = create_client(config, profile)
286
+ response = client.delete_app(app_id)
287
+ fmt.printer(
288
+ response,
289
+ output_format=output_format,
290
+ app_id=app_id,
291
+ func=print_app_delete_response,
292
+ )
293
+
294
+
295
+ def get_app(response: Response):
296
+ """Print table with apps list."""
297
+ # pylint: disable=invalid-name
298
+ app = response.json()["app"]
299
+ table = fmt.Table()
300
+ table.header(
301
+ [
302
+ "ID",
303
+ "LOCATION",
304
+ "STATUS",
305
+ "TYPE",
306
+ "IPV4",
307
+ ]
308
+ )
309
+ table.row(
310
+ [
311
+ app["id"],
312
+ app["location"],
313
+ app["status"],
314
+ app["type"],
315
+ app["ip"],
316
+ ]
317
+ )
318
+ table.print()
319
+
320
+
321
+ @apps.command("get")
322
+ def app_get(
323
+ app_id: int,
324
+ verbose: Optional[bool] = verbose_option,
325
+ config: Optional[Path] = config_option,
326
+ profile: Optional[str] = profile_option,
327
+ output_format: Optional[str] = output_format_option,
328
+ status: Optional[bool] = typer.Option(
329
+ False,
330
+ "--status",
331
+ help="Display status and exit with 0 if status is 'started'.",
332
+ ),
333
+ ):
334
+ """Get database info."""
335
+ client = create_client(config, profile)
336
+ response = client.get_app(app_id)
337
+ fmt.printer(response, output_format=output_format, func=get_app)
@@ -198,6 +198,22 @@ def balancer_create(
198
198
  proxy_protocol: bool = typer.Option(False),
199
199
  force_https: bool = typer.Option(False),
200
200
  backend_keepalive: bool = typer.Option(False),
201
+ max_connections: Optional[int] = typer.Option(
202
+ None, help="Backend server's maximum number of concurrent connections."
203
+ ),
204
+ connect_timeout: Optional[int] = typer.Option(
205
+ None,
206
+ help="Maximum time to wait for a connection attempt to a backend server to succeed.",
207
+ ),
208
+ client_timeout: Optional[int] = typer.Option(
209
+ None, help="Maximum inactivity time on the client side."
210
+ ),
211
+ server_timeout: Optional[int] = typer.Option(
212
+ None, help="Maximum time for pending data staying into output buffer."
213
+ ),
214
+ http_timeout: Optional[int] = typer.Option(
215
+ None, help="Maximum allowed time to wait for a complete HTTP request."
216
+ ),
201
217
  project_id: Optional[int] = typer.Option(
202
218
  None,
203
219
  envvar="TWC_PROJECT",
@@ -259,6 +275,11 @@ def balancer_create(
259
275
  "network": {},
260
276
  "project_id": project_id,
261
277
  "certificates": {},
278
+ "max_connections": max_connections,
279
+ "connect_timeout": connect_timeout,
280
+ "client_timeout": client_timeout,
281
+ "server_timeout": server_timeout,
282
+ "http_timeout": http_timeout,
262
283
  }
263
284
 
264
285
  if cert_type == CertType.CUSTOM:
@@ -412,6 +433,22 @@ def balancer_set(
412
433
  proxy_protocol: Optional[bool] = typer.Option(None),
413
434
  force_https: Optional[bool] = typer.Option(None),
414
435
  backend_keepalive: Optional[bool] = typer.Option(None),
436
+ max_connections: Optional[int] = typer.Option(
437
+ None, help="Backend server's maximum number of concurrent connections."
438
+ ),
439
+ connect_timeout: Optional[int] = typer.Option(
440
+ None,
441
+ help="Maximum time to wait for a connection attempt to a backend server to succeed.",
442
+ ),
443
+ client_timeout: Optional[int] = typer.Option(
444
+ None, help="Maximum inactivity time on the client side."
445
+ ),
446
+ server_timeout: Optional[int] = typer.Option(
447
+ None, help="Maximum time for pending data staying into output buffer."
448
+ ),
449
+ http_timeout: Optional[int] = typer.Option(
450
+ None, help="Maximum allowed time to wait for a complete HTTP request."
451
+ ),
415
452
  ):
416
453
  """Change load balancer parameters."""
417
454
  client = create_client(config, profile)
@@ -442,6 +479,11 @@ def balancer_set(
442
479
  proxy_protocol=proxy_protocol,
443
480
  force_https=force_https,
444
481
  backend_keepalive=backend_keepalive,
482
+ max_connections=max_connections,
483
+ connect_timeout=connect_timeout,
484
+ client_timeout=client_timeout,
485
+ server_timeout=server_timeout,
486
+ http_timeout=http_timeout,
445
487
  )
446
488
 
447
489
  fmt.printer(
@@ -611,7 +611,7 @@ def storage_genconfig(
611
611
  "rclone": RCLONE_CONFIG_TEMPLATE.strip(),
612
612
  }
613
613
 
614
- endpoint = "s3.timeweb.cloud"
614
+ endpoint = "s3.twcstorage.ru"
615
615
  if not access_key.isupper():
616
616
  # Legacy object storage service have lowercase usernames only.
617
617
  # New storage, on the contrary, always has keys in uppercase.
@@ -8,7 +8,7 @@ expand or other infrastructure or product changes occur.
8
8
  CONTROL_PANEL_URL = "https://timeweb.cloud/my"
9
9
 
10
10
  # Location specific parameters. May change later.
11
- REGIONS_WITH_IPV6 = ["ru-1", "ru-3", "pl-1", "nl-1"]
11
+ REGIONS_WITH_IPV6 = ["ru-1", "ru-3", "pl-1", "nl-1", "kz-1"]
12
12
  REGIONS_WITH_IMAGES = ["ru-1", "ru-3", "kz-1", "pl-1", "nl-1"]
13
13
  REGIONS_WITH_LAN = ["ru-1", "ru-3", "nl-1", "pl-1", "de-1"]
14
14
  ZONES_WITH_LAN = [
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes