qontract-reconcile 0.10.1rc1168__py3-none-any.whl → 0.10.1rc1170__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.1
2
2
  Name: qontract-reconcile
3
- Version: 0.10.1rc1168
3
+ Version: 0.10.1rc1170
4
4
  Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
5
5
  Home-page: https://github.com/app-sre/qontract-reconcile
6
6
  Author: Red Hat App-SRE Team
@@ -10,7 +10,7 @@ reconcile/aws_iam_password_reset.py,sha256=q96mwr2KeEQ5bpNniGlgIMZTxiuLSodcYfX-t
10
10
  reconcile/aws_support_cases_sos.py,sha256=hl_9L53yQYRQxKs3IWrd69Cc60XK067g_bJRM9B0udo,2975
11
11
  reconcile/blackbox_exporter_endpoint_monitoring.py,sha256=O1wFp52EyF538c6txaWBs8eMtUIy19gyHZ6VzJ6QXS8,3512
12
12
  reconcile/checkpoint.py,sha256=_JhMxrye5BgkRMxWYuf7Upli6XayPINKSsuo3ynHTRc,5010
13
- reconcile/cli.py,sha256=-RT8biWI5mY9o6gYZ7HxXowePxmxbnfGKKqhMeUdiDc,107720
13
+ reconcile/cli.py,sha256=2W2_vnq68954f-lhNwX3yGgCrofwSbDtAk33b4TnicE,107467
14
14
  reconcile/closedbox_endpoint_monitoring_base.py,sha256=rLh16BOlBOxTmJ8Si3wWyyEpmMlhh4Znx1Gc36qsmOc,4865
15
15
  reconcile/cluster_deployment_mapper.py,sha256=5gumAaRCcFXsabUJ1dnuUy9WrP_FEEM5JnOnE8ch9sE,2326
16
16
  reconcile/dashdotdb_base.py,sha256=l34QDu1G96_Ctnh7ZXdxXgSeCE93GQMdLAkWxmN6vDA,4775
@@ -46,7 +46,7 @@ reconcile/jenkins_roles.py,sha256=pNNYcnmyDCTVytG2mi3BFhq9A7_3l301oFRQtY_q6S8,44
46
46
  reconcile/jenkins_webhooks.py,sha256=K5h0OlCghoHlpot40IRq4BuezfKB1rj4Xk0dCUvqp3o,1952
47
47
  reconcile/jenkins_webhooks_cleaner.py,sha256=JsN_NVPfZJwv1JtSzZXDIHUqGiefL-DRffFnDGau9aY,1539
48
48
  reconcile/jenkins_worker_fleets.py,sha256=PMNGOX0krubFjInPiFT0za0KCiWBLEcVDuXdKRd1BrE,5378
49
- reconcile/jira_permissions_validator.py,sha256=t8FBjBJniRMODQNXfoHKst6qLDOzlua6hqy7rm7tmzs,13222
49
+ reconcile/jira_permissions_validator.py,sha256=GSjLwHrstuO4dGb9Oxfmg_9PLLreFsbJJ8WHhUM8FSY,13144
50
50
  reconcile/jira_watcher.py,sha256=L_UL2MKm2SoIGNsCLThm28pnqCkoFc154JWsD6bURug,3593
51
51
  reconcile/ldap_users.py,sha256=7hdO5CAPl-VNBvDRmKHg13LoblHXXPt7YEKNGomAoGg,3158
52
52
  reconcile/mr_client_gateway.py,sha256=WhjMd-sIXDFCV8-rt8CEjurJ5OYB1pOD0K3o0tZRXQg,1885
@@ -94,7 +94,7 @@ reconcile/quay_mirror.py,sha256=mFp4Z5Nwl-DcFbbsJBOB8f9ldohFT-V67o868d5ux1s,1536
94
94
  reconcile/quay_mirror_org.py,sha256=utrJpJaKCs7U6WX6DODdfCeB0EmX-lUC8Y5fkmpgFSs,10764
95
95
  reconcile/quay_permissions.py,sha256=9KOutS1w4RFQqkvMSy54VtsKNx56-phzP6yI_rEW-B8,4244
96
96
  reconcile/quay_repos.py,sha256=cuEYG0HUe0ut5yvLdEwOF5-CmccpXQHRb_wDazvDrvQ,6895
