Coverage for dak/manage_build_queues.py: 85%

63 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2026-01-04 16:18 +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 JOIN suite_build_queue_copy sbqc ON sbqc.suite = ba2.suite 

99 WHERE ba2.bin = ba.bin AND sbqc.build_queue_id = :build_queue_id))""" 

100 ) 

101 binaries = ( 

102 session.query(DBBinary) 

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

104 .params( 

105 { 

106 "build_queue_id": build_queue.queue_id, 

107 "suite_id": suite.suite_id, 

108 "delete_before": delete_before, 

109 } 

110 ) 

111 ) 

112 for binary in binaries: 

113 Logger.log( 

114 [ 

115 "removed binary from build queue", 

116 build_queue.queue_name, 

117 binary.package, 

118 binary.version, 

119 ] 

120 ) 

121 transaction.remove_binary(binary, suite) 

122 suite_was_changed = True 

123 

124 # Remove sources 

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

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

127 query = sql.text( 

128 """ 

129 SELECT s.* 

130 FROM source s 

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

132 WHERE sa.suite = :suite_id 

133 AND NOT EXISTS 

134 (SELECT 1 FROM policy_queue_upload pqu 

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

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

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

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

139 AND sbqc.build_queue_id = :build_queue_id) 

140 AND (sa.created < :delete_before 

141 OR NOT EXISTS 

142 (SELECT 1 FROM src_associations sa2 

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

144 WHERE sbqc.build_queue_id = :build_queue_id 

145 AND sa2.source = sa.source)) 

146 AND NOT EXISTS 

147 (SELECT 1 FROM bin_associations ba 

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

149 WHERE ba.suite = :suite_id 

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

151 ) 

152 sources = ( 

153 session.query(DBSource) 

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

155 .params( 

156 { 

157 "build_queue_id": build_queue.queue_id, 

158 "suite_id": suite.suite_id, 

159 "delete_before": delete_before, 

160 } 

161 ) 

162 ) 

163 for source in sources: 

164 Logger.log( 

165 [ 

166 "removed source from build queue", 

167 build_queue.queue_name, 

168 source.source, 

169 source.version, 

170 ] 

171 ) 

172 transaction.remove_source(source, suite) 

173 suite_was_changed = True 

174 

175 if suite_was_changed: 175 ↛ exitline 175 didn't return from function 'clean' because the condition on line 175 was always true

176 suite.update_last_changed() 

177 

178 

179def main() -> None: 

180 global Options, Logger 

181 

182 cnf = Config() 

183 

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

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

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

187 cnf[key] = "" 

188 

189 Arguments = [ 

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

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

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

193 ] 

194 

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

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

197 

198 if Options["Help"]: 

199 usage() 

200 

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

202 

203 starttime = datetime.now() 

204 

205 with ArchiveTransaction() as transaction: 

206 session = transaction.session 

207 if Options["All"]: 207 ↛ 208line 207 didn't jump to line 208 because the condition on line 207 was never true

208 if len(queue_names) != 0: 

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

210 sys.exit(1) 

211 queues = session.query(BuildQueue) 

212 else: 

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

214 BuildQueue.queue_name.in_(queue_names) 

215 ) 

216 

217 for q in queues: 

218 Logger.log( 

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

220 ) 

221 clean(q, transaction, now=starttime) 

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

223 transaction.commit() 

224 else: 

225 transaction.rollback() 

226 

227 Logger.close() 

228 

229 

230####################################################################################### 

231 

232 

233if __name__ == "__main__": 

234 main()