qontract-reconcile 0.10.2.dev195__py3-none-any.whl → 0.10.2.dev196__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.4
2
2
  Name: qontract-reconcile
3
- Version: 0.10.2.dev195
3
+ Version: 0.10.2.dev196
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
@@ -14,7 +14,7 @@ reconcile/closedbox_endpoint_monitoring_base.py,sha256=al7m8EgnnYx90rY1REryW3byN
14
14
  reconcile/cluster_deployment_mapper.py,sha256=5gumAaRCcFXsabUJ1dnuUy9WrP_FEEM5JnOnE8ch9sE,2326
15
15
  reconcile/dashdotdb_base.py,sha256=83ZWIf5JJk3P_D69y2TmXRcQr6ELJGlv10OM0h7fJVs,4767
16
16
  reconcile/dashdotdb_cso.py,sha256=QRK0YfIqO4rehs8btD3l_GXIO2ZIycTQEKEthBdB0xA,3639
17
- reconcile/dashdotdb_dora.py,sha256=juZvr7R45f3TLbIZy290Oa1OtsSnqX-Ez71ppYAUjXw,17780
17
+ reconcile/dashdotdb_dora.py,sha256=UI83R8VsrX3vd2ocBakQDKPNE5Ym2a8pnAGUhfkDeR0,17771
18
18
  reconcile/dashdotdb_dvo.py,sha256=lCkZ0iby6HrNQb-3kYb6xrt8wCjVUZYxKzz9SiStfHU,8946
19
19
  reconcile/dashdotdb_slo.py,sha256=TvKdMOtUZcZP9QydcUJMKh0zURHgOMN_RTpQpCkD1Z8,3960
20
20
  reconcile/database_access_manager.py,sha256=Z3aAmw2LsmMIIor-bOGzziVZdVNC82Gmw8oHBUAFf-8,25577
@@ -618,8 +618,8 @@ reconcile/utils/external_resource_spec.py,sha256=qeupz4t4trd2uPjlHjf_AFA9Y-EKrMn
618
618
  reconcile/utils/external_resources.py,sha256=YzTb0xAcNdmKO326mGQy7BmST56CZcdru4lX7ai_7kw,7579
619
619
  reconcile/utils/filtering.py,sha256=S4PbMHuFr3ED0P2Q_ea5CAaB7FimI62B-F5YTaKrphA,402
620
620
  reconcile/utils/git.py,sha256=o4p9m8jlzCJDcutl2HErvGLhL6sZ1NB4Aw3zGcQIzso,2427
621
- reconcile/utils/github_api.py,sha256=o4J0ZU1ZSr9808uoorKHv19iae-eLo85yrCZX67p2kw,2822
622
- reconcile/utils/gitlab_api.py,sha256=U7l26fM1lQ4g-O06D0ZawvOXF_yzzH5NdR5iDx2ZJFA,28616
621
+ reconcile/utils/github_api.py,sha256=S1vO-hvYPzm5BIychVIHSYibMns0HBmLgS78MkPfunE,3402
622
+ reconcile/utils/gitlab_api.py,sha256=-wf1praBpU9h4TratCnAPBHEYgYJZLib6dl6Nszzrrw,28761
623
623
  reconcile/utils/gpg.py,sha256=EKG7_fdMv8BMlV5yUdPiqoTx-KrzmVSEAl2sLkaKwWI,1123
624
624
  reconcile/utils/gql.py,sha256=C0thIm_k9MBldfqwHzyqtYZk9sIvMdm9IbbnXLGwjD8,14158
625
625
  reconcile/utils/grouping.py,sha256=vr9SFHZ7bqmHYrvYcEZt-Er3-yQYfAAdq5sHLZVmXPY,456
@@ -656,7 +656,7 @@ reconcile/utils/promtool.py,sha256=xmPBWEApkk0L2qZBAvTxakNXxfTz-tVLPFxGnpsxXnM,2
656
656
  reconcile/utils/quay_api.py,sha256=uE_jxcdy3ViHtYFAfwDQuFDaO7Pr6AAPoVnmORbyHio,7822
657
657
  reconcile/utils/quay_mirror.py,sha256=dpWCNv5lITwIk6Q9RkmqaQKHNk_JPy27UQEribJ7E-U,1324
658
658
  reconcile/utils/raw_github_api.py,sha256=2WKtE8ABYYB9UGOAh9N_kLkksBWL3320Z2_scteZddI,2805
