qontract-reconcile 0.10.2.dev317__py3-none-any.whl → 0.10.2.dev319__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qontract-reconcile
3
- Version: 0.10.2.dev317
3
+ Version: 0.10.2.dev319
4
4
  Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
5
5
  Project-URL: homepage, https://github.com/app-sre/qontract-reconcile
6
6
  Project-URL: repository, https://github.com/app-sre/qontract-reconcile
@@ -35,12 +35,12 @@ reconcile/gitlab_permissions.py,sha256=LkufO5HDeti8N6QSZoGfoJG51_v8aVZYd-5MkK9Du
35
35
  reconcile/gitlab_projects.py,sha256=JIB1UP8CnwSkngEMZE7DFQETMX6sJMp4DXaKoS-Pdkc,1879
36
36
  reconcile/integrations_manager.py,sha256=g6QJOu_hJp_6tUpjpqc9HC_8FNWjvtoYat20TX-bJhg,9398
37
37
  reconcile/jenkins_base.py,sha256=0Gocu3fU2YTltaxBlbDQOUvP-7CP2OSQV1ZRwtWeVXw,875
38
- reconcile/jenkins_job_builder.py,sha256=SAcoJUSs2BgxEzfuRfJ4Poik2OSg_B8LaT5VQISocPs,3474
38
+ reconcile/jenkins_job_builder.py,sha256=eRR3AL3RN7U_8A4hceUlj_k_jWf-qHMyLr9gqxcj3kQ,3469
39
39
  reconcile/jenkins_job_builds_cleaner.py,sha256=l9eLyvdgv1sN2tAlkGx3T8g6Db9kIfWW3LJh5H6dV9A,4080
40
40
  reconcile/jenkins_roles.py,sha256=zdGavYJJNmbOdu_pr-NrNPl_Tj3j8oin14qvydKxXZw,4916
41
41
  reconcile/jenkins_webhooks.py,sha256=8BDPsCJdvuYDl0D3qJc0l6pUtceBh9WiNGpfy-LE4S8,2351
42
42
  reconcile/jenkins_webhooks_cleaner.py,sha256=tFbAzsFGvJ6UrHRZFdIuLdqG-Ocd5_OknfsbtN-eyFY,1619
43
- reconcile/jenkins_worker_fleets.py,sha256=tLFEF3ijnqF0jA7hgmFOOrshmfjGYXHFlQcvMZVvI7U,5386
43
+ reconcile/jenkins_worker_fleets.py,sha256=CqYekCjYv75NvtcegOjxMC-LvKpMYPChxI_9NQVu_VY,8236
44
44
  reconcile/jira_permissions_validator.py,sha256=nVHZg7kNn04Q-ryNM20wthMrhXos28g3O9b0ahzxAKc,14690
45
45
  reconcile/ldap_users.py,sha256=oP1CAxmgSi3zDJ3vKTPySjap6WmEX1U469FmFrov5l4,4599
46
46
  reconcile/mr_client_gateway.py,sha256=3L21YncbetuUI3HYvDAEb5JX5HO5KG2CfUyjapX3w8E,2063
@@ -111,7 +111,7 @@ reconcile/terraform_repo.py,sha256=vVJfaCV9775FGMMTHfoobaPetSlJMiQ4arNudL2pvh8,1
111
111
  reconcile/terraform_resources.py,sha256=AXO3_Ehcg3I6ao7qiKzXC4Mk6BqwMoNooXU50c2zSTA,19555
112
112
  reconcile/terraform_tgw_attachments.py,sha256=P2HivCjT5AlyODy-fu1qAK5355nDEArE8E4NQlIlF7U,18933
113
113
  reconcile/terraform_users.py,sha256=xa-UK2iTy0HRFAGfFQvxTbBaypsqx3O0XGVWIU9yIxI,10659
114
- reconcile/terraform_vpc_peerings.py,sha256=rMotDHy3Fd5QTxrOGjCJ3f2vICk3fmxrKYVCIU8p_y4,27787
114
+ reconcile/terraform_vpc_peerings.py,sha256=OMin9ica4_tpZxoDNUX72aO6IC_YgD3rfowJQeinaJY,27822
115
115
  reconcile/vault_replication.py,sha256=trtbB-jDwca822J5I_s0zlwFtlyaiAtqgbPeqp7Cggc,17714
116
116
  reconcile/vpc_peerings_validator.py,sha256=_77eu6DSy6VjTE5mhV-sOIVOGIiBvDEEDCdwwRdrgVQ,7101
117
117
  reconcile/aus/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -438,8 +438,6 @@ reconcile/gql_definitions/vault_policies/vault_policies.py,sha256=hfKhUXTqz22aBi
438
438
  reconcile/gql_definitions/vpc_peerings_validator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
439
439
  reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py,sha256=8eN4p6csq79qwIsnMzbnWRqvxRHv6AqOZJY6lsx6fFE,4451
440
440
  reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py,sha256=Hez1Oi2t2ow-G8vCQiZpi0WmSEwDFKf8RC-9uC9JcUM,1067
441
- reconcile/jenkins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
442
- reconcile/jenkins/types.py,sha256=8YJVwnwilTbBKcB46mlMztudDwd0x7rzNOI4D_lV4Fc,2958
443
441
  reconcile/ldap_groups/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
444
442
  reconcile/ldap_groups/integration.py,sha256=hx-JX9TgUJMIZoQRWO3nmQLjAPnJaQ-tNud4ky8JQdA,10622
445
443
  reconcile/ocm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -453,7 +451,7 @@ reconcile/oum/base.py,sha256=WCFdHOHXLPrJcvxVqw6HjaJthT7olC5BQqqXlD4DM6c,13552
453
451
  reconcile/oum/labelset.py,sha256=f5kDndbaIT4iNYxTRPSELTUgj_aMlzEJDPzooAkG2mE,2154
454
452
  reconcile/oum/metrics.py,sha256=S_0C-hIW4jHVl9Lltgis9q-p33fdBjADWBouQ9Emeao,1575
455
453
  reconcile/oum/models.py,sha256=teH0bJTCMTzbdbYD9CU4yXDuMr34ceLcM0KuoIPU8gI,1712
456
- reconcile/oum/providers.py,sha256=lfG6d7YV-A4Lte45EMv1Gx4A346piJ_jAkrU5AHJZ_g,1834
454
+ reconcile/oum/providers.py,sha256=M2kfN5gxOgIGJN2Hpprc-oQkMNhCcPPqy9ABETwLABk,1839
457
455
  reconcile/oum/standalone.py,sha256=EN5y1S-3DwUZYzSRqRMtf63mI2slvBHKiU9zOTjYvWM,7334
458
456
  reconcile/prometheus_rules_tester/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
459
457
  reconcile/prometheus_rules_tester/integration.py,sha256=qa1OrD1gCR1PUrHNFc2AL3J01SdBYfMOX8fHcOUh1T8,9618
@@ -608,21 +606,21 @@ reconcile/utils/filtering.py,sha256=S4PbMHuFr3ED0P2Q_ea5CAaB7FimI62B-F5YTaKrphA,
608
606
  reconcile/utils/git.py,sha256=o4p9m8jlzCJDcutl2HErvGLhL6sZ1NB4Aw3zGcQIzso,2427
609
607
  reconcile/utils/github_api.py,sha256=S1vO-hvYPzm5BIychVIHSYibMns0HBmLgS78MkPfunE,3402
610
608
  reconcile/utils/gitlab_api.py,sha256=okuBtZA3dRvIDuZOL-zAmiQqugebdA_H_AOV7luPnDk,28343
611
- reconcile/utils/gpg.py,sha256=EKG7_fdMv8BMlV5yUdPiqoTx-KrzmVSEAl2sLkaKwWI,1123
609
+ reconcile/utils/gpg.py,sha256=VCNERVnIFZtIu62VbBtM_MtokTPVQ0bsCWs2evGKLeo,1254
612
610
  reconcile/utils/gql.py,sha256=_GtzCoYIvDSS7PHXIImShs_g3k85MLlGNTJqqwhYFLQ,14703
613
611
  reconcile/utils/grouping.py,sha256=vr9SFHZ7bqmHYrvYcEZt-Er3-yQYfAAdq5sHLZVmXPY,456
614
612
  reconcile/utils/helm.py,sha256=wC1h0GylhDFeZ6hZEtYy2giAGIIQroaQhkAtURoSlI8,3893
