qontract-reconcile 0.10.1rc613__py3-none-any.whl → 0.10.1rc614__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.1rc613
3
+ Version: 0.10.1rc614
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=iYjF1R-M2FdokkMzjlCFennx-E9Go8xdJN5QGTFZMjU,91560
12
+ reconcile/cli.py,sha256=Gu9RMIRj9mA9vP7AkTj1sxqMyMdKZk1VU4XNQGVhuX0,93058
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
@@ -53,7 +53,7 @@ reconcile/ocm_additional_routers.py,sha256=KfcFDVbNoc6n5dHWjYdAf1_DiVqVG6Tw23WLK
53
53
  reconcile/ocm_addons.py,sha256=qqAyqRBRbdZQvAcjb-QlSVyRAyQBZk6iVlgnI4jyi7s,3353
54
54
  reconcile/ocm_addons_upgrade_tests_trigger.py,sha256=L1ktYynIR7pmHiYtdCTdLGJRKX72B54KUAuPLOccXzo,3995
55
55
  reconcile/ocm_aws_infrastructure_access.py,sha256=iq6Rj5SgF7yIMvBhukTuy-KcrFsvLs1ATG6-dyiArqY,6960
56
- reconcile/ocm_clusters.py,sha256=r2Y_crmX8v-HMWUh0GoBy5pBQOkl3sFaxeAbQmfEdhU,13906
56
+ reconcile/ocm_clusters.py,sha256=YWfBF25E55Odm0L3dXnqRbC_bDuYIsBPg4j6DEpWCGw,16703
57
57
  reconcile/ocm_external_configuration_labels.py,sha256=imEpDv1RBpCSj8tHDv0R76hmNCFtcUzVNgS1yOVl8vs,3870
58
58
  reconcile/ocm_github_idp.py,sha256=glwXMsIBcl38-OmDDQCpe0YoLLXfoRgVQmqwXMEXjds,3946
59
59
  reconcile/ocm_groups.py,sha256=AmQ61fjJYS5PxwNEWtOAvOoJM86VfRQ0-ic6wgw6PU0,2888
@@ -338,7 +338,7 @@ reconcile/jenkins/types.py,sha256=0UlyJxv3KY1WXHkfI_ghUI6FAwRJTL4EwvLg-62tNcg,30
338
338
  reconcile/ldap_groups/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
339
339
  reconcile/ldap_groups/integration.py,sha256=GscI7833EOEOiaySc2yKMg0u0hqzVJ3D3r27JfCMY4g,11425
340
340
  reconcile/ocm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
341
- reconcile/ocm/types.py,sha256=Q9vPQI3LpLajGtPqks7i36AV6CjRho96mmsTkdJrEWM,2477
341
+ reconcile/ocm/types.py,sha256=ibJYvzfAZyyMFkcF1bP8u3rkXciYJRplt_7Z1pKHFh0,2484
342
342
  reconcile/ocm_internal_notifications/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
343
343
  reconcile/ocm_internal_notifications/integration.py,sha256=6JFLu6TlcMC1OB5WgTAd-ITlhH_56W5uBytggukNk3A,4391
344
344
  reconcile/ocm_labels/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -392,6 +392,7 @@ reconcile/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
392
392
  reconcile/templates/aws_access_key_email.j2,sha256=2MUr1ERmyISzKgHqsWYLd-1Wbl-peUa-FsGUS-JLUFc,238
393
393
  reconcile/templates/email.yml.j2,sha256=OZgczNRgXPj2gVYTgwQyHAQrMGu7xp-e4W1rX19GcrU,690
394
394
  reconcile/templates/jira-checkpoint-missinginfo.j2,sha256=c_Vvg-lEENsB3tgxm9B6Y9igCUQhCnFDYh6xw-zcIbU,570
395
+ reconcile/templates/rosa-hcp-cluster-creation.sh.j2,sha256=6qjnvWgkJ6A0Mh08uRKMWewxDeOqc0ju4JSmlp5sW_I,2146
395
396
  reconcile/templating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
396
397
  reconcile/templating/renderer.py,sha256=IOZN0ASSKqp4JGXZ1BDba4XosD3nCuqhStxHMGBcxDI,5119
397
398
  reconcile/templating/rendering.py,sha256=msMGqCNh7M0c6A1l_7KBmT3GRHH9gsB8htN_9bGtwe4,4949
@@ -429,11 +430,11 @@ reconcile/test/test_jump_host.py,sha256=yczTqvT-hNAf9zBMuFjqka9fQOA31SCNG7D-9K9M
429
430
  reconcile/test/test_ldap_users.py,sha256=8jjzVgoiRRylGad6-TvkugoFGXt3eko--zVVKjmZDn4,3812