659
- reconcile/utils/repo_owners.py,sha256=Xwe1HOcMZe7Pknk47GLZHg5LDpDElmGfmc_x6pAdzsg,6589
659
+ reconcile/utils/repo_owners.py,sha256=P0QX6F0oB8wYA08yiyzhYUiBtU57iIK_PsxbzKENbKM,6571
660
660
  reconcile/utils/rest_api_base.py,sha256=MT7tp6CQO2S5aKfVOzw_hipWg7wAGoOqkm4qurI1hEU,4342
661
661
  reconcile/utils/ruamel.py,sha256=FzL4_L0FnMOUZmgThrZSMJs5MTdXwiy-E9MZWfk8bh8,397
662
662
  reconcile/utils/secret_reader.py,sha256=MaP56KZaAE35EyYbgAitdm6fUSxdzWeGFSOym9qiZkw,10206
@@ -675,7 +675,7 @@ reconcile/utils/three_way_diff_strategy.py,sha256=oQcHXd9LVhirJfoaOBoHUYuZVGfyL2
675
675
  reconcile/utils/throughput.py,sha256=iP4UWAe2LVhDo69mPPmgo9nQ7RxHD6_GS8MZe-aSiuM,344
676
676
  reconcile/utils/vault.py,sha256=aSA8l9cJlPUHpChFGl27nSY-Mpq9FMjBo7Dcgb1BVfM,15036
677
677
  reconcile/utils/vaultsecretref.py,sha256=0KUSzuvTRxPyKY919TO3-B_eYg4_76fzKvMF8j5s1G0,911
678
- reconcile/utils/vcs.py,sha256=AK35vIjx9bXYclKmvNekpaG_OETt-ZybibwV-m123xc,10186
678
+ reconcile/utils/vcs.py,sha256=X9wCe0aWrDxP9kuOABLjkex0fYW-6vuYwjboYYlNMZM,10225
679
679
  reconcile/utils/acs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
680
680
  reconcile/utils/acs/base.py,sha256=4UsDrCpAOuddL3PKNuIQYoJP1BtZQNNB8_KEX0lXneg,2532
681
681
  reconcile/utils/acs/policies.py,sha256=jpbi3qpGkBD_X6MfzsX12dPajUbmACmhIOz_0rDvYzs,5489
@@ -706,7 +706,7 @@ reconcile/utils/internal_groups/models.py,sha256=y_IqBVqfGqNXiu0VudvBWFrm_-uafVm
706
706
  reconcile/utils/jinja2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
707
707
  reconcile/utils/jinja2/extensions.py,sha256=7K-uo6G2eCWa98MHT8fRPYIKCLQB_5D2keqQ_LyAfHM,1293
708
708
  reconcile/utils/jinja2/filters.py,sha256=JfO_14APySBPidsMvHXG-8dULNPddZCE15Umjk_aSBk,4830
709
- reconcile/utils/jinja2/utils.py,sha256=AQYEhrA4eEl7-tjf8KyVheSGWS3n-56K7zoJIH9IMF4,8819
709
+ reconcile/utils/jinja2/utils.py,sha256=UTA0oOdhUgUBY57lWxgJS9WIAzP1ONm7mgGhY_DrczM,8821
710
710
  reconcile/utils/jobcontroller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
711
711
  reconcile/utils/jobcontroller/controller.py,sha256=Vh08lZuCSIIceWGSDhBB00iFwTI9eeKZW1sfHlkAvSo,15373
712
712
  reconcile/utils/jobcontroller/models.py,sha256=x9YIvWfYOOvXNKToFVx1H7qDrZb0Sa1KI_4Y0gl7rMM,6336
@@ -721,17 +721,17 @@ reconcile/utils/mr/README.md,sha256=i9sCLkDFhSxAUtpa_I1_TxhR5vPOLcowuwn2VEWO41w,
721
721
  reconcile/utils/mr/__init__.py,sha256=hcfHDIIIsJT4C0BnzDnyeZEfZdamrqHzMLcBzIT1ibI,2578
722
722
  reconcile/utils/mr/app_interface_reporter.py,sha256=6Kpg93V9FvcOke9Jimkva359MQ-ZyBIkUpf8QIA6-to,1793
723
723
  reconcile/utils/mr/app_sre_tekton_access_report.py,sha256=i7JeTkyaq0HyVGbAFKCNbiLy3NVE05IXqJzwArsGre4,1513
724
- reconcile/utils/mr/aws_access.py,sha256=9MMpYD24j2lLr_hLeMSh_OsJ07waalrlNpz-JlOsKAM,2575
724
+ reconcile/utils/mr/aws_access.py,sha256=_nQyRkNbz_UVnRv5YkGkc6o8NMlRJEHQ91BVscrRQag,2609
725
725
  reconcile/utils/mr/base.py,sha256=pHRmyxJzlBy9ldjYKOdHg9-9KbsK8bdRScntKZa5Tew,8130