615
613
  reconcile/utils/helpers.py,sha256=koyAtYnxsUVx-HIn6GpedcUE-ekz_VtoYDkiZ0iv8ik,1795
616
- reconcile/utils/imap_client.py,sha256=h8YDiCSCvroErhpH_-KGYI7Y2WU2Q2oSpuxDFbOkSbY,1989
614
+ reconcile/utils/imap_client.py,sha256=sJSBBCObR34JTk70raIkJYXZbmEOOyc8IDQ1CrcSO1Q,2019
617
615
  reconcile/utils/instrumented_wrappers.py,sha256=VqT4s0Bdicv224-uSeSaugtHXm-xJ3oSeBiqj0QQRiU,1942
618
616
  reconcile/utils/jenkins_api.py,sha256=jNwdtBtO8DgMW_H8XfqkQs2r4JsLovHe03t5_F3M1xg,7961
619
617
  reconcile/utils/jira_client.py,sha256=qC1mE47_WCp3pgbMn9YqBH6LJFeQKn93biH4oG0P1bc,10574
620
- reconcile/utils/jjb_client.py,sha256=p3I5Aup5NtvCrc2XS2V2GVtTqnsrPXbjQvmcHuacTHY,15196
618
+ reconcile/utils/jjb_client.py,sha256=ZwoQvrufRxyMGXo2rg2-_b5U0rJASLG398MDBQI3K20,16416
621
619
  reconcile/utils/jsonpath.py,sha256=wdxOMqR-GMpQf5vRPWRMqAF7bCiXDBkkcFfY2U4j_tk,5536
622
620
  reconcile/utils/jump_host.py,sha256=gi8vGUDgdTVwJvROvRVauFxtL0YAramhbWvG70L7AY8,5137
623
621
  reconcile/utils/keycloak.py,sha256=YWSEUGrOVqFaJUk055dKUWpLDPdDRvhcmvR-lfbmxdE,3388
624
- reconcile/utils/ldap_client.py,sha256=HxOxpqyLRbXxOARZyBNbccceWIk6gF8PQjNQmBvjF2o,2148
625
- reconcile/utils/lean_terraform_client.py,sha256=X9358loxzkhwRExTeDv_NC8Q6HNr2tJK6Lx897YtJUc,4004
622
+ reconcile/utils/ldap_client.py,sha256=EIq2A19LzEQNcNCUeJQxp161tkWV1qpj9ufU15uBefE,2216
623
+ reconcile/utils/lean_terraform_client.py,sha256=cVxx0jR3-Jiuirub_gJDf-G0FC8lNoFHdI5H6xc5TyA,4047
626
624
  reconcile/utils/make.py,sha256=QaEwucrzbl8-VHS66Wfdjfo0ubmAcvt_hZGpiGsKU50,231
627
625
  reconcile/utils/metrics.py,sha256=kiOoWO0b0mO-MDZWxyClYz9SeohQ0QU-xji0p-cSiLo,18462
628
626
  reconcile/utils/models.py,sha256=N-cOLsLbZAzVazFImue4d1pllCGd_vVcIrFKykYZXZo,4682
@@ -631,7 +629,7 @@ reconcile/utils/oc.py,sha256=c1O-qqKUNCgPrFNFCzVkgA0TCLY_vKpsaSY3XGMHpRg,65531
631
629
  reconcile/utils/oc_connection_parameters.py,sha256=-H2crz0UOVKWlNrexf04ip8Vu57rE2QZLJuurvin1_c,9705
632
630
  reconcile/utils/oc_filters.py,sha256=Tz3OwtbUaYKmxENFls5CtPVzkZDeFXknw53dJe-wbT8,1382
633
631
  reconcile/utils/oc_map.py,sha256=ougQ-Wlsa8ymoE_lPQ7g2LlpsUOsHVeRCLYW_6fjeWU,8976
634
- reconcile/utils/ocm_base_client.py,sha256=t5kxhklEqOpenXPkXiwQIk8d3D7hIUndBm5qGusS0bc,6681
632
+ reconcile/utils/ocm_base_client.py,sha256=niBN-yHiML5ljqKMOabFTaYW9iQ8JAway37BQF2NvtM,6713
635
633
  reconcile/utils/openshift_resource.py,sha256=Nbte4oCzxL27Hrt2vNs2ybV3X3pV4vGJEnErEsiA6UY,24810
636
634
  reconcile/utils/openssl.py,sha256=qdEdSmNXDgx_hhj2psEea6O12cmn3pb4GNhQJtI5l_E,399
637
635
  reconcile/utils/output.py,sha256=wvsyf8NsFTaFHNkc8to75ta8f474Y4TMO4cHyAHEePk,2209
@@ -643,7 +641,7 @@ reconcile/utils/promotion_state.py,sha256=McSgGj3oog83ThJCrMR2v8q6Xb_Pxij-HEe_Rb
643
641
  reconcile/utils/promtool.py,sha256=YnqwMAzsQVGuBZ1j9zy3UcVPFQVJgBMLzQkxhK_KFkU,3079
644
642
  reconcile/utils/quay_api.py,sha256=ZWjfjzFnIsbKRDcdAnP9tWQezclf53I7VWZJ0gbF2kE,8260
645
643
  reconcile/utils/quay_mirror.py,sha256=dpWCNv5lITwIk6Q9RkmqaQKHNk_JPy27UQEribJ7E-U,1324
646
- reconcile/utils/raw_github_api.py,sha256=2WKtE8ABYYB9UGOAh9N_kLkksBWL3320Z2_scteZddI,2805
644
+ reconcile/utils/raw_github_api.py,sha256=TlGfpitmqh_aX0V5b231TRQjuY2bAD6_FLvIA5P5a_o,3045
647
645
  reconcile/utils/repo_owners.py,sha256=c6Z-U5TkiRPvuhr_zYWvZG9HZGzoT-l-d2PJ33lGflE,6507
648
646
  reconcile/utils/rest_api_base.py,sha256=MT7tp6CQO2S5aKfVOzw_hipWg7wAGoOqkm4qurI1hEU,4342
649
647
  reconcile/utils/rhcsv2_certs.py,sha256=ZnlUlEI2k6UrljGarkm1ey0znMlQtjeZB7VEfCH1A64,2545
@@ -728,7 +726,7 @@ reconcile/utils/ocm/identity_providers.py,sha256=dKed09N8iWmn39tI_MpwgVe47x23eLs
728
726
  reconcile/utils/ocm/label_sources.py,sha256=ES_5VP4X6gsRxMFZ95WgbwE_HqqIUo_JRjHjdGYw6Ss,1846
729
727
  reconcile/utils/ocm/labels.py,sha256=CmAgaOEPiaUb4gLtKab9vNkSDJceuREPd4ApgGcIA1U,6240
730
728
  reconcile/utils/ocm/manifests.py,sha256=Q6kgOeiAwLbJY_vO_BEW2oePvbLDZcMZk20YpJJGpOA,1195
731
- reconcile/utils/ocm/ocm.py,sha256=tHRGWdpHkvrq6XakDjE1KrXNijdh-ubxKgK183fdhyQ,31378
729
+ reconcile/utils/ocm/ocm.py,sha256=1nBhA_h1SjTwCFF6aMMVRSDw0geg9vs0ul3WfnN2F_M,32748
732
730
  reconcile/utils/ocm/products.py,sha256=UtWpkAvSMCxPOulEB7aV5ZY8ej_rmErlE_HVdm9Gnjk,26021
733
731
  reconcile/utils/ocm/search_filters.py,sha256=09p4Wq1d1HGrDiinf1dmLJ46VtFhkkRCOL4V-N-zwjY,14808
734
732
  reconcile/utils/ocm/service_log.py,sha256=RG1f0MMn6joKaRCAm2xveSJCavdOPP1BVo9FXecDxaI,2018
@@ -775,7 +773,7 @@ tools/template_validation.py,sha256=Xn9X4sGFznx-rvBDnq9Kq16rfET8V3bqH1EwavsGBac,
775
773
  tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
776
774
  tools/cli_commands/container_images_report.py,sha256=8mAjCS6XR0yD7k0mfiVBlt6xbYU47q_ftdYNi5o5VKE,5566