430
431
  reconcile/test/test_make.py,sha256=zTdjgq-3idFlec_0qJenk9wWw0QMLvSpJfPsptXmync,677
431
432
  reconcile/test/test_ocm_additional_routers.py,sha256=dtbpUnD5un6Q3VoLbuFRb_njmt5SSCnBzvSSBcO_Xxs,4248
432
- reconcile/test/test_ocm_clusters.py,sha256=NOxhJnlgOlRZF0-sfXCiRP0aiNiBiMl35MTCCh_Ff5s,23130
433
+ reconcile/test/test_ocm_clusters.py,sha256=yiymyepNwV_JiCGEsvqKVAxq3WNJHsvrpJRuNRlyvtM,25468
433
434
  reconcile/test/test_ocm_clusters_manifest_updates.py,sha256=jFRVfc5jby1kI2x_gT6wcqPPgkav1et9wZH6JqQbNSY,3278
434
435
  reconcile/test/test_ocm_machine_pools.py,sha256=3qo6t2Jfr1Wee0NUacyLTDmatp0o7CUNpkVOpHiOiGk,29737
435
436
  reconcile/test/test_ocm_update_recommended_version.py,sha256=iA4BVirTGVXlwcOyeR52IuNO81X_8NR6ZNd7ZFE7igs,4328
436
- reconcile/test/test_ocm_upgrade_scheduler_org_updater.py,sha256=hT6sbdGUx8LGnMVvNI7wOVHcoan1YurckytlvtJdzGk,4347
437
+ reconcile/test/test_ocm_upgrade_scheduler_org_updater.py,sha256=V91g2XQMa2nvKwkLVWkiPwNL6pMQE16s4jO0oXJ6wdk,4330
437
438
  reconcile/test/test_openshift_base.py,sha256=uVsnMghAQhHaJTreeOw4x2INTKJ6qeiZiiteWeKflW8,33874
438
439
  reconcile/test/test_openshift_cluster_bots.py,sha256=L-yuKvMgB0LBCdfLu7wozh_lk6S_m3umXt3m_ECfLEI,8023
439
440
  reconcile/test/test_openshift_namespace_labels.py,sha256=P1hqi6P88NijNrurdXG_QR2usyo3EYZSy9zpwYHvDsM,12104
@@ -638,7 +639,7 @@ reconcile/utils/jinja2/extensions.py,sha256=zV_x8MhSHAynKhFnG3fULXrwsm5fUG_88Iyg
638
639
  reconcile/utils/jinja2/filters.py,sha256=_kJjdMsY3lGS5PUn4NnpXUQDNrL1IwiKsB-0MhTMGYM,4521
639
640
  reconcile/utils/jinja2/utils.py,sha256=1TV_BoKrCFxVPbRY4hhUmbasrPa_wm9AoabCEIOXRLg,5947
640
641
  reconcile/utils/jobcontroller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
641
- reconcile/utils/jobcontroller/controller.py,sha256=BeGm2KNfVnxPuQkfIFy3Tp9H_NSpoAu-gy40elUgvxs,14181
642
+ reconcile/utils/jobcontroller/controller.py,sha256=QE4x-ahIWLA5GlI0KZEET0Elxq_aq4Hw1GBCRFSfv3U,14185
642
643
  reconcile/utils/jobcontroller/models.py,sha256=z0gRJy8Ow1leYS-GrvTUv-t-mQDPHTJVDoHb1nVcZ_8,6349
643
644
  reconcile/utils/membershipsources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
644
645
  reconcile/utils/membershipsources/app_interface_resolver.py,sha256=IlDiRtJZ0AfAGKEawybB6SvsKbm1POTXL6fpEt699E0,1979
@@ -655,14 +656,15 @@ reconcile/utils/mr/notificator.py,sha256=2Xyg5Xh3xFQITdTmZ8Nry5qe0uC_w_On4uMPdlN
655
656
  reconcile/utils/mr/ocm_update_recommended_version.py,sha256=p_aVP0TGrlKk9WBwgQnYWqUDsED_Hg6G5Bqj0UvtRwA,1536
656
657
  reconcile/utils/mr/ocm_upgrade_scheduler_org_updates.py,sha256=RzEKRT_BhvB2ud9pyNFZ1cNZFmDGBUah9vjpP0y9f9w,2810
657
658
  reconcile/utils/mr/user_maintenance.py,sha256=cHPBn8zrReWLHalyk-EFdkFJe9zjVjRoZhT4t2zZfGE,3956
