Coverage for n2vc/tests/unit/utils.py: 96%

91 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-05-07 06:04 +0000

1# Copyright 2020 Canonical Ltd. 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14 

15import asyncio 

16 

17from n2vc.utils import Dict, N2VCDeploymentStatus 

18from n2vc.n2vc_conn import N2VCConnector 

19from unittest.mock import MagicMock 

20 

21 

22kubeconfig = """apiVersion: v1 

23clusters: 

24- cluster: 

25 certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1\ 

26 JSURBVENDQWVtZ0F3SUJBZ0lKQUxjMk9xVUpwcnVCTUEwR0NTcUdTSWIzRFFFQk\ 

27 N3VUFNQmN4RlRBVEJnTlYKQkFNTURERXdMakUxTWk0eE9ETXVNVEFlRncweU1EQ\ 

28 TVNVEV4TkRJeU16VmFGdzB6TURBNU1Ea3hOREl5TXpWYQpNQmN4RlRBVEJnTlZC\ 

29 QU1NRERFd0xqRTFNaTR4T0RNdU1UQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQUR\ 

30 nZ0VQCkFEQ0NBUW9DZ2dFQkFNV0tyQkdxWlJRT0VONDExR2RESmY2ckZWRDcvMU\ 

31 xHNlZMWjNhd1BRdHBhRTRxdVdyNisKWjExTWwra2kwVEU1cGZFV3dKenVUZXlCU\ 

32 XVkUEpnYm1QTjF1VWROdGRiNlpocHEzeC9oT0hCMVJLNC9iSlNFUgpiZ0dITmN6\ 

33 MzR6SHRaZ1dwb2NPTXpPOW9oRUdhMTZUaDhmQWVxYU1CQTJRaklmeUFlaVp3VHJ\ 

34 nZ3BrY2dBMUlOCjBvQkdqSURnSGVoSU5tbGZOOURkQ3hNN1FNTmtSbzRXdE13bF\ 

35 JSRWZ4QnFiVkNpZGFjbVhhb1VPUjJPeFVmQWEKN1orSUU1TmN5ZFQ1TGovazdwd\ 

36 XZCVkdIa0JQWnE0TmlBa3R4aXd5NVB5R29GTk9mT0NrV2I2VnBzVzNhTlNJeAo4\ 

37 aXBITkc3enV3elc1TGQ5TkhQYWpRckZwdFZBSHpJNWNhRUNBd0VBQWFOUU1FNHd\ 

38 IUVlEVlIwT0JCWUVGQ1dVCkFaTXNaeE13L1k1OGlXMGZJWVAzcDdTYk1COEdBMV\ 

39 VkSXdRWU1CYUFGQ1dVQVpNc1p4TXcvWTU4aVcwZklZUDMKcDdTYk1Bd0dBMVVkR\ 

40 XdRRk1BTUJBZjh3RFFZSktvWklodmNOQVFFTEJRQURnZ0VCQUJaMlYxMWowRzhh\ 

41 Z1Z6Twp2YWtKTGt4UGZ0UE1NMFFOaVRzZmV6RzlicnBkdEVLSjFyalFCblNXYTN\ 

42 WbThWRGZTYkhLQUNXaGh0OEhzcXhtCmNzdVQyOWUyaGZBNHVIOUxMdy9MVG5EdE\ 

43 tJSjZ6aWFzaTM5RGh3UGwwaExuamJRMjk4VVo5TGovVlpnZGlqemIKWnVPdHlpT\ 

44 nVOS0E2Nmd0dGxXcWZRQ2hkbnJ5MlZUbjBjblR5dU9UalByYWdOdXJMdlVwL3Nl\ 

45 eURhZmsxNXJ4egozcmlYZldiQnRhUUk1dnM0ekFKU2xneUg2RnpiZStoTUhlUzF\ 

46 mM2ppb3dJV0lRR2NNbHpGT1RpMm1xWFRybEJYCnh1WmpLZlpOcndjQVNGbk9qYV\ 

47 BWeFQ1ODJ4WWhtTm8wR3J2MlZEck51bDlSYkgvK3lNS2J5NEhkOFRvVThMU2kKY\ 

48 3Uxajh3cz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= 

49 server: https://192.168.0.22:16443 

50 name: microk8s-cluster 

51contexts: 

52- context: 

53 cluster: microk8s-cluster 

54 user: admin 

55 name: microk8s 

56current-context: microk8s 

57kind: Config 

58preferences: {} 

59users: 

60- name: admin 

61 user: 

62 token: clhkRExRem5Xd1dCdnFEVXdvRGtDRGE5b1F3WnNrZk5qeHFCOU10bHBZRT0K 

63""" 