777
775
  tools/cli_commands/erv2.py,sha256=H5RKytXP2BRYxixd-6Z3qfRJRfs2cOhtmXTgEOsn9_s,26079
778
- tools/cli_commands/gpg_encrypt.py,sha256=RHRSI_Oc_Y3nBE-_lcmJbo8UEL9IWz4xzv1PFLWWVPQ,4927
776
+ tools/cli_commands/gpg_encrypt.py,sha256=C6y0KnZxnycYOxgpDvCfAUZYGhDZjIYfZ7tnlyg3a7M,4920
779
777
  tools/cli_commands/systems_and_tools.py,sha256=n6IFdPPLpueGP8Eh3dqv2errk0ZqHWUDcan_Nrhnt9I,16747
780
778
  tools/cli_commands/cost_report/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
781
779
  tools/cli_commands/cost_report/aws.py,sha256=JtwDfhaYLfa4Uz1LR6OfSBh_3nBlb90kQq6i3MV_ims,4563
@@ -796,7 +794,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
796
794
  tools/saas_promotion_state/saas_promotion_state.py,sha256=uQv2QJAmUXP1g2GPIH30WTlvL9soY6m9lefpZEVDM5w,3965
797
795
  tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
798
796
  tools/sre_checkpoints/util.py,sha256=KcYVfa3UmJHVP_ocgrKe8NkrO5IDB9aWEDydSokPcRk,975