658
- reconcile/utils/ocm/__init__.py,sha256=5Pcf5cyftDWT5XRi1EzvNklOVxGplJi-v12HN3TDarc,57
659
+ reconcile/utils/ocm/__init__.py,sha256=xv7CJp7K9LCQfa4gL_W0MMCOD1P4qOy8t5aZj1xXNUE,808
659
660
  reconcile/utils/ocm/addons.py,sha256=8wVrt16i69KkXq1fQByVheSQRhrRELbuOHb7Tz9bKT0,1675
660
661
  reconcile/utils/ocm/base.py,sha256=GclZtCrPkPJmGP9HHvqIlV-8VXSKuaQTQJkA2pklN60,13817
661
662
  reconcile/utils/ocm/cluster_groups.py,sha256=F8oqVqN_4QUnGL0K61zZhoYIzJeP57EcmZpwmoV0mr4,1751
662
- reconcile/utils/ocm/clusters.py,sha256=Q6g5kGSNfxZUZ56LPFAYjOz8xJ2c6QG76V78GvyLxB0,7448
663
+ reconcile/utils/ocm/clusters.py,sha256=Nw9m-jgN3GHHCh6w9UOBbMV4rtS24_-Ep09jAWQ-_fE,7653
663
664
  reconcile/utils/ocm/identity_providers.py,sha256=dKed09N8iWmn39tI_MpwgVe47x23eLsknGbjMUxtwr4,2175
664
665
  reconcile/utils/ocm/labels.py,sha256=pRzTE506hKAgVbBNO51IBlGOuCbJmTYDMsL0pWBEwp8,5945
665
- reconcile/utils/ocm/ocm.py,sha256=Vxz07SWvlLHGm6E7zeG3Oi4hHD5iNVZVc1SqXIbpPdY,58522
666
+ reconcile/utils/ocm/ocm.py,sha256=axXuTUpJR-fqhDo6OiDzEygOsR9OCX8ZV1I_Q2vWJQc,36712
667
+ reconcile/utils/ocm/products.py,sha256=tm8uRc1FxeIhqHv-f_ColD5IHKcsqGaEnoe1RFQtH-8,25296
666
668
  reconcile/utils/ocm/search_filters.py,sha256=zExZpYBh7_tucG-xKoPHUxz1b_6l9qwbEMpMihQg7nA,15043
667
669
  reconcile/utils/ocm/service_log.py,sha256=XmGOP6hwhKwMEMeD0GhErTGzdgk1IHaKs-QatL1mAdY,2052
668
670
  reconcile/utils/ocm/sre_capability_labels.py,sha256=-TUmTzvK5yu7qY8SUMGj6-684wa8ZVYbjrJ6X3M7z-o,1576
@@ -672,7 +674,7 @@ reconcile/utils/ocm/syncsets.py,sha256=zvji9qWvInIRTdoybMaM9-VUhq4L1VewWfWCQmp4u
672
674
  reconcile/utils/ocm/upgrades.py,sha256=ZWDfg3VrmRGx7Re-JjecRjwmn7Rh-dsuLA3OljbCByg,6616
673
675
  reconcile/utils/rosa/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
674
676
  reconcile/utils/rosa/rosa_cli.py,sha256=b7V3EgO4b4hOnvZzrxdn-bxRbkINQysOg_ecuB8l5GA,11364
675
- reconcile/utils/rosa/session.py,sha256=nPEmSNos0_ATQEVg2KckAlVyIEBvxUVo7pa0WMACoOU,4150
677
+ reconcile/utils/rosa/session.py,sha256=imjCFgHHjlh3TPUDg6dZFzkmQqFq2kCKAM948XDKiUI,6098
676
678
  reconcile/utils/runtime/__init__.py,sha256=sfk92MGfsBh9tKYHl_FH17NdEsrGBwgDFTb7KNKoIfY,107
677
679
  reconcile/utils/runtime/desired_state_diff.py,sha256=finZnWoVDCiYTgu4lGk8G4QOFAGgiIDhD3fcnVqYoxM,8108
678
680
  reconcile/utils/runtime/environment.py,sha256=JLptHJoYyeLdMBghJppttP3wZ5HxHLMLgUcfGjIiKLM,2087
@@ -712,8 +714,8 @@ tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvf
712
714
  tools/test/test_qontract_cli.py,sha256=OvalpVRfY4pNmpMaWHHYqBjV68b1eGQjX8SCyTAXb1w,3501
