Coverage for dak/admin.py: 45%

861 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2026-01-04 16:18 +0000

1#! /usr/bin/env python3 

2 

3"""Configure dak parameters in the database""" 

4# Copyright (C) 2009 Mark Hymers <mhy@debian.org> 

5 

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

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

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

9# (at your option) any later version. 

10 

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

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

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

14# GNU General Public License for more details. 

15 

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

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

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

19 

20################################################################################ 

21 

22import collections 

23import json 

24import sys 

25from collections.abc import Callable, Sequence 

26from dataclasses import dataclass 

27from typing import Any, NoReturn, Optional 

28 

29import apt_pkg 

30from sqlalchemy.exc import IntegrityError, SQLAlchemyError 

31from sqlalchemy.orm.exc import NoResultFound 

32 

33import daklib.archive 

34import daklib.gpg 

35from daklib import utils 

36from daklib.dbconn import ( 

37 ACL, 

38 Architecture, 

39 Archive, 

40 BuildQueue, 

41 Component, 

42 DBBinary, 

43 DBConfig, 

44 DBConn, 

45 DBSource, 

46 Keyring, 

47 Override, 

48 OverrideType, 

49 PolicyQueue, 

50 SignatureHistory, 

51 SrcFormat, 

52 Suite, 

53 VersionCheck, 

54 get_architecture, 

55 get_archive, 

56 get_component, 

57 get_policy_queue, 

58 get_suite, 

59 get_version_checks, 

60 session_wrapper, 

61) 

62 

63################################################################################ 

64 

65dispatch = {} 

66dryrun = False 

67 

68################################################################################ 

69 

70 

71def warn(msg: str) -> None: 

72 print(msg, file=sys.stderr) 

73 

74 

75def die(msg: str, exit_code: int = 1) -> NoReturn: 

76 print(msg, file=sys.stderr) 

77 sys.exit(exit_code) 

78 

79 

80def die_arglen(args: Sequence, args_needed: int, msg: str) -> None: 

81 if len(args) < args_needed: 81 ↛ 82line 81 didn't jump to line 82 because the condition on line 81 was never true

82 die(msg) 

83 

84 

85def get_suite_or_die( 

86 suite_name: str, 

87 session=None, 

88 error_message="E: Invalid/unknown suite %(suite_name)s", 

89) -> Suite: 

90 suite = get_suite(suite_name.lower(), session=session) 

91 if suite is None: 91 ↛ 92line 91 didn't jump to line 92 because the condition on line 91 was never true

92 die(error_message % {"suite_name": suite_name}) 

93 return suite 

94 

95 

96def usage(exit_code: int = 0) -> NoReturn: 

97 """Perform administrative work on the dak database.""" 

98 

99 print( 

100 """Usage: dak admin COMMAND 

101Perform administrative work on the dak database. 

102 

103 -h, --help show this help and exit. 

104 -n, --dry-run don't do anything, just show what would have been done 

105 (only applies to add or rm operations). 

106 

107 Commands can use a long or abbreviated form: 

108 

109 config / c: 

110 c db show db config 

111 c db-shell show db config in a usable form for psql 

112 c NAME show option NAME as set in configuration table 

113 

114 keyring / k: 

115 k list-all list all keyrings 

116 k list-binary list all keyrings with a NULL source acl 

117 k list-source list all keyrings with a non NULL source acl 

118 k add-buildd NAME ARCH... add buildd keyring with upload permission 

119 for the given architectures 

120 

121 architecture / a: 

122 a list show a list of architectures 

123 a rm ARCH remove an architecture (will only work if 

124 no longer linked to any suites) 

125 a add ARCH DESCRIPTION [SUITELIST] 

126 add architecture ARCH with DESCRIPTION. 

127 If SUITELIST is given, add to each of the 

128 suites at the same time 

129 

130 component: 

131 component list show a list of components 

132 component rm COMPONENT remove a component (will only work if 

133 empty) 

134 component add NAME DESCRIPTION ORDERING 

135 add component NAME with DESCRIPTION. 

136 Ordered at ORDERING. 

137 

138 suite / s: 

139 s list [--print-archive] 

140 show a list of suites 

141 s show SUITE show config details for a suite 

142 s add SUITE VERSION [ label=LABEL ] [ description=DESCRIPTION ] 

143 [ origin=ORIGIN ] [ codename=CODENAME ] 

144 [ signingkey=SIGNINGKEY ] [ archive=ARCHIVE ] 

145 add suite SUITE, version VERSION. 

146 label, description, origin, codename 

147 and signingkey are optional. 

148 s rm SUITE remove a suite (will only work if empty) 

149 

150 s add-all-arches SUITE VERSION... as "s add" but adds suite-architecture 

151 relationships for all architectures 

152 s add-build-queue SUITE BUILD-QUEUE BUILD-QUEUE-CODENAME BUILD-QUEUE-ARCHIVE 

153 add a build queue for an existing suite 

154 

155 suite-architecture / s-a: 

156 s-a list show the architectures for all suites 

157 s-a list-suite ARCH show the suites an ARCH is in 

158 s-a list-arch SUITE show the architectures in a SUITE 

159 s-a add SUITE ARCH add ARCH to suite 

160 s-a rm SUITE ARCH remove ARCH from suite (will only work if 

161 no packages remain for the arch in the suite) 

162 

163 suite-component / s-c: 

164 s-c list show the architectures for all suites 

165 s-c list-suite COMPONENT 

166 show the suites a COMPONENT is in 

167 s-c list-component SUITE 

168 show the components in a SUITE 

169 s-c add SUITE COMPONENT 

170 add COMPONENT to suite 

171 s-c rm SUITE COMPONENT remove component from suite (will only work if 

172 no packages remain for the component in the suite) 

173 

174 suite-config / suite-cfg / s-cfg: 

175 s-cfg list show the names of the configurations 

176 s-cfg list SUITE show the configuration values for SUITE 

177 s-cfg list-json SUITE show the configuration values for SUITE in JSON format 

178 s-cfg get SUITE NAME ... 

179 show the value for NAME in SUITE (format: NAME=VALUE) 

180 s-cfg get-value SUITE NAME ... 

181 show the value for NAME in SUITE (format: VALUE) 

182 s-cfg get-json SUITE NAME ... 

183 show the value for NAME in SUITE (format: JSON object) 

184 s-cfg set SUITE NAME=VALUE ... 

185 set NAME to VALUE in SUITE 

186 s-cfg set-json SUITE 

187 s-cfg set-json SUITE FILENAME 

188 parse FILENAME (if absent or "-", then stdin) as JSON 

189 and update all configurations listed to match the 

190 value in the JSON. 

191 Uses the same format as list-json or get-json outputs. 

192 

193 archive: 

194 archive list list all archives 

195 archive add NAME ROOT DESCRIPTION [primary-mirror=MIRROR] [tainted=1] 

196 add archive NAME with path ROOT, 

197 primary mirror MIRROR. 

198 archive rm NAME remove archive NAME (will only work if there are 

199 no files and no suites in the archive) 

200 archive rename OLD NEW rename archive OLD to NEW 

201 

202 version-check / v-c: 

203 v-c list show version checks for all suites 

204 v-c list-suite SUITE show version checks for suite SUITE 

205 v-c add SUITE CHECK REFERENCE add a version check for suite SUITE 

206 v-c rm SUITE CHECK REFERENCE remove a version check 

207 where 

208 CHECK is one of Enhances, MustBeNewerThan, MustBeOlderThan 

209 REFERENCE is another suite name 

210 

211 change-component: 

212 change-component SUITE COMPONENT source SOURCE... 

213 change-component SUITE COMPONENT binary BINARY... 

214 Move source or binary packages to a different component by copying 

215 associated files and changing the overrides. 

216 

217 forget-signature FILE: forget that we saw FILE 

218""" 

219 ) 