799
- qontract_reconcile-0.10.2.dev317.dist-info/METADATA,sha256=Sq5cR104VjHigyaBW68DsOjQJ9CEBQ80THxUjbrE7G8,24916
800
- qontract_reconcile-0.10.2.dev317.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
801
- qontract_reconcile-0.10.2.dev317.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
802
- qontract_reconcile-0.10.2.dev317.dist-info/RECORD,,
797
+ qontract_reconcile-0.10.2.dev319.dist-info/METADATA,sha256=FNa6A7BaR7ZXxqf7JG1YpJbOmrGsQFwzlwruMjRnGys,24916
798
+ qontract_reconcile-0.10.2.dev319.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
799
+ qontract_reconcile-0.10.2.dev319.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
800
+ qontract_reconcile-0.10.2.dev319.dist-info/RECORD,,
@@ -81,7 +81,7 @@ def run(
81
81
  if not print_only and config_name is not None:
82
82
  raise Exception("--config-name must works with --print-only mode")
83
83
  secret_reader = SecretReader(queries.get_secret_reader_settings())
84
- jjb: JJB = init_jjb(secret_reader, instance_name, config_name, print_only)
84
+ jjb = init_jjb(secret_reader, instance_name, config_name, print_only)
85
85
  if defer:
86
86
  defer(jjb.cleanup)
87
87
 
@@ -1,14 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  import logging
2
- from typing import (
3
- Any,
4
- cast,
5
- )
4
+ from enum import Enum
5
+ from typing import Any, cast
6
+
7
+ from pydantic import BaseModel, Field
6
8
 
7
9
  from reconcile import queries
8
- from reconcile.jenkins.types import (
9
- JenkinsWorkerFleet,
10
- SSHConnector,
11
- )
12
10
  from reconcile.utils.external_resources import get_external_resource_specs
13
11
  from reconcile.utils.jenkins_api import JenkinsApi
14
12
  from reconcile.utils.secret_reader import SecretReader
@@ -17,6 +15,75 @@ from reconcile.utils.terrascript_aws_client import TerrascriptClient as Terrascr
17
15
  QONTRACT_INTEGRATION = "jenkins-worker-fleets"
18
16
 
19
17
 
18
+ class SSHHostKeyVerificationStrategy(Enum):
19
+ MANUALLY_TRUSTED_KEY_VERIFICATION_STRATEGY = (
20
+ "manuallyTrustedKeyVerificationStrategy"
21
+ )
22
+ MANUALLY_PROVIDED_KEY_VERIFICATION_STRATEGY = (
23
+ "manuallyProvidedKeyVerificationStrategy"
24
+ )
25
+ NON_VERIFYING_KEY_VERIFICATION_STRATEGY = "nonVerifyingKeyVerificationStrategy"
26
+ KNOWN_HOSTS_FILE_KEY_VERIFICATION_STRATEGY = "knownHostsFileKeyVerificationStrategy"
27
+
28
+
29
+ class SSHConnector(BaseModel):
30
+ credentials_id: str = Field(..., alias="credentialsId")
31
+ launch_timeout_seconds: int | None = Field(None, alias="launchTimeoutSeconds")
32
+ max_num_retries: int | None = Field(None, alias="maxNumRetries")
33
+ retry_wait_time: int | None = Field(None, alias="retryWaitTime")
34
+ port: int | None = 22
35
+ jvm_options: str | None = Field(None, alias="jvmOptions")
36
+ ssh_host_key_verification_strategy: SSHHostKeyVerificationStrategy = Field(
37
+ SSHHostKeyVerificationStrategy.NON_VERIFYING_KEY_VERIFICATION_STRATEGY,
38
+ alias="sshHostKeyVerificationStrategy",
39
+ )
40
+
41
+ class Config:
42
+ use_enum_values = True
43
+
44
+
45
+ class ComputerConnector(BaseModel):
46
+ # alias name is defined by jcasc schema
47
+ ssh_connector: SSHConnector = Field(..., alias="sSHConnector")
48
+
49
+
50
+ class JenkinsWorkerFleet(BaseModel):
51
+ # following options comes form https://github.com/jenkinsci/ec2-fleet-plugin/blob/master/docs/
52
+ name: str
53
+ fleet: str
54
+ region: str
55
+ min_size: int = Field(..., alias="minSize")
56
+ max_size: int = Field(..., alias="maxSize")
57
+ computer_connector: ComputerConnector = Field(..., alias="computerConnector")
58
+ fs_root: str = Field(..., alias="fsRoot")
59
+ label_string: str = Field(..., alias="labelString")
60
+ num_executors: int = Field(2, alias="numExecutors")
61
+ idle_minutes: int = Field(30, alias="idleMinutes")
62
+ min_spare_size: int = Field(0, alias="minSpareSize")
63
+ max_total_uses: int = Field(-1, alias="maxTotalUses")
64
+ no_delay_provision: bool = Field(False, alias="noDelayProvision")
65
+ add_node_only_if_running: bool = Field(True, alias="addNodeOnlyIfRunning")
66
+ always_reconnect: bool = Field(True, alias="alwaysReconnect")
67
+ private_ip_used: bool = Field(True, alias="privateIpUsed")
68
+ restrict_usage: bool = Field(True, alias="restrictUsage")
69
+
70
+ def __lt__(self, other: JenkinsWorkerFleet) -> bool:
71
+ return self.fleet < other.fleet
72
+
73
+ def __eq__(self, other: object) -> bool:
74
+ if not isinstance(other, JenkinsWorkerFleet):
75
+ raise NotImplementedError(
76
+ "Cannot compare to non JenkinsWorkerFleet objects."
77
+ )
78
+ return self.fleet == other.fleet and self.region == other.region
79
+
80
+ def __hash__(self) -> int:
81
+ return hash(self.fleet + self.region)
82
+
83
+ def differ(self, other: JenkinsWorkerFleet) -> bool:
84
+ return self.dict() != other.dict()
85
+
86
+
20
87
  def get_current_state(jenkins: JenkinsApi) -> list[JenkinsWorkerFleet]:
21
88
  current_state = []
22
89
 
@@ -33,7 +33,7 @@ class LdapGroupMemberProvider(GroupMemberProvider):
33
33
  if len(group_ids) == 0:
34
34
  return {}
35
35
  with self.ldap_client as lc:
36
- groups_members_by_dn = lc.get_group_members(group_dn_mapping.keys())
36
+ groups_members_by_dn = lc.get_group_members(set(group_dn_mapping.keys()))
37
37
  return {
38
38
  group_dn_mapping[dn]: members
39
39
  for dn, members in groups_members_by_dn.items()
@@ -73,6 +73,7 @@ def _build_infrastructure_assume_role(
73
73
  ocm: OCM | None,
74
74
  provided_assume_role: str | None,
75
75
  ) -> dict[str, Any] | None:
76
+ assume_role: str | None = None
76
77
  if provided_assume_role:
77
78
  assume_role = provided_assume_role
78
79
  elif cluster["spec"].get("account"):
reconcile/utils/gpg.py CHANGED
@@ -8,7 +8,7 @@ from subprocess import (
8
8
  )
9
9
 
10
10
 
11
- def gpg_encrypt(content, public_gpg_key):
11
+ def gpg_encrypt(content: str, public_gpg_key: str) -> str:
12
12
  public_gpg_key_dec = base64.b64decode(public_gpg_key)
13
13
 
14
14
  with tempfile.TemporaryDirectory() as gnupg_home_dir:
@@ -22,6 +22,8 @@ def gpg_encrypt(content, public_gpg_key):
22
22
  )
23
23
  out = proc.stdout.decode("utf-8")
24
24
  match = re.search(r"<\S+>", out)
25
+ if not match:
26
+ raise ValueError("No recipient found in GPG import output")
25
27
  recipient = match.group(0)[1:-1]
26
28
  # encrypt content
27
29
  proc = run(
@@ -41,5 +43,5 @@ def gpg_encrypt(content, public_gpg_key):
41
43
  stderr=STDOUT,
42
44
  check=True,
43
45
  )
44
- out = proc.stdout
45
- return out.decode("utf-8")
46
+ encrypted_out = proc.stdout
47
+ return encrypted_out.decode("utf-8")
@@ -21,7 +21,7 @@ class ImapClient:
21
21
  self._server.login(self.user, self.password)
22
22
  return self
23
23
 
24
- def __exit__(self, *args, **kwargs) -> None:
24
+ def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
25
25
  if self._server:
26
26
  self._server.logout()
27
27
 
@@ -8,6 +8,7 @@ import shutil
8
8
  import subprocess
9
9
  import tempfile
10
10
  import xml.etree.ElementTree as ET
11
+ from collections.abc import Iterable, Mapping
11
12
  from os import path
12
13
  from subprocess import (
13
14
  PIPE,
@@ -25,6 +26,8 @@ from sretoolbox.utils import retry
25
26
 
26
27
  from reconcile.utils import throughput
27
28
  from reconcile.utils.helpers import toggle_logger
29
+ from reconcile.utils.secret_reader import SecretReaderBase
30
+ from reconcile.utils.state import State
28
31
  from reconcile.utils.vcs import GITHUB_BASE_URL
29
32
 
30
33
  JJB_INI = "[jenkins]\nurl = https://JENKINS_URL"
@@ -33,14 +36,22 @@ JJB_INI = "[jenkins]\nurl = https://JENKINS_URL"
33
36
  class JJB:
34
37
  """Wrapper around Jenkins Jobs"""
35
38
 
36
- def __init__(self, configs, ssl_verify=True, secret_reader=None, print_only=False):
39
+ def __init__(
40
+ self,
41
+ configs: list[dict[str, Any]],
42
+ ssl_verify: bool = True,
43
+ secret_reader: SecretReaderBase | None = None,
44
+ print_only: bool = False,
45
+ ) -> None:
37
46
  self.print_only = print_only
38
47
  self.secret_reader = secret_reader
48
+ if not self.print_only and self.secret_reader is None:
49
+ raise ValueError("secret_reader must be provided if print_only is False")
39
50
  self.collect_configs(configs)
40
51
  self.modify_logger()
41
52
  self.python_https_verify = str(int(ssl_verify))
42
53
 
43
- def collect_configs(self, configs):
54
+ def collect_configs(self, configs: list[dict[str, Any]]) -> None:
44
55
  instances = {
45
56
  c["instance"]["name"]: {
46
57
  "serverUrl": c["instance"]["serverUrl"],
@@ -57,7 +68,7 @@ class JJB:
57
68
  server_url = data["serverUrl"]
58
69
  wd = tempfile.mkdtemp()
59
70
  ini = JJB_INI
60
- if not self.print_only:
71
+ if not self.print_only and self.secret_reader:
61
72
  ini = self.secret_reader.read(token)
62
73
  ini = ini.replace('"', "")
63
74
  ini = ini.replace("false", "False")
@@ -92,7 +103,7 @@ class JJB:
92
103
  self.instance_urls = instance_urls
93
104
  self.working_dirs = working_dirs
94
105
 
95
- def overwrite_configs(self, configs):
106
+ def overwrite_configs(self, configs: Mapping[str, str] | State) -> None:
96
107
  """This function will override the existing
97
108
  config files in the working directories with
98
109
  the supplied configs"""
@@ -101,12 +112,12 @@ class JJB:
101
112
  with open(config_path, "w", encoding="locale") as f:
102
113
  f.write(configs[name])
103
114
 
104
- def sort(self, configs):
115
+ def sort(self, configs: list[dict[str, Any]]) -> None:
105
116
  configs.sort(key=self.sort_by_name)
106
- configs.sort(key=self.sort_by_type)
117
+ configs.sort(key=lambda x: self.sort_by_type(x) or 0)
107
118
 
108
119
  @staticmethod
109
- def sort_by_type(config):
120
+ def sort_by_type(config: Mapping[str, Any]) -> int:
110
121
  if config["type"] == "defaults":
111
122
  return 0
112
123
  if config["type"] == "global-defaults":
@@ -123,12 +134,13 @@ class JJB:
123
134
  return 40
124
135
  if config["type"] == "jobs":
125
136
  return 50
137
+ return 100
126
138
 
127
139
  @staticmethod
128
- def sort_by_name(config):
140
+ def sort_by_name(config: Mapping[str, Any]) -> str:
129
141
  return config["name"]
130
142
 
131
- def get_configs(self):
143
+ def get_configs(self) -> dict[str, str]:
132
144
  """This function gets the configs from the
133
145
  working directories"""
134
146
  configs = {}
@@ -139,7 +151,7 @@ class JJB:
139
151
 
140
152
  return configs
141
153
 
142
- def generate(self, io_dir, fetch_state):
154
+ def generate(self, io_dir: str, fetch_state: str) -> None:
143
155
  """
144
156
  Generates job definitions from JJB configs
145
157
 
@@ -163,7 +175,7 @@ class JJB:
163
175
  self.execute(args)
164
176
  throughput.change_files_ownership(io_dir)
165
177
 
166
- def print_diffs(self, io_dir, instance_name=None):
178
+ def print_diffs(self, io_dir: str, instance_name: str | None = None) -> None:
167
179
  """Print the diffs between the current and
168
180
  the desired job definitions"""
169
181
  current_path = path.join(io_dir, "jjb", "current")
@@ -179,7 +191,7 @@ class JJB:
179
191
  self.print_diff(delete, current_path, "delete")
180
192
  self.print_diff(common, desired_path, "update")
181
193
 
182
- def print_diff(self, files, replace_path, action):
194
+ def print_diff(self, files: Iterable[str], replace_path: str, action: str) -> None:
183
195
  for f in files:
184
196
  if action == "update":
185
197
  ft = self.toggle_cd(f)
@@ -210,11 +222,16 @@ class JJB:
210
222
  ]
211
223
  logging.debug("DIFF:\n" + "".join(diff))
212
224
 
213
- def compare_files(self, from_files, subtract_files, in_op=False):
225
+ def compare_files(
226
+ self,
227
+ from_files: Iterable[str],
228
+ subtract_files: Iterable[str],
229
+ in_op: bool = False,
230
+ ) -> list[str]:
214
231
  return [f for f in from_files if (self.toggle_cd(f) in subtract_files) is in_op]
215
232
 
216
233
  @staticmethod
217
- def get_files(search_path, instance_name=None):
234
+ def get_files(search_path: str, instance_name: str | None = None) -> list[str]:
218
235
  if instance_name is not None:
219
236
  search_path = path.join(search_path, instance_name)
220
237
  return [
@@ -222,7 +239,7 @@ class JJB:
222
239
  ]
223
240
 
224
241
  @staticmethod
225
- def toggle_cd(file_name):
242
+ def toggle_cd(file_name: str) -> str:
226
243
  if "desired" in file_name:
227
244
  return file_name.replace("desired", "current")
228
245
  return file_name.replace("current", "desired")
@@ -248,28 +265,28 @@ class JJB:
248
265
  raise
249
266
 
250
267
  @staticmethod
251
- def get_jjb(args):
268
+ def get_jjb(args: Iterable[str]) -> Any:
252
269
  from jenkins_jobs.cli.entry import JenkinsJobs # noqa: PLC0415
253
270
 
254
271
  return JenkinsJobs(args)
255
272
 
256
- def execute(self, args):
273
+ def execute(self, args: Iterable[str]) -> None:
257
274
  jjb = self.get_jjb(args)
258
275
  with toggle_logger():
259
276
  jjb.execute()
260
277
 
261
- def modify_logger(self):
278
+ def modify_logger(self) -> None:
262
279
  yaml.warnings({"YAMLLoadWarning": False})
263
280
  formatter = logging.Formatter("%(levelname)s: %(message)s")
264
281
  logger = logging.getLogger()
265
282
  logger.handlers[0].setFormatter(formatter)
266
283
 
267
- def cleanup(self):
284
+ def cleanup(self) -> None:
268
285
  for wd in self.working_dirs.values():
269
286
  shutil.rmtree(wd)
270
287
 
271
288
  @retry(exceptions=(JenkinsJobsException))
272
- def get_jobs(self, wd, name):
289
+ def get_jobs(self, wd: str, name: str) -> list[dict[str, Any]]:
273
290
  ini_path = f"{wd}/{name}.ini"
274
291
  config_path = f"{wd}/config.yaml"
275
292
 
@@ -283,8 +300,8 @@ class JJB:
283
300
 
284
301
  return jobs
285
302
 
286
- def get_job_webhooks_data(self):
287
- job_webhooks_data = {}
303
+ def get_job_webhooks_data(self) -> dict[str, list[dict[str, Any]]]:
304
+ job_webhooks_data: dict[str, list[dict[str, Any]]] = {}
288
305
  for name, wd in self.working_dirs.items():
289
306
  jobs = self.get_jobs(wd, name)
290
307
 
@@ -313,7 +330,7 @@ class JJB:
313
330
 
314
331
  return job_webhooks_data
315
332
 
316
- def get_repos(self):
333
+ def get_repos(self) -> set[str]:
317
334
  repos = set()
318
335
  for name, wd in self.working_dirs.items():
319
336
  jobs = self.get_jobs(wd, name)
@@ -325,7 +342,7 @@ class JJB:
325
342
  logging.debug(f"missing github url: {job_name}")
326
343
  return repos
327
344
 
328
- def get_admins(self):
345
+ def get_admins(self) -> set[str]:
329
346
  admins = set()
330
347
  for name, wd in self.working_dirs.items():
331
348
  jobs = self.get_jobs(wd, name)
@@ -340,15 +357,17 @@ class JJB:
340
357
  return admins
341
358
 
342
359
  @staticmethod
343
- def get_repo_url(job):
360
+ def get_repo_url(job: Mapping[str, Any]) -> str:
344
361
  repo_url_raw = job["properties"][0]["github"]["url"]
345
362
  return repo_url_raw.strip("/").replace(".git", "")
346
363
 
347
364
  @staticmethod
348
- def get_ref(job: dict) -> str:
365
+ def get_ref(job: Mapping[str, Any]) -> str:
349
366
  return job["scm"][0]["git"]["branches"][0]
350
367
 
351
- def get_all_jobs(self, job_types=None, instance_name=None) -> dict[str, list[dict]]:
368
+ def get_all_jobs(
369
+ self, job_types: Iterable[str] | None = None, instance_name: str | None = None
370
+ ) -> dict[str, list[dict[str, Any]]]:
352
371
  if job_types is None:
353
372
  job_types = []
354
373
  all_jobs: dict[str, list[dict]] = {}
@@ -366,8 +385,8 @@ class JJB:
366
385
 
367
386
  return all_jobs
368
387
 
369
- def print_jobs(self, job_name=None):
370
- all_jobs = {}
388
+ def print_jobs(self, job_name: str | None = None) -> None:
389
+ all_jobs: dict[str, list[dict[str, Any]]] = {}
371
390
  found = False
372
391
  for name, wd in self.working_dirs.items():
373
392
  logging.debug(f"getting jobs from {name}")
@@ -394,7 +413,7 @@ class JJB:
394
413
  raise ValueError(f"job with {job_type=} and {repo_url=} not found")
395
414
 
396
415
  @staticmethod
397
- def get_trigger_phrases_regex(job: dict) -> str | None:
416
+ def get_trigger_phrases_regex(job: Mapping[str, Any]) -> str | None:
398
417
  for trigger in job["triggers"]:
399
418
  if "gitlab" in trigger:
400
419
  return trigger["gitlab"].get("note-regex")
@@ -403,7 +422,7 @@ class JJB:
403
422
  return None
404
423
 
405
424
  @staticmethod
406
- def get_gitlab_webhook_trigger(job: dict) -> list[str]:
425
+ def get_gitlab_webhook_trigger(job: Mapping[str, Any]) -> list[str]:
407
426
  gitlab_triggers = job["triggers"][0]["gitlab"]
408
427
  # pr-check job should be triggered by merge request events
409
428
  # and certain comments: [test]|/retest|/lgtm|/lgtm cancel|/hold|/hold cancel
@@ -1,5 +1,6 @@
1
1
  from collections import defaultdict
2
2
  from collections.abc import Iterable
3
+ from typing import Any, Self
3
4
 
4
5
  from ldap3 import (
5
6
  ALL,
@@ -17,15 +18,15 @@ class LdapClient:
17
18
  appropriately.
18
19
  """
19
20
 
20
- def __init__(self, base_dn: str, connection: Connection):
21
+ def __init__(self, base_dn: str, connection: Connection) -> None:
21
22
  self.base_dn = base_dn
22
23
  self.connection = connection
23
24
 
24
- def __enter__(self):
25
+ def __enter__(self) -> Self:
25
26
  self.connection.bind()
26
27
  return self
27
28
 
28
- def __exit__(self, exc_type, exc_val, exc_tb):
29
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
29
30
  self.connection.unbind()
30
31
 
31
32
  def get_users(self, uids: Iterable[str]) -> set[str]:
@@ -6,7 +6,9 @@ from collections.abc import Mapping
6
6
  from typing import Any
7
7
 
8
8
 
9
- def state_rm_access_key(working_dirs, account, user):
9
+ def state_rm_access_key(
10
+ working_dirs: Mapping[str, str], account: str, user: str
11
+ ) -> bool:
10
12
  wd = working_dirs[account]
11
13
  init_result = subprocess.run(["terraform", "init"], check=False, cwd=wd)
12
14
  if init_result.returncode != 0:
@@ -21,7 +21,7 @@ from reconcile.utils.ocm_base_client import (
21
21
  from reconcile.utils.secret_reader import SecretReader
22
22
 
23
23
  if TYPE_CHECKING:
24
- from collections.abc import Mapping
24
+ from collections.abc import Iterable, Mapping, MutableMapping
25
25
 
26
26
  from reconcile.ocm.types import OCMSpec
27
27
 
@@ -83,13 +83,13 @@ class OCM:
83
83
 
84
84
  def __init__(
85
85
  self,
86
- name,
87
- org_id,
86
+ name: str,
87
+ org_id: str,
88
88
  ocm_env: str,
89
89
  ocm_client: OCMBaseClient,
90
- init_provision_shards=False,
91
- init_addons=False,
92
- init_version_gates=False,
90
+ init_provision_shards: bool = False,
91
+ init_addons: bool = False,
92
+ init_version_gates: bool = False,
93
93
  product_portfolio: OCMProductPortfolio | None = None,
94
94
  ):
95
95
  """Initiates access token and gets clusters information."""
@@ -130,7 +130,7 @@ class OCM:
130
130
  and cluster["product"]["id"] in self.product_portfolio.product_names
131
131
  )
132
132
 
133
- def _init_clusters(self, init_provision_shards: bool):
133
+ def _init_clusters(self, init_provision_shards: bool) -> None:
134
134
  api = f"{CS_API_BASE}/v1/clusters"
135
135
  product_csv = ",".join([f"'{p}'" for p in self.product_portfolio.product_names])
136
136
  params = {
@@ -168,19 +168,19 @@ class OCM:
168
168
  spec = impl.get_ocm_spec(self.ocm_api, cluster, init_provision_shards)
169
169
  return spec
170
170
 
171
- def create_cluster(self, name: str, cluster: OCMSpec, dry_run: bool):
171
+ def create_cluster(self, name: str, cluster: OCMSpec, dry_run: bool) -> None:
172
172
  impl = self.get_product_impl(cluster.spec.product, cluster.spec.hypershift)
173
173
  impl.create_cluster(self.ocm_api, self.org_id, name, cluster, dry_run)
174
174
 
175
175
  def update_cluster(
176
- self, cluster_name: str, update_spec: Mapping[str, Any], dry_run=False
177
- ):
176
+ self, cluster_name: str, update_spec: Mapping[str, Any], dry_run: bool = False
177
+ ) -> None:
178
178
  cluster = self.clusters[cluster_name]
179
179
  cluster_id = self.cluster_ids[cluster_name]
180
180
  impl = self.get_product_impl(cluster.spec.product, cluster.spec.hypershift)
181
181
  impl.update_cluster(self.ocm_api, cluster_id, update_spec, dry_run)
182
182
 
183
- def get_group_if_exists(self, cluster, group_id):
183
+ def get_group_if_exists(self, cluster: str, group_id: str) -> dict[str, Any] | None:
184
184
  """Returns a list of users in a group in a cluster.
185
185
  If the group does not exist, None will be returned.
186
186
 
@@ -202,7 +202,7 @@ class OCM:
202
202
  users = self._get_json(api).get("items", [])
203
203
  return {"users": [u["id"] for u in users]}
204
204
 
205
- def add_user_to_group(self, cluster, group_id, user):
205
+ def add_user_to_group(self, cluster: str, group_id: str, user: str) -> None:
206
206
  """
207
207
  Adds a user to a group in a cluster.
208
208
 
@@ -218,7 +218,7 @@ class OCM:
218
218
  api = f"{CS_API_BASE}/v1/clusters/{cluster_id}/" + f"groups/{group_id}/users"
219
219
  self._post(api, {"id": user})
220
220
 
221
- def del_user_from_group(self, cluster, group_id, user_id):
221
+ def del_user_from_group(self, cluster: str, group_id: str, user_id: str) -> None:
222
222
  """Deletes a user from a group in a cluster.
223
223
 
224
224
  :param cluster: cluster name
@@ -250,7 +250,9 @@ class OCM:
250
250
  switch_role_link = role_grants[0][-1]
251
251
  return awsh.get_account_uid_from_role_link(switch_role_link)
252
252
 
253
- def get_aws_infrastructure_access_role_grants(self, cluster):
253
+ def get_aws_infrastructure_access_role_grants(
254
+ self, cluster: str
255
+ ) -> list[tuple[str, str, str, str]]:
254
256
  """Returns a list of AWS users (ARN, access level)
255
257
  who have AWS infrastructure access in a cluster.
256
258
 
@@ -272,8 +274,8 @@ class OCM:
272
274
  ]
273
275
 
274
276
  def get_aws_infrastructure_access_terraform_assume_role(
275
- self, cluster, tf_account_id, tf_user
276
- ):
277
+ self, cluster: str, tf_account_id: str, tf_user: str | None
278
+ ) -> str | None:
277
279
  role_grants = self.get_aws_infrastructure_access_role_grants(cluster)
278
280
  user_arn = f"arn:aws:iam::{tf_account_id}:user/{tf_user}"
279
281
  for arn, role_id, _, console_url in role_grants:
@@ -288,9 +290,11 @@ class OCM:
288
290
  role_name = role.replace("roleName=", "")
289
291
  return f"arn:aws:iam::{role_account_id}:role/{role_name}"
290
292
 
293
+ return None
294
+
291
295
  def add_user_to_aws_infrastructure_access_role_grants(
292
- self, cluster, user_arn, access_level
293
- ):
296
+ self, cluster: str, user_arn: str, access_level: str
297
+ ) -> None:
294
298
  """
295
299
  Adds a user to AWS infrastructure access in a cluster.
296
300
 
@@ -310,8 +314,8 @@ class OCM:
310
314
  self._post(api, {"user_arn": user_arn, "role": {"id": access_level}})
311
315
 
312
316
  def del_user_from_aws_infrastructure_access_role_grants(
313
- self, cluster, user_arn, access_level
314
- ):
317
+ self, cluster: str, user_arn: str, access_level: str
318
+ ) -> None:
315
319
  """
316
320
  Deletes a user from AWS infrastructure access in a cluster.
317
321
 
@@ -375,7 +379,9 @@ class OCM:
375
379
 
376
380
  return results
377
381
 
378
- def create_external_configuration_label(self, cluster, label):
382
+ def create_external_configuration_label(
383
+ self, cluster: str, label: dict[str, str]
384
+ ) -> None:
379
385
  """Creates a new External Configuration label
380
386
 
381
387
  :param cluster: cluster name
@@ -390,7 +396,9 @@ class OCM:
390
396
  )
391
397
  self._post(api, label)
392
398
 
393
- def delete_external_configuration_label(self, cluster, label):
399
+ def delete_external_configuration_label(
400
+ self, cluster: str, label: Mapping[str, str]
401
+ ) -> None:
394
402
  """Deletes an existing External Configuration label
395
403
 
396
404
  :param cluster: cluster name
@@ -414,14 +422,9 @@ class OCM:
414
422
  )