64 

65 

66async def AsyncMockFunc(): 

67 await asyncio.sleep(1) 

68 

69 

70class AsyncMock(MagicMock): 

71 async def __call__(self, *args, **kwargs): 

72 return super(AsyncMock, self).__call__(*args, **kwargs) 

73 

74 

75class FakeN2VC(MagicMock): 

76 last_written_values = None 

77 

78 async def write_app_status_to_db( 

79 self, 

80 db_dict: dict, 

81 status: N2VCDeploymentStatus, 

82 detailed_status: str, 

83 vca_status: str, 

84 entity_type: str, 

85 vca_id: str = None, 

86 ): 

87 """ 

88 Write application status to database 

89 

90 :param: db_dict: DB dictionary 

91 :param: status: Status of the application 

92 :param: detailed_status: Detailed status 

93 :param: vca_status: VCA status 

94 :param: entity_type: Entity type ("application", "machine, and "action") 

95 :param: vca_id: Id of the VCA. If None, the default VCA will be used. 

96 """ 

97 self.last_written_values = Dict( 

98 { 

99 "n2vc_status": status, 

100 "message": detailed_status, 

101 "vca_status": vca_status, 

102 "entity": entity_type, 

103 } 

104 ) 

105 

106 osm_status = N2VCConnector.osm_status 

107 

108 

109class FakeMachine(MagicMock): 

110 entity_id = "2" 

111 dns_name = "FAKE ENDPOINT" 

112 model_name = "FAKE MODEL" 

113 entity_type = "machine" 

114 safe_data = {"instance-id": "myid"} 

115 

116 async def destroy(self, force): 

117 pass 

118 

119 

120class FakeManualMachine(MagicMock): 

121 entity_id = "2" 

122 dns_name = "FAKE ENDPOINT" 

123 model_name = "FAKE MODEL" 

124 entity_type = "machine" 

125 safe_data = {"instance-id": "manual:myid"} 

126 series = "FAKE SERIES" 

127 

128 async def destroy(self, force): 

129 pass 

130 

131 

132class FakeWatcher(AsyncMock): 

133 delta_to_return = None 

134 

135 async def Next(self): 

136 return Dict({"deltas": self.delta_to_return}) 

137 

138 

139class FakeConnection(MagicMock): 

140 endpoint = None 

141 is_open = False 

142 

143 

144class FakeAction(MagicMock): 

145 entity_id = "id" 

146 status = "ready" 

147 

148 

149class FakeModel: 

150 def __init__(self, applications: dict = {}): 

151 self._applications = applications 

152 

153 @property 

154 def applications(self): 

155 return self._applications 

156 

157 

158class FakeUnit(MagicMock): 

159 async def is_leader_from_status(self): 

160 return True 

161 

162 async def run_action(self, action_name, **kwargs): 

163 return FakeAction() 

164 

165 @property 

166 def machine_id(self): 

167 return "existing_machine_id" 

168 

169 name = "existing_unit" 

170 

171 

172class FakeApplication(AsyncMock): 

173 async def set_config(self, config): 

174 pass 

175 

176 async def add_unit(self, to): 

177 pass 

178 

179 async def destroy_unit(self, unit_name): 

180 pass 

181 

182 async def get_actions(self): 

183 return ["existing_action"] 

184 

185 async def get_config(self): 

186 return ["app_config"] 

187 

188 async def scale(self, scale): 

189 pass 

190 

191 units = [FakeUnit(), FakeUnit()] 

