qontract-reconcile 0.10.2.dev215__py3-none-any.whl → 0.10.2.dev217__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.
@@ -2,6 +2,7 @@ import logging
2
2
  import os
3
3
  import sys
4
4
  from collections.abc import (
5
+ Callable,
5
6
  Iterable,
6
7
  Mapping,
7
8
  Sequence,
@@ -119,7 +120,7 @@ def _build_helm_integration_spec(
119
120
  integration_name: str,
120
121
  managed: IntegrationManagedV1,
121
122
  shard_manager: IntegrationShardManager,
122
- ):
123
+ ) -> HelmIntegrationSpec:
123
124
  integration_spec = managed.spec.dict(by_alias=True)
124
125
  shard_specs = shard_manager.build_integration_shards(integration_name, managed)
125
126
  his = HelmIntegrationSpec(
@@ -151,7 +152,7 @@ class IntegrationsEnvironment(BaseModel):
151
152
 
152
153
  def collect_integrations_environment(
153
154
  integrations: Iterable[IntegrationV1],
154
- environment_name: str,
155
+ environment_name: str | None,
155
156
  shard_manager: IntegrationShardManager,
156
157
  ) -> list[IntegrationsEnvironment]:
157
158
  int_envs: dict[str, IntegrationsEnvironment] = {}
@@ -210,7 +211,7 @@ def fetch_desired_state(
210
211
  upstream: str,
211
212
  image: str,
212
213
  image_tag_from_ref: Mapping[str, str] | None,
213
- ):
214
+ ) -> None:
214
215
  for ie in integrations_environments:
215
216
  oc_resources = construct_oc_resources(ie, upstream, image, image_tag_from_ref)
216
217
  for r in oc_resources:
@@ -230,17 +231,17 @@ def filter_integrations(
230
231
 
231
232
  @defer
232
233
  def run(
233
- dry_run,
234
- environment_name,
234
+ dry_run: bool,
235
+ environment_name: str | None,
235
236
  integration_runtime_meta: dict[str, IntegrationMeta],
236
- thread_pool_size=10,
237
- internal=None,
238
- use_jump_host=True,
239
- image_tag_from_ref=None,
240
- upstream=None,
241
- image=None,
242
- defer=None,
243
- ):
237
+ thread_pool_size: int = 10,
238
+ internal: bool = False,
239
+ use_jump_host: bool = True,
240
+ image_tag_from_ref: dict[str, str] | None = None,
241
+ upstream: str | None = None,
242
+ image: str | None = None,
243
+ defer: Callable | None = None,
244
+ ) -> None:
244
245
  # Beware, environment_name can be empty! It's optional to set it!
245
246
  # If not set, all environments should be considered.
246
247
 