415
423
  self._delete(api)
416
424
 
417
- def get_machine_pools(self, cluster):
418
- """Returns a list of details of Machine Pools
419
-
420
- :param cluster: cluster name
421
-
422
- :type cluster: string
423
- """
424
- results = []
425
+ def get_machine_pools(self, cluster: str) -> list[dict[str, Any]]:
426
+ """Returns a list of details of Machine Pools"""
427
+ results: list[dict[str, Any]] = []
425
428
  cluster_id = self.cluster_ids.get(cluster)
426
429
  if not cluster_id:
427
430
  return results
@@ -436,7 +439,7 @@ class OCM:
436
439
 
437
440
  return results
438
441
 
439
- def create_machine_pool(self, cluster, spec):
442
+ def create_machine_pool(self, cluster: str, spec: Mapping[str, Any]) -> None:
440
443
  """Creates a new Machine Pool
441
444
 
442
445
  :param cluster: cluster name
@@ -449,7 +452,7 @@ class OCM:
449
452
  api = f"{CS_API_BASE}/v1/clusters/{cluster_id}/machine_pools"
450
453
  self._post(api, spec)
451
454
 
452
- def update_machine_pool(self, cluster, spec):
455
+ def update_machine_pool(self, cluster: str, spec: MutableMapping[str, Any]) -> None:
453
456
  """Updates an existing Machine Pool
