qontract-reconcile 0.10.1rc693__py3-none-any.whl → 0.10.1rc694__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.1rc693
3
+ Version: 0.10.1rc694
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
@@ -9,7 +9,7 @@ reconcile/aws_iam_password_reset.py,sha256=NwErtrqgBiXr7eGCAHdtGGOx0S7-4JnSc29Ie
9
9
  reconcile/aws_support_cases_sos.py,sha256=Jk6_XjDeJSYxgRGqcEAOcynt9qJF2r5HPIPcSKmoBv8,2974
10
10
  reconcile/blackbox_exporter_endpoint_monitoring.py,sha256=W_VJagnsJR1v5oqjlI3RJJE0_nhtJ0m81RS8zWA5u5c,3538
11
11
  reconcile/checkpoint.py,sha256=R2WFXUXLTB4sWMi4GeA4eegsuf_1-Q4vH8M0Toh3Ij4,5036
12
- reconcile/cli.py,sha256=2cp3O1XtwAdmN5VN7I7J-sUX41gRS2Y2lWoj-l4aZ_8,93610
12
+ reconcile/cli.py,sha256=Xj_msd-518yR9r1hy39FI3Q9gBRZQsfM9HRA-Ep6CKY,93661
13
13
  reconcile/closedbox_endpoint_monitoring_base.py,sha256=SMhkcQqprWvThrIJa3U_3uh5w1h-alleW1QnCJFY4Qw,4909
14
14
  reconcile/cluster_deployment_mapper.py,sha256=2Ah-nu-Mdig0pjuiZl_XLrmVAjYzFjORR3dMlCgkmw0,2352
15
15
  reconcile/dashdotdb_base.py,sha256=a5aPLVxyqPSbjdB0Ty-uliOtxwvEbbEljHJKxdK3-Zk,4813
@@ -92,7 +92,7 @@ reconcile/quay_mirror.py,sha256=9NzbNoxl-NdD8CwImcXNG5xTdHmUJxBfeVk5XHH41J8,1488
92
92
  reconcile/quay_mirror_org.py,sha256=Oq-t3kSkgfeSAOUDjLCDRBeEvOIEBacfX38qrX_s0oc,10801
93
93
  reconcile/quay_permissions.py,sha256=9KOutS1w4RFQqkvMSy54VtsKNx56-phzP6yI_rEW-B8,4244
94
94
  reconcile/quay_repos.py,sha256=cuEYG0HUe0ut5yvLdEwOF5-CmccpXQHRb_wDazvDrvQ,6895
95
- reconcile/queries.py,sha256=wvlKhaLgID1YhHSLHW9h8C6BjyiB--y11J0XMLf4vdo,50547
95
+ reconcile/queries.py,sha256=hgQizeUT5_a1ZI9i8wH-ajr7e956u5ynIp90zKMeRCA,50560
96
96
  reconcile/query_validator.py,sha256=BAjGrU8_VhzTOv5k0-uz0hY9ziZyconv8VAhgre1Auc,1497
97
97
  reconcile/requests_sender.py,sha256=914iluuF4UVgG3VyxxtnHOu4yf6YKS2fIy6PViSsFTQ,3875
98
98
  reconcile/resource_scraper.py,sha256=vo1N9vLJCYWvXlTwFRIpEuWjx_39ZV9zxJlpoPq4g3U,2330
@@ -120,12 +120,12 @@ reconcile/vpc_peerings_validator.py,sha256=Kv22HJVlTW9l9GB2eXwjPWqdDbr_VuvQBNPtt
120
120
  reconcile/aus/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
121
121
  reconcile/aus/advanced_upgrade_service.py,sha256=MPqcWpq1QeYzBsB8rtTlo102h_cR_q83oOYtWAncuAk,22391
122
122
  reconcile/aus/aus_label_source.py,sha256=qoP8Fgxuu1tCuhG6ixCWve7Ll-KD6a79E2uLAmC0ifw,4184
123
- reconcile/aus/base.py,sha256=eNZT4aKEWFm03fOcDfD7Y37odeqw6hPcFprOXHsnz7M,47703
123
+ reconcile/aus/base.py,sha256=4VTHqc22IW1xyLCgxhUrSBDRsaF99RW3uzoQafTQDN4,49410
124
124
  reconcile/aus/cluster_version_data.py,sha256=j4UyEBi5mQuvPq5Lo7a_L_0blxvH790wJV07uAiikFU,7126
125
125
  reconcile/aus/healthchecks.py,sha256=S8_KPn_zFiOo_wf5XlVmz-I3tUDYAim3EGSqiSPMvLQ,2707
126
126
  reconcile/aus/metrics.py,sha256=nKT4m2zGT-QOMR0c-z-npVNKWsNMubzdffpU_f9n4II,3927
