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.
- {qontract_reconcile-0.10.2.dev317.dist-info → qontract_reconcile-0.10.2.dev318.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.2.dev317.dist-info → qontract_reconcile-0.10.2.dev318.dist-info}/RECORD +13 -15
- reconcile/jenkins_job_builder.py +1 -1
- reconcile/jenkins_worker_fleets.py +75 -8
- reconcile/oum/providers.py +1 -1
- reconcile/utils/gpg.py +5 -3
- reconcile/utils/imap_client.py +1 -1
- reconcile/utils/jjb_client.py +50 -31
- reconcile/utils/ldap_client.py +4 -3
- reconcile/utils/lean_terraform_client.py +3 -1
- tools/cli_commands/gpg_encrypt.py +1 -1
- reconcile/jenkins/__init__.py +0 -0
- reconcile/jenkins/types.py +0 -77
- {qontract_reconcile-0.10.2.dev317.dist-info → qontract_reconcile-0.10.2.dev318.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev317.dist-info → qontract_reconcile-0.10.2.dev318.dist-info}/entry_points.txt +0 -0
{qontract_reconcile-0.10.2.dev317.dist-info → qontract_reconcile-0.10.2.dev318.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: qontract-reconcile
|
3
|
-
Version: 0.10.2.
|
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
|
{qontract_reconcile-0.10.2.dev317.dist-info → qontract_reconcile-0.10.2.dev318.dist-info}/RECORD
RENAMED
@@ -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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
625
|
-
reconcile/utils/lean_terraform_client.py,sha256=
|
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=
|
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.
|
800
|
-
qontract_reconcile-0.10.2.
|
801
|
-
qontract_reconcile-0.10.2.
|
802
|
-
qontract_reconcile-0.10.2.
|
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,,
|
reconcile/jenkins_job_builder.py
CHANGED
@@ -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
|
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
|
3
|
-
|
4
|
-
|
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
|
|
reconcile/oum/providers.py
CHANGED
@@ -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
|
-
|
45
|
-
return
|
46
|
+
encrypted_out = proc.stdout
|
47
|
+
return encrypted_out.decode("utf-8")
|
reconcile/utils/imap_client.py
CHANGED
reconcile/utils/jjb_client.py
CHANGED
@@ -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__(
|
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(
|
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:
|
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(
|
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:
|
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:
|
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
|
reconcile/utils/ldap_client.py
CHANGED
@@ -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(
|
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
|
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,
|
reconcile/jenkins/__init__.py
DELETED
File without changes
|
reconcile/jenkins/types.py
DELETED
@@ -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()
|
{qontract_reconcile-0.10.2.dev317.dist-info → qontract_reconcile-0.10.2.dev318.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|