qontract-reconcile 0.10.1rc1201__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 (140) hide show
  1. qontract_reconcile-0.10.2.dev1.dist-info/METADATA +500 -0
  2. {qontract_reconcile-0.10.1rc1201.dist-info → qontract_reconcile-0.10.2.dev1.dist-info}/RECORD +14 -132
  3. {qontract_reconcile-0.10.1rc1201.dist-info → qontract_reconcile-0.10.2.dev1.dist-info}/WHEEL +1 -2
  4. {qontract_reconcile-0.10.1rc1201.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/external_resources/manager.py +12 -1
  8. reconcile/external_resources/model.py +11 -0
  9. reconcile/glitchtip/README.md +150 -0
  10. reconcile/gql_definitions/introspection.json +51176 -0
  11. reconcile/run_integration.py +293 -0
  12. reconcile/utils/binary.py +2 -2
  13. reconcile/utils/mr/README.md +198 -0
  14. reconcile/utils/oc_map.py +2 -2
  15. tools/qontract_cli.py +0 -0
  16. qontract_reconcile-0.10.1rc1201.dist-info/METADATA +0 -64
  17. qontract_reconcile-0.10.1rc1201.dist-info/top_level.txt +0 -3
  18. reconcile/test/__init__.py +0 -0
  19. reconcile/test/conftest.py +0 -157
  20. reconcile/test/fixtures.py +0 -24
  21. reconcile/test/saas_auto_promotions_manager/__init__.py +0 -0
  22. reconcile/test/saas_auto_promotions_manager/conftest.py +0 -170
  23. reconcile/test/saas_auto_promotions_manager/merge_request_manager/__init__.py +0 -0
  24. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/__init__.py +0 -0
  25. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/conftest.py +0 -115
  26. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/data_keys.py +0 -19
  27. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_desired_state.py +0 -66
  28. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_merge_request_manager.py +0 -86
  29. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_mr_parser.py +0 -352
  30. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_reconciler.py +0 -494
  31. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/__init__.py +0 -0
  32. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/conftest.py +0 -25
  33. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_multiple_namespaces.py +0 -37
  34. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_single_namespace.py +0 -81
  35. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_single_target.py +0 -61
  36. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_json_path_selector.py +0 -74
  37. reconcile/test/saas_auto_promotions_manager/test_integration_test.py +0 -52
  38. reconcile/test/saas_auto_promotions_manager/utils/__init__.py +0 -0
  39. reconcile/test/test_acs_notifiers.py +0 -393
  40. reconcile/test/test_acs_policies.py +0 -497
  41. reconcile/test/test_acs_rbac.py +0 -865
  42. reconcile/test/test_aggregated_list.py +0 -237
  43. reconcile/test/test_amtool.py +0 -37
  44. reconcile/test/test_aws_ami_cleanup.py +0 -230
  45. reconcile/test/test_aws_ami_share.py +0 -68
  46. reconcile/test/test_aws_cloudwatch_log_retention.py +0 -434
  47. reconcile/test/test_aws_iam_keys.py +0 -70
  48. reconcile/test/test_aws_iam_password_reset.py +0 -35
  49. reconcile/test/test_aws_support_cases_sos.py +0 -23
  50. reconcile/test/test_checkpoint.py +0 -178
  51. reconcile/test/test_cli.py +0 -41
  52. reconcile/test/test_closedbox_endpoint_monitoring.py +0 -207
  53. reconcile/test/test_dashdotdb_dora.py +0 -245
  54. reconcile/test/test_database_access_manager.py +0 -660
  55. reconcile/test/test_deadmanssnitch.py +0 -290
  56. reconcile/test/test_gabi_authorized_users.py +0 -72
  57. reconcile/test/test_gcr_mirror.py +0 -14
  58. reconcile/test/test_github_org.py +0 -156
  59. reconcile/test/test_github_repo_invites.py +0 -119
  60. reconcile/test/test_gitlab_housekeeping.py +0 -333
  61. reconcile/test/test_gitlab_labeler.py +0 -126
  62. reconcile/test/test_gitlab_members.py +0 -219
  63. reconcile/test/test_gitlab_permissions.py +0 -164
  64. reconcile/test/test_instrumented_wrappers.py +0 -18
  65. reconcile/test/test_integrations_manager.py +0 -1252
  66. reconcile/test/test_jenkins_worker_fleets.py +0 -57
  67. reconcile/test/test_jira_permissions_validator.py +0 -519
  68. reconcile/test/test_jump_host.py +0 -114
  69. reconcile/test/test_ldap_users.py +0 -125
  70. reconcile/test/test_make.py +0 -28
  71. reconcile/test/test_ocm_additional_routers.py +0 -133
  72. reconcile/test/test_ocm_clusters.py +0 -798
  73. reconcile/test/test_ocm_clusters_manifest_updates.py +0 -87
  74. reconcile/test/test_ocm_machine_pools.py +0 -1103
  75. reconcile/test/test_ocm_update_recommended_version.py +0 -145
  76. reconcile/test/test_ocm_upgrade_scheduler_org_updater.py +0 -125
  77. reconcile/test/test_openshift_base.py +0 -1269
  78. reconcile/test/test_openshift_cluster_bots.py +0 -240
  79. reconcile/test/test_openshift_namespace_labels.py +0 -344
  80. reconcile/test/test_openshift_namespaces.py +0 -256
  81. reconcile/test/test_openshift_resource.py +0 -443
  82. reconcile/test/test_openshift_resources_base.py +0 -478
  83. reconcile/test/test_openshift_saas_deploy.py +0 -188
  84. reconcile/test/test_openshift_saas_deploy_change_tester.py +0 -308
  85. reconcile/test/test_openshift_saas_deploy_trigger_cleaner.py +0 -65
  86. reconcile/test/test_openshift_serviceaccount_tokens.py +0 -282
  87. reconcile/test/test_openshift_tekton_resources.py +0 -265
  88. reconcile/test/test_openshift_upgrade_watcher.py +0 -223
  89. reconcile/test/test_prometheus_rules_tester.py +0 -151
  90. reconcile/test/test_quay_membership.py +0 -86
  91. reconcile/test/test_quay_mirror.py +0 -172
  92. reconcile/test/test_quay_mirror_org.py +0 -82
  93. reconcile/test/test_quay_repos.py +0 -59
  94. reconcile/test/test_queries.py +0 -53
  95. reconcile/test/test_repo_owners.py +0 -47
  96. reconcile/test/test_requests_sender.py +0 -139
  97. reconcile/test/test_saasherder.py +0 -1611
  98. reconcile/test/test_saasherder_allowed_secret_paths.py +0 -125
  99. reconcile/test/test_secret_reader.py +0 -153
  100. reconcile/test/test_slack_base.py +0 -183
  101. reconcile/test/test_slack_usergroups.py +0 -785
  102. reconcile/test/test_sql_query.py +0 -316
  103. reconcile/test/test_status_board.py +0 -258
  104. reconcile/test/test_terraform_aws_route53.py +0 -29
  105. reconcile/test/test_terraform_cloudflare_dns.py +0 -117
  106. reconcile/test/test_terraform_cloudflare_resources.py +0 -408
  107. reconcile/test/test_terraform_cloudflare_users.py +0 -747
  108. reconcile/test/test_terraform_repo.py +0 -440
  109. reconcile/test/test_terraform_resources.py +0 -519
  110. reconcile/test/test_terraform_tgw_attachments.py +0 -1295
  111. reconcile/test/test_terraform_users.py +0 -152
  112. reconcile/test/test_terraform_vpc_peerings.py +0 -576
  113. reconcile/test/test_terraform_vpc_peerings_build_desired_state.py +0 -1434
  114. reconcile/test/test_three_way_diff_strategy.py +0 -131
  115. reconcile/test/test_utils_jinja2.py +0 -130
  116. reconcile/test/test_vault_replication.py +0 -534
  117. reconcile/test/test_vault_utils.py +0 -47
  118. reconcile/test/test_version_bump.py +0 -18
  119. reconcile/test/test_vpc_peerings_validator.py +0 -194
  120. reconcile/test/test_wrong_region.py +0 -78
  121. release/__init__.py +0 -0
  122. release/test_version.py +0 -50
  123. release/version.py +0 -104
  124. tools/cli_commands/test/__init__.py +0 -0
  125. tools/cli_commands/test/conftest.py +0 -332
  126. tools/cli_commands/test/test_aws_cost_report.py +0 -258
  127. tools/cli_commands/test/test_cost_management_api.py +0 -326
  128. tools/cli_commands/test/test_gpg_encrypt.py +0 -235
  129. tools/cli_commands/test/test_openshift_cost_optimization_report.py +0 -255
  130. tools/cli_commands/test/test_openshift_cost_report.py +0 -295
  131. tools/cli_commands/test/test_util.py +0 -70
  132. tools/test/__init__.py +0 -0
  133. tools/test/conftest.py +0 -77
  134. tools/test/test_app_interface_metrics_exporter.py +0 -48
  135. tools/test/test_erv2.py +0 -80
  136. tools/test/test_get_container_images.py +0 -230
  137. tools/test/test_qontract_cli.py +0 -197
  138. tools/test/test_saas_promotion_state.py +0 -187
  139. tools/test/test_sd_app_sre_alert_report.py +0 -74
  140. 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.1rc1201
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