220 sys.exit(exit_code) 

221 

222 

223################################################################################ 

224 

225 

226def __architecture_list(d: DBConn, args: list[str]) -> NoReturn: 

227 q = d.session().query(Architecture).order_by(Architecture.arch_string) 

228 for j in q.all(): 

229 # HACK: We should get rid of source from the arch table 

230 if j.arch_string == "source": 

231 continue 

232 print(j.arch_string) 

233 sys.exit(0) 

234 

235 

236def __architecture_add(d: DBConn, args: list[str]) -> None: 

237 die_arglen(args, 4, "E: adding an architecture requires a name and a description") 

238 print("Adding architecture %s" % args[2]) 

239 suites = [str(x) for x in args[4:]] 

240 if len(suites) > 0: 240 ↛ 241line 240 didn't jump to line 241 because the condition on line 240 was never true

241 print("Adding to suites %s" % ", ".join(suites)) 

242 if not dryrun: 242 ↛ 263line 242 didn't jump to line 263 because the condition on line 242 was always true

243 try: 

244 s = d.session() 

245 a = Architecture() 

246 a.arch_string = str(args[2]).lower() 

247 a.description = str(args[3]) 

248 s.add(a) 

249 for sn in suites: 249 ↛ 250line 249 didn't jump to line 250 because the loop on line 249 never started

250 su = get_suite(sn, s) 

251 if su is not None: 

252 a.suites.append(su) 

253 else: 

254 warn("W: Cannot find suite %s" % su) 

255 s.commit() 

256 except IntegrityError: 

257 die( 

258 "E: Integrity error adding architecture %s (it probably already exists)" 

259 % args[2] 

260 ) 

261 except SQLAlchemyError as e: 

262 die("E: Error adding architecture %s (%s)" % (args[2], e)) 

263 print("Architecture %s added" % (args[2])) 

264 

265 

266def __architecture_rm(d: DBConn, args: list[str]) -> None: 

267 die_arglen(args, 3, "E: removing an architecture requires at least a name") 

268 print("Removing architecture %s" % args[2]) 

269 if not dryrun: 

270 try: 

271 s = d.session() 

272 a = get_architecture(args[2].lower(), s) 

273 if a is None: 

274 die("E: Cannot find architecture %s" % args[2]) 

275 s.delete(a) 

276 s.commit() 

277 except IntegrityError: 

278 die( 

279 "E: Integrity error removing architecture %s (suite-arch entries probably still exist)" 

280 % args[2] 

281 ) 

282 except SQLAlchemyError as e: 

283 die("E: Error removing architecture %s (%s)" % (args[2], e)) 

284 print("Architecture %s removed" % args[2]) 

285 

286 

287def architecture(args: list[str]) -> None: 

288 d = DBConn() 

289 

290 die_arglen(args, 2, "E: architecture needs at least a command") 

291 

292 mode = args[1].lower() 

293 if mode == "list": 293 ↛ 294line 293 didn't jump to line 294 because the condition on line 293 was never true

294 __architecture_list(d, args) 

295 elif mode == "add": 295 ↛ 297line 295 didn't jump to line 297 because the condition on line 295 was always true

296 __architecture_add(d, args) 

297 elif mode == "rm": 

298 __architecture_rm(d, args) 

299 else: 

300 die("E: architecture command unknown") 

301 

302 

303dispatch["architecture"] = architecture 

304dispatch["a"] = architecture 

305 

306################################################################################ 

307 

308 

309def component_list() -> None: 

310 session = DBConn().session() 

311 for component in session.query(Component).order_by(Component.component_name): 

312 print("{0} ordering={1}".format(component.component_name, component.ordering)) 

313 

314 

315def component_add(args: list[str]) -> None: 

316 (name, description, ordering) = args[0:3] 

317 

318 attributes = dict( 

319 component_name=name, 

320 description=description, 

321 ordering=ordering, 

322 ) 

323 

324 for option in args[3:]: 

325 (key, value) = option.split("=") 

326 attributes[key] = value 

327 

328 session = DBConn().session() 

329 

330 component = Component() 

331 for key, value in attributes.items(): 

332 setattr(component, key, value) 

333 

334 session.add(component) 

335 session.flush() 

336 

337 if dryrun: 

338 session.rollback() 

339 else: 

340 session.commit() 

341 

342 

343def component_rm(name: str) -> None: 

344 session = DBConn().session() 

345 component = get_component(name, session) 

346 session.delete(component) 

347 session.flush() 

348 

349 if dryrun: 

350 session.rollback() 

351 else: 

352 session.commit() 

353 

354 

355def component_rename(oldname: str, newname: str) -> None: 

356 session = DBConn().session() 

357 component = get_component(oldname, session) 

358 assert component is not None 

359 component.component_name = newname 

360 session.flush() 

361 

362 if dryrun: 

363 session.rollback() 

364 else: 

365 session.commit() 

366 

367 

368def component(args: list[str]) -> None: 

369 mode = args[1] 

370 if mode == "list": 

371 component_list() 

372 elif mode == "rename": 

373 component_rename(args[2], args[3]) 

374 elif mode == "add": 

375 component_add(args[2:]) 

376 elif mode == "rm": 

377 component_rm(args[2]) 

378 else: 

379 die("E: component command unknown") 

380 

381 

382dispatch["component"] = component 

383 

384################################################################################ 

385 

386 

387def __suite_list(d: DBConn, args: list[str]) -> None: 

388 s = d.session() 

389 for j in ( 

390 s.query(Suite) 

391 .join(Suite.archive) 

392 .order_by(Archive.archive_name, Suite.suite_name) 

393 .all() 

394 ): 

395 if len(args) > 2 and args[2] == "--print-archive": 395 ↛ 396line 395 didn't jump to line 396 because the condition on line 395 was never true

396 print("{0} {1}".format(j.archive.archive_name, j.suite_name)) 

397 else: 

398 print("{0}".format(j.suite_name)) 

399 

400 

401def __suite_show(d: DBConn, args: list[str]) -> None: 

402 if len(args) < 2: 

403 die("E: showing an suite entry requires a suite") 

404 

405 su = get_suite_or_die(args[2]) 

406 

407 print(su.details()) 

408 

409 

410def __suite_add(d: DBConn, args: list[str], addallarches=False) -> None: 

411 die_arglen(args, 4, "E: adding a suite requires at least a name and a version") 

412 suite_name = args[2].lower() 

413 version = args[3] or None 

414 kvpairs = __suite_config_set_confing_args_as_dict(args[4:]) 

415 

416 print("Adding suite %s" % suite_name) 

417 if not dryrun: 417 ↛ 459line 417 didn't jump to line 459 because the condition on line 417 was always true

418 try: 

419 s = d.session() 

420 suite = Suite() 

421 suite.suite_name = suite_name 

422 suite.overridecodename = None 

423 suite.version = version or None 

424 # Most configurations will be handled by 

425 # __suite_config_internal_set. However, a few are managed 

426 # manually here because __suite_config_internal_set cannot 

427 # handle them. Either because they are create-only or 

428 # because suite-add handled them different (historically) 

429 suite.codename = kvpairs.pop("codename", None) 

430 signingkey = kvpairs.pop("signingkey", None) 

431 if signingkey is not None: 431 ↛ 432line 431 didn't jump to line 432 because the condition on line 431 was never true

432 suite.signingkeys = [signingkey.upper()] 

433 archive_name = kvpairs.pop("archive", None) 