192 

193 

194class FakeFile: 

195 def __init__(self, content: str = ""): 

196 self.content = content 

197 

198 def read(self, size: int = -1): 

199 return self.content 

200 

201 

202class FakeFileWrapper: 

203 def __init__(self, content: str = ""): 

204 self.file = FakeFile(content=content) 

205 

206 def __enter__(self): 

207 return self.file 

208 

209 def __exit__(self, type, value, traceback): 

210 pass 

211 

212 

213FAKE_DELTA_MACHINE_PENDING = Dict( 

214 { 

215 "deltas": ["machine", "change", {}], 

216 "entity": "machine", 

217 "type": "change", 

218 "data": { 

219 "id": "2", 

220 "instance-id": "juju-1b5808-2", 

221 "agent-status": {"current": "pending", "message": "", "version": ""}, 

222 "instance-status": {"current": "running", "message": "Running"}, 

223 }, 

224 } 

225) 

226FAKE_DELTA_MACHINE_STARTED = Dict( 

227 { 

228 "deltas": ["machine", "change", {}], 

229 "entity": "machine", 

230 "type": "change", 

231 "data": { 

232 "id": "2", 

233 "instance-id": "juju-1b5808-2", 

234 "agent-status": {"current": "started", "message": "", "version": ""}, 

235 "instance-status": {"current": "running", "message": "Running"}, 

236 }, 

237 } 

238) 

239 

240FAKE_DELTA_UNIT_PENDING = Dict( 

241 { 

242 "deltas": ["unit", "change", {}], 

243 "entity": "unit", 

244 "type": "change", 

245 "data": { 

246 "name": "git/0", 

247 "application": "git", 

248 "machine-id": "6", 

249 "workload-status": {"current": "waiting", "message": ""}, 

250 "agent-status": {"current": "idle", "message": ""}, 

251 }, 

252 } 

253) 

254 

255FAKE_DELTA_UNIT_STARTED = Dict( 

256 { 

257 "deltas": ["unit", "change", {}], 

258 "entity": "unit", 

259 "type": "change", 

260 "data": { 

261 "name": "git/0", 

262 "application": "git", 

263 "machine-id": "6", 

264 "workload-status": {"current": "active", "message": ""}, 

265 "agent-status": {"current": "idle", "message": ""}, 

266 }, 

267 } 

268) 

269 

270FAKE_DELTA_APPLICATION_MAINTENANCE = Dict( 

271 { 

272 "deltas": ["application", "change", {}], 

273 "entity": "application", 

274 "type": "change", 

275 "data": { 

276 "name": "git", 

277 "status": { 

278 "current": "maintenance", 

279 "message": "installing charm software", 

280 }, 

281 }, 

282 } 

283) 

284 

285FAKE_DELTA_APPLICATION_ACTIVE = Dict( 

286 { 

287 "deltas": ["application", "change", {}], 

288 "entity": "application", 

289 "type": "change", 

290 "data": {"name": "git", "status": {"current": "active", "message": "Ready!"}}, 

291 } 

292) 

293 

294FAKE_DELTA_ACTION_COMPLETED = Dict( 

295 { 

296 "deltas": ["action", "change", {}], 

297 "entity": "action", 

298 "type": "change", 

299 "data": { 

300 "model-uuid": "af19cdd4-374a-4d9f-86b1-bfed7b1b5808", 

301 "id": "1", 

302 "receiver": "git/0", 

303 "name": "add-repo", 

304 "status": "completed", 

305 "message": "", 

306 }, 

307 } 

308) 

309 