713
715
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
714
716
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
715
- qontract_reconcile-0.10.1rc613.dist-info/METADATA,sha256=OmG5nHaRuu7nDCZ91tOV_K4XRN0ZAvgzKYz1IbS9j0E,2382
716
- qontract_reconcile-0.10.1rc613.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
717
- qontract_reconcile-0.10.1rc613.dist-info/entry_points.txt,sha256=rIxI5zWtHNlfpDeq1a7pZXAPoqf7HG32KMTN3MeWK_8,429
718
- qontract_reconcile-0.10.1rc613.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
719
- qontract_reconcile-0.10.1rc613.dist-info/RECORD,,
717
+ qontract_reconcile-0.10.1rc614.dist-info/METADATA,sha256=-4XRtExmYEBBglO9ovJfxuTQRZdgwNWRUWLEXfb1Yx4,2382
718
+ qontract_reconcile-0.10.1rc614.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
719
+ qontract_reconcile-0.10.1rc614.dist-info/entry_points.txt,sha256=rIxI5zWtHNlfpDeq1a7pZXAPoqf7HG32KMTN3MeWK_8,429
720
+ qontract_reconcile-0.10.1rc614.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
721
+ qontract_reconcile-0.10.1rc614.dist-info/RECORD,,
reconcile/cli.py CHANGED
@@ -2235,12 +2235,65 @@ def ocm_groups(ctx, thread_pool_size):
2235
2235
  @integration.command(short_help="Manages clusters via OCM.")
2236
2236
  @gitlab_project_id
2237
2237
  @threaded()
2238
+ @click.option(
2239
+ "--job-controller-cluster",
2240
+ help="The cluster holding the job-controller namepsace",
2241
+ required=False,
2242
+ envvar="JOB_CONTROLLER_CLUSTER",
2243
+ )
2244
+ @click.option(
2245
+ "--job-controller-namespace",
2246
+ help="The namespace used for ROSA jobs",
2247
+ required=False,
2248
+ envvar="JOB_CONTROLLER_NAMESPACE",
2249
+ )
2250
+ @click.option(
2251
+ "--rosa-job-service-account",
2252
+ help="The service-account used for ROSA jobs",
2253
+ required=False,
2254
+ envvar="ROSA_JOB_SERVICE_ACCOUNT",
2255
+ )
2256
+ @click.option(
2257
+ "--rosa-job-image",
2258
+ help="The container image to use to run ROSA cli command jobs",
2259
+ required=False,
2260
+ envvar="ROSA_JOB_IMAGE",
2261
+ )
2262
+ @click.option(
2263
+ "--rosa-role",
2264
+ help="The role to assume in the ROSA cluster account",
2265
+ required=False,
2266
+ envvar="ROSA_ROLE",
2267
+ )
2238
2268
  @click.pass_context
2239
- def ocm_clusters(ctx, gitlab_project_id, thread_pool_size):
2240
- import reconcile.ocm_clusters
2269
+ def ocm_clusters(
2270
+ ctx,
2271
+ gitlab_project_id: Optional[str],
2272
+ thread_pool_size: int,
2273
+ job_controller_cluster: Optional[str],
2274
+ job_controller_namespace: Optional[str],
2275
+ rosa_job_service_account: Optional[str],
2276
+ rosa_role: Optional[str],
2277
+ rosa_job_image: Optional[str],
2278
+ ):
2279
+ from reconcile.ocm_clusters import (
2280
+ OcmClusters,
2281
+ OcmClustersParams,
2282
+ )
2241
2283
 
