qontract-reconcile 0.10.1rc1202__py3-none-any.whl → 0.10.2.dev1__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.
Files changed (138) hide show
  1. qontract_reconcile-0.10.2.dev1.dist-info/METADATA +500 -0
  2. {qontract_reconcile-0.10.1rc1202.dist-info → qontract_reconcile-0.10.2.dev1.dist-info}/RECORD +12 -130
  3. {qontract_reconcile-0.10.1rc1202.dist-info → qontract_reconcile-0.10.2.dev1.dist-info}/WHEEL +1 -2
  4. {qontract_reconcile-0.10.1rc1202.dist-info → qontract_reconcile-0.10.2.dev1.dist-info}/entry_points.txt +1 -0
  5. reconcile/aws_account_manager/README.md +5 -0
  6. reconcile/change_owners/README.md +34 -0
  7. reconcile/glitchtip/README.md +150 -0
  8. reconcile/gql_definitions/introspection.json +51176 -0
  9. reconcile/run_integration.py +293 -0
  10. reconcile/utils/binary.py +2 -2
  11. reconcile/utils/mr/README.md +198 -0
  12. reconcile/utils/oc_map.py +2 -2
  13. tools/qontract_cli.py +0 -0
  14. qontract_reconcile-0.10.1rc1202.dist-info/METADATA +0 -64
  15. qontract_reconcile-0.10.1rc1202.dist-info/top_level.txt +0 -3
  16. reconcile/test/__init__.py +0 -0
  17. reconcile/test/conftest.py +0 -157
  18. reconcile/test/fixtures.py +0 -24
  19. reconcile/test/saas_auto_promotions_manager/__init__.py +0 -0
  20. reconcile/test/saas_auto_promotions_manager/conftest.py +0 -170
  21. reconcile/test/saas_auto_promotions_manager/merge_request_manager/__init__.py +0 -0
  22. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/__init__.py +0 -0
  23. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/conftest.py +0 -115
  24. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/data_keys.py +0 -19
  25. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_desired_state.py +0 -66
  26. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_merge_request_manager.py +0 -86
  27. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_mr_parser.py +0 -352
  28. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_reconciler.py +0 -494
  29. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/__init__.py +0 -0
  30. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/conftest.py +0 -25
  31. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_multiple_namespaces.py +0 -37
  32. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_single_namespace.py +0 -81
  33. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_single_target.py +0 -61
  34. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_json_path_selector.py +0 -74
  35. reconcile/test/saas_auto_promotions_manager/test_integration_test.py +0 -52
  36. reconcile/test/saas_auto_promotions_manager/utils/__init__.py +0 -0
  37. reconcile/test/test_acs_notifiers.py +0 -393
  38. reconcile/test/test_acs_policies.py +0 -497
  39. reconcile/test/test_acs_rbac.py +0 -865
  40. reconcile/test/test_aggregated_list.py +0 -237
  41. reconcile/test/test_amtool.py +0 -37
  42. reconcile/test/test_aws_ami_cleanup.py +0 -230
  43. reconcile/test/test_aws_ami_share.py +0 -68
  44. reconcile/test/test_aws_cloudwatch_log_retention.py +0 -434
  45. reconcile/test/test_aws_iam_keys.py +0 -70
  46. reconcile/test/test_aws_iam_password_reset.py +0 -35
  47. reconcile/test/test_aws_support_cases_sos.py +0 -23
  48. reconcile/test/test_checkpoint.py +0 -178
  49. reconcile/test/test_cli.py +0 -41
  50. reconcile/test/test_closedbox_endpoint_monitoring.py +0 -207
  51. reconcile/test/test_dashdotdb_dora.py +0 -245
  52. reconcile/test/test_database_access_manager.py +0 -660
  53. reconcile/test/test_deadmanssnitch.py +0 -290
  54. reconcile/test/test_gabi_authorized_users.py +0 -72
  55. reconcile/test/test_gcr_mirror.py +0 -14
  56. reconcile/test/test_github_org.py +0 -156
  57. reconcile/test/test_github_repo_invites.py +0 -119
  58. reconcile/test/test_gitlab_housekeeping.py +0 -333
  59. reconcile/test/test_gitlab_labeler.py +0 -126
  60. reconcile/test/test_gitlab_members.py +0 -219
  61. reconcile/test/test_gitlab_permissions.py +0 -164
  62. reconcile/test/test_instrumented_wrappers.py +0 -18
  63. reconcile/test/test_integrations_manager.py +0 -1252
  64. reconcile/test/test_jenkins_worker_fleets.py +0 -57
  65. reconcile/test/test_jira_permissions_validator.py +0 -519
  66. reconcile/test/test_jump_host.py +0 -114
  67. reconcile/test/test_ldap_users.py +0 -125
  68. reconcile/test/test_make.py +0 -28
  69. reconcile/test/test_ocm_additional_routers.py +0 -133
  70. reconcile/test/test_ocm_clusters.py +0 -798
  71. reconcile/test/test_ocm_clusters_manifest_updates.py +0 -87
  72. reconcile/test/test_ocm_machine_pools.py +0 -1103
  73. reconcile/test/test_ocm_update_recommended_version.py +0 -145
  74. reconcile/test/test_ocm_upgrade_scheduler_org_updater.py +0 -125
  75. reconcile/test/test_openshift_base.py +0 -1269
  76. reconcile/test/test_openshift_cluster_bots.py +0 -240
  77. reconcile/test/test_openshift_namespace_labels.py +0 -344
  78. reconcile/test/test_openshift_namespaces.py +0 -256
  79. reconcile/test/test_openshift_resource.py +0 -443
  80. reconcile/test/test_openshift_resources_base.py +0 -478
  81. reconcile/test/test_openshift_saas_deploy.py +0 -188
  82. reconcile/test/test_openshift_saas_deploy_change_tester.py +0 -308
  83. reconcile/test/test_openshift_saas_deploy_trigger_cleaner.py +0 -65
  84. reconcile/test/test_openshift_serviceaccount_tokens.py +0 -282
  85. reconcile/test/test_openshift_tekton_resources.py +0 -265
  86. reconcile/test/test_openshift_upgrade_watcher.py +0 -223
  87. reconcile/test/test_prometheus_rules_tester.py +0 -151
  88. reconcile/test/test_quay_membership.py +0 -86
  89. reconcile/test/test_quay_mirror.py +0 -172
  90. reconcile/test/test_quay_mirror_org.py +0 -82
  91. reconcile/test/test_quay_repos.py +0 -59
  92. reconcile/test/test_queries.py +0 -53
  93. reconcile/test/test_repo_owners.py +0 -47
  94. reconcile/test/test_requests_sender.py +0 -139
  95. reconcile/test/test_saasherder.py +0 -1611
  96. reconcile/test/test_saasherder_allowed_secret_paths.py +0 -125
  97. reconcile/test/test_secret_reader.py +0 -153
  98. reconcile/test/test_slack_base.py +0 -183
  99. reconcile/test/test_slack_usergroups.py +0 -785
  100. reconcile/test/test_sql_query.py +0 -316
  101. reconcile/test/test_status_board.py +0 -258
  102. reconcile/test/test_terraform_aws_route53.py +0 -29
  103. reconcile/test/test_terraform_cloudflare_dns.py +0 -117
  104. reconcile/test/test_terraform_cloudflare_resources.py +0 -408
  105. reconcile/test/test_terraform_cloudflare_users.py +0 -747
  106. reconcile/test/test_terraform_repo.py +0 -440
  107. reconcile/test/test_terraform_resources.py +0 -519
  108. reconcile/test/test_terraform_tgw_attachments.py +0 -1295
  109. reconcile/test/test_terraform_users.py +0 -152
  110. reconcile/test/test_terraform_vpc_peerings.py +0 -576
  111. reconcile/test/test_terraform_vpc_peerings_build_desired_state.py +0 -1434
  112. reconcile/test/test_three_way_diff_strategy.py +0 -131
  113. reconcile/test/test_utils_jinja2.py +0 -130
  114. reconcile/test/test_vault_replication.py +0 -534
  115. reconcile/test/test_vault_utils.py +0 -47
  116. reconcile/test/test_version_bump.py +0 -18
  117. reconcile/test/test_vpc_peerings_validator.py +0 -194
  118. reconcile/test/test_wrong_region.py +0 -78
  119. release/__init__.py +0 -0
  120. release/test_version.py +0 -50
  121. release/version.py +0 -104
  122. tools/cli_commands/test/__init__.py +0 -0
  123. tools/cli_commands/test/conftest.py +0 -332
  124. tools/cli_commands/test/test_aws_cost_report.py +0 -258
  125. tools/cli_commands/test/test_cost_management_api.py +0 -326
  126. tools/cli_commands/test/test_gpg_encrypt.py +0 -235
  127. tools/cli_commands/test/test_openshift_cost_optimization_report.py +0 -255
  128. tools/cli_commands/test/test_openshift_cost_report.py +0 -295
  129. tools/cli_commands/test/test_util.py +0 -70
  130. tools/test/__init__.py +0 -0
  131. tools/test/conftest.py +0 -77
  132. tools/test/test_app_interface_metrics_exporter.py +0 -48
  133. tools/test/test_erv2.py +0 -80
  134. tools/test/test_get_container_images.py +0 -230
  135. tools/test/test_qontract_cli.py +0 -197
  136. tools/test/test_saas_promotion_state.py +0 -187
  137. tools/test/test_sd_app_sre_alert_report.py +0 -74
  138. tools/test/test_sre_checkpoints.py +0 -79
@@ -0,0 +1,293 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import logging
4
+ import os
5
+ import sys
6
+ import time
7
+ from collections.abc import Callable
8
+ from importlib import metadata
9
+
10
+ import click
11
+ from prometheus_client import (
12
+ push_to_gateway,
13
+ start_http_server,
14
+ )
15
+ from prometheus_client.exposition import basic_auth_handler
16
+
17
+ from reconcile.status import ExitCodes
18
+ from reconcile.utils.metrics import (
19
+ execution_counter,
20
+ pushgateway_registry,
21
+ pushgateway_run_status,
22
+ pushgateway_run_time,
23
+ run_status,
24
+ run_time,
25
+ )
26
+ from reconcile.utils.runtime.environment import (
27
+ LOG_DATEFMT,
28
+ log_fmt,
29
+ )
30
+
31
+ SHARDS = int(os.environ.get("SHARDS", 1))
32
+ SHARD_ID = int(os.environ.get("SHARD_ID", 0))
33
+ SHARD_ID_LABEL = os.environ.get("SHARD_KEY", f"{SHARD_ID}-{SHARDS}")
34
+ PREFIX_LOG_LEVEL = os.environ.get("PREFIX_LOG_LEVEL", "false")
35
+
36
+ INTEGRATION_NAME = os.environ.get("INTEGRATION_NAME")
37
+ COMMAND_NAME = os.environ.get("COMMAND_NAME", "qontract-reconcile")
38
+
39
+ RUN_ONCE = os.environ.get("RUN_ONCE")
40
+ DRY_RUN = (
41
+ os.environ.get("MANAGER_DRY_RUN")
42
+ if INTEGRATION_NAME == "integrations-manager"
43
+ else os.environ.get("DRY_RUN")
44
+ )
45
+ INTEGRATION_EXTRA_ARGS = os.environ.get("INTEGRATION_EXTRA_ARGS")
46
+ CONFIG = os.environ.get("CONFIG", "/config/config.toml")
47
+ PROMETHEUS_PORT = os.environ.get("PROMETHEUS_PORT", 9090)
48
+
49
+ LOG_FILE = os.environ.get("LOG_FILE")
50
+ LOG_LEVEL = os.environ.get("LOG_LEVEL", "INFO")
51
+ SLEEP_DURATION_SECS = os.environ.get("SLEEP_DURATION_SECS", 600)
52
+ SLEEP_ON_ERROR = os.environ.get("SLEEP_ON_ERROR", 10)
53
+
54
+ PUSHGATEWAY_ENABLED = os.environ.get("PUSHGATEWAY_ENABLED", False)
55
+
56
+ LOG = logging.getLogger(__name__)
57
+
58
+ # Messages to stdout
59
+ STREAM_HANDLER = logging.StreamHandler(sys.stdout)
60
+ STREAM_HANDLER.setFormatter(
61
+ logging.Formatter(fmt=log_fmt(dry_run_option=DRY_RUN), datefmt=LOG_DATEFMT)
62
+ )
63
+ HANDLERS = [STREAM_HANDLER]
64
+
65
+ # Messages to the log file
66
+ if LOG_FILE is not None:
67
+ FILE_HANDLER = logging.FileHandler(LOG_FILE)
68
+ logFileFormat = "%(message)s"
69
+ if PREFIX_LOG_LEVEL == "true":
70
+ logFileFormat = "[%(levelname)s] %(message)s"
71
+ FILE_HANDLER.setFormatter(logging.Formatter(fmt=logFileFormat))
72
+ HANDLERS.append(FILE_HANDLER) # type: ignore
73
+
74
+ # Setting up the root logger
75
+ logging.basicConfig(level=LOG_LEVEL, handlers=HANDLERS)
76
+
77
+
78
+ class PushgatewayBadConfigError(Exception):
79
+ pass
80
+
81
+
82
+ def _parse_dry_run_flag(dry_run: str | None) -> str | None:
83
+ dry_run_options = ["--dry-run", "--no-dry-run"]
84
+ if dry_run is not None and dry_run not in dry_run_options:
85
+ msg = (
86
+ f'Invalid DRY_RUN option given: "{dry_run}".'
87
+ f"Only the following options are allowed: {dry_run_options}"
88
+ )
89
+ logging.error(msg)
90
+ raise ValueError(msg)
91
+ return dry_run if dry_run else None
92
+
93
+
94
+ def build_entry_point_args(
95
+ command: click.Command,
96
+ config: str,
97
+ dry_run: str | None,
98
+ integration_name: str,
99
+ extra_args: str | None,
100
+ ) -> list[str]:
101
+ args = ["--config", config]
102
+ if dry_run_flag := _parse_dry_run_flag(dry_run):
103
+ args.append(dry_run_flag)
104
+
105
+ # if the integration_name is a known sub command,
106
+ # we add it right before the extra_args
107
+ if (
108
+ integration_name
109
+ and isinstance(command, click.MultiCommand)
110
+ and command.get_command(None, integration_name) # type: ignore
111
+ ):
112
+ args.append(integration_name)
113
+
114
+ if extra_args is not None:
115
+ args.extend(extra_args.split())
116
+ return args
117
+
118
+
119
+ def build_entry_point_func(command_name: str) -> click.Command:
120
+ """
121
+ Use the entry point information from setup.py to
122
+ find the function to invoke for a command.
123
+ """
124
+ console_script_entry_points = {
125
+ ep.name: ep for ep in metadata.entry_points().select(group="console_scripts")
126
+ }
127
+ entry_point: metadata.EntryPoint | None = console_script_entry_points.get(
128
+ command_name
129
+ )
130
+ if entry_point:
131
+ return entry_point.load()
132
+ raise ValueError(
133
+ f"Command {command_name} unknown."
134
+ f"Have a look at setup.py for valid entry points."
135
+ )
136
+
137
+
138
+ def _get_pushgateway_env_vars() -> dict[str, str]:
139
+ env = {}
140
+ missing_vars = []
141
+ for var in ["PUSHGATEWAY_USERNAME", "PUSHGATEWAY_PASSWORD", "PUSHGATEWAY_URL"]:
142
+ value = os.environ.get(var)
143
+ if not value:
144
+ missing_vars.append(var)
145
+ continue
146
+
147
+ env[var] = value
148
+
149
+ if missing_vars:
150
+ missing_str = ", ".join(missing_vars)
151
+ raise PushgatewayBadConfigError(
152
+ f"Failed to check env variables to configure Pushgateway: {missing_str}"
153
+ )
154
+
155
+ return env
156
+
157
+
158
+ def _push_gateway_basic_auth_handler(
159
+ url: str,
160
+ method: str,
161
+ timeout: float | None,
162
+ headers: list[tuple[str, str]],
163
+ data: bytes,
164
+ ) -> Callable[[], None]:
165
+ username = os.environ.get("PUSHGATEWAY_USERNAME")
166
+ password = os.environ.get("PUSHGATEWAY_PASSWORD")
167
+
168
+ # We should not get here, but this will make mypy happy
169
+ if not username or not password:
170
+ raise PushgatewayBadConfigError(
171
+ "Failed to check env variables to configure Pushgateway."
172
+ )
173
+
174
+ return basic_auth_handler(url, method, timeout, headers, data, username, password)
175
+
176
+
177
+ def main() -> None:
178
+ """
179
+ This entry point script expects certain env variables
180
+ * COMMAND_NAME (optional, defaults to qontract-reconcile)
181
+ an entry point as defined in setup.py must be a click.Command
182
+ * INTEGRATION_NAME
183
+ used as name for the subcommand for command if present as a subcommand
184
+ on the click command
185
+ * INTEGRATION_EXTRA_ARGS (optional)
186
+ space separated list of arguments that will be passed to the command
187
+ or subcommand
188
+ * CONFIG
189
+ path to the config toml file
190
+ * LOG_LEVEL
191
+ Log level (defaults to INFO)
192
+ * LOG_FILE
193
+ path for the logfile to write to
194
+ * DRY_RUN (optional)
195
+ this is not a boolean but must contain the actual dry-run flag value,
196
+ so --dry-run or --no-dry-run
197
+ * RUN_ONCE (optional)
198
+ if 'true', execute the integration once and exit
199
+ otherwise run the integration in a loop controlled by SLEEP_DURATION_SECS
200
+ and SLEEP_ON_ERROR
201
+ * SLEEP_DURATION_SECS (default 600)
202
+ amount of seconds to sleep between successful integration runs
203
+ * SLEEP_ON_ERROR (default 10)
204
+ amount of seconds to sleep before another integration run is started
205
+ * PUSHGATEWAY_ENABLED (defaults to false)
206
+ send metrics to a Prometheus Pushgateway after the run. In expects
207
+ "PUSHGATEWAY_USERNAME", "PUSHGATEWAY_PASSWORD" and "PUSHGATEWAY_URL" to be defined.
208
+
209
+
210
+ Based on those variables, the following command will be executed
211
+ $COMMAND --config $CONFIG $DRY_RUN $INTEGRATION_NAME \
212
+ $INTEGRATION_EXTRA_ARGS
213
+ """
214
+ if len(sys.argv) > 1 and sys.argv[1] == "--help":
215
+ print(main.__doc__)
216
+ sys.exit(0)
217
+
218
+ if not INTEGRATION_NAME:
219
+ raise ValueError("INTEGRATION_NAME env variable is required")
220
+
221
+ start_http_server(int(PROMETHEUS_PORT))
222
+
223
+ command = build_entry_point_func(COMMAND_NAME)
224
+ while True:
225
+ args = build_entry_point_args(
226
+ command, CONFIG, DRY_RUN, INTEGRATION_NAME, INTEGRATION_EXTRA_ARGS
227
+ )
228
+ sleep = SLEEP_DURATION_SECS
229
+ start_time = time.monotonic()
230
+ # Running the integration via Click, so we don't have to replicate
231
+ # the CLI logic here
232
+ execution_counter.labels(
233
+ integration=INTEGRATION_NAME, shards=SHARDS, shard_id=SHARD_ID_LABEL
234
+ ).inc()
235
+ try:
236
+ with command.make_context(info_name=COMMAND_NAME, args=args) as ctx: # type: ignore
237
+ ctx.ensure_object(dict)
238
+ command.invoke(ctx)
239
+ return_code = 0
240
+ # This is for when the integration explicitly
241
+ # calls sys.exit(N)
242
+ except SystemExit as exc_obj:
243
+ return_code = int(exc_obj.code) # type: ignore[arg-type]
244
+ # We have to be generic since we don't know what can happen
245
+ # in the integrations, but we want to continue the loop anyway
246
+ except Exception:
247
+ sleep = SLEEP_ON_ERROR
248
+ LOG.exception(f"Error running {COMMAND_NAME}")
249
+ return_code = ExitCodes.ERROR
250
+
251
+ time_spent = time.monotonic() - start_time
252
+
253
+ run_time.labels(
254
+ integration=INTEGRATION_NAME, shards=SHARDS, shard_id=SHARD_ID_LABEL
255
+ ).set(time_spent)
256
+ run_status.labels(
257
+ integration=INTEGRATION_NAME, shards=SHARDS, shard_id=SHARD_ID_LABEL
258
+ ).set(return_code)
259
+
260
+ if PUSHGATEWAY_ENABLED:
261
+ try:
262
+ env = _get_pushgateway_env_vars()
263
+ pushgateway_run_time.labels(
264
+ integration=INTEGRATION_NAME, shards=SHARDS, shard_id=SHARD_ID_LABEL
265
+ ).set(time_spent)
266
+ pushgateway_run_status.labels(
267
+ integration=INTEGRATION_NAME, shards=SHARDS, shard_id=SHARD_ID_LABEL
268
+ ).set(return_code)
269
+
270
+ grouping_key = {
271
+ "integration": INTEGRATION_NAME,
272
+ "shards": SHARDS,
273
+ "shard_id": SHARD_ID_LABEL,
274
+ }
275
+ push_to_gateway(
276
+ gateway=env["PUSHGATEWAY_URL"],
277
+ job="qontract-reconcile",
278
+ registry=pushgateway_registry,
279
+ handler=_push_gateway_basic_auth_handler,
280
+ grouping_key=grouping_key,
281
+ )
282
+ except PushgatewayBadConfigError as err:
283
+ LOG.exception(f"Error pushing to PushGateway: {err}")
284
+ return_code = ExitCodes.ERROR
285
+
286
+ if RUN_ONCE:
287
+ sys.exit(return_code)
288
+
289
+ time.sleep(int(sleep))
290
+
291
+
292
+ if __name__ == "__main__":
293
+ main()
reconcile/utils/binary.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import re
2
+ import shutil
2
3
  import subprocess
3
- from distutils.spawn import find_executable # pylint: disable=deprecated-module
4
4
  from functools import wraps
5
5
 
6
6
 
@@ -13,7 +13,7 @@ def binary(binaries=None):
13
13
  @wraps(f)
14
14
  def f_binary(*args, **kwargs):
15
15
  for b in binaries:
16
- if not find_executable(b):
16
+ if not shutil.which(b):
17
17
  raise Exception(
18
18
  f"Aborting: Could not find binary: {b}. "
19
19
  + f"Hint: https://command-not-found.com/{b}"
@@ -0,0 +1,198 @@
1
+ # Merge Requests
2
+
3
+ Module responsible for abstracting a Gitlab Merge Request.
4
+
5
+ ## Creating New MergeRequest Classes
6
+
7
+ Each type of merge request requires a class that inherits
8
+ from reconcile.utils.mr.base.MergeRequestBase.
9
+
10
+ That class has to comply with the specification below.
11
+
12
+ ### Class Name
13
+
14
+ The class name is given by the class variable `name`. That name will be
15
+ used all around, the most important place being the SQS Message parameter
16
+ `pr_type`, used later to create an instance of the same class.
17
+
18
+ While the name can be anything it is mandatory that it is defined.
19
+
20
+ ### Class Initialization
21
+
22
+ The `__init__` method has to call `super().__init__()` right after the minimum
23
+ parameters initialization. That is required to compose the SQS Message that
24
+ will later be used to create a new instance of the same class.
25
+
26
+ ### Class Methods
27
+
28
+ The minimum methods that have to be defined are:
29
+
30
+ * `title`: a property that is used to give the Gitlab Merge Request a title.
31
+ It can also be used for building commit messages or as content to committed
32
+ files.
33
+ * `description`: a description for the Merge Request.
34
+ * `process`: this method is called when submitting the Merge Request to gitlab.
35
+ it's the place for the merge request changes, like creating, updating and
36
+ deleting files. The `process` method is called after the local branch is
37
+ created and right after it the `gitlab_cli.project.mergerequests.create()`
38
+ is called.
39
+
40
+ ### Example
41
+
42
+ This is an example of a minimum implementation for a new MergeRequest class:
43
+
44
+ ```python
45
+ from reconcile.utils.mr.base import MergeRequestBase
46
+
47
+
48
+ class CreateDeleteUser(MergeRequestBase):
49
+
50
+ name = 'create_delete_user_mr'
51
+
52
+ def __init__(self, username, paths):
53
+ self.username = username
54
+ self.paths = paths
55
+
56
+ # Called right after the minimum parameters to recreate
57
+ # an instance with the same data. This is important for
58
+ # building up the SQS message payload.
59
+ super().__init__()
60
+
61
+ @property
62
+ def title(self) -> str:
63
+ return f'[{self.name}] delete user {self.username}'
64
+
65
+ @property
66
+ def description(self) -> str:
67
+ return f'delete user {self.username}'
68
+
69
+ def process(self, gitlab_cli):
70
+ for path in self.paths:
71
+ gitlab_cli.delete_file(branch_name=self.branch,
72
+ file_path=path,
73
+ commit_message=self.title)
74
+ ```
75
+
76
+ ## Sending MRs to SQS
77
+
78
+ To send a Merge Request to SQS, create the corresponding MergeRequest object:
79
+
80
+ ```python
81
+ from reconcile.utils.mr import CreateAppInterfaceNotificator
82
+
83
+
84
+ notification = {
85
+ 'notification_type': 'Outage',
86
+ 'description': 'The AppSRE team is currently investigating an outage',
87
+ 'short_description': 'Short Description',
88
+ }
89
+
90
+ merge_request = CreateAppInterfaceNotificator(notification=notification)
91
+
92
+ ```
93
+
94
+ then create the SQS Client instance:
95
+
96
+ ```python
97
+ from reconcile import queries
98
+
99
+ from reconcile.utils.sqs_gateway import SQSGateway
100
+ from reconcile.utils.secret_reader import SecretReader
101
+
102
+
103
+ accounts = queries.get_queue_aws_accounts()
104
+ secretReader = SecretReader(queries.get_secret_reader_settings())
105
+ sqs_cli = SQSGateway(accounts, secret_reader=secret_reader)
106
+ ```
107
+
108
+ and then submit the merge request to the SQS:
109
+
110
+ ```python
111
+ merge_request.submit_to_sqs(sqs_cli=sqs_cli)
112
+ ```
113
+
114
+ ## Sending MRs to Gitlab
115
+
116
+ To get the message from SQS and use it for sending a Merge Request to gitlab,
117
+ first get the SQS messages:
118
+
119
+
120
+ ```python
121
+ from reconcile import queries
122
+
123
+ from reconcile.utils.sqs_gateway import SQSGateway
124
+ from reconcile.utils.secret_reader import SecretReader
125
+
126
+
127
+ accounts = queries.get_queue_aws_accounts()
128
+ settings = queries.get_app_interface_settings()
129
+
130
+ secretReader = SecretReader(queries.get_secret_reader_settings())
131
+ sqs_cli = SQSGateway(accounts, secret_reader=secret_reader)
132
+ messages = sqs_cli.receive_messages()
133
+ ```
134
+
135
+ then create the Gitlab client instance:
136
+
137
+ ```python
138
+ from reconcile.utils.gitlab_api import GitLabApi
139
+
140
+ instance = queries.get_gitlab_instance()
141
+ saas_files = queries.get_saas_files_minimal()
142
+ gitlab_cli = GitLabApi(instance, project_id=gitlab_project_id,
143
+ settings=settings)
144
+ ```
145
+
146
+ and then loop the messages, creating the MergeRequest objects and submitting
147
+ the merge requests:
148
+
149
+ ```python
150
+ from reconcile.utils.mr import init_from_sqs_message
151
+
152
+ for message in messages:
153
+ receipt_handle, body = message[0], message[1]
154
+ merge_request = init_from_sqs_message(body)
155
+ merge_request.submit_to_gitlab(gitlab_cli=gitlab_cli)
156
+ sqs_cli.delete_message(receipt_handle)
157
+ ```
158
+
159
+ ## Using the MR Module for Qontract-reconcile Integrations
160
+
161
+ A given integration might be executed in different environments, like:
162
+
163
+ * Developer local machine.
164
+ * OpenShift cluster outside the VPN.
165
+ * OpenShift cluster inside the VPN.
166
+ * Jenkins periodic job.
167
+
168
+ Because we don't want the integrations to care if they are running inside or
169
+ outside the VPN, we use the `reconcile.mr_client_gateway.init()` to get the
170
+ on of SQS or GitLab client, according to the App Interface settings. Example:
171
+
172
+ ```python
173
+ from reconcile import mr_client_gateway
174
+ from reconcile.utils.mr import CreateDeleteAwsAccessKey
175
+
176
+
177
+ def run(dry_run, gitlab_project_id):
178
+ ...
179
+ mr = CreateDeleteAwsAccessKey(...)
180
+ with mr_client_gateway.init(gitlab_project_id=gitlab_project_id) as mr_cli:
181
+ mr.submit(cli=mr_cli)
182
+ ```
183
+
184
+ If we want to override what's set in App Interface and get a specific client,
185
+ say `gitlab`, we would:
186
+
187
+ ```python
188
+ from reconcile import mr_client_gateway
189
+ from reconcile.utils.mr import CreateDeleteAwsAccessKey
190
+
191
+
192
+ def run(dry_run, gitlab_project_id):
193
+ ...
194
+ mr = CreateDeleteAwsAccessKey(...)
195
+ with mr_client_gateway.init(sqs_or_gitlab='gitlab',
196
+ gitlab_project_id=gitlab_project_id) as mr_cli:
197
+ mr.submit(cli=mr_cli)
198
+ ```
reconcile/utils/oc_map.py CHANGED
@@ -122,7 +122,7 @@ class OCMap:
122
122
  connection_parameters=connection_parameters
123
123
  )
