qontract-reconcile 0.10.1rc972__py3-none-any.whl → 0.10.1rc974__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qontract-reconcile
3
- Version: 0.10.1rc972
3
+ Version: 0.10.1rc974
4
4
  Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
5
5
  Home-page: https://github.com/app-sre/qontract-reconcile
6
6
  Author: Red Hat App-SRE Team
@@ -83,7 +83,7 @@ reconcile/openshift_saas_deploy_trigger_configs.py,sha256=eUejMGWuaQabZTLuvPLLvR
83
83
  reconcile/openshift_saas_deploy_trigger_images.py,sha256=iUsiBGJf-CyFw7tSLWo59rXmSvsVnN6TTaAObbsVpNg,936
84
84
  reconcile/openshift_saas_deploy_trigger_moving_commits.py,sha256=fpanSH-EGH15C9me--0VSpcpaw9BY4RTb8_mPtsSZGc,942
85
85
  reconcile/openshift_saas_deploy_trigger_upstream_jobs.py,sha256=0CjfeVQE0QrRrOVuTxkXvBUdKNtYLYuX4mZRB48PQ9g,940
86
- reconcile/openshift_serviceaccount_tokens.py,sha256=9zNSMEHwcaGWy4H-OLkk0zLw80CYTBnLVEZKB_xq6Ew,7331
86
+ reconcile/openshift_serviceaccount_tokens.py,sha256=ha3I7gk5C0Y-gD5_h-jvSheM2JamCx4KuihHpsaipUU,8840
87
87
  reconcile/openshift_tekton_resources.py,sha256=jKH5nw84aeYkgikxjQnjSOSF3m2kk3lp2BaPd3FfTew,16220
88
88
  reconcile/openshift_upgrade_watcher.py,sha256=9IB321hlRZZhzdaR9G3zoWAhVv0-KzNiEqx73p3-wmk,6539
89
89
  reconcile/openshift_users.py,sha256=63mar-swgidz8f10TCPJcofbMN9FETq-HuVFpi8dUL4,5293
@@ -316,7 +316,7 @@ reconcile/gql_definitions/glitchtip/glitchtip_project.py,sha256=AojrkCDGbVjY0TOk
316
316
  reconcile/gql_definitions/glitchtip_project_alerts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
317
317
  reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py,sha256=Pv6RcuIzpNmGc43eEq64nnKG0Dr7H0wjy5Xg1_oRltM,5197
318
318
  reconcile/gql_definitions/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
319
- reconcile/gql_definitions/integrations/integrations.py,sha256=LfpgVbCCCk20ohwP5pDea5fwxMFGrcgE6J_WHBuGqek,11595
319
+ reconcile/gql_definitions/integrations/integrations.py,sha256=HosEgRUlAkxLNoj2cnq3mrTdWDn9UvbNmtz6OcweIYk,11668
320
320
  reconcile/gql_definitions/jenkins_configs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
321
321
  reconcile/gql_definitions/jenkins_configs/jenkins_configs.py,sha256=0nMkH0G-AjQwu53fqHykth6X6jjbHdW2hBp5n7N-r24,2766
322
322
  reconcile/gql_definitions/jenkins_configs/jenkins_instances.py,sha256=b3gYVzQavxeLe4jSM5ZxrO77Vvs7kOljVOXEkTO943U,2165
@@ -530,7 +530,7 @@ reconcile/test/test_openshift_resources_base.py,sha256=LtlR9x3o7KkSEw0JN0fZhinFe
530
530
  reconcile/test/test_openshift_saas_deploy.py,sha256=3QXMrN9dXIiR0JktVDNQ7yJSexMTjZLb1tbRrB3-7uU,5991
531
531
  reconcile/test/test_openshift_saas_deploy_change_tester.py,sha256=1yVe54Hx9YdVjn6qdnKge5Sa_s732c-8uZqCnuT1gGI,12871
532
532
  reconcile/test/test_openshift_saas_deploy_trigger_cleaner.py,sha256=UQx1iJ21rsMa2whG-rtUIuTXbUzc0Ngr7jRLKXZCCCI,2838
533
- reconcile/test/test_openshift_serviceaccount_tokens.py,sha256=btaI8Au5XdZXXHtQfFPA8bGSTrXReC8ywJKUjaIQc-A,7341
533
+ reconcile/test/test_openshift_serviceaccount_tokens.py,sha256=tpCJMJw2nkaJQjTPCajd_0CZ1yivdV2qWJFCytPuyDo,8923
534
534
  reconcile/test/test_openshift_tekton_resources.py,sha256=RtRWsdm51S13OSkENC9nY_rOH0QELSCaO5tjF0XqIDI,11222
