ansible-core 2.14.4rc1__py3-none-any.whl → 2.14.5rc1__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/cli/arguments/option_helpers.py +0 -2
- ansible/cli/doc.py +10 -4
- ansible/cli/galaxy.py +1 -1
- ansible/cli/playbook.py +2 -0
- ansible/executor/powershell/module_manifest.py +1 -1
- ansible/galaxy/api.py +29 -10
- ansible/galaxy/collection/__init__.py +3 -0
- ansible/galaxy/collection/concrete_artifact_manager.py +34 -17
- ansible/galaxy/dependency_resolution/dataclasses.py +11 -1
- ansible/galaxy/dependency_resolution/providers.py +0 -1
- ansible/module_utils/ansible_release.py +1 -1
- ansible/module_utils/api.py +14 -1
- ansible/module_utils/csharp/Ansible.Basic.cs +265 -7
- ansible/modules/uri.py +10 -3
- ansible/playbook/helpers.py +1 -0
- ansible/playbook/included_file.py +1 -0
- ansible/playbook/role_include.py +3 -3
- ansible/plugins/action/__init__.py +1 -1
- ansible/plugins/filter/basename.yml +3 -1
- ansible/plugins/filter/dirname.yml +3 -1
- ansible/plugins/lookup/password.py +78 -39
- ansible/release.py +1 -1
- ansible/utils/encrypt.py +9 -6
- {ansible_core-2.14.4rc1.dist-info → ansible_core-2.14.5rc1.dist-info}/METADATA +1 -1
- {ansible_core-2.14.4rc1.dist-info → ansible_core-2.14.5rc1.dist-info}/RECORD +33 -33
- ansible_test/_internal/classification/__init__.py +13 -5
- ansible_test/_internal/cli/argparsing/argcompletion.py +20 -5
- ansible_test/_util/controller/sanity/mypy/ansible-test.ini +3 -0
- {ansible_core-2.14.4rc1.data → ansible_core-2.14.5rc1.data}/scripts/ansible-test +0 -0
- {ansible_core-2.14.4rc1.dist-info → ansible_core-2.14.5rc1.dist-info}/COPYING +0 -0
- {ansible_core-2.14.4rc1.dist-info → ansible_core-2.14.5rc1.dist-info}/WHEEL +0 -0
- {ansible_core-2.14.4rc1.dist-info → ansible_core-2.14.5rc1.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.14.4rc1.dist-info → ansible_core-2.14.5rc1.dist-info}/top_level.txt +0 -0
|
@@ -235,8 +235,6 @@ def add_check_options(parser):
|
|
|
235
235
|
"""Add options for commands which can run with diagnostic information of tasks"""
|
|
236
236
|
parser.add_argument("-C", "--check", default=False, dest='check', action='store_true',
|
|
237
237
|
help="don't make any changes; instead, try to predict some of the changes that may occur")
|
|
238
|
-
parser.add_argument('--syntax-check', dest='syntax', action='store_true',
|
|
239
|
-
help="perform a syntax check on the playbook, but do not execute it")
|
|
240
238
|
parser.add_argument("-D", "--diff", default=C.DIFF_ALWAYS, dest='diff', action='store_true',
|
|
241
239
|
help="when changing (small) files and templates, show the differences in those"
|
|
242
240
|
" files; works great with --check")
|
ansible/cli/doc.py
CHANGED
|
@@ -1241,10 +1241,16 @@ class DocCLI(CLI, RoleMixin):
|
|
|
1241
1241
|
if 'module' in item:
|
|
1242
1242
|
text.append(textwrap.fill(DocCLI.tty_ify('Module %s' % item['module']),
|
|
1243
1243
|
limit - 6, initial_indent=opt_indent[:-2] + "* ", subsequent_indent=opt_indent))
|
|
1244
|
-
description = item.get('description'
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1244
|
+
description = item.get('description')
|
|
1245
|
+
if description is None and item['module'].startswith('ansible.builtin.'):
|
|
1246
|
+
description = 'The official documentation on the %s module.' % item['module']
|
|
1247
|
+
if description is not None:
|
|
1248
|
+
text.append(textwrap.fill(DocCLI.tty_ify(description),
|
|
1249
|
+
limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent + ' '))
|
|
1250
|
+
if item['module'].startswith('ansible.builtin.'):
|
|
1251
|
+
relative_url = 'collections/%s_module.html' % item['module'].replace('.', '/', 2)
|
|
1252
|
+
text.append(textwrap.fill(DocCLI.tty_ify(get_versioned_doclink(relative_url)),
|
|
1253
|
+
limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent))
|
|
1248
1254
|
elif 'name' in item and 'link' in item and 'description' in item:
|
|
1249
1255
|
text.append(textwrap.fill(DocCLI.tty_ify(item['name']),
|
|
1250
1256
|
limit - 6, initial_indent=opt_indent[:-2] + "* ", subsequent_indent=opt_indent))
|
ansible/cli/galaxy.py
CHANGED
|
@@ -242,7 +242,7 @@ class GalaxyCLI(CLI):
|
|
|
242
242
|
help='The Ansible Galaxy API key which can be found at '
|
|
243
243
|
'https://galaxy.ansible.com/me/preferences.')
|
|
244
244
|
common.add_argument('-c', '--ignore-certs', action='store_true', dest='ignore_certs', help='Ignore SSL certificate validation errors.', default=None)
|
|
245
|
-
common.add_argument('--timeout', dest='timeout', type=int,
|
|
245
|
+
common.add_argument('--timeout', dest='timeout', type=int, default=60,
|
|
246
246
|
help="The time to wait for operations against the galaxy server, defaults to 60s.")
|
|
247
247
|
|
|
248
248
|
opt_help.add_verbosity_options(common)
|
ansible/cli/playbook.py
CHANGED
|
@@ -54,6 +54,8 @@ class PlaybookCLI(CLI):
|
|
|
54
54
|
opt_help.add_module_options(self.parser)
|
|
55
55
|
|
|
56
56
|
# ansible playbook specific opts
|
|
57
|
+
self.parser.add_argument('--syntax-check', dest='syntax', action='store_true',
|
|
58
|
+
help="perform a syntax check on the playbook, but do not execute it")
|
|
57
59
|
self.parser.add_argument('--list-tasks', dest='listtasks', action='store_true',
|
|
58
60
|
help="list all tasks that would be executed")
|
|
59
61
|
self.parser.add_argument('--list-tags', dest='listtags', action='store_true',
|
|
@@ -319,7 +319,7 @@ def _create_powershell_wrapper(b_module_data, module_path, module_args,
|
|
|
319
319
|
|
|
320
320
|
exec_manifest["actions"].insert(0, 'async_watchdog')
|
|
321
321
|
exec_manifest["actions"].insert(0, 'async_wrapper')
|
|
322
|
-
exec_manifest["async_jid"] =
|
|
322
|
+
exec_manifest["async_jid"] = f'j{random.randint(0, 999999999999)}'
|
|
323
323
|
exec_manifest["async_timeout_sec"] = async_timeout
|
|
324
324
|
exec_manifest["async_startup_timeout"] = C.config.get_config_value("WIN_ASYNC_STARTUP_TIMEOUT", variables=task_vars)
|
|
325
325
|
|
ansible/galaxy/api.py
CHANGED
|
@@ -11,12 +11,15 @@ import functools
|
|
|
11
11
|
import hashlib
|
|
12
12
|
import json
|
|
13
13
|
import os
|
|
14
|
+
import socket
|
|
14
15
|
import stat
|
|
15
16
|
import tarfile
|
|
16
17
|
import time
|
|
17
18
|
import threading
|
|
18
19
|
|
|
19
|
-
from
|
|
20
|
+
from http import HTTPStatus
|
|
21
|
+
from http.client import BadStatusLine, IncompleteRead
|
|
22
|
+
from urllib.error import HTTPError, URLError
|
|
20
23
|
from urllib.parse import quote as urlquote, urlencode, urlparse, parse_qs, urljoin
|
|
21
24
|
|
|
22
25
|
from ansible import constants as C
|
|
@@ -34,10 +37,11 @@ from ansible.utils.path import makedirs_safe
|
|
|
34
37
|
display = Display()
|
|
35
38
|
_CACHE_LOCK = threading.Lock()
|
|
36
39
|
COLLECTION_PAGE_SIZE = 100
|
|
37
|
-
RETRY_HTTP_ERROR_CODES =
|
|
38
|
-
|
|
40
|
+
RETRY_HTTP_ERROR_CODES = { # TODO: Allow user-configuration
|
|
41
|
+
HTTPStatus.TOO_MANY_REQUESTS,
|
|
39
42
|
520, # Galaxy rate limit error code (Cloudflare unknown error)
|
|
40
|
-
|
|
43
|
+
HTTPStatus.BAD_GATEWAY, # Common error from galaxy that may represent any number of transient backend issues
|
|
44
|
+
}
|
|
41
45
|
|
|
42
46
|
|
|
43
47
|
def cache_lock(func):
|
|
@@ -48,11 +52,24 @@ def cache_lock(func):
|
|
|
48
52
|
return wrapped
|
|
49
53
|
|
|
50
54
|
|
|
51
|
-
def
|
|
55
|
+
def should_retry_error(exception):
|
|
52
56
|
# Note: cloud.redhat.com masks rate limit errors with 403 (Forbidden) error codes.
|
|
53
57
|
# Since 403 could reflect the actual problem (such as an expired token), we should
|
|
54
58
|
# not retry by default.
|
|
55
|
-
|
|
59
|
+
if isinstance(exception, GalaxyError) and exception.http_code in RETRY_HTTP_ERROR_CODES:
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
if isinstance(exception, AnsibleError) and (orig_exc := getattr(exception, 'orig_exc', None)):
|
|
63
|
+
# URLError is often a proxy for an underlying error, handle wrapped exceptions
|
|
64
|
+
if isinstance(orig_exc, URLError):
|
|
65
|
+
orig_exc = orig_exc.reason
|
|
66
|
+
|
|
67
|
+
# Handle common URL related errors such as TimeoutError, and BadStatusLine
|
|
68
|
+
# Note: socket.timeout is only required for Py3.9
|
|
69
|
+
if isinstance(orig_exc, (TimeoutError, BadStatusLine, IncompleteRead, socket.timeout)):
|
|
70
|
+
return True
|
|
71
|
+
|
|
72
|
+
return False
|
|
56
73
|
|
|
57
74
|
|
|
58
75
|
def g_connect(versions):
|
|
@@ -327,7 +344,7 @@ class GalaxyAPI:
|
|
|
327
344
|
|
|
328
345
|
@retry_with_delays_and_condition(
|
|
329
346
|
backoff_iterator=generate_jittered_backoff(retries=6, delay_base=2, delay_threshold=40),
|
|
330
|
-
should_retry_error=
|
|
347
|
+
should_retry_error=should_retry_error
|
|
331
348
|
)
|
|
332
349
|
def _call_galaxy(self, url, args=None, headers=None, method=None, auth_required=False, error_context_msg=None,
|
|
333
350
|
cache=False, cache_key=None):
|
|
@@ -385,7 +402,10 @@ class GalaxyAPI:
|
|
|
385
402
|
except HTTPError as e:
|
|
386
403
|
raise GalaxyError(e, error_context_msg)
|
|
387
404
|
except Exception as e:
|
|
388
|
-
raise AnsibleError(
|
|
405
|
+
raise AnsibleError(
|
|
406
|
+
"Unknown error when attempting to call Galaxy at '%s': %s" % (url, to_native(e)),
|
|
407
|
+
orig_exc=e
|
|
408
|
+
)
|
|
389
409
|
|
|
390
410
|
resp_data = to_text(resp.read(), errors='surrogate_or_strict')
|
|
391
411
|
try:
|
|
@@ -906,8 +926,7 @@ class GalaxyAPI:
|
|
|
906
926
|
try:
|
|
907
927
|
signatures = data["signatures"]
|
|
908
928
|
except KeyError:
|
|
909
|
-
|
|
910
|
-
display.vvvvvv(f"Server {self.api_server} has not signed {namespace}.{name}:{version}")
|
|
929
|
+
display.vvvv(f"Server {self.api_server} has not signed {namespace}.{name}:{version}")
|
|
911
930
|
return []
|
|
912
931
|
else:
|
|
913
932
|
return [signature_info["signature"] for signature_info in signatures]
|
|
@@ -767,6 +767,9 @@ def install_collections(
|
|
|
767
767
|
"Skipping signature verification."
|
|
768
768
|
)
|
|
769
769
|
|
|
770
|
+
if concrete_coll_pin.type == 'galaxy':
|
|
771
|
+
concrete_coll_pin = concrete_coll_pin.with_signatures_repopulated()
|
|
772
|
+
|
|
770
773
|
try:
|
|
771
774
|
install(concrete_coll_pin, output_path, artifacts_manager)
|
|
772
775
|
except AnsibleError as err:
|
|
@@ -27,9 +27,12 @@ if t.TYPE_CHECKING:
|
|
|
27
27
|
|
|
28
28
|
from ansible.errors import AnsibleError
|
|
29
29
|
from ansible.galaxy import get_collections_galaxy_meta_info
|
|
30
|
+
from ansible.galaxy.api import should_retry_error
|
|
30
31
|
from ansible.galaxy.dependency_resolution.dataclasses import _GALAXY_YAML
|
|
31
32
|
from ansible.galaxy.user_agent import user_agent
|
|
32
33
|
from ansible.module_utils._text import to_bytes, to_native, to_text
|
|
34
|
+
from ansible.module_utils.api import retry_with_delays_and_condition
|
|
35
|
+
from ansible.module_utils.api import generate_jittered_backoff
|
|
33
36
|
from ansible.module_utils.common.process import get_bin_path
|
|
34
37
|
from ansible.module_utils.common._collections_compat import MutableMapping
|
|
35
38
|
from ansible.module_utils.common.yaml import yaml_load
|
|
@@ -160,17 +163,24 @@ class ConcreteArtifactsManager:
|
|
|
160
163
|
token=token,
|
|
161
164
|
) # type: bytes
|
|
162
165
|
except URLError as err:
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
download_err=to_native(err),
|
|
170
|
-
),
|
|
166
|
+
raise AnsibleError(
|
|
167
|
+
'Failed to download collection tar '
|
|
168
|
+
"from '{coll_src!s}': {download_err!s}".
|
|
169
|
+
format(
|
|
170
|
+
coll_src=to_native(collection.src),
|
|
171
|
+
download_err=to_native(err),
|
|
171
172
|
),
|
|
172
|
-
|
|
173
|
-
|
|
173
|
+
) from err
|
|
174
|
+
except Exception as err:
|
|
175
|
+
raise AnsibleError(
|
|
176
|
+
'Failed to download collection tar '
|
|
177
|
+
"from '{coll_src!s}' due to the following unforeseen error: "
|
|
178
|
+
'{download_err!s}'.
|
|
179
|
+
format(
|
|
180
|
+
coll_src=to_native(collection.src),
|
|
181
|
+
download_err=to_native(err),
|
|
182
|
+
),
|
|
183
|
+
) from err
|
|
174
184
|
else:
|
|
175
185
|
display.vvv(
|
|
176
186
|
"Collection '{coll!s}' obtained from "
|
|
@@ -460,6 +470,10 @@ def _extract_collection_from_git(repo_url, coll_ver, b_path):
|
|
|
460
470
|
|
|
461
471
|
|
|
462
472
|
# FIXME: use random subdirs while preserving the file names
|
|
473
|
+
@retry_with_delays_and_condition(
|
|
474
|
+
backoff_iterator=generate_jittered_backoff(retries=6, delay_base=2, delay_threshold=40),
|
|
475
|
+
should_retry_error=should_retry_error
|
|
476
|
+
)
|
|
463
477
|
def _download_file(url, b_path, expected_hash, validate_certs, token=None, timeout=60):
|
|
464
478
|
# type: (str, bytes, t.Optional[str], bool, GalaxyToken, int) -> bytes
|
|
465
479
|
# ^ NOTE: used in download and verify_collections ^
|
|
@@ -478,13 +492,16 @@ def _download_file(url, b_path, expected_hash, validate_certs, token=None, timeo
|
|
|
478
492
|
display.display("Downloading %s to %s" % (url, to_text(b_tarball_dir)))
|
|
479
493
|
# NOTE: Galaxy redirects downloads to S3 which rejects the request
|
|
480
494
|
# NOTE: if an Authorization header is attached so don't redirect it
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
495
|
+
try:
|
|
496
|
+
resp = open_url(
|
|
497
|
+
to_native(url, errors='surrogate_or_strict'),
|
|
498
|
+
validate_certs=validate_certs,
|
|
499
|
+
headers=None if token is None else token.headers(),
|
|
500
|
+
unredirected_headers=['Authorization'], http_agent=user_agent(),
|
|
501
|
+
timeout=timeout
|
|
502
|
+
)
|
|
503
|
+
except Exception as err:
|
|
504
|
+
raise AnsibleError(to_native(err), orig_exc=err)
|
|
488
505
|
|
|
489
506
|
with open(b_file_path, 'wb') as download_file: # type: t.BinaryIO
|
|
490
507
|
actual_hash = _consume_file(resp, write_to=download_file)
|
|
@@ -27,7 +27,7 @@ if t.TYPE_CHECKING:
|
|
|
27
27
|
)
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
from ansible.errors import AnsibleError
|
|
30
|
+
from ansible.errors import AnsibleError, AnsibleAssertionError
|
|
31
31
|
from ansible.galaxy.api import GalaxyAPI
|
|
32
32
|
from ansible.module_utils._text import to_bytes, to_native, to_text
|
|
33
33
|
from ansible.module_utils.common.arg_spec import ArgumentSpecValidator
|
|
@@ -571,3 +571,13 @@ class Candidate(
|
|
|
571
571
|
|
|
572
572
|
def __init__(self, *args, **kwargs):
|
|
573
573
|
super(Candidate, self).__init__()
|
|
574
|
+
|
|
575
|
+
def with_signatures_repopulated(self): # type: (Candidate) -> Candidate
|
|
576
|
+
"""Populate a new Candidate instance with Galaxy signatures.
|
|
577
|
+
:raises AnsibleAssertionError: If the supplied candidate is not sourced from a Galaxy-like index.
|
|
578
|
+
"""
|
|
579
|
+
if self.type != 'galaxy':
|
|
580
|
+
raise AnsibleAssertionError(f"Invalid collection type for {self!r}: unable to get signatures from a galaxy server.")
|
|
581
|
+
|
|
582
|
+
signatures = self.src.get_collection_signatures(self.namespace, self.name, self.ver)
|
|
583
|
+
return self.__class__(self.fqcn, self.ver, self.src, self.type, frozenset([*self.signatures, *signatures]))
|
|
@@ -392,7 +392,6 @@ class CollectionDependencyProviderBase(AbstractProvider):
|
|
|
392
392
|
|
|
393
393
|
if not unsatisfied:
|
|
394
394
|
if self._include_signatures:
|
|
395
|
-
signatures = src_server.get_collection_signatures(first_req.namespace, first_req.name, version)
|
|
396
395
|
for extra_source in extra_signature_sources:
|
|
397
396
|
signatures.append(get_signature_from_source(extra_source))
|
|
398
397
|
latest_matches.append(
|
ansible/module_utils/api.py
CHANGED
|
@@ -26,11 +26,15 @@ The 'api' module provides the following common argument specs:
|
|
|
26
26
|
from __future__ import (absolute_import, division, print_function)
|
|
27
27
|
__metaclass__ = type
|
|
28
28
|
|
|
29
|
+
import copy
|
|
29
30
|
import functools
|
|
31
|
+
import itertools
|
|
30
32
|
import random
|
|
31
33
|
import sys
|
|
32
34
|
import time
|
|
33
35
|
|
|
36
|
+
import ansible.module_utils.compat.typing as t
|
|
37
|
+
|
|
34
38
|
|
|
35
39
|
def rate_limit_argument_spec(spec=None):
|
|
36
40
|
"""Creates an argument spec for working with rate limiting"""
|
|
@@ -141,6 +145,15 @@ def retry_with_delays_and_condition(backoff_iterator, should_retry_error=None):
|
|
|
141
145
|
:param backoff_iterator: An iterable of delays in seconds.
|
|
142
146
|
:param should_retry_error: A callable that takes an exception of the decorated function and decides whether to retry or not (returns a bool).
|
|
143
147
|
"""
|
|
148
|
+
def _emit_isolated_iterator_copies(original_iterator): # type: (t.Iterable[t.Any]) -> t.Generator
|
|
149
|
+
# Ref: https://stackoverflow.com/a/30232619/595220
|
|
150
|
+
_copiable_iterator, _first_iterator_copy = itertools.tee(original_iterator)
|
|
151
|
+
yield _first_iterator_copy
|
|
152
|
+
while True:
|
|
153
|
+
yield copy.copy(_copiable_iterator)
|
|
154
|
+
backoff_iterator_generator = _emit_isolated_iterator_copies(backoff_iterator)
|
|
155
|
+
del backoff_iterator # prevent accidental use elsewhere
|
|
156
|
+
|
|
144
157
|
if should_retry_error is None:
|
|
145
158
|
should_retry_error = retry_never
|
|
146
159
|
|
|
@@ -152,7 +165,7 @@ def retry_with_delays_and_condition(backoff_iterator, should_retry_error=None):
|
|
|
152
165
|
"""
|
|
153
166
|
call_retryable_function = functools.partial(function, *args, **kwargs)
|
|
154
167
|
|
|
155
|
-
for delay in
|
|
168
|
+
for delay in next(backoff_iterator_generator):
|
|
156
169
|
try:
|
|
157
170
|
return call_retryable_function()
|
|
158
171
|
except Exception as e:
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
using Microsoft.Win32.SafeHandles;
|
|
1
2
|
using System;
|
|
2
3
|
using System.Collections;
|
|
3
4
|
using System.Collections.Generic;
|
|
5
|
+
using System.ComponentModel;
|
|
4
6
|
using System.Diagnostics;
|
|
5
7
|
using System.IO;
|
|
6
8
|
using System.Linq;
|
|
@@ -176,7 +178,8 @@ namespace Ansible.Basic
|
|
|
176
178
|
}
|
|
177
179
|
|
|
178
180
|
string dateTime = DateTime.Now.ToFileTime().ToString();
|
|
179
|
-
string dirName = String.Format("ansible-moduletmp-{0}-{1}", dateTime,
|
|
181
|
+
string dirName = String.Format("ansible-moduletmp-{0}-{1}-{2}", dateTime, System.Diagnostics.Process.GetCurrentProcess().Id,
|
|
182
|
+
new Random().Next(0, int.MaxValue));
|
|
180
183
|
string newTmpdir = Path.Combine(baseDir, dirName);
|
|
181
184
|
#if CORECLR
|
|
182
185
|
DirectoryInfo tmpdirInfo = Directory.CreateDirectory(newTmpdir);
|
|
@@ -309,8 +312,8 @@ namespace Ansible.Basic
|
|
|
309
312
|
|
|
310
313
|
public void ExitJson()
|
|
311
314
|
{
|
|
312
|
-
WriteLine(GetFormattedResults(Result));
|
|
313
315
|
CleanupFiles(null, null);
|
|
316
|
+
WriteLine(GetFormattedResults(Result));
|
|
314
317
|
Exit(0);
|
|
315
318
|
}
|
|
316
319
|
|
|
@@ -337,8 +340,8 @@ namespace Ansible.Basic
|
|
|
337
340
|
Result["exception"] = exception.ToString();
|
|
338
341
|
}
|
|
339
342
|
|
|
340
|
-
WriteLine(GetFormattedResults(Result));
|
|
341
343
|
CleanupFiles(null, null);
|
|
344
|
+
WriteLine(GetFormattedResults(Result));
|
|
342
345
|
Exit(1);
|
|
343
346
|
}
|
|
344
347
|
|
|
@@ -1444,10 +1447,22 @@ namespace Ansible.Basic
|
|
|
1444
1447
|
{
|
|
1445
1448
|
foreach (string path in cleanupFiles)
|
|
1446
1449
|
{
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1450
|
+
try
|
|
1451
|
+
{
|
|
1452
|
+
#if WINDOWS
|
|
1453
|
+
FileCleaner.Delete(path);
|
|
1454
|
+
#else
|
|
1455
|
+
if (File.Exists(path))
|
|
1456
|
+
File.Delete(path);
|
|
1457
|
+
else if (Directory.Exists(path))
|
|
1458
|
+
Directory.Delete(path, true);
|
|
1459
|
+
#endif
|
|
1460
|
+
}
|
|
1461
|
+
catch (Exception e)
|
|
1462
|
+
{
|
|
1463
|
+
Warn(string.Format("Failure cleaning temp path '{0}': {1} {2}",
|
|
1464
|
+
path, e.GetType().Name, e.Message));
|
|
1465
|
+
}
|
|
1451
1466
|
}
|
|
1452
1467
|
cleanupFiles = new List<string>();
|
|
1453
1468
|
}
|
|
@@ -1486,4 +1501,247 @@ namespace Ansible.Basic
|
|
|
1486
1501
|
Console.WriteLine(line);
|
|
1487
1502
|
}
|
|
1488
1503
|
}
|
|
1504
|
+
|
|
1505
|
+
#if WINDOWS
|
|
1506
|
+
// Windows is tricky as AVs and other software might still
|
|
1507
|
+
// have an open handle to files causing a failure. Use a
|
|
1508
|
+
// custom deletion mechanism to remove the files/dirs.
|
|
1509
|
+
// https://github.com/ansible/ansible/pull/80247
|
|
1510
|
+
internal static class FileCleaner
|
|
1511
|
+
{
|
|
1512
|
+
private const int FileDispositionInformation = 13;
|
|
1513
|
+
private const int FileDispositionInformationEx = 64;
|
|
1514
|
+
|
|
1515
|
+
private const int ERROR_INVALID_PARAMETER = 0x00000057;
|
|
1516
|
+
private const int ERROR_DIR_NOT_EMPTY = 0x00000091;
|
|
1517
|
+
|
|
1518
|
+
private static bool? _supportsPosixDelete = null;
|
|
1519
|
+
|
|
1520
|
+
[Flags()]
|
|
1521
|
+
public enum DispositionFlags : uint
|
|
1522
|
+
{
|
|
1523
|
+
FILE_DISPOSITION_DO_NOT_DELETE = 0x00000000,
|
|
1524
|
+
FILE_DISPOSITION_DELETE = 0x00000001,
|
|
1525
|
+
FILE_DISPOSITION_POSIX_SEMANTICS = 0x00000002,
|
|
1526
|
+
FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK = 0x00000004,
|
|
1527
|
+
FILE_DISPOSITION_ON_CLOSE = 0x00000008,
|
|
1528
|
+
FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE = 0x00000010,
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
[Flags()]
|
|
1532
|
+
public enum FileFlags : uint
|
|
1533
|
+
{
|
|
1534
|
+
FILE_FLAG_OPEN_NO_RECALL = 0x00100000,
|
|
1535
|
+
FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000,
|
|
1536
|
+
FILE_FLAG_SESSION_AWARE = 0x00800000,
|
|
1537
|
+
FILE_FLAG_POSIX_SEMANTICS = 0x01000000,
|
|
1538
|
+
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000,
|
|
1539
|
+
FILE_FLAG_DELETE_ON_CLOSE = 0x04000000,
|
|
1540
|
+
FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000,
|
|
1541
|
+
FILE_FLAG_RANDOM_ACCESS = 0x10000000,
|
|
1542
|
+
FILE_FLAG_NO_BUFFERING = 0x20000000,
|
|
1543
|
+
FILE_FLAG_OVERLAPPED = 0x40000000,
|
|
1544
|
+
FILE_FLAG_WRITE_THROUGH = 0x80000000,
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
1548
|
+
private static extern SafeFileHandle CreateFileW(
|
|
1549
|
+
[MarshalAs(UnmanagedType.LPWStr)] string lpFileName,
|
|
1550
|
+
FileSystemRights dwDesiredAccess,
|
|
1551
|
+
FileShare dwShareMode,
|
|
1552
|
+
IntPtr lpSecurityAttributes,
|
|
1553
|
+
FileMode dwCreationDisposition,
|
|
1554
|
+
uint dwFlagsAndAttributes,
|
|
1555
|
+
IntPtr hTemplateFile);
|
|
1556
|
+
|
|
1557
|
+
private static SafeFileHandle CreateFile(string path, FileSystemRights access, FileShare share, FileMode mode,
|
|
1558
|
+
FileAttributes attributes, FileFlags flags)
|
|
1559
|
+
{
|
|
1560
|
+
uint flagsAndAttributes = (uint)attributes | (uint)flags;
|
|
1561
|
+
SafeFileHandle handle = CreateFileW(path, access, share, IntPtr.Zero, mode, flagsAndAttributes,
|
|
1562
|
+
IntPtr.Zero);
|
|
1563
|
+
if (handle.IsInvalid)
|
|
1564
|
+
{
|
|
1565
|
+
int errCode = Marshal.GetLastWin32Error();
|
|
1566
|
+
string msg = string.Format("CreateFileW({0}) failed 0x{1:X8}: {2}",
|
|
1567
|
+
path, errCode, new Win32Exception(errCode).Message);
|
|
1568
|
+
throw new Win32Exception(errCode, msg);
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
return handle;
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
[DllImport("Ntdll.dll")]
|
|
1575
|
+
private static extern int NtSetInformationFile(
|
|
1576
|
+
SafeFileHandle FileHandle,
|
|
1577
|
+
out IntPtr IoStatusBlock,
|
|
1578
|
+
ref int FileInformation,
|
|
1579
|
+
int Length,
|
|
1580
|
+
int FileInformationClass);
|
|
1581
|
+
|
|
1582
|
+
[DllImport("Ntdll.dll")]
|
|
1583
|
+
private static extern int RtlNtStatusToDosError(
|
|
1584
|
+
int Status);
|
|
1585
|
+
|
|
1586
|
+
public static void Delete(string path)
|
|
1587
|
+
{
|
|
1588
|
+
if (File.Exists(path))
|
|
1589
|
+
{
|
|
1590
|
+
DeleteEntry(path, FileAttributes.ReadOnly);
|
|
1591
|
+
}
|
|
1592
|
+
else if (Directory.Exists(path))
|
|
1593
|
+
{
|
|
1594
|
+
Queue<DirectoryInfo> dirQueue = new Queue<DirectoryInfo>();
|
|
1595
|
+
dirQueue.Enqueue(new DirectoryInfo(path));
|
|
1596
|
+
bool nonEmptyDirs = false;
|
|
1597
|
+
HashSet<string> processedDirs = new HashSet<string>();
|
|
1598
|
+
|
|
1599
|
+
while (dirQueue.Count > 0)
|
|
1600
|
+
{
|
|
1601
|
+
DirectoryInfo currentDir = dirQueue.Dequeue();
|
|
1602
|
+
|
|
1603
|
+
bool deleteDir = true;
|
|
1604
|
+
if (processedDirs.Add(currentDir.FullName))
|
|
1605
|
+
{
|
|
1606
|
+
foreach (FileSystemInfo entry in currentDir.EnumerateFileSystemInfos())
|
|
1607
|
+
{
|
|
1608
|
+
// Tries to delete each entry. Failures are ignored
|
|
1609
|
+
// as they will be picked up when the dir is
|
|
1610
|
+
// deleted and not empty.
|
|
1611
|
+
if (entry is DirectoryInfo)
|
|
1612
|
+
{
|
|
1613
|
+
if ((entry.Attributes & FileAttributes.ReparsePoint) != 0)
|
|
1614
|
+
{
|
|
1615
|
+
// If it's a reparse point, just delete it directly.
|
|
1616
|
+
DeleteEntry(entry.FullName, entry.Attributes, ignoreFailure: true);
|
|
1617
|
+
}
|
|
1618
|
+
else
|
|
1619
|
+
{
|
|
1620
|
+
// Add the dir to the queue to delete and it will be processed next round.
|
|
1621
|
+
dirQueue.Enqueue((DirectoryInfo)entry);
|
|
1622
|
+
deleteDir = false;
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
else
|
|
1626
|
+
{
|
|
1627
|
+
DeleteEntry(entry.FullName, entry.Attributes, ignoreFailure: true);
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
if (deleteDir)
|
|
1633
|
+
{
|
|
1634
|
+
try
|
|
1635
|
+
{
|
|
1636
|
+
DeleteEntry(currentDir.FullName, FileAttributes.Directory);
|
|
1637
|
+
}
|
|
1638
|
+
catch (Win32Exception e)
|
|
1639
|
+
{
|
|
1640
|
+
if (e.NativeErrorCode == ERROR_DIR_NOT_EMPTY)
|
|
1641
|
+
{
|
|
1642
|
+
nonEmptyDirs = true;
|
|
1643
|
+
}
|
|
1644
|
+
else
|
|
1645
|
+
{
|
|
1646
|
+
throw;
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
else
|
|
1651
|
+
{
|
|
1652
|
+
dirQueue.Enqueue(currentDir);
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
if (nonEmptyDirs)
|
|
1657
|
+
{
|
|
1658
|
+
throw new IOException("Directory contains files still open by other processes");
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
private static void DeleteEntry(string path, FileAttributes attr, bool ignoreFailure = false)
|
|
1664
|
+
{
|
|
1665
|
+
try
|
|
1666
|
+
{
|
|
1667
|
+
if ((attr & FileAttributes.ReadOnly) != 0)
|
|
1668
|
+
{
|
|
1669
|
+
// Windows does not allow files set with ReadOnly to be
|
|
1670
|
+
// deleted. Pre-emptively unset the attribute.
|
|
1671
|
+
// FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE is quite new,
|
|
1672
|
+
// look at using that flag with POSIX delete once Server 2019
|
|
1673
|
+
// is the baseline.
|
|
1674
|
+
File.SetAttributes(path, FileAttributes.Normal);
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
// REPARSE - Only touch the symlink itself and not the target
|
|
1678
|
+
// BACKUP - Needed for dir handles, bypasses access checks for admins
|
|
1679
|
+
// DELETE_ON_CLOSE is not used as it interferes with the POSIX delete
|
|
1680
|
+
FileFlags flags = FileFlags.FILE_FLAG_OPEN_REPARSE_POINT |
|
|
1681
|
+
FileFlags.FILE_FLAG_BACKUP_SEMANTICS;
|
|
1682
|
+
|
|
1683
|
+
using (SafeFileHandle fileHandle = CreateFile(path, FileSystemRights.Delete,
|
|
1684
|
+
FileShare.ReadWrite | FileShare.Delete, FileMode.Open, FileAttributes.Normal, flags))
|
|
1685
|
+
{
|
|
1686
|
+
if (_supportsPosixDelete == null || _supportsPosixDelete == true)
|
|
1687
|
+
{
|
|
1688
|
+
// A POSIX delete will delete the filesystem entry even if
|
|
1689
|
+
// it's still opened by another process so favour that if
|
|
1690
|
+
// available.
|
|
1691
|
+
DispositionFlags deleteFlags = DispositionFlags.FILE_DISPOSITION_DELETE |
|
|
1692
|
+
DispositionFlags.FILE_DISPOSITION_POSIX_SEMANTICS;
|
|
1693
|
+
|
|
1694
|
+
SetInformationFile(fileHandle, FileDispositionInformationEx, (int)deleteFlags);
|
|
1695
|
+
if (_supportsPosixDelete == true)
|
|
1696
|
+
{
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
// FileDispositionInformation takes in a struct with only a BOOLEAN value.
|
|
1702
|
+
// Using an int will also do the same thing to set that flag to true.
|
|
1703
|
+
SetInformationFile(fileHandle, FileDispositionInformation, Int32.MaxValue);
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
catch
|
|
1707
|
+
{
|
|
1708
|
+
if (!ignoreFailure)
|
|
1709
|
+
{
|
|
1710
|
+
throw;
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
private static void SetInformationFile(SafeFileHandle handle, int infoClass, int value)
|
|
1716
|
+
{
|
|
1717
|
+
IntPtr ioStatusBlock = IntPtr.Zero;
|
|
1718
|
+
|
|
1719
|
+
int ntStatus = NtSetInformationFile(handle, out ioStatusBlock, ref value,
|
|
1720
|
+
Marshal.SizeOf(typeof(int)), infoClass);
|
|
1721
|
+
|
|
1722
|
+
if (ntStatus != 0)
|
|
1723
|
+
{
|
|
1724
|
+
int errCode = RtlNtStatusToDosError(ntStatus);
|
|
1725
|
+
|
|
1726
|
+
// The POSIX delete was added in Server 2016 (Win 10 14393/Redstone 1)
|
|
1727
|
+
// Mark this flag so we don't try again.
|
|
1728
|
+
if (infoClass == FileDispositionInformationEx && _supportsPosixDelete == null &&
|
|
1729
|
+
errCode == ERROR_INVALID_PARAMETER)
|
|
1730
|
+
{
|
|
1731
|
+
_supportsPosixDelete = false;
|
|
1732
|
+
return;
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
string msg = string.Format("NtSetInformationFile() failed 0x{0:X8}: {1}",
|
|
1736
|
+
errCode, new Win32Exception(errCode).Message);
|
|
1737
|
+
throw new Win32Exception(errCode, msg);
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
if (infoClass == FileDispositionInformationEx)
|
|
1741
|
+
{
|
|
1742
|
+
_supportsPosixDelete = true;
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
#endif
|
|
1489
1747
|
}
|
ansible/modules/uri.py
CHANGED
|
@@ -94,9 +94,16 @@ options:
|
|
|
94
94
|
force_basic_auth:
|
|
95
95
|
description:
|
|
96
96
|
- Force the sending of the Basic authentication header upon initial request.
|
|
97
|
-
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
- When this setting is C(false), this module will first try an unauthenticated request, and when the server replies
|
|
98
|
+
with an C(HTTP 401) error, it will submit the Basic authentication header.
|
|
99
|
+
- When this setting is C(true), this module will immediately send a Basic authentication header on the first
|
|
100
|
+
request.
|
|
101
|
+
- "Use this setting in any of the following scenarios:"
|
|
102
|
+
- You know the webservice endpoint always requires HTTP Basic authentication, and you want to speed up your
|
|
103
|
+
requests by eliminating the first roundtrip.
|
|
104
|
+
- The web service does not properly send an HTTP 401 error to your client, so Ansible's HTTP library will not
|
|
105
|
+
properly respond with HTTP credentials, and logins will fail.
|
|
106
|
+
- The webservice bans or rate-limits clients that cause any HTTP 401 errors.
|
|
100
107
|
type: bool
|
|
101
108
|
default: no
|
|
102
109
|
follow_redirects:
|
ansible/playbook/helpers.py
CHANGED
|
@@ -308,6 +308,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
|
|
|
308
308
|
# template the role name now, if needed
|
|
309
309
|
all_vars = variable_manager.get_vars(play=play, task=ir)
|
|
310
310
|
templar = Templar(loader=loader, variables=all_vars)
|
|
311
|
+
ir.post_validate(templar=templar)
|
|
311
312
|
ir._role_name = templar.template(ir._role_name)
|
|
312
313
|
|
|
313
314
|
# uses compiled list from object
|
|
@@ -187,6 +187,7 @@ class IncludedFile:
|
|
|
187
187
|
role_name = templar.template(role_name)
|
|
188
188
|
|
|
189
189
|
new_task = original_task.copy()
|
|
190
|
+
new_task.post_validate(templar=templar)
|
|
190
191
|
new_task._role_name = role_name
|
|
191
192
|
for from_arg in new_task.FROM_ARGS:
|
|
192
193
|
if from_arg in include_args:
|
ansible/playbook/role_include.py
CHANGED
|
@@ -52,9 +52,9 @@ class IncludeRole(TaskInclude):
|
|
|
52
52
|
# ATTRIBUTES
|
|
53
53
|
|
|
54
54
|
# private as this is a 'module options' vs a task property
|
|
55
|
-
allow_duplicates = NonInheritableFieldAttribute(isa='bool', default=True, private=True)
|
|
56
|
-
public = NonInheritableFieldAttribute(isa='bool', default=False, private=True)
|
|
57
|
-
rolespec_validate = NonInheritableFieldAttribute(isa='bool', default=True)
|
|
55
|
+
allow_duplicates = NonInheritableFieldAttribute(isa='bool', default=True, private=True, always_post_validate=True)
|
|
56
|
+
public = NonInheritableFieldAttribute(isa='bool', default=False, private=True, always_post_validate=True)
|
|
57
|
+
rolespec_validate = NonInheritableFieldAttribute(isa='bool', default=True, private=True, always_post_validate=True)
|
|
58
58
|
|
|
59
59
|
def __init__(self, block=None, role=None, task_include=None):
|
|
60
60
|
|
|
@@ -1125,7 +1125,7 @@ class ActionBase(ABC):
|
|
|
1125
1125
|
remote_files.append(remote_async_module_path)
|
|
1126
1126
|
|
|
1127
1127
|
async_limit = self._task.async_val
|
|
1128
|
-
async_jid =
|
|
1128
|
+
async_jid = f'j{random.randint(0, 999999999999)}'
|
|
1129
1129
|
|
|
1130
1130
|
# call the interpreter for async_wrapper directly
|
|
1131
1131
|
# this permits use of a script for an interpreter on non-Linux platforms
|
|
@@ -5,6 +5,8 @@ DOCUMENTATION:
|
|
|
5
5
|
short_description: get a path's base name
|
|
6
6
|
description:
|
|
7
7
|
- Returns the last name component of a path, what is left in the string that is not 'dirname'.
|
|
8
|
+
notes:
|
|
9
|
+
- The result of this filter is different from the Unix basename program; where basename for C(/foo/bar/) returns C(bar), the basename filter returns an empty string (C('')).
|
|
8
10
|
options:
|
|
9
11
|
_input:
|
|
10
12
|
description: A path.
|
|
@@ -15,7 +17,7 @@ DOCUMENTATION:
|
|
|
15
17
|
plugin: ansible.builtin.dirname
|
|
16
18
|
EXAMPLES: |
|
|
17
19
|
|
|
18
|
-
# To get the last name of a file path, like 'foo.txt' out of '/etc/asdf/foo.txt'
|
|
20
|
+
# To get the last name of a file path, like 'foo.txt' out of '/etc/asdf/foo.txt'.
|
|
19
21
|
{{ mypath | basename }}
|
|
20
22
|
|
|
21
23
|
RETURN:
|
|
@@ -5,6 +5,8 @@ DOCUMENTATION:
|
|
|
5
5
|
short_description: get a path's directory name
|
|
6
6
|
description:
|
|
7
7
|
- Returns the 'head' component of a path, basically everything that is not the 'basename'.
|
|
8
|
+
notes:
|
|
9
|
+
- The result of this filter is different from the Unix dirname program; where dirname for C(/foo/bar/) returns C(/foo), the dirname filter returns the full path (C(/foo/bar/)).
|
|
8
10
|
options:
|
|
9
11
|
_input:
|
|
10
12
|
description: A path.
|
|
@@ -15,7 +17,7 @@ DOCUMENTATION:
|
|
|
15
17
|
plugin_type: filter
|
|
16
18
|
EXAMPLES: |
|
|
17
19
|
|
|
18
|
-
# To get the dir name of a file path, like '/etc/asdf' out of '/etc/asdf/foo.txt'
|
|
20
|
+
# To get the dir name of a file path, like '/etc/asdf' out of '/etc/asdf/foo.txt'.
|
|
19
21
|
{{ mypath | dirname }}
|
|
20
22
|
|
|
21
23
|
RETURN:
|
|
@@ -197,18 +197,31 @@ def _parse_content(content):
|
|
|
197
197
|
'''
|
|
198
198
|
password = content
|
|
199
199
|
salt = None
|
|
200
|
+
ident = None
|
|
200
201
|
|
|
201
202
|
salt_slug = u' salt='
|
|
203
|
+
ident_slug = u' ident='
|
|
204
|
+
rem = u''
|
|
202
205
|
try:
|
|
203
206
|
sep = content.rindex(salt_slug)
|
|
204
207
|
except ValueError:
|
|
205
208
|
# No salt
|
|
206
209
|
pass
|
|
207
210
|
else:
|
|
208
|
-
|
|
211
|
+
rem = content[sep + len(salt_slug):]
|
|
209
212
|
password = content[:sep]
|
|
210
213
|
|
|
211
|
-
|
|
214
|
+
if rem:
|
|
215
|
+
try:
|
|
216
|
+
sep = rem.rindex(ident_slug)
|
|
217
|
+
except ValueError:
|
|
218
|
+
# no ident
|
|
219
|
+
salt = rem
|
|
220
|
+
else:
|
|
221
|
+
ident = rem[sep + len(ident_slug):]
|
|
222
|
+
salt = rem[:sep]
|
|
223
|
+
|
|
224
|
+
return password, salt, ident
|
|
212
225
|
|
|
213
226
|
|
|
214
227
|
def _format_content(password, salt, encrypt=None, ident=None):
|
|
@@ -338,47 +351,73 @@ class LookupModule(LookupBase):
|
|
|
338
351
|
self.set_options(var_options=variables, direct=kwargs)
|
|
339
352
|
|
|
340
353
|
for term in terms:
|
|
354
|
+
|
|
355
|
+
changed = None
|
|
341
356
|
relpath, params = self._parse_parameters(term)
|
|
342
357
|
path = self._loader.path_dwim(relpath)
|
|
343
358
|
b_path = to_bytes(path, errors='surrogate_or_strict')
|
|
344
359
|
chars = _gen_candidate_chars(params['chars'])
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
360
|
+
ident = None
|
|
361
|
+
first_process = None
|
|
362
|
+
lockfile = None
|
|
363
|
+
|
|
364
|
+
try:
|
|
365
|
+
# make sure only one process finishes all the job first
|
|
366
|
+
first_process, lockfile = _get_lock(b_path)
|
|
367
|
+
content = _read_password_file(b_path)
|
|
368
|
+
|
|
369
|
+
if content is None or b_path == to_bytes('/dev/null'):
|
|
370
|
+
plaintext_password = random_password(params['length'], chars, params['seed'])
|
|
371
|
+
salt = None
|
|
372
|
+
changed = True
|
|
373
|
+
else:
|
|
374
|
+
plaintext_password, salt, ident = _parse_content(content)
|
|
375
|
+
|
|
376
|
+
encrypt = params['encrypt']
|
|
377
|
+
if encrypt and not salt:
|
|
378
|
+
changed = True
|
|
379
|
+
try:
|
|
380
|
+
salt = random_salt(BaseHash.algorithms[encrypt].salt_size)
|
|
381
|
+
except KeyError:
|
|
382
|
+
salt = random_salt()
|
|
383
|
+
|
|
384
|
+
ident = params['ident']
|
|
385
|
+
if encrypt and not ident:
|
|
386
|
+
changed = True
|
|
387
|
+
try:
|
|
388
|
+
ident = BaseHash.algorithms[encrypt].implicit_ident
|
|
389
|
+
except KeyError:
|
|
390
|
+
ident = None
|
|
391
|
+
|
|
392
|
+
encrypt = params['encrypt']
|
|
393
|
+
if encrypt and not salt:
|
|
394
|
+
changed = True
|
|
395
|
+
try:
|
|
396
|
+
salt = random_salt(BaseHash.algorithms[encrypt].salt_size)
|
|
397
|
+
except KeyError:
|
|
398
|
+
salt = random_salt()
|
|
399
|
+
|
|
400
|
+
if not ident:
|
|
401
|
+
ident = params['ident']
|
|
402
|
+
elif params['ident'] and ident != params['ident']:
|
|
403
|
+
raise AnsibleError('The ident parameter provided (%s) does not match the stored one (%s).' % (ident, params['ident']))
|
|
404
|
+
|
|
405
|
+
if encrypt and not ident:
|
|
406
|
+
try:
|
|
407
|
+
ident = BaseHash.algorithms[encrypt].implicit_ident
|
|
408
|
+
except KeyError:
|
|
409
|
+
ident = None
|
|
410
|
+
if ident:
|
|
411
|
+
changed = True
|
|
412
|
+
|
|
413
|
+
if changed and b_path != to_bytes('/dev/null'):
|
|
414
|
+
content = _format_content(plaintext_password, salt, encrypt=encrypt, ident=ident)
|
|
415
|
+
_write_password_file(b_path, content)
|
|
416
|
+
|
|
417
|
+
finally:
|
|
418
|
+
if first_process:
|
|
419
|
+
# let other processes continue
|
|
420
|
+
_release_lock(lockfile)
|
|
382
421
|
|
|
383
422
|
if encrypt:
|
|
384
423
|
password = do_encrypt(plaintext_password, encrypt, salt=salt, ident=ident)
|
ansible/release.py
CHANGED
ansible/utils/encrypt.py
CHANGED
|
@@ -240,12 +240,15 @@ class PasslibHash(BaseHash):
|
|
|
240
240
|
settings['ident'] = ident
|
|
241
241
|
|
|
242
242
|
# starting with passlib 1.7 'using' and 'hash' should be used instead of 'encrypt'
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
243
|
+
try:
|
|
244
|
+
if hasattr(self.crypt_algo, 'hash'):
|
|
245
|
+
result = self.crypt_algo.using(**settings).hash(secret)
|
|
246
|
+
elif hasattr(self.crypt_algo, 'encrypt'):
|
|
247
|
+
result = self.crypt_algo.encrypt(secret, **settings)
|
|
248
|
+
else:
|
|
249
|
+
raise AnsibleError("installed passlib version %s not supported" % passlib.__version__)
|
|
250
|
+
except ValueError as e:
|
|
251
|
+
raise AnsibleError("Could not hash the secret.", orig_exc=e)
|
|
249
252
|
|
|
250
253
|
# passlib.hash should always return something or raise an exception.
|
|
251
254
|
# Still ensure that there is always a result.
|
|
@@ -3,20 +3,20 @@ ansible/__main__.py,sha256=IvyRvY64pT0on94qCLibxgDJ0-7_2CRoaZ5kfGOl54Q,1395
|
|
|
3
3
|
ansible/constants.py,sha256=JLIDnuSz3_PbtXWsL4vnvVBbxlh3lSrJREd7T73atEI,8293
|
|
4
4
|
ansible/context.py,sha256=OzSlaA_GgGRyyf5I209sy19_eGOX6HXn441W9w_FcvU,2018
|
|
5
5
|
ansible/keyword_desc.yml,sha256=FYY0Ld1Xc3AxJ_Tefz78kRSYzIKGS8qcPtVk370J118,7367
|
|
6
|
-
ansible/release.py,sha256=
|
|
6
|
+
ansible/release.py,sha256=MN3F2lsQhXwkSVF7FWDp8Ub67eOlE9GPccTYxfvMFrI,922
|
|
7
7
|
ansible/_vendor/__init__.py,sha256=wJRKH7kI9OzYVY9hgSchOsTNTmTnugpPLGYj9Y5akX0,2086
|
|
8
8
|
ansible/cli/__init__.py,sha256=yDt8_ny7HauC9Aj-MMHQr3Y6gDQADfdEU0O1BfzkSwA,27957
|
|
9
9
|
ansible/cli/adhoc.py,sha256=pGW6eysaireovp4sVsUuntg-l1o7DSujuhxVhVC2zsM,8230
|
|
10
10
|
ansible/cli/config.py,sha256=zH9hNrXyd_E3WAbBSQ6_RqZDFchcMP9ZbkkpDWVXnSY,22109
|
|
11
11
|
ansible/cli/console.py,sha256=rc-6s-Exf9b8lead40RyfugZdU1-cMoN-kA1iI8Uhs8,21941
|
|
12
|
-
ansible/cli/doc.py,sha256=
|
|
13
|
-
ansible/cli/galaxy.py,sha256=
|
|
12
|
+
ansible/cli/doc.py,sha256=PerGaymHYb5TtKKbcuXs93VXMSYVapDEapQGZ4qYoxY,60432
|
|
13
|
+
ansible/cli/galaxy.py,sha256=NaHPqyzE6fHLGyS0gl_ofn-v0LAcN2tNJjF-KkvUpso,92453
|
|
14
14
|
ansible/cli/inventory.py,sha256=Qh0QSMqG8Y8Wy6XXRJvP0HBlMntuRBVkq4ip2edExWo,16417
|
|
15
|
-
ansible/cli/playbook.py,sha256=
|
|
15
|
+
ansible/cli/playbook.py,sha256=EqpEZ8Rb1q-0HBcnCb7QxP7OzkoJXw9P33uiQBtUmMM,10495
|
|
16
16
|
ansible/cli/pull.py,sha256=TI3xfqcO-f8I60gRvVdiAEELghSq5Mlb8YPX6SdiitM,17010
|
|
17
17
|
ansible/cli/vault.py,sha256=1x9Mn6KEdtGhyT9f8Z5ESWp52ft51uJjceinPcEObq4,22400
|
|
18
18
|
ansible/cli/arguments/__init__.py,sha256=CL2cOeYgVnD4r0iJTzEjjldSkJjGKPZc_t44UKSF4n8,221
|
|
19
|
-
ansible/cli/arguments/option_helpers.py,sha256=
|
|
19
|
+
ansible/cli/arguments/option_helpers.py,sha256=zu0VTuuUHCvw5X_nvcNe5qjCPmjJPNdHVmOUgDRsa7E,18107
|
|
20
20
|
ansible/cli/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
21
|
ansible/cli/scripts/ansible_connection_cli_stub.py,sha256=zaet96eOYPUk4v5p4AwPiDn3RAKtDWl31Z0L_By-4SM,13476
|
|
22
22
|
ansible/collections/__init__.py,sha256=t8x1TZiQQa9vaQAm7ECYoP3MCdzquEtAlPzG9HzEV50,814
|
|
@@ -48,19 +48,19 @@ ansible/executor/powershell/become_wrapper.ps1,sha256=c0a4nYlFbpoZHzRpRHKgndZbYB
|
|
|
48
48
|
ansible/executor/powershell/bootstrap_wrapper.ps1,sha256=8xudggKTMGibFCspF5m2BrJALARd67Rj062qCbKM2eY,487
|
|
49
49
|
ansible/executor/powershell/coverage_wrapper.ps1,sha256=EM4X0tEWw2vMrNVQoxen6y8rYxTB8kEjx3PHoH4Mx5c,8296
|
|
50
50
|
ansible/executor/powershell/exec_wrapper.ps1,sha256=6jHQZntr_WTD6tRlVV3mxgsvSZksdVtJM95HC_b9N2E,10128
|
|
51
|
-
ansible/executor/powershell/module_manifest.py,sha256=
|
|
51
|
+
ansible/executor/powershell/module_manifest.py,sha256=GgOc4a03ryNAP1Tzm9wl4p9MSoTtc_md93ERCpTGnC0,17432
|
|
52
52
|
ansible/executor/powershell/module_powershell_wrapper.ps1,sha256=6dPo5tSV6B-KipCBrz3iTvOM2Y_yj-gn_u7OKKmfjAs,3179
|
|
53
53
|
ansible/executor/powershell/module_script_wrapper.ps1,sha256=Th1KTjKE3aOul6kRMunUj0EXFf7Jn7WOH-byCap9pqU,872
|
|
54
54
|
ansible/executor/powershell/module_wrapper.ps1,sha256=JkCL_6aAZoXgQmFjU5hgEzTCMdzzh46G44rAJ_tfSzU,9038
|
|
55
55
|
ansible/executor/process/__init__.py,sha256=1lMXN1i2fFqslda4BmeI5tpYMFP95D5Wpr1AjDJi-SQ,833
|
|
56
56
|
ansible/executor/process/worker.py,sha256=mO9-GR7bDVxgDh65ROBhEBNg8qb9FWkkaP1vb_sklxY,8843
|
|
57
57
|
ansible/galaxy/__init__.py,sha256=_ccTedn8dUGGtkmHcQLIkeje_YD0TYSXlvCl1AOY5fE,2533
|
|
58
|
-
ansible/galaxy/api.py,sha256=
|
|
58
|
+
ansible/galaxy/api.py,sha256=deSYsFinaJodT2Y9-XnOerWIwYY8V2AWQ_9kZI0pWCE,39872
|
|
59
59
|
ansible/galaxy/role.py,sha256=roEhuloz2-UHLdNwK7pqRCYsOLpu_Xg6sC_nyE5A30w,19086
|
|
60
60
|
ansible/galaxy/token.py,sha256=K0dAwD3Fjkn3Zs2N9sG98UesSWfAukie47QGyYpIf0M,6167
|
|
61
61
|
ansible/galaxy/user_agent.py,sha256=x7cJzzpnTngHcwqSUd2hg0i28Dv0tbAyBdke5CSiNhM,813
|
|
62
|
-
ansible/galaxy/collection/__init__.py,sha256=
|
|
63
|
-
ansible/galaxy/collection/concrete_artifact_manager.py,sha256=
|
|
62
|
+
ansible/galaxy/collection/__init__.py,sha256=VeKnmVeCyg0PFFus_5xmkt0kAsUiX59NRQB5kQVUUMc,75824
|
|
63
|
+
ansible/galaxy/collection/concrete_artifact_manager.py,sha256=btOQ__jt6gQgmHGitGoOQTQ3Ezvc1BssPaJLEs7nKl8,29515
|
|
64
64
|
ansible/galaxy/collection/galaxy_api_proxy.py,sha256=HWnMiWIEt1YW7srbnFXjRsgpSC-3Iwj7-wkrkmVtXkA,7972
|
|
65
65
|
ansible/galaxy/collection/gpg.py,sha256=1wk22RJnX--FsB-4h_EdaT05PWlx9AMxhfH3H7db1i4,7312
|
|
66
66
|
ansible/galaxy/data/collections_galaxy_meta.yml,sha256=UNkGJWZEWSRbyiPbpoqLQdBR4DbAg9DeIB38HSZycoI,4018
|
|
@@ -125,9 +125,9 @@ ansible/galaxy/data/network/tests/inventory,sha256=4CIzgZsaCYREEFSRkYE_fMe6Ng8hK
|
|
|
125
125
|
ansible/galaxy/data/network/tests/test.yml.j2,sha256=rjvtKhlkGT_CI_dgBLHX14eI41S6jFPypqESN8Uztgg,199
|
|
126
126
|
ansible/galaxy/data/network/vars/main.yml.j2,sha256=3qtsgmeZTZ37OdmQNpmI9R6XxOEzJcOhjjGQYGFb85w,36
|
|
127
127
|
ansible/galaxy/dependency_resolution/__init__.py,sha256=HDqJKqsDP4UHTCnoGq6c3rGpVwOI2DdYaH-4s7tkL0A,2196
|
|
128
|
-
ansible/galaxy/dependency_resolution/dataclasses.py,sha256=
|
|
128
|
+
ansible/galaxy/dependency_resolution/dataclasses.py,sha256=qta8D8F-1uyaWh9SujIfPFGfYB3RYowQsNdKw4zJplk,21699
|
|
129
129
|
ansible/galaxy/dependency_resolution/errors.py,sha256=7QSmgfeGrespnlr-eqKRQP3fopl4G_wRIiTSDILBrKs,717
|
|
130
|
-
ansible/galaxy/dependency_resolution/providers.py,sha256=
|
|
130
|
+
ansible/galaxy/dependency_resolution/providers.py,sha256=aY2FQgkucYMMGGjlK8v0V8bzR6pL1PAcaCner_vHIS8,24946
|
|
131
131
|
ansible/galaxy/dependency_resolution/reporters.py,sha256=q-jyfsRu5qve8nikZ0_cBQar3dOX4_WMSAUXZd9xlqg,687
|
|
132
132
|
ansible/galaxy/dependency_resolution/resolvers.py,sha256=XHBYqltTS7AV4mSiAF2ImXP53W6Yadog_rrqKxrpZU0,676
|
|
133
133
|
ansible/galaxy/dependency_resolution/versioning.py,sha256=fGmuNhgwHaCKfHb6uH91A7ztuB86nDgiMW3htSI06DY,1779
|
|
@@ -139,8 +139,8 @@ ansible/inventory/host.py,sha256=wXJp6kpSaZtDr4JNsgdAuhi5MzQ9LTQzaAH10zoVbIA,505
|
|
|
139
139
|
ansible/inventory/manager.py,sha256=tGwhBR6poLuG_i4jZ5RGOG-rH4gu4DBfT0-4iLLZZMs,29490
|
|
140
140
|
ansible/module_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
141
141
|
ansible/module_utils/_text.py,sha256=VCjTJovTxGfLahnzyrvOehpwTXLpRzMUug6wheirt4A,565
|
|
142
|
-
ansible/module_utils/ansible_release.py,sha256=
|
|
143
|
-
ansible/module_utils/api.py,sha256=
|
|
142
|
+
ansible/module_utils/ansible_release.py,sha256=MN3F2lsQhXwkSVF7FWDp8Ub67eOlE9GPccTYxfvMFrI,922
|
|
143
|
+
ansible/module_utils/api.py,sha256=BTo7stVOANbtd-ngZslaqx70r9t5gfvo44cKyu5SFjU,5837
|
|
144
144
|
ansible/module_utils/basic.py,sha256=dGTJD-84x2a0hqKgoqB6PNhjmOEqYuuf2EgWyX5zVC8,86621
|
|
145
145
|
ansible/module_utils/connection.py,sha256=XHxMlyAdwLiXDSo8jBMkV61-lz_0FDJUYH1B152UGJU,8430
|
|
146
146
|
ansible/module_utils/errors.py,sha256=LYv9EWkvBRAmYW6qQY4Vz2hMULqkAGkMG2Opf86Ep4M,3396
|
|
@@ -180,7 +180,7 @@ ansible/module_utils/compat/selinux.py,sha256=FDJE4H5flBECz6k11dI0BObuCZkhToAEcN
|
|
|
180
180
|
ansible/module_utils/compat/typing.py,sha256=w8Yy7Rm5MtsgTFA6rVe0YTRSY3FaatZeLCyS-rAOokM,741
|
|
181
181
|
ansible/module_utils/compat/version.py,sha256=UKmpFhMk-Y9vuYfgmaT5NYctS3WFU-Xn9ycEzl01Nlc,12787
|
|
182
182
|
ansible/module_utils/csharp/Ansible.AccessToken.cs,sha256=bGKFk92EEiG4omGPdUk3YZ_GzrDhzn0mO5yu_qC2rVs,16066
|
|
183
|
-
ansible/module_utils/csharp/Ansible.Basic.cs,sha256=
|
|
183
|
+
ansible/module_utils/csharp/Ansible.Basic.cs,sha256=slTfWp_cpHuxlzjSQMT5VMqIBKg8XE0Ynsa_w0WoX7Q,77734
|
|
184
184
|
ansible/module_utils/csharp/Ansible.Become.cs,sha256=py7VKd4XjFahf8blwX_Zj5pUvVVCA547SaGDCpMJfME,30728
|
|
185
185
|
ansible/module_utils/csharp/Ansible.Privilege.cs,sha256=xdreXzRqhvp2kpGWVqFrDrnZozb_JANVVhH_7MDZ4fw,19592
|
|
186
186
|
ansible/module_utils/csharp/Ansible.Process.cs,sha256=YTTK2LNwxlUa1Xdvf3KER71JFyrZSyrsrZtEX5vkoqo,19568
|
|
@@ -338,7 +338,7 @@ ansible/modules/sysvinit.py,sha256=xFhfQLwTdcbV7xVdlItHeLFDdU0t3rjiWoY1qocCIs0,1
|
|
|
338
338
|
ansible/modules/tempfile.py,sha256=D4l0CHjp9AIC0o1BBDvw8UWQkWpilnc9VdmxNChxq6E,3502
|
|
339
339
|
ansible/modules/template.py,sha256=k0h7j9n9v2efC0f1boCsTq2NwgTLkFuQxgxmUgq4nZE,3171
|
|
340
340
|
ansible/modules/unarchive.py,sha256=77kv3buZezvXMb-8KmFvV-rtiyVdcvynaOw84bzk2Js,43815
|
|
341
|
-
ansible/modules/uri.py,sha256=
|
|
341
|
+
ansible/modules/uri.py,sha256=jU4WykeTWqa0KAkxF5hrmfJ19hbsn5-ALpZAorkT6fY,28158
|
|
342
342
|
ansible/modules/user.py,sha256=qOnO_lxilSM--HAuO7UtC5g8PKaMS8hiD7QNpnTbyrY,116313
|
|
343
343
|
ansible/modules/validate_argument_spec.py,sha256=nnRXyK1LmSSLxr2wsHnKdz8hbtvRft2_ifoTDhMm71k,3067
|
|
344
344
|
ansible/modules/wait_for.py,sha256=8xLXpbwlXiL4Wwan0cQgpHoQaB6xaqn9Oc5TM4W9Xis,26530
|
|
@@ -370,13 +370,13 @@ ansible/playbook/collectionsearch.py,sha256=dfTz79Af_3iurr0YZDcNWsAffaRlbrffXz_K
|
|
|
370
370
|
ansible/playbook/conditional.py,sha256=0uRGEegPxrKxZEEFNl50VcIsFrhQBScb7MsMdbaDsHg,9982
|
|
371
371
|
ansible/playbook/handler.py,sha256=-gh7wMi0ZGLgVt3otzcA1ChyYOPYOchnVJREuuxHtZo,2249
|
|
372
372
|
ansible/playbook/handler_task_include.py,sha256=grMLsGCeEb2YG4f_-Q6n7YZTRfoKFmXvSdLKivQJ1FA,1475
|
|
373
|
-
ansible/playbook/helpers.py,sha256=
|
|
374
|
-
ansible/playbook/included_file.py,sha256=
|
|
373
|
+
ansible/playbook/helpers.py,sha256=jotNlRQmN4SoHpbuMv0Mx_pfVjIjAGpf3TYwc9Iw7xQ,16744
|
|
374
|
+
ansible/playbook/included_file.py,sha256=r3A19Iy_9BtUBfIMeZL_Ep755MfkzVJAYE3uSvHc4KA,11591
|
|
375
375
|
ansible/playbook/loop_control.py,sha256=BJv5j6jFNo_M5nK6HaXCsNdaM-JClMcCv9D21TCRku4,1764
|
|
376
376
|
ansible/playbook/play.py,sha256=CI6pJarF19Mcc7vAeGsgBSPXRH4x2jzXF3kPXTF7pOE,15717
|
|
377
377
|
ansible/playbook/play_context.py,sha256=1YK_3nV5OPY8vnqiuC1JWb640KitWZaATSMEAUpylcs,14710
|
|
378
378
|
ansible/playbook/playbook_include.py,sha256=QK6rccZUtKra60_VvF0Cd4LZ9yaH_t4Sq9evmald6jA,7565
|
|
379
|
-
ansible/playbook/role_include.py,sha256=
|
|
379
|
+
ansible/playbook/role_include.py,sha256=F5gpKxdkJunyuxLTRUxMbiRujZUTlaFl02WnSvU8lEE,7904
|
|
380
380
|
ansible/playbook/taggable.py,sha256=7pCyNHhAs2TBTV6h4LT9gd9v2syY_gJZqg5x6p0w9C4,3169
|
|
381
381
|
ansible/playbook/task.py,sha256=e0AV5WzEWQ9QimuBryZeJ75qqysGbioScAZ4pTtxHOc,19997
|
|
382
382
|
ansible/playbook/task_include.py,sha256=Qs6CpbwRcuyZeVaDGDclR5V-ZjZErncLpvmAGywRIro,6043
|
|
@@ -388,7 +388,7 @@ ansible/playbook/role/requirement.py,sha256=T_PljD0hgVstV325iALyCUKkAZxzLLlcQjpp
|
|
|
388
388
|
ansible/plugins/__init__.py,sha256=i984DJbHLaZ1rirIIGSk3mS-dRAYeCWMmS6jPWkK2bw,5117
|
|
389
389
|
ansible/plugins/list.py,sha256=RBasSwr51nQLK9I3NDmLGmUo1ntzGiprMpdc2Mny64c,8635
|
|
390
390
|
ansible/plugins/loader.py,sha256=OfKF01mtOgr787iiHvd-l7FvyN6eJpGIe5L5gyNGE-U,71936
|
|
391
|
-
ansible/plugins/action/__init__.py,sha256=
|
|
391
|
+
ansible/plugins/action/__init__.py,sha256=8JJS8PD4yhrn02sp60_qqW6VRoXEXkYFHMUecp_znxM,68790
|
|
392
392
|
ansible/plugins/action/add_host.py,sha256=5r_HKyiIyiKAj3PZMshuepYliTppjBp6-o8b9_aFTJA,3667
|
|
393
393
|
ansible/plugins/action/assemble.py,sha256=YlgV3zbB_gOtVFycTB9eC1u16essMRqYMDhb6ur4UVg,6573
|
|
394
394
|
ansible/plugins/action/assert.py,sha256=l3BIU0ZfG7z832NpfvfvP8RfJwkBe-T5_PRsTAbyFZo,3891
|
|
@@ -460,7 +460,7 @@ ansible/plugins/doc_fragments/vars_plugin_staging.py,sha256=zsCh9HKY_FUVrs5VJF7_
|
|
|
460
460
|
ansible/plugins/filter/__init__.py,sha256=c0E2OnVoIwjxAxvF-H9WkvN_MnJSSVdbHRTEoztEdEY,510
|
|
461
461
|
ansible/plugins/filter/b64decode.yml,sha256=nkL5EqrNcDdQ7H8R1TlkPs9oThtcuJ4L-6pSV2tG7tM,927
|
|
462
462
|
ansible/plugins/filter/b64encode.yml,sha256=QgnRLocoe5rIBXQkyCRJp0afbaDObVUxe5gKJ4io9C8,543
|
|
463
|
-
ansible/plugins/filter/basename.yml,sha256=
|
|
463
|
+
ansible/plugins/filter/basename.yml,sha256=_Q5IxOAepLsSL8ENDd3MmgCXxbjazfSmE8gBSWJAlOU,809
|
|
464
464
|
ansible/plugins/filter/bool.yml,sha256=PkoiSy7pwnkiyOEN59cRNaY56TAWEtzFhIuYxfLQTnc,631
|
|
465
465
|
ansible/plugins/filter/checksum.yml,sha256=CMk4pVtvOWGyXoqH98KlvGOjMzMBDLTYl82SkzlOC0k,524
|
|
466
466
|
ansible/plugins/filter/combinations.yml,sha256=LttrIICjapNtZHWnvJD-C9Pv3PIKP16i8WAXcU7TSLI,780
|
|
@@ -469,7 +469,7 @@ ansible/plugins/filter/comment.yml,sha256=3oYuPwp_stX1rOkkVinSFXoZLcpMvk93oj9RpF
|
|
|
469
469
|
ansible/plugins/filter/core.py,sha256=7Bezfm4-w2mmeCsJZXIktLWhJyd-nGSfPuBjZ14nONs,21370
|
|
470
470
|
ansible/plugins/filter/dict2items.yml,sha256=tUWJLVdZQ4sO0GYrJaC9wALSitTactFTdsrI9xb09KM,1314
|
|
471
471
|
ansible/plugins/filter/difference.yml,sha256=a72tTaJswM3SS2NhFG2lQ5kRUnf5yYaouwS19f97bQA,1024
|
|
472
|
-
ansible/plugins/filter/dirname.yml,sha256=
|
|
472
|
+
ansible/plugins/filter/dirname.yml,sha256=Z7p7ay8s3_Zee6gIu7qr4wUC-an7lwLwuoVmgHQCKyg,820
|
|
473
473
|
ansible/plugins/filter/encryption.py,sha256=dFznqIS2Q0XPnT-3yc77BHzF3sdfDXTVN_8AtfUdyCo,2693
|
|
474
474
|
ansible/plugins/filter/expanduser.yml,sha256=P_AD4oBYbADe2f7pzHjbnkM95t8P1OzN0S3AHLT6C9o,506
|
|
475
475
|
ansible/plugins/filter/expandvars.yml,sha256=KqtMP_oHLXsKFxHn_3iKX5PPGV5l-yhWX1jPD-sJOeE,569
|
|
@@ -556,7 +556,7 @@ ansible/plugins/lookup/items.py,sha256=TIeLaiW2PXHxZQ7Rd2cvoNg2blHtYkzSP8uI783xa
|
|
|
556
556
|
ansible/plugins/lookup/lines.py,sha256=InvhZbBIIKpz-y0sTzCQRQmoG0RuOwGyvdpWXh_bmxk,2214
|
|
557
557
|
ansible/plugins/lookup/list.py,sha256=7BylyTlSdmafiMXCVG7DIwqF2I5nAiDj9ot3nE_r3Ok,1142
|
|
558
558
|
ansible/plugins/lookup/nested.py,sha256=pYhPRzrJ8JeILXAiBMvVK1-WXinvuus47SVqQopILnc,2702
|
|
559
|
-
ansible/plugins/lookup/password.py,sha256=
|
|
559
|
+
ansible/plugins/lookup/password.py,sha256=DIgDQXGqnHTS6YaSxaBzyH-5UL1xevJ_Jl5NXo9Ny-E,18098
|
|
560
560
|
ansible/plugins/lookup/pipe.py,sha256=542Mcosq93UAwJlv7oE5JjpS3KKQTPfBfyZX7qE89tQ,3004
|
|
561
561
|
ansible/plugins/lookup/random_choice.py,sha256=c7Cq2ufuDX1cOlMqvbjeoDT0B4r6jllprnl0FysUSgA,1574
|
|
562
562
|
ansible/plugins/lookup/sequence.py,sha256=klnr8ewcq7gz5S_Z-CMSR8OsSR2KgLSUrhDQ4xRV1zU,9171
|
|
@@ -642,7 +642,7 @@ ansible/utils/cmd_functions.py,sha256=rh7_j2C93vVt-nueP9L2Ouz3Y_QAEQ7kI4dZTu2S6M
|
|
|
642
642
|
ansible/utils/color.py,sha256=sBoONbLleSeCVPJMzyP4pvwSs1tKiSYwpCmAUq1Tzy0,4110
|
|
643
643
|
ansible/utils/context_objects.py,sha256=1-j7SRLgVJU3J5b2PkXMcXS391z435SsKsYIU1m0eCw,3119
|
|
644
644
|
ansible/utils/display.py,sha256=xKJFz0qLq2e4pXYCUUnQEmYSD0cIhm7Fi0QypVWHLFQ,19188
|
|
645
|
-
ansible/utils/encrypt.py,sha256=
|
|
645
|
+
ansible/utils/encrypt.py,sha256=PRH8UG485_4PFH7B4VxXvz12iwprx8qWFbgBSTmlZEQ,10508
|
|
646
646
|
ansible/utils/fqcn.py,sha256=bGTXbsj7wt0zfIf2c_mpLPTj8bGnjs1ACiRZAZpEs58,1268
|
|
647
647
|
ansible/utils/galaxy.py,sha256=KkaFwPFCcn8S50R4jWDYzYm2H-2b95JUOxY2dAYPGIg,3914
|
|
648
648
|
ansible/utils/hashing.py,sha256=YwnljDgZv2pJ82aydTSBGqmYWUujcOLHuwb-3S4AWfc,2904
|
|
@@ -674,7 +674,7 @@ ansible/vars/hostvars.py,sha256=dg3jpVmNwSg8EJ4SIvYGT80uxMgRtrOW6vvtDfrQzDU,5152
|
|
|
674
674
|
ansible/vars/manager.py,sha256=9d-9uD9x1X35QgLOZRyJtAxg3b1j4sp8Ivc5C78viOk,36178
|
|
675
675
|
ansible/vars/plugins.py,sha256=B7L3fXoSOoBZSXqJ2ulk0adx1g5SpAb8BxyLGPNA7d4,4695
|
|
676
676
|
ansible/vars/reserved.py,sha256=FBD7n2dnA0CW4I0J1LtWwk2hQqvGW0KTRPcxaRtMKWo,2615
|
|
677
|
-
ansible_core-2.14.
|
|
677
|
+
ansible_core-2.14.5rc1.data/scripts/ansible-test,sha256=CYIYL99IxWdVTtDIj3avilIJXhGAmtjuKPPWNuLWuc8,1690
|
|
678
678
|
ansible_test/__init__.py,sha256=6e721yAyyyocRKzbCKtQXloAfFP7Aqv0L3zG70uh-4A,190
|
|
679
679
|
ansible_test/_data/ansible.cfg,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
680
680
|
ansible_test/_data/coveragerc,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -769,7 +769,7 @@ ansible_test/_internal/venv.py,sha256=DPHAt4tuoIdP7BOXa75-i4T7Paild8eGDsV2UUKOZ7
|
|
|
769
769
|
ansible_test/_internal/ci/__init__.py,sha256=QOaC_8_wUzqFEbsFCXYAnElWoUo6gB40CXvP9RJ-Iyo,7738
|
|
770
770
|
ansible_test/_internal/ci/azp.py,sha256=5ev2kSfqTHWVvluPbu7vV52gaXuBYt3mRGistKv4Kcs,10097
|
|
771
771
|
ansible_test/_internal/ci/local.py,sha256=E4nnerMKdBoVEbsT8IBkl0nSdXyxO2gT8WAaxyzA1EY,6739
|
|
772
|
-
ansible_test/_internal/classification/__init__.py,sha256=
|
|
772
|
+
ansible_test/_internal/classification/__init__.py,sha256=MIYq_hhKRbZ9L31p7tRzeGlCPuX0M4IP1aH6f29K1UE,34475
|
|
773
773
|
ansible_test/_internal/classification/common.py,sha256=jd5VLRegcOX-GNTZqN_7PBzwKF6akFQYsPEltfynGtU,894
|
|
774
774
|
ansible_test/_internal/classification/csharp.py,sha256=3QpVZjamTTG7h86oeVm7d4UMbyojPbBALHVqCpxS1ic,3241
|
|
775
775
|
ansible_test/_internal/classification/powershell.py,sha256=i8t8LxG_-wDPpz1VlnvqAPpRGgVJsuA_xglMtCpibCc,3053
|
|
@@ -783,7 +783,7 @@ ansible_test/_internal/cli/environments.py,sha256=M5Vcolpz-GBBUVlv8gVBl7_54RTSJL
|
|
|
783
783
|
ansible_test/_internal/cli/epilog.py,sha256=kzCYlmqDccMZnSCV57iXUITo6Z9FMMUIagjWJHHA0yY,658
|
|
784
784
|
ansible_test/_internal/cli/argparsing/__init__.py,sha256=ravr0Yv7tEOBFv2s2DuZtEl9BPAQNy-KMKcJNSk4dmc,8922
|
|
785
785
|
ansible_test/_internal/cli/argparsing/actions.py,sha256=VplAf5K9G-loJmLXMAZwbRbIsuFJ-yDrRrP4El5p4RM,606
|
|
786
|
-
ansible_test/_internal/cli/argparsing/argcompletion.py,sha256=
|
|
786
|
+
ansible_test/_internal/cli/argparsing/argcompletion.py,sha256=zOZtYVDkqWIdbmuASkyJuMUKrFh4w3MJzYS2O9DoIQA,5166
|
|
787
787
|
ansible_test/_internal/cli/argparsing/parsers.py,sha256=i7bEPWy7q2mcgiBb3sZ0EN5wQ0G5SetOMQKsOMSsw4M,21490
|
|
788
788
|
ansible_test/_internal/cli/commands/__init__.py,sha256=d8FNvVbSVR2JlnyDUxnS-lZDIQqbdEEPU0cqJA9663Q,5436
|
|
789
789
|
ansible_test/_internal/cli/commands/env.py,sha256=-3zKICX4STeo_lObMh6EmvBy3s2ORL53idmLKN2vCHk,1397
|
|
@@ -930,7 +930,7 @@ ansible_test/_util/controller/sanity/code-smell/use-compat-six.py,sha256=CkYomOt
|
|
|
930
930
|
ansible_test/_util/controller/sanity/code-smell/changelog/sphinx.py,sha256=M3aEK_XugBtVJjfUZbeoVc10hzRylxRxNfEiNq1JVWQ,193
|
|
931
931
|
ansible_test/_util/controller/sanity/integration-aliases/yaml_to_json.py,sha256=qxXHZboRVEqISZYOIXrutsAgobEyh6fiUibk133fzhI,299
|
|
932
932
|
ansible_test/_util/controller/sanity/mypy/ansible-core.ini,sha256=B13dYyd5PGoN-BrFShPMhGBCGbV2oiTsBD8TdRDAh3Q,2327
|
|
933
|
-
ansible_test/_util/controller/sanity/mypy/ansible-test.ini,sha256=
|
|
933
|
+
ansible_test/_util/controller/sanity/mypy/ansible-test.ini,sha256=BFX5QmOpZiEpRf5zVcLvuIl00NtsJhTFXMAPsZ8sLyg,912
|
|
934
934
|
ansible_test/_util/controller/sanity/mypy/modules.ini,sha256=48N2I3ubw3yAuE8layHQ_d0CTfH_eATuXt-K5Bq-ifw,1694
|
|
935
935
|
ansible_test/_util/controller/sanity/pep8/current-ignore.txt,sha256=7J79Z7ypUs0AOEYy4didumaJI0z5Bo69V5u8eZ1M2Bc,20
|
|
936
936
|
ansible_test/_util/controller/sanity/pslint/pslint.ps1,sha256=h0fLdkwF7JhGGjApvqAsCU87BKy0E_UiFJ_O7MARz6U,1089
|
|
@@ -991,9 +991,9 @@ ansible_test/config/cloud-config-vultr.ini.template,sha256=yO2Xz76Ay3kbG54jX7y8-
|
|
|
991
991
|
ansible_test/config/config.yml,sha256=wb3knoBmZewG3GWOMnRHoVPQWW4vPixKLPMNS6vJmTc,2620
|
|
992
992
|
ansible_test/config/inventory.networking.template,sha256=vQ7x1-u5Q4Y5CqZU-7eMSEJOSCzAbPOL1rmK_AQOQuY,1262
|
|
993
993
|
ansible_test/config/inventory.winrm.template,sha256=_M2i1B9RYfwSjwvgf3M-H_5Br5FO_kney_kMRPmoggk,1025
|
|
994
|
-
ansible_core-2.14.
|
|
995
|
-
ansible_core-2.14.
|
|
996
|
-
ansible_core-2.14.
|
|
997
|
-
ansible_core-2.14.
|
|
998
|
-
ansible_core-2.14.
|
|
999
|
-
ansible_core-2.14.
|
|
994
|
+
ansible_core-2.14.5rc1.dist-info/COPYING,sha256=CuBIWlvTemPmNgNZZBfk6w5lMzT6bH-TLKOg6F1K8ic,35148
|
|
995
|
+
ansible_core-2.14.5rc1.dist-info/METADATA,sha256=anDEOrJmuSJQno8QE6F5ZtOuxDORy_FGFvYwKr8OrfA,7433
|
|
996
|
+
ansible_core-2.14.5rc1.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
|
997
|
+
ansible_core-2.14.5rc1.dist-info/entry_points.txt,sha256=0mpmsrIhODChxKl3eS-NcVQCaMetBn8KdPLtVxQgR64,453
|
|
998
|
+
ansible_core-2.14.5rc1.dist-info/top_level.txt,sha256=IFbRLjAvih1DYzJWg3_F6t4sCzEMxRO7TOMNs6GkYHo,21
|
|
999
|
+
ansible_core-2.14.5rc1.dist-info/RECORD,,
|
|
@@ -668,6 +668,10 @@ class PathMapper:
|
|
|
668
668
|
|
|
669
669
|
minimal: dict[str, str] = {}
|
|
670
670
|
|
|
671
|
+
packaging = {
|
|
672
|
+
'integration': 'packaging/',
|
|
673
|
+
}
|
|
674
|
+
|
|
671
675
|
# Early classification that needs to occur before common classification belongs here.
|
|
672
676
|
|
|
673
677
|
if path.startswith('test/units/compat/'):
|
|
@@ -749,6 +753,9 @@ class PathMapper:
|
|
|
749
753
|
return minimal
|
|
750
754
|
|
|
751
755
|
if path.startswith('packaging/'):
|
|
756
|
+
if path.startswith('packaging/pep517_backend/'):
|
|
757
|
+
return packaging
|
|
758
|
+
|
|
752
759
|
return minimal
|
|
753
760
|
|
|
754
761
|
if path.startswith('test/ansible_test/'):
|
|
@@ -836,16 +843,17 @@ class PathMapper:
|
|
|
836
843
|
return minimal
|
|
837
844
|
|
|
838
845
|
if path in (
|
|
839
|
-
|
|
846
|
+
'MANIFEST.in',
|
|
847
|
+
'pyproject.toml',
|
|
848
|
+
'requirements.txt',
|
|
849
|
+
'setup.cfg',
|
|
850
|
+
'setup.py',
|
|
840
851
|
):
|
|
841
|
-
return
|
|
852
|
+
return packaging
|
|
842
853
|
|
|
843
854
|
if ext in (
|
|
844
|
-
'.in',
|
|
845
855
|
'.md',
|
|
846
856
|
'.rst',
|
|
847
|
-
'.toml',
|
|
848
|
-
'.txt',
|
|
849
857
|
):
|
|
850
858
|
return minimal
|
|
851
859
|
|
|
@@ -17,10 +17,19 @@ class Substitute:
|
|
|
17
17
|
try:
|
|
18
18
|
import argcomplete
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
try:
|
|
21
|
+
# argcomplete 3+
|
|
22
|
+
# see: https://github.com/kislyuk/argcomplete/commit/bd781cb08512b94966312377186ebc5550f46ae0
|
|
23
|
+
from argcomplete.finders import (
|
|
24
|
+
CompletionFinder,
|
|
25
|
+
default_validator,
|
|
26
|
+
)
|
|
27
|
+
except ImportError:
|
|
28
|
+
# argcomplete <3
|
|
29
|
+
from argcomplete import (
|
|
30
|
+
CompletionFinder,
|
|
31
|
+
default_validator,
|
|
32
|
+
)
|
|
24
33
|
|
|
25
34
|
warn = argcomplete.warn # pylint: disable=invalid-name
|
|
26
35
|
except ImportError:
|
|
@@ -72,7 +81,13 @@ class CompType(enum.Enum):
|
|
|
72
81
|
def register_safe_action(action_type: t.Type[argparse.Action]) -> None:
|
|
73
82
|
"""Register the given action as a safe action for argcomplete to use during completion if it is not already registered."""
|
|
74
83
|
if argcomplete and action_type not in argcomplete.safe_actions:
|
|
75
|
-
argcomplete.safe_actions
|
|
84
|
+
if isinstance(argcomplete.safe_actions, set):
|
|
85
|
+
# argcomplete 3+
|
|
86
|
+
# see: https://github.com/kislyuk/argcomplete/commit/bd781cb08512b94966312377186ebc5550f46ae0
|
|
87
|
+
argcomplete.safe_actions.add(action_type)
|
|
88
|
+
else:
|
|
89
|
+
# argcomplete <3
|
|
90
|
+
argcomplete.safe_actions += (action_type,)
|
|
76
91
|
|
|
77
92
|
|
|
78
93
|
def get_comp_type() -> t.Optional[CompType]:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|