310Deltas = [ 

311 Dict( 

312 { 

313 "entity": Dict({"id": "2", "type": "machine"}), 

314 "filter": Dict({"entity_id": "2", "entity_type": "machine"}), 

315 "delta": FAKE_DELTA_MACHINE_PENDING, 

316 "entity_status": Dict( 

317 {"status": "pending", "message": "Running", "vca_status": "running"} 

318 ), 

319 "db": Dict( 

320 { 

321 "written": True, 

322 "data": Dict( 

323 { 

324 "message": "Running", 

325 "entity": "machine", 

326 "vca_status": "running", 

327 "n2vc_status": N2VCDeploymentStatus.PENDING, 

328 } 

329 ), 

330 } 

331 ), 

332 } 

333 ), 

334 Dict( 

335 { 

336 "entity": Dict({"id": "2", "type": "machine"}), 

337 "filter": Dict({"entity_id": "1", "entity_type": "machine"}), 

338 "delta": FAKE_DELTA_MACHINE_PENDING, 

339 "entity_status": Dict( 

340 {"status": "pending", "message": "Running", "vca_status": "running"} 

341 ), 

342 "db": Dict({"written": False, "data": None}), 

343 } 

344 ), 

345 Dict( 

346 { 

347 "entity": Dict({"id": "2", "type": "machine"}), 

348 "filter": Dict({"entity_id": "2", "entity_type": "machine"}), 

349 "delta": FAKE_DELTA_MACHINE_STARTED, 

350 "entity_status": Dict( 

351 {"status": "started", "message": "Running", "vca_status": "running"} 

352 ), 

353 "db": Dict( 

354 { 

355 "written": True, 

356 "data": Dict( 

357 { 

358 "message": "Running", 

359 "entity": "machine", 

360 "vca_status": "running", 

361 "n2vc_status": N2VCDeploymentStatus.COMPLETED, 

362 } 

363 ), 

364 } 

365 ), 

366 } 

367 ), 

368 Dict( 

369 { 

370 "entity": Dict({"id": "2", "type": "machine"}), 

371 "filter": Dict({"entity_id": "1", "entity_type": "machine"}), 

372 "delta": FAKE_DELTA_MACHINE_STARTED, 

373 "entity_status": Dict( 

374 {"status": "started", "message": "Running", "vca_status": "running"} 

375 ), 

376 "db": Dict({"written": False, "data": None}), 

377 } 

378 ), 

379 Dict( 

380 { 

381 "entity": Dict({"id": "git/0", "type": "unit"}), 

382 "filter": Dict({"entity_id": "git", "entity_type": "application"}), 

383 "delta": FAKE_DELTA_UNIT_PENDING, 

384 "entity_status": Dict( 

385 {"status": "waiting", "message": "", "vca_status": "waiting"} 

386 ), 

387 "db": Dict( 

388 { 

389 "written": True, 

390 "data": Dict( 

391 { 

392 "message": "", 

393 "entity": "unit", 

394 "vca_status": "waiting", 

395 "n2vc_status": N2VCDeploymentStatus.RUNNING, 

396 } 

397 ), 

398 } 

399 ), 

400 } 

401 ), 

402 Dict( 

403 { 

404 "entity": Dict({"id": "git/0", "type": "unit"}), 

405 "filter": Dict({"entity_id": "2", "entity_type": "machine"}), 

406 "delta": FAKE_DELTA_UNIT_PENDING, 

407 "entity_status": Dict( 

408 {"status": "waiting", "message": "", "vca_status": "waiting"} 

409 ), 

410 "db": Dict({"written": False, "data": None}), 

411 } 

412 ), 

413 Dict( 

414 { 

415 "entity": Dict({"id": "git/0", "type": "unit"}), 

416 "filter": Dict({"entity_id": "git", "entity_type": "application"}), 

417 "delta": FAKE_DELTA_UNIT_STARTED, 

418 "entity_status": Dict( 

419 {"status": "active", "message": "", "vca_status": "active"} 

420 ), 

421 "db": Dict( 

422 { 

423 "written": True, 

424 "data": Dict( 

425 { 

426 "message": "", 

427 "entity": "unit", 

428 "vca_status": "active", 

429 "n2vc_status": N2VCDeploymentStatus.COMPLETED, 

430 } 

431 ), 

432 } 

433 ), 

434 } 

435 ), 

436 Dict( 

437 { 

438 "entity": Dict({"id": "git/0", "type": "unit"}), 

439 "filter": Dict({"entity_id": "1", "entity_type": "action"}), 

440 "delta": FAKE_DELTA_UNIT_STARTED, 

441 "entity_status": Dict( 

442 {"status": "active", "message": "", "vca_status": "active"} 

443 ), 

444 "db": Dict({"written": False, "data": None}), 

445 } 

446 ), 

447 Dict( 

448 { 

449 "entity": Dict({"id": "git", "type": "application"}), 

450 "filter": Dict({"entity_id": "git", "entity_type": "application"}), 

451 "delta": FAKE_DELTA_APPLICATION_MAINTENANCE, 

452 "entity_status": Dict( 

453 { 

454 "status": "maintenance", 

455 "message": "installing charm software", 

456 "vca_status": "maintenance", 

457 } 

458 ), 

459 "db": Dict( 

460 { 

461 "written": True, 

462 "data": Dict( 

463 { 

464 "message": "installing charm software", 

465 "entity": "application", 

466 "vca_status": "maintenance", 

467 "n2vc_status": N2VCDeploymentStatus.RUNNING, 

468 } 

469 ), 

470 } 

471 ), 

472 } 

473 ), 

474 Dict( 

475 { 

476 "entity": Dict({"id": "git", "type": "application"}), 

477 "filter": Dict({"entity_id": "2", "entity_type": "machine"}), 

478 "delta": FAKE_DELTA_APPLICATION_MAINTENANCE, 

479 "entity_status": Dict( 

480 { 

481 "status": "maintenance", 

482 "message": "installing charm software", 

483 "vca_status": "maintenance", 

484 } 

485 ), 

486 "db": Dict({"written": False, "data": None}), 

487 } 

488 ), 

489 Dict( 

490 { 

491 "entity": Dict({"id": "git", "type": "application"}), 

492 "filter": Dict({"entity_id": "git", "entity_type": "application"}), 

493 "delta": FAKE_DELTA_APPLICATION_ACTIVE, 

494 "entity_status": Dict( 

495 {"status": "active", "message": "Ready!", "vca_status": "active"} 

496 ), 

497 "db": Dict( 

498 { 

499 "written": True, 

500 "data": Dict( 

501 { 

502 "message": "Ready!", 

503 "entity": "application", 

504 "vca_status": "active", 

505 "n2vc_status": N2VCDeploymentStatus.COMPLETED, 

506 } 

507 ), 

508 } 

509 ), 

510 } 

511 ), 

512 Dict( 

513 { 

514 "entity": Dict({"id": "git", "type": "application"}), 

515 "filter": Dict({"entity_id": "1", "entity_type": "action"}), 

516 "delta": FAKE_DELTA_APPLICATION_ACTIVE, 

517 "entity_status": Dict( 

518 {"status": "active", "message": "Ready!", "vca_status": "active"} 

519 ), 

520 "db": Dict({"written": False, "data": None}), 

521 } 

522 ), 

523 Dict( 

524 { 

525 "entity": Dict({"id": "1", "type": "action"}), 

526 "filter": Dict({"entity_id": "1", "entity_type": "action"}), 

527 "delta": FAKE_DELTA_ACTION_COMPLETED, 

528 "entity_status": Dict( 

529 { 

530 "status": "completed", 

531 "message": "completed", 

532 "vca_status": "completed", 

533 } 

534 ), 

535 "db": Dict( 

536 { 

537 "written": True, 

538 "data": Dict( 

539 { 

540 "message": "completed", 

541 "entity": "action", 

542 "vca_status": "completed", 

543 "n2vc_status": N2VCDeploymentStatus.COMPLETED, 

544 } 

545 ), 

546 } 

547 ), 

548 } 

549 ), 

550 Dict( 

551 { 

552 "entity": Dict({"id": "git", "type": "action"}), 

553 "filter": Dict({"entity_id": "1", "entity_type": "machine"}), 

554 "delta": FAKE_DELTA_ACTION_COMPLETED, 

555 "entity_status": Dict( 

556 { 

557 "status": "completed", 

558 "message": "completed", 

559 "vca_status": "completed", 

560 } 

561 ), 

562 "db": Dict({"written": False, "data": None}), 

563 } 

564 ), 

565]