qontract-reconcile 0.9.1rc213__py3-none-any.whl → 0.9.1rc215__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.9.1rc213
3
+ Version: 0.9.1rc215
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
@@ -16,7 +16,7 @@ reconcile/aws_iam_password_reset.py,sha256=GDoBo4VFSABgRFjEfwRXlXlNGb7beVt5pSrNr
16
16
  reconcile/aws_support_cases_sos.py,sha256=i6bSWnlH9fh14P14PjVhFLwNl-q3fD733_rXKM_O51c,2992
17
17
  reconcile/blackbox_exporter_endpoint_monitoring.py,sha256=W_VJagnsJR1v5oqjlI3RJJE0_nhtJ0m81RS8zWA5u5c,3538
18
18
  reconcile/checkpoint.py,sha256=figtZRuWUvdpdSnkhAqeGvO5dI02TT6J3heyeFhlwqM,5016
19
- reconcile/cli.py,sha256=aSm4aDGb80HXHrq7lYH1UMunoVrGo0F-a7-NgMWFmJg,71079
19
+ reconcile/cli.py,sha256=k4NQ-Uu4EY50JkXK6QPAeCRo6vLzu49Al27vuAh4wuI,72161
20
20
  reconcile/closedbox_endpoint_monitoring_base.py,sha256=0xg_d8dwd36Y8GY1mE-LLO1LQpPEMM77bzAfc_KdgzU,4870
21
21
  reconcile/cluster_deployment_mapper.py,sha256=2Ah-nu-Mdig0pjuiZl_XLrmVAjYzFjORR3dMlCgkmw0,2352
22
22
  reconcile/dashdotdb_base.py,sha256=Ca75-OQiu5HeA8Q6zQpEYuhyCSjeuWe99K4y9ipTORM,4032
@@ -132,12 +132,12 @@ reconcile/unleash_watcher.py,sha256=xNLUFpIr66XESEyXUkmHTTmHghVWHiMtnS_k0OC7gd8,
132
132
  reconcile/vault_replication.py,sha256=xobxnsOfUcwvdQ-RZ7JH_sZCDh8rpEY7MJ36nkvfFqE,17262
133
133
  reconcile/vpc_peerings_validator.py,sha256=10igLYTQpBMGXO9mTO7sJBzgr4jXQ2hf1OH5r5DKugE,3586
134
134
  reconcile/aus/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
135
- reconcile/aus/base.py,sha256=umMBJEUQNg-cwhHNprHkPX0mPBEyfSUvkpRDsHp_fYE,20466
136
- reconcile/aus/label_base_ocm_upgrade_scheduler_org.py,sha256=L8adHnFNtiiWJccJKEYkAZyVfsDy_oTKtqfAb30SfCE,602
135
+ reconcile/aus/advanced_upgrade_service.py,sha256=O2LDgMbFYKsNuflh8b80sIr59WpJwFHSCeZtacMA6Kg,810
136
+ reconcile/aus/base.py,sha256=xWqMCECr5_bYcMy2lQc5tO85i4nFJbUnKS5PvKtEoHM,21475
137
137
  reconcile/aus/models.py,sha256=uExdoZwGc1rdIhLSEBRxi6byvipo1rRGs1jfZyOykyg,486
138
- reconcile/aus/ocm_addons_upgrade_scheduler_org.py,sha256=dsyLW3gbfcvcO_C1o-35uw5qS_3EuDjOnV-PrMweiJA,5062
139
- reconcile/aus/ocm_upgrade_scheduler.py,sha256=JHDdO6lhhiH1Y-lKTjrnyKOIaujU-924-c-10nvlQGA,3161
140
- reconcile/aus/ocm_upgrade_scheduler_org.py,sha256=7Q32TF01086wSk0vzz2wSDni_X_KnrjPO-mcCQecSf0,1405
138
+ reconcile/aus/ocm_addons_upgrade_scheduler_org.py,sha256=d9ExfJmFnEKv0b_YIwghtGiywygxxUb_M2qMJGVvo3k,5086
139
+ reconcile/aus/ocm_upgrade_scheduler.py,sha256=O5mv8bnNZ6WjPYSKOSnylYGhyYfIwwAXFzB62wnhqRM,3219
140
+ reconcile/aus/ocm_upgrade_scheduler_org.py,sha256=g7hLYRojxTUQybCKJHq5YLPb7bAE4eXH6oUmZFvuaFg,1429
141
141
  reconcile/change_owners/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
142
142
  reconcile/change_owners/approver.py,sha256=L7XJWJ-rgn8BOmeMb6lBDV8lHFCUaNoHGDSD7OH03vA,2244
143
143
  reconcile/change_owners/bundle.py,sha256=2_GEcw40O4MOnPvynEJtfZIW01675uwRNeptP0unJ84,1793
@@ -180,6 +180,7 @@ reconcile/gql_definitions/common/clusters.py,sha256=qr8gnFH71pYUGijReg0NvaQ7LF8u
180
180
  reconcile/gql_definitions/common/clusters_minimal.py,sha256=iYnAPQ7jDtEu06XgkDlltXa2_iJsp56rIfDqEYef-uM,5257
181
181
  reconcile/gql_definitions/common/namespaces.py,sha256=HNu55aeBQUGr4JfjQFj8sGAE2SILh_TtTT6LmsHfwPo,9161
182
182
  reconcile/gql_definitions/common/namespaces_minimal.py,sha256=0n9f2ldd_aEN9tzcqKvtzZ53ceH-1v411vVkD8wPaeY,3539
183
+ reconcile/gql_definitions/common/ocm_environments.py,sha256=yV4UVjdnNmqbR5trQCOAv5-UhatbT32R8JGU99GcU3U,1995
183
184
  reconcile/gql_definitions/common/pagerduty_instances.py,sha256=8NBHKRXg_OKG9NsJv6FOj8UVFcjkdJg-9E16ZqZIRPQ,2006
