Coverage for src/gitlabracadabra/packages/pulp_manifest.py: 83%

77 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-23 06:44 +0200

1# 

2# Copyright (C) 2019-2025 Mathieu Parent <math.parent@gmail.com> 

3# 

4# This program is free software: you can redistribute it and/or modify 

5# it under the terms of the GNU Lesser General Public License as published by 

6# the Free Software Foundation, either version 3 of the License, or 

7# (at your option) any later version. 

8# 

9# This program is distributed in the hope that it will be useful, 

10# but WITHOUT ANY WARRANTY; without even the implied warranty of 

11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

12# GNU Lesser General Public License for more details. 

13# 

14# You should have received a copy of the GNU Lesser General Public License 

15# along with this program. If not, see <http://www.gnu.org/licenses/>. 

16 

17from __future__ import annotations 

18 

19from logging import getLogger 

20from typing import TYPE_CHECKING 

21from urllib.parse import urljoin 

22 

23from requests import codes 

24 

25from gitlabracadabra.packages.package_file import PackageFile 

26from gitlabracadabra.packages.source import Source 

27 

28if TYPE_CHECKING: 28 ↛ 29line 28 didn't jump to line 29 because the condition on line 28 was never true

29 from requests.models import Response 

30 

31 from gitlabracadabra.packages.destination import Destination 

32 

33logger = getLogger(__name__) 

34 

35 

36class PulpManifestSource(Source): 

37 """PULP_MANIFEST repository.""" 

38 

39 def __init__( 

40 self, 

41 *, 

42 log_prefix: str = "", 

43 url: str, 

44 package_name: str, 

45 package_version: str | None = None, 

46 ) -> None: 

47 """Initialize a PULP_MANIFEST Source object. 

48 

49 Args: 

50 log_prefix: Log prefix. 

51 url: PULP_MANIFEST URL. 

52 package_name: Destination package name. 

53 package_version: Destination package version. 

54 """ 

55 super().__init__() 

56 self._log_prefix = log_prefix 

57 self._url = url 

58 self._package_name = package_name 

59 self._package_version = package_version or "0" 

60 

61 def __str__(self) -> str: 

62 """Return string representation. 

63 

64 Returns: 

65 A string. 

66 """ 

67 return f"PULP_MANIFEST repository (url={self._url})" 

68 

69 def package_files( 

70 self, 

71 destination: Destination, 

72 ) -> list[PackageFile]: 

73 """Return list of package files. 

74 

75 Returns: 

76 List of package files. 

77 """ 

78 pulp_manifest = self._package_file(None) 

79 pulp_manifest.force = True 

80 destination_pulp_manifest_url = destination.get_url(pulp_manifest) 

81 destination_pulp_manifest_response = destination.session.request( 

82 "GET", destination_pulp_manifest_url, stream=True 

83 ) 

84 current_pulp_manifest_files: list[PackageFile] = [] 

85 if destination_pulp_manifest_response.status_code == codes["ok"]: 85 ↛ 87line 85 didn't jump to line 87 because the condition on line 85 was always true

86 current_pulp_manifest_files = self._pulp_manifest_files(destination_pulp_manifest_response) 

87 elif destination_pulp_manifest_response.status_code != codes["not_found"]: 

88 logger.warning( 

89 '%sUnexpected HTTP status for %s package file "%s" from "%s" version %s (%s): received %i %s with GET method on destination', 

90 self._log_prefix, 

91 pulp_manifest.package_type, 

92 pulp_manifest.file_name, 

93 pulp_manifest.package_name, 

94 pulp_manifest.package_version, 

95 destination_pulp_manifest_url, 

96 destination_pulp_manifest_response.status_code, 

97 destination_pulp_manifest_response.reason, 

98 ) 

99 return [] 

100 

101 source_pulp_manifest_response = self.session.request("GET", self._url, stream=True) 

102 if source_pulp_manifest_response.status_code != codes["ok"]: 102 ↛ 103line 102 didn't jump to line 103 because the condition on line 102 was never true

103 logger.warning( 

104 "%sUnexpected HTTP status for PULP_MANIFEST url %s: received %i %s", 

105 self._log_prefix, 

106 self._url, 

107 source_pulp_manifest_response.status_code, 

108 source_pulp_manifest_response.reason, 

109 ) 

110 return [] 

111 target_pulp_manifest_files = self._pulp_manifest_files(source_pulp_manifest_response) 

112 package_files: list[PackageFile] = [] 

113 for package_file in target_pulp_manifest_files: 

114 if package_file not in current_pulp_manifest_files: 

115 package_file.force = True 

116 package_files.append(package_file) 

117 package_files.append(pulp_manifest) 

118 for package_file in current_pulp_manifest_files: 

119 if package_file not in target_pulp_manifest_files: 

120 package_file.delete = True 

121 package_file.force = True 

122 package_files.append(package_file) 

123 if len(package_files) > 1: 123 ↛ 130line 123 didn't jump to line 130 because the condition on line 123 was always true

124 old_pulp_manifest = self._package_file(None) 

125 old_pulp_manifest.delete = True 

126 old_pulp_manifest.force = True 

127 package_files.append(old_pulp_manifest) 

128 destination.cache_project_package_package_files("generic", self._package_name, self._package_version) 

129 else: 

130 package_files.remove(pulp_manifest) 

131 return package_files 

132 

133 def _pulp_manifest_files(self, pulp_manifest_response: Response) -> list[PackageFile]: 

134 if pulp_manifest_response.encoding is None: 

135 pulp_manifest_response.encoding = "utf-8" 

136 package_files: list[PackageFile] = [] 

137 for pulp_manifest_line in pulp_manifest_response.iter_lines(decode_unicode=True): 

138 try: 

139 filename, sha256_checksum, size_in_bytes = pulp_manifest_line.split(",", 3) 

140 except ValueError: 

141 logger.warning( 

142 "%sInvalid PULP_MANIFEST line: %s", 

143 self._log_prefix, 

144 pulp_manifest_line, 

145 ) 

146 continue 

147 package_file = self._package_file(filename, sha256_checksum, size_in_bytes) 

148 if package_file: 148 ↛ 137line 148 didn't jump to line 137 because the condition on line 148 was always true

149 package_files.append(package_file) 

150 return package_files 

151 

152 def _package_file( 

153 self, filename: str | None, sha256_checksum: str | None = None, size_in_bytes: str | None = None 

154 ) -> PackageFile: 

155 url = urljoin(self._url, filename, allow_fragments=False) 

156 package_file = PackageFile( 

157 url, 

158 "generic", 

159 self._package_name, 

160 self._package_version, 

161 url.split("/").pop(), 

162 ) 

163 if sha256_checksum is not None: 

164 package_file.metadata["sha256_checksum"] = sha256_checksum 

165 if size_in_bytes is not None: 

166 package_file.metadata["size_in_bytes"] = size_in_bytes 

167 return package_file