vgs-cli 1.35.0.dev1__py3-none-any.whl → 1.35.0.dev2__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.
- vgs_cli-1.35.0.dev2.data/data/vgscli/vgs-cli.yaml +17 -0
- {vgs_cli-1.35.0.dev1.dist-info → vgs_cli-1.35.0.dev2.dist-info}/METADATA +12 -4
- {vgs_cli-1.35.0.dev1.dist-info → vgs_cli-1.35.0.dev2.dist-info}/RECORD +32 -30
- vgscli/_version.py +1 -1
- vgscli/cli/commands/apply.py +60 -75
- vgscli/cli/commands/generate.py +1 -0
- vgscli/cli_progress.py +3 -17
- vgscli/click_extensions.py +1 -1
- vgscli/errors.py +18 -3
- vgscli/resource-templates/service-account/vgs-cli.yaml +17 -0
- vgscli/tenant_creation.py +24 -11
- {vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/collect-form-api-schema.yaml +0 -0
- {vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/collect-form-schema.yaml +0 -0
- {vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/credentials-admin.yaml +0 -0
- {vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/form-template.yaml +0 -0
- {vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/http-route-template.yaml +0 -0
- {vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/notification-integration-schema.yaml +0 -0
- {vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/notification-template.yaml +0 -0
- {vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/public-credential-collect.yaml +0 -0
- {vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/read-credentials-no-pci.yaml +0 -0
- {vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/read-credentials-with-pci.yaml +0 -0
- {vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/service-account-schema.yaml +0 -0
- {vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/sftp-route-template.yaml +0 -0
- {vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/tenant-schema.yaml +0 -0
- {vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/tenant-template.yaml +0 -0
- {vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/vault-resources.yaml +0 -0
- {vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/vault-schema.yaml +0 -0
- {vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/vault-template.yaml +0 -0
- {vgs_cli-1.35.0.dev1.dist-info → vgs_cli-1.35.0.dev2.dist-info}/WHEEL +0 -0
- {vgs_cli-1.35.0.dev1.dist-info → vgs_cli-1.35.0.dev2.dist-info}/entry_points.txt +0 -0
- {vgs_cli-1.35.0.dev1.dist-info → vgs_cli-1.35.0.dev2.dist-info}/licenses/LICENSE +0 -0
- {vgs_cli-1.35.0.dev1.dist-info → vgs_cli-1.35.0.dev2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
apiVersion: 1.0.0
|
|
2
|
+
kind: ServiceAccount
|
|
3
|
+
data:
|
|
4
|
+
name: vgs-cli
|
|
5
|
+
{%- if vaults | length == 0 %}
|
|
6
|
+
{{- cli_warn("Service Account won't have access to any tenants inside the organization. If it needs tenant access, use --tenant <TENANT_ID>.") or "" }}
|
|
7
|
+
{%- else %}
|
|
8
|
+
vaults:
|
|
9
|
+
{%- for vault_id in vaults %}
|
|
10
|
+
- {{ vault_id }}
|
|
11
|
+
{%- endfor %}
|
|
12
|
+
{%- endif %}
|
|
13
|
+
scopes:
|
|
14
|
+
- name: access-logs:read
|
|
15
|
+
- name: organizations:read
|
|
16
|
+
- name: routes:write
|
|
17
|
+
- name: vaults:write
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vgs-cli
|
|
3
|
-
Version: 1.35.0.
|
|
3
|
+
Version: 1.35.0.dev2
|
|
4
4
|
Summary: VGS Client
|
|
5
5
|
Home-page: https://github.com/verygoodsecurity/vgs-cli
|
|
6
6
|
Author: Very Good Security
|
|
@@ -200,9 +200,16 @@ access. The CLI masks generated webhook secrets in its output.
|
|
|
200
200
|
|
|
201
201
|
### Service accounts
|
|
202
202
|
|
|
203
|
-
Generate a service-account resource
|
|
204
|
-
|
|
205
|
-
|
|
203
|
+
Generate a service-account resource and save the YAML so you can review it
|
|
204
|
+
before applying it. The general `vgs-cli` template accepts zero or more tenants;
|
|
205
|
+
when no tenant is supplied, the generated account has no tenant access.
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
vgs generate service-account --template vgs-cli -T <TENANT_ID> > service-account.yaml
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Payment-credential templates require exactly one tenant and a name containing
|
|
212
|
+
1–20 characters:
|
|
206
213
|
|
|
207
214
|
```bash
|
|
208
215
|
vgs generate service-account --template <TEMPLATE> -T <TENANT_ID> --var name=<NAME> > service-account.yaml
|
|
@@ -210,6 +217,7 @@ vgs generate service-account --template <TEMPLATE> -T <TENANT_ID> --var name=<NA
|
|
|
210
217
|
|
|
211
218
|
| Template | Purpose |
|
|
212
219
|
| --- | --- |
|
|
220
|
+
| `vgs-cli` | Automate common CLI organization, tenant, route, and access-log operations. |
|
|
213
221
|
| `public-credential-collect` | Collect payment credentials without read access. |
|
|
214
222
|
| `read-credentials-no-pci` | Read tokenized credentials without PCI data. |
|
|
215
223
|
| `read-credentials-with-pci` | Read credentials including PCI data. |
|
|
@@ -1,23 +1,24 @@
|
|
|
1
|
-
vgs_cli-1.35.0.
|
|
2
|
-
vgs_cli-1.35.0.
|
|
3
|
-
vgs_cli-1.35.0.
|
|
4
|
-
vgs_cli-1.35.0.
|
|
5
|
-
vgs_cli-1.35.0.
|
|
6
|
-
vgs_cli-1.35.0.
|
|
7
|
-
vgs_cli-1.35.0.
|
|
8
|
-
vgs_cli-1.35.0.
|
|
9
|
-
vgs_cli-1.35.0.
|
|
10
|
-
vgs_cli-1.35.0.
|
|
11
|
-
vgs_cli-1.35.0.
|
|
12
|
-
vgs_cli-1.35.0.
|
|
13
|
-
vgs_cli-1.35.0.
|
|
14
|
-
vgs_cli-1.35.0.
|
|
15
|
-
vgs_cli-1.35.0.
|
|
16
|
-
vgs_cli-1.35.0.
|
|
17
|
-
vgs_cli-1.35.0.
|
|
18
|
-
vgs_cli-1.35.0.
|
|
1
|
+
vgs_cli-1.35.0.dev2.data/data/vgscli/collect-form-api-schema.yaml,sha256=THuJbLUo7LOr0c1JUo3FPJEc9Jvde59K4JjdlkVF3hw,1246
|
|
2
|
+
vgs_cli-1.35.0.dev2.data/data/vgscli/collect-form-schema.yaml,sha256=vHAN6_Gsg14CeSL41sHEQgboYF-CHF2RaNw72aJ3W8o,1337
|
|
3
|
+
vgs_cli-1.35.0.dev2.data/data/vgscli/credentials-admin.yaml,sha256=acyLd54Cp7l2fpugfjVfYh07-MIE0BX-JVq4lHraE1s,869
|
|
4
|
+
vgs_cli-1.35.0.dev2.data/data/vgscli/form-template.yaml,sha256=VblOlsDLfwZP6wR6nIIX1WSrUAt4leqfMdbXr3xYvWg,934
|
|
5
|
+
vgs_cli-1.35.0.dev2.data/data/vgscli/http-route-template.yaml,sha256=vGHvBXA_6pwFtH1vVRWKiwxb4sahzuLvpqGAlavrStc,1085
|
|
6
|
+
vgs_cli-1.35.0.dev2.data/data/vgscli/notification-integration-schema.yaml,sha256=1qHzS5ZxlhTdrDab6-qJhszwu7cpith2HleX3uAO_4E,1837
|
|
7
|
+
vgs_cli-1.35.0.dev2.data/data/vgscli/notification-template.yaml,sha256=hr36msbrgiTjWligLHVDHMmO49QDzjTwBSLYoBw3WQE,1288
|
|
8
|
+
vgs_cli-1.35.0.dev2.data/data/vgscli/public-credential-collect.yaml,sha256=CiorC-6TMx7M0TI8DHmoRSfVKDpOom2wIr_ilwmHQNA,696
|
|
9
|
+
vgs_cli-1.35.0.dev2.data/data/vgscli/read-credentials-no-pci.yaml,sha256=_kWRne_ARh8v7A_jHG08gwgPG6NqZhrDRyq6L-ULs2M,769
|
|
10
|
+
vgs_cli-1.35.0.dev2.data/data/vgscli/read-credentials-with-pci.yaml,sha256=jWXgFdcqZYisakAt9COM6cjf2-x_Q4eidDLaUh8CZOk,796
|
|
11
|
+
vgs_cli-1.35.0.dev2.data/data/vgscli/service-account-schema.yaml,sha256=L1pjNXgnks15N1wNgf_1GmGcOvxn0nu49v7ersUjFno,1090
|
|
12
|
+
vgs_cli-1.35.0.dev2.data/data/vgscli/sftp-route-template.yaml,sha256=zkT6lIVDgVyzLKgZ8QnyJJooy2egZk1cQthdjan_JrM,853
|
|
13
|
+
vgs_cli-1.35.0.dev2.data/data/vgscli/tenant-schema.yaml,sha256=gBO7xVe2qXSymG0B_45hC11X5nm_jCCAH4Yjs-sbC8g,1457
|
|
14
|
+
vgs_cli-1.35.0.dev2.data/data/vgscli/tenant-template.yaml,sha256=F0wIBkC2dFJUNeJet-FzPX3qrLMHcGIaJvhsDzzAOCc,765
|
|
15
|
+
vgs_cli-1.35.0.dev2.data/data/vgscli/vault-resources.yaml,sha256=95CCxQher3MBQq96bvjZviNgo1rgv_NhCtI8X-oxIpA,26335
|
|
16
|
+
vgs_cli-1.35.0.dev2.data/data/vgscli/vault-schema.yaml,sha256=a-wW7X8RyHr-669wtOZrlxlIfeQVtx8EUfA-3vpYytk,637
|
|
17
|
+
vgs_cli-1.35.0.dev2.data/data/vgscli/vault-template.yaml,sha256=IlDisL3H5LXUDMkhUaouKMarRdu0-pJxpTz8ij_lp_M,383
|
|
18
|
+
vgs_cli-1.35.0.dev2.data/data/vgscli/vgs-cli.yaml,sha256=5gCjjloyepaKI6zZfDLeUEWxuRExI-dGJI9RO3omP2k,484
|
|
19
|
+
vgs_cli-1.35.0.dev2.dist-info/licenses/LICENSE,sha256=NwSyzgDYdzv6O2ns2C6UODGI8LN1VnVe_FPTnWS4eXY,1082
|
|
19
20
|
vgscli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
-
vgscli/_version.py,sha256=
|
|
21
|
+
vgscli/_version.py,sha256=0HHI66nZPGvwbIf40mXNC7yUBONlS3ZLuT9MVF9ALCM,1681
|
|
21
22
|
vgscli/access_logs.py,sha256=G0GjlLjWiC-N6OzK6-RCvWddcVGQlLCLAcyO9pLWIOM,1928
|
|
22
23
|
vgscli/audits_api.py,sha256=6FjpS1yTALio-DejgIS2eDw75ChjDp4GoAGXh4uZExU,3211
|
|
23
24
|
vgscli/auth.py,sha256=vdUZ12nqtefIuSHNmwCFvxoqwS3m6ZP5TqLZFNwKnSU,2104
|
|
@@ -26,13 +27,13 @@ vgscli/auth_utils.py,sha256=aC1VI0dFdFeupLS9Okz4uDz-wHhhFabqiLUg2R5vO2s,600
|
|
|
26
27
|
vgscli/callback_server.py,sha256=oFFF5AGbbwhF7Hsc5fzS7jeGFzIMbebc9Qdq8uEl1PY,1055
|
|
27
28
|
vgscli/calm_api.py,sha256=5ZZhJUCuA2E5X670L-EkB_mL2kqHPrYy-ca6WtwTacg,1384
|
|
28
29
|
vgscli/cert_manager_api.py,sha256=ek25BcyGeUo0pBoqineMTYMgmQaYJfimFFHMljZh5yA,1646
|
|
29
|
-
vgscli/cli_progress.py,sha256=
|
|
30
|
+
vgscli/cli_progress.py,sha256=Kpc7iZo0zO6YX3WynteBELuWSYGVvqEjKLq2E5x4lnE,2520
|
|
30
31
|
vgscli/cli_utils.py,sha256=NzXK29_yIDrB-3T_XdLLB8NCWneQ-cpZFi9mazQL9tQ,3810
|
|
31
|
-
vgscli/click_extensions.py,sha256=
|
|
32
|
+
vgscli/click_extensions.py,sha256=m-RpaPaH69m4caYbuiu0j2blxeyy8c8hA0Tvygw1Mvc,7271
|
|
32
33
|
vgscli/collect_forms.py,sha256=HAg-tvpXxmVq5Zns4v8G0cP-tgYKuWmVFwzuNjieu-A,4619
|
|
33
34
|
vgscli/collect_forms_api.py,sha256=JHnnsOUyStD7yR92LfGqn8DOpnu0l0yqTUMQnWE_NGs,1246
|
|
34
35
|
vgscli/config_file.py,sha256=HB8lANU0qoO09Wnkf-QDzPh2qtrnFcqcetnCaQgtQ3U,1543
|
|
35
|
-
vgscli/errors.py,sha256=
|
|
36
|
+
vgscli/errors.py,sha256=9iWS8v6Zq9A3Iqt3G1-NJQEwT4d8DI_JoU7fdiqHg00,11946
|
|
36
37
|
vgscli/file_token_util.py,sha256=537b54paMGw0u5PL5VJgQqQ7awKa9lQP88n3tUc_lyw,771
|
|
37
38
|
vgscli/id_generator.py,sha256=3CbvSOUSyLON76p6M8wUhVQVig8j75_rnWdpxLhBifM,1510
|
|
38
39
|
vgscli/keyring_token_util.py,sha256=cnR72-0AInzujUsIwHoXwPbgprbkllgAXN-AAI3qsPM,4632
|
|
@@ -40,7 +41,7 @@ vgscli/notifications.py,sha256=6D8HFDEl3X4V0V9fYZT-9nWYe2KYVahfhRLMF0pbh4I,6252
|
|
|
40
41
|
vgscli/notifications_api.py,sha256=1EXmRb2fOJrU1QOAZ6gmq8yMjBLhNZGSVs3kRtD2Dfw,2342
|
|
41
42
|
vgscli/payment_accounts_api.py,sha256=wF6K3m7nlKtPmqO9RCpmGFSXYHWtwfhXqNfgI1IGqFI,1665
|
|
42
43
|
vgscli/tenant_configuration.py,sha256=iC33f2lBHDEL4oHPvdlWp_w--u2x_nWOb86jTHumDLg,5881
|
|
43
|
-
vgscli/tenant_creation.py,sha256=
|
|
44
|
+
vgscli/tenant_creation.py,sha256=9O1i4JsHuEIQt_QkcWYEcenLi8kGJBivcln6LkoHl7U,22230
|
|
44
45
|
vgscli/tenant_provisioning_api.py,sha256=-mQcSECgWG23LKczu1B9J1d7Bb6ZA6lkpr9RegJiiLg,1203
|
|
45
46
|
vgscli/testing.py,sha256=MLrsvy3mEwvjtqyuG0UjwEX-rDkbWUo52aXvu2H2sTk,1437
|
|
46
47
|
vgscli/text.py,sha256=c2d1As7awq8PlEF9NiI6JrjsYYousj9YT0o1xOQ2MQM,172
|
|
@@ -48,10 +49,10 @@ vgscli/token_handler.py,sha256=MvtmlicqnGrcopNkTr8Q2RlKE7IDj_sJFxF_39Jd7W8,183
|
|
|
48
49
|
vgscli/vgs.py,sha256=BiQuHeOuH25zbTdwNOsW_O69YuKsZFTYA2jr9JFRZng,6262
|
|
49
50
|
vgscli/cli/__init__.py,sha256=112ej_l1olouWMRIM8H4-s5C4qiG8-u6RziWT102wNk,2765
|
|
50
51
|
vgscli/cli/commands/__init__.py,sha256=8ptc9-Vzdx0HU3ZeMcKERYjmJyuB5IMjBSF3Igx5yQ4,206
|
|
51
|
-
vgscli/cli/commands/apply.py,sha256=
|
|
52
|
+
vgscli/cli/commands/apply.py,sha256=jLSllU1Hz8WX3ARL-lU8BtDv72bMzP3fXs0vGDtyoWc,26312
|
|
52
53
|
vgscli/cli/commands/certificate.py,sha256=xQV-G8ZWP9uPFqrczlCpCZkHUt2aJflBP2cc5hjobIw,22930
|
|
53
54
|
vgscli/cli/commands/delete.py,sha256=2nuYKhp0FhoGTcmJUECjRnlYrKLdAgIoIp-y25ViwDg,3873
|
|
54
|
-
vgscli/cli/commands/generate.py,sha256=
|
|
55
|
+
vgscli/cli/commands/generate.py,sha256=cVrU54_o3szgyi7tbMQtMAemx-37xYcFKrAcLcyMglw,5448
|
|
55
56
|
vgscli/cli/commands/get.py,sha256=Xm8bN11vOjnDA3o_uuAkBXM2wtJQZDrTdQY1ykug4LE,11315
|
|
56
57
|
vgscli/cli/types/__init__.py,sha256=a-P-SoGHpLzMg7CL1hhg5x_nf6sHq0qAuGm_AtIITmw,107
|
|
57
58
|
vgscli/cli/types/resource_id.py,sha256=7JILR-KjoVGQDHB40tXJfTcU53iR8wM9Zr24fY5le3o,1172
|
|
@@ -66,6 +67,7 @@ vgscli/resource-templates/service-account/credentials-admin.yaml,sha256=acyLd54C
|
|
|
66
67
|
vgscli/resource-templates/service-account/public-credential-collect.yaml,sha256=CiorC-6TMx7M0TI8DHmoRSfVKDpOom2wIr_ilwmHQNA,696
|
|
67
68
|
vgscli/resource-templates/service-account/read-credentials-no-pci.yaml,sha256=_kWRne_ARh8v7A_jHG08gwgPG6NqZhrDRyq6L-ULs2M,769
|
|
68
69
|
vgscli/resource-templates/service-account/read-credentials-with-pci.yaml,sha256=jWXgFdcqZYisakAt9COM6cjf2-x_Q4eidDLaUh8CZOk,796
|
|
70
|
+
vgscli/resource-templates/service-account/vgs-cli.yaml,sha256=5gCjjloyepaKI6zZfDLeUEWxuRExI-dGJI9RO3omP2k,484
|
|
69
71
|
vgscli/validation-schemas/collect-form-api-schema.yaml,sha256=THuJbLUo7LOr0c1JUo3FPJEc9Jvde59K4JjdlkVF3hw,1246
|
|
70
72
|
vgscli/validation-schemas/collect-form-schema.yaml,sha256=vHAN6_Gsg14CeSL41sHEQgboYF-CHF2RaNw72aJ3W8o,1337
|
|
71
73
|
vgscli/validation-schemas/notification-integration-schema.yaml,sha256=1qHzS5ZxlhTdrDab6-qJhszwu7cpith2HleX3uAO_4E,1837
|
|
@@ -73,8 +75,8 @@ vgscli/validation-schemas/service-account-schema.yaml,sha256=L1pjNXgnks15N1wNgf_
|
|
|
73
75
|
vgscli/validation-schemas/tenant-schema.yaml,sha256=gBO7xVe2qXSymG0B_45hC11X5nm_jCCAH4Yjs-sbC8g,1457
|
|
74
76
|
vgscli/validation-schemas/vault-resources.yaml,sha256=95CCxQher3MBQq96bvjZviNgo1rgv_NhCtI8X-oxIpA,26335
|
|
75
77
|
vgscli/validation-schemas/vault-schema.yaml,sha256=a-wW7X8RyHr-669wtOZrlxlIfeQVtx8EUfA-3vpYytk,637
|
|
76
|
-
vgs_cli-1.35.0.
|
|
77
|
-
vgs_cli-1.35.0.
|
|
78
|
-
vgs_cli-1.35.0.
|
|
79
|
-
vgs_cli-1.35.0.
|
|
80
|
-
vgs_cli-1.35.0.
|
|
78
|
+
vgs_cli-1.35.0.dev2.dist-info/METADATA,sha256=BgcCMIQRMIizLbofbdWzRQKrwq0V5iYsEfO_n22u24U,8984
|
|
79
|
+
vgs_cli-1.35.0.dev2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
80
|
+
vgs_cli-1.35.0.dev2.dist-info/entry_points.txt,sha256=wCWhRQiMX4Ja6kkqJlYr8LbuiywpmXyyRAZPsau-_RY,39
|
|
81
|
+
vgs_cli-1.35.0.dev2.dist-info/top_level.txt,sha256=gabTxCWMkl80q6ha_m-hs_1wQMfGntZcv5DEmkl8JEw,7
|
|
82
|
+
vgs_cli-1.35.0.dev2.dist-info/RECORD,,
|
vgscli/_version.py
CHANGED
vgscli/cli/commands/apply.py
CHANGED
|
@@ -62,26 +62,12 @@ from vgscli.tenant_creation import (
|
|
|
62
62
|
TenantCreationStageError,
|
|
63
63
|
TenantCreationState,
|
|
64
64
|
TenantCreator,
|
|
65
|
+
archive_state,
|
|
65
66
|
default_state_path,
|
|
66
67
|
)
|
|
67
68
|
|
|
68
69
|
logger = logging.getLogger()
|
|
69
70
|
|
|
70
|
-
TENANT_PROGRESS_LABELS = {
|
|
71
|
-
"environment": "Resolving environment",
|
|
72
|
-
"tenant": "Creating tenant",
|
|
73
|
-
"provisioning": "Waiting for provisioning",
|
|
74
|
-
"merchant": "Configuring merchant",
|
|
75
|
-
"account": "Creating payment account",
|
|
76
|
-
"account_configurations": "Applying account configurations",
|
|
77
|
-
"collect_form": "Creating default Collect Form",
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
TENANT_OPERATION_CHOICES = {
|
|
81
|
-
"1": "resume",
|
|
82
|
-
"2": "cancel",
|
|
83
|
-
}
|
|
84
|
-
|
|
85
71
|
|
|
86
72
|
def _route_error_message(error: exceptions.ClientError) -> str:
|
|
87
73
|
try:
|
|
@@ -351,37 +337,20 @@ def _resolve_tenant_organization(data, org_id: Optional[ResourceId]) -> None:
|
|
|
351
337
|
data["organizationId"] = org_id.base58
|
|
352
338
|
|
|
353
339
|
|
|
354
|
-
def
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
"another tenant, update the file to use a unique tenant name."
|
|
340
|
+
def _echo_tenant_creation_state(state, state_path) -> None:
|
|
341
|
+
click.echo(
|
|
342
|
+
dump_camelized_yaml(
|
|
343
|
+
{
|
|
344
|
+
"apiVersion": "1.0.0",
|
|
345
|
+
"kind": "Tenant",
|
|
346
|
+
"data": state.output(state_path),
|
|
347
|
+
}
|
|
363
348
|
)
|
|
349
|
+
)
|
|
364
350
|
|
|
365
|
-
if not _is_interactive_terminal():
|
|
366
|
-
raise click.UsageError(
|
|
367
|
-
"A tenant creation already exists for this configuration. Run the "
|
|
368
|
-
"command in an interactive terminal to resume it or cancel."
|
|
369
|
-
)
|
|
370
351
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
)
|
|
374
|
-
click.echo("What would you like to do?", err=True)
|
|
375
|
-
click.echo(" [1] Resume the existing creation", err=True)
|
|
376
|
-
click.echo(" [2] Cancel", err=True)
|
|
377
|
-
choice = click.prompt(
|
|
378
|
-
"Choice",
|
|
379
|
-
type=click.Choice(tuple(TENANT_OPERATION_CHOICES)),
|
|
380
|
-
default="1",
|
|
381
|
-
show_choices=False,
|
|
382
|
-
err=True,
|
|
383
|
-
)
|
|
384
|
-
return TENANT_OPERATION_CHOICES[choice]
|
|
352
|
+
def _tenant_progress_message(action: str, name: str) -> str:
|
|
353
|
+
return f'{action} "{name}"'
|
|
385
354
|
|
|
386
355
|
|
|
387
356
|
def _apply_tenant_data(
|
|
@@ -393,7 +362,7 @@ def _apply_tenant_data(
|
|
|
393
362
|
"organizationId": data["organizationId"],
|
|
394
363
|
}
|
|
395
364
|
progress = (
|
|
396
|
-
Spinner("
|
|
365
|
+
Spinner(_tenant_progress_message("Creating tenant", data["name"]))
|
|
397
366
|
if show_progress
|
|
398
367
|
else nullcontext()
|
|
399
368
|
)
|
|
@@ -429,12 +398,15 @@ def _apply_tenant_data(
|
|
|
429
398
|
"password": attributes["credentials"]["secret"],
|
|
430
399
|
}
|
|
431
400
|
|
|
401
|
+
if show_progress:
|
|
402
|
+
progress.update(
|
|
403
|
+
_tenant_progress_message("Configuring tenant", data["name"])
|
|
404
|
+
)
|
|
405
|
+
|
|
432
406
|
vault_mgmt = create_vault_mgmt_api(
|
|
433
407
|
ctx, response.body["data"]["links"]["vault_management_api"]
|
|
434
408
|
)
|
|
435
409
|
|
|
436
|
-
if show_progress:
|
|
437
|
-
progress.update("Waiting for provisioning")
|
|
438
410
|
while True:
|
|
439
411
|
# noinspection PyUnresolvedReferences
|
|
440
412
|
response = vault_mgmt.vaults.retrieve(
|
|
@@ -511,6 +483,7 @@ def apply_tenant(
|
|
|
511
483
|
if resume is not None and state_file is not None:
|
|
512
484
|
raise click.UsageError("--state-file cannot be used with --resume.")
|
|
513
485
|
|
|
486
|
+
completed_state = None
|
|
514
487
|
if resume is not None:
|
|
515
488
|
state = TenantCreationState.load(resume)
|
|
516
489
|
resolved_state_file = resume
|
|
@@ -536,17 +509,27 @@ def apply_tenant(
|
|
|
536
509
|
raise click.UsageError(
|
|
537
510
|
"The existing tenant creation state does not match this configuration."
|
|
538
511
|
)
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
if operation == "resume":
|
|
512
|
+
if existing_state.status == "complete":
|
|
513
|
+
completed_state = existing_state
|
|
514
|
+
else:
|
|
543
515
|
state = existing_state
|
|
544
516
|
is_resuming = True
|
|
545
517
|
|
|
546
518
|
try:
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
519
|
+
account_mgmt = create_account_mgmt_api(ctx)
|
|
520
|
+
existing_tenant_name = None
|
|
521
|
+
configuring_message = _tenant_progress_message(
|
|
522
|
+
"Configuring tenant", state.spec["name"]
|
|
523
|
+
)
|
|
524
|
+
creating_message = _tenant_progress_message(
|
|
525
|
+
"Creating tenant", state.spec["name"]
|
|
526
|
+
)
|
|
527
|
+
initial_message = (
|
|
528
|
+
configuring_message
|
|
529
|
+
if completed_state is not None or state.tenant_resource_id
|
|
530
|
+
else creating_message
|
|
531
|
+
)
|
|
532
|
+
with Spinner(initial_message) as progress:
|
|
550
533
|
creator = TenantCreator(
|
|
551
534
|
account_mgmt=account_mgmt,
|
|
552
535
|
provisioning_api=create_tenant_provisioning_api(ctx),
|
|
@@ -554,37 +537,39 @@ def apply_tenant(
|
|
|
554
537
|
calm_api_factory=partial(create_calm_api, ctx),
|
|
555
538
|
collect_forms_api_factory=partial(create_collect_forms_api, ctx),
|
|
556
539
|
platform_environment=ctx.obj.env,
|
|
557
|
-
on_stage=lambda stage: progress.update(
|
|
540
|
+
on_stage=lambda stage: progress.update(
|
|
541
|
+
creating_message
|
|
542
|
+
if stage in {"environment", "tenant"}
|
|
543
|
+
and not state.tenant_resource_id
|
|
544
|
+
else configuring_message
|
|
545
|
+
),
|
|
558
546
|
)
|
|
559
547
|
|
|
560
|
-
if
|
|
561
|
-
|
|
562
|
-
|
|
548
|
+
if completed_state is not None:
|
|
549
|
+
tenant_exists = creator.tenant_exists(completed_state)
|
|
550
|
+
if tenant_exists:
|
|
551
|
+
existing_tenant_name = completed_state.spec["name"]
|
|
552
|
+
else:
|
|
553
|
+
archive_state(resolved_state_file)
|
|
554
|
+
|
|
555
|
+
if existing_tenant_name is None:
|
|
556
|
+
if is_resuming:
|
|
557
|
+
creator.verify_resume_state(state)
|
|
563
558
|
|
|
564
|
-
|
|
559
|
+
creator.run(state, resolved_state_file)
|
|
565
560
|
except TenantCreationStageError as error:
|
|
566
|
-
|
|
567
|
-
dump_camelized_yaml(
|
|
568
|
-
{
|
|
569
|
-
"apiVersion": "1.0.0",
|
|
570
|
-
"kind": "Tenant",
|
|
571
|
-
"data": error.state.output(error.state_path),
|
|
572
|
-
}
|
|
573
|
-
)
|
|
574
|
-
)
|
|
561
|
+
_echo_tenant_creation_state(error.state, error.state_path)
|
|
575
562
|
raise TenantCreationError(
|
|
576
563
|
error.stage, error.state_path, error.cause, ctx=ctx
|
|
577
564
|
) from None
|
|
578
565
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
{
|
|
582
|
-
"apiVersion": "1.0.0",
|
|
583
|
-
"kind": "Tenant",
|
|
584
|
-
"data": state.output(resolved_state_file),
|
|
585
|
-
}
|
|
566
|
+
if existing_tenant_name is not None:
|
|
567
|
+
click.echo(
|
|
568
|
+
f'Tenant "{existing_tenant_name}" already exists. No changes were made.'
|
|
586
569
|
)
|
|
587
|
-
|
|
570
|
+
return
|
|
571
|
+
|
|
572
|
+
_echo_tenant_creation_state(state, resolved_state_file)
|
|
588
573
|
|
|
589
574
|
|
|
590
575
|
@apply.command(
|
vgscli/cli/commands/generate.py
CHANGED
vgscli/cli_progress.py
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import itertools
|
|
4
4
|
import threading
|
|
5
|
-
import time
|
|
6
|
-
from collections.abc import Callable
|
|
7
5
|
from typing import Optional, TextIO
|
|
8
6
|
|
|
9
7
|
import click
|
|
@@ -19,19 +17,14 @@ class Spinner:
|
|
|
19
17
|
self,
|
|
20
18
|
message: str,
|
|
21
19
|
*,
|
|
22
|
-
detail: Optional[str] = None,
|
|
23
20
|
stream: Optional[TextIO] = None,
|
|
24
21
|
interval: float = 0.1,
|
|
25
|
-
monotonic: Callable[[], float] = time.monotonic,
|
|
26
22
|
) -> None:
|
|
27
23
|
self.message = message
|
|
28
|
-
self.detail = detail
|
|
29
24
|
self.stream = stream or click.get_text_stream("stderr")
|
|
30
25
|
self.interval = interval
|
|
31
|
-
self.monotonic = monotonic
|
|
32
26
|
self._stop = threading.Event()
|
|
33
27
|
self._thread: Optional[threading.Thread] = None
|
|
34
|
-
self._started_at = self.monotonic()
|
|
35
28
|
self._max_width = 0
|
|
36
29
|
try:
|
|
37
30
|
self._enabled = self.stream.isatty()
|
|
@@ -39,7 +32,6 @@ class Spinner:
|
|
|
39
32
|
self._enabled = False
|
|
40
33
|
self._unicode = self._enabled and self._supports_unicode()
|
|
41
34
|
self._frames = self.UNICODE_FRAMES if self._unicode else self.ASCII_FRAMES
|
|
42
|
-
self._separator = " · " if self._unicode else " - "
|
|
43
35
|
|
|
44
36
|
def __enter__(self):
|
|
45
37
|
if self._enabled:
|
|
@@ -66,17 +58,11 @@ class Spinner:
|
|
|
66
58
|
def _clear(self) -> None:
|
|
67
59
|
self._write(f"\r{' ' * self._max_width}\r")
|
|
68
60
|
|
|
69
|
-
def update(self,
|
|
70
|
-
self.
|
|
61
|
+
def update(self, message: str) -> None:
|
|
62
|
+
self.message = message
|
|
71
63
|
|
|
72
64
|
def _line(self, frame: str) -> str:
|
|
73
|
-
|
|
74
|
-
if self.detail:
|
|
75
|
-
parts.append(self.detail)
|
|
76
|
-
elapsed = max(0, int(self.monotonic() - self._started_at))
|
|
77
|
-
minutes, seconds = divmod(elapsed, 60)
|
|
78
|
-
parts.append(f"{minutes:02d}:{seconds:02d}")
|
|
79
|
-
return f"{frame} {self._separator.join(parts)}"
|
|
65
|
+
return f"{frame} {self.message}"
|
|
80
66
|
|
|
81
67
|
def _supports_unicode(self) -> bool:
|
|
82
68
|
encoding = getattr(self.stream, "encoding", None)
|
vgscli/click_extensions.py
CHANGED
|
@@ -121,7 +121,7 @@ def tenant_selection_option(command):
|
|
|
121
121
|
"tenants",
|
|
122
122
|
type=click.STRING,
|
|
123
123
|
multiple=True,
|
|
124
|
-
help="Tenant the Service Account may access.
|
|
124
|
+
help="Tenant the Service Account may access. Payment templates require exactly one.",
|
|
125
125
|
)(wrapper)
|
|
126
126
|
return click.option(
|
|
127
127
|
"--vault",
|
vgscli/errors.py
CHANGED
|
@@ -182,10 +182,25 @@ class TenantCreationError(VgsCliError):
|
|
|
182
182
|
|
|
183
183
|
message = f"Tenant creation stopped during {stage}."
|
|
184
184
|
if details:
|
|
185
|
-
|
|
185
|
+
detail_message = ", ".join(details)
|
|
186
|
+
if not detail_message.endswith((".", "!", "?")):
|
|
187
|
+
detail_message = f"{detail_message}."
|
|
188
|
+
message = f"{message} {detail_message}"
|
|
189
|
+
|
|
190
|
+
if stage == "account" and any(
|
|
191
|
+
"account already exists" in detail.casefold() for detail in details or []
|
|
192
|
+
):
|
|
193
|
+
self.message = (
|
|
194
|
+
f"{message} A payment account already uses this tenant name. "
|
|
195
|
+
"To create a separate tenant, change the tenant name. "
|
|
196
|
+
"To recover this tenant, contact VGS Support."
|
|
197
|
+
)
|
|
198
|
+
self.ctx = ctx
|
|
199
|
+
return
|
|
200
|
+
|
|
186
201
|
self.message = (
|
|
187
|
-
f"{message} Run the same `vgs apply tenant --file` command again "
|
|
188
|
-
"
|
|
202
|
+
f"{message} Run the same `vgs apply tenant --file` command again; "
|
|
203
|
+
"the CLI will continue automatically."
|
|
189
204
|
)
|
|
190
205
|
self.ctx = ctx
|
|
191
206
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
apiVersion: 1.0.0
|
|
2
|
+
kind: ServiceAccount
|
|
3
|
+
data:
|
|
4
|
+
name: vgs-cli
|
|
5
|
+
{%- if vaults | length == 0 %}
|
|
6
|
+
{{- cli_warn("Service Account won't have access to any tenants inside the organization. If it needs tenant access, use --tenant <TENANT_ID>.") or "" }}
|
|
7
|
+
{%- else %}
|
|
8
|
+
vaults:
|
|
9
|
+
{%- for vault_id in vaults %}
|
|
10
|
+
- {{ vault_id }}
|
|
11
|
+
{%- endfor %}
|
|
12
|
+
{%- endif %}
|
|
13
|
+
scopes:
|
|
14
|
+
- name: access-logs:read
|
|
15
|
+
- name: organizations:read
|
|
16
|
+
- name: routes:write
|
|
17
|
+
- name: vaults:write
|
vgscli/tenant_creation.py
CHANGED
|
@@ -172,6 +172,19 @@ def default_state_path(request_digest):
|
|
|
172
172
|
return Path(APP_DIR) / "tenant-operations" / f"{request_digest}.json"
|
|
173
173
|
|
|
174
174
|
|
|
175
|
+
def archive_state(path):
|
|
176
|
+
state_path = Path(path)
|
|
177
|
+
archive_directory = state_path.parent / "archive"
|
|
178
|
+
archive_directory.mkdir(mode=0o700, parents=True, exist_ok=True)
|
|
179
|
+
os.chmod(archive_directory, 0o700)
|
|
180
|
+
archive_path = archive_directory / (
|
|
181
|
+
f"{state_path.stem}-{uuid.uuid4().hex}{state_path.suffix}"
|
|
182
|
+
)
|
|
183
|
+
os.replace(state_path, archive_path)
|
|
184
|
+
os.chmod(archive_path, 0o600)
|
|
185
|
+
return archive_path
|
|
186
|
+
|
|
187
|
+
|
|
175
188
|
class TenantCreator:
|
|
176
189
|
def __init__(
|
|
177
190
|
self,
|
|
@@ -230,20 +243,20 @@ class TenantCreator:
|
|
|
230
243
|
state.save(state_path)
|
|
231
244
|
return state
|
|
232
245
|
|
|
233
|
-
def
|
|
246
|
+
def tenant_exists(self, state):
|
|
234
247
|
if not state.tenant_resource_id:
|
|
235
|
-
return
|
|
248
|
+
return False
|
|
236
249
|
response = self.provisioning.organization_tenants(state.spec["organizationId"])
|
|
237
|
-
|
|
238
|
-
(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
if item.get("id") == state.tenant_resource_id
|
|
242
|
-
and item.get("attributes", {}).get("identifier") == state.tenant_id
|
|
243
|
-
),
|
|
244
|
-
None,
|
|
250
|
+
return any(
|
|
251
|
+
item.get("id") == state.tenant_resource_id
|
|
252
|
+
and item.get("attributes", {}).get("identifier") == state.tenant_id
|
|
253
|
+
for item in _resources(response)
|
|
245
254
|
)
|
|
246
|
-
|
|
255
|
+
|
|
256
|
+
def verify_resume_state(self, state):
|
|
257
|
+
if not state.tenant_resource_id:
|
|
258
|
+
return
|
|
259
|
+
if not self.tenant_exists(state):
|
|
247
260
|
raise click.UsageError(
|
|
248
261
|
"The tenant in the resume state is no longer available in the organization."
|
|
249
262
|
)
|
{vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/collect-form-api-schema.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/notification-template.yaml
RENAMED
|
File without changes
|
{vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/public-credential-collect.yaml
RENAMED
|
File without changes
|
{vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/read-credentials-no-pci.yaml
RENAMED
|
File without changes
|
{vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/read-credentials-with-pci.yaml
RENAMED
|
File without changes
|
{vgs_cli-1.35.0.dev1.data → vgs_cli-1.35.0.dev2.data}/data/vgscli/service-account-schema.yaml
RENAMED
|
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
|