qontract-reconcile 0.10.2.dev317__py3-none-any.whl → 0.10.2.dev318__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.dev318
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
@@ -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
@@ -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.dev318.dist-info/METADATA,sha256=YtxUnWzyEhN3lydGG0PUts7xTOW4MbgAbsDAyCWT0qc,24916
798
+ qontract_reconcile-0.10.2.dev318.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
799
+ qontract_reconcile-0.10.2.dev318.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
800
+ qontract_reconcile-0.10.2.dev318.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()
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:
@@ -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()