124
124
  try:
125
- oc_client: OCCli | OCLogMsg = self._oc_cls(
125
+ oc_client: OCCli | OCLogMsg = self._oc_cls( # type: ignore[assignment]
126
126
  connection_parameters=connection_parameters,
127
127
  init_projects=self._init_projects,
128
128
  init_api_resources=self._init_api_resources,
@@ -133,7 +133,7 @@ class OCMap:
133
133
  cluster,
134
134
  OCLogMsg(
135
135
  log_level=logging.ERROR,
136
- message=f"[{cluster}]" f" is unreachable: {e}",
136
+ message=f"[{cluster}] is unreachable: {e}",
137
137
  ),
138
138
  privileged,
139
139
  )
tools/qontract_cli.py CHANGED
File without changes
@@ -1,64 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: qontract-reconcile
3
- Version: 0.10.1rc1202
4
- Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
5
- Home-page: https://github.com/app-sre/qontract-reconcile
6
- Author: Red Hat App-SRE Team
7
- Author-email: sd-app-sre@redhat.com
8
- License: Apache License 2.0
9
- Classifier: Development Status :: 2 - Pre-Alpha
10
- Classifier: Programming Language :: Python
11
- Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.11
13
- Requires-Python: >=3.11
14
- Requires-Dist: sretoolbox~=2.6
15
- Requires-Dist: Click<9.0,>=7.0
16
- Requires-Dist: gql==3.1.0
17
- Requires-Dist: toml<0.11.0,>=0.10.0
18
- Requires-Dist: jsonpath-rw<1.5.0,>=1.4.0
19
- Requires-Dist: PyGithub<1.59,>=1.58
20
- Requires-Dist: hvac<0.8.0,>=0.7.0
21
- Requires-Dist: ldap3<2.10.0,>=2.9.1
22
- Requires-Dist: anymarkup<0.9.0,>=0.7.0
23
- Requires-Dist: python-gitlab~=4.6
24
- Requires-Dist: semver~=3.0
25
- Requires-Dist: boto3==1.34.94
26
- Requires-Dist: botocore==1.34.94
27
- Requires-Dist: urllib3~=2.2
28
- Requires-Dist: slack-sdk<4.0,>=3.10
29
- Requires-Dist: pypd<1.2.0,>=1.1.0
30
- Requires-Dist: Jinja2<3.2.0,>=2.10.1
31
- Requires-Dist: jira~=3.1
32
- Requires-Dist: pyOpenSSL~=23.0
33
- Requires-Dist: ruamel.yaml<0.18.0,>=0.17.22
34
- Requires-Dist: terrascript==0.9.0
35
- Requires-Dist: tabulate<0.9.0,>=0.8.6
36
- Requires-Dist: UnleashClient~=5.11
37
- Requires-Dist: prometheus-client~=0.8
38
- Requires-Dist: sentry-sdk~=2.0
39
- Requires-Dist: jenkins-job-builder~=4.3.0
40
- Requires-Dist: parse==1.18.0
41
- Requires-Dist: sendgrid<6.5.0,>=6.4.8
42
- Requires-Dist: dnspython~=2.1
43
- Requires-Dist: requests~=2.32
44
- Requires-Dist: kubernetes~=24.0
45
- Requires-Dist: websocket-client<0.55.0,>=0.35
46
- Requires-Dist: sshtunnel>=0.4.0
47
- Requires-Dist: croniter<1.1.0,>=1.0.15
48
- Requires-Dist: pydantic~=1.10.6
49
- Requires-Dist: MarkupSafe==2.1.1
50
- Requires-Dist: filetype~=1.2.0
51
- Requires-Dist: psycopg2~=2.9
52
- Requires-Dist: packaging~=23.1
53
- Requires-Dist: deepdiff==6.7.1
54
- Requires-Dist: jsonpath-ng==1.5.3
55
- Requires-Dist: networkx~=2.8
56
- Requires-Dist: rich<14.0.0,>=13.3.0
57
- Requires-Dist: dateparser~=1.1.7
58
- Requires-Dist: pyjwt~=2.7
59
- Requires-Dist: requests-oauthlib~=1.3
60
- Requires-Dist: dt==1.1.61
61
- Requires-Dist: jsonpatch~=1.33
62
- Requires-Dist: jsonpointer~=2.4
63
- Requires-Dist: yamllint==1.34.0
64
-
@@ -1,3 +0,0 @@
1
- reconcile
2
- release
3
- tools
File without changes