726
- reconcile/utils/mr/clusters_updates.py,sha256=pcusPAwRUkvyk_-bixsRNTzSvpTLypJ1kflq5UEVgcM,2271
726
+ reconcile/utils/mr/clusters_updates.py,sha256=T9eONmyTshEcd6UbYNDAslC6bw-Klql-Vr0scQBZnJE,2313
727
727
  reconcile/utils/mr/glitchtip_access_reporter.py,sha256=cTkOtzdgeKPaqro0VS2hDuAClQiN4nZATh-mplQC-AI,1369
728
728
  reconcile/utils/mr/labels.py,sha256=9QRTRjZAtq45zELd9SwavaraczMjwjn5no3RK1YxFTg,825
729
729
  reconcile/utils/mr/notificator.py,sha256=f8IcGQ1_iBsXJFnhPsWQ7UE3NfigaOrXcVieJPplYrY,2955
730
- reconcile/utils/mr/ocm_update_recommended_version.py,sha256=p_aVP0TGrlKk9WBwgQnYWqUDsED_Hg6G5Bqj0UvtRwA,1536
731
- reconcile/utils/mr/ocm_upgrade_scheduler_org_updates.py,sha256=5EncHGr4QRnZgHedRfCwMYZ9CaijYzHGj7-M6lhtQRo,3004
732
- reconcile/utils/mr/promote_qontract.py,sha256=wgvX2CBlcZaihKJSXJ0zcEK8NGaEP2_DUQDz0STzGes,7158
733
- reconcile/utils/mr/update_access_report_base.py,sha256=4Grohtp44v1sSHZyIAYOwClxH8SLj_nnOrCcHPKp9p0,4361
734
- reconcile/utils/mr/user_maintenance.py,sha256=KSfl4i0k1CqCa9mj93bvFxHeBHPRMn3sBWdGS1nNzYY,5371
730
+ reconcile/utils/mr/ocm_update_recommended_version.py,sha256=KGOBURLE3XMynjAFej5D5PO9MJRDqMeoL1C8jLJMhD8,1570
731
+ reconcile/utils/mr/ocm_upgrade_scheduler_org_updates.py,sha256=0myPruETuT6V2bjSSVPVsQu0ZHE9Jf3kRx5htt_fhKM,3038
732
+ reconcile/utils/mr/promote_qontract.py,sha256=oOjfxkDAFhMDWCT9xaRyLSYHcc67jCc34s_tENOuHGw,7226
733
+ reconcile/utils/mr/update_access_report_base.py,sha256=LOXTIonpfxXrvZI9nPqkszW3OnuIgbFM7pFpqHxTMp0,4395
734
+ reconcile/utils/mr/user_maintenance.py,sha256=OC9-3YkpXsYKZGsdOdx-NLq7AkjgrhDR2JL7hov7BV8,5669
735
735
  reconcile/utils/ocm/__init__.py,sha256=Y-bp8GomMpyCo0tFW6kJ78-ZG1UIupYRtBzbMWU0kwM,798
736
736
  reconcile/utils/ocm/addons.py,sha256=_LDdJ-gapM3s5exKlIUt-MlXZTAUoHezbYBU0QmvfWQ,7335
737
737
  reconcile/utils/ocm/base.py,sha256=8rZ8WilNeAfq7HRNI8kNOLB4VcYzQpqQ5gvWbS54MuM,14576
@@ -763,7 +763,7 @@ reconcile/utils/runtime/sharding.py,sha256=r0ieUtNed7NvknSw6qQrCkKpVXE1shuHGnfFc
763
763
  reconcile/utils/saasherder/__init__.py,sha256=3U8plqMAPRE1kjwZ5YnIsYsggTf4_gS7flRUEuXVBAs,343
764
764
  reconcile/utils/saasherder/interfaces.py,sha256=NEYQspYfyWQhBeJyNCqSFbixi1A4wRVGB7FeNM5BDCk,9141
765
765
  reconcile/utils/saasherder/models.py,sha256=JaOz_DEtudJZhiDe90kaBlJkppFufn81V92oK9PHYx0,10208
766
- reconcile/utils/saasherder/saasherder.py,sha256=V8DOaMvY1POYd_EquIlU2sI62l64C-PjtaTCuzwVbn4,87306
766
+ reconcile/utils/saasherder/saasherder.py,sha256=uQovEpcnZXpJlG_tBUY0X1v5obYMH55vc5cSvi3eZjU,86862
767
767
  reconcile/utils/terraform/__init__.py,sha256=zNbiyTWo35AT1sFTElL2j_AA0jJ_yWE_bfFn-nD2xik,250
