qontract-reconcile 0.10.2.dev50__py3-none-any.whl → 0.10.2.dev52__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 qontract-reconcile might be problematic. Click here for more details.
- {qontract_reconcile-0.10.2.dev50.dist-info → qontract_reconcile-0.10.2.dev52.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.2.dev50.dist-info → qontract_reconcile-0.10.2.dev52.dist-info}/RECORD +28 -29
- reconcile/aws_cloudwatch_log_retention/integration.py +10 -17
- reconcile/dashdotdb_dora.py +5 -4
- reconcile/gitlab_housekeeping.py +10 -6
- reconcile/terraform_tgw_attachments.py +5 -5
- reconcile/terraform_vpc_peerings.py +1 -1
- reconcile/utils/aggregated_list.py +30 -20
- reconcile/utils/aws_api.py +596 -169
- reconcile/utils/aws_helper.py +7 -7
- reconcile/utils/binary.py +14 -7
- reconcile/utils/config.py +9 -6
- reconcile/utils/defer.py +4 -2
- reconcile/utils/elasticsearch_exceptions.py +7 -4
- reconcile/utils/environ.py +5 -3
- reconcile/utils/exceptions.py +5 -2
- reconcile/utils/git.py +6 -4
- reconcile/utils/gitlab_api.py +103 -82
- reconcile/utils/mr/base.py +6 -3
- reconcile/utils/mr/update_access_report_base.py +2 -2
- reconcile/utils/output.py +3 -6
- reconcile/utils/vcs.py +5 -3
- reconcile/vpc_peerings_validator.py +21 -15
- tools/app_interface_reporter.py +44 -70
- tools/cli_commands/gpg_encrypt.py +2 -2
- tools/qontract_cli.py +317 -246
- reconcile/utils/data_structures.py +0 -13
- {qontract_reconcile-0.10.2.dev50.dist-info → qontract_reconcile-0.10.2.dev52.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev50.dist-info → qontract_reconcile-0.10.2.dev52.dist-info}/entry_points.txt +0 -0
reconcile/utils/aws_helper.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
from collections.abc import Iterable
|
1
|
+
from collections.abc import Iterable, Mapping
|
2
2
|
from typing import Any, Protocol
|
3
3
|
|
4
4
|
from reconcile.utils.disabled_integrations import (
|
@@ -12,15 +12,15 @@ class AccountNotFoundError(Exception):
|
|
12
12
|
pass
|
13
13
|
|
14
14
|
|
15
|
-
Account =
|
15
|
+
Account = Mapping[str, Any]
|
16
16
|
|
17
17
|
|
18
|
-
def get_id_from_arn(arn):
|
18
|
+
def get_id_from_arn(arn: str) -> str:
|
19
19
|
# arn:aws:iam::12345:<arntype>/id --> id
|
20
20
|
return arn.split("/")[1]
|
21
21
|
|
22
22
|
|
23
|
-
def get_account_uid_from_arn(arn):
|
23
|
+
def get_account_uid_from_arn(arn: str) -> str:
|
24
24
|
# arn:aws:iam::12345:role/role-1 --> 12345
|
25
25
|
return arn.split(":")[4]
|
26
26
|
|
@@ -36,7 +36,7 @@ def is_aws_managed_resource(arn: str) -> bool:
|
|
36
36
|
return get_account_uid_from_arn(arn) == "aws"
|
37
37
|
|
38
38
|
|
39
|
-
def get_details_from_role_link(role_link):
|
39
|
+
def get_details_from_role_link(role_link: str) -> tuple[str, str]:
|
40
40
|
# https://signin.aws.amazon.com/switchrole?
|
41
41
|
# account=<uid>&roleName=<role_name> -->
|
42
42
|
# 12345, role-1
|
@@ -46,7 +46,7 @@ def get_details_from_role_link(role_link):
|
|
46
46
|
return uid, role_name
|
47
47
|
|
48
48
|
|
49
|
-
def get_role_arn_from_role_link(role_link):
|
49
|
+
def get_role_arn_from_role_link(role_link: str) -> str:
|
50
50
|
# https://signin.aws.amazon.com/switchrole?
|
51
51
|
# account=<uid>&roleName=<role_name> -->
|
52
52
|
# arn:aws:iam::12345:role/role-1
|
@@ -54,7 +54,7 @@ def get_role_arn_from_role_link(role_link):
|
|
54
54
|
return f"arn:aws:iam::{uid}:role/{role_name}"
|
55
55
|
|
56
56
|
|
57
|
-
def get_account_uid_from_role_link(role_link):
|
57
|
+
def get_account_uid_from_role_link(role_link: str) -> str:
|
58
58
|
uid, _ = get_details_from_role_link(role_link)
|
59
59
|
return uid
|
60
60
|
|
reconcile/utils/binary.py
CHANGED
@@ -1,17 +1,19 @@
|
|
1
1
|
import re
|
2
2
|
import shutil
|
3
3
|
import subprocess
|
4
|
+
from collections.abc import Callable, Iterable
|
4
5
|
from functools import wraps
|
6
|
+
from typing import Any
|
5
7
|
|
6
8
|
|
7
|
-
def binary(binaries=None):
|
9
|
+
def binary(binaries: Iterable[str] | None = None) -> Callable:
|
8
10
|
"""Check that a binary exists before execution."""
|
9
11
|
if binaries is None:
|
10
12
|
binaries = []
|
11
13
|
|
12
|
-
def deco_binary(f):
|
14
|
+
def deco_binary(f: Callable) -> Callable:
|
13
15
|
@wraps(f)
|
14
|
-
def f_binary(*args, **kwargs):
|
16
|
+
def f_binary(*args: Any, **kwargs: Any) -> None:
|
15
17
|
for b in binaries:
|
16
18
|
if not shutil.which(b):
|
17
19
|
raise Exception(
|
@@ -25,12 +27,17 @@ def binary(binaries=None):
|
|
25
27
|
return deco_binary
|
26
28
|
|
27
29
|
|
28
|
-
def binary_version(
|
30
|
+
def binary_version(
|
31
|
+
binary: str,
|
32
|
+
version_args: Iterable[str],
|
33
|
+
search_regex: str,
|
34
|
+
expected_versions: Iterable[str],
|
35
|
+
) -> Callable:
|
29
36
|
"""Check that a binary exists and is a desired version"""
|
30
37
|
|
31
|
-
def deco_binary_version(f):
|
38
|
+
def deco_binary_version(f: Callable) -> Callable:
|
32
39
|
@wraps(f)
|
33
|
-
def f_binary_version(*args, **kwargs):
|
40
|
+
def f_binary_version(*args: Any, **kwargs: Any) -> None:
|
34
41
|
regex = re.compile(search_regex)
|
35
42
|
|
36
43
|
cmd = [binary]
|
@@ -51,7 +58,7 @@ def binary_version(binary, version_args, search_regex, expected_versions):
|
|
51
58
|
found = True
|
52
59
|
break
|
53
60
|
|
54
|
-
if not found:
|
61
|
+
if not found or not match:
|
55
62
|
raise Exception(
|
56
63
|
f"Could not find version for binary '{binary}' via regex "
|
57
64
|
f"for binary version check: "
|
reconcile/utils/config.py
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
from collections.abc import Mapping
|
2
|
+
from typing import Any
|
3
|
+
|
1
4
|
import toml
|
2
5
|
|
3
|
-
_config =
|
6
|
+
_config: dict = {}
|
4
7
|
|
5
8
|
|
6
9
|
class ConfigNotFound(Exception):
|
@@ -11,21 +14,21 @@ class SecretNotFound(Exception):
|
|
11
14
|
pass
|
12
15
|
|
13
16
|
|
14
|
-
def get_config():
|
17
|
+
def get_config() -> dict:
|
15
18
|
return _config
|
16
19
|
|
17
20
|
|
18
|
-
def init(config):
|
21
|
+
def init(config: dict) -> dict:
|
19
22
|
global _config # noqa: PLW0603
|
20
23
|
_config = config
|
21
24
|
return _config
|
22
25
|
|
23
26
|
|
24
|
-
def init_from_toml(configfile):
|
27
|
+
def init_from_toml(configfile: str) -> dict:
|
25
28
|
return init(toml.load(configfile))
|
26
29
|
|
27
30
|
|
28
|
-
def read(secret):
|
31
|
+
def read(secret: Mapping[str, Any]) -> str:
|
29
32
|
path = secret["path"]
|
30
33
|
field = secret["field"]
|
31
34
|
try:
|
@@ -38,7 +41,7 @@ def read(secret):
|
|
38
41
|
raise SecretNotFound(f"key not found in config file {path}: {e!s}") from None
|
39
42
|
|
40
43
|
|
41
|
-
def read_all(secret):
|
44
|
+
def read_all(secret: Mapping[str, Any]) -> dict:
|
42
45
|
path = secret["path"]
|
43
46
|
try:
|
44
47
|
path_tokens = path.split("/")
|
reconcile/utils/defer.py
CHANGED
@@ -1,14 +1,16 @@
|
|
1
|
+
from collections.abc import Callable
|
1
2
|
from contextlib import ExitStack
|
2
3
|
from functools import wraps
|
4
|
+
from typing import Any
|
3
5
|
|
4
6
|
|
5
|
-
def defer(func):
|
7
|
+
def defer(func: Callable) -> Callable:
|
6
8
|
"""Defer code execution until the surrounding function returns.
|
7
9
|
Useful for registering cleanup work.
|
8
10
|
"""
|
9
11
|
|
10
12
|
@wraps(func)
|
11
|
-
def func_wrapper(*args, **kwargs):
|
13
|
+
def func_wrapper(*args: Any, **kwargs: Any) -> Any:
|
12
14
|
with ExitStack() as stack:
|
13
15
|
return func(*args, defer=stack.callback, **kwargs)
|
14
16
|
|
@@ -1,18 +1,21 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
|
1
4
|
class ElasticSearchResourceNameInvalidError(Exception):
|
2
|
-
def __init__(self, msg):
|
5
|
+
def __init__(self, msg: Any) -> None:
|
3
6
|
super().__init__(str(msg))
|
4
7
|
|
5
8
|
|
6
9
|
class ElasticSearchResourceMissingSubnetIdError(Exception):
|
7
|
-
def __init__(self, msg):
|
10
|
+
def __init__(self, msg: Any) -> None:
|
8
11
|
super().__init__(str(msg))
|
9
12
|
|
10
13
|
|
11
14
|
class ElasticSearchResourceZoneAwareSubnetInvalidError(Exception):
|
12
|
-
def __init__(self, msg):
|
15
|
+
def __init__(self, msg: Any) -> None:
|
13
16
|
super().__init__(str(msg))
|
14
17
|
|
15
18
|
|
16
19
|
class ElasticSearchResourceColdStorageError(Exception):
|
17
|
-
def __init__(self, msg):
|
20
|
+
def __init__(self, msg: Any) -> None:
|
18
21
|
super().__init__(str(msg))
|
reconcile/utils/environ.py
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
import os
|
2
|
+
from collections.abc import Callable, Iterable
|
2
3
|
from functools import wraps
|
4
|
+
from typing import Any
|
3
5
|
|
4
6
|
|
5
|
-
def environ(variables=None):
|
7
|
+
def environ(variables: Iterable[str] | None = None) -> Callable:
|
6
8
|
"""Check that environment variables are set before execution."""
|
7
9
|
if variables is None:
|
8
10
|
variables = []
|
9
11
|
|
10
|
-
def deco_environ(f):
|
12
|
+
def deco_environ(f: Callable) -> Callable:
|
11
13
|
@wraps(f)
|
12
|
-
def f_environ(*args, **kwargs):
|
14
|
+
def f_environ(*args: Any, **kwargs: Any) -> None:
|
13
15
|
for e in variables:
|
14
16
|
if not os.environ.get(e):
|
15
17
|
raise KeyError(f"Could not find environment variable: {e}")
|
reconcile/utils/exceptions.py
CHANGED
@@ -1,10 +1,13 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
|
1
4
|
class FetchResourceError(Exception):
|
2
|
-
def __init__(self, msg):
|
5
|
+
def __init__(self, msg: Any) -> None:
|
3
6
|
super().__init__("error fetching resource: " + str(msg))
|
4
7
|
|
5
8
|
|
6
9
|
class PrintToFileInGitRepositoryError(Exception):
|
7
|
-
def __init__(self, msg):
|
10
|
+
def __init__(self, msg: Any) -> None:
|
8
11
|
super().__init__("can not print to a git repository: " + str(msg))
|
9
12
|
|
10
13
|
|
reconcile/utils/git.py
CHANGED
@@ -6,7 +6,9 @@ class GitError(Exception):
|
|
6
6
|
pass
|
7
7
|
|
8
8
|
|
9
|
-
def clone(
|
9
|
+
def clone(
|
10
|
+
repo_url: str, wd: str, depth: int | None = None, verify: bool | None = True
|
11
|
+
) -> None:
|
10
12
|
cmd = ["git"]
|
11
13
|
if not verify:
|
12
14
|
cmd += ["-c", "http.sslVerify=false"]
|
@@ -35,7 +37,7 @@ def fetch(
|
|
35
37
|
remote: str = "origin",
|
36
38
|
depth: int | None = None,
|
37
39
|
verify: bool = True,
|
38
|
-
):
|
40
|
+
) -> None:
|
39
41
|
cmd = ["git"]
|
40
42
|
if not verify:
|
41
43
|
cmd += ["-c", "http.sslVerify=false"]
|
@@ -51,7 +53,7 @@ def checkout(
|
|
51
53
|
ref: str,
|
52
54
|
wd: str,
|
53
55
|
verify: bool = True,
|
54
|
-
):
|
56
|
+
) -> None:
|
55
57
|
if not is_current_ref(ref, wd):
|
56
58
|
fetch(ref, wd, depth=1, verify=verify)
|
57
59
|
cmd = ["git", "checkout", ref]
|
@@ -60,7 +62,7 @@ def checkout(
|
|
60
62
|
raise GitError(f"git checkout failed for {ref}: {result.stderr}")
|
61
63
|
|
62
64
|
|
63
|
-
def is_file_in_git_repo(file_path):
|
65
|
+
def is_file_in_git_repo(file_path: str) -> bool:
|
64
66
|
real_path = os.path.realpath(file_path)
|
65
67
|
dir_path = os.path.dirname(real_path)
|
66
68
|
cmd = ["git", "rev-parse", "--is-inside-work-tree"]
|