Coverage for dak/manage_build_queues.py: 91%

63 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2026-08-03 16:46 +0000

1"""Manage build queues 

2 

3@contact: Debian FTPMaster <ftpmaster@debian.org> 

4@copyright: 2000, 2001, 2002, 2006 James Troup <james@nocrew.org> 

5@copyright: 2009 Mark Hymers <mhy@debian.org> 

6@copyright: 2012, Ansgar Burchardt <ansgar@debian.org> 

7 

8""" 

9 

10# This program is free software; you can redistribute it and/or modify 

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

12# the Free Software Foundation; either version 2 of the License, or 

13# (at your option) any later version. 

14 

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

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

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

18# GNU General Public License for more details. 

19 

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

21# along with this program; if not, write to the Free Software 

22# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 

23 

24################################################################################ 

25 

26import sys 

27from datetime import datetime, timedelta 

28from typing import NoReturn 

29 

30import apt_pkg 

31from sqlalchemy import select, sql 

32 

33from daklib import daklog 

34from daklib.archive import ArchiveTransaction 

35from daklib.config import Config 

36from daklib.dbconn import BuildQueue, DBBinary, DBSource 

37 

38################################################################################ 

39 

40Options: apt_pkg.Configuration 

41Logger: daklog.Logger 

42 

43################################################################################ 

44 

45 

46def usage(exit_code=0) -> NoReturn: 

47 print( 

48 """Usage: dak manage-build-queues [OPTIONS] buildqueue1 buildqueue2 

49Manage the contents of one or more build queues 

50 

51 -a, --all run on all known build queues 

52 -n, --no-action don't do anything 

53 -h, --help show this help and exit""" 

54 ) 

55 

56 sys.exit(exit_code) 

57 

58 

59################################################################################ 

60 

61 

62def clean( 

63 build_queue: BuildQueue, 

64 transaction: ArchiveTransaction, 

65 now: datetime | None = None, 

66) -> None: 

67 session = transaction.session 

68 if now is None: 68 ↛ 69line 68 didn't jump to line 69 because the condition on line 68 was never true

69 now = datetime.now() 

70 

71 delete_before = now - timedelta(seconds=build_queue.stay_of_execution) 

72 suite = build_queue.suite 

73 suite_was_changed = False 

74 

75 # Remove binaries subject to the following conditions: 

76 # 1. Keep binaries that are in policy queues. 

77 # 2. Remove binaries that are not in suites. 

78 # 3. Remove binaries that have been in the build queue for some time. 

79 query = sql.text( 

80 """ 

81 SELECT b.* 

82 FROM binaries b 

83 JOIN bin_associations ba ON b.id = ba.bin 

84 WHERE ba.suite = :suite_id 

85 AND NOT EXISTS 

86 (SELECT 1 FROM policy_queue_upload_binaries_map pqubm 

87 JOIN policy_queue_upload pqu ON pqu.id = pqubm.policy_queue_upload_id 

88 JOIN policy_queue pq ON pq.id = pqu.policy_queue_id 

89 JOIN suite s ON s.policy_queue_id = pq.id 

90 JOIN suite_build_queue_copy sbqc ON sbqc.suite = s.id 

91 WHERE pqubm.binary_id = ba.bin AND pq.send_to_build_queues 

92 AND sbqc.build_queue_id = :build_queue_id) 

93 AND (ba.created < :delete_before 

94 OR NOT EXISTS 

95 (SELECT 1 FROM bin_associations ba2 

96 WHERE ba2.bin = ba.bin 

97 AND (EXISTS (SELECT 1 FROM suite_build_queue_copy sbqc 

98 WHERE sbqc.build_queue_id = :build_queue_id 

99 AND sbqc.suite = ba2.suite) 

100 OR EXISTS (SELECT 1 FROM suite_build_queue_copy sbqc 

101 JOIN suite s ON s.id = sbqc.suite 

102 WHERE sbqc.build_queue_id = :build_queue_id 

103 AND s.debugsuite_id = ba2.suite))))""" 

104 ) 

105 binaries = session.scalars( 

106 select(DBBinary).from_statement(query), 

107 { 

108 "build_queue_id": build_queue.queue_id, 

109 "suite_id": suite.suite_id, 

110 "delete_before": delete_before, 

111 }, 

112 ) 

113 for binary in binaries: 