184
185
  reconcile/gql_definitions/common/pgp_reencryption_settings.py,sha256=tS68-tBBd7BJYmfTjtdTlxpABF3f_z9eJdtaKnyZc0Q,2305
185
186
  reconcile/gql_definitions/common/pipeline_providers.py,sha256=6GgiGxV4Y25VTlfmhunFO_isU0gHKfid4L0YH9AGf80,9398
@@ -352,6 +353,7 @@ reconcile/test/test_utils_ldap_client.py,sha256=THq8ZrjxgEBpMS-AhU_x8hcx65DD9u_I
352
353
  reconcile/test/test_utils_mr.py,sha256=KYnF8SY8ybBVqXLQu8K40ZGcO8wSEkOW4PIS8RexLGQ,6622
353
354
  reconcile/test/test_utils_mr_clusters_updates.py,sha256=HZi70vg60huxjGKpasR07Ap0tYag_kJCFQQfxqi9LAU,2333
354
355
  reconcile/test/test_utils_oc.py,sha256=fhwghPVhJme1c6y293oo5GuJxYEQRQuyaZx1Aim5LxY,33674
356
+ reconcile/test/test_utils_oc_native.py,sha256=gM49PCaM4NPXPlnj1OcXU0PdTcFnbamPFq0IWqHVEyw,1451
355
357
  reconcile/test/test_utils_ocm.py,sha256=aHqEDIplmuUbLeQvu_ED35XGmvkoNJjlQa_M1p9dA20,14158
356
358
  reconcile/test/test_utils_ocm_versions.py,sha256=8evUvlMmUTm6vMyrdnC3C4flzJ4hkWSluD01lC8eHLk,1862
357
359
  reconcile/test/test_utils_pagerduty_api.py,sha256=nGudGdVgruBRh1btuJ28CFaJTFqchHfsp2JnXY-_5Oo,7827
@@ -427,11 +429,11 @@ reconcile/utils/ldap_client.py,sha256=atreTLA1f7gnG54Ub3JWkToP9tCwAci-q17dqB5XSh
427
429
  reconcile/utils/lean_terraform_client.py,sha256=PJio087h5zUoievA3SppDP_io6ypiS9refaN5Hpm1Dg,1055
428
430
  reconcile/utils/make.py,sha256=QaEwucrzbl8-VHS66Wfdjfo0ubmAcvt_hZGpiGsKU50,231
429
431
  reconcile/utils/metrics.py,sha256=7DO12hEJ7sNOjrTNmA24l5wmAe4MT0WgY-Y2iPhBgXI,2015
430
- reconcile/utils/oc.py,sha256=I-xwIjvV0exXFOK7bIx3SaRfpsK-c-gubnbWhacrdkA,64465
432
+ reconcile/utils/oc.py,sha256=Y0hu3i2pBi9qXGGnvyRl7JM3Op5AR6kfw1VA96QN5aM,68094
431
433
  reconcile/utils/oc_connection_parameters.py,sha256=jlEMObRAx0NuWQfEF7NXkLoY-Ewrr9egA7nUr_oUHyM,10011
432
434
  reconcile/utils/oc_filters.py,sha256=RWn8pC5A7ZZT7C6WPq9bOw5KBNkiAb5puFSr_FpdAf8,1358
433
435
  reconcile/utils/oc_map.py,sha256=2y8A2B7iSNG4dTxaF9OkKlbw0DYcUwjeF3uv-P0lTBM,9538
434
- reconcile/utils/ocm_base_client.py,sha256=79FkWLrSgqgFOYofiCo8H4ACThwUio8UFyLzlmC0FK4,4924
436
+ reconcile/utils/ocm_base_client.py,sha256=858MkZXwqjinwRHhz7BPB42djbL_M-m287cesjCGycU,4964
435
437
  reconcile/utils/openshift_resource.py,sha256=ezJPf8udfc5e8LFDrCNqzV5y37i6hib0xAW1uZMMq_0,23599
436
438
  reconcile/utils/openssl.py,sha256=QVvhzhpChq_4Daf_5wE1qeZJr4thg3DDjJPn4bOPD4E,365
437
439
  reconcile/utils/output.py,sha256=htcMXMe0y2dNnwu8MW8x0JZ5YBaEJRy5w4tR8yLF0OU,1738
@@ -504,7 +506,7 @@ release/test_version.py,sha256=jOMn3Qx-mZC5pnJR0LU9ieIdNaYZSmr1kQ6aCkPngAU,2053
504
506
  release/version.py,sha256=Ud36t9FxGHLubMrE2o5aaaZRGB9_9hU_z0RN9go0TQM,3876
505
507
  tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
506
508
  tools/app_interface_reporter.py,sha256=DIWu0dpBDk_cBweaf2Qun5HnyrLER1bu-pv3kHM5YGY,21861
507
- tools/qontract_cli.py,sha256=KH7_tx7Uf-x25DxIHM92onaePnfMi2z44V1_0EBZZ1A,83840
509
+ tools/qontract_cli.py,sha256=CUbL1ugBlfjs-r5zU2TGeuaYcUYkiuRdo480b_tBMBU,85495
508
510
  tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
509
511
  tools/cli_commands/gpg_encrypt.py,sha256=JryinrDdvztN931enUY3FuDeLVnfs6y58mnK7itNK6Y,4940
510
512
  tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
@@ -512,8 +514,8 @@ tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y
512
514
  tools/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
513
515
  tools/test/test_qontract_cli.py,sha256=awwTHEc2DWlykuqGIYM0WOBoSL0KRnOraCLk3C7izis,1401