127
127
  reconcile/aus/models.py,sha256=2e0IPHDJNSM4opqIJUGGxjXLF3zFyrBCAFWRJKjgEEQ,7371
128
- reconcile/aus/ocm_addons_upgrade_scheduler_org.py,sha256=gDl8NDFSj7Dz0x1o9q8fjMEM7IJS3H1jlnMRJzDGw4o,10015
128
+ reconcile/aus/ocm_addons_upgrade_scheduler_org.py,sha256=t2_J7CuovTm6FGvwWI6HsyiftPbt3ZRWEKjsGmIRcEI,10207
129
129
  reconcile/aus/ocm_upgrade_scheduler.py,sha256=2uPn13y3QGCHLoKwCc1Z7q9wQsoQf_F1HATMYUbl53s,3695
130
130
  reconcile/aus/ocm_upgrade_scheduler_org.py,sha256=f7pE_XrX9695EMZpg-CFZiakZ9vZ9BoRpkE2I9UC0sI,3190
131
131
  reconcile/aus/upgrades.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -667,7 +667,7 @@ reconcile/utils/mr/ocm_update_recommended_version.py,sha256=p_aVP0TGrlKk9WBwgQnY
667
667
  reconcile/utils/mr/ocm_upgrade_scheduler_org_updates.py,sha256=RzEKRT_BhvB2ud9pyNFZ1cNZFmDGBUah9vjpP0y9f9w,2810
668
668
  reconcile/utils/mr/user_maintenance.py,sha256=cHPBn8zrReWLHalyk-EFdkFJe9zjVjRoZhT4t2zZfGE,3956
669
669
  reconcile/utils/ocm/__init__.py,sha256=xv7CJp7K9LCQfa4gL_W0MMCOD1P4qOy8t5aZj1xXNUE,808
670
- reconcile/utils/ocm/addons.py,sha256=8wVrt16i69KkXq1fQByVheSQRhrRELbuOHb7Tz9bKT0,1675
670
+ reconcile/utils/ocm/addons.py,sha256=cofEAtnoBGKbtShSe3oAz9YbEuql1z6w_2FoNiBcFwA,6887
671
671
  reconcile/utils/ocm/base.py,sha256=GclZtCrPkPJmGP9HHvqIlV-8VXSKuaQTQJkA2pklN60,13817
672
672
  reconcile/utils/ocm/cluster_groups.py,sha256=F8oqVqN_4QUnGL0K61zZhoYIzJeP57EcmZpwmoV0mr4,1751
673
673
  reconcile/utils/ocm/clusters.py,sha256=Nw9m-jgN3GHHCh6w9UOBbMV4rtS24_-Ep09jAWQ-_fE,7653
@@ -681,7 +681,7 @@ reconcile/utils/ocm/sre_capability_labels.py,sha256=-TUmTzvK5yu7qY8SUMGj6-684wa8
681
681
  reconcile/utils/ocm/status_board.py,sha256=fOuyzkykxU9Rr7zhW5VaY0Vkn8Ur-GxvElzb8No-DZg,2283
682
682
  reconcile/utils/ocm/subscriptions.py,sha256=4qSH8uF9JvFxiecVSV1Kh8-mPcGSTfTTK7In5qHz_2w,2568
683
683
  reconcile/utils/ocm/syncsets.py,sha256=zvji9qWvInIRTdoybMaM9-VUhq4L1VewWfWCQmp4urc,940
684
- reconcile/utils/ocm/upgrades.py,sha256=ZWDfg3VrmRGx7Re-JjecRjwmn7Rh-dsuLA3OljbCByg,6616
684
+ reconcile/utils/ocm/upgrades.py,sha256=zrr_J9X2WIgnnW4hz5NCqdLq22jmoIAX82xKhz-_LY8,4910
685
685
  reconcile/utils/rosa/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
686
686
  reconcile/utils/rosa/rosa_cli.py,sha256=b7V3EgO4b4hOnvZzrxdn-bxRbkINQysOg_ecuB8l5GA,11364
687
687
  reconcile/utils/rosa/session.py,sha256=jYJ0QsJv0mgV15MQIrfTuk2arscmgO4zkBhKcwyO5qo,6915
@@ -712,7 +712,7 @@ tools/app_interface_metrics_exporter.py,sha256=zkwkxdAUAxjdc-pzx2_oJXG25fo0Fnyd5
712
712
  tools/app_interface_reporter.py,sha256=upA-J-n-HXHKVDINRuMR7vTt-iJvQORKUVi9D3leQto,17738
713
713
  tools/glitchtip_access_reporter.py,sha256=oPBnk_YoDuljU3v0FaChzOwwnk4vap1xEE67QEjzdqs,2948