97
- reconcile/queries.py,sha256=RF3lGuwo_Y6J_NDdb7XN4s9vBWxHPzfTzw_8mCZcwLQ,51542
97
+ reconcile/queries.py,sha256=of54G-Vqy-_deKdZcWs-B9LwxIdhQFUlQ198pBGCp3g,51488
98
98
  reconcile/query_validator.py,sha256=MSh5pKLBksws4AqfuvT8nrIGucIbqX-IOzYyPYTLO7k,1491
99
99
  reconcile/requests_sender.py,sha256=914iluuF4UVgG3VyxxtnHOu4yf6YKS2fIy6PViSsFTQ,3875
100
100
  reconcile/resource_scraper.py,sha256=znXCHrU7YwPfKuxGBiUrV7T1tYtn4vlz9qmZlfy6Flg,2307
@@ -113,7 +113,7 @@ reconcile/terraform_cloudflare_dns.py,sha256=-aLEe2QnH5cJPu7HWqs-R9NmQ1NlFbcVUm0
113
113
  reconcile/terraform_cloudflare_resources.py,sha256=pq8Ieo5NmB-dYQ9X2F0s6iEoINMzhiqGw2yQK4ovok4,14980
114
114
  reconcile/terraform_cloudflare_users.py,sha256=iyTG5sj20Jg4J4qWJ144KVptfIHGOSfH8wQKxu0imq0,13942
115
115
  reconcile/terraform_repo.py,sha256=TKqlodhQGoAtQ6nDm04TNlpx4wpgJ_n4atoUK5Rfd7o,16444
116
- reconcile/terraform_resources.py,sha256=jpBtp6vezq79jQ7rWdk49_mW-PIUFVzFK54ilVSEZFM,19564
116
+ reconcile/terraform_resources.py,sha256=iufjMJs_aSEvmh7Cg11beCxKmV8nrOLOpEtiTryPNx0,19470
117
117
  reconcile/terraform_tgw_attachments.py,sha256=09svJG9pAiwWp4aY0xRoQRV90T4ZNwHG3r8flI-ZS_s,18810
118
118
  reconcile/terraform_users.py,sha256=HqSm3ev3b8dZ9J6F_phDZB-FQsnlsdeKp9RPoY1cU94,10188
119
119
  reconcile/terraform_vpc_peerings.py,sha256=VLSfuO7FvHN5McopRiKoKJDHCmIhYtlJEHv_hxV5kcM,27669
@@ -525,7 +525,7 @@ reconcile/test/test_gitlab_permissions.py,sha256=aMf5SUeVp-aQ1bWGQPQLYa85auzRlyf
525
525
  reconcile/test/test_instrumented_wrappers.py,sha256=CZzhnQH0c4i7-Rxjg7-0dfFMvVPegLHL46z5NHOOCwo,608
526
526
  reconcile/test/test_integrations_manager.py,sha256=xpyQAVz57wAbovrcQzAeuyq8VzdItUyW2d2kp1WW_5c,38184
527
527
  reconcile/test/test_jenkins_worker_fleets.py,sha256=o1jlT7OBBSgu0M3iI4xMdz_x6SciF7yhNBpLk5gTJfg,2361
528
- reconcile/test/test_jira_permissions_validator.py,sha256=zhtAL97IkCyY9R29fDRvDCE1z9S7OVQV7gqu-7Vo5-4,16279
528
+ reconcile/test/test_jira_permissions_validator.py,sha256=OA3hcnsH0eYXA4khJO6d1PE7zo2f0vYg73PrCqTzkyo,17646
529
529
  reconcile/test/test_jump_host.py,sha256=EeHMhT5rTZgx__R_29mtCBWt-NZCcKsQ6CR-B3xmCps,3284
530
530
  reconcile/test/test_ldap_users.py,sha256=_clylG-Qfes8yNb9T3CMaDQovLwyVZlTfJgRtGHARgE,4080
531
531
  reconcile/test/test_make.py,sha256=zTdjgq-3idFlec_0qJenk9wWw0QMLvSpJfPsptXmync,677
@@ -687,7 +687,7 @@ reconcile/utils/helpers.py,sha256=womAD2bKPUAFOjHvNPAe_2Hsb-oVTxuQiYPGeR-Thp0,17
687
687
  reconcile/utils/imap_client.py,sha256=h8YDiCSCvroErhpH_-KGYI7Y2WU2Q2oSpuxDFbOkSbY,1989
688
688
  reconcile/utils/instrumented_wrappers.py,sha256=eVwMoa6FCrYxLv3RML3WpZF9qKVfCTjMxphgVXG03OM,1073
689
689
  reconcile/utils/jenkins_api.py,sha256=RaKuZmO7_lbI-hE6c_Pq2a6CQdmBVj7BcP2jR68cIbI,7081
690
- reconcile/utils/jira_client.py,sha256=VdWJfI9VAK2ZtYHmvuqL635t0S5eYtmBY6zR8R-qr-s,7775
690
+ reconcile/utils/jira_client.py,sha256=oWi7rcAP1C59oIBTPg6kRntI25Zm4e7FyvdVYvZ9RZ8,7881
691
691
  reconcile/utils/jjb_client.py,sha256=9Aw4SfV4pBNW5Kj7dGZwakUlwsWuqtAAiSD9o1F4AZA,14524
692
692
  reconcile/utils/jsonpath.py,sha256=wdxOMqR-GMpQf5vRPWRMqAF7bCiXDBkkcFfY2U4j_tk,5536
693
693
  reconcile/utils/jump_host.py,sha256=svtWy64zZAx7XYY62vu580KgwdatmHjX4-BdxEl58Uw,5158
@@ -727,7 +727,7 @@ reconcile/utils/state.py,sha256=W0_awkLAPX18hNOF_60o73tkPxDUylqbzYNHfl_sDsk,1638
727
727
  reconcile/utils/structs.py,sha256=LcbLEg8WxfRqM6nW7NhcWN0YeqF7SQzxOgntmLs1SgY,352
728
728
  reconcile/utils/template.py,sha256=wTvRU4AnAV_o042tD4Mwls2dwWMuk7MKnde3MaCjaYg,331
729
729
  reconcile/utils/terraform_client.py,sha256=LjX2U2E0Dglt2S_KA5jWQ_dVC8sPn4FEAh0xW_d6JTk,35953
730
- reconcile/utils/terrascript_aws_client.py,sha256=k2N_ojFPhiPwI0KdkPpfgboq7D9OgledxTSwKarHpKM,283460
730
+ reconcile/utils/terrascript_aws_client.py,sha256=q6Ydbjle7K5Z3LYpoRJGXnb50ix6aGN6jLvP2vglPz8,283769
731
731
  reconcile/utils/three_way_diff_strategy.py,sha256=oQcHXd9LVhirJfoaOBoHUYuZVGfyL2voKr6KVI34zZE,4833
732
732
  reconcile/utils/throughput.py,sha256=iP4UWAe2LVhDo69mPPmgo9nQ7RxHD6_GS8MZe-aSiuM,344
733
733
  reconcile/utils/vault.py,sha256=pi0PuyopvCq1gW0cldvy1-Ff6bqLUlCKC2MW0sifvSE,15043
@@ -880,8 +880,8 @@ tools/test/test_qontract_cli.py,sha256=iuzKbQ6ahinvjoQmQLBrG4shey0z-1rB6qCgS8T6d
880
880
  tools/test/test_saas_promotion_state.py,sha256=dy4kkSSAQ7bC0Xp2CociETGN-2aABEfL6FU5D9Jl00Y,6056
881
881
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
882
882
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
883
- qontract_reconcile-0.10.1rc1168.dist-info/METADATA,sha256=S5PpMUixHxBbTgNxlV_1wOQVEw9FKiyt5Tw4H9p0Uuc,2213
884
- qontract_reconcile-0.10.1rc1168.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
885
- qontract_reconcile-0.10.1rc1168.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
886
- qontract_reconcile-0.10.1rc1168.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
887
- qontract_reconcile-0.10.1rc1168.dist-info/RECORD,,
883
+ qontract_reconcile-0.10.1rc1170.dist-info/METADATA,sha256=jImSzLQtbCTK_xWaoXSosEFCUSEwNc55O8fQkGPhaxA,2213
884
+ qontract_reconcile-0.10.1rc1170.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
885
+ qontract_reconcile-0.10.1rc1170.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
886
+ qontract_reconcile-0.10.1rc1170.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
887
+ qontract_reconcile-0.10.1rc1170.dist-info/RECORD,,
reconcile/cli.py CHANGED
@@ -1158,18 +1158,12 @@ def jenkins_webhooks_cleaner(ctx):
1158
1158
 
1159
1159
 
1160
1160
  @integration.command(short_help="Validate permissions in Jira.")
1161
- @click.option(
1162
- "--exit-on-permission-errors/--no-exit-on-permission-errors",
1163
- help="Throw and error in case of board permission errors. Useful for PR checks.",
1164
- default=True,
1165
- )
1166
1161
  @enable_extended_early_exit