2242
- run_integration(
2243
- reconcile.ocm_clusters, ctx.obj, gitlab_project_id, thread_pool_size
2284
+ run_class_integration(
2285
+ integration=OcmClusters(
2286
+ OcmClustersParams(
2287
+ gitlab_project_id=gitlab_project_id,
2288
+ thread_pool_size=thread_pool_size,
2289
+ job_controller_cluster=job_controller_cluster,
2290
+ job_controller_namespace=job_controller_namespace,
2291
+ rosa_job_service_account=rosa_job_service_account,
2292
+ rosa_job_image=rosa_job_image,
2293
+ rosa_role=rosa_role,
2294
+ )
2295
+ ),
2296
+ ctx=ctx.obj,
2244
2297
  )
2245
2298
 
2246
2299
 
reconcile/ocm/types.py CHANGED
@@ -74,7 +74,7 @@ class ROSAOcmAwsAttrs(BaseModel):
74
74
 
75
75
  class ROSAClusterAWSAccount(BaseModel):
76
76
  uid: str
77
- rosa: ROSAOcmAwsAttrs
77
+ rosa: ROSAOcmAwsAttrs | None
78
78
 
79
79
  class Config:
80
80
  extra = Extra.forbid
reconcile/ocm_clusters.py CHANGED
@@ -4,7 +4,9 @@ from collections.abc import (
4
4
  Iterable,
5
5
  Mapping,
6
6
  )
7
- from typing import Any
7
+ from typing import Any, Optional
8
+
9
+ import semver
8
10
 
9
11
  import reconcile.utils.mr.clusters_updates as cu
10
12
  import reconcile.utils.ocm as ocmmod
@@ -21,9 +23,22 @@ from reconcile.ocm.types import (
21
23
  )
22
24
  from reconcile.status import ExitCodes
23
25
  from reconcile.utils.disabled_integrations import integration_is_enabled
26
+ from reconcile.utils.jobcontroller.controller import build_job_controller
27
+ from reconcile.utils.ocm.products import (
28
+ OCMProduct,
29
+ OCMProductPortfolio,
30
+ OCMValidationException,
31
+ build_product_portfolio,
32
+ )
33
+ from reconcile.utils.rosa.session import RosaSessionBuilder
34
+ from reconcile.utils.runtime.integration import (
35
+ PydanticRunParams,
36
+ QontractReconcileIntegration,
37
+ )
24
38
  from reconcile.utils.semver_helper import parse_semver
25
39
 
26
40
  QONTRACT_INTEGRATION = "ocm-clusters"
41
+ QONTRACT_INTEGRATION_VERSION = semver.format_version(0, 1, 0)
27
42
 
28
43
 
29
44
  def _set_rosa_ocm_attrs(cluster: Mapping[str, Any]):
@@ -32,26 +47,24 @@ def _set_rosa_ocm_attrs(cluster: Mapping[str, Any]):
32
47
  but the cluster only needs the target OCM environment where it belongs.
33
48
  This method changes the cluster dictionary to include just those.
34
49
  """
35
- ocm_env = [
36
- env
37
- for env in cluster["spec"]["account"]["rosa"]["ocm_environments"]
38
- if env["ocm"]["name"] == cluster["ocm"]["name"]
39
- ]
40
-
41
- if len(ocm_env) != 1:
42
- logging.error(
43
- "The cluster's OCM reference does not exist or it is duplicated in the AWS account manifest. "
44
- "Check the cluster's AWS account rosa configuration. "
45
- f"OCM:{cluster['ocm']['name']}, AWSAcc:{cluster['spec']['account']['uid']}"
46
- )
47
- sys.exit(ExitCodes.ERROR)
48
-
49
50
  uid = cluster["spec"]["account"]["uid"]
50
- env = ocm_env[0]
51
- # doing this allows to exclude account fields which can be queried in graphql
52
- cluster["spec"]["account"] = ROSAClusterAWSAccount(
53
- uid=uid,
54
- rosa=ROSAOcmAwsAttrs(
51
+ rosa_ocm_configs = cluster["spec"]["account"]["rosa"]
52
+ rosa: ROSAOcmAwsAttrs | None = None
53
+ if rosa_ocm_configs:
54
+ ocm_env = [
55
+ env
56
+ for env in rosa_ocm_configs["ocm_environments"]
57
+ if env["ocm"]["name"] == cluster["ocm"]["name"]
58
+ ]
59
+ if len(ocm_env) != 1:
60
+ logging.error(
61
+ "The cluster's OCM reference does not exist or it is duplicated in the AWS account manifest. "
62
+ "Check the cluster's AWS account rosa configuration. "
63
+ f"OCM:{cluster['ocm']['name']}, AWSAcc:{cluster['spec']['account']['uid']}"
64
+ )
65
+ sys.exit(ExitCodes.ERROR)
66
+ env = ocm_env[0]
67
+ rosa = ROSAOcmAwsAttrs(
55
68
  creator_role_arn=env["creator_role_arn"],
56
69
  sts=ROSAOcmAwsStsAttrs(
57
70
  installer_role_arn=env["installer_role_arn"],
@@ -59,7 +72,14 @@ def _set_rosa_ocm_attrs(cluster: Mapping[str, Any]):
59
72
  controlplane_role_arn=env.get("controlplane_role_arn"),
60
73
  worker_role_arn=env["worker_role_arn"],
61
74
  ),
62
- ),
75
+ )
76
+ else:
77
+ rosa = None
78
+
79
+ # doing this allows to exclude account fields which can be queried in graphql
80
+ cluster["spec"]["account"] = ROSAClusterAWSAccount(
81
+ uid=uid,
82
+ rosa=rosa,
63
83
  )
64
84
 
65
85
 
@@ -210,7 +230,7 @@ def get_app_interface_spec_updates(
210
230
 
211
231
 
212
232
  def get_cluster_ocm_update_spec(
213
- ocm: ocmmod.OCM, cluster: str, current_spec: OCMSpec, desired_spec: OCMSpec
233
+ product: OCMProduct, cluster: str, current_spec: OCMSpec, desired_spec: OCMSpec
214
234
  ) -> tuple[dict[str, Any], bool]:
215
235
  """Get cluster updates to request to OCM api
216
236
 
@@ -221,8 +241,6 @@ def get_cluster_ocm_update_spec(
221
241
  :return: a tuple with the updates to request to OCM and a bool to notify errors
222
242
  """
223
243
 
224
- impl = ocmmod.OCM_PRODUCTS_IMPL[current_spec.spec.product]
225
-
226
244
  error = False
227
245
  if not desired_spec.network.type:
228
246
  desired_spec.network.type = "OVNKubernetes"
@@ -237,13 +255,13 @@ def get_cluster_ocm_update_spec(
237
255
  current_ocm_spec = {
238
256
  k: v
239
257
  for k, v in cspec.items()
240
- if v is not None and k not in impl.EXCLUDED_SPEC_FIELDS
258
+ if v is not None and k not in product.EXCLUDED_SPEC_FIELDS
241
259
  }
242
260
 
243
261
  desired_ocm_spec = {
244
262
  k: v
245
263
  for k, v in dspec.items()
246
- if v is not None and k not in impl.EXCLUDED_SPEC_FIELDS
264
+ if v is not None and k not in product.EXCLUDED_SPEC_FIELDS
247
265
  }
248
266
 
249
267
  # Updated attributes in app-interface
@@ -258,7 +276,7 @@ def get_cluster_ocm_update_spec(
258
276
 
259
277
  diffs = deleted_attrs | updated_attrs
260
278
 
261
- not_allowed_updates = set(diffs) - impl.ALLOWED_SPEC_UPDATE_FIELDS
279
+ not_allowed_updates = set(diffs) - product.ALLOWED_SPEC_UPDATE_FIELDS
262
280
  if not_allowed_updates:
263
281
  error = True
264
282
  logging.error(f"[{cluster}] invalid updates: {not_allowed_updates}")
@@ -267,7 +285,7 @@ def get_cluster_ocm_update_spec(
267
285
 
268
286
 
269
287
  def _app_interface_updates_mr(
270
- clusters_updates: Mapping[str, Any], gitlab_project_id: str, dry_run: bool
288
+ clusters_updates: Mapping[str, Any], gitlab_project_id: Optional[str], dry_run: bool
271
289
  ):
272
290
  """Creates an MR to app-interface with the necessary cluster manifest updates
273
291
 
@@ -301,83 +319,133 @@ def _cluster_is_compatible(cluster: Mapping[str, Any]) -> bool:
301
319
  return cluster.get("ocm") is not None
302
320
 
303
321
 
304
- def run(dry_run: bool, gitlab_project_id=None, thread_pool_size=10):
305
- settings = queries.get_app_interface_settings()
306
- clusters = queries.get_clusters()
307
- clusters = [
308
- c
309
- for c in clusters
310
- if integration_is_enabled(QONTRACT_INTEGRATION, c) and _cluster_is_compatible(c)
311
- ]
312
- if not clusters:
313
- logging.debug("No OCM cluster definitions found in app-interface")
314
- sys.exit(ExitCodes.SUCCESS)
315
-
316
- ocm_map = ocmmod.OCMMap(
317
- clusters=clusters,
318
- integration=QONTRACT_INTEGRATION,
319
- settings=settings,
320
- init_provision_shards=True,
321
- )
322
+ class OcmClustersParams(PydanticRunParams):
323
+ gitlab_project_id: Optional[str] = None
324
+ thread_pool_size: int = 10
322
325
 
323
- # current_state is the state got from the ocm api
324
- current_state, pending_state = ocm_map.cluster_specs()
325
- desired_state = fetch_desired_state(clusters)
326
+ # rosa job controller params
327
+ job_controller_cluster: Optional[str] = None
328
+ job_controller_namespace: Optional[str] = None
329
+ rosa_job_service_account: Optional[str] = None
330
+ rosa_role: Optional[str] = None
331
+ rosa_job_image: Optional[str] = None
326
332
 
327
- error = False
328
- clusters_updates = {}
329
-
330
- for cluster_name, desired_spec in desired_state.items():
331
- current_spec = current_state.get(cluster_name)
332
- if current_spec:
333
- # App-Interface manifests updates.
334
- # OCM populated attributes that are not set in app-interface.
335
- # These updates are performed with a single MR out of this main loop
336
- clusters_updates[cluster_name], err = get_app_interface_spec_updates(
337
- cluster_name, current_spec, desired_spec
338
- )
339
- if err:
340
- error = True
341
-
342
- # OCM API Updates
343
- # Changes made to app-interface manifests that need to be requested
344
- # to the OCM Api
345
- ocm = ocm_map.get(cluster_name)
346
- update_spec, err = get_cluster_ocm_update_spec(
347
- ocm, cluster_name, current_spec, desired_spec
348
- )
349
- if err:
350
- error = True
351
- continue
352
-
353
- # update cluster
354
- if update_spec:
355
- logging.info(["update_cluster", cluster_name])
356
- logging.debug(
357
- f"current_spec: {current_spec}, desired_spec: {desired_spec}"
333
+
334
+ class OcmClusters(QontractReconcileIntegration[OcmClustersParams]):
335
+ @property
336
+ def name(self) -> str:
337
+ return QONTRACT_INTEGRATION
338
+
339
+ def rosa_session_builder(self) -> RosaSessionBuilder | None:
340
+ if (
341
+ self.params.job_controller_cluster is None
342
+ or self.params.job_controller_namespace is None
343
+ or self.params.rosa_job_service_account is None
344
+ or self.params.rosa_role is None
345
+ or self.params.rosa_job_image is None
346
+ ):
347
+ return None
348
+ return RosaSessionBuilder(
349
+ aws_iam_role=self.params.rosa_role,
350
+ image=self.params.rosa_job_image,
351
+ service_account=self.params.rosa_job_service_account,
352
+ job_controller=build_job_controller(
353
+ cluster=self.params.job_controller_cluster,
354
+ namespace=self.params.job_controller_namespace,
355
+ integration=self.name,
356
+ integration_version=QONTRACT_INTEGRATION_VERSION,
357
+ secret_reader=self.secret_reader,
358
+ dry_run=False,
359
+ ),
360
+ )
361
+
362
+ def assemble_product_portfolio(self) -> OCMProductPortfolio:
363
+ return build_product_portfolio(self.rosa_session_builder())
364
+
365
+ def run(self, dry_run: bool) -> None:
366
+ settings = queries.get_app_interface_settings()
367
+ clusters = queries.get_clusters()
368
+ clusters = [
369
+ c
370
+ for c in clusters
371
+ if integration_is_enabled(self.name, c) and _cluster_is_compatible(c)
372
+ ]
373
+ if not clusters:
374
+ logging.debug("No OCM cluster definitions found in app-interface")
375
+ sys.exit(ExitCodes.SUCCESS)
376
+
377
+ product_portfolio = self.assemble_product_portfolio()
378
+ ocm_map = ocmmod.OCMMap(
379
+ clusters=clusters,
380
+ integration=self.name,
381
+ settings=settings,
382
+ init_provision_shards=True,
383
+ product_portfolio=product_portfolio,
384
+ )
385
+
386
+ # current_state is the state got from the ocm api
387
+ current_state, pending_state = ocm_map.cluster_specs()
388
+ desired_state = fetch_desired_state(clusters)
389
+
390
+ error = False
391
+ clusters_updates = {}
392
+
393
+ for cluster_name, desired_spec in desired_state.items():
394
+ current_spec = current_state.get(cluster_name)
395
+ if current_spec:
396
+ # App-Interface manifests updates.
397
+ # OCM populated attributes that are not set in app-interface.
398
+ # These updates are performed with a single MR out of this main loop
399
+ clusters_updates[cluster_name], err = get_app_interface_spec_updates(
400
+ cluster_name, current_spec, desired_spec
358
401
  )
402
+ if err:
403
+ error = True
404
+
405
+ # OCM API Updates
406
+ # Changes made to app-interface manifests that need to be requested
407
+ # to the OCM Api
359
408
  ocm = ocm_map.get(cluster_name)
360
- ocm.update_cluster(cluster_name, update_spec, dry_run)
361
-
362
- else:
363
- # create cluster
364
- if cluster_name in pending_state:
365
- continue
366
- logging.info(["create_cluster", cluster_name])
367
- ocm = ocm_map.get(cluster_name)
368
- try:
369
- ocm.create_cluster(cluster_name, desired_spec, dry_run)
370
- except NotImplementedError:
371
- logging.error(
372
- f"[{cluster_name}] Create clusters is not currently implemented "
373
- f"for [{desired_spec.spec.product}] product type. Make sure the "
374
- "cluster exists and it is returned by the OCM api before adding "
375
- "its manifest to app-interface"
409
+ product = product_portfolio.get_product_impl(
410
+ current_spec.spec.product, current_spec.spec.hypershift
376
411
  )
377
- error = True
378
- except ocmmod.OCMValidationException as e:
379
- logging.error("[%s] Error creating cluster: %s", cluster_name, e)
380
- error = True
381
-
382
- _app_interface_updates_mr(clusters_updates, gitlab_project_id, dry_run)
383
- sys.exit(int(error))
412
+ update_spec, err = get_cluster_ocm_update_spec(
413
+ product, cluster_name, current_spec, desired_spec
414
+ )
415
+ if err:
416
+ error = True
417
+ continue
418
+
419
+ # update cluster
420
+ if update_spec:
421
+ logging.info(["update_cluster", cluster_name])
422
+ logging.debug(
423
+ f"current_spec: {current_spec}, desired_spec: {desired_spec}"
424
+ )
425
+ ocm = ocm_map.get(cluster_name)
426
+ ocm.update_cluster(cluster_name, update_spec, dry_run)
427
+
428
+ else:
429
+ # create cluster
430
+ if cluster_name in pending_state:
431
+ continue
432
+ logging.info(["create_cluster", cluster_name])
433
+ ocm = ocm_map.get(cluster_name)
434
+ try:
435
+ ocm.create_cluster(cluster_name, desired_spec, dry_run)
436
+ except NotImplementedError:
437
+ logging.error(
438
+ f"[{cluster_name}] Create clusters is not currently implemented "
439
+ f"for [{desired_spec.spec.product}] product type. Make sure the "
440
+ "cluster exists and it is returned by the OCM api before adding "
441
+ "its manifest to app-interface"
442
+ )
443
+ error = True
444
+ except OCMValidationException as e:
445
+ logging.error("[%s] Error creating cluster: %s", cluster_name, e)
446
+ error = True
447
+
448
+ _app_interface_updates_mr(
449
+ clusters_updates, self.params.gitlab_project_id, dry_run
450
+ )
451
+ sys.exit(int(error))
@@ -0,0 +1,54 @@
1
+ #!/bin/bash
2
+
3
+ rosa init
4
+ rosa create ocm-role --admin -y -m auto
5
+ rosa create account-roles --hosted-cp -y -m auto
6
+ rosa create user-role -y -m auto
7
+
8
+ # OIDC config
9
+ {% if cluster.spec.oidc_endpoint_url %}
10
+ OIDC_CONFIG_ID="{{ (cluster.spec.oidc_endpoint_url | split("/")) | last }}"
11
+ {% else %}
12
+ OIDC_CONFIG_ID=$(rosa list oidc-provider -o json | jq '.[0].arn // "/" | split("/") | .[-1]' -r)
13
+ if [[ -z "${OIDC_CONFIG_ID}" ]]; then
14
+ rosa create oidc-config -m auto -y
15
+ OIDC_CONFIG_ID=$(rosa list oidc-provider -o json | jq '.[0].arn // "/" | split("/") | .[-1]' -r)
16
+ else
17
+ echo "reuse existing OIDC config ${OIDC_CONFIG_ID}"
18
+ fi
19
+ {% endif %}
20
+
21
+ # operator roles
22
+ INSTALLER_ROLE_ARN=$(rosa list account-roles --region us-east-1 -o json | jq '.[] | select(.RoleType == "Installer") | .RoleARN' -r)
23
+ rosa create operator-roles --prefix {{ cluster_name }} --oidc-config-id ${OIDC_CONFIG_ID} --hosted-cp --installer-role-arn ${INSTALLER_ROLE_ARN} -m auto -y
24
+
25
+ # cluster creation
26
+ BILLING_ACCOUNT_ID=$(aws organizations describe-organization | jq .Organization.MasterAccountId -r)
27
+ rosa create cluster --cluster-name={{ cluster_name }} \
28
+ --billing-account ${BILLING_ACCOUNT_ID} \
29
+ {% if dry_run -%}
30
+ --dry-run \
31
+ {% endif -%}
32
+ --sts \
33
+ --hosted-cp \
34
+ --oidc-config-id ${OIDC_CONFIG_ID} \
35
+ --operator-roles-prefix {{ cluster_name }} \
36
+ --subnet-ids {{ cluster.spec.subnet_ids | join(",") }} \
37
+ --region {{ cluster.spec.region }} \
38
+ --version {{ cluster.spec.version }} \
39
+ --machine-cidr {{ cluster.network.vpc }} \
40
+ --service-cidr {{ cluster.network.service }} \
41
+ --pod-cidr {{ cluster.network.pod }} \
42
+ --host-prefix 23 \
43
+ --replicas {{ cluster.machine_pools | length }} \
44
+ --compute-machine-type {{ cluster.machine_pools[0].instance_type }} \
45
+ {% if cluster.spec.private -%}
46
+ --private \
47
+ {% endif -%}
48
+ {% if cluster.spec.disable_user_workload_monitoring -%}
49
+ --disable-workload-monitoring \
50
+ {% endif -%}
51
+ {% if cluster.spec.provision_shard_id -%}
52
+ --properties provision_shard_id:{{ cluster.spec.provision_shard_id }} \
53
+ {% endif -%}
54
+ --channel-group {{ cluster.spec.channel }}