twc-cli 1.1.0__py3-none-any.whl → 1.3.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 twc-cli might be problematic. Click here for more details.

@@ -0,0 +1,818 @@
1
+ """Object Storage management commands."""
2
+
3
+ import sys
4
+
5
+ import click
6
+ from click_aliases import ClickAliasedGroup
7
+
8
+ from twc import fmt
9
+ from twc.vars import TWC_S3_ENDPOINT
10
+ from . import (
11
+ create_client,
12
+ handle_request,
13
+ set_value_from_config,
14
+ options,
15
+ debug,
16
+ GLOBAL_OPTIONS,
17
+ OUTPUT_FORMAT_OPTION,
18
+ )
19
+ from .project import (
20
+ get_default_project_id,
21
+ _project_list,
22
+ _project_resource_move,
23
+ _project_resource_list_buckets,
24
+ )
25
+
26
+
27
+ @handle_request
28
+ def _storage_list(client, *args, **kwargs):
29
+ return client.get_buckets(*args, **kwargs)
30
+
31
+
32
+ @handle_request
33
+ def _storage_mb(client, *args, **kwargs):
34
+ return client.create_bucket(*args, **kwargs)
35
+
36
+
37
+ @handle_request
38
+ def _storage_rb(client, *args, **kwargs):
39
+ return client.delete_bucket(*args, **kwargs)
40
+
41
+
42
+ @handle_request
43
+ def _storage_set(client, *args, **kwargs):
44
+ return client.update_bucket(*args, **kwargs)
45
+
46
+
47
+ @handle_request
48
+ def _storage_user_list(client, *args, **kwargs):
49
+ return client.get_storage_users(*args, **kwargs)
50
+
51
+
52
+ @handle_request
53
+ def _storage_user_passwd(client, *args, **kwargs):
54
+ return client.update_storage_user_secret(*args, **kwargs)
55
+
56
+
57
+ @handle_request
58
+ def _storage_transfer_new(client, *args, **kwargs):
59
+ return client.start_storage_transfer(*args, **kwargs)
60
+
61
+
62
+ @handle_request
63
+ def _storage_transfer_status(client, *args, **kwargs):
64
+ return client.get_storage_transfer_status(*args, **kwargs)
65
+
66
+
67
+ @handle_request
68
+ def _storage_subdomain_list(client, *args, **kwargs):
69
+ return client.get_bucket_subdomains(*args, **kwargs)
70
+
71
+
72
+ @handle_request
73
+ def _storage_subdomain_add(client, *args, **kwargs):
74
+ return client.add_bucket_subdomains(*args, **kwargs)
75
+
76
+
77
+ @handle_request
78
+ def _storage_subdomain_remove(client, *args, **kwargs):
79
+ return client.delete_bucket_subdomains(*args, **kwargs)
80
+
81
+
82
+ @handle_request
83
+ def _storage_subdomain_gencert(client, *args, **kwargs):
84
+ return client.gen_cert_for_bucket_subdomain(*args, **kwargs)
85
+
86
+
87
+ @handle_request
88
+ def _storage_list_presets(client, *args, **kwargs):
89
+ return client.get_storage_presets(*args, **kwargs)
90
+
91
+
92
+ # ------------------------------------------------------------- #
93
+ # $ twc storage #
94
+ # ------------------------------------------------------------- #
95
+
96
+
97
+ @click.group(
98
+ "storage",
99
+ cls=ClickAliasedGroup,
100
+ short_help="Manage Object Storage buckets.",
101
+ )
102
+ @options(GLOBAL_OPTIONS[:2])
103
+ def storage():
104
+ """Manage Object Storage buckets.
105
+
106
+ NOTE: TWC CLI does not implement S3-compatible API client, it uses
107
+ Timeweb Cloud specific API methods instead. Use third party S3 clients
108
+ to manage objects e.g. s3cmd, rclone, etc.
109
+ """
110
+
111
+
112
+ # ------------------------------------------------------------- #
113
+ # $ twc storage list #
114
+ # ------------------------------------------------------------- #
115
+
116
+
117
+ def print_buckets(response: object, filters: str):
118
+ if filters:
119
+ buckets = fmt.filter_list(response.json()["buckets"], filters)
120
+ else:
121
+ buckets = response.json()["buckets"]
122
+
123
+ table = fmt.Table()
124
+ table.header(
125
+ [
126
+ "ID",
127
+ "NAME",
128
+ "REGION",
129
+ "STATUS",
130
+ "TYPE",
131
+ ]
132
+ )
133
+ for bucket in buckets:
134
+ table.row(
135
+ [
136
+ bucket["id"],
137
+ bucket["name"],
138
+ bucket["location"],
139
+ bucket["status"],
140
+ bucket["type"],
141
+ ]
142
+ )
143
+ table.print()
144
+
145
+
146
+ @storage.command("list", aliases=["ls"], help="List buckets.")
147
+ @options(GLOBAL_OPTIONS)
148
+ @options(OUTPUT_FORMAT_OPTION)
149
+ @click.option("--filter", "-f", "filters", default="", help="Filter output.")
150
+ def storage_list(config, profile, verbose, output_format, filters):
151
+ client = create_client(config, profile)
152
+ response = _storage_list(client)
153
+ fmt.printer(
154
+ response,
155
+ output_format=output_format,
156
+ filters=filters,
157
+ func=print_buckets,
158
+ )
159
+
160
+
161
+ # ------------------------------------------------------------- #
162
+ # $ twc storage list-presets #
163
+ # ------------------------------------------------------------- #
164
+
165
+
166
+ def print_storage_presets(response: object, filters: str):
167
+ if filters:
168
+ presets = fmt.filter_list(response.json()["storages_presets"], filters)
169
+ else:
170
+ presets = response.json()["storages_presets"]
171
+
172
+ table = fmt.Table()
173
+ table.header(
174
+ [
175
+ "ID",
176
+ "REGION",
177
+ "PRICE",
178
+ "DISK",
179
+ ]
180
+ )
181
+ for preset in presets:
182
+ table.row(
183
+ [
184
+ preset["id"],
185
+ preset["location"],
186
+ preset["price"],
187
+ str(round(preset["disk"] / 1024)) + "G",
188
+ ]
189
+ )
190
+ table.print()
191
+
192
+
193
+ @storage.command(
194
+ "list-presets", aliases=["lp"], help="List Object Storage presets."
195
+ )
196
+ @options(GLOBAL_OPTIONS)
197
+ @options(OUTPUT_FORMAT_OPTION)
198
+ @click.option("--filter", "-f", "filters", default="", help="Filter output.")
199
+ @click.option("--region", help="Use region (location).")
200
+ def storage_list_presets(
201
+ config, profile, verbose, output_format, filters, region
202
+ ):
203
+ if filters:
204
+ filters = filters.replace("region", "location")
205
+ if region:
206
+ if filters:
207
+ filters = filters + f",location:{region}"
208
+ else:
209
+ filters = f"location:{region}"
210
+
211
+ client = create_client(config, profile)
212
+ response = _storage_list_presets(client)
213
+ fmt.printer(
214
+ response,
215
+ output_format=output_format,
216
+ filters=filters,
217
+ func=print_storage_presets,
218
+ )
219
+
220
+
221
+ # ------------------------------------------------------------- #
222
+ # $ twc storage mb #
223
+ # ------------------------------------------------------------- #
224
+
225
+
226
+ @storage.command("mb", help="Make bucket.")
227
+ @options(GLOBAL_OPTIONS)
228
+ @options(OUTPUT_FORMAT_OPTION)
229
+ @click.option("--preset-id", type=int, required=True, help="Bucket preset ID.")
230
+ @click.option(
231
+ "--project-id",
232
+ type=int,
233
+ default=None,
234
+ envvar="TWC_PROJECT",
235
+ callback=set_value_from_config,
236
+ help="Add bucket to specific project.",
237
+ )
238
+ @click.option(
239
+ "--type",
240
+ "bucket_type",
241
+ type=click.Choice(["public", "private"]),
242
+ default="private",
243
+ show_default=True,
244
+ help="Bucket access policy.",
245
+ )
246
+ @click.argument("bucket_name", type=str, required=True)
247
+ def storage_mb(
248
+ config,
249
+ profile,
250
+ verbose,
251
+ output_format,
252
+ preset_id,
253
+ project_id,
254
+ bucket_type,
255
+ bucket_name,
256
+ ):
257
+ # pylint: disable=too-many-locals
258
+
259
+ client = create_client(config, profile)
260
+ is_public = False
261
+ if bucket_type == "public":
262
+ is_public = True
263
+
264
+ if project_id:
265
+ debug("Check project_id")
266
+ projects = _project_list(client).json()["projects"]
267
+ if not project_id in [prj["id"] for prj in projects]:
268
+ raise click.BadParameter("Wrong project ID.")
269
+
270
+ debug(f"Create bucket 'NAME-PREFIX-{bucket_name}'")
271
+ response = _storage_mb(
272
+ client, name=bucket_name, preset_id=preset_id, is_public=is_public
273
+ )
274
+
275
+ # Add created bucket to project if set
276
+ if project_id:
277
+ src_project = get_default_project_id(client)
278
+ # Make useless request to avoid API bug (409 resource_not_found)
279
+ _r = _project_resource_list_buckets(client, src_project)
280
+ new_bucket_id = response.json()["bucket"]["id"]
281
+ debug(f"Add bucket '{new_bucket_id}' to project '{project_id}'")
282
+ project_resp = _project_resource_move(
283
+ client,
284
+ from_project=src_project,
285
+ to_project=project_id,
286
+ resource_id=new_bucket_id,
287
+ resource_type="storage",
288
+ )
289
+ debug(project_resp.text)
290
+
291
+ fmt.printer(
292
+ response,
293
+ output_format=output_format,
294
+ func=lambda response: click.echo(response.json()["bucket"]["name"]),
295
+ )
296
+
297
+
298
+ # ------------------------------------------------------------- #
299
+ # $ twc storage rb #
300
+ # ------------------------------------------------------------- #
301
+
302
+
303
+ def get_bucket_id_by_name(client, bucket_name: str):
304
+ debug(f"Get bucket_id by name '{bucket_name}'")
305
+ buckets = _storage_list(client).json()["buckets"]
306
+ for bucket in buckets:
307
+ if bucket["name"] == bucket_name:
308
+ return bucket["id"]
309
+ if str(bucket["id"]) == bucket_name:
310
+ return bucket["id"]
311
+ return None
312
+
313
+
314
+ @storage.command("rb", help="Remove bucket.")
315
+ @options(GLOBAL_OPTIONS)
316
+ @click.confirmation_option(prompt="This action cannot be undone. Continue?")
317
+ @click.argument("buckets", nargs=-1, required=True)
318
+ def storage_rb(config, profile, verbose, buckets):
319
+ client = create_client(config, profile)
320
+ for bucket in buckets:
321
+ bucket_id = get_bucket_id_by_name(client, bucket)
322
+ if not bucket_id:
323
+ sys.exit(f"Error: Bucket '{bucket}' not found.")
324
+
325
+ response = _storage_rb(client, bucket_id)
326
+
327
+ if response.status_code == 200:
328
+ del_hash = response.json()["bucket_delete"]["hash"]
329
+ del_code = click.prompt("Please enter confirmation code", type=int)
330
+ response = _storage_rb(
331
+ client, bucket_id, delete_hash=del_hash, code=del_code
332
+ )
333
+ if response.status_code == 204:
334
+ click.echo(bucket)
335
+ else:
336
+ fmt.printer(response)
337
+
338
+
339
+ # ------------------------------------------------------------- #
340
+ # $ twc storage set #
341
+ # ------------------------------------------------------------- #
342
+
343
+
344
+ @storage.command("set", help="Set bucket parameters and properties.")
345
+ @options(GLOBAL_OPTIONS)
346
+ @options(OUTPUT_FORMAT_OPTION)
347
+ @click.option("--preset-id", type=int, help="Bucket preset ID.")
348
+ @click.option(
349
+ "--type",
350
+ "bucket_type",
351
+ type=click.Choice(["public", "private"]),
352
+ default=None,
353
+ help="Bucket access policy.",
354
+ )
355
+ @click.argument("bucket", required=True)
356
+ def storage_set(
357
+ config,
358
+ profile,
359
+ verbose,
360
+ output_format,
361
+ preset_id,
362
+ bucket_type,
363
+ bucket,
364
+ ):
365
+ client = create_client(config, profile)
366
+
367
+ bucket_id = get_bucket_id_by_name(client, bucket)
368
+ if not bucket_id:
369
+ sys.exit(f"Error: Bucket '{bucket}' not found.")
370
+
371
+ payload = {}
372
+
373
+ if preset_id:
374
+ payload["preset_id"] = preset_id
375
+
376
+ if bucket_type:
377
+ if bucket_type == "public":
378
+ payload["is_public"] = True
379
+ else:
380
+ payload["is_public"] = False
381
+
382
+ response = _storage_set(client, bucket_id, **payload)
383
+
384
+ fmt.printer(
385
+ response,
386
+ output_format=output_format,
387
+ func=lambda response: click.echo(response.json()["bucket"]["name"]),
388
+ )
389
+
390
+
391
+ # ------------------------------------------------------------- #
392
+ # $ twc storage user #
393
+ # ------------------------------------------------------------- #
394
+
395
+
396
+ @storage.group("user", cls=ClickAliasedGroup)
397
+ @options(GLOBAL_OPTIONS[:2])
398
+ def storage_user():
399
+ """Manage Object Storage users."""
400
+
401
+
402
+ # ------------------------------------------------------------- #
403
+ # $ twc storage user list #
404
+ # ------------------------------------------------------------- #
405
+
406
+
407
+ def print_storage_users(response: object):
408
+ users = response.json()["users"]
409
+
410
+ table = fmt.Table()
411
+ table.header(
412
+ [
413
+ "ID",
414
+ "ACCESS KEY",
415
+ ]
416
+ )
417
+ for user in users:
418
+ table.row(
419
+ [
420
+ user["id"],
421
+ user["access_key"],
422
+ ]
423
+ )
424
+ table.print()
425
+
426
+
427
+ @storage_user.command("list", aliases=["ls"], help="List storage users.")
428
+ @options(GLOBAL_OPTIONS)
429
+ @options(OUTPUT_FORMAT_OPTION)
430
+ def storage_user_list(config, profile, verbose, output_format):
431
+ client = create_client(config, profile)
432
+ response = _storage_user_list(client)
433
+ fmt.printer(
434
+ response,
435
+ output_format=output_format,
436
+ func=print_storage_users,
437
+ )
438
+
439
+
440
+ # ------------------------------------------------------------- #
441
+ # $ twc storage user passwd #
442
+ # ------------------------------------------------------------- #
443
+
444
+
445
+ def get_storage_user(client, access_key: str = None):
446
+ """Return user_id and access_key. If customer have only one storage
447
+ user on account `access_key` is optional, user_id and access_key will
448
+ taken from user info. If customer have multile storage users `access_key`
449
+ is required.
450
+ """
451
+ users = _storage_user_list(client).json()
452
+ user_id = None
453
+
454
+ # If access_key argument is set, set effective access key
455
+ if users["meta"]["total"] == 1:
456
+ user_id = users["users"][0]["id"]
457
+ access_key = users["users"][0]["access_key"]
458
+ else:
459
+ if access_key:
460
+ for user in users["users"]:
461
+ if user["access_key"] == access_key:
462
+ user_id = user["id"]
463
+ return user_id, access_key
464
+
465
+
466
+ @storage_user.command("passwd", help="Set new secret_key for storage user.")
467
+ @options(GLOBAL_OPTIONS)
468
+ @options(OUTPUT_FORMAT_OPTION)
469
+ @click.option(
470
+ "--secret-key", prompt=True, hide_input=True, confirmation_prompt=True
471
+ )
472
+ @click.argument("access_key", metavar="[ACCESS_KEY]", nargs=-1, type=str)
473
+ def storage_user_passwd(
474
+ config, profile, verbose, output_format, secret_key, access_key
475
+ ):
476
+ client = create_client(config, profile)
477
+ if access_key:
478
+ access_key = list(access_key)[0] # get element from tuple
479
+ user_id, access_key = get_storage_user(client, access_key)
480
+ if not user_id:
481
+ sys.exit(f"User with access key '{access_key}' not found.")
482
+ debug(f"User ID is '{user_id}'")
483
+ debug(f"Change secret_key for '{user_id}'")
484
+ response = _storage_user_passwd(
485
+ client, user_id=user_id, secret_key=secret_key
486
+ )
487
+
488
+ fmt.printer(
489
+ response,
490
+ output_format=output_format,
491
+ func=lambda response: click.echo(
492
+ response.json()["user"]["access_key"]
493
+ ),
494
+ )
495
+
496
+
497
+ # ------------------------------------------------------------- #
498
+ # $ twc storage transfer #
499
+ # ------------------------------------------------------------- #
500
+
501
+
502
+ @storage.group(
503
+ "transfer",
504
+ short_help="File transfer between object storage buckets.",
505
+ hidden=True,
506
+ )
507
+ @options(GLOBAL_OPTIONS[:2])
508
+ def storage_transfer():
509
+ """File transfer between object storage buckets.
510
+
511
+ You can start file transfer from any S3-compatible object storage
512
+ (including Timeweb Cloud Object Storage) to specified destination
513
+ bucket.
514
+
515
+ WARNING: This feature have unstable API, may occur errors.
516
+ """
517
+
518
+
519
+ # ------------------------------------------------------------- #
520
+ # $ twc storage transfer new #
521
+ # ------------------------------------------------------------- #
522
+
523
+
524
+ @storage_transfer.command("new", help="Start new file tranfer.")
525
+ @options(GLOBAL_OPTIONS)
526
+ @click.option(
527
+ "--bucket", "src_bucket", required=True, help="Source bucket name."
528
+ )
529
+ @click.option("--access-key", required=True, help="Source bucket access key.")
530
+ @click.option("--secret-key", required=True, help="Source bucket secret key.")
531
+ @click.option("--region", default="", help="Source region.")
532
+ @click.option("--endpoint", default=None, help="Source storage endpoint.")
533
+ @click.option(
534
+ "--force-path-style", is_flag=True, help="Force path-style bucket address."
535
+ )
536
+ @click.argument("dst_bucket", required=True)
537
+ def storage_transfer_new(
538
+ config,
539
+ profile,
540
+ verbose,
541
+ access_key,
542
+ secret_key,
543
+ region,
544
+ endpoint,
545
+ force_path_style,
546
+ src_bucket,
547
+ dst_bucket,
548
+ ):
549
+ client = create_client(config, profile)
550
+ response = _storage_transfer_new(
551
+ client,
552
+ src_bucket=src_bucket,
553
+ dst_bucket=dst_bucket,
554
+ access_key=access_key,
555
+ secret_key=secret_key,
556
+ endpoint=endpoint,
557
+ location=region,
558
+ force_path_style=force_path_style,
559
+ )
560
+ if response.status_code == 204:
561
+ click.echo(dst_bucket)
562
+ else:
563
+ fmt.printer(response)
564
+
565
+
566
+ # ------------------------------------------------------------- #
567
+ # $ twc storage transfer status #
568
+ # ------------------------------------------------------------- #
569
+
570
+
571
+ def print_transfer_status(response):
572
+ transfer = response.json()["transfer_status"]
573
+
574
+ table = fmt.Table()
575
+ translated_keys = {
576
+ "status": "Status",
577
+ "tries": "Tries",
578
+ "total_count": "Total objects",
579
+ "total_size": "Total size",
580
+ "uploaded_count": "Uploaded objects",
581
+ "uploaded_size": "Uploaded (size)",
582
+ "errors": "Errors",
583
+ }
584
+ for key in transfer.keys():
585
+ table.row([translated_keys[key], ":", transfer[key]])
586
+ table.print()
587
+
588
+
589
+ @storage_transfer.command("status", help="Display file tranfer status.")
590
+ @options(GLOBAL_OPTIONS)
591
+ @options(OUTPUT_FORMAT_OPTION)
592
+ @click.argument("bucket", required=True)
593
+ def storage_transfer_status(config, profile, verbose, output_format, bucket):
594
+ client = create_client(config, profile)
595
+ bucket_id = get_bucket_id_by_name(client, bucket)
596
+ response = _storage_transfer_status(client, bucket_id)
597
+ fmt.printer(
598
+ response,
599
+ output_format=output_format,
600
+ func=print_transfer_status,
601
+ )
602
+
603
+
604
+ # ------------------------------------------------------------- #
605
+ # $ twc storage subdomain #
606
+ # ------------------------------------------------------------- #
607
+
608
+
609
+ @storage.group("subdomain", cls=ClickAliasedGroup)
610
+ @options(GLOBAL_OPTIONS[:2])
611
+ def storage_subdomain():
612
+ """Manage subdomains."""
613
+
614
+
615
+ # ------------------------------------------------------------- #
616
+ # $ twc storage subdomain list #
617
+ # ------------------------------------------------------------- #
618
+
619
+
620
+ def print_subdomains(response):
621
+ subdomains = response.json()["subdomains"]
622
+ table = fmt.Table()
623
+ table.header(
624
+ [
625
+ "ID",
626
+ "SUBDOMAIN",
627
+ "CERT RELEASED",
628
+ "STATUS",
629
+ ]
630
+ )
631
+ for sub in subdomains:
632
+ table.row(
633
+ [
634
+ sub["id"],
635
+ sub["subdomain"],
636
+ sub["cert_released"],
637
+ sub["status"],
638
+ ]
639
+ )
640
+ table.print()
641
+
642
+
643
+ @storage_subdomain.command(
644
+ "list", aliases=["ls"], help="List subdomains attached to bucket."
645
+ )
646
+ @options(GLOBAL_OPTIONS)
647
+ @options(OUTPUT_FORMAT_OPTION)
648
+ @click.argument("bucket", required=True)
649
+ def storage_subdomain_list(config, profile, verbose, output_format, bucket):
650
+ client = create_client(config, profile)
651
+ bucket_id = get_bucket_id_by_name(client, bucket)
652
+ debug(f"bucket_id {bucket_id}")
653
+ response = _storage_subdomain_list(client, bucket_id)
654
+ fmt.printer(
655
+ response,
656
+ output_format=output_format,
657
+ func=print_subdomains,
658
+ )
659
+
660
+
661
+ # ------------------------------------------------------------- #
662
+ # $ twc storage subdomain add #
663
+ # ------------------------------------------------------------- #
664
+
665
+
666
+ def print_subdomains_state(response):
667
+ subdomains = response.json()["subdomains"]
668
+ table = fmt.Table()
669
+ table.header(
670
+ [
671
+ "SUBDOMAIN",
672
+ "STATUS",
673
+ ]
674
+ )
675
+ for sub in subdomains:
676
+ table.row(
677
+ [
678
+ sub["subdomain"],
679
+ sub["status"],
680
+ ]
681
+ )
682
+ table.print()
683
+
684
+
685
+ @storage_subdomain.command("add", help="Attach subdomains to bucket.")
686
+ @options(GLOBAL_OPTIONS)
687
+ @options(OUTPUT_FORMAT_OPTION)
688
+ @click.argument("subdomains", nargs=-1, required=True)
689
+ @click.argument("bucket", required=True)
690
+ def storage_subdomain_add(
691
+ config, profile, verbose, output_format, bucket, subdomains
692
+ ):
693
+ client = create_client(config, profile)
694
+ bucket_id = get_bucket_id_by_name(client, bucket)
695
+ response = _storage_subdomain_add(client, bucket_id, list(subdomains))
696
+ fmt.printer(
697
+ response,
698
+ output_format=output_format,
699
+ func=print_subdomains_state,
700
+ )
701
+
702
+
703
+ # ------------------------------------------------------------- #
704
+ # $ twc storage subdomain remove #
705
+ # ------------------------------------------------------------- #
706
+
707
+
708
+ @storage_subdomain.command("remove", aliases=["rm"], help="Remove subdomains.")
709
+ @options(GLOBAL_OPTIONS)
710
+ @options(OUTPUT_FORMAT_OPTION)
711
+ @click.confirmation_option(prompt="Subdomains will be deleted. Continue?")
712
+ @click.argument("bucket", required=True)
713
+ @click.argument("subdomains", nargs=-1, required=True)
714
+ def storage_subdomain_remove(
715
+ config, profile, verbose, output_format, bucket, subdomains
716
+ ):
717
+ client = create_client(config, profile)
718
+ bucket_id = get_bucket_id_by_name(client, bucket)
719
+ response = _storage_subdomain_remove(client, bucket_id, list(subdomains))
720
+ fmt.printer(
721
+ response,
722
+ output_format=output_format,
723
+ func=print_subdomains_state,
724
+ )
725
+
726
+
727
+ # ------------------------------------------------------------- #
728
+ # $ twc storage subdomain gencert #
729
+ # ------------------------------------------------------------- #
730
+
731
+
732
+ @storage_subdomain.command(
733
+ "gencert", help="Request TLS certificate for subdomains."
734
+ )
735
+ @options(GLOBAL_OPTIONS)
736
+ @click.argument("subdomains", nargs=-1, required=True)
737
+ def storage_subdomain_gencert(config, profile, verbose, subdomains):
738
+ client = create_client(config, profile)
739
+ for subdomain in subdomains:
740
+ response = _storage_subdomain_gencert(client, subdomain)
741
+ if response.status_code == 201:
742
+ click.echo(subdomain)
743
+ else:
744
+ fmt.printer(response)
745
+
746
+
747
+ # ------------------------------------------------------------- #
748
+ # $ twc storage genconfig #
749
+ # ------------------------------------------------------------- #
750
+
751
+
752
+ S3CMD_CONFIG_TEMPLATE = """
753
+ [default]
754
+ access_key = {access_key}
755
+ secret_key = {secret_key}
756
+ bucket_location = ru-1
757
+ host_base = {endpoint}
758
+ host_bucket = {endpoint}
759
+ use_https = True
760
+ """
761
+
762
+ RCLONE_CONFIG_TEMPLATE = """
763
+ [twc]
764
+ type = s3
765
+ provider = Other
766
+ env_auth = false
767
+ access_key_id = {access_key}
768
+ secret_access_key = {secret_key}
769
+ region = ru-1
770
+ endpoint = https://{endpoint}
771
+ """
772
+
773
+
774
+ @storage.command("genconfig", help="Generate config file for S3 clients.")
775
+ @options(GLOBAL_OPTIONS)
776
+ @click.option("--user-id", type=int, help="Object Storage user ID.")
777
+ @click.option(
778
+ "--client",
779
+ "s3_client",
780
+ type=click.Choice(["s3cmd", "rclone"]),
781
+ required=True,
782
+ help="S3 client.",
783
+ )
784
+ @click.option(
785
+ "--save-to", help="Path to file. NOTE: Existing file will be overwitten."
786
+ )
787
+ def storage_genconfig(config, profile, verbose, user_id, s3_client, save_to):
788
+ client = create_client(config, profile)
789
+
790
+ # Get access_key and secret_key by user_id (or not)
791
+ storage_users = _storage_user_list(client).json()["users"]
792
+ if user_id:
793
+ for user in storage_users:
794
+ if user_id == user["id"]:
795
+ access_key = user["access_key"]
796
+ secret_key = user[user_id]["secret_key"]
797
+ else:
798
+ sys.exit(f"Error: user with ID '{user_id}' not found.")
799
+ else:
800
+ user_id, access_key = get_storage_user(client)
801
+ secret_key = storage_users[0]["secret_key"]
802
+
803
+ templates = {
804
+ "s3cmd": S3CMD_CONFIG_TEMPLATE.strip(),
805
+ "rclone": RCLONE_CONFIG_TEMPLATE.strip(),
806
+ }
807
+
808
+ file_content = templates[s3_client].format(
809
+ access_key=access_key,
810
+ secret_key=secret_key,
811
+ endpoint=TWC_S3_ENDPOINT,
812
+ )
813
+
814
+ if save_to:
815
+ with open(save_to, "w", encoding="utf-8") as s3_config:
816
+ s3_config.write(file_content)
817
+ else:
818
+ fmt.print_colored(file_content, lang="ini")