768
768
  reconcile/utils/terraform/config.py,sha256=5UVrd563TMcvi4ooa5JvWVDW1I3bIWg484u79evfV_8,164
769
769
  reconcile/utils/terraform/config_client.py,sha256=gRL1rQ0AqvShei_rcGqC3HDYGskOFKE1nPrJyJE9yno,4676
@@ -809,7 +809,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
809
809
  tools/saas_promotion_state/saas_promotion_state.py,sha256=UfwwRLS5Ya4_Nh1w5n1dvoYtchQvYE9yj1VANt2IKqI,3925
810
810
  tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
811
811
  tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
812
- qontract_reconcile-0.10.2.dev195.dist-info/METADATA,sha256=MHzXq3sy6lXSH9U2kaPoAkQ9w9LDLvg6vK7zNg4CbwQ,24555
813
- qontract_reconcile-0.10.2.dev195.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
814
- qontract_reconcile-0.10.2.dev195.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
815
- qontract_reconcile-0.10.2.dev195.dist-info/RECORD,,
812
+ qontract_reconcile-0.10.2.dev196.dist-info/METADATA,sha256=uRgPJGf2hvsWk3jhJUQz581Qv8WkL4Q4R70sX5tYxSo,24555
813
+ qontract_reconcile-0.10.2.dev196.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
814
+ qontract_reconcile-0.10.2.dev196.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
815
+ qontract_reconcile-0.10.2.dev196.dist-info/RECORD,,
@@ -402,7 +402,7 @@ class DashdotdbDORA(DashdotdbBase):
402
402
  if not saas_file_yaml:
403
403
  LOG.info(f"failed to fetch saas file {saastarget.path} at {sha}")
404
404
  return (None, None)
405
- saas_file = yaml.safe_load(saas_file_yaml.decode())
405
+ saas_file = yaml.safe_load(saas_file_yaml)
406
406
  except Exception as e:
407
407
  LOG.info(f"failed to decode saas file {saastarget.path} with error: {e}")
408
408
  return (None, None)
@@ -1,13 +1,21 @@
1
+ import base64
1
2
  import os
2
3
  from pathlib import Path
3
4
  from types import TracebackType
4
5
  from urllib.parse import urlparse
5
6
 
6
7
  from github import Commit, Github, GithubException, UnknownObjectException
8
+ from github.Repository import Repository
7
9
  from sretoolbox.utils import retry
8
10
 
9
11
  GH_BASE_URL = os.environ.get("GITHUB_API", "https://api.github.com")
10
12
 
13
+ MAX_FILE_CONTENT_SIZE = 1024**2 # 1MB
14
+
15
+
16
+ class UnsupportedDirectoryError(Exception):
17
+ pass
18
+
11
19
 
12
20
  class GithubRepositoryApi:
13
21
  """
@@ -66,16 +74,36 @@ class GithubRepositoryApi:
66
74
  tree_items.append(tree_item)
67
75
  return tree_items
68
76
 
77
+ @staticmethod
78
+ def get_raw_file(
79
+ repo: Repository,
80
+ path: str,
81
+ ref: str,
82
+ ) -> bytes:
83
+ content = repo.get_contents(path=path, ref=ref)
84
+ if isinstance(content, list):
85
+ raise UnsupportedDirectoryError(
86
+ f"Path {path} of ref {ref} in repo {repo.full_name} is a directory!"
87
+ )
88
+ if content.size < MAX_FILE_CONTENT_SIZE:
89
+ return content.decoded_content
90
+ blob = repo.get_git_blob(content.sha)
91
+ return base64.b64decode(blob.content)
92
+
69
93
  @retry()
70
- def get_file(self, path: str, ref: str = "master") -> bytes | None:
94
+ def get_file(
95
+ self,
96
+ path: str,
97
+ ref: str = "master",
98
+ ) -> bytes | None:
71
99
  try:
72
- content = self._repo.get_contents(path=path, ref=ref)
73
- if isinstance(content, list):
74
- # TODO: we should probably raise an exception here
75
- # or handle this properly
76
- # -> for now staying backwards compatible
77
- return None
78
- return content.decoded_content
100
+ return self.get_raw_file(
101
+ repo=self._repo,
102
+ path=path,
103
+ ref=ref,
104
+ )
105
+ except UnsupportedDirectoryError:
106
+ return None
79
107
  except GithubException as e:
80
108
  # handling a bug in the upstream GH library
81
109
  # https://github.com/PyGithub/PyGithub/issues/3179
@@ -41,7 +41,6 @@ from gitlab.v4.objects import (
41
41
  GroupMember,
42
42
  PersonalAccessToken,
43
43
  Project,
44
- ProjectFile,
45
44
  ProjectHook,
46
45
  ProjectIssue,
47
46
  ProjectIssueManager,
@@ -666,25 +665,19 @@ class GitLabApi:
666
665
  *,
667
666
  ref: str = "master",
668
667
  recursive: bool = False,
669
- project: Project | None = None,
670
- path: str = "",
671
668
  ) -> list[dict]:
672
669
  """