714
714
  tools/glitchtip_access_revalidation.py,sha256=8kbBJk04mkq28kWoRDDkfCGIF3GRg3pJrFAh1sW0dbk,2821
715
- tools/qontract_cli.py,sha256=0dbDYuRsD62Ih4CDAgQdOfluiH4nDc2ZuLwhfXJ7Mmk,111316
715
+ tools/qontract_cli.py,sha256=fxXsgjmLBzC1zDkmNnB5jfOnwL-kGci5xC_4BsoIe3M,111648
716
716
  tools/sd_app_sre_alert_report.py,sha256=e9vAdyenUz2f5c8-z-5WY0wv-SJ9aePKDH2r4IwB6pc,5063
717
717
  tools/template_validation.py,sha256=-U-lTGeLaci8yWPEblCJeev2DOlY1jM9QOOh-O1zts8,3376
718
718
  tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -724,8 +724,8 @@ tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvf
724
724
  tools/test/test_qontract_cli.py,sha256=OvalpVRfY4pNmpMaWHHYqBjV68b1eGQjX8SCyTAXb1w,3501
725
725
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
726
726
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
727
- qontract_reconcile-0.10.1rc693.dist-info/METADATA,sha256=_lYcekIts_mySeJ5ZIUu-l7jZVTAV-Ht205CksOVQ60,2382
728
- qontract_reconcile-0.10.1rc693.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
729
- qontract_reconcile-0.10.1rc693.dist-info/entry_points.txt,sha256=rIxI5zWtHNlfpDeq1a7pZXAPoqf7HG32KMTN3MeWK_8,429
730
- qontract_reconcile-0.10.1rc693.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
731
- qontract_reconcile-0.10.1rc693.dist-info/RECORD,,
727
+ qontract_reconcile-0.10.1rc694.dist-info/METADATA,sha256=cJGxg2IBbK-ByqiYdq5k4iFEqTyNRhmB3W8V5a1a_II,2382
728
+ qontract_reconcile-0.10.1rc694.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
729
+ qontract_reconcile-0.10.1rc694.dist-info/entry_points.txt,sha256=rIxI5zWtHNlfpDeq1a7pZXAPoqf7HG32KMTN3MeWK_8,429
730
+ qontract_reconcile-0.10.1rc694.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
731
+ qontract_reconcile-0.10.1rc694.dist-info/RECORD,,
reconcile/aus/base.py CHANGED
@@ -8,6 +8,7 @@ from abc import (
8
8
  from datetime import (
9
9
  datetime,
10
10
  timedelta,
11
+ timezone,
11
12
  )
12
13
  from typing import (
13
14
  Callable,
@@ -74,6 +75,7 @@ from reconcile.utils.clusterhealth.telemeter import (
74
75
  from reconcile.utils.defer import defer
75
76
  from reconcile.utils.disabled_integrations import integration_is_enabled
76
77
  from reconcile.utils.filtering import remove_none_values_from_dict
78
+ from reconcile.utils.ocm.addons import AddonService, AddonServiceV1, AddonServiceV2
77
79
  from reconcile.utils.ocm.clusters import (
78
80
  OCMCluster,
79
81
  get_node_pools,
@@ -81,14 +83,11 @@ from reconcile.utils.ocm.clusters import (
81
83
  )
82
84
  from reconcile.utils.ocm.upgrades import (
83
85
  OCMVersionGate,
84
- create_addon_upgrade_policy,
85
86
  create_control_plane_upgrade_policy,
86
87
  create_node_pool_upgrade_policy,
87
88
  create_upgrade_policy,
88
- delete_addon_upgrade_policy,
89
89
  delete_control_plane_upgrade_policy,
90
90
  delete_upgrade_policy,
91
- get_addon_upgrade_policies,
92
91
  get_control_plane_upgrade_policies,
93
92
  get_node_pool_upgrade_policies,
94
93
  get_upgrade_policies,
@@ -365,6 +364,34 @@ class AdvancedUpgradeSchedulerBaseIntegration(
365
364
  return None
366
365
 
367
366
 
367
+ def init_addon_service(ocm_env: OCMEnvironment) -> AddonService:
368
+ """
369
+ Initialize the right version of addon-service for an OCM environment.
370
+ Since this is just temporary until all OCM environments are on v2, we
371
+ use a label on the OCM environmentschema to determine which version to use.
372
+ """
373
+ addon_service_version = (ocm_env.labels or {}).get(
374
+ "feature_flag_addon_service_version"
375
+ ) or "v2"
376
+ return init_addon_service_version(addon_service_version)
377
+
378
+
379
+ def init_addon_service_version(addon_service_version: str) -> AddonService:
380
+ """
381
+ Initialize the right version of addon-service based on the version string.
382
+ Supported versions are:
383
+ - v1: part of CS
384
+ - v2: standalone service using upgrade-plans instead of upgrade-policies
385
+ """
386
+ match addon_service_version:
387
+ case "v1":
388
+ return AddonServiceV1()
389
+ case "v2":
390
+ return AddonServiceV2()
391
+ case _:
392
+ raise ValueError(f"Unknown addon service version: {addon_service_version}")
393
+
394
+
368
395
  class RemainingSoakDayMetricsBuilder(Protocol):
369
396
  def __call__(
370
397
  self, cluster_uuid: str, soaking_version: str
@@ -397,27 +424,39 @@ class AbstractUpgradePolicy(ABC, BaseModel):
397
424
  pass
398
425
 
399
426
 
427
+ def addon_upgrade_policy_soonest_next_run() -> str:
428
+ now = datetime.now(tz=timezone.utc)
429
+ next_run = now + timedelta(minutes=MIN_DELTA_MINUTES)
430
+ return next_run.strftime("%Y-%m-%dT%H:%M:%SZ")
431
+
432
+
400
433
  class AddonUpgradePolicy(AbstractUpgradePolicy):
401
434
  """Class to create and delete Addon upgrade policies in OCM"""
402
435
 
403
436
  addon_id: str
437
+ addon_service: AddonService
438
+
439
+ class Config:
440
+ arbitrary_types_allowed = True
404
441
 
405
442
  def create(self, ocm_api: OCMBaseClient) -> None:
406
- item = {
407
- "version": self.version,
408
- "schedule_type": "manual",
409
- "addon_id": self.addon_id,
410
- "cluster_id": self.cluster.id,
411
- "upgrade_type": "ADDON",
412
- }
413
- create_addon_upgrade_policy(ocm_api, self.cluster.id, item)
443
+ self.addon_service.create_addon_upgrade_policy(
444
+ ocm_api=ocm_api,
445
+ cluster_id=self.cluster.id,
446
+ addon_id=self.addon_id,
447
+ schedule_type="manual",
448
+ version=self.version,
449
+ next_run=self.next_run or addon_upgrade_policy_soonest_next_run(),
450
+ )
414
451
 
415
452
  def delete(self, ocm_api: OCMBaseClient) -> None:
416
453
  if not self.id:
417
454
  raise ValueError(
418
455
  "Cannot delete addon upgrade policy without id (not created yet)"
419
456
  )
420
- delete_addon_upgrade_policy(ocm_api, self.cluster.id, self.id)
457
+ self.addon_service.delete_addon_upgrade_policy(
458
+ ocm_api=ocm_api, cluster_id=self.cluster.id, policy_id=self.id
459
+ )
421
460
 
422
461
  def summarize(self) -> str:
423
462
  details = {
@@ -544,15 +583,18 @@ def fetch_current_state(
544
583
  addons: bool = False,
545
584
  ) -> list[AbstractUpgradePolicy]:
546
585
  current_state: list[AbstractUpgradePolicy] = []
586
+ addon_service = init_addon_service(org_upgrade_spec.org.environment)
547
587
  for spec in org_upgrade_spec.specs:
548
588
  if addons and isinstance(spec, ClusterAddonUpgradeSpec):
549
589
  addon_spec = cast(ClusterAddonUpgradeSpec, spec)
550
- upgrade_policies = get_addon_upgrade_policies(
590
+ upgrade_policies = addon_service.get_addon_upgrade_policies(
551
591
  ocm_api, spec.cluster.id, addon_id=addon_spec.addon.addon.id
552
592
  )
553
593
  for upgrade_policy in upgrade_policies:
554
594
  upgrade_policy["cluster"] = spec.cluster
555
- current_state.append(AddonUpgradePolicy(**upgrade_policy))
595
+ current_state.append(
596
+ AddonUpgradePolicy(**upgrade_policy, addon_service=addon_service)
597
+ )
556
598
  elif spec.cluster.is_rosa_hypershift():
557
599
  upgrade_policies = get_control_plane_upgrade_policies(
558
600
  ocm_api, spec.cluster.id
@@ -1015,6 +1057,7 @@ def calculate_diff(
1015
1057
  for mutex in spec.effective_mutexes:
1016
1058
  locked[mutex] = spec.cluster.id
1017
1059
 
1060
+ addon_service = init_addon_service(desired_state.org.environment)
1018
1061
  now = datetime.utcnow()
1019
1062
  gates = get_version_gates(ocm_api)
1020
1063
  for spec in desired_state.specs:
@@ -1063,6 +1106,7 @@ def calculate_diff(
1063
1106
  schedule_type="manual",
1064
1107
  addon_id=addon_id,
1065
1108
  upgrade_type="ADDON",
1109
+ addon_service=addon_service,
1066
1110
  ),
1067
1111
  )
1068
1112
  )
@@ -6,6 +6,7 @@ from reconcile.aus import base as aus
6
6
  from reconcile.aus.base import (
7
7
  AbstractUpgradePolicy,
8
8
  AddonUpgradePolicy,
9
+ init_addon_service,
9
10
  )
10
11
  from reconcile.aus.cluster_version_data import VersionData
11
12
  from reconcile.aus.healthchecks import (
@@ -26,8 +27,6 @@ from reconcile.gql_definitions.fragments.ocm_environment import OCMEnvironment
26
27
  from reconcile.utils import metrics
27
28
  from reconcile.utils.ocm.addons import (
28
29
  OCMAddonInstallation,
29
- get_addon_latest_versions,
30
- get_addons_for_cluster,
31
30
  )
32
31
  from reconcile.utils.ocm.clusters import (
33
32
  OCMCluster,
@@ -137,15 +136,17 @@ class OCMAddonsUpgradeSchedulerOrgIntegration(
137
136
  if not organizations:
138
137
  return {}
139
138
 
139
+ addon_service = init_addon_service(ocm_env)
140
+
140
141
  # lookup cluster in OCM to figure out if they exist
141
142
  # and to get their UUID
142
143
  with init_ocm_base_client(ocm_env, self.secret_reader) as ocm_api:
143
144
  clusters = discover_clusters_for_organizations(
144
145
  ocm_api, [org.org_id for org in organizations]
145
146
  )
146
- addon_latest_versions = get_addon_latest_versions(ocm_api)
147
+ addon_latest_versions = addon_service.get_addon_latest_versions(ocm_api)
147
148
  addons_per_cluster: dict[str, list[OCMAddonInstallation]] = {
148
- cluster.ocm_cluster.name: get_addons_for_cluster(
149
+ cluster.ocm_cluster.name: addon_service.get_addons_for_cluster(
149
150
  ocm_api=ocm_api,
150
151
  cluster_id=cluster.ocm_cluster.id,
151
152
  addon_latest_versions=addon_latest_versions,
@@ -260,6 +261,9 @@ def calculate_diff(
260
261
  id=current.id,
261
262
  addon_id=current.addon_id,
262
263
  schedule_type=current.schedule_type,
264
+ addon_service=init_addon_service(
265
+ org_upgrade_spec.org.environment
266
+ ),
263
267
  ),
264
268
  )
265
269
  )
reconcile/cli.py CHANGED
@@ -2469,6 +2469,7 @@ def ocm_addons_upgrade_scheduler_org(
2469
2469
  run_class_integration(
2470
2470
  integration=OCMAddonsUpgradeSchedulerOrgIntegration(
2471
2471
  AdvancedUpgradeSchedulerBaseIntegrationParams(
2472
+ ocm_environment="ocm-integration",
2472
2473
  ocm_organization_ids=set(org_id),
2473
2474
  excluded_ocm_organization_ids=set(exclude_org_id),
2474
2475
  )
reconcile/queries.py CHANGED
@@ -1280,6 +1280,7 @@ OCM_QUERY = """
1280
1280
  environment {
1281
1281
  name
1282
1282
  url
1283
+ labels
1283
1284
  accessTokenClientId
1284
1285
  accessTokenUrl
1285
1286
  accessTokenClientSecret {
@@ -6,46 +6,217 @@ from typing import (
6
6
  from reconcile.utils.ocm.base import OCMAddonInstallation
7
7
  from reconcile.utils.ocm_base_client import OCMBaseClient
8
8
 
9
+ ADDON_UPGRADE_POLICY_DESIRED_KEYS = {
10
+ "id",
11
+ "addon_id",
12
+ "schedule_type",
13
+ "schedule",
14
+ "next_run",
15
+ "version",
16
+ }
9
17
 
10
- def get_addons_for_cluster(
11
- ocm_api: OCMBaseClient,
12
- cluster_id: str,
13
- addon_latest_versions: dict[str, str],
14
- required_state: Optional[str],
15
- ) -> list[OCMAddonInstallation]:
16
- """
17
- Returns a list of Addons installed on a cluster
18
18
 
19
- :param cluster_id: ID of the cluster
20
- :param addon_latest_versions: dict of addon_id -> latest version. This allows us to
21
- populate the addonsinstalation available upgrades in an efficient way.
22
- :param required_state: only return addons with this state
19
+ class AddonService:
20
+ def get_addon_latest_versions(self, ocm_api: OCMBaseClient) -> dict[str, str]:
21
+ """
22
+ Returns the latest version for each addon.
23
+ """
24
+ latest_versions: dict[str, str] = {}
25
+ for addon in ocm_api.get_paginated(f"{self.addon_base_api_path()}/addons"):
26
+ addon_id = addon["id"]
27
+ latest_versions[addon_id] = addon["version"]["id"]
28
+ return latest_versions
29
+
30
+ def get_addons_for_cluster(
31
+ self,
32
+ ocm_api: OCMBaseClient,
33
+ cluster_id: str,
34
+ addon_latest_versions: dict[str, str],
35
+ required_state: Optional[str],
36
+ ) -> list[OCMAddonInstallation]:
37
+ """
38
+ Returns a list of Addons installed on a cluster
39
+
40
+ :param cluster_id: ID of the cluster
41
+ :param addon_latest_versions: dict of addon_id -> latest version. This allows us to
42
+ populate the addonsinstalation available upgrades in an efficient way.
43
+ :param required_state: only return addons with this state
44
+ """
45
+
46
+ params: Optional[dict[str, Any]] = None
47
+ if required_state:
48
+ params = {"search": f"state='{required_state}'"}
49
+
50
+ addons = []
51
+ for addon in ocm_api.get_paginated(
52
+ api_path=f"{self.addon_base_api_path()}/clusters/{cluster_id}/addons",
53
+ params=params,
54
+ ):
55
+ current_version = addon["addon_version"]["id"]
56
+ latest_version = addon_latest_versions.get(addon["id"])
57
+ addon["addon_version"]["available_upgrades"] = (
58
+ [latest_version] if latest_version != current_version else []
59
+ )
60
+ addons.append(OCMAddonInstallation(**addon))
61
+ return addons
62
+
63
+ def addon_base_api_path(self) -> str:
64
+ raise NotImplementedError()
65
+
66
+ def get_addon_upgrade_policies(
67
+ self, ocm_api: OCMBaseClient, cluster_id: str, addon_id: Optional[str] = None
68
+ ) -> list[dict[str, Any]]:
69
+ raise NotImplementedError()
70
+
71
+ def create_addon_upgrade_policy(
72
+ self,
73
+ ocm_api: OCMBaseClient,
74
+ cluster_id: str,
75
+ addon_id: str,
76
+ schedule_type: str,
77
+ version: str,
78
+ next_run: str,
79
+ ) -> None:
80
+ raise NotImplementedError()
81
+
82
+ def delete_addon_upgrade_policy(
83
+ self, ocm_api: OCMBaseClient, cluster_id: str, policy_id: str
84
+ ) -> None:
85
+ raise NotImplementedError()
86
+
87
+
88
+ class AddonServiceV1(AddonService):
23
89
  """
90
+ The original addon-service API that is part of CS.
91
+ """
92
+
93
+ def addon_base_api_path(self) -> str:
94
+ return "/api/clusters_mgmt/v1"
95
+
96
+ def get_addon_upgrade_policies(
97
+ self, ocm_api: OCMBaseClient, cluster_id: str, addon_id: Optional[str] = None
98
+ ) -> list[dict[str, Any]]:
99
+ results: list[dict[str, Any]] = []
24
100
 
25
- params: Optional[dict[str, Any]] = None
26
- if required_state:
27
- params = {"search": f"state='{required_state}'"}
28
-
29
- addons = []
30
- for addon in ocm_api.get_paginated(
31
- api_path=f"/api/clusters_mgmt/v1/clusters/{cluster_id}/addons",
32
- params=params,
33
- ):
34
- current_version = addon["addon_version"]["id"]
35
- latest_version = addon_latest_versions.get(addon["id"])
36
- addon["addon_version"]["available_upgrades"] = (
37
- [latest_version] if latest_version != current_version else []
101
+ for policy in ocm_api.get_paginated(
102
+ f"{self.addon_base_api_path()}/clusters/{cluster_id}/addon_upgrade_policies"
103
+ ):
104
+ if addon_id and policy["addon_id"] != addon_id:
105
+ continue
106
+ policy_data = {
107
+ k: v
108
+ for k, v in policy.items()
109
+ if k in ADDON_UPGRADE_POLICY_DESIRED_KEYS
110
+ }
111
+ policy_data["state"] = self._get_addon_upgrade_policy_state(
112
+ ocm_api, cluster_id, policy["id"]
113
+ )
114
+ results.append(policy_data)
115
+
116
+ return results
117
+
118
+ def _get_addon_upgrade_policy_state(
119
+ self, ocm_api: OCMBaseClient, cluster_id: str, addon_upgrade_policy_id: str
120
+ ) -> Optional[str]:
121
+ try:
122
+ state_data = ocm_api.get(
123
+ f"{self.addon_base_api_path()}/clusters/{cluster_id}/addon_upgrade_policies/{addon_upgrade_policy_id}/state"
124
+ )
125
+ return state_data.get("value")
126
+ except Exception:
127
+ return None
128
+
129
+ def create_addon_upgrade_policy(
130
+ self,
131
+ ocm_api: OCMBaseClient,
132
+ cluster_id: str,
133
+ addon_id: str,
134
+ schedule_type: str,
135
+ version: str,
136
+ next_run: str,
137
+ ) -> None:
138
+ """
139
+ Creates a new Addon Upgrade Policy
140
+ """
141
+ spec = {
142
+ "version": version,
143
+ "schedule_type": schedule_type,
144
+ "addon_id": addon_id,
145
+ "cluster_id": cluster_id,
146
+ "upgrade_type": "ADDON",
147
+ }
148
+ ocm_api.post(
149
+ f"{self.addon_base_api_path()}/clusters/{cluster_id}/addon_upgrade_policies",
150
+ spec,
38
151
  )
39
- addons.append(OCMAddonInstallation(**addon))
40
- return addons
41
152
 
153
+ def delete_addon_upgrade_policy(
154
+ self, ocm_api: OCMBaseClient, cluster_id: str, policy_id: str
155
+ ) -> None:
156
+ """
157
+ Deletes an existing Addon Upgrade Policy
158
+ """
159
+ ocm_api.delete(
160
+ f"{self.addon_base_api_path()}/clusters/{cluster_id}/addon_upgrade_policies/{policy_id}"
161
+ )
42
162
 
43
- def get_addon_latest_versions(ocm_api: OCMBaseClient) -> dict[str, str]:
163
+
164
+ class AddonServiceV2(AddonService):
44
165
  """
45
- Returns the latest version for each addon.
166
+ The dedicated addon-service API that is part of OCM.
46
167
  """
47
- latest_versions: dict[str, str] = {}
48
- for addon in ocm_api.get_paginated("/api/clusters_mgmt/v1/addons"):
49
- addon_id = addon["id"]
50
- latest_versions[addon_id] = addon["version"]["id"]
51
- return latest_versions
168
+
169
+ def addon_base_api_path(self) -> str:
170
+ return "/api/addons_mgmt/v1"
171
+
172
+ def get_addon_upgrade_policies(
173
+ self, ocm_api: OCMBaseClient, cluster_id: str, addon_id: Optional[str] = None
174
+ ) -> list[dict[str, Any]]:
175
+ results: list[dict[str, Any]] = []
176
+
177
+ for policy in ocm_api.get_paginated(
178
+ f"{self.addon_base_api_path()}/clusters/{cluster_id}/upgrade_plan"
179
+ ):
180
+ if addon_id and policy["addon_id"] != addon_id:
181
+ continue
182
+ policy_data = {
183
+ k: v
184
+ for k, v in policy.items()
185
+ if k in ADDON_UPGRADE_POLICY_DESIRED_KEYS
186
+ }
187
+ results.append(policy_data)
188
+
189
+ return results
190
+
191
+ def create_addon_upgrade_policy(
192
+ self,
193
+ ocm_api: OCMBaseClient,
194
+ cluster_id: str,
195
+ addon_id: str,
196
+ schedule_type: str,
197
+ version: str,
198
+ next_run: str,
199
+ ) -> None:
200
+ """
201
+ Schedules an addon upgrade. Leverages addon-service upgrade plans behind the scene.
202
+ """
203
+ spec = {
204
+ "version": version,
205
+ "type": schedule_type,
206
+ "addon_id": addon_id,
207
+ "cluster_id": cluster_id,
208
+ "next_run": next_run,
209
+ }
210
+ ocm_api.post(
211
+ f"{self.addon_base_api_path()}/clusters/{cluster_id}/upgrade_plan", spec
212
+ )
213
+
214
+ def delete_addon_upgrade_policy(
215
+ self, ocm_api: OCMBaseClient, cluster_id: str, policy_id: str
216
+ ) -> None:
217
+ """
218
+ Deletes an existing upgrade plan.
219
+ """
220
+ ocm_api.delete(
221
+ f"{self.addon_base_api_path()}/clusters/{cluster_id}/upgrade_plan/{policy_id}"
222
+ )
@@ -8,78 +8,12 @@ from reconcile.utils.ocm.base import OCMVersionGate
8
8
  from reconcile.utils.ocm_base_client import OCMBaseClient
9
9
 
10
10
  UPGRADE_POLICY_DESIRED_KEYS = {"id", "schedule_type", "schedule", "next_run", "version"}
11
- ADDON_UPGRADE_POLICY_DESIRED_KEYS = {
12
- "id",
13
- "addon_id",
14
- "schedule_type",
15
- "schedule",
16
- "next_run",
17
- "version",
18
- }
19
11
 
20
12
 
21
13
  def build_cluster_url(cluster_id: str) -> str:
22
14
  return f"/api/clusters_mgmt/v1/clusters/{cluster_id}"
23
15
 
24
16
 
25
- #
26
- # ADDON UPGRADE POLICIES
27
- #
28
-
29
-
30
- def get_addon_upgrade_policies(
31
- ocm_api: OCMBaseClient, cluster_id: str, addon_id: Optional[str] = None
32
- ) -> list[dict[str, Any]]:
33
- results: list[dict[str, Any]] = []
34
-
35
- for policy in ocm_api.get_paginated(
36
- f"{build_cluster_url(cluster_id)}/addon_upgrade_policies"
37
- ):
38
- if addon_id and policy["addon_id"] != addon_id:
39
- continue
40
- policy_data = {
41
- k: v for k, v in policy.items() if k in ADDON_UPGRADE_POLICY_DESIRED_KEYS
42
- }
43
- policy_data["state"] = get_addon_upgrade_policy_state(
44
- ocm_api, cluster_id, policy["id"]
45
- )
46
- results.append(policy_data)
47
-
48
- return results
49
-
50
-
51
- def get_addon_upgrade_policy_state(
52
- ocm_api: OCMBaseClient, cluster_id: str, addon_upgrade_policy_id: str
53
- ) -> Optional[str]:
54
- try:
55
- state_data = ocm_api.get(
56
- f"{build_cluster_url(cluster_id)}/addon_upgrade_policies/{addon_upgrade_policy_id}/state"
57
- )
58
- return state_data.get("value")
59
- except Exception:
60
- return None
61
-
62
-
63
- def create_addon_upgrade_policy(
64
- ocm_api: OCMBaseClient, cluster_id: str, spec: dict
65
- ) -> None:
66
- """
67
- Creates a new Addon Upgrade Policy
68
- """
69
- ocm_api.post(f"{build_cluster_url(cluster_id)}/addon_upgrade_policies", spec)
70
-
71
-
72
- def delete_addon_upgrade_policy(
73
- ocm_api: OCMBaseClient, cluster_id: str, policy_id: str
74
- ) -> None:
75
- """
76
- Deletes an existing Addon Upgrade Policy
77
- """
78
- ocm_api.delete(
79
- f"{build_cluster_url(cluster_id)}/addon_upgrade_policies/{policy_id}"
80
- )
81
-
82
-
83
17
  #
84
18
  # UPGRADE POLICIES
85
19
  #
tools/qontract_cli.py CHANGED
@@ -46,6 +46,8 @@ from reconcile.aus.base import (
46
46
  AbstractUpgradePolicy,
47
47
  AdvancedUpgradeSchedulerBaseIntegration,
48
48
  AdvancedUpgradeSchedulerBaseIntegrationParams,
49
+ addon_upgrade_policy_soonest_next_run,
50
+ init_addon_service_version,
49
51
  )
50
52
  from reconcile.aus.models import OrganizationUpgradeSpec
51
53
  from reconcile.change_owners.bundle import NoOpFileDiffResolver
@@ -834,7 +836,6 @@ def upgrade_cluster_addon(
834
836
  ocm_org: str, cluster: str, addon: str, dry_run: bool, force: bool
835
837
  ) -> None:
836
838
  import reconcile.aus.ocm_addons_upgrade_scheduler_org as oauso
837
- from reconcile.utils.ocm.upgrades import create_addon_upgrade_policy
838
839
 
839
840
  settings = queries.get_app_interface_settings()
840
841
  ocms = queries.get_openshift_cluster_managers()
@@ -886,14 +887,21 @@ def upgrade_cluster_addon(
886
887
  )
887
888
  print(["create", ocm_org, cluster, addon, ocm_addon_version])
888
889
  if not dry_run:
889
- spec = {
890
- "version": ocm_addon_version,
891
- "schedule_type": "manual",
892
- "addon_id": addon,
893
- "cluster_id": ocm.cluster_ids[cluster],
894
- "upgrade_type": "ADDON",
895
- }
896
- create_addon_upgrade_policy(ocm._ocm_client, ocm.cluster_ids[cluster], spec)
890
+ # detection addon service version
891
+ ocm_env_labels = json.loads(ocm_info["environment"].get("labels") or "{}")
892
+ addon_service_version = (
893
+ ocm_env_labels.get("feature_flag_addon_service_version") or "v2"
894
+ )
895
+ addon_service = init_addon_service_version(addon_service_version)
896
+
897
+ addon_service.create_addon_upgrade_policy(
898
+ ocm_api=ocm._ocm_client,
899
+ cluster_id=ocm.cluster_ids[cluster],
900
+ addon_id=ocm_addon["id"],
901
+ schedule_type="manual",
902
+ version=ocm_addon_version,
903
+ next_run=addon_upgrade_policy_soonest_next_run(),
904
+ )
897
905
 
898
906
 
899
907
  def has_cluster_account_access(cluster: dict[str, Any]):