535
535
  reconcile/test/test_openshift_upgrade_watcher.py,sha256=0GDQ_YFHIX8DbkbDYSuLv9uZeeg4NwP1vlOqvSaZvN4,7183
536
536
  reconcile/test/test_prometheus_rules_tester.py,sha256=cgVkPM3KcAw69bOkJ6iR2Lfog_WgblyoqVRtXv4ly7o,5685
@@ -820,7 +820,7 @@ tools/app_interface_metrics_exporter.py,sha256=zkwkxdAUAxjdc-pzx2_oJXG25fo0Fnyd5
820
820
  tools/app_interface_reporter.py,sha256=uy9eRHf6EdvD8ZY2WYdroGXm18DOdnqVZyxaWN3Bm_0,17724
821
821
  tools/glitchtip_access_reporter.py,sha256=oPBnk_YoDuljU3v0FaChzOwwnk4vap1xEE67QEjzdqs,2948
822
822
  tools/glitchtip_access_revalidation.py,sha256=8kbBJk04mkq28kWoRDDkfCGIF3GRg3pJrFAh1sW0dbk,2821
823
- tools/qontract_cli.py,sha256=XPna9b9Khpj81uJJBL6KIHhIPYoDKeMi5jBuNTNGGrM,124486
823
+ tools/qontract_cli.py,sha256=S5oiRXYf_k47ovhoB4RIF7ysgqzaBp3CooTqBKg21mg,126061
824
824
  tools/sd_app_sre_alert_report.py,sha256=e9vAdyenUz2f5c8-z-5WY0wv-SJ9aePKDH2r4IwB6pc,5063
825
825
  tools/template_validation.py,sha256=qpKYaTgk0GOPGa2Ct5_5sKdwIHtCAKIBGzsMPuJU5fw,3371
826
826
  tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -851,8 +851,8 @@ tools/test/test_qontract_cli.py,sha256=_D61RFGAN5x44CY1tYbouhlGXXABwYfxKSWSQx3Jr
851
851
  tools/test/test_saas_promotion_state.py,sha256=dy4kkSSAQ7bC0Xp2CociETGN-2aABEfL6FU5D9Jl00Y,6056
852
852
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
853
853
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
854
- qontract_reconcile-0.10.1rc972.dist-info/METADATA,sha256=xWOaHmFbbnZOTavYlu2Vm9acmJ3tUubkXEJ37diSsT8,2262
855
- qontract_reconcile-0.10.1rc972.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
856
- qontract_reconcile-0.10.1rc972.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
857
- qontract_reconcile-0.10.1rc972.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
858
- qontract_reconcile-0.10.1rc972.dist-info/RECORD,,
854
+ qontract_reconcile-0.10.1rc974.dist-info/METADATA,sha256=94eluuqiBo17X4lkF_9R7aWCwxh250QExPnHgaFnazU,2262
855
+ qontract_reconcile-0.10.1rc974.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
856
+ qontract_reconcile-0.10.1rc974.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
857
+ qontract_reconcile-0.10.1rc974.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
858
+ qontract_reconcile-0.10.1rc974.dist-info/RECORD,,
@@ -72,6 +72,7 @@ query Integrations {
72
72
  }