454
457
 
455
458
  :param cluster: cluster name
@@ -460,7 +463,7 @@ class OCM:
460
463
  """
461
464
  cluster_id = self.cluster_ids[cluster]
462
465
  machine_pool_id = spec["id"]
463
- labels = spec.get("labels", {})
466
+ labels: dict[str, str] = spec.get("labels", {})
464
467
  spec["labels"] = labels
465
468
  api = (
466
469
  f"{CS_API_BASE}/v1/clusters/{cluster_id}/machine_pools/"
@@ -468,7 +471,7 @@ class OCM:
468
471
  )
469
472
  self._patch(api, spec)
470
473
 
471
- def delete_machine_pool(self, cluster, spec):
474
+ def delete_machine_pool(self, cluster: str, spec: Mapping[str, Any]) -> None:
472
475
  """Deletes an existing Machine Pool
473
476
 
474
477
  :param cluster: cluster name
@@ -485,21 +488,21 @@ class OCM:
485
488
  )
486
489
  self._delete(api)
487
490
 
488
- def get_node_pools(self, cluster):
491
+ def get_node_pools(self, cluster: str) -> list[dict[str, Any]]:
489
492
  """Returns a list of details of Node Pools
490
493
 
491
494
  :param cluster: cluster name
492
495
 
493
496
  :type cluster: string
494
497
  """
495
- results = []
498
+ results: list[dict[str, Any]] = []
496
499
  cluster_id = self.cluster_ids.get(cluster)
497
500
  if not cluster_id:
498
501
  return results
499
502
 
500
503
  return get_node_pools(self._ocm_client, cluster_id)
501
504
 
502
- def delete_node_pool(self, cluster, spec):
505
+ def delete_node_pool(self, cluster: str, spec: Mapping[str, Any]) -> None:
503
506
  """Deletes an existing Node Pool
504
507
 
505
508
  :param cluster: cluster name
@@ -513,7 +516,7 @@ class OCM:
513
516
  api = f"{CS_API_BASE}/v1/clusters/{cluster_id}/node_pools/" + f"{node_pool_id}"
514
517
  self._delete(api)
515
518
 
516
- def create_node_pool(self, cluster, spec):
519
+ def create_node_pool(self, cluster: str, spec: Mapping[str, Any]) -> None:
517
520
  """Creates a new Node Pool