@@ -269,41 +270,31 @@ def run(
269
270
  logging.debug("Nothing to do, exiting.")
270
271
  sys.exit(ExitCodes.SUCCESS)
271
272
 
272
- fetch_args = {
273
- "namespaces": [
273
+ ri, oc_map = ob.fetch_current_state(
274
+ namespaces=[
274
275
  ie.namespace.dict(by_alias=True) for ie in integration_environments
275
276
  ],
276
- "thread_pool_size": thread_pool_size,
277
- "integration": QONTRACT_INTEGRATION,
278
- "integration_version": QONTRACT_INTEGRATION_VERSION,
279
- "override_managed_types": ["Deployment", "StatefulSet", "CronJob", "Service"],
280
- "internal": internal,
281
- "use_jump_host": use_jump_host,
282
- }
283
-
284
- if not image:
285
- image = IMAGE_DEFAULT
286
-
287
- if upstream:
288
- use_upstream = True
289
- fetch_args["caller"] = upstream
290
- else:
291
- # Not set to fetch_args on purpose, fallback for cases where caller is not yet set
292
- use_upstream = False
293
- upstream = UPSTREAM_DEFAULT
294
-
295
- ri, oc_map = ob.fetch_current_state(**fetch_args)
296
- defer(oc_map.cleanup)
277
+ thread_pool_size=thread_pool_size,
278
+ integration=QONTRACT_INTEGRATION,
279
+ integration_version=QONTRACT_INTEGRATION_VERSION,
280
+ override_managed_types=["Deployment", "StatefulSet", "CronJob", "Service"],
281
+ internal=internal,
282
+ use_jump_host=use_jump_host,
283
+ caller=upstream,
284
+ )
285
+ if defer:
286
+ defer(oc_map.cleanup)
297
287
 
298
288
  fetch_desired_state(
299
- integration_environments, ri, upstream, image, image_tag_from_ref
289
+ integration_environments,
290
+ ri,
291
+ upstream or UPSTREAM_DEFAULT,
292
+ image or IMAGE_DEFAULT,
293
+ image_tag_from_ref,
300
294
  )
301
295
 
302
296
  ob.publish_metrics(ri, QONTRACT_INTEGRATION)
303
- if use_upstream:
304
- ob.realize_data(dry_run, oc_map, ri, thread_pool_size, caller=upstream)
305
- else:
306
- ob.realize_data(dry_run, oc_map, ri, thread_pool_size)
297
+ ob.realize_data(dry_run, oc_map, ri, thread_pool_size, caller=upstream)
307
298
 
308
299
  if ri.has_error_registered():
309
300
  sys.exit(ExitCodes.ERROR)
@@ -46,7 +46,7 @@ def init_jjb(
46
46
  return JJB(configs, secret_reader=secret_reader, print_only=print_only)
47
47
 
48
48
 
49
- def validate_repos_and_admins(jjb: JJB):
49
+ def validate_repos_and_admins(jjb: JJB) -> None:
50
50
  jjb_repos = jjb.get_repos()
51
51
  app_int_repos = queries.get_repos()
52
52
  missing_repos = [r for r in jjb_repos if r not in app_int_repos]
@@ -2,6 +2,7 @@ import logging
2
2
  import operator
3
3
  import re
4
4
  import time
5
+ from typing import Any
5
6
 
6
7
  from reconcile import queries
7
8
  from reconcile.utils.jenkins_api import JenkinsApi
@@ -10,11 +11,13 @@ from reconcile.utils.secret_reader import SecretReader
10
11
  QONTRACT_INTEGRATION = "jenkins-job-builds-cleaner"
11
12
 
12
13
 
13
- def hours_to_ms(hours):
14
+ def hours_to_ms(hours: int) -> int:
14
15
  return hours * 60 * 60 * 1000
15
16
 
16
17
 
17
- def delete_builds(jenkins, builds_todel, dry_run=True):
18
+ def delete_builds(
19
+ jenkins: JenkinsApi, builds_todel: list[dict[str, Any]], dry_run: bool = True
20
+ ) -> None:
18
21
  delete_builds_count = len(builds_todel)
19
22
  for idx, build in enumerate(builds_todel, start=1):
20
23
  job_name = build["job_name"]
@@ -35,7 +38,7 @@ def delete_builds(jenkins, builds_todel, dry_run=True):
35
38
  logging.exception(msg)
36
39
 
37
40
 
38
- def get_last_build_ids(builds):
41
+ def get_last_build_ids(builds: list[dict[str, Any]]) -> list[str]:
39
42
  builds_to_keep = []
40
43
  sorted_builds = sorted(builds, key=operator.itemgetter("timestamp"), reverse=True)
41
44
  if sorted_builds:
@@ -49,7 +52,9 @@ def get_last_build_ids(builds):
49
52
  return builds_to_keep
50
53
 
51
54
 
52
- def find_builds(jenkins, job_names, rules):
55
+ def find_builds(
56
+ jenkins: JenkinsApi, job_names: list[str], rules: list[dict[str, Any]]
57
+ ) -> list[dict[str, Any]]:
53
58
  # Current time in ms
54
59
  time_ms = time.time() * 1000
55
60
 
@@ -78,7 +83,7 @@ def find_builds(jenkins, job_names, rules):
78
83
  return builds_found
79
84
 
80
85
 
81
- def run(dry_run):
86
+ def run(dry_run: bool) -> None:
82
87
  jenkins_instances = queries.get_jenkins_instances()
83
88
  secret_reader = SecretReader(queries.get_secret_reader_settings())
84
89
 
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ from collections.abc import Iterable
2
3
 
3
4
  from reconcile import queries
4
5
  from reconcile.jenkins_job_builder import init_jjb
@@ -8,7 +9,9 @@ from reconcile.utils.secret_reader import SecretReader
8
9
  QONTRACT_INTEGRATION = "jenkins-job-cleaner"
9
10
 
10
11
 
11
- def get_managed_job_names(job_names, managed_projects):
12
+ def get_managed_job_names(
13
+ job_names: Iterable[str], managed_projects: Iterable[str]
14
+ ) -> list[str]:
12
15
  managed_jobs = set()
13
16
  for job_name in job_names:
14
17
  for managed_project in managed_projects:
@@ -18,13 +21,13 @@ def get_managed_job_names(job_names, managed_projects):
18
21
  return list(managed_jobs)
19
22
 
20
23
 
21
- def get_desired_job_names(instance_name: str, secret_reader: SecretReader):
24
+ def get_desired_job_names(instance_name: str, secret_reader: SecretReader) -> list[str]:
22
25
  jjb = init_jjb(secret_reader)
23
26
  desired_jobs = jjb.get_all_jobs(instance_name=instance_name)[instance_name]
24
27
  return [j["name"] for j in desired_jobs]
25
28
 
26
29
 
27
- def run(dry_run):
30
+ def run(dry_run: bool) -> None:
28
31
  jenkins_instances = queries.get_jenkins_instances()
29
32
  secret_reader = SecretReader(queries.get_secret_reader_settings())
30
33
 
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ from collections.abc import Iterable, Mapping
2
3
 
3
4
  from reconcile import queries
4
5
  from reconcile.utils import (
@@ -76,8 +77,8 @@ def get_jenkins_map() -> dict[str, JenkinsApi]:
76
77
  return jenkins_map
77
78
 
78
79
 
79
- def get_current_state(jenkins_map):
80
- current_state = []
80
+ def get_current_state(jenkins_map: Mapping[str, JenkinsApi]) -> list[dict[str, str]]:
81
+ current_state: list[dict[str, str]] = []
81
82
 
82
83
  for instance, jenkins in jenkins_map.items():
83
84
  roles = jenkins.get_all_roles()
@@ -97,11 +98,11 @@ def get_current_state(jenkins_map):
97
98
  return current_state
98
99
 
99
100
 
100
- def get_desired_state():
101
+ def get_desired_state() -> list[dict[str, str]]:
101
102
  gqlapi = gql.get_api()
102
103
  roles: list[dict] = expiration.filter(gqlapi.query(ROLES_QUERY)["roles"])
103
104
 
104
- desired_state = []
105
+ desired_state: list[dict[str, str]] = []
105
106
  for r in roles:
106
107
  for p in r["permissions"]:
107
108
  if p["service"] != "jenkins-role":
@@ -128,7 +129,9 @@ def get_desired_state():
128
129
  return desired_state
129
130
 
130
131
 
131
- def calculate_diff(current_state, desired_state):
132
+ def calculate_diff(
133
+ current_state: Iterable[dict[str, str]], desired_state: Iterable[dict[str, str]]
134
+ ) -> list[dict[str, str]]:
132
135
  diff = []
133
136
  users_to_assign = subtract_states(
134
137
  desired_state, current_state, "assign_role_to_user"
@@ -142,7 +145,11 @@ def calculate_diff(current_state, desired_state):
142
145
  return diff
143
146
 
144
147
 
145
- def subtract_states(from_state, subtract_state, action):
148
+ def subtract_states(
149
+ from_state: Iterable[dict[str, str]],
150
+ subtract_state: Iterable[dict[str, str]],
151
+ action: str,
152
+ ) -> list[dict[str, str]]:
146
153
  result = []
147
154
 
148
155
  for f_user in from_state:
@@ -163,7 +170,7 @@ def subtract_states(from_state, subtract_state, action):
163
170
  return result
164
171
 
165
172
 
166
- def act(diff, jenkins_map):
173
+ def act(diff: dict[str, str], jenkins_map: Mapping[str, JenkinsApi]) -> None:
167
174
  instance = diff["instance"]
168
175
  role = diff["role"]
169
176
  user = diff["user"]
@@ -177,7 +184,7 @@ def act(diff, jenkins_map):
177
184
  raise Exception(f"invalid action: {action}")
178
185
 
179
186
 
180
- def run(dry_run):
187
+ def run(dry_run: bool) -> None:
181
188
  jenkins_map = get_jenkins_map()
182
189
  current_state = get_current_state(jenkins_map)
183
190
  desired_state = get_desired_state()
@@ -1,5 +1,6 @@
1
1
  import copy
2
2
  import logging
3
+ from collections.abc import Callable, MutableMapping
3
4
  from typing import Any
4
5
 
5
6
  from reconcile import queries
@@ -17,7 +18,9 @@ def get_gitlab_api(secret_reader: SecretReader) -> GitLabApi:
17
18
  return GitLabApi(instance, secret_reader=secret_reader)
18
19
 
19
20
 
20
- def get_hooks_to_add(desired_state, gl):
21
+ def get_hooks_to_add(
22
+ desired_state: MutableMapping, gl: GitLabApi
23
+ ) -> MutableMapping[str, list[dict[str, Any]]]:
21
24
  diff = copy.deepcopy(desired_state)
22
25
  for project_url, desired_hooks in diff.items():
23
26
  try:
@@ -45,7 +48,7 @@ def get_hooks_to_add(desired_state, gl):
45
48
 
46
49
 
47
50
  @defer
48
- def run(dry_run, defer=None):
51
+ def run(dry_run: bool, defer: Callable | None = None) -> None:
49
52
  secret_reader = SecretReader(queries.get_secret_reader_settings())
50
53
  jjb: JJB = init_jjb(secret_reader)
51
54
  gl = get_gitlab_api(secret_reader)
@@ -63,7 +66,7 @@ def run(dry_run, defer=None):
63
66
  gl.create_project_hook(project_url, h)
64
67
 
65
68
 
66
- def early_exit_desired_state(*args, **kwargs) -> dict[str, Any]:
69
+ def early_exit_desired_state(*args: Any, **kwargs: Any) -> dict[str, Any]:
67
70
  return {
68
71
  "jenkins_configs": queries.get_jenkins_configs(),
69
72
  }
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ from collections.abc import Callable
2
3
  from typing import Any
3
4
 
4
5
  from reconcile import queries
@@ -9,7 +10,7 @@ QONTRACT_INTEGRATION = "jenkins-webhooks-cleaner"
9
10
 
10
11
 
11
12
  @defer
12
- def run(dry_run, defer=None):
13
+ def run(dry_run: bool, defer: Callable | None = None) -> None:
13
14
  instance = queries.get_gitlab_instance()
14
15
  settings = queries.get_app_interface_settings()
15
16
  gl = GitLabApi(instance, settings=settings)
@@ -40,7 +41,7 @@ def run(dry_run, defer=None):
40
41
  logging.warning("no access to project: " + repo)
41
42
 
42
43
 
43
- def early_exit_desired_state(*args, **kwargs) -> dict[str, Any]:
44
+ def early_exit_desired_state(*args: Any, **kwargs: Any) -> dict[str, Any]:
44
45
  return {
45
46
  "previous_urls": queries.get_jenkins_instances_previous_urls(),
46
47
  }
reconcile/jira_watcher.py CHANGED
@@ -1,4 +1,6 @@
1
1
  import logging
2
+ from collections.abc import Callable, Mapping, Sequence
3
+ from typing import Any
2
4
 
3
5
  from reconcile import queries
4
6
  from reconcile.slack_base import slackapi_from_slack_workspace
@@ -6,6 +8,7 @@ from reconcile.utils.defer import defer
6
8
  from reconcile.utils.jira_client import JiraClient
7
9
  from reconcile.utils.secret_reader import SecretReader
8
10
  from reconcile.utils.sharding import is_in_shard_round_robin
11
+ from reconcile.utils.slack_api import SlackApi
9
12
  from reconcile.utils.state import (
10
13
  State,
11
14
  init_state,
@@ -14,7 +17,9 @@ from reconcile.utils.state import (
14
17
  QONTRACT_INTEGRATION = "jira-watcher"
15
18
 
16
19
 
17
- def fetch_current_state(jira_board, settings):
20
+ def fetch_current_state(
21
+ jira_board: Mapping, settings: Mapping
22
+ ) -> tuple[JiraClient, dict[str, dict[str, str]]]:
18
23
  jira = JiraClient(jira_board, settings=settings)
19
24
  issues = jira.get_issues(fields=["key", "status", "summary"])
20
25
  return jira, {
@@ -23,11 +28,18 @@ def fetch_current_state(jira_board, settings):
23
28
  }
24
29
 
25
30
 
26
- def fetch_previous_state(state, project):
31
+ def fetch_previous_state(state: State, project: str) -> dict:
27
32
  return state.get(project, {})
28
33
 
29
34
 
30
- def format_message(server, key, data, event, previous_state=None, current_state=None):
35
+ def format_message(
36
+ server: str,
37
+ key: str,
38
+ data: Mapping,
39
+ event: str,
40
+ previous_state: Mapping | None = None,
41
+ current_state: Mapping | None = None,
42
+ ) -> str:
31
43
  summary = data["summary"]
32
44
  info = (
33
45
  ": {} -> {}".format(previous_state["status"], current_state["status"])
@@ -38,7 +50,9 @@ def format_message(server, key, data, event, previous_state=None, current_state=
38
50
  return f"{url} ({summary}) {event}{info}"
39
51
 
40
52
 
41
- def calculate_diff(server, current_state, previous_state):
53
+ def calculate_diff(
54
+ server: str, current_state: Mapping, previous_state: Mapping
55
+ ) -> list[str]:
42
56
  messages = []
43
57
  new_issues = [
44
58
  format_message(server, key, data, "created")
@@ -64,7 +78,7 @@ def calculate_diff(server, current_state, previous_state):
64
78
  return messages
65
79
 
66
80
 
67
- def init_slack(jira_board):
81
+ def init_slack(jira_board: Mapping[str, Any]) -> SlackApi:
68
82
  secret_reader = SecretReader(queries.get_secret_reader_settings())
69
83
  slack_info = jira_board["slack"]
70
84
 
@@ -77,7 +91,7 @@ def init_slack(jira_board):
77
91
  )
78
92
 
79
93
 
80
- def act(dry_run, jira_board, diffs):
94
+ def act(dry_run: bool, jira_board: Mapping[str, str], diffs: Sequence[str]) -> None:
81
95
  if not dry_run and diffs:
82
96
  slack = init_slack(jira_board)
83
97
 
@@ -87,16 +101,17 @@ def act(dry_run, jira_board, diffs):
87
101
  slack.chat_post_message(diff)
88
102
 
89
103
 
90
- def write_state(state: State, project, state_to_write):
104
+ def write_state(state: State, project: str, state_to_write: Mapping) -> None:
91
105
  state.add(project, value=state_to_write, force=True)
92
106
 
93
107
 
94
108
  @defer
95
- def run(dry_run, defer):
109
+ def run(dry_run: bool, defer: Callable | None = None) -> None:
96
110
  jira_boards = [j for j in queries.get_jira_boards() if j.get("slack")]
97
111
  settings = queries.get_app_interface_settings()
98
112
  state = init_state(integration=QONTRACT_INTEGRATION)
99
- defer(state.cleanup)
113
+ if defer:
114
+ defer(state.cleanup)
100
115
  for index, jira_board in enumerate(jira_boards):
101
116
  if not is_in_shard_round_robin(jira_board["name"], index):
102
117
  continue
@@ -109,6 +124,7 @@ def run(dry_run, defer):
109
124
  continue
110
125
  previous_state = fetch_previous_state(state, jira.project)
111
126
  if previous_state:
127
+ assert jira.server
112
128
  diffs = calculate_diff(jira.server, current_state, previous_state)
113
129
  act(dry_run, jira_board, diffs)
114
130
  if not dry_run:
@@ -176,7 +176,7 @@ class JiraClient:
176
176
  server=server_url,
177
177
  )
178
178
 
179
- def get_issues(self, fields: Mapping | None = None) -> list[Issue]:
179
+ def get_issues(self, fields: Iterable | None = None) -> list[Issue]:
180
180
  block_size = 100
181
181
  block_num = 0
182
182