514
516
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
515
- qontract_reconcile-0.9.1rc213.dist-info/METADATA,sha256=eHJZam1c3MqW9K_ZVNUqAw04bWlWvXg4Ch6EvFfNot4,2248
516
- qontract_reconcile-0.9.1rc213.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
517
- qontract_reconcile-0.9.1rc213.dist-info/entry_points.txt,sha256=3BPvsRryM1C4S_mb5kXmP5AVv-wJBzVCrOJyv6qUmc0,195
518
- qontract_reconcile-0.9.1rc213.dist-info/top_level.txt,sha256=j0CHPIc8TsVRB50wOz_jhxjjaRyCJB3NOQeXhuHS67c,34
519
- qontract_reconcile-0.9.1rc213.dist-info/RECORD,,
517
+ qontract_reconcile-0.9.1rc215.dist-info/METADATA,sha256=dnTK7jCNUEBuvm2p53Ix4upIX4FuUEezQQ7gXrtCP2k,2248
518
+ qontract_reconcile-0.9.1rc215.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
519
+ qontract_reconcile-0.9.1rc215.dist-info/entry_points.txt,sha256=3BPvsRryM1C4S_mb5kXmP5AVv-wJBzVCrOJyv6qUmc0,195
520
+ qontract_reconcile-0.9.1rc215.dist-info/top_level.txt,sha256=j0CHPIc8TsVRB50wOz_jhxjjaRyCJB3NOQeXhuHS67c,34
521
+ qontract_reconcile-0.9.1rc215.dist-info/RECORD,,
@@ -4,13 +4,18 @@ from reconcile.aus.models import OrganizationUpgradeSpec
4
4
  from reconcile.aus.ocm_upgrade_scheduler_org import (
5
5
  OCMClusterUpgradeSchedulerOrgIntegration,
6
6
  )
7
+ from reconcile.gql_definitions.fragments.ocm_environment import OCMEnvironment
7
8
 
9
+ QONTRACT_INTEGRATION = "advanced-upgrade-scheduler"
8
10
 