518
521
 
519
522
  :param cluster: cluster name
@@ -526,7 +529,7 @@ class OCM:
526
529
  api = f"{CS_API_BASE}/v1/clusters/{cluster_id}/node_pools"
527
530
  self._post(api, spec)
528
531
 
529
- def update_node_pool(self, cluster, spec):
532
+ def update_node_pool(self, cluster: str, spec: MutableMapping[str, Any]) -> None:
530
533
  """Updates an existing Node Pool
531
534
 
532
535
  :param cluster: cluster name
@@ -537,19 +540,19 @@ class OCM:
537
540
  """
538
541
  cluster_id = self.cluster_ids[cluster]
539
542
  node_pool_id = spec["id"]
540
- labels = spec.get("labels", {})
543
+ labels: dict[str, str] = spec.get("labels", {})
541
544
  spec["labels"] = labels
542
545
  api = f"{CS_API_BASE}/v1/clusters/{cluster_id}/node_pools/" + f"{node_pool_id}"
543
546
  self._patch(api, spec)
544
547
 
545
- def get_additional_routers(self, cluster):
548
+ def get_additional_routers(self, cluster: str) -> list[dict[str, Any]]:
546
549
  """Returns a list of Additional Application Routers
547
550
 
548
551
  :param cluster: cluster name
549
552
 
550
553
  :type cluster: string
551
554
  """
552
- results = []
555
+ results: list[dict[str, Any]] = []
553
556
  cluster_id = self.cluster_ids.get(cluster)
554
557
  if not cluster_id:
555
558
  return results
@@ -567,7 +570,7 @@ class OCM:
567
570
 
568
571
  return results
569
572
 
570
- def create_additional_router(self, cluster, spec):
573
+ def create_additional_router(self, cluster: str, spec: Mapping[str, Any]) -> None:
571
574
  """Creates a new Additional Application Router
572
575
 
573
576
  :param cluster: cluster name
@@ -580,7 +583,7 @@ class OCM:
580
583
  api = f"{CS_API_BASE}/v1/clusters/{cluster_id}/ingresses"
581
584
  self._post(api, spec)
582
585
 
583
- def delete_additional_router(self, cluster, spec):
586
+ def delete_additional_router(self, cluster: str, spec: Mapping[str, Any]) -> None:
584
587
  """Deletes an existing Additional Application Router
585
588
 
586
589
  :param cluster: cluster name
@@ -594,19 +597,19 @@ class OCM:
594
597
  api = f"{CS_API_BASE}/v1/clusters/{cluster_id}/" + f"ingresses/{router_id}"
595
598
  self._delete(api)
596
599
 
597
- def _init_addons(self):
600
+ def _init_addons(self) -> None:
598
601
  """Returns a list of Addons"""
599
602
  api = f"{CS_API_BASE}/v1/addons"
600
603
  self.addons = self._get_json(api).get("items", [])
601
604
 
602
- def _init_version_gates(self):
605
+ def _init_version_gates(self) -> None:
603
606
  """Returns a list of version gates"""
604
607
  if self.version_gates:
605
608
  return
606
609
  api = f"{CS_API_BASE}/v1/version_gates"
607
610
  self.version_gates = self._get_json(api).get("items", [])
608
611
 
609
- def get_addon(self, id):
612
+ def get_addon(self, id: str) -> dict[str, Any] | None:
610
613
  for addon in self.addons:
611
614
  addon_id = addon["id"]
612
615
  if id == addon_id:
@@ -652,7 +655,7 @@ class OCM:
652
655
 
653
656
  return results
654
657
 
655
- def install_addon(self, cluster, spec):
658
+ def install_addon(self, cluster: str, spec: MutableMapping[str, Any]) -> None:
656
659
  """Installs an addon on a cluster
657
660
 
658
661
  :param cluster: cluster name
@@ -714,21 +717,28 @@ class OCM:
714
717
  return ret_items
715
718
  return responses[0]
716
719
 
717
- def _post(self, api, data=None, params=None):
720
+ def _post(
721
+ self,
722
+ api: str,
723
+ data: Mapping[str, Any] | None = None,
724
+ params: Mapping[str, str] | None = None,
725
+ ) -> Any:
718
726
  return self._ocm_client.post(
719
727
  api_path=api,
720
728
  data=data,
721
729
  params=params,
722
730
  )
723
731
 
724
- def _patch(self, api, data, params=None):
732
+ def _patch(
733
+ self, api: str, data: Mapping[str, Any], params: Mapping[str, str] | None = None
734
+ ) -> None:
725
735
  return self._ocm_client.patch(
726
736
  api_path=api,
727
737
  data=data,
728
738
  params=params,
729
739
  )
730
740
 
731
- def _delete(self, api):
741
+ def _delete(self, api: str) -> None:
732
742
  return self._ocm_client.delete(
733
743
  api_path=api,
734
744
  )
@@ -762,14 +772,14 @@ class OCMMap:
762
772
 
763
773
  def __init__(
764
774
  self,
765
- clusters=None,
766
- namespaces=None,
767
- ocms=None,
768
- integration="",
769
- settings=None,
770
- init_provision_shards=False,
771
- init_addons=False,
772
- init_version_gates=False,
775
+ clusters: Iterable[Mapping[str, Any]] | None = None,
776
+ namespaces: Iterable[Mapping[str, Any]] | None = None,
777
+ ocms: Iterable[Mapping[str, Any]] | None = None,
778
+ integration: str = "",
779
+ settings: Mapping[str, Any] | None = None,
780
+ init_provision_shards: bool = False,
781
+ init_addons: bool = False,
782
+ init_version_gates: bool = False,
773
783
  product_portfolio: OCMProductPortfolio | None = None,
774
784
  ) -> None:
775
785
  """Initiates OCM instances for each OCM referenced in a cluster."""
@@ -817,12 +827,12 @@ class OCMMap:
817
827
 
818
828
  def init_ocm_client_from_cluster(
819
829
  self,
820
- cluster_info,
821
- init_provision_shards,
822
- init_addons,
823
- init_version_gates,
830
+ cluster_info: Mapping[str, Any],
831
+ init_provision_shards: bool,
832
+ init_addons: bool,
833
+ init_version_gates: bool,
824
834
  product_portfolio: OCMProductPortfolio | None = None,
825
- ):
835
+ ) -> None:
826
836
  if self.cluster_disabled(cluster_info):
827
837
  return
828
838
  cluster_name = cluster_info["name"]
@@ -842,12 +852,12 @@ class OCMMap:
842
852
 
843
853
  def init_ocm_client(
844
854
  self,
845
- ocm_info,
846
- init_provision_shards,
847
- init_addons,
848
- init_version_gates,
855
+ ocm_info: Mapping[str, Any],
856
+ init_provision_shards: bool,
857
+ init_addons: bool,
858
+ init_version_gates: bool,
849
859
  product_portfolio: OCMProductPortfolio | None = None,
850
- ):
860
+ ) -> None:
851
861
  """
852
862
  Initiate OCM client.
853
863
  Gets the OCM information and initiates an OCM client.
@@ -901,7 +911,7 @@ class OCMMap:
901
911
  """Get list of OCM instance names initiated in the OCM map."""
902
912
  return list(self.ocm_map.keys())
903
913
 
904
- def cluster_disabled(self, cluster_info):
914
+ def cluster_disabled(self, cluster_info: Mapping[str, Any]) -> bool:
905
915
  """
906
916
  Checks if the calling integration is disabled in this cluster.
907
917
 
@@ -918,7 +928,7 @@ class OCMMap:
918
928
 
919
929
  return False
920
930
 
921
- def get(self, cluster) -> OCM:
931
+ def get(self, cluster: str) -> OCM:
922
932
  """
923
933
  Gets an OCM instance by cluster.
924
934
 
@@ -49,7 +49,7 @@ class OCMBaseClient:
49
49
  self._init_request_headers()
50
50
 
51
51
  @retry()