73
73
  cluster {
74
74
  name
75
+ labels
75
76
  serverUrl
76
77
  insecureSkipTLSVerify
77
78
  jumpHost {
@@ -210,6 +211,7 @@ class EnvironmentV1(ConfiguredBaseModel):
210
211
 
211
212
  class ClusterV1(ConfiguredBaseModel):
212
213
  name: str = Field(..., alias="name")
214
+ labels: Optional[Json] = Field(..., alias="labels")
213
215
  server_url: str = Field(..., alias="serverUrl")
214
216
  insecure_skip_tls_verify: Optional[bool] = Field(..., alias="insecureSkipTLSVerify")
215
217
  jump_host: Optional[CommonJumphostFields] = Field(..., alias="jumpHost")
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ import random
2
3
  import sys
3
4
  from collections.abc import Callable, Iterable
4
5
 
@@ -35,6 +36,31 @@ def construct_sa_token_oc_resource(name: str, sa_token: str) -> OR:
35
36
  )
36
37
 
37
38
 
39
+ def service_account_token_request(name: str) -> OR:
40
+ """Create a service account token secret for a given service account."""
41
+ body = {
42
+ "apiVersion": "v1",
43
+ "kind": "Secret",
44
+ "type": "kubernetes.io/service-account-token",
45
+ "metadata": {
46
+ # service-account-name-<random-number>
47
+ "name": f"{name}-token-{random.randrange(99999):05d}",
48
+ "annotations": {
49
+ "kubernetes.io/service-account.name": name,
50
+ },
51
+ },
52
+ }
53
+ return OR(
54
+ body,
55
+ # We are marking this token secret as "unmanaged" because we just want to create it
56
+ # and not manage it in the future.
57
+ # Openshift will delete this token secret automatically if the service account is deleted.
58
+ f"{QONTRACT_INTEGRATION}-unmanaged",
59
+ QONTRACT_INTEGRATION_VERSION,
60
+ error_details=name,
61
+ )
62
+
63
+
38
64
  def get_tokens_for_service_account(
39
65
  service_account: str, tokens: list[dict]
40
66
  ) -> list[dict]:
@@ -79,6 +105,21 @@ def fetch_desired_state(
79
105
  sa_tokens = get_tokens_for_service_account(
80
106
  sat.service_account_name, namespace_secrets
81
107
  )
108
+ if not sa_tokens:
109
+ # OpenShfit 4.16+ does not automatically create service account tokens anymore so we need to create them manually.
110
+ logging.info(
111
+ f"[{sat.namespace.cluster.name}/{sat.namespace.name}] Creating token for service account: {sat.service_account_name}"
112
+ )
113
+ # Be aware: The secret won't be created by OpenShift as long as the service account doesn't exist.
114
+ ri.add_desired_resource(
115
+ cluster=sat.namespace.cluster.name,
116
+ namespace=sat.namespace.name,
117
+ resource=service_account_token_request(
118
+ sat.service_account_name
119
+ ),
120
+ )
121
+ continue
122
+
82
123
  sa_tokens.sort(key=lambda t: t["metadata"]["name"])
83
124
  # take the first token found
84
125
  sa_token = sa_tokens[0]["data"]["token"]
@@ -87,11 +128,6 @@ def fetch_desired_state(
87
128
  f"[{sat.namespace.cluster.name}/{sat.namespace.name}] Token not found for service account: {sat.service_account_name}"
88
129
  )
89
130
  raise
90
- except IndexError:
91
- logging.error(
92
- f"[{sat.namespace.cluster.name}/{sat.namespace.name}] 0 Secret found for service account: {sat.service_account_name}"
93
- )
94
- raise
95
131
 
96
132
  oc_resource = construct_sa_token_oc_resource(
97
133
  name=(
@@ -7,11 +7,13 @@ from pytest_mock import MockerFixture
7
7
 
8
8
  from reconcile.gql_definitions.openshift_serviceaccount_tokens.tokens import NamespaceV1
9
9
  from reconcile.openshift_serviceaccount_tokens import (
10
+ QONTRACT_INTEGRATION,
10
11
  canonicalize_namespaces,
11
12
  construct_sa_token_oc_resource,
12
13
  fetch_desired_state,
13
14
  get_namespaces_with_serviceaccount_tokens,
14
15
  get_tokens_for_service_account,
16
+ service_account_token_request,
15
17
  write_outputs_to_vault,
16
18
  )
17
19
  from reconcile.test.fixtures import Fixtures
@@ -50,7 +52,19 @@ def namespaces(query_func: Callable) -> list[NamespaceV1]:
50
52
  def ri(namespaces: list[NamespaceV1]) -> ResourceInventory:
51
53
  _ri = ResourceInventory()
52
54
  _ri.initialize_resource_type(
53
- cluster="cluster", namespace="namespace", resource_type="Secret"
55
+ cluster="cluster",
56
+ namespace="namespace",
57
+ resource_type="Secret",
58
+ )
59
+ _ri.initialize_resource_type(
60
+ cluster="another-cluster",
61
+ namespace="platform-changelog-stage",
62
+ resource_type="Secret",
63
+ )
64
+ _ri.initialize_resource_type(
65
+ cluster="another-cluster",
66
+ namespace="with-openshift-serviceaccount-tokens",
67
+ resource_type="Secret",
54
68
  )
55
69
  for ns in namespaces:
56
70
  _ri.initialize_resource_type(
@@ -226,3 +240,41 @@ def test_openshift_serviceaccount_tokens__fetch_desired_state(
226
240
  "desired"
227
241
  ]
228
242
  )
243
+
244
+
245
+ def test_openshift_serviceaccount_tokens__fetch_desired_state_create_token(
246
+ mocker: MockerFixture, namespaces: list[NamespaceV1], ri: ResourceInventory
247
+ ) -> None:
248
+ oc_map = mocker.create_autospec(OC_Map)
249
+ oc = mocker.create_autospec(OCCli)
250
+ oc_map.get.return_value = oc
251
+ oc.get_items.return_value = []
252
+
253
+ fetch_desired_state(
254
+ namespaces=[namespaces[0]],
255
+ ri=ri,
256
+ oc_map=oc_map,
257
+ )
258
+
259
+ assert (
260
+ len(
261
+ ri._clusters["cluster"]["with-openshift-serviceaccount-tokens"]["Secret"][
262
+ "desired"
263
+ ].keys()
264
+ )
265
+ == 1
266
+ )
267
+ r = list(
268
+ ri._clusters["cluster"]["with-openshift-serviceaccount-tokens"]["Secret"][
269
+ "desired"
270
+ ].values()
271
+ )[0]
272
+ assert r.body["type"] == "kubernetes.io/service-account-token"
273
+
274
+
275
+ def test_openshift_serviceaccount_tokens__service_account_token_request() -> None:
276
+ resource = service_account_token_request("grafana")
277
+ assert resource.name.startswith("grafana-")
278
+ assert resource.body["type"] == "kubernetes.io/service-account-token"
279
+ assert resource.kind == "Secret"
280
+ assert resource.integration != QONTRACT_INTEGRATION
tools/qontract_cli.py CHANGED
@@ -71,6 +71,7 @@ from reconcile.gql_definitions.common.app_interface_vault_settings import (
71
71
  AppInterfaceSettingsV1,
72
72
  )
73
73
  from reconcile.gql_definitions.fragments.aus_organization import AUSOCMOrganization
74
+ from reconcile.gql_definitions.integrations import integrations as integrations_gql
74
75
  from reconcile.gql_definitions.maintenance import maintenances as maintenances_gql
75
76
  from reconcile.jenkins_job_builder import init_jjb
76
77
  from reconcile.slack_base import slackapi_from_queries
@@ -2728,6 +2729,49 @@ def systems_and_tools(ctx):
2728
2729
  print_output(ctx.obj["options"], inventory.data, inventory.columns)
2729
2730
 
2730
2731
 
2732
+ @get.command(short_help="get integration logs")
2733
+ @click.argument("integration_name")
2734
+ @click.option(
2735
+ "--environment_name", default="production", help="environment to get logs from"
2736
+ )
2737
+ @click.pass_context
2738
+ def logs(ctx, integration_name: str, environment_name: str):
2739
+ integrations = [
2740
+ i
2741
+ for i in integrations_gql.query(query_func=gql.get_api().query).integrations
2742
+ or []
2743
+ if i.name == integration_name
2744
+ ]
2745
+ if not integrations:
2746
+ print("integration not found")
2747
+ return
2748
+ integration = integrations[0]
2749
+ vault_settings = get_app_interface_vault_settings()
2750
+ secret_reader = create_secret_reader(use_vault=vault_settings.vault)
2751
+ managed = integration.managed
2752
+ if not managed:
2753
+ print("integration is not managed")
2754
+ return
2755
+ namespaces = [
2756
+ m.namespace
2757
+ for m in managed
2758
+ if m.namespace.cluster.labels
2759
+ and m.namespace.cluster.labels.get("environment") == environment_name
2760
+ ]
2761
+ if not namespaces:
2762
+ print(f"no managed {environment_name} namespace found")
2763
+ return
2764
+ namespace = namespaces[0]
2765
+ cluster = namespaces[0].cluster
2766
+ if not cluster.automation_token:
2767
+ print("cluster automation token not found")
2768
+ return
2769
+ token = secret_reader.read_secret(cluster.automation_token)
2770
+
2771
+ command = f"oc --server {cluster.server_url} --token {token} --namespace {namespace.name} logs -c int -l app=qontract-reconcile-{integration.name}"
2772
+ print(command)
2773
+
2774
+
2731
2775
  @get.command
2732
2776
  @click.pass_context
2733
2777
  def jenkins_jobs(ctx):