434 if archive_name is not None: 

435 archive = get_archive(archive_name, s) 

436 assert archive is not None 

437 suite.archive = archive 

438 else: 

439 suite.archive = ( 

440 s.query(Archive) 

441 .filter( 

442 ~Archive.archive_name.in_(["build-queues", "new", "policy"]) 

443 ) 

444 .one() 

445 ) 

446 __suite_config_internal_set( 

447 suite, suite_name, kvpairs, print_config_set=False 

448 ) 

449 s.add(suite) 

450 suite.srcformats = s.query(SrcFormat).all() 

451 s.flush() 

452 except IntegrityError: 

453 die( 

454 "E: Integrity error adding suite %s (it probably already exists)" 

455 % suite_name 

456 ) 

457 except SQLAlchemyError as e: 

458 die("E: Error adding suite %s (%s)" % (suite_name, e)) 

459 print("Suite %s added" % (suite_name)) 

460 

461 if addallarches: 

462 arches = [] 

463 q = s.query(Architecture).order_by(Architecture.arch_string) 

464 for arch in q.all(): 

465 suite.architectures.append(arch) 

466 arches.append(arch.arch_string) 

467 

468 print("Architectures %s added to %s" % (",".join(arches), suite_name)) 

469 

470 s.commit() 

471 

472 

473def __suite_rm(d: DBConn, args: list[str]) -> None: 

474 die_arglen(args, 3, "E: removing a suite requires at least a name") 

475 name = args[2] 

476 print("Removing suite {0}".format(name)) 

477 if not dryrun: 

478 try: 

479 s = d.session() 

480 su = get_suite_or_die(name, s) 

481 s.delete(su) 

482 s.commit() 

483 except IntegrityError: 

484 die( 

485 "E: Integrity error removing suite {0} (suite-arch entries probably still exist)".format( 

486 name 

487 ) 

488 ) 

489 except SQLAlchemyError as e: 

490 die("E: Error removing suite {0} ({1})".format(name, e)) 

491 print("Suite {0} removed".format(name)) 

492 

493 

494def __suite_add_build_queue(d: DBConn, args: list[str]) -> None: 

495 session = d.session() 

496 

497 die_arglen(args, 6, "E: Adding a build queue needs four parameters.") 

498 

499 suite_name = args[2] 

500 build_queue_name = args[3] 

501 build_queue_codename = args[4] 

502 build_queue_archive_name = args[5] 

503 try: 

504 suite = session.query(Suite).filter_by(suite_name=suite_name).one() 

505 except NoResultFound: 

506 die("E: Unknown suite '{0}'".format(suite_name)) 

507 try: 

508 build_queue_archive = ( 

509 session.query(Archive) 

510 .filter_by(archive_name=build_queue_archive_name) 

511 .one() 

512 ) 

513 except NoResultFound: 

514 die("E: Unknown archive '{0}'".format(build_queue_archive_name)) 

515 

516 # Create suite 

517 s = Suite() 

518 s.suite_name = build_queue_name 

519 s.origin = suite.origin 

520 s.label = suite.label 

521 s.description = "buildd {0} incoming".format(suite_name) 

522 s.codename = build_queue_codename 

523 s.notautomatic = suite.notautomatic 

524 s.overridesuite = suite.overridesuite or suite.suite_name 

525 s.butautomaticupgrades = suite.butautomaticupgrades 

526 s.signingkeys = suite.signingkeys 

527 s.include_long_description = False 

528 

529 # Do not accept direct uploads to the build queue 

530 s.accept_source_uploads = False 

531 s.accept_binary_uploads = False 

532 

533 session.add(s) 

534 

535 s.archive = build_queue_archive 

536 s.architectures.extend(suite.architectures) 

537 s.components.extend(suite.components) 

538 s.srcformats.extend(suite.srcformats) 

539 

540 session.flush() 

541 

542 bq = BuildQueue() 

543 bq.queue_name = build_queue_codename 

544 bq.suite = s 

545 

546 session.add(bq) 

547 session.flush() 

548 

549 suite.copy_queues.append(bq) 

550 

551 session.commit() 

552 

553 

554def suite(args: list[str]) -> None: 

555 d = DBConn() 

556 

557 die_arglen(args, 2, "E: suite needs at least a command") 

558 

559 mode = args[1].lower() 

560 

561 if mode == "list": 

562 __suite_list(d, args) 

563 elif mode == "show": 563 ↛ 564line 563 didn't jump to line 564 because the condition on line 563 was never true

564 __suite_show(d, args) 

565 elif mode == "rm": 565 ↛ 566line 565 didn't jump to line 566 because the condition on line 565 was never true

566 __suite_rm(d, args) 

567 elif mode == "add": 

568 __suite_add(d, args, False) 

569 elif mode == "add-all-arches": 

570 __suite_add(d, args, True) 

571 elif mode == "add-build-queue": 571 ↛ 574line 571 didn't jump to line 574 because the condition on line 571 was always true

572 __suite_add_build_queue(d, args) 

573 else: 

574 die("E: suite command unknown") 

575 

576 

577dispatch["suite"] = suite 

578dispatch["s"] = suite 

579 

580################################################################################ 

581 

582 

583def __suite_architecture_list(d: DBConn, args: list[str]) -> None: 

584 s = d.session() 

585 for j in s.query(Suite).order_by(Suite.suite_name): 

586 architectures = j.get_architectures(skipsrc=True, skipall=True) 

587 print(j.suite_name + ": " + ", ".join([a.arch_string for a in architectures])) 

588 

589 

590def __suite_architecture_listarch(d: DBConn, args: list[str]) -> None: 

591 die_arglen(args, 3, "E: suite-architecture list-arch requires a suite") 

592 suite = get_suite_or_die(args[2], d.session()) 

593 a = suite.get_architectures(skipsrc=True, skipall=True) 

594 for j in a: 

595 print(j.arch_string) 

596 

597 

598def __suite_architecture_listsuite(d: DBConn, args: list[str]) -> None: 

599 die_arglen(args, 3, "E: suite-architecture list-suite requires an arch") 

600 architecture = get_architecture(args[2].lower(), d.session()) 

601 if architecture is None: 

602 die("E: architecture %s is invalid" % args[2].lower()) 

603 for j in architecture.suites: 

604 print(j.suite_name) 

605 

606 

607def __suite_architecture_add(d: DBConn, args: list[str]) -> None: 

608 if len(args) < 3: 608 ↛ 609line 608 didn't jump to line 609 because the condition on line 608 was never true

609 die("E: adding a suite-architecture entry requires a suite and arch") 

610 

611 s = d.session() 

612 

613 suite = get_suite_or_die(args[2], s) 

614 

615 for arch_name in args[3:]: 

616 arch = get_architecture(arch_name.lower(), s) 

617 if arch is None: 617 ↛ 618line 617 didn't jump to line 618 because the condition on line 617 was never true

618 die("E: Can't find architecture %s" % args[3].lower()) 

619 

620 try: 

621 suite.architectures.append(arch) 

622 s.flush() 

623 except IntegrityError: 

624 die( 

625 "E: Can't add suite-architecture entry (%s, %s) - probably already exists" 

626 % (args[2].lower(), arch_name) 

627 ) 

628 except SQLAlchemyError as e: 

629 die( 

630 "E: Can't add suite-architecture entry (%s, %s) - %s" 

631 % (args[2].lower(), arch_name, e) 

632 ) 

633 

634 print( 

635 "Added suite-architecture entry for %s, %s" % (args[2].lower(), arch_name) 

636 ) 

637 

638 if not dryrun: 638 ↛ 641line 638 didn't jump to line 641 because the condition on line 638 was always true