673
670
  Get a list of repository files and directories in a project.
674
671
 
675
672
  :param ref: The name of a repository branch or tag or, if not given, the default branch.
676
673
  :param recursive: Boolean value used to get a recursive tree. Default is false.
677
- :param project: The project to get the tree from, if None, use the current project
678
- :param path: The path inside the repository. Used to get content of subdirectories.
679
674
 
680
675
  :return: list of tree objects
681
676
  """
682
- target_project = self.project if project is None else project
683
677
  return cast(
684
678
  list[dict],
685
- target_project.repository_tree(
679
+ self.project.repository_tree(
686
680
  ref=ref,
687
- path=path,
688
681
  recursive=recursive,
689
682
  pagination="keyset",
690
683
  per_page=MAX_PER_PAGE,
@@ -692,13 +685,36 @@ class GitLabApi:
692
685
  ),
693
686
  )
694
687
 
695
- def get_file(self, path: str, ref: str = "master") -> ProjectFile | None:
688
+ @staticmethod
689
+ def get_raw_file(
690
+ project: Project,
691
+ path: str,
692
+ ref: str,
693
+ ) -> bytes:
694
+ file_path = path.lstrip("/")
695
+ return project.files.raw(
696
+ file_path=file_path,
697
+ ref=ref,
698
+ )
699
+
700
+ def get_file(
701
+ self,
702
+ path: str,
703
+ ref: str = "master",
704
+ ) -> bytes | None:
696
705
  """
