ansible-core 2.19.1rc1__py3-none-any.whl → 2.19.2rc1__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 ansible-core might be problematic. Click here for more details.
- ansible/executor/play_iterator.py +1 -1
- ansible/module_utils/ansible_release.py +1 -1
- ansible/plugins/filter/core.py +1 -0
- ansible/release.py +1 -1
- {ansible_core-2.19.1rc1.dist-info → ansible_core-2.19.2rc1.dist-info}/METADATA +1 -1
- {ansible_core-2.19.1rc1.dist-info → ansible_core-2.19.2rc1.dist-info}/RECORD +22 -22
- ansible_test/_internal/ci/__init__.py +64 -130
- ansible_test/_internal/ci/azp.py +15 -17
- ansible_test/_internal/ci/local.py +117 -21
- ansible_test/_internal/core_ci.py +31 -11
- ansible_test/_internal/host_profiles.py +5 -2
- ansible_test/_internal/provisioning.py +3 -0
- ansible_test/_internal/util.py +1 -0
- {ansible_core-2.19.1rc1.dist-info → ansible_core-2.19.2rc1.dist-info}/WHEEL +0 -0
- {ansible_core-2.19.1rc1.dist-info → ansible_core-2.19.2rc1.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.19.1rc1.dist-info → ansible_core-2.19.2rc1.dist-info}/licenses/COPYING +0 -0
- {ansible_core-2.19.1rc1.dist-info → ansible_core-2.19.2rc1.dist-info}/licenses/licenses/Apache-License.txt +0 -0
- {ansible_core-2.19.1rc1.dist-info → ansible_core-2.19.2rc1.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
- {ansible_core-2.19.1rc1.dist-info → ansible_core-2.19.2rc1.dist-info}/licenses/licenses/MIT-license.txt +0 -0
- {ansible_core-2.19.1rc1.dist-info → ansible_core-2.19.2rc1.dist-info}/licenses/licenses/PSF-license.txt +0 -0
- {ansible_core-2.19.1rc1.dist-info → ansible_core-2.19.2rc1.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
- {ansible_core-2.19.1rc1.dist-info → ansible_core-2.19.2rc1.dist-info}/top_level.txt +0 -0
|
@@ -574,7 +574,7 @@ class PlayIterator:
|
|
|
574
574
|
Given the current HostState state, determines if the current block, or any child blocks,
|
|
575
575
|
are in rescue mode.
|
|
576
576
|
"""
|
|
577
|
-
if state.run_state
|
|
577
|
+
if state.run_state in (IteratingStates.TASKS, IteratingStates.HANDLERS) and state.get_current_block().rescue:
|
|
578
578
|
return True
|
|
579
579
|
if state.tasks_child_state is not None:
|
|
580
580
|
return self.is_any_block_rescuing(state.tasks_child_state)
|
ansible/plugins/filter/core.py
CHANGED
ansible/release.py
CHANGED
|
@@ -3,7 +3,7 @@ ansible/__main__.py,sha256=24j-7-YT4lZ2fmV80JD-VRoYBnxR7YoP_VP-orJtDt0,796
|
|
|
3
3
|
ansible/constants.py,sha256=qef45QpHi-yFFMvllvNKmqFpXdKKr304e5fEZnjgwZc,7989
|
|
4
4
|
ansible/context.py,sha256=oKYyfjfWpy8vDeProtqfnqSmuij_t75_5e5t0U_hQ1g,1933
|
|
5
5
|
ansible/keyword_desc.yml,sha256=5rGCsr-0B8w2D67qBD6q_2WFxfqj9ieb0V_2J-dZJ5E,7547
|
|
6
|
-
ansible/release.py,sha256=
|
|
6
|
+
ansible/release.py,sha256=JLb_BEVsdGYnLJUp7vyx1FkTEnTJunGAJYEFt-RZrhU,855
|
|
7
7
|
ansible/_internal/__init__.py,sha256=J3yCEAZoJLwxHMPEIWHwX6seRTCQ4Sr7cfHSw11ik9k,2208
|
|
8
8
|
ansible/_internal/_collection_proxy.py,sha256=V3Zns3jdWR1hTP6q4mrNWoIKL67ayiQFPDOb6F7igsc,1228
|
|
9
9
|
ansible/_internal/_event_formatting.py,sha256=cHMsuYi6v2W3fgEYdKLSe8O34kW5bZE26zyj7FOt268,4222
|
|
@@ -101,7 +101,7 @@ ansible/errors/__init__.py,sha256=W1s19PaheqXMI2yKnZCuaKKjSAJRPgU1_xF2_J9B1NU,16
|
|
|
101
101
|
ansible/executor/__init__.py,sha256=mRvbCJPA-_veSG5ka3v04G5vsarLVDeB3EWFsu6geSI,749
|
|
102
102
|
ansible/executor/interpreter_discovery.py,sha256=UWeAxnHknJCci2gG3zt6edx5Nj4WbHYfJVcmW_DzItY,3858
|
|
103
103
|
ansible/executor/module_common.py,sha256=sXMOvKj_9ubeBaCPVBHh76uHaRYZm-8mOhsSG55aXQ8,60450
|
|
104
|
-
ansible/executor/play_iterator.py,sha256=
|
|
104
|
+
ansible/executor/play_iterator.py,sha256=ybui896hQFJ4wLsYC3fZoJY4KEsX69QkCoMfomQyEqE,32310
|
|
105
105
|
ansible/executor/playbook_executor.py,sha256=5wjvqw22RG4g_JlYDQnLFrUEa8aYQBWdgKhEpNonhKQ,14806
|
|
106
106
|
ansible/executor/stats.py,sha256=Rw-Q73xYvXnYOt-LJFnHAR03NvVR3ESgbMkHnVGhIPI,3180
|
|
107
107
|
ansible/executor/task_executor.py,sha256=irbdKCK6BZfBVn00nbvNMZJOltAUum5ykuqpAHh40-U,61437
|
|
@@ -211,7 +211,7 @@ ansible/inventory/host.py,sha256=cZw906LeMYe6oF3ZxW6K2HWoW2Qc0jxHssg_C8cRumE,493
|
|
|
211
211
|
ansible/inventory/manager.py,sha256=fxg2sq7s-VBJnn9TvJCgv-xvYIu0DLJTix_y3w0wLcc,31811
|
|
212
212
|
ansible/module_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
213
213
|
ansible/module_utils/_text.py,sha256=VkWgAnSNVCbTQqZgllUObBFsH3uM4EUW5srl1UR9t1g,544
|
|
214
|
-
ansible/module_utils/ansible_release.py,sha256=
|
|
214
|
+
ansible/module_utils/ansible_release.py,sha256=JLb_BEVsdGYnLJUp7vyx1FkTEnTJunGAJYEFt-RZrhU,855
|
|
215
215
|
ansible/module_utils/api.py,sha256=8BmCzQtp9rClsLGlDn4I9iJrUFLCdnoEIxYX59_IL9c,5756
|
|
216
216
|
ansible/module_utils/basic.py,sha256=hoGCYYxypID_5wdpwkSAhMTb5Mfkr9x0RKsJaSHMwSA,90113
|
|
217
217
|
ansible/module_utils/connection.py,sha256=ZwtQEs-TtT-XecoEmFWiDevSkJLIj348YkiW6PP7G9E,7471
|
|
@@ -583,7 +583,7 @@ ansible/plugins/filter/combinations.yml,sha256=LttrIICjapNtZHWnvJD-C9Pv3PIKP16i8
|
|
|
583
583
|
ansible/plugins/filter/combine.yml,sha256=QH2zy4qr9wPpEyr-XKmphbls60M4ZSdAkj7r3cuvC3Q,1671
|
|
584
584
|
ansible/plugins/filter/comment.yml,sha256=nJVzBF2Qiwa-qQRioJK42cbWt3Rb5LYmfvGPhrhU8Rc,2139
|
|
585
585
|
ansible/plugins/filter/commonpath.yml,sha256=SPx3fPy4GPJaKmY2S8aJI1I800FOgErYAKVXV1etp1Q,696
|
|
586
|
-
ansible/plugins/filter/core.py,sha256=
|
|
586
|
+
ansible/plugins/filter/core.py,sha256=iMvK6wfEnQ53nBZos9boI72rExGeQn8ImgA3AW_E2-w,27393
|
|
587
587
|
ansible/plugins/filter/dict2items.yml,sha256=A3gL25dyGrSqP44PtqQgg6paUnwReAzC7Brkqel-39E,1523
|
|
588
588
|
ansible/plugins/filter/difference.yml,sha256=YJnJJMYejCcBaNgxFBhYj-z6OysRHmss1gUgrIJBQFk,1091
|
|
589
589
|
ansible/plugins/filter/dirname.yml,sha256=Z7p7ay8s3_Zee6gIu7qr4wUC-an7lwLwuoVmgHQCKyg,820
|
|
@@ -789,12 +789,12 @@ ansible/vars/hostvars.py,sha256=cRK_4dssUwIN4aDxxYXEj7KzTazrykQ4PbJotne5oJc,4364
|
|
|
789
789
|
ansible/vars/manager.py,sha256=1SNGcwMTT7m8aPC45DHdkOZRtnf7OEcuExBtocJusq4,28023
|
|
790
790
|
ansible/vars/plugins.py,sha256=8svEABS2yBPzEdymdsrZ-0D70boUoCNvcgkWasvtVNo,4533
|
|
791
791
|
ansible/vars/reserved.py,sha256=NgxlMBm_tloqDVb5TEX4eGhpYsz_AO6-Fmyi3kJpIFk,3107
|
|
792
|
-
ansible_core-2.19.
|
|
793
|
-
ansible_core-2.19.
|
|
794
|
-
ansible_core-2.19.
|
|
795
|
-
ansible_core-2.19.
|
|
796
|
-
ansible_core-2.19.
|
|
797
|
-
ansible_core-2.19.
|
|
792
|
+
ansible_core-2.19.2rc1.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
793
|
+
ansible_core-2.19.2rc1.dist-info/licenses/licenses/Apache-License.txt,sha256=y16Ofl9KOYjhBjwULGDcLfdWBfTEZRXnduOspt-XbhQ,11325
|
|
794
|
+
ansible_core-2.19.2rc1.dist-info/licenses/licenses/BSD-3-Clause.txt,sha256=la0N3fE3Se8vBiuvUcFKA8b-E41G7flTic6P8CkUroE,1548
|
|
795
|
+
ansible_core-2.19.2rc1.dist-info/licenses/licenses/MIT-license.txt,sha256=jLXp2XurnyZKbye40g9tfmLGtVlxh3pPD4n8xNqX8xc,1023
|
|
796
|
+
ansible_core-2.19.2rc1.dist-info/licenses/licenses/PSF-license.txt,sha256=g7BC_H1qyg8Q1o5F76Vrm8ChSWYI5-dyj-CdGlNKBUo,2484
|
|
797
|
+
ansible_core-2.19.2rc1.dist-info/licenses/licenses/simplified_bsd.txt,sha256=8R5R7R7sOa0h1Fi6RNgFgHowHBfun-OVOMzJ4rKAk2w,1237
|
|
798
798
|
ansible_test/__init__.py,sha256=20VPOj11c6Ut1Av9RaurgwJvFhMqkWG3vAvcCbecNKw,66
|
|
799
799
|
ansible_test/_data/ansible.cfg,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
800
800
|
ansible_test/_data/coveragerc,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -854,7 +854,7 @@ ansible_test/_internal/connections.py,sha256=_wKp7ercAJlJxuB6nXGChqdIC2PGQRuwg8g
|
|
|
854
854
|
ansible_test/_internal/constants.py,sha256=qgVmng71FWdsIZXgUaQUEZfB9V0GC4jjhYbSp4pzA_M,1970
|
|
855
855
|
ansible_test/_internal/containers.py,sha256=iOqs3Bi9127cl7h7kIPw-mo3tbM-7JAkmOwB-9ESvRE,34138
|
|
856
856
|
ansible_test/_internal/content_config.py,sha256=qbYoSKOBT-X7FhSCuVkYzgiIz5H4MIbua3DxcN-gYdA,5589
|
|
857
|
-
ansible_test/_internal/core_ci.py,sha256=
|
|
857
|
+
ansible_test/_internal/core_ci.py,sha256=GSRt726aNL167p3LWLWum36EFhYhOaRvJGOjzHi7J1g,17977
|
|
858
858
|
ansible_test/_internal/coverage_util.py,sha256=NnlgbpJ47h2oaAc5m3QyVs65GOq-cuRR9gEVV1ZE4rI,9408
|
|
859
859
|
ansible_test/_internal/data.py,sha256=8APH-Bm6CeFO9ngWgZC8IxHeFzlxn0OQd6D5G1msSZU,11185
|
|
860
860
|
ansible_test/_internal/debugging.py,sha256=bL2mT7AHiKGSmZp2lZVDWDpkeMdOg3HgCZW1m1qTaYc,15563
|
|
@@ -865,7 +865,7 @@ ansible_test/_internal/encoding.py,sha256=ymPqkmgg7mXUkW6MOARx0cYanX9TLLnu_NXp6n
|
|
|
865
865
|
ansible_test/_internal/executor.py,sha256=-SSTYgKckI-dWltBWt67zTU6zO7NVu_O3pgFiJG4DeQ,2960
|
|
866
866
|
ansible_test/_internal/git.py,sha256=TkYoTZ8CKWlP8dZZmThzzT1myItdP7_LseZ_2BMnIMA,4367
|
|
867
867
|
ansible_test/_internal/host_configs.py,sha256=fuY7CAhM8Ky3cPcVhHe28Kwzuokzyg9lvr7GVL3o2Bo,18635
|
|
868
|
-
ansible_test/_internal/host_profiles.py,sha256=
|
|
868
|
+
ansible_test/_internal/host_profiles.py,sha256=i7qp8CBV94dK_8o7GrU-jMrofHkJAvDzGGaIMGjGj1Y,74466
|
|
869
869
|
ansible_test/_internal/http.py,sha256=P_C5n8hSZ3Q1zA08smmJCh2LvOoaflGasEqnLXZP0L0,3865
|
|
870
870
|
ansible_test/_internal/init.py,sha256=-OdOvJ3Fz4Sx2aTG9qq7ekKsbVVTqOvRqetOqjOvA6w,506
|
|
871
871
|
ansible_test/_internal/inventory.py,sha256=8Ajk67x5a8zt1bgvT8IrR9kCuEXXfkMxO2_ioFtlgh4,7074
|
|
@@ -875,7 +875,7 @@ ansible_test/_internal/locale_util.py,sha256=tjRbwKmgMQc1ysIhvP8yBhFcNA-2UCaWfQB
|
|
|
875
875
|
ansible_test/_internal/metadata.py,sha256=HWM-sQT-ovt2lwTSK1xN8-IYHWrB5joVY4BRh7HHHC0,7036
|
|
876
876
|
ansible_test/_internal/payload.py,sha256=F9sLPiTw-zNq0-zU-L_RIYOsXZmA3nsLWha2W2MoeEs,8013
|
|
877
877
|
ansible_test/_internal/processes.py,sha256=H3n7jOGzvWdeTxsTWFx4TPIjSpt40g0T6j0wvYdOsWY,2231
|
|
878
|
-
ansible_test/_internal/provisioning.py,sha256=
|
|
878
|
+
ansible_test/_internal/provisioning.py,sha256=kStzjL6EYfuf_R9Xi91TO8S0fqqD7pZaj-Lt9hMjh4Y,7581
|
|
879
879
|
ansible_test/_internal/pypi_proxy.py,sha256=N9_kuBk6Bko3e8dKC1zi4UfhU0untpQgOK2W984GT_0,6020
|
|
880
880
|
ansible_test/_internal/python_requirements.py,sha256=0tXTRO9m8q5ORaM6lELal5n8VEqlD1f16OrN8m7C_w8,16038
|
|
881
881
|
ansible_test/_internal/ssh.py,sha256=7gTyNiwszPwFSM4aYT4YtAWfAR4lYLnOi7dpnv0SqwA,10635
|
|
@@ -883,12 +883,12 @@ ansible_test/_internal/target.py,sha256=3W4J6T79Pv2kB6KOpC_lRq2qZFS7L6GobByyVshy
|
|
|
883
883
|
ansible_test/_internal/test.py,sha256=q17SmItAsiBWrSilDBZFSEBugv9QNsG5HzFOAFXcyh4,14516
|
|
884
884
|
ansible_test/_internal/thread.py,sha256=XN9jshWoLPdqTMDjcOQxGk9691FnjUo1K_5UhcRy-O8,2633
|
|
885
885
|
ansible_test/_internal/timeout.py,sha256=KOYPTjgsAX4N2q-4Qn5vFpCWJWBT9YtQCpsU7ZIBvro,4073
|
|
886
|
-
ansible_test/_internal/util.py,sha256=
|
|
886
|
+
ansible_test/_internal/util.py,sha256=BELYusenCvkpKJ136STWQsoAFttIJL5-erAud8JAVaE,39600
|
|
887
887
|
ansible_test/_internal/util_common.py,sha256=sw8ySIrPKCTK4AH8naulDXPU7kabSHD2EJMMhQ5G1o8,17814
|
|
888
888
|
ansible_test/_internal/venv.py,sha256=eb5RfjapntulFMTIQieyx8QdHo2LJfjgZY_wx3_htMw,5522
|
|
889
|
-
ansible_test/_internal/ci/__init__.py,sha256=
|
|
890
|
-
ansible_test/_internal/ci/azp.py,sha256=
|
|
891
|
-
ansible_test/_internal/ci/local.py,sha256=
|
|
889
|
+
ansible_test/_internal/ci/__init__.py,sha256=wNgXvmq517FQPVQIW5HeUuuFBNjBAshcNoPEO8I6jDQ,4684
|
|
890
|
+
ansible_test/_internal/ci/azp.py,sha256=pdMf4sgHVB8wsusY_9FTxPkhw_7Qnc9qQWgJgBZlf2w,10104
|
|
891
|
+
ansible_test/_internal/ci/local.py,sha256=YnpfQfvheLNwV6rzb_YElASy-S-tH0zhOXtVtABNAUs,9906
|
|
892
892
|
ansible_test/_internal/classification/__init__.py,sha256=8Hzmr2pqAMR7sibHNDub1YGkcnLJzb4I_3MqeZbpJzw,34143
|
|
893
893
|
ansible_test/_internal/classification/common.py,sha256=WWM6LRHcO29nRorSLveSzRLIarb5-dPbwHCf8Qd1xvU,895
|
|
894
894
|
ansible_test/_internal/classification/csharp.py,sha256=EM7yxfbwnHsKFjjpiQUTsZcPY567qmue1eXR3GI5JJE,3242
|
|
@@ -1091,8 +1091,8 @@ ansible_test/config/cloud-config-vultr.ini.template,sha256=XLKHk3lg_8ReQMdWfZzhh
|
|
|
1091
1091
|
ansible_test/config/config.yml,sha256=1zdGucnIl6nIecZA7ISIANvqXiHWqq6Dthsk_6MUwNc,2642
|
|
1092
1092
|
ansible_test/config/inventory.networking.template,sha256=bFNSk8zNQOaZ_twaflrY0XZ9mLwUbRLuNT0BdIFwvn4,1335
|
|
1093
1093
|
ansible_test/config/inventory.winrm.template,sha256=1QU8W-GFLnYEw8yY9bVIvUAVvJYPM3hyoijf6-M7T00,1098
|
|
1094
|
-
ansible_core-2.19.
|
|
1095
|
-
ansible_core-2.19.
|
|
1096
|
-
ansible_core-2.19.
|
|
1097
|
-
ansible_core-2.19.
|
|
1098
|
-
ansible_core-2.19.
|
|
1094
|
+
ansible_core-2.19.2rc1.dist-info/METADATA,sha256=GKPO88evKJ7s3y_H_Fe-y94ganzsioNiAqMnw7Do05c,7733
|
|
1095
|
+
ansible_core-2.19.2rc1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
1096
|
+
ansible_core-2.19.2rc1.dist-info/entry_points.txt,sha256=S9yJij5Im6FgRQxzkqSCnPQokC7PcWrDW_NSygZczJU,451
|
|
1097
|
+
ansible_core-2.19.2rc1.dist-info/top_level.txt,sha256=IFbRLjAvih1DYzJWg3_F6t4sCzEMxRO7TOMNs6GkYHo,21
|
|
1098
|
+
ansible_core-2.19.2rc1.dist-info/RECORD,,
|
|
@@ -3,22 +3,13 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import abc
|
|
6
|
-
import
|
|
6
|
+
import dataclasses
|
|
7
|
+
import datetime
|
|
7
8
|
import json
|
|
8
|
-
import
|
|
9
|
+
import pathlib
|
|
9
10
|
import tempfile
|
|
10
11
|
import typing as t
|
|
11
12
|
|
|
12
|
-
from ..encoding import (
|
|
13
|
-
to_bytes,
|
|
14
|
-
to_text,
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
from ..io import (
|
|
18
|
-
read_text_file,
|
|
19
|
-
write_text_file,
|
|
20
|
-
)
|
|
21
|
-
|
|
22
13
|
from ..config import (
|
|
23
14
|
CommonConfig,
|
|
24
15
|
TestConfig,
|
|
@@ -34,6 +25,65 @@ from ..util import (
|
|
|
34
25
|
)
|
|
35
26
|
|
|
36
27
|
|
|
28
|
+
@dataclasses.dataclass(frozen=True, kw_only=True)
|
|
29
|
+
class AuthContext:
|
|
30
|
+
"""Information about the request to which authentication will be applied."""
|
|
31
|
+
|
|
32
|
+
stage: str
|
|
33
|
+
provider: str
|
|
34
|
+
request_id: str
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class AuthHelper:
|
|
38
|
+
"""Authentication helper."""
|
|
39
|
+
|
|
40
|
+
NAMESPACE: t.ClassVar = 'ci@core.ansible.com'
|
|
41
|
+
|
|
42
|
+
def __init__(self, key_file: pathlib.Path) -> None:
|
|
43
|
+
self.private_key_file = pathlib.Path(str(key_file).removesuffix('.pub'))
|
|
44
|
+
self.public_key_file = pathlib.Path(f'{self.private_key_file}.pub')
|
|
45
|
+
|
|
46
|
+
def sign_request(self, request: dict[str, object], context: AuthContext) -> None:
|
|
47
|
+
"""Sign the given auth request using the provided context."""
|
|
48
|
+
request.update(
|
|
49
|
+
stage=context.stage,
|
|
50
|
+
provider=context.provider,
|
|
51
|
+
request_id=context.request_id,
|
|
52
|
+
timestamp=datetime.datetime.now(tz=datetime.timezone.utc).replace(microsecond=0).isoformat(),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
56
|
+
payload_path = pathlib.Path(temp_dir) / 'auth.json'
|
|
57
|
+
payload_path.write_text(json.dumps(request, sort_keys=True))
|
|
58
|
+
|
|
59
|
+
cmd = ['ssh-keygen', '-q', '-Y', 'sign', '-f', str(self.private_key_file), '-n', self.NAMESPACE, str(payload_path)]
|
|
60
|
+
raw_command(cmd, capture=False, interactive=True)
|
|
61
|
+
|
|
62
|
+
signature_path = pathlib.Path(f'{payload_path}.sig')
|
|
63
|
+
signature = signature_path.read_text()
|
|
64
|
+
|
|
65
|
+
request.update(signature=signature)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class GeneratingAuthHelper(AuthHelper, metaclass=abc.ABCMeta):
|
|
69
|
+
"""Authentication helper which generates a key pair on demand."""
|
|
70
|
+
|
|
71
|
+
def __init__(self) -> None:
|
|
72
|
+
super().__init__(pathlib.Path('~/.ansible/test/ansible-core-ci').expanduser())
|
|
73
|
+
|
|
74
|
+
def sign_request(self, request: dict[str, object], context: AuthContext) -> None:
|
|
75
|
+
if not self.private_key_file.exists():
|
|
76
|
+
self.generate_key_pair()
|
|
77
|
+
|
|
78
|
+
super().sign_request(request, context)
|
|
79
|
+
|
|
80
|
+
def generate_key_pair(self) -> None:
|
|
81
|
+
"""Generate key pair."""
|
|
82
|
+
self.private_key_file.parent.mkdir(parents=True, exist_ok=True)
|
|
83
|
+
|
|
84
|
+
raw_command(['ssh-keygen', '-q', '-f', str(self.private_key_file), '-N', ''], capture=True)
|
|
85
|
+
|
|
86
|
+
|
|
37
87
|
class ChangeDetectionNotSupported(ApplicationError):
|
|
38
88
|
"""Exception for cases where change detection is not supported."""
|
|
39
89
|
|
|
@@ -75,8 +125,8 @@ class CIProvider(metaclass=abc.ABCMeta):
|
|
|
75
125
|
"""Return True if Ansible Core CI is supported."""
|
|
76
126
|
|
|
77
127
|
@abc.abstractmethod
|
|
78
|
-
def
|
|
79
|
-
"""
|
|
128
|
+
def prepare_core_ci_request(self, config: dict[str, object], context: AuthContext) -> dict[str, object]:
|
|
129
|
+
"""Prepare an Ansible Core CI request using the given config and context."""
|
|
80
130
|
|
|
81
131
|
@abc.abstractmethod
|
|
82
132
|
def get_git_details(self, args: CommonConfig) -> t.Optional[dict[str, t.Any]]:
|
|
@@ -101,119 +151,3 @@ def get_ci_provider() -> CIProvider:
|
|
|
101
151
|
display.info('Detected CI provider: %s' % provider.name)
|
|
102
152
|
|
|
103
153
|
return provider
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
class AuthHelper(metaclass=abc.ABCMeta):
|
|
107
|
-
"""Public key based authentication helper for Ansible Core CI."""
|
|
108
|
-
|
|
109
|
-
def sign_request(self, request: dict[str, t.Any]) -> None:
|
|
110
|
-
"""Sign the given auth request and make the public key available."""
|
|
111
|
-
payload_bytes = to_bytes(json.dumps(request, sort_keys=True))
|
|
112
|
-
signature_raw_bytes = self.sign_bytes(payload_bytes)
|
|
113
|
-
signature = to_text(base64.b64encode(signature_raw_bytes))
|
|
114
|
-
|
|
115
|
-
request.update(signature=signature)
|
|
116
|
-
|
|
117
|
-
def initialize_private_key(self) -> str:
|
|
118
|
-
"""
|
|
119
|
-
Initialize and publish a new key pair (if needed) and return the private key.
|
|
120
|
-
The private key is cached across ansible-test invocations, so it is only generated and published once per CI job.
|
|
121
|
-
"""
|
|
122
|
-
path = os.path.expanduser('~/.ansible-core-ci-private.key')
|
|
123
|
-
|
|
124
|
-
if os.path.exists(to_bytes(path)):
|
|
125
|
-
private_key_pem = read_text_file(path)
|
|
126
|
-
else:
|
|
127
|
-
private_key_pem = self.generate_private_key()
|
|
128
|
-
write_text_file(path, private_key_pem)
|
|
129
|
-
|
|
130
|
-
return private_key_pem
|
|
131
|
-
|
|
132
|
-
@abc.abstractmethod
|
|
133
|
-
def sign_bytes(self, payload_bytes: bytes) -> bytes:
|
|
134
|
-
"""Sign the given payload and return the signature, initializing a new key pair if required."""
|
|
135
|
-
|
|
136
|
-
@abc.abstractmethod
|
|
137
|
-
def publish_public_key(self, public_key_pem: str) -> None:
|
|
138
|
-
"""Publish the given public key."""
|
|
139
|
-
|
|
140
|
-
@abc.abstractmethod
|
|
141
|
-
def generate_private_key(self) -> str:
|
|
142
|
-
"""Generate a new key pair, publishing the public key and returning the private key."""
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
class CryptographyAuthHelper(AuthHelper, metaclass=abc.ABCMeta):
|
|
146
|
-
"""Cryptography based public key based authentication helper for Ansible Core CI."""
|
|
147
|
-
|
|
148
|
-
def sign_bytes(self, payload_bytes: bytes) -> bytes:
|
|
149
|
-
"""Sign the given payload and return the signature, initializing a new key pair if required."""
|
|
150
|
-
# import cryptography here to avoid overhead and failures in environments which do not use/provide it
|
|
151
|
-
from cryptography.hazmat.backends import default_backend
|
|
152
|
-
from cryptography.hazmat.primitives import hashes
|
|
153
|
-
from cryptography.hazmat.primitives.asymmetric import ec
|
|
154
|
-
from cryptography.hazmat.primitives.serialization import load_pem_private_key
|
|
155
|
-
|
|
156
|
-
private_key_pem = self.initialize_private_key()
|
|
157
|
-
private_key = load_pem_private_key(to_bytes(private_key_pem), None, default_backend())
|
|
158
|
-
|
|
159
|
-
assert isinstance(private_key, ec.EllipticCurvePrivateKey)
|
|
160
|
-
|
|
161
|
-
signature_raw_bytes = private_key.sign(payload_bytes, ec.ECDSA(hashes.SHA256()))
|
|
162
|
-
|
|
163
|
-
return signature_raw_bytes
|
|
164
|
-
|
|
165
|
-
def generate_private_key(self) -> str:
|
|
166
|
-
"""Generate a new key pair, publishing the public key and returning the private key."""
|
|
167
|
-
# import cryptography here to avoid overhead and failures in environments which do not use/provide it
|
|
168
|
-
from cryptography.hazmat.backends import default_backend
|
|
169
|
-
from cryptography.hazmat.primitives import serialization
|
|
170
|
-
from cryptography.hazmat.primitives.asymmetric import ec
|
|
171
|
-
|
|
172
|
-
private_key = ec.generate_private_key(ec.SECP384R1(), default_backend())
|
|
173
|
-
public_key = private_key.public_key()
|
|
174
|
-
|
|
175
|
-
private_key_pem = to_text(private_key.private_bytes( # type: ignore[attr-defined] # documented method, but missing from type stubs
|
|
176
|
-
encoding=serialization.Encoding.PEM,
|
|
177
|
-
format=serialization.PrivateFormat.PKCS8,
|
|
178
|
-
encryption_algorithm=serialization.NoEncryption(),
|
|
179
|
-
))
|
|
180
|
-
|
|
181
|
-
public_key_pem = to_text(public_key.public_bytes(
|
|
182
|
-
encoding=serialization.Encoding.PEM,
|
|
183
|
-
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
|
184
|
-
))
|
|
185
|
-
|
|
186
|
-
self.publish_public_key(public_key_pem)
|
|
187
|
-
|
|
188
|
-
return private_key_pem
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
class OpenSSLAuthHelper(AuthHelper, metaclass=abc.ABCMeta):
|
|
192
|
-
"""OpenSSL based public key based authentication helper for Ansible Core CI."""
|
|
193
|
-
|
|
194
|
-
def sign_bytes(self, payload_bytes: bytes) -> bytes:
|
|
195
|
-
"""Sign the given payload and return the signature, initializing a new key pair if required."""
|
|
196
|
-
private_key_pem = self.initialize_private_key()
|
|
197
|
-
|
|
198
|
-
with tempfile.NamedTemporaryFile() as private_key_file:
|
|
199
|
-
private_key_file.write(to_bytes(private_key_pem))
|
|
200
|
-
private_key_file.flush()
|
|
201
|
-
|
|
202
|
-
with tempfile.NamedTemporaryFile() as payload_file:
|
|
203
|
-
payload_file.write(payload_bytes)
|
|
204
|
-
payload_file.flush()
|
|
205
|
-
|
|
206
|
-
with tempfile.NamedTemporaryFile() as signature_file:
|
|
207
|
-
raw_command(['openssl', 'dgst', '-sha256', '-sign', private_key_file.name, '-out', signature_file.name, payload_file.name], capture=True)
|
|
208
|
-
signature_raw_bytes = signature_file.read()
|
|
209
|
-
|
|
210
|
-
return signature_raw_bytes
|
|
211
|
-
|
|
212
|
-
def generate_private_key(self) -> str:
|
|
213
|
-
"""Generate a new key pair, publishing the public key and returning the private key."""
|
|
214
|
-
private_key_pem = raw_command(['openssl', 'ecparam', '-genkey', '-name', 'secp384r1', '-noout'], capture=True)[0]
|
|
215
|
-
public_key_pem = raw_command(['openssl', 'ec', '-pubout'], data=private_key_pem, capture=True)[0]
|
|
216
|
-
|
|
217
|
-
self.publish_public_key(public_key_pem)
|
|
218
|
-
|
|
219
|
-
return private_key_pem
|
ansible_test/_internal/ci/azp.py
CHANGED
|
@@ -31,9 +31,10 @@ from ..util import (
|
|
|
31
31
|
)
|
|
32
32
|
|
|
33
33
|
from . import (
|
|
34
|
+
AuthContext,
|
|
34
35
|
ChangeDetectionNotSupported,
|
|
35
36
|
CIProvider,
|
|
36
|
-
|
|
37
|
+
GeneratingAuthHelper,
|
|
37
38
|
)
|
|
38
39
|
|
|
39
40
|
CODE = 'azp'
|
|
@@ -112,10 +113,11 @@ class AzurePipelines(CIProvider):
|
|
|
112
113
|
"""Return True if Ansible Core CI is supported."""
|
|
113
114
|
return True
|
|
114
115
|
|
|
115
|
-
def
|
|
116
|
-
"""Return authentication details for Ansible Core CI."""
|
|
116
|
+
def prepare_core_ci_request(self, config: dict[str, object], context: AuthContext) -> dict[str, object]:
|
|
117
117
|
try:
|
|
118
|
-
request = dict(
|
|
118
|
+
request: dict[str, object] = dict(
|
|
119
|
+
type="azp:ssh",
|
|
120
|
+
config=config,
|
|
119
121
|
org_name=os.environ['SYSTEM_COLLECTIONURI'].strip('/').split('/')[-1],
|
|
120
122
|
project_name=os.environ['SYSTEM_TEAMPROJECT'],
|
|
121
123
|
build_id=int(os.environ['BUILD_BUILDID']),
|
|
@@ -124,13 +126,9 @@ class AzurePipelines(CIProvider):
|
|
|
124
126
|
except KeyError as ex:
|
|
125
127
|
raise MissingEnvironmentVariable(name=ex.args[0]) from None
|
|
126
128
|
|
|
127
|
-
self.auth.sign_request(request)
|
|
129
|
+
self.auth.sign_request(request, context)
|
|
128
130
|
|
|
129
|
-
|
|
130
|
-
azp=request,
|
|
131
|
-
)
|
|
132
|
-
|
|
133
|
-
return auth
|
|
131
|
+
return request
|
|
134
132
|
|
|
135
133
|
def get_git_details(self, args: CommonConfig) -> t.Optional[dict[str, t.Any]]:
|
|
136
134
|
"""Return details about git in the current environment."""
|
|
@@ -144,14 +142,14 @@ class AzurePipelines(CIProvider):
|
|
|
144
142
|
return details
|
|
145
143
|
|
|
146
144
|
|
|
147
|
-
class AzurePipelinesAuthHelper(
|
|
148
|
-
"""
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
145
|
+
class AzurePipelinesAuthHelper(GeneratingAuthHelper):
|
|
146
|
+
"""Authentication helper for Azure Pipelines."""
|
|
147
|
+
|
|
148
|
+
def generate_key_pair(self) -> None:
|
|
149
|
+
super().generate_key_pair()
|
|
150
|
+
|
|
151
|
+
public_key_pem = self.public_key_file.read_text()
|
|
152
152
|
|
|
153
|
-
def publish_public_key(self, public_key_pem: str) -> None:
|
|
154
|
-
"""Publish the given public key."""
|
|
155
153
|
try:
|
|
156
154
|
agent_temp_directory = os.environ['AGENT_TEMPDIRECTORY']
|
|
157
155
|
except KeyError as ex:
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import abc
|
|
6
|
+
import inspect
|
|
6
7
|
import platform
|
|
7
8
|
import random
|
|
8
9
|
import re
|
|
10
|
+
import pathlib
|
|
9
11
|
import typing as t
|
|
10
12
|
|
|
11
13
|
from ..config import (
|
|
@@ -24,11 +26,14 @@ from ..git import (
|
|
|
24
26
|
from ..util import (
|
|
25
27
|
ApplicationError,
|
|
26
28
|
display,
|
|
29
|
+
get_subclasses,
|
|
27
30
|
is_binary_file,
|
|
28
31
|
SubprocessError,
|
|
29
32
|
)
|
|
30
33
|
|
|
31
34
|
from . import (
|
|
35
|
+
AuthContext,
|
|
36
|
+
AuthHelper,
|
|
32
37
|
CIProvider,
|
|
33
38
|
)
|
|
34
39
|
|
|
@@ -120,34 +125,20 @@ class Local(CIProvider):
|
|
|
120
125
|
|
|
121
126
|
def supports_core_ci_auth(self) -> bool:
|
|
122
127
|
"""Return True if Ansible Core CI is supported."""
|
|
123
|
-
|
|
124
|
-
return os.path.exists(path)
|
|
128
|
+
return Authenticator.available()
|
|
125
129
|
|
|
126
|
-
def
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
auth_key = read_text_file(path).strip()
|
|
130
|
+
def prepare_core_ci_request(self, config: dict[str, object], context: AuthContext) -> dict[str, object]:
|
|
131
|
+
if not (authenticator := Authenticator.load()):
|
|
132
|
+
raise ApplicationError('Ansible Core CI authentication has not been configured.')
|
|
130
133
|
|
|
131
|
-
|
|
132
|
-
key=auth_key,
|
|
133
|
-
nonce=None,
|
|
134
|
-
)
|
|
134
|
+
display.info(f'Using {authenticator} for Ansible Core CI.', verbosity=1)
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
remote=request,
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
return auth
|
|
136
|
+
return authenticator.prepare_auth_request(config, context)
|
|
141
137
|
|
|
142
138
|
def get_git_details(self, args: CommonConfig) -> t.Optional[dict[str, t.Any]]:
|
|
143
139
|
"""Return details about git in the current environment."""
|
|
144
140
|
return None # not yet implemented for local
|
|
145
141
|
|
|
146
|
-
@staticmethod
|
|
147
|
-
def _get_aci_key_path() -> str:
|
|
148
|
-
path = os.path.expanduser('~/.ansible-core-ci.key')
|
|
149
|
-
return path
|
|
150
|
-
|
|
151
142
|
|
|
152
143
|
class InvalidBranch(ApplicationError):
|
|
153
144
|
"""Exception for invalid branch specification."""
|
|
@@ -214,3 +205,108 @@ class LocalChanges:
|
|
|
214
205
|
return True
|
|
215
206
|
|
|
216
207
|
return False
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class Authenticator(metaclass=abc.ABCMeta):
|
|
211
|
+
"""Base class for authenticators."""
|
|
212
|
+
|
|
213
|
+
@staticmethod
|
|
214
|
+
def list() -> list[type[Authenticator]]:
|
|
215
|
+
"""List all authenticators in priority order."""
|
|
216
|
+
return sorted((sc for sc in get_subclasses(Authenticator) if not inspect.isabstract(sc)), key=lambda obj: obj.priority())
|
|
217
|
+
|
|
218
|
+
@staticmethod
|
|
219
|
+
def load() -> Authenticator | None:
|
|
220
|
+
"""Load an authenticator instance, returning None if not configured."""
|
|
221
|
+
for implementation in Authenticator.list():
|
|
222
|
+
if implementation.config_file().exists():
|
|
223
|
+
return implementation()
|
|
224
|
+
|
|
225
|
+
return None
|
|
226
|
+
|
|
227
|
+
@staticmethod
|
|
228
|
+
def available() -> bool:
|
|
229
|
+
"""Return True if an authenticator is available, otherwise False."""
|
|
230
|
+
return bool(Authenticator.load())
|
|
231
|
+
|
|
232
|
+
@classmethod
|
|
233
|
+
@abc.abstractmethod
|
|
234
|
+
def priority(cls) -> int:
|
|
235
|
+
"""Priority used to determine which authenticator is tried first, from lowest to highest."""
|
|
236
|
+
|
|
237
|
+
@classmethod
|
|
238
|
+
@abc.abstractmethod
|
|
239
|
+
def config_file(cls) -> pathlib.Path:
|
|
240
|
+
"""Path to the config file for this authenticator."""
|
|
241
|
+
|
|
242
|
+
@abc.abstractmethod
|
|
243
|
+
def prepare_auth_request(self, config: dict[str, object], context: AuthContext) -> dict[str, object]:
|
|
244
|
+
"""Prepare an authenticated Ansible Core CI request using the given config and context."""
|
|
245
|
+
|
|
246
|
+
def __str__(self) -> str:
|
|
247
|
+
return self.__class__.__name__
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
class PasswordAuthenticator(Authenticator):
|
|
251
|
+
"""Authenticate using a password."""
|
|
252
|
+
|
|
253
|
+
@classmethod
|
|
254
|
+
def priority(cls) -> int:
|
|
255
|
+
return 200
|
|
256
|
+
|
|
257
|
+
@classmethod
|
|
258
|
+
def config_file(cls) -> pathlib.Path:
|
|
259
|
+
return pathlib.Path('~/.ansible-core-ci.key').expanduser()
|
|
260
|
+
|
|
261
|
+
def prepare_auth_request(self, config: dict[str, object], context: AuthContext) -> dict[str, object]:
|
|
262
|
+
parts = self.config_file().read_text().strip().split(maxsplit=1)
|
|
263
|
+
|
|
264
|
+
if len(parts) == 1: # temporary backward compatibility for legacy API keys
|
|
265
|
+
request = dict(
|
|
266
|
+
config=config,
|
|
267
|
+
auth=dict(
|
|
268
|
+
remote=dict(
|
|
269
|
+
key=parts[0],
|
|
270
|
+
),
|
|
271
|
+
),
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
return request
|
|
275
|
+
|
|
276
|
+
username, password = parts
|
|
277
|
+
|
|
278
|
+
request = dict(
|
|
279
|
+
type="remote:password",
|
|
280
|
+
config=config,
|
|
281
|
+
username=username,
|
|
282
|
+
password=password,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
return request
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
class SshAuthenticator(Authenticator):
|
|
289
|
+
"""Authenticate using an SSH key."""
|
|
290
|
+
|
|
291
|
+
@classmethod
|
|
292
|
+
def priority(cls) -> int:
|
|
293
|
+
return 100
|
|
294
|
+
|
|
295
|
+
@classmethod
|
|
296
|
+
def config_file(cls) -> pathlib.Path:
|
|
297
|
+
return pathlib.Path('~/.ansible-core-ci.auth').expanduser()
|
|
298
|
+
|
|
299
|
+
def prepare_auth_request(self, config: dict[str, object], context: AuthContext) -> dict[str, object]:
|
|
300
|
+
parts = self.config_file().read_text().strip().split(maxsplit=1)
|
|
301
|
+
username, key_file = parts
|
|
302
|
+
|
|
303
|
+
request: dict[str, object] = dict(
|
|
304
|
+
type="remote:ssh",
|
|
305
|
+
config=config,
|
|
306
|
+
username=username,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
auth_helper = AuthHelper(pathlib.Path(key_file).expanduser())
|
|
310
|
+
auth_helper.sign_request(request, context)
|
|
311
|
+
|
|
312
|
+
return request
|
|
@@ -42,6 +42,7 @@ from .config import (
|
|
|
42
42
|
)
|
|
43
43
|
|
|
44
44
|
from .ci import (
|
|
45
|
+
AuthContext,
|
|
45
46
|
get_ci_provider,
|
|
46
47
|
)
|
|
47
48
|
|
|
@@ -68,6 +69,10 @@ class Resource(metaclass=abc.ABCMeta):
|
|
|
68
69
|
def persist(self) -> bool:
|
|
69
70
|
"""True if the resource is persistent, otherwise false."""
|
|
70
71
|
|
|
72
|
+
@abc.abstractmethod
|
|
73
|
+
def get_config(self, core_ci: AnsibleCoreCI) -> dict[str, object]:
|
|
74
|
+
"""Return the configuration for this resource."""
|
|
75
|
+
|
|
71
76
|
|
|
72
77
|
@dataclasses.dataclass(frozen=True)
|
|
73
78
|
class VmResource(Resource):
|
|
@@ -92,6 +97,16 @@ class VmResource(Resource):
|
|
|
92
97
|
"""True if the resource is persistent, otherwise false."""
|
|
93
98
|
return True
|
|
94
99
|
|
|
100
|
+
def get_config(self, core_ci: AnsibleCoreCI) -> dict[str, object]:
|
|
101
|
+
"""Return the configuration for this resource."""
|
|
102
|
+
return dict(
|
|
103
|
+
type="vm",
|
|
104
|
+
platform=self.platform,
|
|
105
|
+
version=self.version,
|
|
106
|
+
architecture=self.architecture,
|
|
107
|
+
public_key=core_ci.ssh_key.pub_contents,
|
|
108
|
+
)
|
|
109
|
+
|
|
95
110
|
|
|
96
111
|
@dataclasses.dataclass(frozen=True)
|
|
97
112
|
class CloudResource(Resource):
|
|
@@ -112,6 +127,12 @@ class CloudResource(Resource):
|
|
|
112
127
|
"""True if the resource is persistent, otherwise false."""
|
|
113
128
|
return False
|
|
114
129
|
|
|
130
|
+
def get_config(self, core_ci: AnsibleCoreCI) -> dict[str, object]:
|
|
131
|
+
"""Return the configuration for this resource."""
|
|
132
|
+
return dict(
|
|
133
|
+
type="cloud",
|
|
134
|
+
)
|
|
135
|
+
|
|
115
136
|
|
|
116
137
|
class AnsibleCoreCI:
|
|
117
138
|
"""Client for Ansible Core CI services."""
|
|
@@ -189,7 +210,7 @@ class AnsibleCoreCI:
|
|
|
189
210
|
display.info(f'Skipping started {self.label} instance.', verbosity=1)
|
|
190
211
|
return None
|
|
191
212
|
|
|
192
|
-
return self._start(
|
|
213
|
+
return self._start()
|
|
193
214
|
|
|
194
215
|
def stop(self) -> None:
|
|
195
216
|
"""Stop instance."""
|
|
@@ -288,26 +309,25 @@ class AnsibleCoreCI:
|
|
|
288
309
|
def _uri(self) -> str:
|
|
289
310
|
return f'{self.endpoint}/{self.stage}/{self.provider}/{self.instance_id}'
|
|
290
311
|
|
|
291
|
-
def _start(self
|
|
312
|
+
def _start(self) -> dict[str, t.Any]:
|
|
292
313
|
"""Start instance."""
|
|
293
314
|
display.info(f'Initializing new {self.label} instance using: {self._uri}', verbosity=1)
|
|
294
315
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
)
|
|
316
|
+
config = self.resource.get_config(self)
|
|
317
|
+
|
|
318
|
+
context = AuthContext(
|
|
319
|
+
request_id=self.instance_id,
|
|
320
|
+
stage=self.stage,
|
|
321
|
+
provider=self.provider,
|
|
302
322
|
)
|
|
303
323
|
|
|
304
|
-
|
|
324
|
+
request = self.ci_provider.prepare_core_ci_request(config, context)
|
|
305
325
|
|
|
306
326
|
headers = {
|
|
307
327
|
'Content-Type': 'application/json',
|
|
308
328
|
}
|
|
309
329
|
|
|
310
|
-
response = self._start_endpoint(
|
|
330
|
+
response = self._start_endpoint(request, headers)
|
|
311
331
|
|
|
312
332
|
self.started = True
|
|
313
333
|
self._save()
|
|
@@ -265,6 +265,9 @@ class HostProfile(t.Generic[THostConfig], metaclass=abc.ABCMeta):
|
|
|
265
265
|
def name(self) -> str:
|
|
266
266
|
"""The name of the host profile."""
|
|
267
267
|
|
|
268
|
+
def pre_provision(self) -> None:
|
|
269
|
+
"""Pre-provision the host profile."""
|
|
270
|
+
|
|
268
271
|
def provision(self) -> None:
|
|
269
272
|
"""Provision the host before delegation."""
|
|
270
273
|
|
|
@@ -522,8 +525,8 @@ class RemoteProfile(SshTargetHostProfile[TRemoteConfig], metaclass=abc.ABCMeta):
|
|
|
522
525
|
"""The saved Ansible Core CI state."""
|
|
523
526
|
self.state['core_ci'] = value
|
|
524
527
|
|
|
525
|
-
def
|
|
526
|
-
"""
|
|
528
|
+
def pre_provision(self) -> None:
|
|
529
|
+
"""Pre-provision the host before delegation."""
|
|
527
530
|
self.core_ci = self.create_core_ci(load=True)
|
|
528
531
|
self.core_ci.start()
|
|
529
532
|
|
|
@@ -132,6 +132,9 @@ def prepare_profiles(
|
|
|
132
132
|
|
|
133
133
|
ExitHandler.register(functools.partial(cleanup_profiles, host_state))
|
|
134
134
|
|
|
135
|
+
for pre_profile in host_state.profiles:
|
|
136
|
+
pre_profile.pre_provision()
|
|
137
|
+
|
|
135
138
|
def provision(profile: HostProfile) -> None:
|
|
136
139
|
"""Provision the given profile."""
|
|
137
140
|
profile.provision()
|
ansible_test/_internal/util.py
CHANGED
|
@@ -707,6 +707,7 @@ def common_environment() -> dict[str, str]:
|
|
|
707
707
|
optional = (
|
|
708
708
|
'LD_LIBRARY_PATH',
|
|
709
709
|
'SSH_AUTH_SOCK',
|
|
710
|
+
'SSH_SK_PROVIDER',
|
|
710
711
|
# MacOS High Sierra Compatibility
|
|
711
712
|
# http://sealiesoftware.com/blog/archive/2017/6/5/Objective-C_and_fork_in_macOS_1013.html
|
|
712
713
|
# Example configuration for macOS:
|
|
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
|