1167
1162
  @extended_early_exit_cache_ttl_seconds
1168
1163
  @log_cached_log_output
1169
1164
  @click.pass_context
1170
1165
  def jira_permissions_validator(
1171
1166
  ctx,
1172
- exit_on_permission_errors,
1173
1167
  enable_extended_early_exit,
1174
1168
  extended_early_exit_cache_ttl_seconds,
1175
1169
  log_cached_log_output,
@@ -1179,7 +1173,6 @@ def jira_permissions_validator(
1179
1173
  run_integration(
1180
1174
  reconcile.jira_permissions_validator,
1181
1175
  ctx.obj,
1182
- exit_on_permission_errors,
1183
1176
  enable_extended_early_exit=enable_extended_early_exit,
1184
1177
  extended_early_exit_cache_ttl_seconds=extended_early_exit_cache_ttl_seconds,
1185
1178
  log_cached_log_output=log_cached_log_output,
@@ -61,11 +61,12 @@ class ValidationError(IntFlag):
61
61
  PERMISSION_ERROR = auto()
62
62
  PUBLIC_PROJECT_NO_SECURITY_LEVEL = auto()
63
63
  INVALID_COMPONENT = auto()
64
+ PROJECT_ARCHIVED = auto()
64
65
 
65
66
 
66
67
  class RunnerParams(TypedDict):
67
- exit_on_permission_errors: bool
68
68
  boards: list[JiraBoardV1]
69
+ dry_run: bool
69
70
 
70
71
 
71
72
  class CacheSource(TypedDict):
@@ -82,6 +83,10 @@ def board_is_valid(
82
83
  ) -> ValidationError:
83
84
  error = ValidationError(0)
84
85
  try:
86
+ if jira.is_archived:
87
+ logging.error(f"[{board.name}] project is archived")
88
+ return ValidationError.PROJECT_ARCHIVED
89
+
85
90
  if not jira.can_create_issues():
86
91
  logging.error(f"[{board.name}] can not create issues in project")
87
92
  error |= ValidationError.CANT_CREATE_ISSUE
@@ -196,11 +201,11 @@ def board_is_valid(
196
201
  def validate_boards(
197
202
  metrics_container: metrics.MetricsContainer,
198
203
  secret_reader: SecretReaderBase,
199
- exit_on_permission_errors: bool,
200
204
  jira_client_settings: JiraWatcherSettings | None,
201
205
  jira_boards: Iterable[JiraBoardV1],
202
206
  default_issue_type: str,
203
207
  default_reopen_state: str,
208
+ dry_run: bool,
204
209
  jira_client_class: type[JiraClient] = JiraClient,
205
210
  ) -> bool:
206
211
  error = False
@@ -238,15 +243,15 @@ def validate_boards(
238
243
  ),
239
244
  value=1,
240
245
  )
241
- # don't fail during PR checks at the moment
242
- # this make the transistion to the new integration behaviour much smoother
243
- if exit_on_permission_errors:
246
+ if dry_run:
247
+ # throw an error for MR checks but not in prod mode
244
248
  error = True
245
249
  case (
246
- ValidationError.PERMISSION_ERROR | ValidationError.CANT_CREATE_ISSUE
250
+ ValidationError.CANT_CREATE_ISSUE | ValidationError.PROJECT_ARCHIVED
247
251
  ):
248
- # we can't create jira tickets, and we don't have all needed the permissions
249
- error = True
252
+ if dry_run:
253
+ # throw an error for MR checks but not in prod mode
254
+ error = True
250
255
  case _:
251
256
  error = True
252
257
  except Exception as e:
@@ -269,7 +274,6 @@ def export_boards(boards: list[JiraBoardV1]) -> list[dict]:
269
274
 
270
275
  def run(
271
276
  dry_run: bool,
272
- exit_on_permission_errors: bool,
273
277
  enable_extended_early_exit: bool = False,
274
278
  extended_early_exit_cache_ttl_seconds: int = 3600,
275
279
  log_cached_log_output: bool = False,
@@ -277,8 +281,8 @@ def run(
277
281
  gql_api = gql.get_api()
278
282
  boards = get_jira_boards(query_func=gql_api.query)
279
283
  runner_params: RunnerParams = {
280
- "exit_on_permission_errors": exit_on_permission_errors,
281
284
  "boards": boards,
285
+ "dry_run": dry_run,
282
286
  }
283
287
  if enable_extended_early_exit and get_feature_toggle_state(
284
288
  "jira-permissions-validator-extended-early-exit",
@@ -308,9 +312,7 @@ def run(
308
312
  runner(**runner_params)
309
313
 
310
314
 
311
- def runner(
312
- exit_on_permission_errors: bool, boards: list[JiraBoardV1]
313
- ) -> ExtendedEarlyExitRunnerResult:
315
+ def runner(boards: list[JiraBoardV1], dry_run: bool) -> ExtendedEarlyExitRunnerResult:
314
316
  gql_api = gql.get_api()
315
317
  settings = get_jira_settings(gql_api=gql_api)
316
318
  jiralert_settings = get_jiralert_settings(query_func=gql_api.query)
@@ -321,11 +323,11 @@ def runner(
321
323
  error = validate_boards(
322
324
  metrics_container=metrics_container,
323
325
  secret_reader=secret_reader,
324
- exit_on_permission_errors=exit_on_permission_errors,
325
326
  jira_client_settings=settings.jira_watcher,
326
327
  jira_boards=boards,
327
328
  default_issue_type=jiralert_settings.default_issue_type,
328
329
  default_reopen_state=jiralert_settings.default_reopen_state,
330
+ dry_run=dry_run,
329
331
  )
330
332
 
331
333
  if error:
reconcile/queries.py CHANGED
@@ -102,11 +102,12 @@ APP_INTERFACE_SETTINGS_QUERY = """
102
102
  readTimeout
103
103
  connectTimeout
104
104
  }
105
- terraformResourcesProviderExclusionsByProvisioner {
106
- provisioner {
107
- name
105
+ terraformResourcesProviderExclusions {
106
+ provider
107
+ excludeProvisioners {
108
+ name
108
109
  }
109
- excludedProviders
110
+ excludeAllProvisioners
110
111
  }
111
112
  }
112
113
  }
@@ -2767,12 +2768,13 @@ def get_jenkins_configs():
2767
2768
 
2768
2769
  TF_RESOURCES_PROVIDER_EXCLUSIONS_BY_PROVISIONER = """
2769
2770
  {
2770
- tf_provider_exclusions_by_provisioner: app_interface_settings_v1 {
2771
- terraformResourcesProviderExclusionsByProvisioner {
2772
- provisioner {
2773
- name
2771
+ tf_provider_exclusions: app_interface_settings_v1 {
2772
+ terraformResourcesProviderExclusions {
2773
+ provider
2774
+ excludeProvisioners {
2775
+ name
2774
2776
  }
2775
- excludedProviders
2777
+ excludeAllProvisioners
2776
2778
  }
2777
2779
  }
2778
2780
  }
@@ -2784,13 +2786,10 @@ def get_tf_resources_provider_exclusions_by_provisioner() -> (
2784
2786
  ):
2785
2787
  gqlapi = gql.get_api()
2786
2788
  settings = gqlapi.query(TF_RESOURCES_PROVIDER_EXCLUSIONS_BY_PROVISIONER)[
2787
- "tf_provider_exclusions_by_provisioner"
2789
+ "tf_provider_exclusions"
2788
2790
  ]
2789
- if (
2790
- len(settings) == 1
2791
- and "terraformResourcesProviderExclusionsByProvisioner" in settings[0]
2792
- ):
2793
- return settings[0]["terraformResourcesProviderExclusionsByProvisioner"]
2791
+ if len(settings) == 1 and "terraformResourcesProviderExclusions" in settings[0]:
2792
+ return settings[0]["terraformResourcesProviderExclusions"]
2794
2793
  return None
2795
2794
 
2796
2795
 
@@ -266,13 +266,11 @@ def setup(
266
266
  ocm_map = None
267
267
  tf_namespaces_dicts = [ns.dict(by_alias=True) for ns in tf_namespaces]
268
268
 
269
- provider_exclusions_by_provisioner = (
270
- settings.get("terraformResourcesProviderExclusionsByProvisioner") or []
271
- )
269
+ provider_exclusions = settings.get("terraformResourcesProviderExclusions") or []
272
270
  ts.init_populate_specs(
273
271
  tf_namespaces_dicts,
274
272
  account_names,
275
- provider_exclusions_by_provisioner=provider_exclusions_by_provisioner,
273
+ provider_exclusions,
276
274
  )
277
275
  tf.populate_terraform_output_secrets(
278
276
  resource_specs=ts.resource_spec_inventory, init_rds_replica_source=True
@@ -93,7 +93,7 @@ def test_jira_permissions_validator_get_jira_boards(
93
93
 
94
94
 
95
95
  @pytest.mark.parametrize(
96
- "board_is_valid, exit_on_permission_errors, error_returned, metric_set",
96
+ "board_is_valid, dry_run, error_returned, metric_set",
97
97
  [
98
98
  (0, True, False, False),
99
99
  (ValidationError.CANT_CREATE_ISSUE, True, True, False),
@@ -104,19 +104,11 @@ def test_jira_permissions_validator_get_jira_boards(
104
104
  (ValidationError.INVALID_PRIORITY, True, True, False),
105
105
  (ValidationError.PUBLIC_PROJECT_NO_SECURITY_LEVEL, True, True, False),
106
106
  (ValidationError.PERMISSION_ERROR, True, True, True),
107
- # special case: CANT_CREATE_ISSUE and PERMISSION_ERROR
108
- (
109
- ValidationError.CANT_CREATE_ISSUE | ValidationError.PERMISSION_ERROR,
110
- True,
111
- True,
112
- False,
113
- ),
114
- (
115
- ValidationError.CANT_CREATE_ISSUE | ValidationError.PERMISSION_ERROR,
116
- False,
117
- True,
118
- False,
119
- ),
107
+ (ValidationError.PROJECT_ARCHIVED, True, True, False),
108
+ # no dry-run
109
+ (ValidationError.CANT_CREATE_ISSUE, False, False, False),
110
+ (ValidationError.PERMISSION_ERROR, False, False, True),
111
+ (ValidationError.PROJECT_ARCHIVED, False, False, False),
120
112
  # test with another error
121
113
  (
122
114
  ValidationError.INVALID_PRIORITY | ValidationError.PERMISSION_ERROR,
@@ -137,7 +129,7 @@ def test_jira_permissions_validator_validate_boards(
137
129
  boards: list[JiraBoardV1],
138
130
  secret_reader: Mock,
139
131
  board_is_valid: ValidationError,
140
- exit_on_permission_errors: bool,
132
+ dry_run: bool,
141
133
  error_returned: bool,
142
134
  metric_set: bool,
143
135
  ) -> None:
@@ -151,11 +143,11 @@ def test_jira_permissions_validator_validate_boards(
151
143
  validate_boards(
152
144
  metrics_container=metrics_container_mock,
153
145
  secret_reader=secret_reader,
154
- exit_on_permission_errors=exit_on_permission_errors,
155
146
  jira_client_settings=None,
156
147
  jira_boards=boards,
157
148
  default_issue_type="task",
158
149
  default_reopen_state="new",
150
+ dry_run=dry_run,
159
151
  jira_client_class=jira_client_class,
160
152
  )
161
153
  == error_returned
@@ -192,6 +184,7 @@ def test_jira_permissions_validator_board_is_valid_happy_path(
192
184
  },
193
185
  )
194
186
  jira_client = mocker.create_autospec(spec=JiraClient)
187
+ jira_client.is_archived = False
195
188
  jira_client.can_create_issues.return_value = True
196
189
  jira_client.can_transition_issues.return_value = True
197
190
  jira_client.project_issue_types.return_value = [
@@ -239,6 +232,7 @@ def test_jira_permissions_validator_board_is_valid_all_errors(
239
232
  },
240
233
  )
241
234
  jira_client = mocker.create_autospec(spec=JiraClient)
235
+ jira_client.is_archived = False
242
236
  jira_client.can_create_issues.return_value = False
243
237
  jira_client.can_transition_issues.return_value = False
244
238
  jira_client.project_issue_types.return_value = []
@@ -290,6 +284,7 @@ def test_jira_permissions_validator_board_is_valid_bad_issue_status(
290
284
  },
291
285
  )
292
286
  jira_client = mocker.create_autospec(spec=JiraClient)
287
+ jira_client.is_archived = False
293
288
  jira_client.can_create_issues.return_value = True
294
289
  jira_client.can_transition_issues.return_value = True
295
290
  jira_client.project_issue_types.return_value = [
@@ -340,6 +335,7 @@ def test_jira_permissions_validator_board_is_valid_public_project(
340
335
  },
341
336
  )
342
337
  jira_client = mocker.create_autospec(spec=JiraClient)
338
+ jira_client.is_archived = False
343
339
  jira_client.can_create_issues.return_value = True
344
340
  jira_client.can_transition_issues.return_value = True
345
341
  jira_client.project_issue_types.return_value = [
@@ -390,6 +386,7 @@ def test_jira_permissions_validator_board_is_valid_permission_error(
390
386
  },
391
387
  )
392
388
  jira_client = mocker.create_autospec(spec=JiraClient)
389
+ jira_client.is_archived = False
393
390
  jira_client.can_create_issues.side_effect = JIRAError(status_code=403)
394
391
  assert (
395
392
  board_is_valid(
@@ -430,6 +427,7 @@ def test_jira_permissions_validator_board_is_valid_exception(
430
427
  },
431
428
  )
432
429
  jira_client = mocker.create_autospec(spec=JiraClient)
430
+ jira_client.is_archived = False
433
431
  jira_client.can_create_issues.side_effect = JIRAError(status_code=500)
434
432
  with pytest.raises(JIRAError):
435
433
  board_is_valid(
@@ -468,6 +466,7 @@ def test_jira_permissions_validator_board_is_valid_exception_401(
468
466
  },
469
467
  )
470
468
  jira_client = mocker.create_autospec(spec=JiraClient)
469
+ jira_client.is_archived = False
471
470
  jira_client.can_create_issues.side_effect = JIRAError(status_code=401)
472
471
  # no error for 401
473
472
  board_is_valid(
@@ -478,3 +477,43 @@ def test_jira_permissions_validator_board_is_valid_exception_401(
478
477
  jira_server_priorities={"Minor": "1", "Major": "2", "Critical": "3"},
479
478
  public_projects=[],
480
479
  )
480
+
481
+
482
+ def test_jira_permissions_validator_board_is_valid_archived(
483
+ mocker: MockerFixture, gql_class_factory: Callable
484
+ ) -> None:
485
+ board = gql_class_factory(
486
+ JiraBoardV1,
487
+ {
488
+ "name": "jira-board-default",
489
+ "server": {
490
+ "serverUrl": "https://jira-server.com",
491
+ "token": {"path": "vault/path/token", "field": "token"},
492
+ },
493
+ "issueType": "bug",
494
+ "issueResolveState": "Closed",
495
+ "issueReopenState": "Open",
496
+ "issueSecurityId": "32168",
497
+ "severityPriorityMappings": {
498
+ "name": "major-major",
499
+ "mappings": [
500
+ {"priority": "Minor"},
501
+ {"priority": "Major"},
502
+ {"priority": "Critical"},
503
+ ],
504
+ },
505
+ },
506
+ )
507
+ jira_client = mocker.create_autospec(spec=JiraClient)
508
+ jira_client.is_archived = True
509
+ assert (
510
+ board_is_valid(
511
+ jira=jira_client,
512
+ board=board,
513
+ default_issue_type="task",
514
+ default_reopen_state="new",
515
+ jira_server_priorities={"Minor": "1", "Major": "2", "Critical": "3"},
516
+ public_projects=[],
517
+ )
518
+ == ValidationError.PROJECT_ARCHIVED
519
+ )
@@ -241,3 +241,7 @@ class JiraClient:
241
241
  def components(self) -> list[str]:
242
242
  """Return a list of all components for the project."""
243
243
  return [c.name for c in self.jira.project_components(self.project)]
244
+
245
+ @property
246
+ def is_archived(self) -> bool:
247
+ return self.jira.project(self.project).archived
@@ -9,7 +9,7 @@ import string
9
9
  import tempfile
10
10
  from collections import Counter
11
11
  from collections.abc import Iterable, Mapping, MutableMapping
12
- from dataclasses import dataclass
12
+ from dataclasses import dataclass, field
13
13
  from ipaddress import (
14
14
  ip_address,
15
15
  ip_network,
@@ -379,6 +379,12 @@ class ElasticSearchLogGroupInfo:
379
379
  log_group_identifier: str
380
380
 
381
381
 
382
+ @dataclass
383
+ class Exclusion:
384
+ all: bool = False
385
+ provisioners: set[str] = field(default_factory=set)
386
+
387
+
382
388
  class ProviderExcludedError(Exception):
383
389
  def __init__(self, spec: ExternalResourceSpec) -> None:
384
390
  super().__init__(
@@ -1543,38 +1549,49 @@ class TerrascriptClient: # pylint: disable=too-many-public-methods
1543
1549
  for spec in specs:
1544
1550
  self.populate_tf_resources(spec, ocm_map=ocm_map)
1545
1551
 
1546
- def _get_provisioner_provider_exclusions(
1552
+ def _is_provisioner_excluded(
1547
1553
  self,
1548
1554
  spec: ExternalResourceSpec,
1549
- provider_exclusions_by_provisioner_name: Mapping[str, Iterable[str]],
1550
- ) -> list[str]:
1551
- return list(
1552
- provider_exclusions_by_provisioner_name.get(spec.provisioner["name"], [])
1553
- )
1555
+ provider_exclusions: Mapping[str, Exclusion],
1556
+ ) -> bool:
1557
+ e = provider_exclusions.get(spec.provider)
1558
+ if not e:
1559
+ return False
1560
+ return e.all or spec.provisioner_name in e.provisioners
1554
1561
 
1555
1562
  def _filter_specs_managed_by_erv2(
1556
1563
  self,
1557
1564
  specs: Iterable[ExternalResourceSpec],
1558
- provider_exclusions_by_provisioner_name: Mapping[str, Iterable[str]],
1565
+ provider_exclusions: Mapping[str, Exclusion],
1559
1566
  ) -> list[ExternalResourceSpec]:
1560
- filtered_specs: list[ExternalResourceSpec] = []
1561
- for spec in specs:
1562
- if spec.resource.get("managed_by_erv2"):
1563
- continue
1567
+ filtered_specs = [
1568
+ spec for spec in specs if not spec.resource.get("managed_by_erv2")
1569
+ ]
1564
1570
 
1565
- if spec.provider in self._get_provisioner_provider_exclusions(
1566
- spec, provider_exclusions_by_provisioner_name
1567
- ):
1571
+ for spec in filtered_specs:
1572
+ if self._is_provisioner_excluded(spec, provider_exclusions):
1568
1573
  raise ProviderExcludedError(spec)
1569
1574
 
1570
- filtered_specs.append(spec)
1571
1575
  return filtered_specs
1572
1576
 
1577
+ def _get_provider_exclusions_query_dict(
1578
+ self, provider_exclusions: Iterable[Mapping[str, Any]]
1579
+ ) -> dict[str, Exclusion]:
1580
+ return {
1581
+ item["provider"]: Exclusion(
1582
+ all=item.get("excludeAllProvisioners") or False,
1583
+ provisioners={
1584
+ p["name"] for p in (item.get("excludeProvisioners") or [])
1585
+ },
1586
+ )
1587
+ for item in provider_exclusions
1588
+ }
1589
+
1573
1590
  def init_populate_specs(
1574
1591
  self,
1575
1592
  namespaces: Iterable[Mapping[str, Any]],
1576
1593
  account_names: Iterable[str] | None,
1577
- provider_exclusions_by_provisioner: Iterable[Mapping[str, Any]] | None = None,
1594
+ provider_exclusions: Iterable[Mapping[str, Any]] | None = None,
1578
1595
  ) -> None:
1579
1596
  """
1580
1597
  Initiates resource specs from the definitions in app-interface
@@ -1586,15 +1603,14 @@ class TerrascriptClient: # pylint: disable=too-many-public-methods
1586
1603
  self.resource_spec_inventory: ExternalResourceSpecInventory = {}
1587
1604
 
1588
1605
  # Ensure provider exclusions are fetched
1589
- if provider_exclusions_by_provisioner is None:
1590
- provider_exclusions_by_provisioner = (
1606
+ if provider_exclusions is None:
1607
+ provider_exclusions = (
1591
1608
  queries.get_tf_resources_provider_exclusions_by_provisioner() or []
1592
1609
  )
1593
1610
 
1594
- provider_exclusions_by_provisioner_name = {
1595
- p["provisioner"]["name"]: p["excludedProviders"]
1596
- for p in provider_exclusions_by_provisioner or []
1597
- }
1611
+ provider_exclusions_query_dict = self._get_provider_exclusions_query_dict(
1612
+ provider_exclusions
1613
+ )
1598
1614
 
1599
1615
  for namespace_info in namespaces:
1600
1616
  all_specs = get_external_resource_specs(
@@ -1602,7 +1618,7 @@ class TerrascriptClient: # pylint: disable=too-many-public-methods
1602
1618
  provision_provider=PROVIDER_AWS,
1603
1619
  )
1604
1620
  specs = self._filter_specs_managed_by_erv2(
1605
- all_specs, provider_exclusions_by_provisioner_name
1621
+ all_specs, provider_exclusions_query_dict
1606
1622
  )
1607
1623
  name_counter = Counter(spec.output_resource_name for spec in specs)
1608
1624
  duplicates = [name for name, count in name_counter.items() if count > 1]