qontract-reconcile 0.10.2.dev268__py3-none-any.whl → 0.10.2.dev269__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qontract-reconcile
3
- Version: 0.10.2.dev268
3
+ Version: 0.10.2.dev269
4
4
  Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
5
5
  Project-URL: homepage, https://github.com/app-sre/qontract-reconcile
6
6
  Project-URL: repository, https://github.com/app-sre/qontract-reconcile
@@ -8,7 +8,7 @@ reconcile/aws_iam_password_reset.py,sha256=FpAqAXngmLFqkOtOjrz_i90qteUyLHJL0GMjo
8
8
  reconcile/aws_support_cases_sos.py,sha256=PDhilxQ4TBxVnxUPIUdTbKEaNUI0wzPiEsB91oHT2fY,3384
9
9
  reconcile/blackbox_exporter_endpoint_monitoring.py,sha256=O1wFp52EyF538c6txaWBs8eMtUIy19gyHZ6VzJ6QXS8,3512
10
10
  reconcile/checkpoint.py,sha256=gjtS8g6KIyKFYlHMSZjAqDUOlVh83nh4go-9yNrhWZU,5016
11
- reconcile/cli.py,sha256=C4x30JxgSmKfqhOliWlJaBMsJjtLkd7UpWC9c5wjB4A,112236
11
+ reconcile/cli.py,sha256=ysDzZ-LRzO5D2FfderoNFx_NmYbxfd2iCYaGOhz6Z90,112239
12
12
  reconcile/closedbox_endpoint_monitoring_base.py,sha256=_OKz7K7HHw0-gzxeEma8PcUCtd70pRBy7JMoaAm8IVU,4940
13
13
  reconcile/cluster_deployment_mapper.py,sha256=5gumAaRCcFXsabUJ1dnuUy9WrP_FEEM5JnOnE8ch9sE,2326
14
14
  reconcile/dashdotdb_base.py,sha256=83ZWIf5JJk3P_D69y2TmXRcQr6ELJGlv10OM0h7fJVs,4767
@@ -138,7 +138,7 @@ reconcile/automated_actions/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQe
138
138
  reconcile/automated_actions/config/integration.py,sha256=EbVJGyEAFf91dHiIlnwbetbqZ3Y7HJOez2Fnexzd550,13853
139
139
  reconcile/aws_account_manager/README.md,sha256=_XFM3GZNHUzv--e_navqJuaUWpjC6QrHfulreHynFf0,262
140
140
  reconcile/aws_account_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
141
- reconcile/aws_account_manager/integration.py,sha256=XTamC824imAezzVoQhhwdMOawNcPCOghR_y7i_8bpJI,15343
141
+ reconcile/aws_account_manager/integration.py,sha256=WEbcvF8GocFHJrlz3mmXSOyYHIUrrHxq_SKaImgJQ2c,15346
142
142
  reconcile/aws_account_manager/merge_request_manager.py,sha256=q8t-YwD4y_4UpxdoG9TQrEbDpjlzasOQIynFoCjP2OE,3947
143
143
  reconcile/aws_account_manager/metrics.py,sha256=YB10ea4kIGwJfs5N14RF-RoXPb-QQWaDBz1jLZ3YWE0,917
144
144
  reconcile/aws_account_manager/reconciler.py,sha256=oSuR3xrN6fihmJUl79HSDxH5-MsR7Sfpf7eqiMlwwRg,17141
@@ -669,10 +669,10 @@ reconcile/utils/acs/base.py,sha256=4UsDrCpAOuddL3PKNuIQYoJP1BtZQNNB8_KEX0lXneg,2
669
669
  reconcile/utils/acs/policies.py,sha256=jpbi3qpGkBD_X6MfzsX12dPajUbmACmhIOz_0rDvYzs,5489
670
670
  reconcile/utils/acs/rbac.py,sha256=ugsLM9Pb7FbUbdq85E3VzXGMaB9ZovXob7tdWCxwqZ8,8808
671
671
  reconcile/utils/aws_api_typed/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
672
- reconcile/utils/aws_api_typed/account.py,sha256=f1UQul336_9rC_iCdzkbTQkhcFh5BjIZsf3X18SACfY,2048
672
+ reconcile/utils/aws_api_typed/account.py,sha256=4UfsVcOKIkR1b6bYlr6lcnYk5A6T-KZWUi1mZoLL40c,3376
673
673
  reconcile/utils/aws_api_typed/api.py,sha256=1h_dQXnE7FUAZi4RRgBEU7nbyAz8awRo0cpuilXyhHE,9239
674
674
  reconcile/utils/aws_api_typed/dynamodb.py,sha256=AKUbz8HGzmSq4cnpjJe7PgqsikMkjbpbzUD2UJv2b58,383
675
- reconcile/utils/aws_api_typed/iam.py,sha256=LrHCFRIB5h5GcAD_4Bfb9tOZqH961LI1r03-EuUWfmI,2243
675
+ reconcile/utils/aws_api_typed/iam.py,sha256=uuK5JzVmlWpokyixhd_Oy2pBr_31z_d4Gz7bPeDK71E,2864
676
676
  reconcile/utils/aws_api_typed/organization.py,sha256=rmZkm14t30Bm0ctMTtCNzcDrTjOFkal1TO0pHncNxcg,5513
677
677
  reconcile/utils/aws_api_typed/s3.py,sha256=J2uOTtEFgMyKT22pa4DbFnV7zfg575m2DeidQaeselM,1034
678
678
  reconcile/utils/aws_api_typed/service_quotas.py,sha256=ywPligZTxGgDhJ4TjWzXg5MbQ4j1iGUxonngII_KOHo,3022
@@ -797,7 +797,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
797
797
  tools/saas_promotion_state/saas_promotion_state.py,sha256=oF7C4hpIgyMTwTRm3Aun3cDCHIjVar65JoLp6NcJHlU,3909
798
798
  tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
799
799
  tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
800
- qontract_reconcile-0.10.2.dev268.dist-info/METADATA,sha256=tDfzT1dkLIg6YlPz3iT9K-MvZ2rJOTdMSGsUJ8c3Fzs,24501
801
- qontract_reconcile-0.10.2.dev268.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
802
- qontract_reconcile-0.10.2.dev268.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
803
- qontract_reconcile-0.10.2.dev268.dist-info/RECORD,,
800
+ qontract_reconcile-0.10.2.dev269.dist-info/METADATA,sha256=Wzl-5Z8F3fPHuNHeOahSh8r4e1Nk-KKbpKlVrcaRheI,24501
801
+ qontract_reconcile-0.10.2.dev269.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
802
+ qontract_reconcile-0.10.2.dev269.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
803
+ qontract_reconcile-0.10.2.dev269.dist-info/RECORD,,
@@ -53,7 +53,7 @@ class AwsAccountMgmtIntegrationParams(PydanticRunParams):
53
53
  )
54
54
  # To avoid the accidental deletion of the resource file, explicitly set the
55
55
  # qontract.cli option in the integration extraArgs!
56
- account_tmpl_resource: str = "/aws-account-manager/account-tmpl.yml"
56
+ account_tmpl_resource: str = "/aws-account-manager/account-tmpl.yml.j2"
57
57
  template_collection_root_path: str = "data/templating/collections/aws-account"
58
58
 
59
59
 
reconcile/cli.py CHANGED
@@ -969,7 +969,7 @@ def aws_saml_roles(
969
969
  "--account-tmpl-resource",
970
970
  help="Resource name of the account template-collection template in the app-interface.",
971
971
  required=True,
972
- default="/aws-account-manager/account-tmpl.yml",
972
+ default="/aws-account-manager/account-tmpl.yml.j2",
973
973
  )
974
974
  @click.option(
975
975
  "--template-collection-root-path",
@@ -1,11 +1,14 @@
1
+ import logging
1
2
  from enum import StrEnum
2
3
  from typing import TYPE_CHECKING
3
4
 
4
5
  if TYPE_CHECKING:
5
6
  from mypy_boto3_account import AccountClient
7
+ from mypy_boto3_account.type_defs import AlternateContactTypeDef
6
8
  else:
7
- AccountClient = object
9
+ AccountClient = AlternateContactTypeDef = object
8
10
 
11
+ import botocore
9
12
  from pydantic import BaseModel
10
13
 
11
14
 
@@ -22,6 +25,9 @@ class Region(BaseModel):
22
25
  status: OptStatus
23
26
 
24
27
 
28
+ log = logging.getLogger(__name__)
29
+
30
+
25
31
  class AWSApiAccount:
26
32
  def __init__(self, client: AccountClient) -> None:
27
33
  self.client = client
@@ -30,13 +36,39 @@ class AWSApiAccount:
30
36
  self, name: str, title: str, email: str, phone_number: str
31
37
  ) -> None:
32
38
  """Set the security contact for the account."""
33
- self.client.put_alternate_contact(
34
- AlternateContactType="SECURITY",
35
- EmailAddress=email,
36
- Name=name,
37
- Title=title,
38
- PhoneNumber=phone_number,
39
- )
39
+ try:
40
+ self.client.put_alternate_contact(
41
+ AlternateContactType="SECURITY",
42
+ EmailAddress=email,
43
+ Name=name,
44
+ Title=title,
45
+ PhoneNumber=phone_number,
46
+ )
47
+ except botocore.exceptions.ClientError as e:
48
+ if e.response["Error"]["Code"] != "AccessDenied":
49
+ raise
50
+
51
+ # This exception is raised if the user does not have permission to perform this action.
52
+ # Let's see if the current security contact is already set to the same values.
53
+ current_contact = self.get_security_contact()
54
+ if (
55
+ not current_contact
56
+ or current_contact["EmailAddress"] != email
57
+ or current_contact["Name"] != name
58
+ or current_contact["Title"] != title
59
+ or current_contact["PhoneNumber"] != phone_number
60
+ ):
61
+ raise
62
+
63
+ def get_security_contact(self) -> AlternateContactTypeDef | None:
64
+ """Get the security contact for the account."""
65
+ try:
66
+ return self.client.get_alternate_contact(AlternateContactType="SECURITY")[
67
+ "AlternateContact"
68
+ ]
69
+ except self.client.exceptions.ResourceNotFoundException:
70
+ log.warning("Security contact not set.")
71
+ return None
40
72
 
41
73
  def list_regions(self) -> list[Region]:
42
74
  """List all regions in the account."""
@@ -1,5 +1,6 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
+ import botocore
3
4
  from pydantic import BaseModel, Field
4
5
 
5
6
  if TYPE_CHECKING:
@@ -40,10 +41,12 @@ class AWSApiIam:
40
41
  try:
41
42
  user = self.client.create_user(UserName=user_name)
42
43
  return AWSUser(**user["User"])
43
- except self.client.exceptions.EntityAlreadyExistsException:
44
- raise AWSEntityAlreadyExistsError(
45
- f"User {user_name} already exists"
46
- ) from None
44
+ except botocore.exceptions.ClientError as e:
45
+ if e.response["Error"]["Code"] == "EntityAlreadyExists":
46
+ raise AWSEntityAlreadyExistsError(
47
+ f"User {user_name} already exists"
48
+ ) from e
49
+ raise
47
50
 
48
51
  def attach_user_policy(self, user_name: str, policy_arn: str) -> None:
49
52
  """Attach a policy to a user."""
@@ -60,8 +63,15 @@ class AWSApiIam:
60
63
  """Set the account alias."""
61
64
  try:
62
65
  self.client.create_account_alias(AccountAlias=account_alias)
63
- except self.client.exceptions.EntityAlreadyExistsException:
64
- if self.get_account_alias() != account_alias:
65
- raise ValueError(
66
- "Account alias already exists for another AWS account. Choose another one!"
67
- ) from None
66
+ except botocore.exceptions.ClientError as e:
67
+ if e.response["Error"]["Code"] == "EntityAlreadyExists":
68
+ if self.get_account_alias() != account_alias:
69
+ raise ValueError(
70
+ "Account alias already exists for another AWS account. Choose another one!"
71
+ ) from e
72
+ elif e.response["Error"]["Code"] == "AccessDenied":
73
+ # AccessDeniedException can occur if the user does not have permission to create an account alias.
74
+ # This can happen if the alias is already set and we don't have permission to change it.
75
+ # If the existing alias is the one we want, we can ignore the error.
76
+ if self.get_account_alias() != account_alias:
77
+ raise