|
7 | 7 | import os |
8 | 8 | import yaml |
9 | 9 |
|
| 10 | +from datetime import datetime |
10 | 11 | from kubernetes import client, config |
11 | 12 |
|
12 | 13 |
|
@@ -614,6 +615,73 @@ def test_infrastructure_roles(self): |
614 | 615 | "Origin": 2, |
615 | 616 | }) |
616 | 617 |
|
| 618 | + @timeout_decorator.timeout(TEST_TIMEOUT_SEC) |
| 619 | + def test_x_cluster_deletion(self): |
| 620 | + ''' |
| 621 | + Test deletion with configured protection |
| 622 | + ''' |
| 623 | + k8s = self.k8s |
| 624 | + cluster_label = 'application=spilo,cluster-name=acid-minimal-cluster' |
| 625 | + |
| 626 | + # configure delete protection |
| 627 | + patch_delete_annotations = { |
| 628 | + "data": { |
| 629 | + "delete_annotation_date_key": "delete-date", |
| 630 | + "delete_annotation_name_key": "delete-clustername" |
| 631 | + } |
| 632 | + } |
| 633 | + k8s.update_config(patch_delete_annotations) |
| 634 | + |
| 635 | + # this delete attempt should be omitted because of missing annotations |
| 636 | + k8s.api.custom_objects_api.delete_namespaced_custom_object( |
| 637 | + "acid.zalan.do", "v1", "default", "postgresqls", "acid-minimal-cluster") |
| 638 | + |
| 639 | + # check that pods and services are still there |
| 640 | + k8s.wait_for_running_pods(cluster_label, 2) |
| 641 | + k8s.wait_for_service(cluster_label) |
| 642 | + |
| 643 | + # recreate Postgres cluster resource |
| 644 | + k8s.create_with_kubectl("manifests/minimal-postgres-manifest.yaml") |
| 645 | + |
| 646 | + # wait a little before proceeding |
| 647 | + time.sleep(10) |
| 648 | + |
| 649 | + # add annotations to manifest |
| 650 | + deleteDate = datetime.today().strftime('%Y-%m-%d') |
| 651 | + pg_patch_delete_annotations = { |
| 652 | + "metadata": { |
| 653 | + "annotations": { |
| 654 | + "delete-date": deleteDate, |
| 655 | + "delete-clustername": "acid-minimal-cluster", |
| 656 | + } |
| 657 | + } |
| 658 | + } |
| 659 | + k8s.api.custom_objects_api.patch_namespaced_custom_object( |
| 660 | + "acid.zalan.do", "v1", "default", "postgresqls", "acid-minimal-cluster", pg_patch_delete_annotations) |
| 661 | + |
| 662 | + # wait a little before proceeding |
| 663 | + time.sleep(10) |
| 664 | + k8s.wait_for_running_pods(cluster_label, 2) |
| 665 | + k8s.wait_for_service(cluster_label) |
| 666 | + |
| 667 | + # now delete process should be triggered |
| 668 | + k8s.api.custom_objects_api.delete_namespaced_custom_object( |
| 669 | + "acid.zalan.do", "v1", "default", "postgresqls", "acid-minimal-cluster") |
| 670 | + |
| 671 | + # wait until cluster is deleted |
| 672 | + time.sleep(120) |
| 673 | + |
| 674 | + print('Operator log: {}'.format(k8s.get_operator_log())) |
| 675 | + |
| 676 | + # check if everything has been deleted |
| 677 | + self.assertEqual(0, k8s.count_pods_with_label(cluster_label)) |
| 678 | + self.assertEqual(0, k8s.count_services_with_label(cluster_label)) |
| 679 | + self.assertEqual(0, k8s.count_endpoints_with_label(cluster_label)) |
| 680 | + self.assertEqual(0, k8s.count_statefulsets_with_label(cluster_label)) |
| 681 | + self.assertEqual(0, k8s.count_deployments_with_label(cluster_label)) |
| 682 | + self.assertEqual(0, k8s.count_pdbs_with_label(cluster_label)) |
| 683 | + self.assertEqual(0, k8s.count_secrets_with_label(cluster_label)) |
| 684 | + |
617 | 685 | def get_failover_targets(self, master_node, replica_nodes): |
618 | 686 | ''' |
619 | 687 | If all pods live on the same node, failover will happen to other worker(s) |
@@ -700,11 +768,12 @@ def __init__(self): |
700 | 768 | self.apps_v1 = client.AppsV1Api() |
701 | 769 | self.batch_v1_beta1 = client.BatchV1beta1Api() |
702 | 770 | self.custom_objects_api = client.CustomObjectsApi() |
| 771 | + self.policy_v1_beta1 = client.PolicyV1beta1Api() |
703 | 772 |
|
704 | 773 |
|
705 | 774 | class K8s: |
706 | 775 | ''' |
707 | | - Wraps around K8 api client and helper methods. |
| 776 | + Wraps around K8s api client and helper methods. |
708 | 777 | ''' |
709 | 778 |
|
710 | 779 | RETRY_TIMEOUT_SEC = 10 |
@@ -824,6 +893,25 @@ def get_services(): |
824 | 893 | def count_pods_with_label(self, labels, namespace='default'): |
825 | 894 | return len(self.api.core_v1.list_namespaced_pod(namespace, label_selector=labels).items) |
826 | 895 |
|
| 896 | + def count_services_with_label(self, labels, namespace='default'): |
| 897 | + return len(self.api.core_v1.list_namespaced_service(namespace, label_selector=labels).items) |
| 898 | + |
| 899 | + def count_endpoints_with_label(self, labels, namespace='default'): |
| 900 | + return len(self.api.core_v1.list_namespaced_endpoints(namespace, label_selector=labels).items) |
| 901 | + |
| 902 | + def count_secrets_with_label(self, labels, namespace='default'): |
| 903 | + return len(self.api.core_v1.list_namespaced_secret(namespace, label_selector=labels).items) |
| 904 | + |
| 905 | + def count_statefulsets_with_label(self, labels, namespace='default'): |
| 906 | + return len(self.api.apps_v1.list_namespaced_stateful_set(namespace, label_selector=labels).items) |
| 907 | + |
| 908 | + def count_deployments_with_label(self, labels, namespace='default'): |
| 909 | + return len(self.api.apps_v1.list_namespaced_deployment(namespace, label_selector=labels).items) |
| 910 | + |
| 911 | + def count_pdbs_with_label(self, labels, namespace='default'): |
| 912 | + return len(self.api.policy_v1_beta1.list_namespaced_pod_disruption_budget( |
| 913 | + namespace, label_selector=labels).items) |
| 914 | + |
827 | 915 | def wait_for_pod_failover(self, failover_targets, labels, namespace='default'): |
828 | 916 | pod_phase = 'Failing over' |
829 | 917 | new_pod_node = '' |
|
0 commit comments