697
- Wrapper around Gitlab.files.get() with exception handling.
706
+ Get the raw content of a file in a project.
707
+
708
+ :param path: The path to the file in the repository.
709
+ :param ref: The name of branch, tag or commit.
710
+ :return: The content of the file as bytes, or None if the file does not exist.
698
711
  """
699
712
  try:
700
- path = path.lstrip("/")
701
- return self.project.files.get(file_path=path, ref=ref)
713
+ return self.get_raw_file(
714
+ project=self.project,
715
+ path=path,
716
+ ref=ref,
717
+ )
702
718
  except GitlabGetError:
703
719
  return None
704
720
 
@@ -12,6 +12,7 @@ from reconcile.checkpoint import url_makes_sense
12
12
  from reconcile.github_users import init_github
13
13
  from reconcile.utils import gql
14
14
  from reconcile.utils.aws_api import AWSApi
15
+ from reconcile.utils.github_api import GithubRepositoryApi
15
16
  from reconcile.utils.helpers import flatten
16
17
  from reconcile.utils.jinja2.extensions import B64EncodeExtension, RaiseErrorExtension
17
18
  from reconcile.utils.jinja2.filters import (
@@ -116,10 +117,12 @@ def lookup_github_file_content(
116
117
  )
117
118
 
118
119
  gh = init_github()
119
- content = gh.get_repo(repo).get_contents(path, ref)
120
- if isinstance(content, list):
121
- raise Exception(f"multiple files found for {repo}/{path}/{ref}")
122
- return content.decoded_content.decode("utf-8")
120
+ content = GithubRepositoryApi.get_raw_file(
121
+ repo=gh.get_repo(repo),
122
+ path=path,
123
+ ref=ref,
124
+ )
125
+ return content.decode("utf-8")
123
126
 
124
127
 
125
128
  def lookup_graphql_query_results(query: str, **kwargs: dict[str, Any]) -> list[Any]:
@@ -37,10 +37,12 @@ class CreateDeleteAwsAccessKey(MergeRequestBase):
37
37
 
38
38
  def process(self, gitlab_cli: GitLabApi) -> None:
39
39
  # add key to deleteKeys list to be picked up by aws-iam-keys
40
- raw_file = gitlab_cli.project.files.get(
41
- file_path=self.path, ref=gitlab_cli.main_branch
40
+ raw_file = gitlab_cli.get_raw_file(
41
+ project=gitlab_cli.project,
42
+ path=self.path,
43
+ ref=gitlab_cli.main_branch,
42
44
  )
43
- content = yaml.load(raw_file.decode(), Loader=yaml.RoundTripLoader)
45
+ content = yaml.load(raw_file, Loader=yaml.RoundTripLoader)
44
46
 
45
47
  content.setdefault("deleteKeys", [])
46
48
  content["deleteKeys"].append(self.key)
@@ -37,10 +37,12 @@ class CreateClustersUpdates(MergeRequestBase):
37
37
  continue
38
38
 
39
39
  cluster_path = cluster_updates.pop("path")
40
- raw_file = gitlab_cli.project.files.get(
41
- file_path=cluster_path, ref=gitlab_cli.main_branch
40
+ raw_file = gitlab_cli.get_raw_file(
41
+ project=gitlab_cli.project,
42
+ path=cluster_path,
43
+ ref=gitlab_cli.main_branch,
42
44
  )
43
- content = yaml.load(raw_file.decode())
45
+ content = yaml.load(raw_file)
44
46
  if "spec" not in content:
45
47
  self.cancel("Spec missing. Nothing to do.")
46
48
 
@@ -28,10 +28,12 @@ class CreateOCMUpdateRecommendedVersion(MergeRequestBase):
28
28
  return f"ocm update recommended version for {self.ocm_name}"
29
29
 
30
30
  def process(self, gitlab_cli: GitLabApi) -> None:
31
- raw_file = gitlab_cli.project.files.get(
32
- file_path=self.path, ref=gitlab_cli.main_branch
31
+ raw_file = gitlab_cli.get_raw_file(
32
+ project=gitlab_cli.project,
33
+ path=self.path,
34
+ ref=gitlab_cli.main_branch,
33
35
  )
34
- content = yaml.load(raw_file.decode(), Loader=yaml.RoundTripLoader)
36
+ content = yaml.load(raw_file, Loader=yaml.RoundTripLoader)
35
37
 
36
38
  content["recommendedVersions"] = self.recommended_versions
37
39
 
@@ -28,10 +28,12 @@ class CreateOCMUpgradeSchedulerOrgUpdates(MergeRequestBase):
28
28
  ocm_path = self.updates_info["path"]
29
29
  ocm_name = self.updates_info["name"]
30
30
 
31
- raw_file = gitlab_cli.project.files.get(
32
- file_path=ocm_path, ref=gitlab_cli.main_branch
31
+ raw_file = gitlab_cli.get_raw_file(
32
+ project=gitlab_cli.project,
33
+ path=ocm_path,
34
+ ref=gitlab_cli.main_branch,
33
35
  )
34
- content = yaml.load(raw_file.decode(), Loader=yaml.RoundTripLoader)
36
+ content = yaml.load(raw_file, Loader=yaml.RoundTripLoader)
35
37
  upgrade_policy_clusters = content["upgradePolicyClusters"]
36
38
 
37
39
  for update in self.updates_info["updates"]:
@@ -37,10 +37,12 @@ class PromoteQontractSchemas(MergeRequestBase):
37
37
  return f"promote qontract-schemas to version {self.version}"
38
38
 
39
39
  def process(self, gitlab_cli: GitLabApi) -> None:
40
- raw_file = gitlab_cli.project.files.get(
41
- file_path=self.path, ref=gitlab_cli.main_branch
40
+ raw_file = gitlab_cli.get_raw_file(
41
+ project=gitlab_cli.project,
42
+ path=self.path,
43
+ ref=gitlab_cli.main_branch,
42
44
  )
43
- content = raw_file.decode().decode("utf-8")
45
+ content = raw_file.decode("utf-8")
44
46
  lines = content.splitlines()
45
47
  for index, text in enumerate(lines):
46
48
  if text.startswith("export SCHEMAS_IMAGE_TAG="):
@@ -121,9 +123,11 @@ class PromoteQontractReconcileCommercial(MergeRequestBase):
121
123
  search_text: str,
122
124
  replace_text: str,
123
125
  ) -> None:
124
- raw_file = gitlab_cli.project.files.get(
125
- file_path=path, ref=gitlab_cli.main_branch
126
- ).decode()
126
+ raw_file = gitlab_cli.get_raw_file(
127
+ project=gitlab_cli.project,
128
+ path=path,
129
+ ref=gitlab_cli.main_branch,
130
+ )
127
131
  match method:
128
132
  case "line_search":
129
133
  new_content = self._process_by_line_search(
@@ -100,10 +100,12 @@ class UpdateAccessReportBase(MergeRequestBase):
100
100
  return new_workbook_md
101
101
 
102
102
  def process(self, gitlab_cli: GitLabApi) -> None:
103
- workbook_file = gitlab_cli.project.files.get(
104
- file_path=self._workbook_file_name, ref=self.branch
103
+ workbook_file = gitlab_cli.get_raw_file(
104
+ project=gitlab_cli.project,
105
+ path=self._workbook_file_name,
106
+ ref=self.branch,
105
107
  )
106
- workbook_md = self._update_workbook(workbook_file.decode().decode("utf-8"))
108
+ workbook_md = self._update_workbook(workbook_file.decode("utf-8"))
107
109
 
108
110
  if not self._dry_run:
109
111
  logging.info(
@@ -56,8 +56,12 @@ class CreateDeleteUserAppInterface(MergeRequestBase):
56
56
  branch_name=self.branch, file_path=path, commit_message=self.title
57
57
  )
58
58
  elif path_type == PathTypes.GABI:
59
- raw_file = gitlab_cli.project.files.get(file_path=path, ref=self.branch)
60
- content = yaml.load(raw_file.decode(), Loader=yaml.RoundTripLoader)
59
+ raw_file = gitlab_cli.get_raw_file(
60
+ project=gitlab_cli.project,
61
+ path=path,
62
+ ref=self.branch,
63
+ )
64
+ content = yaml.load(raw_file, Loader=yaml.RoundTripLoader)
61
65
  for gabi_user in content["users"][:]:
62
66
  if self.username in gabi_user["$ref"]:
63
67
  content["users"].remove(gabi_user)
@@ -70,8 +74,12 @@ class CreateDeleteUserAppInterface(MergeRequestBase):
70
74
  content=new_content,
71
75
  )
72
76
  elif path_type == PathTypes.AWS_ACCOUNTS:
73
- raw_file = gitlab_cli.project.files.get(file_path=path, ref=self.branch)
74
- content = yaml.load(raw_file.decode(), Loader=yaml.RoundTripLoader)
77
+ raw_file = gitlab_cli.get_raw_file(
78
+ project=gitlab_cli.project,
79
+ path=path,
80
+ ref=self.branch,
81
+ )
82
+ content = yaml.load(raw_file, Loader=yaml.RoundTripLoader)
75
83
  for reset_record in content["resetPasswords"]:
76
84
  if self.username in reset_record["user"]["$ref"]:
77
85
  content["resetPasswords"].remove(reset_record)
@@ -84,8 +92,12 @@ class CreateDeleteUserAppInterface(MergeRequestBase):
84
92
  content=new_content,
85
93
  )
86
94
  elif path_type == PathTypes.SCHEDULE:
87
- raw_file = gitlab_cli.project.files.get(file_path=path, ref=self.branch)
88
- content = yaml.load(raw_file.decode(), Loader=yaml.RoundTripLoader)
95
+ raw_file = gitlab_cli.get_raw_file(
96
+ project=gitlab_cli.project,
97
+ path=path,
98
+ ref=self.branch,
99
+ )
100
+ content = yaml.load(raw_file, Loader=yaml.RoundTripLoader)
89
101
  delete_indexes: list[tuple[int, int]] = []
90
102
  for schedule_index, schedule_record in enumerate(content["schedule"]):
91
103
  for user_index, user in enumerate(schedule_record["users"]):
@@ -124,10 +136,12 @@ class CreateDeleteUserInfra(MergeRequestBase):
124
136
  return "delete user(s)"
125
137
 
126
138
  def process(self, gitlab_cli):
127
- raw_file = gitlab_cli.project.files.get(
128
- file_path=self.PLAYBOOK, ref=self.branch
139
+ raw_file = gitlab_cli.get_raw_file(
140
+ project=gitlab_cli.project,
141
+ path=self.PLAYBOOK,
142
+ ref=self.branch,
129
143
  )
130
- content = yaml.load(raw_file.decode(), Loader=yaml.RoundTripLoader)
144
+ content = yaml.load(raw_file, Loader=yaml.RoundTripLoader)
131
145
 
132
146
  new_list = []
133
147
  for user in content[0]["vars"]["users"]:
@@ -131,7 +131,7 @@ class RepoOwners:
131
131
  _LOG.warning(f"{self._git_cli!s}:{owner_file['path']} not found")
132
132
  continue
133
133
  try:
134
- owners = yaml.safe_load(raw_owners.decode())
134
+ owners = yaml.safe_load(raw_owners)
135
135
  except yaml.parser.ParserError:
136
136
  owners = None
137
137
  if owners is None:
@@ -184,7 +184,7 @@ class RepoOwners:
184
184
  if raw_aliases is None:
185
185
  return {}
186
186
 
187
- aliases = yaml.safe_load(raw_aliases.decode())
187
+ aliases = yaml.safe_load(raw_aliases)
188
188
  if aliases is None:
189
189
  return {}
190
190
 
@@ -27,7 +27,6 @@ from github import (
27
27
  GithubException,
28
28
  )
29
29
  from github.ContentFile import ContentFile
30
- from github.Repository import Repository
31
30
  from gitlab.exceptions import GitlabError
32
31
  from requests import exceptions as rqexc
33
32
  from sretoolbox.container import Image
@@ -39,6 +38,7 @@ from sretoolbox.utils import (
39
38
  from reconcile.github_org import get_default_config
40
39
  from reconcile.status import RunningState
41
40
  from reconcile.utils import helm
41
+ from reconcile.utils.github_api import GithubRepositoryApi
42
42
  from reconcile.utils.gitlab_api import GitLabApi
43
43
  from reconcile.utils.jenkins_api import JenkinsApi
44
44
  from reconcile.utils.jjb_client import JJB
@@ -715,24 +715,6 @@ class SaasHerder: # pylint: disable=too-many-public-methods
715
715
  def _collect_image_patterns(self) -> set[str]:
716
716
  return {p for sf in self.saas_files for p in sf.image_patterns}
717
717
 
718
- @staticmethod
719
- def _get_file_contents_github(repo: Repository, path: str, commit_sha: str) -> str:
720
- f = repo.get_contents(path, commit_sha)
721
- if isinstance(f, list):
722
- raise Exception(f"Path {path} and sha {commit_sha} is a directory!")
723
-
724
- if f.size < 1024**2: # 1 MB
725
- return f.decoded_content.decode("utf8")
726
-
727
- tree = repo.get_git_tree(commit_sha, recursive="/" in path).tree
728
- for x in tree:
729
- if x.path != path.lstrip("/"):
730
- continue
731
- blob = repo.get_git_blob(x.sha)
732
- return base64.b64decode(blob.content).decode("utf8")
733
-
734
- return ""
735
-
736
718
  @retry(max_attempts=20)
737
719
  def get_archive_info(
738
720
  self,
@@ -768,13 +750,20 @@ class SaasHerder: # pylint: disable=too-many-public-methods
768
750
  match repo_info.platform:
769
751
  case "github":
770
752
  repo = github.get_repo(repo_info.name)
771
- content = self._get_file_contents_github(repo, path, commit_sha)
753
+ content = GithubRepositoryApi.get_raw_file(
754
+ repo=repo,
755
+ path=path,
756
+ ref=commit_sha,
757
+ )
772
758
  case "gitlab":
773
759
  if not self.gitlab:
774
760
  raise Exception("gitlab is not initialized")
775
761
  project = self.gitlab.get_project(url)
776
- f = project.files.get(file_path=path.lstrip("/"), ref=commit_sha)
777
- content = f.decode()
762
+ content = self.gitlab.get_raw_file(
763
+ project=project,
764
+ path=path,
765
+ ref=commit_sha,
766
+ )
778
767
  case _:
779
768
  raise Exception(f"Only GitHub and GitLab are supported: {url}")
780
769
 
@@ -795,10 +784,12 @@ class SaasHerder: # pylint: disable=too-many-public-methods
795
784
  raise Exception(f"Path {path} and sha {commit_sha} is a file!")
796
785
  for f in directory:
797
786
  file_path = os.path.join(path, f.name)
798
- file_contents_decoded = self._get_file_contents_github(
799
- repo, file_path, commit_sha
787
+ raw_file = GithubRepositoryApi.get_raw_file(
788
+ repo=repo,
789
+ path=file_path,
790
+ ref=commit_sha,
800
791
  )
801
- result_resources = yaml.safe_load_all(file_contents_decoded)
792
+ result_resources = yaml.safe_load_all(raw_file)
802
793
  resources.extend(result_resources)
803
794
  case "gitlab":
804
795
  if not self.gitlab:
reconcile/utils/vcs.py CHANGED
@@ -266,11 +266,12 @@ class VCS:
266
266
  if not file_path.startswith("data")
267
267
  else file_path
268
268
  )
269
- return (
270
- self._app_interface_api.project.files.get(file_path=file_path, ref=ref)
271
- .decode()
272
- .decode("utf-8")
269
+ file = self._app_interface_api.get_raw_file(
270
+ project=self._app_interface_api.project,
271
+ path=file_path,
272
+ ref=ref,
273
273
  )
274
+ return file.decode("utf-8")
274
275
 
275
276
  def get_open_app_interface_merge_requests(self) -> list[ProjectMergeRequest]:
276
277
  return self._app_interface_api.get_merge_requests(state=MRState.OPENED)