639 s.commit() 

640 

641 s.close() 

642 

643 

644def __suite_architecture_rm(d: DBConn, args: list[str]) -> None: 

645 if len(args) < 3: 

646 die("E: removing an suite-architecture entry requires a suite and arch") 

647 

648 s = d.session() 

649 if not dryrun: 

650 try: 

651 suite_name = args[2].lower() 

652 suite = get_suite_or_die(suite_name, s) 

653 arch_string = args[3].lower() 

654 architecture = get_architecture(arch_string, s) 

655 if architecture not in suite.architectures: 

656 die( 

657 "E: architecture %s not found in suite %s" 

658 % (arch_string, suite_name) 

659 ) 

660 suite.architectures.remove(architecture) 

661 s.commit() 

662 except IntegrityError: 

663 die( 

664 "E: Can't remove suite-architecture entry (%s, %s) - it's probably referenced" 

665 % (args[2].lower(), args[3].lower()) 

666 ) 

667 except SQLAlchemyError as e: 

668 die( 

669 "E: Can't remove suite-architecture entry (%s, %s) - %s" 

670 % (args[2].lower(), args[3].lower(), e) 

671 ) 

672 

673 print( 

674 "Removed suite-architecture entry for %s, %s" 

675 % (args[2].lower(), args[3].lower()) 

676 ) 

677 

678 

679def suite_architecture(args: list[str]) -> None: 

680 d = DBConn() 

681 

682 die_arglen(args, 2, "E: suite-architecture needs at least a command") 

683 

684 mode = args[1].lower() 

685 

686 if mode == "list": 686 ↛ 687line 686 didn't jump to line 687 because the condition on line 686 was never true

687 __suite_architecture_list(d, args) 

688 elif mode == "list-arch": 

689 __suite_architecture_listarch(d, args) 

690 elif mode == "list-suite": 690 ↛ 691line 690 didn't jump to line 691 because the condition on line 690 was never true

691 __suite_architecture_listsuite(d, args) 

692 elif mode == "add": 692 ↛ 694line 692 didn't jump to line 694 because the condition on line 692 was always true

693 __suite_architecture_add(d, args) 

694 elif mode == "rm": 

695 __suite_architecture_rm(d, args) 

696 else: 

697 die("E: suite-architecture command unknown") 

698 

699 

700dispatch["suite-architecture"] = suite_architecture 

701dispatch["s-a"] = suite_architecture 

702 

703################################################################################ 

704 

705 

706def __suite_component_list(d: DBConn, args: list[str]) -> None: 

707 s = d.session() 

708 for j in s.query(Suite).order_by(Suite.suite_name): 

709 components = j.components 

710 print(j.suite_name + ": " + ", ".join([c.component_name for c in components])) 

711 

712 

713def __suite_component_listcomponent(d: DBConn, args: list[str]) -> None: 

714 die_arglen(args, 3, "E: suite-component list-component requires a suite") 

715 suite = get_suite_or_die(args[2], d.session()) 

716 for c in suite.components: 

717 print(c.component_name) 

718 

719 

720def __suite_component_listsuite(d: DBConn, args: list[str]) -> None: 

721 die_arglen(args, 3, "E: suite-component list-suite requires an component") 

722 component = get_component(args[2].lower(), d.session()) 

723 if component is None: 

724 die("E: component %s is invalid" % args[2].lower()) 

725 for s in component.suites: 

726 print(s.suite_name) 

727 

728 

729def __suite_component_add(d: DBConn, args: list[str]) -> None: 

730 if len(args) < 3: 730 ↛ 731line 730 didn't jump to line 731 because the condition on line 730 was never true

731 die("E: adding a suite-component entry requires a suite and component") 

732 

733 s = d.session() 

734 

735 suite = get_suite_or_die(args[2], s) 

736 

737 for component_name in args[3:]: 

738 component = get_component(component_name.lower(), s) 

739 if component is None: 739 ↛ 740line 739 didn't jump to line 740 because the condition on line 739 was never true

740 die("E: Can't find component %s" % args[3].lower()) 

741 

742 try: 

743 suite.components.append(component) 

744 s.flush() 

745 except IntegrityError: 

746 die( 

747 "E: Can't add suite-component entry (%s, %s) - probably already exists" 

748 % (args[2].lower(), component_name) 

749 ) 

750 except SQLAlchemyError as e: 

751 die( 

752 "E: Can't add suite-component entry (%s, %s) - %s" 

753 % (args[2].lower(), component_name, e) 

754 ) 

755 

756 print( 

757 "Added suite-component entry for %s, %s" % (args[2].lower(), component_name) 

758 ) 

759 

760 if not dryrun: 760 ↛ 762line 760 didn't jump to line 762 because the condition on line 760 was always true

761 s.commit() 

762 s.close() 

763 

764 

765def __suite_component_rm(d: DBConn, args: list[str]) -> None: 

766 if len(args) < 3: 

767 die("E: removing an suite-component entry requires a suite and component") 

768 

769 s = d.session() 

770 if not dryrun: 

771 try: 

772 suite_name = args[2].lower() 

773 suite = get_suite_or_die(suite_name, s) 

774 component_string = args[3].lower() 

775 component = get_component(component_string, s) 

776 if component not in suite.components: 

777 die( 

778 "E: component %s not found in suite %s" 

779 % (component_string, suite_name) 

780 ) 

781 suite.components.remove(component) 

782 s.commit() 

783 except IntegrityError: 

784 die( 

785 "E: Can't remove suite-component entry (%s, %s) - it's probably referenced" 

786 % (args[2].lower(), args[3].lower()) 

787 ) 

788 except SQLAlchemyError as e: 

789 die( 

790 "E: Can't remove suite-component entry (%s, %s) - %s" 

791 % (args[2].lower(), args[3].lower(), e) 

792 ) 

793 

794 print( 

795 "Removed suite-component entry for %s, %s" % (args[2].lower(), args[3].lower()) 

796 ) 

797 

798 

799def suite_component(args: list[str]) -> None: 

800 d = DBConn() 

801 

802 die_arglen(args, 2, "E: suite-component needs at least a command") 

803 

804 mode = args[1].lower() 

805 

806 if mode == "list": 806 ↛ 807line 806 didn't jump to line 807 because the condition on line 806 was never true

807 __suite_component_list(d, args) 

808 elif mode == "list-component": 808 ↛ 809line 808 didn't jump to line 809 because the condition on line 808 was never true

809 __suite_component_listcomponent(d, args) 

810 elif mode == "list-suite": 810 ↛ 811line 810 didn't jump to line 811 because the condition on line 810 was never true

811 __suite_component_listsuite(d, args) 

812 elif mode == "add": 812 ↛ 817line 812 didn't jump to line 817 because the condition on line 812 was always true

813 __suite_component_add(d, args) 

814 # elif mode == 'rm': 

815 # __suite_architecture_rm(d, args) 

816 else: 

817 die("E: suite-component command unknown") 

818 

819 

820dispatch["suite-component"] = suite_component 

821dispatch["s-c"] = suite_component 

822 

823 

824################################################################################ 

825 

826# Sentinel for detecting read-only configurations 

827SUITE_CONFIG_READ_ONLY = object() 

828SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON = object() 

829 

830 

831@dataclass 

832class SuiteConfigSerializer: 

833 db_name: str 

834 serialize: Callable[[Any], str | None] 

835 deserialize: Optional[Callable[[str | None], Any]] 

836 

837 

838def _serialize_suite(x: int | None) -> str | None: 

839 if x is None: 839 ↛ 841line 839 didn't jump to line 841 because the condition on line 839 was always true

840 return None 

841 return Suite.get(x).suite_name 