9
- class LabelBasedOCMClusterUpgradeSchedulerOrgIntegration(
10
- OCMClusterUpgradeSchedulerOrgIntegration
11
- ):
12
- def get_organization_upgrade_spec(
13
- self, org_name: Optional[str] = None
11
+
12
+ class AdvancedUpgradeServiceIntegration(OCMClusterUpgradeSchedulerOrgIntegration):
13
+ @property
14
+ def name(self) -> str:
15
+ return QONTRACT_INTEGRATION
16
+
17
+ def get_ocm_env_upgrade_specs(
18
+ self, ocm_env: OCMEnvironment, org_name: Optional[str] = None
14
19
  ) -> dict[str, OrganizationUpgradeSpec]:
15
20
  return {
16
21
  # todo
reconcile/aus/base.py CHANGED
@@ -17,6 +17,11 @@ from croniter import croniter
17
17
  from semver import VersionInfo
18
18
 
19
19
  from reconcile.aus.models import OrganizationUpgradeSpec
20
+ from reconcile.gql_definitions.common.ocm_environments import (
21
+ query as ocm_environment_query,
22
+ )
23
+ from reconcile.gql_definitions.fragments.ocm_environment import OCMEnvironment
24
+ from reconcile.utils import gql
20
25
  from reconcile.utils.cluster_version_data import (
21
26
  VersionData,
22
27
  WorkloadHistory,
@@ -29,7 +34,7 @@ from reconcile.utils.ocm import (
29
34
  Sector,
30
35
  )
31
36
  from reconcile.utils.runtime.integration import (
32
- NoParams,
37
+ PydanticRunParams,
33
38
  QontractReconcileIntegration,
34
39
  )
35
40
  from reconcile.utils.semver_helper import (
@@ -39,20 +44,43 @@ from reconcile.utils.semver_helper import (
39
44
  from reconcile.utils.state import init_state
40
45
 
41
46
 
42
- class AdvancedUpgradeSchedulerBaseIntegration(QontractReconcileIntegration[NoParams]):
47
+ class AdvancedUpgradeSchedulerBaseIntegrationParams(PydanticRunParams):
48
+
49
+ ocm_environment: Optional[str] = None
50
+ ocm_organization: Optional[str] = None
51
+
52
+
53
+ class AdvancedUpgradeSchedulerBaseIntegration(
54
+ QontractReconcileIntegration[AdvancedUpgradeSchedulerBaseIntegrationParams]
55
+ ):
43
56
  def run(self, dry_run: bool) -> None:
44
- upgrade_specs_per_org = self.get_organization_upgrade_spec()
45
- if not upgrade_specs_per_org:
46
- logging.debug("No upgrade policy definitions found")
47
- sys.exit(0)
48
-
49
- for org_name, org_upgrade_spec in upgrade_specs_per_org.items():
50
- if org_upgrade_spec.specs:
51
- self.process_upgrade_policies_in_org(dry_run, org_upgrade_spec)
52
- else:
53
- logging.debug(
54
- f"Skip org {org_name} because it defines no upgrade policies"
55
- )
57
+ upgrade_specs = self.get_upgrade_specs()
58
+ for ocm_env, env_upgrade_specs in upgrade_specs.items():
59
+ for org_name, org_upgrade_spec in env_upgrade_specs.items():
60
+ if org_upgrade_spec.specs:
61
+ self.process_upgrade_policies_in_org(dry_run, org_upgrade_spec)
62
+ else:
63
+ logging.debug(
64
+ f"Skip org {org_name} in {ocm_env} because it defines no upgrade policies"
65
+ )
66
+ sys.exit(0)
67
+
68
+ def get_upgrade_specs(self) -> dict[str, dict[str, OrganizationUpgradeSpec]]:
69
+ return {
70
+ ocm_env.name: self.get_ocm_env_upgrade_specs(
71
+ ocm_env,
72
+ self.params.ocm_organization,
73
+ )
74
+ for ocm_env in self.get_ocm_environments()
75
+ }
76
+
77
+ def get_ocm_environments(self) -> list[OCMEnvironment]:
78
+ return ocm_environment_query(
79
+ gql.get_api().query,
80
+ variables={"name": self.params.ocm_environment}
81
+ if self.params.ocm_environment
82
+ else None,
83
+ ).environments
56
84
 
57
85
  @abstractmethod
58
86
  def process_upgrade_policies_in_org(
@@ -61,8 +89,8 @@ class AdvancedUpgradeSchedulerBaseIntegration(QontractReconcileIntegration[NoPar
61
89
  ...
62
90
 
63
91
  @abstractmethod
64
- def get_organization_upgrade_spec(
65
- self, org_name: Optional[str] = None
92
+ def get_ocm_env_upgrade_specs(
93
+ self, ocm_env: OCMEnvironment, org_name: Optional[str] = None
66
94
  ) -> dict[str, OrganizationUpgradeSpec]:
67
95
  ...
68
96
 
@@ -14,6 +14,7 @@ from reconcile.aus.models import (
14
14
  from reconcile.gql_definitions.advanced_upgrade_service.aus_organization import (
15
15
  query as aus_organizations_query,
16
16
  )
17
+ from reconcile.gql_definitions.fragments.ocm_environment import OCMEnvironment
17
18
  from reconcile.utils import gql
18
19
  from reconcile.utils.cluster_version_data import VersionData
19
20
  from reconcile.utils.ocm import OCMMap
@@ -58,10 +59,27 @@ class OCMAddonsUpgradeSchedulerOrgIntegration(
58
59
  )
59
60
  aus.act(dry_run, diffs, ocm_map, addon_id=addon_state.addon_id)
60
61
 
61
- def get_organization_upgrade_spec(
62
- self, org_name: Optional[str] = None
62
+ def get_ocm_env_upgrade_specs(
63
+ self, ocm_env: OCMEnvironment, org_name: Optional[str] = None
63
64
  ) -> dict[str, OrganizationUpgradeSpec]:
64
- return organization_upgrade_spec(org_name=org_name)
65
+ return {
66
+ org.name: OrganizationUpgradeSpec(
67
+ org=org,
68
+ specs=[
69
+ ClusterUpgradeSpec(
70
+ name=cluster.name,
71
+ ocm=org,
72
+ upgradePolicy=cluster.upgrade_policy,
73
+ )
74
+ for cluster in org.upgrade_policy_clusters or []
75
+ ],
76
+ )
77
+ for org in aus_organizations_query(
78
+ query_func=gql.get_api().query
79
+ ).organizations
80
+ or []
81
+ if org.addon_managed_upgrades and (org_name is None or org.name == org_name)
82
+ }
65
83
 
66
84
 
67
85
  def get_state_for_org_spec(
@@ -110,27 +128,6 @@ def get_state_for_org_spec_per_addon(
110
128
  return ocm_map, result
111
129
 
112
130
 
113
- def organization_upgrade_spec(
114
- org_name: Optional[str] = None,
115
- ) -> dict[str, OrganizationUpgradeSpec]:
116
- return {
117
- org.name: OrganizationUpgradeSpec(
118
- org=org,
119
- specs=[
120
- ClusterUpgradeSpec(
121
- name=cluster.name,
122
- ocm=org,
123
- upgradePolicy=cluster.upgrade_policy,
124
- )
125
- for cluster in org.upgrade_policy_clusters or []
126
- ],
127
- )
128
- for org in aus_organizations_query(query_func=gql.get_api().query).organizations
129
- or []
130
- if org.addon_managed_upgrades and (org_name is None or org.name == org_name)
131
- }
132
-
133
-
134
131
  def calculate_diff(
135
132
  addon_current_state: list[dict[str, Any]],
136
133
  addon_desired_state: list[dict[str, Any]],
@@ -10,6 +10,7 @@ from reconcile.aus.models import (
10
10
  from reconcile.gql_definitions.advanced_upgrade_service.aus_clusters import (
11
11
  query as aus_clusters_query,
12
12
  )
13
+ from reconcile.gql_definitions.fragments.ocm_environment import OCMEnvironment
13
14
  from reconcile.utils import gql
14
15
  from reconcile.utils.disabled_integrations import integration_is_enabled
15
16
  from reconcile.utils.ocm import (
@@ -59,38 +60,34 @@ class OCMClusterUpgradeSchedulerIntegration(
59
60
  )
60
61
  aus.act(dry_run, diffs, ocm_map)
61
62
 
62
- def get_organization_upgrade_spec(
63
- self, org_name: Optional[str] = None
63
+ def get_ocm_env_upgrade_specs(
64
+ self, ocm_env: OCMEnvironment, org_name: Optional[str] = None
64
65
  ) -> dict[str, OrganizationUpgradeSpec]:
65
- return organization_upgrade_spec(org_name=org_name)
66
-
67
-
68
- def organization_upgrade_spec(
69
- org_name: Optional[str] = None,
70
- ) -> dict[str, OrganizationUpgradeSpec]:
71
- specs_per_org: dict[str, list[ClusterUpgradeSpec]] = defaultdict(list)
72
- for cluster in aus_clusters_query(query_func=gql.get_api().query).clusters or []:
73
- supported_product = (
74
- cluster.spec and cluster.spec.product in SUPPORTED_OCM_PRODUCTS
75
- )
76
- in_org_shard = org_name is None or (
77
- cluster.ocm and cluster.ocm.name == org_name
78
- )
79
- if (
80
- integration_is_enabled(QONTRACT_INTEGRATION, cluster)
81
- and cluster.ocm
82
- and cluster.upgrade_policy
83
- and supported_product
84
- and in_org_shard
66
+ specs_per_org: dict[str, list[ClusterUpgradeSpec]] = defaultdict(list)
67
+ for cluster in (
68
+ aus_clusters_query(query_func=gql.get_api().query).clusters or []
85
69
  ):
86
- specs_per_org[cluster.ocm.name].append(
87
- ClusterUpgradeSpec(
88
- name=cluster.name,
89
- ocm=cluster.ocm,
90
- upgradePolicy=cluster.upgrade_policy,
91
- )
70
+ supported_product = (
71
+ cluster.spec and cluster.spec.product in SUPPORTED_OCM_PRODUCTS
72
+ )
73
+ in_org_shard = org_name is None or (
74
+ cluster.ocm and cluster.ocm.name == org_name
92
75
  )
93
- return {
94
- org_name: OrganizationUpgradeSpec(org=specs[0].ocm, specs=specs)
95
- for org_name, specs in specs_per_org.items()
96
- }
76
+ if (
77
+ integration_is_enabled(QONTRACT_INTEGRATION, cluster)
78
+ and cluster.ocm
79
+ and cluster.upgrade_policy
80
+ and supported_product
81
+ and in_org_shard
82
+ ):
83
+ specs_per_org[cluster.ocm.name].append(
84
+ ClusterUpgradeSpec(
85
+ name=cluster.name,
86
+ ocm=cluster.ocm,
87
+ upgradePolicy=cluster.upgrade_policy,
88
+ )
89
+ )
90
+ return {
91
+ org_name: OrganizationUpgradeSpec(org=specs[0].ocm, specs=specs)
92
+ for org_name, specs in specs_per_org.items()
93
+ }
@@ -8,6 +8,7 @@ from reconcile.aus.ocm_upgrade_scheduler import OCMClusterUpgradeSchedulerIntegr
8
8
  from reconcile.gql_definitions.advanced_upgrade_service.aus_organization import (
9
9
  query as aus_organizations_query,
10
10
  )
11
+ from reconcile.gql_definitions.fragments.ocm_environment import OCMEnvironment
11
12
  from reconcile.utils import gql
12
13
 
13
14
  QONTRACT_INTEGRATION = "ocm-upgrade-scheduler-org"
@@ -18,28 +19,24 @@ class OCMClusterUpgradeSchedulerOrgIntegration(OCMClusterUpgradeSchedulerIntegra
18
19
  def name(self) -> str:
19
20
  return QONTRACT_INTEGRATION
20
21
 
21
- def get_organization_upgrade_spec(
22
- self, org_name: Optional[str] = None
22
+ def get_ocm_env_upgrade_specs(
23
+ self, ocm_env: OCMEnvironment, org_name: Optional[str] = None
23
24
  ) -> dict[str, OrganizationUpgradeSpec]:
24
- return organization_upgrade_spec(org_name=org_name)
25
-
26
-
27
- def organization_upgrade_spec(
28
- org_name: Optional[str] = None,
29
- ) -> dict[str, OrganizationUpgradeSpec]:
30
- return {
31
- org.name: OrganizationUpgradeSpec(
32
- org=org,
33
- specs=[
34
- ClusterUpgradeSpec(
35
- name=cluster.name,
36
- ocm=org,
37
- upgradePolicy=cluster.upgrade_policy,
38
- )
39
- for cluster in org.upgrade_policy_clusters or []
40
- ],
41
- )
42
- for org in aus_organizations_query(query_func=gql.get_api().query).organizations
43
- or []
44
- if org_name is None or org.name == org_name
45
- }
25
+ return {
26
+ org.name: OrganizationUpgradeSpec(
27
+ org=org,
28
+ specs=[
29
+ ClusterUpgradeSpec(
30
+ name=cluster.name,
31
+ ocm=org,
32
+ upgradePolicy=cluster.upgrade_policy,
33
+ )
34
+ for cluster in org.upgrade_policy_clusters or []
35
+ ],
36
+ )
37
+ for org in aus_organizations_query(
38
+ query_func=gql.get_api().query
39
+ ).organizations
40
+ or []
41
+ if org_name is None or org.name == org_name
42
+ }
reconcile/cli.py CHANGED
@@ -29,7 +29,6 @@ from reconcile.utils.runtime.environment import init_env
29
29
  from reconcile.utils.runtime.integration import (
30
30
  ModuleArgsKwargsRunParams,
31
31
  ModuleBasedQontractReconcileIntegration,
32
- NoParams,
33
32
  QontractReconcileIntegration,
34
33
  )
35
34
  from reconcile.utils.runtime.meta import IntegrationMeta
@@ -1958,11 +1957,14 @@ def ocm_machine_pools(ctx, thread_pool_size):
1958
1957
  @environ(["APP_INTERFACE_STATE_BUCKET", "APP_INTERFACE_STATE_BUCKET_ACCOUNT"])
1959
1958
  @click.pass_context
1960
1959
  def ocm_upgrade_scheduler(ctx):
1961
- import reconcile.aus.ocm_upgrade_scheduler
1960
+ from reconcile.aus.base import AdvancedUpgradeSchedulerBaseIntegrationParams
1961
+ from reconcile.aus.ocm_upgrade_scheduler import (
1962
+ OCMClusterUpgradeSchedulerIntegration,
1963
+ )
1962
1964
 
1963
1965
  run_class_integration(
1964
- integration=reconcile.aus.ocm_upgrade_scheduler.OCMClusterUpgradeSchedulerIntegration(
1965
- NoParams()
1966
+ integration=OCMClusterUpgradeSchedulerIntegration(
1967
+ AdvancedUpgradeSchedulerBaseIntegrationParams()
1966
1968
  ),
1967
1969
  ctx=ctx.obj,
1968
1970
  )
@@ -1972,11 +1974,16 @@ def ocm_upgrade_scheduler(ctx):
1972
1974
  @environ(["APP_INTERFACE_STATE_BUCKET", "APP_INTERFACE_STATE_BUCKET_ACCOUNT"])
1973
1975
  @click.pass_context
1974
1976
  def ocm_upgrade_scheduler_org(ctx):
1975
- import reconcile.aus.ocm_upgrade_scheduler_org
1977
+ from reconcile.aus.base import AdvancedUpgradeSchedulerBaseIntegrationParams
1978
+ from reconcile.aus.ocm_upgrade_scheduler_org import (
1979
+ OCMClusterUpgradeSchedulerOrgIntegration,
1980
+ )
1976
1981
 
1977
1982
  run_class_integration(
1978
- integration=reconcile.aus.ocm_upgrade_scheduler_org.OCMClusterUpgradeSchedulerOrgIntegration(
1979
- NoParams()
1983
+ integration=OCMClusterUpgradeSchedulerOrgIntegration(
1984
+ AdvancedUpgradeSchedulerBaseIntegrationParams(
1985
+ # pass in env or org here for sharding
1986
+ )
1980
1987
  ),
1981
1988
  ctx=ctx.obj,
1982
1989
  )
@@ -2001,11 +2008,32 @@ def ocm_upgrade_scheduler_org_updater(ctx, gitlab_project_id):
2001
2008
  @click.pass_context
2002
2009
  def ocm_addons_upgrade_scheduler_org(ctx):
2003
2010
 
2004
- import reconcile.aus.ocm_addons_upgrade_scheduler_org
2011
+ from reconcile.aus.base import AdvancedUpgradeSchedulerBaseIntegrationParams
2012
+ from reconcile.aus.ocm_addons_upgrade_scheduler_org import (
2013
+ OCMAddonsUpgradeSchedulerOrgIntegration,
2014
+ )
2015
+
2016
+ run_class_integration(
2017
+ integration=OCMAddonsUpgradeSchedulerOrgIntegration(
2018
+ AdvancedUpgradeSchedulerBaseIntegrationParams()
2019
+ ),
2020
+ ctx=ctx.obj,
2021
+ )
2022
+
2023
+
2024
+ @integration.command(
2025
+ short_help="Manage Cluster Upgrade Policy schedules in OCM organizations based on OCM labels."
2026
+ )
2027
+ @environ(["APP_INTERFACE_STATE_BUCKET", "APP_INTERFACE_STATE_BUCKET_ACCOUNT"])
2028
+ @click.pass_context
2029
+ def aus_upgrade_scheduler_org(ctx):
2030
+
2031
+ from reconcile.aus.advanced_upgrade_service import AdvancedUpgradeServiceIntegration
2032
+ from reconcile.aus.base import AdvancedUpgradeSchedulerBaseIntegrationParams
2005
2033
 
2006
2034
  run_class_integration(
2007
- integration=reconcile.aus.ocm_addons_upgrade_scheduler_org.OCMAddonsUpgradeSchedulerOrgIntegration(
2008
- NoParams()
2035
+ integration=AdvancedUpgradeServiceIntegration(
2036
+ AdvancedUpgradeSchedulerBaseIntegrationParams()
2009
2037
  ),
2010
2038
  ctx=ctx.obj,
2011
2039
  )
@@ -0,0 +1,75 @@
1
+ """
2
+ Generated by qenerate plugin=pydantic_v1. DO NOT MODIFY MANUALLY!
3
+ """
4
+ from collections.abc import Callable # noqa: F401 # pylint: disable=W0611
5
+ from datetime import datetime # noqa: F401 # pylint: disable=W0611
6
+ from enum import Enum # noqa: F401 # pylint: disable=W0611
7
+ from typing import ( # noqa: F401 # pylint: disable=W0611
8
+ Any,
9
+ Optional,
10
+ Union,
11
+ )
12
+
13
+ from pydantic import ( # noqa: F401 # pylint: disable=W0611
14
+ BaseModel,
15
+ Extra,
16
+ Field,
17
+ Json,
18
+ )
19
+
20
+ from reconcile.gql_definitions.fragments.ocm_environment import OCMEnvironment
21
+
22
+
23
+ DEFINITION = """
24
+ fragment OCMEnvironment on OpenShiftClusterManagerEnvironment_v1 {
25
+ name
26
+ url
27
+ accessTokenClientId
28
+ accessTokenUrl
29
+ accessTokenClientSecret {
30
+ ... VaultSecret
31
+ }
32
+ }
33
+
34
+ fragment VaultSecret on VaultSecret_v1 {
35
+ path
36
+ field
37
+ version
38
+ format
39
+ }
40
+
41
+ query OCMEnvironments($name: String) {
42
+ environments: ocm_environments_v1(name: $name) {
43
+ ... OCMEnvironment
44
+ }
45
+ }
46
+ """
47
+
48
+
49
+ class ConfiguredBaseModel(BaseModel):
50
+ class Config:
51
+ smart_union = True
52
+ extra = Extra.forbid
53
+
54
+
55
+ class OCMEnvironmentsQueryData(ConfiguredBaseModel):
56
+ environments: list[OCMEnvironment] = Field(..., alias="environments")
57
+
58
+
59
+ def query(query_func: Callable, **kwargs: Any) -> OCMEnvironmentsQueryData:
60
+ """
61
+ This is a convenience function which queries and parses the data into
62
+ concrete types. It should be compatible with most GQL clients.
63
+ You do not have to use it to consume the generated data classes.
64
+ Alternatively, you can also mime and alternate the behavior
65
+ of this function in the caller.
66
+
67
+ Parameters:
68
+ query_func (Callable): Function which queries your GQL Server
69
+ kwargs: optional arguments that will be passed to the query function
70
+
71
+ Returns:
72
+ OCMEnvironmentsQueryData: queried data parsed into generated classes
73
+ """
74
+ raw_data: dict[Any, Any] = query_func(DEFINITION, **kwargs)
75
+ return OCMEnvironmentsQueryData(**raw_data)
@@ -0,0 +1,58 @@
1
+ import json
2
+ import os
3
+ from unittest import TestCase
4
+ from unittest.mock import patch
5
+
6
+ from reconcile.test.fixtures import Fixtures
7
+ from reconcile.utils.oc import (
8
+ OC,
9
+ ApiClient,
10
+ StatusCodeError,
11
+ )
12
+
13
+ fixture = Fixtures("oc_native").get_anymarkup("api.yml")
14
+
15
+
16
+ class RespMock:
17
+ def __init__(self, data):
18
+ self.data = json.dumps(data).encode()
19
+
20
+
21
+ def request(
22
+ method,
23
+ url,
24
+ query_params=None,
25
+ headers=None,
26
+ post_params=None,
27
+ body=None,
28
+ _preload_content=True,
29
+ _request_timeout=None,
30
+ ):
31
+ url_list = url.split("/", 1)
32
+ cluster = fixture.get(url_list[0])
33
+ data = cluster.get(url_list[1], None)
34
+ return RespMock(data)
35
+
36
+
37
+ class TestOCNative(TestCase):
38
+ @patch.dict(os.environ, {"USE_NATIVE_CLIENT": "True"}, clear=True)
39
+ @patch.object(ApiClient, "request")
40
+ def test_oc_native(self, mock_request):
41
+ mock_request.side_effect = request
42
+
43
+ oc = OC("cluster", "server", "token", init_projects=True, local=True)
44
+
45
+ expected = fixture["projects"]
46
+ self.assertEqual(oc.projects, expected)
47
+
48
+ kind = "Project.config.openshift.io"
49
+ _k, group_version = oc._parse_kind(kind)
50
+ self.assertEqual(group_version, "config.openshift.io/v1")
51
+
52
+ kind = "Test"
53
+ with self.assertRaises(StatusCodeError):
54
+ oc._parse_kind(kind)
55
+
56
+ kind = "Project.test.io"
57
+ with self.assertRaises(StatusCodeError):
58
+ oc._parse_kind(kind)
reconcile/utils/oc.py CHANGED
@@ -30,6 +30,7 @@ from kubernetes.client import (
30
30
  ApiClient,
31
31
  Configuration,
32
32
  )
33
+ from kubernetes.client.exceptions import ApiException
33
34
  from kubernetes.dynamic.client import DynamicClient
34
35
  from kubernetes.dynamic.discovery import (
35
36
  LazyDiscoverer,
@@ -1291,6 +1292,46 @@ class OCNative(OCDeprecated):
1291
1292
  )
1292
1293
  return self.object_clients[key]
1293
1294
 
1295
+ # this function returns a kind:Resource for each kind on the
1296
+ # cluster
1297
+ def get_api_resources(self):
1298
+ c_res = self.client.resources
1299
+ # this returns a prefix:apis map
1300
+ apis = c_res.parse_api_groups(request_resources=False, update=True)
1301
+ api_resources = {}
1302
+ for prefix, api_groups in apis.items():
1303
+ # each api prefix consists of api:versions map
1304
+ for group, versions in api_groups.items():
1305
+ if prefix == "apis" and len(group) == 0:
1306
+ # the apis group has an entry with an empty api, but
1307
+ # querying the apis group with a blank api produces an
1308
+ # error. We skip that condition with this hack
1309
+ continue
1310
+ # each version is a version:resource_group map, where resource_group
1311
+ # contains if this api version is preferred and optionally a list of
1312
+ # kinds that are part of that apigroup/version.
1313
+ for version, resource_group in versions.items():
1314
+ try:
1315
+ resource_list = c_res.get_resources_for_api_version(
1316
+ prefix, group, version, True
1317
+ )
1318
+ except ApiException:
1319
+ # there may be apigroups/versions that require elevated
1320
+ # permisions, so go to the next one
1321
+ continue
1322
+ # resources is a map containing kind:Resource and
1323
+ # {kind}List:ResourceList where a Resource contains the api
1324
+ # group_version (group/api_version) and a ResourceList
1325
+ # represents a list of API objects
1326
+ for kind, resources in resource_list.items():
1327
+ for r in resources:
1328
+ if isinstance(r, ResourceList):
1329
+ continue
1330
+ api_resources = self.add_api_resource(
1331
+ kind, api_resources, resource_group.preferred, r
1332
+ )
1333
+ return api_resources
1334
+
1294
1335
  @retry(max_attempts=5, exceptions=(ServerTimeoutError))
1295
1336
  def get_items(self, kind, **kwargs):
1296
1337
  k, group_version = self._parse_kind(kind)
@@ -1357,6 +1398,42 @@ class OCNative(OCDeprecated):
1357
1398
  except NotFoundError as e:
1358
1399
  raise StatusCodeError(f"[{self.server}]: {e}")
1359
1400
 
1401
+ @staticmethod
1402
+ def add_api_resource(kind, api_resources, preferred, resource):
1403
+ new_api_resources = copy.copy(api_resources)
1404
+
1405
+ if kind not in api_resources:
1406
+ # this is a new kind so add it
1407
+ new_api_resources[kind] = [resource]
1408
+ else:
1409
+ # this kind already exists, so check if this apigroup has
1410
+ # already been added as an option. If this apigroup/version is the
1411
+ # preferred one, then replace the apigroup/version so that the
1412
+ # preferred apigroup/version is used instead of a non-preferred one
1413
+ # group = resource.group_version.split("/", 1)[0]
1414
+ new_group = True
1415
+ for pos in range(len(api_resources[kind])):
1416
+ if resource.group == api_resources[kind][pos].group:
1417
+ new_group = False
1418
+ if preferred:
1419
+ new_api_resources[kind][pos] = resource
1420
+ break
1421
+
1422
+ if new_group:
1423
+ # this is a new apigroup
1424
+ new_api_resources[kind].append(resource)
1425
+ return new_api_resources
1426
+
1427
+ def is_kind_supported(self, kind: str) -> bool:
1428
+ if "." in kind:
1429
+ try:
1430
+ self._parse_kind(kind)
1431
+ return True
1432
+ except StatusCodeError:
1433
+ return False
1434
+ else:
1435
+ return kind in (self.api_resources or {})
1436
+
1360
1437
 
1361
1438
  OCClient = Union[OCNative, OCDeprecated]
1362
1439
 
@@ -155,7 +155,10 @@ class OCMAPIClientConfiguration(Protocol):
155
155
  url: str
156
156
  access_token_client_id: str
157
157
  access_token_url: str
158
- access_token_client_secret: HasSecret
158
+
159
+ @property
160
+ def access_token_client_secret(self) -> HasSecret:
161
+ ...
159
162
 
160
163
 
161
164
  def init_ocm_base_client(
tools/qontract_cli.py CHANGED
@@ -22,6 +22,10 @@ import reconcile.terraform_resources as tfr
22
22
  import reconcile.terraform_users as tfu
23
23
  import reconcile.terraform_vpc_peerings as tfvpc
24
24
  from reconcile import queries
25
+ from reconcile.aus.base import (
26
+ AdvancedUpgradeSchedulerBaseIntegration,
27
+ AdvancedUpgradeSchedulerBaseIntegrationParams,
28
+ )
25
29
  from reconcile.change_owners.bundle import NoOpFileDiffResolver
26
30
  from reconcile.change_owners.change_owners import (
27
31
  fetch_change_type_processors,
@@ -295,6 +299,9 @@ def get_upgrade_policies_data(
295
299
  show_only_soaking_upgrades=False,
296
300
  by_workload=False,
297
301
  ):
302
+ if not clusters:
303
+ return []
304
+
298
305
  settings = queries.get_app_interface_settings()
299
306
  ocm_map = OCMMap(
300
307
  clusters=clusters,
@@ -467,14 +474,38 @@ def cluster_upgrade_policies(
467
474
  show_only_soaking_upgrades=False,
468
475
  by_workload=False,
469
476
  ):
470
- import reconcile.aus.ocm_upgrade_scheduler as ous
477
+ from reconcile.aus.ocm_upgrade_scheduler import (
478
+ OCMClusterUpgradeSchedulerIntegration,
479
+ )
480
+
481
+ integration = OCMClusterUpgradeSchedulerIntegration(
482
+ AdvancedUpgradeSchedulerBaseIntegrationParams()
483
+ )
484
+ generate_cluster_upgrade_policies_report(
485
+ ctx,
486
+ integration=integration,
487
+ cluster=cluster,
488
+ workload=workload,
489
+ show_only_soaking_upgrades=show_only_soaking_upgrades,
490
+ by_workload=by_workload,
491
+ )
492
+
471
493
 
494
+ def generate_cluster_upgrade_policies_report(
495
+ ctx,
496
+ integration: AdvancedUpgradeSchedulerBaseIntegration,
497
+ cluster: Optional[str],
498
+ workload: Optional[str],
499
+ show_only_soaking_upgrades: bool,
500
+ by_workload: bool,
501
+ ) -> None:
472
502
  md_output = ctx.obj["options"]["output"] == "md"
473
503
 
474
- upgrade_policies_per_org = ous.organization_upgrade_spec()
504
+ upgrade_specs = integration.get_upgrade_specs()
475
505
  clusters = [
476
506
  s.dict(by_alias=True)
477
- for org_upgrade_spec in upgrade_policies_per_org.values()
507
+ for org_upgrade_specs in upgrade_specs.values()
508
+ for org_upgrade_spec in org_upgrade_specs.values()
478
509
  for s in org_upgrade_spec.specs
479
510
  ]
480
511
 
@@ -488,7 +519,7 @@ def cluster_upgrade_policies(
488
519
  results = get_upgrade_policies_data(
489
520
  clusters,
490
521
  md_output,
491
- ous.QONTRACT_INTEGRATION,
522
+ integration.name,
492
523
  workload,
493
524
  show_only_soaking_upgrades,
494
525
  by_workload,
@@ -560,23 +591,55 @@ def inherit_version_data_text(ocm_org: str, ocm_specs: list[dict]) -> str:
560
591
  def ocm_fleet_upgrade_policies(
561
592
  ctx,
562
593
  ):
563
- import reconcile.aus.ocm_upgrade_scheduler_org as ouso
594
+ from reconcile.aus.ocm_upgrade_scheduler_org import (
595
+ OCMClusterUpgradeSchedulerOrgIntegration,
596
+ )
597
+
598
+ generate_fleet_upgrade_policices_report(
599
+ ctx,
600
+ OCMClusterUpgradeSchedulerOrgIntegration(
601
+ AdvancedUpgradeSchedulerBaseIntegrationParams()
602
+ ),
603
+ )
604
+
605
+
606
+ @get.command()
607
+ @click.pass_context
608
+ def aus_fleet_upgrade_policies(
609
+ ctx,
610
+ ):
611
+ from reconcile.aus.advanced_upgrade_service import AdvancedUpgradeServiceIntegration
612
+
613
+ generate_fleet_upgrade_policices_report(
614
+ ctx,
615
+ AdvancedUpgradeServiceIntegration(
616
+ AdvancedUpgradeSchedulerBaseIntegrationParams()
617
+ ),
618
+ )
619
+
620
+
621
+ def generate_fleet_upgrade_policices_report(
622
+ ctx, aus_integration: AdvancedUpgradeSchedulerBaseIntegration
623
+ ):
564
624
 
565
625
  md_output = ctx.obj["options"]["output"] == "md"
566
626
 
567
- upgrade_policies_per_org = ouso.organization_upgrade_spec()
627
+ upgrade_specs = aus_integration.get_upgrade_specs()
568
628
  upgrade_policies = [
569
629
  s.dict(by_alias=True)
570
- for org_upgrade_spec in upgrade_policies_per_org.values()
630
+ for org_upgrade_specs in upgrade_specs.values()
631
+ for org_upgrade_spec in org_upgrade_specs.values()
571
632
  for s in org_upgrade_spec.specs
572
633
  ]
634
+
573
635
  ocm_org_specs = [
574
636
  org_upgrade_spec.org.dict(by_alias=True)
575
- for org_upgrade_spec in upgrade_policies_per_org.values()
637
+ for org_upgrade_specs in upgrade_specs.values()
638
+ for org_upgrade_spec in org_upgrade_specs.values()
576
639
  ]
577
640
 
578
641
  results = get_upgrade_policies_data(
579
- upgrade_policies, md_output, integration=ouso.QONTRACT_INTEGRATION
642
+ upgrade_policies, md_output, integration=aus_integration.name
580
643
  )
581
644
 
582
645
  if md_output: