qontract-reconcile 0.10.2.dev291__py3-none-any.whl → 0.10.2.dev293__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.dev291
3
+ Version: 0.10.2.dev293
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
@@ -213,7 +213,7 @@ reconcile/glitchtip_project_alerts/integration.py,sha256=d3PMy-mQSbSZdIGAVaZCA2U
213
213
  reconcile/glitchtip_project_dsn/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
214
214
  reconcile/glitchtip_project_dsn/integration.py,sha256=3GgcqUM6hWhLpo9Yx5Xr9vrdexF-WNevVCNL9bJ0Upc,8162
215
215
  reconcile/gql_definitions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
216
- reconcile/gql_definitions/introspection.json,sha256=3-WcCYBV8GP1OH1_NLgu9yyXNSNZDJ5_1dZIiTCWhwM,2346833
216
+ reconcile/gql_definitions/introspection.json,sha256=CQAoUjEbdjRlHyYeLjs1PTMiP_RQrPzD0yCj08aYawA,2348381
217
217
  reconcile/gql_definitions/acs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
218
218
  reconcile/gql_definitions/acs/acs_instances.py,sha256=L91WW9LbhJbBSrECqShQpFtjoBOsmNIYLRpMbx1io5o,2181
219
219
  reconcile/gql_definitions/acs/acs_policies.py,sha256=Ygpfl2-VkYLSlJvHgp_dJBfb66K_Rwfdfpsa18w1v1s,4338
@@ -409,8 +409,8 @@ reconcile/gql_definitions/status_board/status_board.py,sha256=BYq-1g_-AjNKHIKmY2
409
409
  reconcile/gql_definitions/statuspage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
410
410
  reconcile/gql_definitions/statuspage/statuspages.py,sha256=CTRzjiR9k41LqlkgyoNHwC2JERsoD_Run_aK7jw_Ono,5299
411
411
  reconcile/gql_definitions/templating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
412
- reconcile/gql_definitions/templating/template_collection.py,sha256=I9razKZ9BThTn1HZW1AamwF0EKXxLACL30DxGxev-rA,4045
413
- reconcile/gql_definitions/templating/templates.py,sha256=bV1JWwxDW8opARBOrZ_h5NBsJfM-9aL-povmTZnpTOk,3296
412
+ reconcile/gql_definitions/templating/template_collection.py,sha256=alQA1qLhL0nOFcnA0O2Z7HCuuAviZXM66t6yeur9XWo,4123
413
+ reconcile/gql_definitions/templating/templates.py,sha256=rQ14gs5dMttxVuW5q26p23Y8c5vCNh8dSec9ucTGlKc,3372
414
414
  reconcile/gql_definitions/terraform_cloudflare_dns/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
415
415
  reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py,sha256=eyGX9HcTF6MZbOYZ6Kl6Mg3k6nJTUtwqs9gDxBP_8Dk,1920
416
416
  reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py,sha256=dBQ2tyAp-eRZs59mguaTc6-x67JUoSxtZ8mOjbRqDuc,5832
@@ -507,12 +507,12 @@ reconcile/templates/jira-checkpoint-missinginfo.j2,sha256=c_Vvg-lEENsB3tgxm9B6Y9
507
507
  reconcile/templates/rosa-classic-cluster-creation.sh.j2,sha256=M-nzp-GtkQNRe8rdoDAndSKJSJhcJwNFKeql-JP2W7M,2094
508
508
  reconcile/templates/rosa-hcp-cluster-creation.sh.j2,sha256=eeT7xUhmz7Q_HtJOQALF5snZE8cUPGkIh6WUlcBHhhs,2349
509
509
  reconcile/templating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
510
- reconcile/templating/renderer.py,sha256=VA_lMMtsyEFc0Cwf0jehQdv3tMEJtfyZMzBgA5us5ME,14856
510
+ reconcile/templating/renderer.py,sha256=YVKhk1piAnk3faT81oeZeMHnFV78Mt-_wgtIC693vtE,14907
511
511
  reconcile/templating/validator.py,sha256=5f9f35PCHOOdjb7KZquL2YdabyuAUokPDa4xutSEHIQ,5360
512
512
  reconcile/templating/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
513
513
  reconcile/templating/lib/merge_request_manager.py,sha256=XwpOR4rVS9ZiJ_Mn8qfCXPZ7CRLMGSJgeIkqD-8Jhgc,5228
514
514
  reconcile/templating/lib/model.py,sha256=YVUIXuPny3_kpFgBMSud8q_ndY5o882wKiX0l0A14L4,481
515
- reconcile/templating/lib/rendering.py,sha256=IzTbXJ5cO0c9mV6P9HrstOo79ovOcoNVtmyc6RgfMe0,6241
515
+ reconcile/templating/lib/rendering.py,sha256=qjs7WAVSvWH4edbBk6Aaql4ZT_OG9W7L8qi_YeJkHVI,6973
516
516
  reconcile/terraform_init/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
517
517
  reconcile/terraform_init/integration.py,sha256=pPi4YAjbEE8vDaaRizGf-d-PewqqSJmjcLgAsWFS7G0,6236
518
518
  reconcile/terraform_init/merge_request.py,sha256=3CYtgSd7Q9zjKg4wsDz437EPCRfGeZZ8fZ0Y-ChKXJY,1475
@@ -658,7 +658,7 @@ reconcile/utils/sqs_gateway.py,sha256=XNIf3PY4UCPNufP2Ul0UJj3fKlt5larBba-VTT-41F
658
658
  reconcile/utils/state.py,sha256=vCHYIfrWLfPyIWEHSaADWlc4OqhwcOiqM3Egqvw-lfo,16372
659
659
  reconcile/utils/structs.py,sha256=LcbLEg8WxfRqM6nW7NhcWN0YeqF7SQzxOgntmLs1SgY,352
660
660
  reconcile/utils/terraform_client.py,sha256=GoLbfs4d4YItNCeV3NZnrth4sD8ziNYgY2IszruRDpg,37303
661
- reconcile/utils/terrascript_aws_client.py,sha256=VtJ7jpvAbEi2gS_2ZTuEhBooVjqwmLieSud2mI-XVUk,292501
661
+ reconcile/utils/terrascript_aws_client.py,sha256=jVzh5PmphbCAN7Pog_PFYHoHj7lmQGb6Q4FwT_c8pF8,295634
662
662
  reconcile/utils/three_way_diff_strategy.py,sha256=oQcHXd9LVhirJfoaOBoHUYuZVGfyL2voKr6KVI34zZE,4833
663
663
  reconcile/utils/throughput.py,sha256=iP4UWAe2LVhDo69mPPmgo9nQ7RxHD6_GS8MZe-aSiuM,344
664
664
  reconcile/utils/vault.py,sha256=6V15LByFghp-U3k0N4lum6V7qt2EAlRfcAxjy5e-FAU,15146
@@ -796,7 +796,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
796
796
  tools/saas_promotion_state/saas_promotion_state.py,sha256=uQv2QJAmUXP1g2GPIH30WTlvL9soY6m9lefpZEVDM5w,3965
797
797
  tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
798
798
  tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
799
- qontract_reconcile-0.10.2.dev291.dist-info/METADATA,sha256=dLj7pJ4qelVJQbwNLC8fr_wYbPGeJ0nx1qC0hJ3z2Mg,24916
800
- qontract_reconcile-0.10.2.dev291.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
801
- qontract_reconcile-0.10.2.dev291.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
802
- qontract_reconcile-0.10.2.dev291.dist-info/RECORD,,
799
+ qontract_reconcile-0.10.2.dev293.dist-info/METADATA,sha256=1dwm4wJoCGKDdjglqMJw3fdWAvuquaPeE2BIpH7wkUc,24916
800
+ qontract_reconcile-0.10.2.dev293.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
801
+ qontract_reconcile-0.10.2.dev293.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
802
+ qontract_reconcile-0.10.2.dev293.dist-info/RECORD,,
@@ -6451,6 +6451,18 @@
6451
6451
  "isDeprecated": false,
6452
6452
  "deprecationReason": null
6453
6453
  },
6454
+ {
6455
+ "name": "externalOrgId",
6456
+ "description": null,
6457
+ "args": [],
6458
+ "type": {
6459
+ "kind": "SCALAR",
6460
+ "name": "String",
6461
+ "ofType": null
6462
+ },
6463
+ "isDeprecated": false,
6464
+ "deprecationReason": null
6465
+ },
6454
6466
  {
6455
6467
  "name": "environment",
6456
6468
  "description": null,
@@ -34178,6 +34190,18 @@
34178
34190
  "isDeprecated": false,
34179
34191
  "deprecationReason": null
34180
34192
  },
34193
+ {
34194
+ "name": "overwrite",
34195
+ "description": null,
34196
+ "args": [],
34197
+ "type": {
34198
+ "kind": "SCALAR",
34199
+ "name": "Boolean",
34200
+ "ofType": null
34201
+ },
34202
+ "isDeprecated": false,
34203
+ "deprecationReason": null
34204
+ },
34181
34205
  {
34182
34206
  "name": "patch",
34183
34207
  "description": null,
@@ -41014,6 +41038,18 @@
41014
41038
  },
41015
41039
  "isDeprecated": false,
41016
41040
  "deprecationReason": null
41041
+ },
41042
+ {
41043
+ "name": "promtool_version",
41044
+ "description": null,
41045
+ "args": [],
41046
+ "type": {
41047
+ "kind": "SCALAR",
41048
+ "name": "String",
41049
+ "ofType": null
41050
+ },
41051
+ "isDeprecated": false,
41052
+ "deprecationReason": null
41017
41053
  }
41018
41054
  ],
41019
41055
  "inputFields": null,
@@ -40,6 +40,7 @@ query TemplateCollection_v1($name: String) {
40
40
  autoApproved
41
41
  condition
42
42
  targetPath
43
+ overwrite
43
44
  patch {
44
45
  path
45
46
  identifier
@@ -92,6 +93,7 @@ class TemplateV1(ConfiguredBaseModel):
92
93
  auto_approved: Optional[bool] = Field(..., alias="autoApproved")
93
94
  condition: Optional[str] = Field(..., alias="condition")
94
95
  target_path: str = Field(..., alias="targetPath")
96
+ overwrite: Optional[bool] = Field(..., alias="overwrite")
95
97
  patch: Optional[TemplatePatchV1] = Field(..., alias="patch")
96
98
  template: str = Field(..., alias="template")
97
99
  template_render_options: Optional[TemplateRenderOptionsV1] = Field(..., alias="templateRenderOptions")
@@ -24,6 +24,7 @@ query Templatev1 {
24
24
  name
25
25
  autoApproved
26
26
  condition
27
+ overwrite
27
28
  patch {
28
29
  path
29
30
  identifier
@@ -78,6 +79,7 @@ class TemplateV1(ConfiguredBaseModel):
78
79
  name: str = Field(..., alias="name")
79
80
  auto_approved: Optional[bool] = Field(..., alias="autoApproved")
80
81
  condition: Optional[str] = Field(..., alias="condition")
82
+ overwrite: Optional[bool] = Field(..., alias="overwrite")
81
83
  patch: Optional[TemplatePatchV1] = Field(..., alias="patch")
82
84
  target_path: str = Field(..., alias="targetPath")
83
85
  template: str = Field(..., alias="template")
@@ -1,5 +1,6 @@
1
1
  import logging
2
2
  from abc import ABC, abstractmethod
3
+ from functools import cached_property
3
4
  from io import StringIO
4
5
  from typing import Any, Protocol
5
6
 
@@ -33,6 +34,7 @@ class Template(Protocol):
33
34
  condition: str | None
34
35
  target_path: str
35
36
  template: str
37
+ overwrite: bool | None
36
38
 
37
39
  def dict(self) -> dict[str, str]: ...
38
40
 
@@ -77,14 +79,28 @@ class Renderer(ABC):
77
79
  """
78
80
  pass
79
81
 
82
+ @abstractmethod
83
+ def target_exist(self) -> bool:
84
+ """
85
+ if target (file or patch block) already exists.
86
+ """
87
+ pass
88
+
80
89
  def render_target_path(self) -> str:
81
90
  return self._render_template(self.template.target_path).strip()
82
91
 
83
92
  def render_condition(self) -> bool:
84
- return self._render_template(self.template.condition or "True") == "True"
93
+ if self._render_template(self.template.condition or "True") != "True":
94
+ return False
95
+ if self.template.overwrite:
96
+ return True
97
+ return not self.target_exist()
85
98
 
86
99
 
87
100
  class FullRenderer(Renderer):
101
+ def target_exist(self) -> bool:
102
+ return self.data.current is not None
103
+
88
104
  def render_output(self) -> str:
89
105
  """
90
106
  Take the variables from Template Data and render the template with it.
@@ -95,6 +111,12 @@ class FullRenderer(Renderer):
95
111
 
96
112
 
97
113
  class PatchRenderer(Renderer):
114
+ def target_exist(self) -> bool:
115
+ if isinstance(self._matched_value, list):
116
+ dta_identifier = self._get_identifier(self._data_to_add)
117
+ return self._find_index(self._matched_value, dta_identifier) is not None
118
+ return True
119
+
98
120
  def render_output(self) -> str:
99
121
  """
100
122
  Takes the variables from Template Data and render the template with it.
@@ -105,70 +127,69 @@ class PatchRenderer(Renderer):
105
127
 
106
128
  This method returns the entire file as a string.
107
129
  """
108
- if self.template.patch is None: # here to satisfy mypy
109
- raise ValueError("PatchRenderer requires a patch")
110
-
111
- p = parse_jsonpath(self._render_template(self.template.patch.path))
112
-
113
- matched_values = [match.value for match in p.find(self.data.current)]
114
-
115
- if len(matched_values) != 1:
116
- raise ValueError(
117
- f"Expected exactly one match for {self.template.patch.path}, got {len(matched_values)}"
118
- )
119
- matched_value = matched_values[0]
120
-
121
- data_to_add = self.ruamel_instance.load(
122
- self._render_template(self.template.template)
123
- )
124
-
125
- if isinstance(matched_value, list):
126
-
127
- def get_identifier(data: dict[str, Any]) -> Any:
128
- assert self.template.patch is not None # mypy
129
- if not self.template.patch.identifier:
130
- raise ValueError(
131
- f"Expected identifier in patch for list at {self.template}"
132
- )
133
- if self.template.patch.identifier.startswith(
134
- "$"
135
- ) and not self.template.patch.identifier.startswith("$ref"):
136
- # jsonpath and list of strings support
137
- if matches := [
138
- match.value
139
- for match in parse_jsonpath(
140
- self.template.patch.identifier
141
- ).find(data)
142
- ]:
143
- return matches[0]
144
- return None
145
- return data.get(self.template.patch.identifier)
146
-
147
- dta_identifier = get_identifier(data_to_add)
130
+ if isinstance(self._matched_value, list):
131
+ dta_identifier = self._get_identifier(self._data_to_add)
148
132
  if not dta_identifier:
133
+ assert self.template.patch is not None # mypy
149
134
  raise ValueError(
150
135
  f"Expected identifier {self.template.patch.identifier} in data to add"
151
136
  )
152
-
153
- index = next(
154
- (
155
- index
156
- for index, data in enumerate(matched_value)
157
- if get_identifier(data) == dta_identifier
158
- ),
159
- None,
160
- )
161
- if index is None:
162
- matched_value.append(data_to_add)
137
+ if (
138
+ index := self._find_index(self._matched_value, dta_identifier)
139
+ ) is not None:
140
+ self._matched_value[index] = self._data_to_add
163
141
  else:
164
- matched_value[index] = data_to_add
142
+ self._matched_value.append(self._data_to_add)
165
143
  else:
166
- matched_value.update(data_to_add)
144
+ self._matched_value.update(self._data_to_add)
167
145
 
168
146
  with StringIO() as s:
169
147
  self.ruamel_instance.dump(self.data.current, s)
170
148
  return s.getvalue()
171
149
 
150
+ @cached_property
151
+ def _matched_value(self) -> Any:
152
+ assert self.template.patch is not None # mypy
153
+ p = parse_jsonpath(self._render_template(self.template.patch.path))
154
+ matched_values = [match.value for match in p.find(self.data.current)]
155
+ if len(matched_values) != 1:
156
+ raise ValueError(
157
+ f"Expected exactly one match for {self.template.patch.path}, got {len(matched_values)}"
158
+ )
159
+ return matched_values[0]
160
+
161
+ @cached_property
162
+ def _data_to_add(self) -> Any:
163
+ return self.ruamel_instance.load(self._render_template(self.template.template))
164
+
165
+ def _get_identifier(self, data: dict[str, Any]) -> Any:
166
+ assert self.template.patch is not None # mypy
167
+ if not self.template.patch.identifier:
168
+ raise ValueError(
169
+ f"Expected identifier in patch for list at {self.template}"
170
+ )
171
+ if self.template.patch.identifier.startswith(
172
+ "$"
173
+ ) and not self.template.patch.identifier.startswith("$ref"):
174
+ # jsonpath and list of strings support
175
+ if matches := [
176
+ match.value
177
+ for match in parse_jsonpath(self.template.patch.identifier).find(data)
178
+ ]:
179
+ return matches[0]
180
+ return None
181
+ return data.get(self.template.patch.identifier)
182
+
183
+ def _find_index(
184
+ self,
185
+ matched_value: list,
186
+ dta_identifier: Any,
187
+ ) -> int | None:
188
+ for index, data in enumerate(matched_value):
189
+ if self._get_identifier(data) == dta_identifier:
190
+ return index
191
+ return None
192
+
172
193
 
173
194
  def create_renderer(
174
195
  template: Template,
@@ -104,14 +104,17 @@ class LocalFilePersistence(FilePersistence):
104
104
  def read(self, path: str) -> str | None:
105
105
  return self._read_local_file(join_path(self.app_interface_data_path, path))
106
106
 
107
- def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
108
- if self.dry_run:
109
- return
107
+ def flush(self) -> None:
110
108
  for output in self.outputs:
111
109
  filepath = Path(join_path(self.app_interface_data_path, output.path))
112
110
  filepath.parent.mkdir(parents=True, exist_ok=True)
113
111
  filepath.write_text(output.content, encoding="utf-8")
114
112
 
113
+ def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
114
+ if self.dry_run:
115
+ return
116
+ self.flush()
117
+
115
118
 
116
119
  class PersistenceTransaction(FilePersistence):
117
120
  """
@@ -190,13 +190,14 @@ from reconcile.utils.terraform import safe_resource_id
190
190
  from reconcile.utils.vcs import VCS
191
191
 
192
192
  GH_BASE_URL = os.environ.get("GITHUB_API", "https://api.github.com")
193
- LOGTOES_RELEASE = "repos/app-sre/logs-to-elasticsearch-lambda/releases/latest"
194
- KINESIS_TO_OS_RELEASE = (
193
+ ROSA_AUTH_LOGTOES_RELEASE = "repos/app-sre/logs-to-elasticsearch-lambda/releases/latest"
194
+ ROSA_AUTH_KINESIS_TO_OS_RELEASE = (
195
195
  "https://github.com/app-sre/kinesis-to-opensearch-lambda/releases/latest"
196
196
  )
197
- ROSA_AUTHENTICATOR_PRE_SIGNUP_RELEASE = (
197
+ ROSA_AUTH_PRE_SIGNUP_RELEASE = (
198
198
  "repos/app-sre/cognito-pre-signup-trigger/releases/latest"
199
199
  )
200
+ ROSA_AUTH_PRE_TOKEN_RELEASE = "repos/app-sre/cognito-pre-token-trigger/releases/latest"
200
201
  # VARIABLE_KEYS are passed to common_values on instantiation of a provider
201
202
  VARIABLE_KEYS = [
202
203
  "region",
@@ -546,12 +547,14 @@ class TerrascriptClient:
546
547
  self.partitions = {
547
548
  a["name"]: a.get("partition") or "aws" for a in filtered_accounts
548
549
  }
549
- self.logtoes_zip = ""
550
- self.logtoes_zip_lock = Lock()
551
- self.rosa_authenticator_pre_signup_zip = ""
552
- self.rosa_authenticator_pre_signup_zip_lock = Lock()
553
- self.lambda_zip: dict[str, str] = {}
554
- self.lambda_lock = Lock()
550
+ self.rosa_auth_logtoes_zip = ""
551
+ self.rosa_auth_logtoes_zip_lock = Lock()
552
+ self.rosa_auth_pre_signup_zip = ""
553
+ self.rosa_auth_pre_signup_zip_lock = Lock()
554
+ self.rosa_auth_pre_token_zip = ""
555
+ self.rosa_auth_pre_token_zip_lock = Lock()
556
+ self.rosa_auth_kinesis_to_os_zip: dict[str, str] = {}
557
+ self.rosa_auth_kinesis_to_os_zip_lock = Lock()
555
558
  self.github: Github | None = None
556
559
  self.github_lock = Lock()
557
560
  self.gitlab: GitLabApi | None = None
@@ -608,15 +611,17 @@ class TerrascriptClient:
608
611
  )
609
612
  raise ValueError(f"No bucket config found for account {account_name}")
610
613
 
611
- def get_lambda_zip(self, release_url: str) -> str:
612
- if not self.lambda_zip.get(release_url):
613
- with self.lambda_lock:
614
+ def get_rosa_auth_kinesis_to_os_zip(self, release_url: str) -> str:
615
+ if not self.rosa_auth_kinesis_to_os_zip.get(release_url):
616
+ with self.rosa_auth_kinesis_to_os_zip_lock:
614
617
  # this may have already happened, so we check again
615
- if not self.lambda_zip.get(release_url):
616
- self.lambda_zip[release_url] = self.download_lambda_zip(release_url)
617
- return self.lambda_zip[release_url]
618
+ if not self.rosa_auth_kinesis_to_os_zip.get(release_url):
619
+ self.rosa_auth_kinesis_to_os_zip[release_url] = (
620
+ self.download_rosa_auth_kinesis_to_os_zip(release_url)
621
+ )
622
+ return self.rosa_auth_kinesis_to_os_zip[release_url]
618
623
 
619
- def download_lambda_zip(self, release_url: str) -> str:
624
+ def download_rosa_auth_kinesis_to_os_zip(self, release_url: str) -> str:
620
625
  github = self.init_github()
621
626
  url = release_url.replace("https://", "").split("/")
622
627
  repo_name = f"{url[1]}/{url[2]}"
@@ -639,14 +644,16 @@ class TerrascriptClient:
639
644
  return zip_file
640
645
 
641
646
  def get_logtoes_zip(self, release_url):
642
- if not self.logtoes_zip:
643
- with self.logtoes_zip_lock:
647
+ if not self.rosa_auth_logtoes_zip:
648
+ with self.rosa_auth_logtoes_zip_lock:
644
649
  # this may have already happened, so we check again
645
- if not self.logtoes_zip:
650
+ if not self.rosa_auth_logtoes_zip:
646
651
  self.token = get_default_config()["token"]
647
- self.logtoes_zip = self.download_logtoes_zip(LOGTOES_RELEASE)
648
- if release_url == LOGTOES_RELEASE:
649
- return self.logtoes_zip
652
+ self.rosa_auth_logtoes_zip = self.download_logtoes_zip(
653
+ ROSA_AUTH_LOGTOES_RELEASE
654
+ )
655
+ if release_url == ROSA_AUTH_LOGTOES_RELEASE:
656
+ return self.rosa_auth_logtoes_zip
650
657
  return self.download_logtoes_zip(release_url)
651
658
 
652
659
  def download_logtoes_zip(self, release_url):
@@ -663,28 +670,57 @@ class TerrascriptClient:
663
670
  f.write(r.content)
664
671
  return zip_file
665
672
 
666
- def get_rosa_authenticator_zip(self, release_url):
667
- if not self.rosa_authenticator_pre_signup_zip:
668
- with self.rosa_authenticator_pre_signup_zip_lock:
673
+ def get_rosa_auth_pre_signup_zip(self, release_url):
674
+ if not self.rosa_auth_pre_signup_zip:
675
+ with self.rosa_auth_pre_signup_zip_lock:
669
676
  # this may have already happened, so we check again
670
- if not self.rosa_authenticator_pre_signup_zip:
677
+ if not self.rosa_auth_pre_signup_zip:
671
678
  self.token = get_default_config()["token"]
672
- self.rosa_authenticator_pre_signup_zip = (
673
- self.download_rosa_authenticator_zip(
674
- ROSA_AUTHENTICATOR_PRE_SIGNUP_RELEASE
679
+ self.rosa_auth_pre_signup_zip = (
680
+ self.download_rosa_auth_pre_signup_zip(
681
+ ROSA_AUTH_PRE_SIGNUP_RELEASE
675
682
  )
676
683
  )
677
- if release_url == ROSA_AUTHENTICATOR_PRE_SIGNUP_RELEASE:
678
- return self.rosa_authenticator_pre_signup_zip
679
- return self.download_rosa_authenticator_zip(release_url)
684
+ if release_url == ROSA_AUTH_PRE_SIGNUP_RELEASE:
685
+ return self.rosa_auth_pre_signup_zip
686
+ return self.download_rosa_auth_pre_signup_zip(release_url)
680
687
 
681
- def download_rosa_authenticator_zip(self, release_url):
688
+ def download_rosa_auth_pre_signup_zip(self, release_url):
682
689
  headers = {"Authorization": "token " + self.token}
683
690
  r = requests.get(GH_BASE_URL + "/" + release_url, headers=headers, timeout=60)
684
691
  r.raise_for_status()
685
692
  data = r.json()
686
693
  zip_url = data["assets"][0]["browser_download_url"]
687
- zip_file = "/tmp/RosaAuthenticatorLambda-" + data["tag_name"] + ".zip"
694
+ zip_file = "/tmp/RosaAuthPreSignUp-" + data["tag_name"] + ".zip"
695
+ if not os.path.exists(zip_file):
696
+ r = requests.get(zip_url, timeout=60)
697
+ r.raise_for_status()
698
+ with open(zip_file, "wb") as f:
699
+ f.write(r.content)
700
+ return zip_file
701
+
702
+ def get_rosa_auth_pre_token_zip(self, release_url):
703
+ if not self.rosa_auth_pre_token_zip:
704
+ with self.rosa_auth_pre_token_zip_lock:
705
+ # this may have already happened, so we check again
706
+ if not self.rosa_auth_pre_token_zip:
707
+ self.token = get_default_config()["token"]
708
+ self.rosa_auth_pre_token_zip = (
709
+ self.download_rosa_auth_pre_token_zip(
710
+ ROSA_AUTH_PRE_TOKEN_RELEASE
711
+ )
712
+ )
713
+ if release_url == ROSA_AUTH_PRE_TOKEN_RELEASE:
714
+ return self.rosa_auth_pre_token_zip
715
+ return self.download_rosa_auth_pre_token_zip(release_url)
716
+
717
+ def download_rosa_auth_pre_token_zip(self, release_url):
718
+ headers = {"Authorization": "token " + self.token}
719
+ r = requests.get(GH_BASE_URL + "/" + release_url, headers=headers, timeout=60)
720
+ r.raise_for_status()
721
+ data = r.json()
722
+ zip_url = data["assets"][0]["browser_download_url"]
723
+ zip_file = "/tmp/RosaAuthPreToken-" + data["tag_name"] + ".zip"
688
724
  if not os.path.exists(zip_file):
689
725
  r = requests.get(zip_url, timeout=60)
690
726
  r.raise_for_status()
@@ -3697,7 +3733,7 @@ class TerrascriptClient:
3697
3733
  data.aws_elasticsearch_domain(es_identifier, **es_domain)
3698
3734
  )
3699
3735
 
3700
- release_url = common_values.get("release_url", LOGTOES_RELEASE)
3736
+ release_url = common_values.get("release_url", ROSA_AUTH_LOGTOES_RELEASE)
3701
3737
  zip_file = self.get_logtoes_zip(release_url)
3702
3738
 
3703
3739
  lambda_identifier = f"{identifier}-lambda"
@@ -4007,8 +4043,10 @@ class TerrascriptClient:
4007
4043
  data.aws_elasticsearch_domain(es_identifier, **es_domain)
4008
4044
  )
4009
4045
 
4010
- release_url = common_values.get("release_url", KINESIS_TO_OS_RELEASE)
4011
- zip_file = self.get_lambda_zip(release_url)
4046
+ release_url = common_values.get(
4047
+ "release_url", ROSA_AUTH_KINESIS_TO_OS_RELEASE
4048
+ )
4049
+ zip_file = self.get_rosa_auth_kinesis_to_os_zip(release_url)
4012
4050
 
4013
4051
  lambda_identifier = f"{identifier}-lambda"
4014
4052
  lambda_values = {
@@ -5983,16 +6021,14 @@ class TerrascriptClient:
5983
6021
  tf_resources.append(lambda_iam_role_resource)
5984
6022
 
5985
6023
  # Setup + manage Lambda resources
5986
- # pre-signup lambda
5987
- release_url = common_values.get(
5988
- "release_url", ROSA_AUTHENTICATOR_PRE_SIGNUP_RELEASE
5989
- )
5990
- zip_file = self.get_rosa_authenticator_zip(release_url)
5991
6024
 
6025
+ # pre-signup lambda
6026
+ release_url = common_values.get("release_url", ROSA_AUTH_PRE_SIGNUP_RELEASE)
6027
+ zip_file = self.get_rosa_auth_pre_signup_zip(release_url)
5992
6028
  cognito_pre_signup_lambda_resource = aws_lambda_function(
5993
6029
  "cognito_pre_signup",
5994
6030
  function_name=f"ocm-{identifier}-cognito-pre-signup",
5995
- runtime="nodejs14.x",
6031
+ runtime="nodejs18.x",
5996
6032
  role=f"${{{lambda_iam_role_resource.arn}}}",
5997
6033
  handler="index.handler",
5998
6034
  filename=zip_file,
@@ -6001,6 +6037,21 @@ class TerrascriptClient:
6001
6037
  )
6002
6038
  tf_resources.append(cognito_pre_signup_lambda_resource)
6003
6039
 
6040
+ # pre-token lambda
6041
+ release_url = common_values.get("release_url", ROSA_AUTH_PRE_TOKEN_RELEASE)
6042
+ zip_file = self.get_rosa_auth_pre_token_zip(release_url)
6043
+ cognito_pre_token_lambda_resource = aws_lambda_function(
6044
+ "cognito_pre_token",
6045
+ function_name=f"ocm-{identifier}-cognito-pre-token",
6046
+ runtime="nodejs18.x",
6047
+ role=f"${{{lambda_iam_role_resource.arn}}}",
6048
+ handler="index.handler",
6049
+ filename=zip_file,
6050
+ source_code_hash='${filebase64sha256("' + zip_file + '")}',
6051
+ tracing_config={"mode": "PassThrough"},
6052
+ )
6053
+ tf_resources.append(cognito_pre_token_lambda_resource)
6054
+
6004
6055
  # setup s3_client
6005
6056
  # pattern followed from utils/state.py
6006
6057
  # The variable "account" is the name of the AWS account we are reconciling
@@ -6084,7 +6135,8 @@ class TerrascriptClient:
6084
6135
  "pool",
6085
6136
  name=f"ocm-{identifier}-pool",
6086
6137
  lambda_config={
6087
- "pre_sign_up": f"${{{cognito_pre_signup_lambda_resource.arn}}}"
6138
+ "pre_sign_up": f"${{{cognito_pre_signup_lambda_resource.arn}}}",
6139
+ "pre_token_generation": f"${{{cognito_pre_token_lambda_resource.arn}}}",
6088
6140
  },
6089
6141
  **pool_args,
6090
6142
  )
@@ -6100,6 +6152,16 @@ class TerrascriptClient:
6100
6152
  )
6101
6153
  tf_resources.append(cognito_pre_signup_lambda_permission_resource)
6102
6154
 
6155
+ # Finish up lambda - pre token
6156
+ cognito_pre_token_lambda_permission_resource = aws_lambda_permission(
6157
+ "cognito_pre_token_permission",
6158
+ action="lambda:InvokeFunction",
6159
+ function_name=cognito_pre_token_lambda_resource.function_name,
6160
+ source_arn=f"${{{cognito_user_pool_resource.arn}}}",
6161
+ principal="cognito-idp.amazonaws.com",
6162
+ )
6163
+ tf_resources.append(cognito_pre_token_lambda_permission_resource)
6164
+
6103
6165
  # POOL DOMAIN
6104
6166
  cognito_user_pool_domain_resource = aws_cognito_user_pool_domain(
6105
6167
  "userpool_domain",
@@ -6582,7 +6644,7 @@ class TerrascriptClient:
6582
6644
  response_parameters={
6583
6645
  "method.response.header.Location": f"'{user_pool_url}/oauth2/authorize?client_id="
6584
6646
  f"${{{cognito_user_pool_client.id}}}\u0026response_type=code"
6585
- f"\u0026scope=openid+gateway/AccessToken\u0026redirect_uri={bucket_url}/"
6647
+ f"\u0026scope=email+openid+gateway/AccessToken\u0026redirect_uri={bucket_url}/"
6586
6648
  "token.html'",
6587
6649
  },
6588
6650
  depends_on=["aws_api_gateway_integration.gw_integration_auth"],