Coverage for dak/manage_build_queues.py: 91%

63 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2026-02-10 22:10 +0000

1#! /usr/bin/env python3 

2 

3"""Manage build queues 

4 

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

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

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

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

9 

10""" 

11 

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

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

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

15# (at your option) any later version. 

16 

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

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

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

20# GNU General Public License for more details. 

21 

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

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

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

25 

26################################################################################ 

27 

28import sys 

29from datetime import datetime, timedelta 

30from typing import NoReturn 

31 

32import apt_pkg 

33import sqlalchemy.sql as sql 

34 

35from daklib import daklog 

36from daklib.archive import ArchiveTransaction 

37from daklib.config import Config 

38from daklib.dbconn import BuildQueue, DBBinary, DBSource 

39 

40################################################################################ 

41 

42Options: apt_pkg.Configuration 

43Logger: daklog.Logger 

44 

45################################################################################ 

46 

47 

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

49 print( 

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

51Manage the contents of one or more build queues 

52 

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

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

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

56 ) 

57 

58 sys.exit(exit_code) 

59 

60 

61################################################################################ 

62 

63 

64def clean( 

65 build_queue: BuildQueue, 

66 transaction: ArchiveTransaction, 

67 now: datetime | None = None, 

68) -> None: 

69 session = transaction.session 

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

71 now = datetime.now() 

72 

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

74 suite = build_queue.suite 

75 suite_was_changed = False 

76 

77 # Remove binaries subject to the following conditions: 

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

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

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

81 query = sql.text( 

82 """ 

83 SELECT b.* 

84 FROM binaries b 

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

86 WHERE ba.suite = :suite_id 

87 AND NOT EXISTS 

88 (SELECT 1 FROM policy_queue_upload_binaries_map pqubm 

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

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

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

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

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

94 AND sbqc.build_queue_id = :build_queue_id) 

95 AND (ba.created < :delete_before 

96 OR NOT EXISTS 

97 (SELECT 1 FROM bin_associations ba2 

98 WHERE ba2.bin = ba.bin 

99 AND (EXISTS (SELECT 1 FROM suite_build_queue_copy sbqc 

100 WHERE sbqc.build_queue_id = :build_queue_id 

101 AND sbqc.suite = ba2.suite) 

102 OR EXISTS (SELECT 1 FROM suite_build_queue_copy sbqc 

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

104 WHERE sbqc.build_queue_id = :build_queue_id 

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

106 ) 

107 binaries = ( 

108 session.query(DBBinary) 

109 .from_statement(query) # type: ignore[arg-type] 

110 .params( 

111 { 

112 "build_queue_id": build_queue.queue_id, 

113 "suite_id": suite.suite_id, 

114 "delete_before": delete_before, 

115 } 

116 ) 

117 ) 

118 for binary in binaries: 

119 Logger.log( 

120 [ 

121 "removed binary from build queue", 

122 build_queue.queue_name, 

123 binary.package, 

124 binary.version, 

125 ] 

126 ) 

127 transaction.remove_binary(binary, suite) 

128 suite_was_changed = True 

129 

130 # Remove sources 

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

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

133 query = sql.text( 

134 """ 

135 SELECT s.* 

136 FROM source s 

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

138 WHERE sa.suite = :suite_id 

139 AND NOT EXISTS 

140 (SELECT 1 FROM policy_queue_upload pqu 

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

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

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

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

145 AND sbqc.build_queue_id = :build_queue_id) 

146 AND (sa.created < :delete_before 

147 OR NOT EXISTS 

148 (SELECT 1 FROM src_associations sa2 

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

150 WHERE sbqc.build_queue_id = :build_queue_id 

151 AND sa2.source = sa.source)) 

152 AND NOT EXISTS 

153 (SELECT 1 FROM bin_associations ba 

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

155 WHERE ba.suite = :suite_id 

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

157 ) 

158 sources = ( 

159 session.query(DBSource) 

160 .from_statement(query) # type: ignore[arg-type] 

161 .params( 

162 { 

163 "build_queue_id": build_queue.queue_id, 

164 "suite_id": suite.suite_id, 

165 "delete_before": delete_before, 

166 } 

167 ) 

168 ) 

169 for source in sources: 

170 Logger.log( 

171 [ 

172 "removed source from build queue", 

173 build_queue.queue_name, 

174 source.source, 

175 source.version, 

176 ] 

177 ) 

178 transaction.remove_source(source, suite) 

179 suite_was_changed = True 

180 

181 if suite_was_changed: 

182 suite.update_last_changed() 

183 

184 

185def main() -> None: 

186 global Options, Logger 

187 

188 cnf = Config() 

189 

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

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

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

193 cnf[key] = "" 

194 

195 Arguments = [ 

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

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

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

199 ] 

200 

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

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

203 

204 if Options["Help"]: 

205 usage() 

206 

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

208 

209 starttime = datetime.now() 

210 

211 with ArchiveTransaction() as transaction: 

212 session = transaction.session 

213 if Options["All"]: 

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

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

216 sys.exit(1) 

217 queues = session.query(BuildQueue) 

218 else: 

219 queues = session.query(BuildQueue).filter( 

220 BuildQueue.queue_name.in_(queue_names) 

221 ) 

222 

223 for q in queues: 

224 Logger.log( 

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

226 ) 

227 clean(q, transaction, now=starttime) 

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

229 transaction.commit() 

230 else: 

231 transaction.rollback() 

232 

233 Logger.close() 

234 

235 

236####################################################################################### 

237 

238 

239if __name__ == "__main__": 

240 main()