842 

843 

844def _deserialize_suite(x: str | None) -> int | None: 

845 if x is None: 845 ↛ 846line 845 didn't jump to line 846 because the condition on line 845 was never true

846 return None 

847 return get_suite_or_die(x).suite_id 

848 

849 

850@session_wrapper 

851def _serialize_policy_queue(x, session=None): 

852 if x is None: 852 ↛ 854line 852 didn't jump to line 854 because the condition on line 852 was always true

853 return None 

854 try: 

855 policy_obj = session.query(PolicyQueue).filter_by(policy_queue_id=x).one() 

856 except NoResultFound: 

857 return None 

858 return policy_obj.queue_name 

859 

860 

861def _deserialize_policy_queue(x): 

862 if x is None: 

863 return None 

864 policy_queue = get_policy_queue(x) 

865 if policy_queue is None: 

866 raise ValueError("There is no policy queue with name %s" % x) 

867 return policy_queue.policy_queue_id 

868 

869 

870@session_wrapper 

871def _serialize_archive(x, session=None): 

872 if x is None: 872 ↛ 873line 872 didn't jump to line 873 because the condition on line 872 was never true

873 return None 

874 try: 

875 archive_obj = session.query(Archive).filter_by(archive_id=x).one() 

876 except NoResultFound: 

877 return None 

878 return archive_obj.archive_name 

879 

880 

881CUSTOM_SUITE_CONFIG_SERIALIZERS = { 

882 "archive": SuiteConfigSerializer( 

883 db_name="archive_id", serialize=_serialize_archive, deserialize=None 

884 ), 

885 "debugsuite": SuiteConfigSerializer( 

886 db_name="debugsuite_id", 

887 serialize=_serialize_suite, 

888 deserialize=_deserialize_suite, 

889 ), 

890 "new_queue": SuiteConfigSerializer( 

891 db_name="new_queue_id", 

892 serialize=_serialize_policy_queue, 

893 deserialize=_deserialize_policy_queue, 

894 ), 

895 "policy_queue": SuiteConfigSerializer( 

896 db_name="policy_queue_id", 

897 serialize=_serialize_policy_queue, 

898 deserialize=_deserialize_policy_queue, 

899 ), 

900} 

901 

902 

903ALLOWED_SUITE_CONFIGS: dict[str, Callable[[str], object] | object] = { 

904 "accept_binary_uploads": utils.parse_boolean_from_user, 

905 "accept_source_uploads": utils.parse_boolean_from_user, 

906 "allowcsset": utils.parse_boolean_from_user, 

907 "announce": SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON, 

908 "archive": SUITE_CONFIG_READ_ONLY, 

909 "butautomaticupgrades": utils.parse_boolean_from_user, 

910 "byhash": utils.parse_boolean_from_user, 

911 "changelog": str, 

912 "changelog_url": str, 

913 "checksums": SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON, 

914 "close_bugs": utils.parse_boolean_from_user, 

915 "codename": SUITE_CONFIG_READ_ONLY, 

916 "debugsuite": str, 

917 "description": str, 

918 "include_long_description": utils.parse_boolean_from_user, 

919 "indices_compression": SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON, 

920 "label": str, 

921 "mail_whitelist": str, 

922 "merged_pdiffs": utils.parse_boolean_from_user, 

923 "new_queue": str, 

924 "notautomatic": utils.parse_boolean_from_user, 

925 "origin": str, 

926 "overridecodename": str, 

927 "overrideorigin": str, 

928 "overrideprocess": utils.parse_boolean_from_user, 

929 "overridesuite": str, 

930 "policy_queue": str, 

931 "priority": int, 

932 "separate_contents_architecture_all": utils.parse_boolean_from_user, 

933 # We do not support separate Packages-all, so do not let people set it. 

934 "separate_packages_architecture_all": SUITE_CONFIG_READ_ONLY, 

935 "signingkeys": SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON, 

936 "suite_name": SUITE_CONFIG_READ_ONLY, 

937 "untouchable": utils.parse_boolean_from_user, 

938 "validtime": int, 

939 "stayofexecution": str, 

940} 

941 

942 

943def _get_suite_value(suite: Suite, conf_name: str): 

944 serial_config = CUSTOM_SUITE_CONFIG_SERIALIZERS.get(conf_name) 

945 if serial_config is None: 

946 return getattr(suite, conf_name) 

947 db_value = getattr(suite, serial_config.db_name) 

948 return serial_config.serialize(db_value) 

949 

950 

951def _set_suite_value(suite: Suite, conf_name: str, new_value): 

952 serial_config = CUSTOM_SUITE_CONFIG_SERIALIZERS.get(conf_name) 

953 db_name = conf_name 

954 if serial_config is not None: 

955 assert serial_config.deserialize is not None, ( 

956 "Changing %s is not supported!" % conf_name 

957 ) 

958 new_value = serial_config.deserialize(new_value) 

959 db_name = serial_config.db_name 

960 setattr(suite, db_name, new_value) 

961 

962 

963def __suite_config_get( 

964 d: DBConn, args: list[str], direct_value=False, json_format=False 

965) -> None: 

966 die_arglen(args, 4, "E: suite-config get needs the name of a configuration") 

967 session = d.session() 

968 suite_name = args[2] 

969 suite = get_suite_or_die(suite_name, session) 

970 values = {} 

971 for arg in args[3:]: 

972 if arg not in ALLOWED_SUITE_CONFIGS: 972 ↛ 973line 972 didn't jump to line 973 because the condition on line 972 was never true

973 die("Unknown (or unsupported) suite configuration variable") 

974 value = _get_suite_value(suite, arg) 

975 if json_format: 975 ↛ 976line 975 didn't jump to line 976 because the condition on line 975 was never true

976 values[arg] = value 

977 elif direct_value: 977 ↛ 978line 977 didn't jump to line 978 because the condition on line 977 was never true

978 print(value) 

979 else: 

980 print("%s=%s" % (arg, value)) 

981 if json_format: 981 ↛ 982line 981 didn't jump to line 982 because the condition on line 981 was never true

982 print(json.dumps(values, indent=2, sort_keys=True)) 

983 

984 

985def __suite_config_set(d: DBConn, args: list[str]) -> None: 

986 die_arglen(args, 4, "E: suite-config set needs the name of a configuration") 

987 session = d.session() 

988 suite_name = args[2] 

989 suite = get_suite_or_die(suite_name, session) 

990 args_as_kvpairs = __suite_config_set_confing_args_as_dict(args[3:]) 

991 __suite_config_internal_set( 

992 suite, suite_name, args_as_kvpairs, print_config_set=True 

993 ) 

994 if dryrun: 

995 session.rollback() 

996 print() 

997 print("This was a dryrun; changes have been rolled back") 

998 else: 

999 session.commit() 

1000 

1001 

1002def __suite_config_set_confing_args_as_dict(args: list[str]) -> dict[str, str]: 

1003 # Use OrderedDict to preserve order (makes "dak admin suite-config set ..." 

1004 # less confusing when things are processed in the input order) 

1005 kvpairs = collections.OrderedDict() 

1006 for arg in args: 

1007 if "=" not in arg: 1007 ↛ 1008line 1007 didn't jump to line 1008 because the condition on line 1007 was never true

1008 die("Missing value for configuration %s: Use key=value format" % arg) 

1009 conf_name, new_value_str = arg.split("=", 1) 

1010 kvpairs[conf_name] = new_value_str 

1011 return kvpairs 

1012 

1013 

1014def __suite_config_internal_set( 

1015 suite: Suite, suite_name: str, kvpairs: dict[str, str], print_config_set=True 

1016) -> None: 

1017 for kvpair in kvpairs.items(): 

1018 conf_name, new_value_str = kvpair 

1019 cli_parser = ALLOWED_SUITE_CONFIGS.get(conf_name) 

1020 if cli_parser is None: 1020 ↛ 1021line 1020 didn't jump to line 1021 because the condition on line 1020 was never true

1021 die("Unknown (or unsupported) suite configuration variable") 

1022 if cli_parser in (SUITE_CONFIG_READ_ONLY, SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON): 1022 ↛ 1023line 1022 didn't jump to line 1023 because the condition on line 1022 was never true

1023 if cli_parser == SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON: 

1024 msg = ( 

1025 "Cannot parse value for %s" 

1026 """ - set via echo '{"%s": <...>}' | dak suite-config set-json %s instead""" 

1027 ) 

1028 warn(msg % (conf_name, conf_name, suite_name)) 

1029 die("Cannot change %s from the command line" % conf_name) 

1030 assert callable(cli_parser) 

1031 try: 

1032 new_value = cli_parser(new_value_str) 

1033 except (RuntimeError, ValueError, TypeError) as e: 

1034 warn( 

1035 "Could not parse new value for %s (given: %s)" 

1036 % (conf_name, new_value_str) 

1037 ) 

1038 raise e 

1039 try: 

1040 _set_suite_value(suite, conf_name, new_value) 

1041 except (RuntimeError, ValueError, TypeError) as e: 

1042 warn("Could not set new value for %s (given: %s)" % (conf_name, new_value)) 

1043 raise e 

1044 if print_config_set: 

1045 print("%s=%s" % (conf_name, _get_suite_value(suite, conf_name))) 

1046 

1047 

1048def __suite_config_set_json(d: DBConn, args: list[str]) -> None: 

1049 session = d.session() 

1050 suite_name = args[2] 

1051 suite = get_suite_or_die(suite_name, session) 

1052 filename = "-" 

1053 if len(args) > 3: 

1054 if len(args) > 4: 

1055 warn("W: Ignoring extra argument after the json file name") 

1056 filename = args[3] 

1057 if filename != "-": 

1058 with open(filename) as fd: 

1059 update_config = json.load(fd) 

1060 else: 

1061 update_config = json.load(sys.stdin) 

1062 if update_config is None or not isinstance(update_config, dict): 

1063 die( 

1064 "E: suite-config set-json expects a dictionary (json object), got %s" 

1065 % type(update_config) 

1066 ) 

1067 

1068 for conf_name in sorted(update_config): 

1069 new_value = update_config[conf_name] 

1070 cli_parser = ALLOWED_SUITE_CONFIGS.get(conf_name) 

1071 if cli_parser is None: 

1072 die("Unknown (or unsupported) suite configuration variable: %s" % conf_name) 

1073 if cli_parser is SUITE_CONFIG_READ_ONLY: 

1074 die("Cannot change %s via JSON" % conf_name) 

1075 try: 

1076 _set_suite_value(suite, conf_name, new_value) 

1077 except (RuntimeError, ValueError, TypeError) as e: 

1078 warn("Could not set new value for %s (given: %s)" % (conf_name, new_value)) 

1079 raise e 

1080 print("%s=%s" % (conf_name, _get_suite_value(suite, conf_name))) 

1081 if dryrun: 

1082 session.rollback() 

1083 print() 

1084 print("This was a dryrun; changes have been rolled back") 

1085 else: 

1086 session.commit() 

1087 

1088 

1089def __suite_config_list(d: DBConn, args: list[str], json_format=False) -> None: 

1090 suite = None 

1091 session = d.session() 

1092 if len(args) > 3: 1092 ↛ 1093line 1092 didn't jump to line 1093 because the condition on line 1092 was never true

1093 warn("W: Ignoring extra argument after the suite name") 

1094 if len(args) == 3: 1094 ↛ 1098line 1094 didn't jump to line 1098 because the condition on line 1094 was always true

1095 suite_name = args[2] 

1096 suite = get_suite_or_die(suite_name, session) 

1097 else: 

1098 if json_format: 

1099 die("E: suite-config list-json requires a suite name!") 

1100 print("Valid suite-config options manageable by this command:") 

1101 print() 

1102 values = {} 

1103 

1104 for arg in sorted(ALLOWED_SUITE_CONFIGS): 

1105 mode = "writable" 

1106 if suite is not None: 1106 ↛ 1113line 1106 didn't jump to line 1113 because the condition on line 1106 was always true

1107 value = _get_suite_value(suite, arg) 

1108 if json_format: 1108 ↛ 1109line 1108 didn't jump to line 1109 because the condition on line 1108 was never true

1109 values[arg] = value 

1110 else: 

1111 print("%s=%s" % (arg, value)) 

1112 else: 

1113 converter = ALLOWED_SUITE_CONFIGS[arg] 

1114 if converter is SUITE_CONFIG_READ_ONLY: 

1115 mode = "read-only" 

1116 elif converter is SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON: 

1117 mode = "writeable (via set-json only)" 

1118 print(" * %s (%s)" % (arg, mode)) 

1119 if json_format: 1119 ↛ 1120line 1119 didn't jump to line 1120 because the condition on line 1119 was never true

1120 print(json.dumps(values, indent=2, sort_keys=True)) 

1121 

1122 

1123def suite_config(args: list[str]) -> None: 

1124 d = DBConn() 

1125 

1126 die_arglen(args, 2, "E: suite-config needs a command") 

1127 mode = args[1].lower() 

1128 

1129 if mode in {"get", "get-value", "get-json"}: 

1130 direct_value = False 

1131 json_format = mode == "get-json" 

1132 if mode == "get-value": 1132 ↛ 1133line 1132 didn't jump to line 1133 because the condition on line 1132 was never true

1133 direct_value = True 

1134 if len(args) > 4: 

1135 die("E: get-value must receive exactly one key to lookup") 

1136 __suite_config_get(d, args, direct_value=direct_value, json_format=json_format) 

1137 elif mode == "set": 

1138 __suite_config_set(d, args) 

1139 elif mode == "set-json": 1139 ↛ 1140line 1139 didn't jump to line 1140 because the condition on line 1139 was never true

1140 __suite_config_set_json(d, args) 

1141 elif mode in {"list", "list-json"}: 1141 ↛ 1145line 1141 didn't jump to line 1145 because the condition on line 1141 was always true

1142 json_format = mode == "list-json" 

1143 __suite_config_list(d, args, json_format=json_format) 

1144 else: 

1145 suite = get_suite(mode, d.session()) 

1146 if suite is not None: 

1147 warn("Did you get the order of the suite and the subcommand wrong?") 

1148 warn("Syntax: dak admin %s {get,set,...} <suite>" % args[0]) 

1149 die("E: suite-config command unknown") 

1150 

1151 

1152dispatch["suite-config"] = suite_config 

1153dispatch["suite-cfg"] = suite_config 

1154dispatch["s-cfg"] = suite_config 

1155 

1156 

1157################################################################################ 

1158 

1159 

1160def archive_list() -> None: 

1161 session = DBConn().session() 

1162 for archive in session.query(Archive).order_by(Archive.archive_name): 

1163 print( 

1164 "{0} path={1} description={2} tainted={3}".format( 

1165 archive.archive_name, archive.path, archive.description, archive.tainted 

1166 ) 

1167 ) 

1168 

1169 

1170def archive_add(args: list[str]) -> None: 

1171 (name, path, description) = args[0:3] 

1172 

1173 attributes = dict( 

1174 archive_name=name, 

1175 path=path, 

1176 description=description, 

1177 ) 

1178 

1179 for option in args[3:]: 1179 ↛ 1180line 1179 didn't jump to line 1180 because the loop on line 1179 never started

1180 (key, value) = option.split("=") 

1181 attributes[key] = value 

1182 

1183 session = DBConn().session() 

1184 

1185 archive = Archive() 

1186 for key, value in attributes.items(): 

1187 setattr(archive, key, value) 

1188 

1189 session.add(archive) 

1190 session.flush() 

1191 

1192 if dryrun: 1192 ↛ 1193line 1192 didn't jump to line 1193 because the condition on line 1192 was never true

1193 session.rollback() 

1194 else: 

1195 session.commit() 

1196 

1197 

1198def archive_rm(name: str) -> None: 

1199 session = DBConn().session() 

1200 archive = get_archive(name, session) 

1201 session.delete(archive) 

1202 session.flush() 

1203 

1204 if dryrun: 

1205 session.rollback() 

1206 else: 

1207 session.commit() 

1208 

1209 

1210def archive_rename(oldname: str, newname: str) -> None: 

1211 session = DBConn().session() 

1212 archive = get_archive(oldname, session) 

1213 assert archive is not None 

1214 archive.archive_name = newname 

1215 session.flush() 

1216 

1217 if dryrun: 

1218 session.rollback() 

1219 else: 

1220 session.commit() 

1221 

1222 

1223def archive(args: list[str]) -> None: 

1224 mode = args[1] 

1225 if mode == "list": 

1226 archive_list() 

1227 elif mode == "rename": 1227 ↛ 1228line 1227 didn't jump to line 1228 because the condition on line 1227 was never true

1228 archive_rename(args[2], args[3]) 

1229 elif mode == "add": 1229 ↛ 1231line 1229 didn't jump to line 1231 because the condition on line 1229 was always true

1230 archive_add(args[2:]) 

1231 elif mode == "rm": 

1232 archive_rm(args[2]) 

1233 else: 

1234 die("E: archive command unknown") 

1235 

1236 

1237dispatch["archive"] = archive 

1238 

1239################################################################################ 

1240 

1241 

1242def __version_check_list(d: DBConn) -> None: 

1243 session = d.session() 

1244 for s in session.query(Suite).order_by(Suite.suite_name): 

1245 __version_check_list_suite(d, s.suite_name) 

1246 

1247 

1248def __version_check_list_suite(d: DBConn, suite_name: str) -> None: 

1249 vcs = get_version_checks(suite_name) 

1250 for vc in vcs: 

1251 print("%s %s %s" % (suite_name, vc.check, vc.reference.suite_name)) 

1252 

1253 

1254def __version_check_add( 

1255 d: DBConn, suite_name: str, check: str, reference_name: str 

1256) -> None: 

1257 suite = get_suite_or_die( 

1258 suite_name, error_message="E: Could not find suite %(suite_name)s" 

1259 ) 

1260 reference = get_suite_or_die( 

1261 reference_name, error_message="E: Could not find reference suite %(suite_name)s" 

1262 ) 

1263 

1264 session = d.session() 

1265 vc = VersionCheck() 

1266 vc.suite = suite 

1267 vc.check = check 

1268 vc.reference = reference 

1269 session.add(vc) 

1270 session.commit() 

1271 

1272 

1273def __version_check_rm( 

1274 d: DBConn, suite_name: str, check: str, reference_name: str 

1275) -> None: 

1276 suite = get_suite_or_die( 

1277 suite_name, error_message="E: Could not find suite %(suite_name)s" 

1278 ) 

1279 reference = get_suite_or_die( 

1280 reference_name, error_message="E: Could not find reference suite %(suite_name)s" 

1281 ) 

1282 

1283 session = d.session() 

1284 try: 

1285 vc = ( 

1286 session.query(VersionCheck) 

1287 .filter_by(suite=suite, check=check, reference=reference) 

1288 .one() 

1289 ) 

1290 session.delete(vc) 

1291 session.commit() 

1292 except NoResultFound: 

1293 print("W: version-check not found.") 

1294 

1295 

1296def version_check(args: list[str]) -> None: 

1297 d = DBConn() 

1298 

1299 die_arglen(args, 2, "E: version-check needs at least a command") 

1300 mode = args[1].lower() 

1301 

1302 if mode == "list": 1302 ↛ 1303line 1302 didn't jump to line 1303 because the condition on line 1302 was never true

1303 __version_check_list(d) 

1304 elif mode == "list-suite": 1304 ↛ 1305line 1304 didn't jump to line 1305 because the condition on line 1304 was never true

1305 if len(args) != 3: 

1306 die("E: version-check list-suite needs a single parameter") 

1307 __version_check_list_suite(d, args[2]) 

1308 elif mode == "add": 1308 ↛ 1312line 1308 didn't jump to line 1312 because the condition on line 1308 was always true

1309 if len(args) != 5: 1309 ↛ 1310line 1309 didn't jump to line 1310 because the condition on line 1309 was never true

1310 die("E: version-check add needs three parameters") 

1311 __version_check_add(d, args[2], args[3], args[4]) 

1312 elif mode == "rm": 

1313 if len(args) != 5: 

1314 die("E: version-check rm needs three parameters") 

1315 __version_check_rm(d, args[2], args[3], args[4]) 

1316 else: 

1317 die("E: version-check command unknown") 

1318 

1319 

1320dispatch["version-check"] = version_check 

1321dispatch["v-c"] = version_check 

1322 

1323################################################################################ 

1324 

1325 

1326def show_config(args: list[str]) -> None: 

1327 cnf = utils.get_conf() 

1328 

1329 die_arglen(args, 2, "E: config needs at least a command") 

1330 

1331 mode = args[1].lower() 

1332 

1333 if mode == "db": 

1334 connstr = "" 

1335 if "DB::Service" in cnf: 

1336 # Service mode 

1337 connstr = "postgresql://service=%s" % cnf["DB::Service"] 

1338 elif "DB::Host" in cnf: 

1339 # TCP/IP 

1340 connstr = "postgresql://%s" % cnf["DB::Host"] 

1341 if "DB::Port" in cnf and cnf["DB::Port"] != "-1": 

1342 connstr += ":%s" % cnf["DB::Port"] 

1343 connstr += "/%s" % cnf["DB::Name"] 

1344 else: 

1345 # Unix Socket 

1346 connstr = "postgresql:///%s" % cnf["DB::Name"] 

1347 if cnf["DB::Port"] and cnf["DB::Port"] != "-1": 

1348 connstr += "?port=%s" % cnf["DB::Port"] 

1349 print(connstr) 

1350 elif mode == "db-shell": 

1351 e = [] 

1352 if "DB::Service" in cnf: 

1353 e.append("PGSERVICE") 

1354 print("PGSERVICE=%s" % cnf["DB::Service"]) 

1355 if "DB::Name" in cnf: 

1356 e.append("PGDATABASE") 

1357 print("PGDATABASE=%s" % cnf["DB::Name"]) 

1358 if "DB::Host" in cnf: 

1359 print("PGHOST=%s" % cnf["DB::Host"]) 

1360 e.append("PGHOST") 

1361 if "DB::Port" in cnf and cnf["DB::Port"] != "-1": 

1362 print("PGPORT=%s" % cnf["DB::Port"]) 

1363 e.append("PGPORT") 

1364 print("export " + " ".join(e)) 

1365 elif mode == "get": 

1366 print(cnf.get(args[2])) 

1367 else: 

1368 session = DBConn().session() 

1369 try: 

1370 o = session.query(DBConfig).filter_by(name=mode).one() 

1371 print(o.value) 

1372 except NoResultFound: 

1373 print("W: option '%s' not set" % mode) 

1374 

1375 

1376dispatch["config"] = show_config 

1377dispatch["c"] = show_config 

1378 

1379################################################################################ 

1380 

1381 

1382def show_keyring(args: list[str]) -> None: 

1383 die_arglen(args, 2, "E: keyring needs at least a command") 

1384 

1385 mode = args[1].lower() 

1386 

1387 d = DBConn() 

1388 

1389 q = d.session().query(Keyring).filter(Keyring.active == True) # noqa:E712 

1390 

1391 if mode == "list-all": 

1392 pass 

1393 elif mode == "list-binary": 

1394 q = q.join(Keyring.acl).filter(ACL.allow_source == False) # noqa:E712 

1395 elif mode == "list-source": 

1396 q = q.join(Keyring.acl).filter(ACL.allow_source == True) # noqa:E712 

1397 else: 

1398 die("E: keyring command unknown") 

1399 

1400 for k in q.all(): 

1401 print(k.keyring_name) 

1402 

1403 

1404def keyring_add_buildd(command: list[str]) -> None: 

1405 name = command[2] 

1406 arch_names = command[3:] 

1407 

1408 session = DBConn().session() 

1409 arches = session.query(Architecture).filter( 

1410 Architecture.arch_string.in_(arch_names) 

1411 ) 

1412 

1413 acl = ACL() 

1414 acl.name = "buildd-{0}".format("+".join(arch_names)) 

1415 acl.architectures.update(arches) 

1416 acl.allow_new = True 

1417 acl.allow_binary = True 

1418 acl.allow_binary_only = True 

1419 acl.allow_hijack = True 

1420 session.add(acl) 

1421 

1422 k = Keyring() 

1423 k.keyring_name = name 

1424 k.acl = acl 

1425 k.priority = 10 

1426 session.add(k) 

1427 

1428 session.commit() 

1429 

1430 

1431def keyring(args: list[str]) -> None: 

1432 if args[1].startswith("list-"): 

1433 show_keyring(args) 

1434 elif args[1] == "add-buildd": 

1435 keyring_add_buildd(args) 

1436 else: 

1437 die("E: keyring command unknown") 

1438 

1439 

1440dispatch["keyring"] = keyring 

1441dispatch["k"] = keyring 

1442 

1443################################################################################ 

1444 

1445 

1446def change_component_source( 

1447 transaction: daklib.archive.ArchiveTransaction, 

1448 suite: Suite, 

1449 component: Component, 

1450 source_names: list[str], 

1451) -> None: 

1452 session = transaction.session 

1453 

1454 overrides = ( 

1455 session.query(Override) 

1456 .filter(Override.package.in_(source_names)) 

1457 .filter_by(suite=suite) 

1458 .join(OverrideType) 

1459 .filter_by(overridetype="dsc") 

1460 ) 

1461 for override in overrides: 

1462 print("Changing override for {0}".format(override.package)) 

1463 override.component = component 

1464 session.flush() 

1465 

1466 sources = ( 

1467 session.query(DBSource) 

1468 .filter(DBSource.source.in_(source_names)) 

1469 .filter(DBSource.suites.contains(suite)) 

1470 ) 

1471 for source in sources: 

1472 print("Copying {0}={1}".format(source.source, source.version)) 

1473 transaction.copy_source(source, suite, component) 

1474 

1475 

1476def change_component_binary( 

1477 transaction: daklib.archive.ArchiveTransaction, 

1478 suite: Suite, 

1479 component: Component, 

1480 binary_names: list[str], 

1481) -> None: 

1482 session = transaction.session 

1483 

1484 overrides = ( 

1485 session.query(Override) 

1486 .filter(Override.package.in_(binary_names)) 

1487 .filter_by(suite=suite) 

1488 .join(OverrideType) 

1489 .filter(OverrideType.overridetype.in_(["deb", "udeb"])) 

1490 ) 

1491 for override in overrides: 

1492 print("Changing override for {0}".format(override.package)) 

1493 override.component = component 

1494 session.flush() 

1495 

1496 binaries = ( 

1497 session.query(DBBinary) 

1498 .filter(DBBinary.package.in_(binary_names)) 

1499 .filter(DBBinary.suites.contains(suite)) 

1500 ) 

1501 for binary in binaries: 

1502 print( 

1503 "Copying {0}={1} [{2}]".format( 

1504 binary.package, binary.version, binary.architecture.arch_string 

1505 ) 

1506 ) 

1507 transaction.copy_binary(binary, suite, component) 

1508 pass 

1509 

1510 

1511def change_component(args: list[str]) -> None: 

1512 with daklib.archive.ArchiveTransaction() as transaction: 

1513 session = transaction.session 

1514 

1515 suite = session.query(Suite).filter_by(suite_name=args[1]).one() 

1516 component = session.query(Component).filter_by(component_name=args[2]).one() 

1517 

1518 if args[3] == "source": 

1519 change_component_source(transaction, suite, component, args[4:]) 

1520 elif args[3] == "binary": 

1521 change_component_binary(transaction, suite, component, args[4:]) 

1522 else: 

1523 raise Exception("Can only move source or binary, not {0}".format(args[3])) 

1524 

1525 transaction.commit() 

1526 

1527 

1528dispatch["change-component"] = change_component 

1529 

1530################################################################################ 

1531 

1532 

1533def forget_signature(args: list[str]) -> None: 

1534 filename = args[1] 

1535 with open(filename, "rb") as fh: 

1536 data = fh.read() 

1537 

1538 session = DBConn().session() 

1539 keyrings = [ 

1540 k.keyring_name 

1541 for k in session.query(Keyring) 

1542 .filter_by(active=True) 

1543 .order_by(Keyring.priority) 

1544 ] 

1545 signed_file = daklib.gpg.SignedFile(data, keyrings) 

1546 history = SignatureHistory.from_signed_file(signed_file).query(session) 

1547 if history is not None: 1547 ↛ 1551line 1547 didn't jump to line 1551 because the condition on line 1547 was always true

1548 session.delete(history) 

1549 session.commit() 

1550 else: 

1551 print("Signature was not known to dak.") 

1552 session.rollback() 

1553 

1554 

1555dispatch["forget-signature"] = forget_signature 

1556 

1557################################################################################ 

1558 

1559 

1560def main() -> None: 

1561 """Perform administrative work on the dak database""" 

1562 global dryrun 

1563 Cnf = utils.get_conf() 

1564 arguments = [ 

1565 ("h", "help", "Admin::Options::Help"), 

1566 ("n", "dry-run", "Admin::Options::Dry-Run"), 

1567 ] 

1568 for i in ["help", "dry-run"]: 

1569 key = "Admin::Options::%s" % i 

1570 if key not in Cnf: 1570 ↛ 1568line 1570 didn't jump to line 1568 because the condition on line 1570 was always true

1571 Cnf[key] = "" # type: ignore[index] 

1572 

1573 args = apt_pkg.parse_commandline(Cnf, arguments, sys.argv) # type: ignore[attr-defined] 

1574 

1575 options = Cnf.subtree("Admin::Options") # type: ignore[attr-defined] 

1576 if options["Help"] or len(args) < 1: 

1577 usage() 

1578 if options["Dry-Run"]: 

1579 dryrun = True 

1580 

1581 subcommand = args[0] 

1582 

1583 if subcommand in dispatch: 1583 ↛ 1586line 1583 didn't jump to line 1586 because the condition on line 1583 was always true

1584 dispatch[subcommand](args) 

1585 else: 

1586 die("E: Unknown command") 

1587 

1588 

1589################################################################################ 

1590 

1591 

1592if __name__ == "__main__": 

1593 main()