114 Logger.log( 

115 [ 

116 "removed binary from build queue", 

117 build_queue.queue_name, 

118 binary.package, 

119 binary.version, 

120 ] 

121 ) 

122 transaction.remove_binary(binary, suite) 

123 suite_was_changed = True 

124 

125 # Remove sources 

126 # Conditions are similar as for binaries, but we also keep sources 

127 # if there is a binary in the build queue that uses it. 

128 query = sql.text( 

129 """ 

130 SELECT s.* 

131 FROM source s 

132 JOIN src_associations sa ON s.id = sa.source 

133 WHERE sa.suite = :suite_id 

134 AND NOT EXISTS 

135 (SELECT 1 FROM policy_queue_upload pqu 

136 JOIN policy_queue pq ON pq.id = pqu.policy_queue_id 

137 JOIN suite s ON s.policy_queue_id = pq.id 

138 JOIN suite_build_queue_copy sbqc ON sbqc.suite = s.id 

139 WHERE pqu.source_id = sa.source AND pq.send_to_build_queues 

140 AND sbqc.build_queue_id = :build_queue_id) 

141 AND (sa.created < :delete_before 

142 OR NOT EXISTS 

143 (SELECT 1 FROM src_associations sa2 

144 JOIN suite_build_queue_copy sbqc ON sbqc.suite = sa2.suite 

145 WHERE sbqc.build_queue_id = :build_queue_id 

146 AND sa2.source = sa.source)) 

147 AND NOT EXISTS 

148 (SELECT 1 FROM bin_associations ba 

149 JOIN binaries b ON ba.bin = b.id 

150 WHERE ba.suite = :suite_id 

151 AND b.source = s.id)""" 

152 ) 

153 sources = session.scalars( 

154 select(DBSource).from_statement(query), 

155 { 

156 "build_queue_id": build_queue.queue_id, 

157 "suite_id": suite.suite_id, 

158 "delete_before": delete_before, 

159 }, 

160 ) 

161 for source in sources: 

162 Logger.log( 

163 [ 

164 "removed source from build queue", 

165 build_queue.queue_name, 

166 source.source, 

167 source.version, 

168 ] 

169 ) 

170 transaction.remove_source(source, suite) 

171 suite_was_changed = True 

172 

173 if suite_was_changed: 

174 suite.update_last_changed() 

175 

176 

177def main() -> None: 

178 global Options, Logger 

179 

180 cnf = Config() 

181 

182 for i in ["Help", "No-Action", "All"]: 

183 key = "Manage-Build-Queues::Options::%s" % i 

184 if key not in cnf: 184 ↛ 182line 184 didn't jump to line 182 because the condition on line 184 was always true

185 cnf[key] = "" 

186 

187 Arguments = [ 

188 ("h", "help", "Manage-Build-Queues::Options::Help"), 

189 ("n", "no-action", "Manage-Build-Queues::Options::No-Action"), 

190 ("a", "all", "Manage-Build-Queues::Options::All"), 

191 ] 

192 

193 queue_names = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv) # type: ignore[attr-defined] 

194 Options = cnf.subtree("Manage-Build-Queues::Options") 

195 

196 if Options["Help"]: 

197 usage() 

198 

199 Logger = daklog.Logger("manage-build-queues", Options["No-Action"]) 

200 

201 starttime = datetime.now() 

202 

203 with ArchiveTransaction() as transaction: 

204 session = transaction.session 

205 if Options["All"]: 

206 if len(queue_names) != 0: 206 ↛ 207line 206 didn't jump to line 207 because the condition on line 206 was never true

207 print("E: Cannot use both -a and a queue name") 

208 sys.exit(1) 

209 queues_query = select(BuildQueue) 

210 else: 

211 queues_query = select(BuildQueue).where( 

212 BuildQueue.queue_name.in_(queue_names) 

213 ) 

214 

215 for q in session.scalars(queues_query): 

216 Logger.log( 

217 ["cleaning queue %s using datetime %s" % (q.queue_name, starttime)] 

218 ) 

219 clean(q, transaction, now=starttime) 

220 if not Options["No-Action"]: 220 ↛ 223line 220 didn't jump to line 223 because the condition on line 220 was always true

221 transaction.commit() 

222 else: 

223 transaction.rollback() 

224 

225 Logger.close()