|
1 | | -import time |
2 | 1 | from concurrent.futures import ThreadPoolExecutor, wait |
3 | 2 | from typing import Callable, Optional |
4 | | -from unittest.mock import patch |
5 | 3 |
|
6 | 4 | import pytest |
7 | 5 | import requests |
@@ -349,246 +347,3 @@ def test_generate_url( |
349 | 347 | response = requests.get(url.strip("\n")) |
350 | 348 | assert response.text == content |
351 | 349 | assert response.status_code == 200 |
352 | | - |
353 | | - |
354 | | -def test_obj_action_triggers_key_cleanup_and_deletes_stale_key( |
355 | | - keys: Keys, |
356 | | - monkeypatch: MonkeyPatch, |
357 | | - create_bucket: callable, |
358 | | -): |
359 | | - patch_keys(keys, monkeypatch) |
360 | | - bucket = create_bucket() |
361 | | - |
362 | | - now = int(time.time()) |
363 | | - stale_timestamp = ( |
364 | | - now - 31 * 24 * 60 * 60 |
365 | | - ) # 31 days ago (assuming 30d lifespan) |
366 | | - fresh_timestamp = now |
367 | | - |
368 | | - stale_key = { |
369 | | - "id": "stale-id", |
370 | | - "label": f"linode-cli-testuser@localhost-{stale_timestamp}", |
371 | | - "access_key": "STALEKEY", |
372 | | - } |
373 | | - fresh_key = { |
374 | | - "id": "fresh-id", |
375 | | - "label": f"linode-cli-testuser@localhost-{fresh_timestamp}", |
376 | | - "access_key": "FRESHKEY", |
377 | | - } |
378 | | - |
379 | | - def call_operation_side_effect(resource, action, *args, **kwargs): |
380 | | - if resource == "object-storage" and action == "keys-list": |
381 | | - return 200, {"data": [stale_key, fresh_key]} |
382 | | - if resource == "object-storage" and action == "keys-delete": |
383 | | - return 200, {} |
384 | | - if resource == "object-storage" and action == "keys-create": |
385 | | - return 200, {"access_key": "NEWKEY", "secret_key": "NEWSECRET"} |
386 | | - if resource == "account" and action == "view": |
387 | | - return 200, {} |
388 | | - return 200, {} |
389 | | - |
390 | | - with patch("linodecli.plugins.obj.__init__.CLI") as MockCLI: |
391 | | - mock_client = MockCLI.return_value |
392 | | - mock_client.config.plugin_get_value.side_effect = ( |
393 | | - lambda k, d=None, t=None: { |
394 | | - "key-cleanup-enabled": True, |
395 | | - "key-lifespan": "30d", |
396 | | - "key-rotation-period": "10d", |
397 | | - "key-cleanup-batch-size": 10, |
398 | | - }[k] |
399 | | - ) |
400 | | - mock_client.call_operation.side_effect = call_operation_side_effect |
401 | | - mock_client.config.plugin_set_value.return_value = None |
402 | | - mock_client.config.write_config.return_value = None |
403 | | - |
404 | | - # Execute the ls command |
405 | | - exec_test_command(BASE_CMD + ["ls", bucket]) |
406 | | - |
407 | | - # Check that keys-delete was called for the stale key only |
408 | | - delete_calls = [ |
409 | | - c |
410 | | - for c in mock_client.call_operation.mock_calls |
411 | | - if c[1][1] == "keys-delete" |
412 | | - ] |
413 | | - assert any( |
414 | | - c[1][2][0] == "stale-id" for c in delete_calls |
415 | | - ), "Stale key was not deleted" |
416 | | - assert not any( |
417 | | - c[1][2][0] == "fresh-id" for c in delete_calls |
418 | | - ), "Fresh key should not be deleted" |
419 | | - |
420 | | - |
421 | | -def test_obj_action_triggers_key_rotation( |
422 | | - keys: Keys, |
423 | | - monkeypatch: MonkeyPatch, |
424 | | - create_bucket: callable, |
425 | | -): |
426 | | - patch_keys(keys, monkeypatch) |
427 | | - bucket = create_bucket() |
428 | | - |
429 | | - now = int(time.time()) |
430 | | - # Key created 31 days ago, rotation period is 30 days |
431 | | - old_timestamp = now - 60 * 60 * 24 * 31 |
432 | | - |
433 | | - key_due_for_rotation = { |
434 | | - "id": "rotate-id", |
435 | | - "label": f"linode-cli-testuser@localhost-{old_timestamp}", |
436 | | - "access_key": "ROTATEKEY", |
437 | | - } |
438 | | - |
439 | | - def call_operation_side_effect(resource, action, *args, **kwargs): |
440 | | - if resource == "object-storage" and action == "keys-list": |
441 | | - return 200, {"data": [key_due_for_rotation]} |
442 | | - if resource == "object-storage" and action == "keys-create": |
443 | | - return 200, {"access_key": "NEWKEY", "secret_key": "NEWSECRET"} |
444 | | - if resource == "object-storage" and action == "keys-delete": |
445 | | - return 200, {} |
446 | | - if resource == "account" and action == "view": |
447 | | - return 200, {} |
448 | | - return 200, {} |
449 | | - |
450 | | - with patch("linodecli.plugins.obj.__init__.CLI") as MockCLI: |
451 | | - mock_client = MockCLI.return_value |
452 | | - mock_client.config.plugin_get_value.side_effect = ( |
453 | | - lambda k, d=None, t=None: { |
454 | | - "key-cleanup-enabled": True, |
455 | | - "key-lifespan": "90d", |
456 | | - "key-rotation-period": "30d", |
457 | | - "key-cleanup-batch-size": 10, |
458 | | - }[k] |
459 | | - ) |
460 | | - mock_client.call_operation.side_effect = call_operation_side_effect |
461 | | - mock_client.config.plugin_set_value.return_value = None |
462 | | - mock_client.config.write_config.return_value = None |
463 | | - |
464 | | - exec_test_command(BASE_CMD + ["ls", bucket]) |
465 | | - |
466 | | - # Check that keys-create (rotation) was called |
467 | | - create_calls = [ |
468 | | - c |
469 | | - for c in mock_client.call_operation.mock_calls |
470 | | - if c[1][1] == "keys-create" |
471 | | - ] |
472 | | - assert create_calls, "Key rotation (keys-create) was not triggered" |
473 | | - |
474 | | - # Check that keys-delete was called for the old key |
475 | | - delete_calls = [ |
476 | | - c |
477 | | - for c in mock_client.call_operation.mock_calls |
478 | | - if c[1][1] == "keys-delete" |
479 | | - ] |
480 | | - assert any( |
481 | | - c[1][2][0] == "rotate-id" for c in delete_calls |
482 | | - ), "Old key was not deleted after rotation" |
483 | | - |
484 | | - |
485 | | -def test_obj_action_does_not_trigger_cleanup_if_recent( |
486 | | - keys: Keys, |
487 | | - monkeypatch: MonkeyPatch, |
488 | | - create_bucket: callable, |
489 | | -): |
490 | | - patch_keys(keys, monkeypatch) |
491 | | - bucket = create_bucket() |
492 | | - |
493 | | - now = int(time.time()) |
494 | | - # Set last cleanup to 1 hour ago (less than 24h) |
495 | | - last_cleanup = now - 60 * 60 |
496 | | - |
497 | | - stale_timestamp = now - 31 * 24 * 60 * 60 |
498 | | - stale_key = { |
499 | | - "id": "stale-id", |
500 | | - "label": f"linode-cli-testuser@localhost-{stale_timestamp}", |
501 | | - "access_key": "STALEKEY", |
502 | | - } |
503 | | - |
504 | | - def call_operation_side_effect(resource, action, *args, **kwargs): |
505 | | - if resource == "object-storage" and action == "keys-list": |
506 | | - return 200, {"data": [stale_key]} |
507 | | - if resource == "object-storage" and action == "keys-delete": |
508 | | - return 200, {} |
509 | | - if resource == "object-storage" and action == "keys-create": |
510 | | - return 200, {"access_key": "NEWKEY", "secret_key": "NEWSECRET"} |
511 | | - if resource == "account" and action == "view": |
512 | | - return 200, {} |
513 | | - return 200, {} |
514 | | - |
515 | | - with patch("linodecli.plugins.obj.__init__.CLI") as MockCLI: |
516 | | - mock_client = MockCLI.return_value |
517 | | - mock_client.config.plugin_get_value.side_effect = ( |
518 | | - lambda k, d=None, t=None: { |
519 | | - "key-cleanup-enabled": True, |
520 | | - "key-lifespan": "30d", |
521 | | - "key-rotation-period": "10d", |
522 | | - "key-cleanup-batch-size": 10, |
523 | | - "last-key-cleanup-timestamp": str(last_cleanup), |
524 | | - }[k] |
525 | | - ) |
526 | | - mock_client.call_operation.side_effect = call_operation_side_effect |
527 | | - mock_client.config.plugin_set_value.return_value = None |
528 | | - mock_client.config.write_config.return_value = None |
529 | | - |
530 | | - exec_test_command(BASE_CMD + ["ls", bucket]) |
531 | | - |
532 | | - # Check that keys-delete was NOT called |
533 | | - delete_calls = [ |
534 | | - c |
535 | | - for c in mock_client.call_operation.mock_calls |
536 | | - if c[1][1] == "keys-delete" |
537 | | - ] |
538 | | - assert ( |
539 | | - not delete_calls |
540 | | - ), "Cleanup should not be performed if it was done in the last 24 hours" |
541 | | - |
542 | | - |
543 | | -def test_obj_action_does_not_trigger_cleanup_if_disabled( |
544 | | - keys: Keys, |
545 | | - monkeypatch: MonkeyPatch, |
546 | | - create_bucket: callable, |
547 | | -): |
548 | | - patch_keys(keys, monkeypatch) |
549 | | - bucket = create_bucket() |
550 | | - |
551 | | - now = int(time.time()) |
552 | | - stale_timestamp = now - 31 * 24 * 60 * 60 |
553 | | - stale_key = { |
554 | | - "id": "stale-id", |
555 | | - "label": f"linode-cli-testuser@localhost-{stale_timestamp}", |
556 | | - "access_key": "STALEKEY", |
557 | | - } |
558 | | - |
559 | | - def call_operation_side_effect(resource, action, *args, **kwargs): |
560 | | - if resource == "object-storage" and action == "keys-list": |
561 | | - return 200, {"data": [stale_key]} |
562 | | - if resource == "object-storage" and action == "keys-delete": |
563 | | - return 200, {} |
564 | | - if resource == "object-storage" and action == "keys-create": |
565 | | - return 200, {"access_key": "NEWKEY", "secret_key": "NEWSECRET"} |
566 | | - if resource == "account" and action == "view": |
567 | | - return 200, {} |
568 | | - return 200, {} |
569 | | - |
570 | | - with patch("linodecli.plugins.obj.__init__.CLI") as MockCLI: |
571 | | - mock_client = MockCLI.return_value |
572 | | - mock_client.config.plugin_get_value.side_effect = ( |
573 | | - lambda k, d=None, t=None: { |
574 | | - "key-cleanup-enabled": False, # Cleanup disabled |
575 | | - "key-lifespan": "30d", |
576 | | - "key-rotation-period": "10d", |
577 | | - "key-cleanup-batch-size": 10, |
578 | | - }[k] |
579 | | - ) |
580 | | - mock_client.config.plugin_set_value.return_value = None |
581 | | - mock_client.config.write_config.return_value = None |
582 | | - mock_client.call_operation.side_effect = call_operation_side_effect |
583 | | - |
584 | | - exec_test_command(BASE_CMD + ["ls", bucket]) |
585 | | - |
586 | | - # Check that keys-delete was NOT called |
587 | | - delete_calls = [ |
588 | | - c |
589 | | - for c in mock_client.call_operation.mock_calls |
590 | | - if c[1][1] == "keys-delete" |
591 | | - ] |
592 | | - assert ( |
593 | | - not delete_calls |
594 | | - ), "Cleanup should not be performed when key-cleanup-enabled is False" |
0 commit comments