52
- def _init_access_token(self):
52
+ def _init_access_token(self) -> None:
53
53
  data = {
54
54
  "grant_type": "client_credentials",
55
55
  "client_id": self._access_token_client_id,
@@ -61,7 +61,7 @@ class OCMBaseClient:
61
61
  r.raise_for_status()
62
62
  self._access_token = r.json().get("access_token")
63
63
 
64
- def _init_request_headers(self):
64
+ def _init_request_headers(self) -> None:
65
65
  self._session.headers.update({
66
66
  "Authorization": f"Bearer {self._access_token}",
67
67
  "accept": "application/json",
@@ -130,7 +130,7 @@ class OCMBaseClient:
130
130
  api_path: str,
131
131
  data: Mapping[str, Any],
132
132
  params: Mapping[str, str] | None = None,
133
- ):
133
+ ) -> None:
134
134
  ocm_request.labels(verb="PATCH", client_id=self._access_token_client_id).inc()
135
135
  r = self._session.patch(
136
136
  f"{self._url}{api_path}",
@@ -144,7 +144,7 @@ class OCMBaseClient:
144
144
  logging.error(r.text)
145
145
  raise e
146
146
 
147
- def delete(self, api_path: str):
147
+ def delete(self, api_path: str) -> None:
148
148
  ocm_request.labels(verb="DELETE", client_id=self._access_token_client_id).inc()
149
149
  r = self._session.delete(f"{self._url}{api_path}", timeout=REQUEST_TIMEOUT_SEC)
150
150
  try:
@@ -1,8 +1,11 @@
1
1
  import os
2
+ from typing import Any
2
3
 
3
4
  import requests
4
5
  from sretoolbox.utils import retry
5
6
 
7
+ Headers = dict[str, str | bytes | None]
8
+
6
9
 
7
10
  class RawGithubApi:
8
11
  """
@@ -18,10 +21,10 @@ class RawGithubApi:
18
21
  "application/vnd.github.dazzler-preview+json"
19
22
  }
20
23
 
21
- def __init__(self, password):
24
+ def __init__(self, password: str) -> None:
22
25
  self.password = password
23
26
 
24
- def headers(self, headers=None):
27
+ def headers(self, headers: Headers | None = None) -> Headers:
25
28
  if headers is None:
26
29
  headers = {}
27
30
  new_headers = headers.copy()
@@ -29,13 +32,13 @@ class RawGithubApi:
29
32
  new_headers["Authorization"] = "token %s" % (self.password,)
30
33
  return new_headers
31
34
 
32
- def patch(self, url):
35
+ def patch(self, url: str) -> requests.Response:
33
36
  res = requests.patch(url, headers=self.headers(), timeout=60)
34
37
  res.raise_for_status()
35
38
  return res
36
39
 
37
40
  @retry()
38
- def query(self, url, headers=None):
41
+ def query(self, url: str, headers: Headers | None = None) -> Any:
39
42
  if headers is None:
40
43
  headers = {}
41
44
  h = self.headers(headers)
@@ -64,7 +67,7 @@ class RawGithubApi:
64
67
 
65
68
  return result
66
69
 
67
- def org_invitations(self, org):
70
+ def org_invitations(self, org: str) -> list[str]:
68
71
  invitations = self.query(f"/orgs/{org}/invitations")
69
72
 
70
73
  return [
@@ -73,7 +76,7 @@ class RawGithubApi:
73
76
  if login is not None
74
77
  ]
75
78
 
76
- def team_invitations(self, org_id, team_id):
79
+ def team_invitations(self, org_id: str, team_id: str) -> list[str]:
77
80
  invitations = self.query(f"/organizations/{org_id}/team/{team_id}/invitations")
78
81
 
79
82
  return [
@@ -82,10 +85,10 @@ class RawGithubApi:
82
85
  if login is not None
83
86
  ]
84
87
 
85
- def repo_invitations(self):
88
+ def repo_invitations(self) -> list[dict[str, Any]]:
86
89
  return self.query("/user/repository_invitations")
87
90
 
88
- def accept_repo_invitation(self, invitation_id):
91
+ def accept_repo_invitation(self, invitation_id: int) -> None:
89
92
  url = self.BASE_URL + f"/user/repository_invitations/{invitation_id}"
90
93
  res = self.patch(url)
91
94
  res.raise_for_status()
@@ -114,7 +114,7 @@ class GPGEncryptCommand:
114
114
  f"No argument given which defines how to fetch the secret {self._command_data}"
115
115
  )
116
116
 
117
- def _get_gpg_key(self) -> str | None:
117
+ def _get_gpg_key(self) -> str:
118
118
  target_user = self._command_data.target_user
119
119
  users = queries.get_users_by(
120
120
  refs=False,
File without changes
@@ -1,77 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from enum import Enum
4
-
5
- from pydantic import (
6
- BaseModel,
7
- Field,
8
- )
9
-
10
-
11
- class SSHHostKeyVerificationStrategy(Enum):
12
- MANUALLY_TRUSTED_KEY_VERIFICATION_STRATEGY = (
13
- "manuallyTrustedKeyVerificationStrategy"
14
- )
15
- MANUALLY_PROVIDED_KEY_VERIFICATION_STRATEGY = (
16
- "manuallyProvidedKeyVerificationStrategy"
17
- )
18
- NON_VERIFYING_KEY_VERIFICATION_STRATEGY = "nonVerifyingKeyVerificationStrategy"
19
- KNOWN_HOSTS_FILE_KEY_VERIFICATION_STRATEGY = "knownHostsFileKeyVerificationStrategy"
20
-
21
-
22
- class SSHConnector(BaseModel):
23
- credentials_id: str = Field(..., alias="credentialsId")
24
- launch_timeout_seconds: int | None = Field(None, alias="launchTimeoutSeconds")
25
- max_num_retries: int | None = Field(None, alias="maxNumRetries")
26
- retry_wait_time: int | None = Field(None, alias="retryWaitTime")
27
- port: int | None = 22
28
- jvm_options: str | None = Field(None, alias="jvmOptions")
29
- ssh_host_key_verification_strategy: SSHHostKeyVerificationStrategy = Field(
30
- SSHHostKeyVerificationStrategy.NON_VERIFYING_KEY_VERIFICATION_STRATEGY,
31
- alias="sshHostKeyVerificationStrategy",
32
- )
33
-
34
- class Config:
35
- use_enum_values = True
36
-
37
-
38
- class ComputerConnector(BaseModel):
39
- # alias name is defined by jcasc schema
40
- ssh_connector: SSHConnector = Field(..., alias="sSHConnector")
41
-
42
-
43
- class JenkinsWorkerFleet(BaseModel):
44
- # following options comes form https://github.com/jenkinsci/ec2-fleet-plugin/blob/master/docs/
45
- name: str
46
- fleet: str
47
- region: str
48
- min_size: int = Field(..., alias="minSize")
49
- max_size: int = Field(..., alias="maxSize")
50
- computer_connector: ComputerConnector = Field(..., alias="computerConnector")
51
- fs_root: str = Field(..., alias="fsRoot")
52
- label_string: str = Field(..., alias="labelString")
53
- num_executors: int = Field(2, alias="numExecutors")
54
- idle_minutes: int = Field(30, alias="idleMinutes")
55
- min_spare_size: int = Field(0, alias="minSpareSize")
56
- max_total_uses: int = Field(-1, alias="maxTotalUses")
57
- no_delay_provision: bool = Field(False, alias="noDelayProvision")
58
- add_node_only_if_running: bool = Field(True, alias="addNodeOnlyIfRunning")
59
- always_reconnect: bool = Field(True, alias="alwaysReconnect")
60
- private_ip_used: bool = Field(True, alias="privateIpUsed")
61
- restrict_usage: bool = Field(True, alias="restrictUsage")
62
-
63
- def __lt__(self, other: JenkinsWorkerFleet) -> bool:
64
- return self.fleet < other.fleet
65
-
66
- def __eq__(self, other: object) -> bool:
67
- if not isinstance(other, JenkinsWorkerFleet):
68
- raise NotImplementedError(
69
- "Cannot compare to non JenkinsWorkerFleet objects."
70
- )
71
- return self.fleet == other.fleet and self.region == other.region
72
-
73
- def __hash__(self) -> int:
74
- return hash(self.fleet + self.region)
75
-
76
- def differ(self, other: JenkinsWorkerFleet) -> bool:
77
- return self.dict() != other.dict()