From c8aab856c74510a5d20f8950780fc608cae225d8 Mon Sep 17 00:00:00 2001 From: Elena Gershkovich Date: Wed, 15 Feb 2023 11:13:04 +0200 Subject: [PATCH 01/12] Add new decorator for replication lock (#654) * Add new decorator for replication lock Signed-off-by: Elena Gershkovich * Adjustments for decorators Signed-off-by: Elena Gershkovich * Adjustments for decorators Signed-off-by: Elena Gershkovich * Adjustments for decorators Signed-off-by: Elena Gershkovich --------- Signed-off-by: Elena Gershkovich --- controllers/servers/csi/addons_server.py | 12 ++--- controllers/servers/csi/decorators.py | 44 ++++++++++++++----- controllers/servers/settings.py | 1 + .../controller_server/addons_server_test.py | 8 ++-- 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/controllers/servers/csi/addons_server.py b/controllers/servers/csi/addons_server.py index c8db8200d..55211b2bb 100644 --- a/controllers/servers/csi/addons_server.py +++ b/controllers/servers/csi/addons_server.py @@ -8,7 +8,7 @@ from controllers.array_action.storage_agent import get_agent from controllers.common.csi_logger import get_stdout_logger from controllers.servers import utils -from controllers.servers.csi.decorators import csi_method +from controllers.servers.csi.decorators import csi_replication_method from controllers.servers.csi.exception_handler import build_error_response logger = get_stdout_logger() @@ -16,7 +16,7 @@ class ReplicationControllerServicer(pb2_grpc.ControllerServicer): - @csi_method(error_response_type=pb2.EnableVolumeReplicationResponse, lock_request_attribute="volume_id") + @csi_replication_method(error_response_type=pb2.EnableVolumeReplicationResponse) def EnableVolumeReplication(self, request, context): replication_type = utils.get_addons_replication_type(request) utils.validate_addons_request(request, replication_type) @@ -55,7 +55,7 @@ def EnableVolumeReplication(self, request, context): return pb2.EnableVolumeReplicationResponse() - @csi_method(error_response_type=pb2.DisableVolumeReplicationResponse, lock_request_attribute="volume_id") + @csi_replication_method(error_response_type=pb2.DisableVolumeReplicationResponse) def DisableVolumeReplication(self, request, context): replication_type = utils.get_addons_replication_type(request) utils.validate_addons_request(request, replication_type) @@ -130,15 +130,15 @@ def _ensure_volume_role(self, request, context, is_to_promote, response_type): logger.info("finished {}".format(method_name)) return response_type() - @csi_method(error_response_type=pb2.PromoteVolumeResponse, lock_request_attribute="volume_id") + @csi_replication_method(error_response_type=pb2.PromoteVolumeResponse) def PromoteVolume(self, request, context): return self._ensure_volume_role(request, context, is_to_promote=True, response_type=pb2.PromoteVolumeResponse) - @csi_method(error_response_type=pb2.DemoteVolumeResponse, lock_request_attribute="volume_id") + @csi_replication_method(error_response_type=pb2.DemoteVolumeResponse) def DemoteVolume(self, request, context): return self._ensure_volume_role(request, context, is_to_promote=False, response_type=pb2.DemoteVolumeResponse) - @csi_method(error_response_type=pb2.ResyncVolumeResponse, lock_request_attribute="volume_id") + @csi_replication_method(error_response_type=pb2.ResyncVolumeResponse) def ResyncVolume(self, request, context): replication_type = utils.get_addons_replication_type(request) utils.validate_addons_request(request, replication_type) diff --git a/controllers/servers/csi/decorators.py b/controllers/servers/csi/decorators.py index 4c3151161..819416dce 100644 --- a/controllers/servers/csi/decorators.py +++ b/controllers/servers/csi/decorators.py @@ -4,6 +4,7 @@ from controllers.common.csi_logger import get_stdout_logger from controllers.common.utils import set_current_thread_name from controllers.servers.errors import ObjectAlreadyProcessingError +from controllers.servers.settings import VOLUME_TYPE_NAME, VOLUME_GROUP_TYPE_NAME, LOCK_REPLICATION_REQUEST_ATTR from controllers.servers.csi.exception_handler import handle_exception, handle_common_exceptions from controllers.servers.csi.sync_lock import SyncLock @@ -14,15 +15,38 @@ def csi_method(error_response_type, lock_request_attribute=''): @decorator def call_csi_method(controller_method, servicer, request, context): lock_id = getattr(request, lock_request_attribute, None) - set_current_thread_name(lock_id) - controller_method_name = controller_method.__name__ - logger.info(controller_method_name) - try: - with SyncLock(lock_request_attribute, lock_id, controller_method_name): - response = handle_common_exceptions(controller_method, servicer, request, context, error_response_type) - except ObjectAlreadyProcessingError as ex: - return handle_exception(ex, context, grpc.StatusCode.ABORTED, error_response_type) - logger.info("finished {}".format(controller_method_name)) - return response + return _set_sync_lock(lock_id, lock_request_attribute, error_response_type, + controller_method, servicer, request, context) return call_csi_method + + +def csi_replication_method(error_response_type): + @decorator + def call_csi_method(controller_method, servicer, request, context): + replication_id = getattr(request, LOCK_REPLICATION_REQUEST_ATTR, None) + if replication_id: + if replication_id.HasField(VOLUME_GROUP_TYPE_NAME): + lock_id = replication_id.volumegroup.volume_group_id + elif replication_id.HasField(VOLUME_TYPE_NAME): + lock_id = replication_id.volume.volume_id + else: + lock_id = None + return _set_sync_lock(lock_id, LOCK_REPLICATION_REQUEST_ATTR, error_response_type, + controller_method, servicer, request, context) + + return call_csi_method + + +def _set_sync_lock(lock_id, lock_request_attribute, error_response_type, + controller_method, servicer, request, context): + set_current_thread_name(lock_id) + controller_method_name = controller_method.__name__ + logger.info(controller_method_name) + try: + with SyncLock(lock_request_attribute, lock_id, controller_method_name): + response = handle_common_exceptions(controller_method, servicer, request, context, error_response_type) + except ObjectAlreadyProcessingError as ex: + return handle_exception(ex, context, grpc.StatusCode.ABORTED, error_response_type) + logger.info("finished {}".format(controller_method_name)) + return response diff --git a/controllers/servers/settings.py b/controllers/servers/settings.py index 6dd4c7f28..77e056432 100644 --- a/controllers/servers/settings.py +++ b/controllers/servers/settings.py @@ -53,6 +53,7 @@ PARAMETERS_ARRAY_ADDRESSES_DELIMITER = "," REQUEST_ACCESSIBILITY_REQUIREMENTS_FIELD = "accessibility_requirements" +LOCK_REPLICATION_REQUEST_ATTR = "replication_source" SNAPSHOT_TYPE_NAME = "snapshot" VOLUME_TYPE_NAME = "volume" diff --git a/controllers/tests/controller_server/addons_server_test.py b/controllers/tests/controller_server/addons_server_test.py index 5db76d15f..7a323ce1b 100644 --- a/controllers/tests/controller_server/addons_server_test.py +++ b/controllers/tests/controller_server/addons_server_test.py @@ -136,7 +136,7 @@ def test_enable_ear_replication_idempotency_succeeds(self): grpc_status=grpc.StatusCode.OK) def test_enable_replication_already_processing(self): - self._test_request_already_processing("volume_id", self.request.volume_id) + self._test_request_already_processing("replication_source", self.request.volume_id) def test_enable_replication_with_wrong_secrets(self): self._test_request_with_wrong_secrets() @@ -192,7 +192,7 @@ def test_disable_replication_idempotency_succeeds(self): self._test_disable_replication_idempotency_succeeds(REPLICATION_TYPE_MIRROR) def test_disable_replication_already_processing(self): - self._test_request_already_processing("volume_id", self.request.volume_id) + self._test_request_already_processing("replication_source", self.request.volume_id) def test_disable_replication_with_wrong_secrets(self): self._test_request_with_wrong_secrets() @@ -246,7 +246,7 @@ def test_promote_replication_fails(self): self._test_promote_replication_fails(REPLICATION_TYPE_MIRROR) def test_promote_replication_already_processing(self): - self._test_request_already_processing("volume_id", self.request.volume_id) + self._test_request_already_processing("replication_source", self.request.volume_id) def test_promote_replication_with_wrong_secrets(self): self._test_request_with_wrong_secrets() @@ -304,7 +304,7 @@ def test_demote_replication_idempotency_succeeds(self): self.mediator.demote_replication_volume.assert_not_called() def test_demote_replication_already_processing(self): - self._test_request_already_processing("volume_id", self.request.volume_id) + self._test_request_already_processing("replication_source", self.request.volume_id) def test_demote_replication_with_wrong_secrets(self): self._test_request_with_wrong_secrets() From 59a3f8701815c0b1754e09f1614ddd3062059a2d Mon Sep 17 00:00:00 2001 From: Elena Gershkovich Date: Wed, 15 Feb 2023 11:32:17 +0200 Subject: [PATCH 02/12] Add examples for vr and vrc in EAR environment (#652) * Add examples for vr and vrc in EAR environment Signed-off-by: Elena Gershkovich * Add examples for vr and vrc in EAR environment Signed-off-by: Elena Gershkovich --------- Signed-off-by: Elena Gershkovich --- .../examples/demo-policy-based-volumereplication.yaml | 11 +++++++++++ .../demo-policy-based-volumereplicationclass.yaml | 11 +++++++++++ 2 files changed, 22 insertions(+) create mode 100644 deploy/kubernetes/examples/demo-policy-based-volumereplication.yaml create mode 100644 deploy/kubernetes/examples/demo-policy-based-volumereplicationclass.yaml diff --git a/deploy/kubernetes/examples/demo-policy-based-volumereplication.yaml b/deploy/kubernetes/examples/demo-policy-based-volumereplication.yaml new file mode 100644 index 000000000..004380614 --- /dev/null +++ b/deploy/kubernetes/examples/demo-policy-based-volumereplication.yaml @@ -0,0 +1,11 @@ +apiVersion: replication.storage.openshift.io/v1alpha1 +kind: VolumeReplication +metadata: + name: demo-volumereplication + namespace: default +spec: + volumeReplicationClass: demo-volumereplicationclass + replicationState: primary # Required. Values primary/secondary. + dataSource: + kind: VolumeGroup + name: demo-volumegroup # Ensure that this is in the same namespace as VolumeReplication. diff --git a/deploy/kubernetes/examples/demo-policy-based-volumereplicationclass.yaml b/deploy/kubernetes/examples/demo-policy-based-volumereplicationclass.yaml new file mode 100644 index 000000000..aa106b162 --- /dev/null +++ b/deploy/kubernetes/examples/demo-policy-based-volumereplicationclass.yaml @@ -0,0 +1,11 @@ +apiVersion: replication.storage.openshift.io/v1alpha1 +kind: VolumeReplicationClass +metadata: + name: demo-volumereplicationclass +spec: + provisioner: block.csi.ibm.com + parameters: + replication_policy: demo-replication-policy-name # Ensure that this is a name of existing replication policy + + replication.storage.openshift.io/replication-secret-name: demo-secret + replication.storage.openshift.io/replication-secret-namespace: default From 2a4140f405f541de59df3e5245c3c24e443015e3 Mon Sep 17 00:00:00 2001 From: Matan Carmeli <45543087+matancarmeli7@users.noreply.github.com> Date: Tue, 21 Mar 2023 11:10:08 +0200 Subject: [PATCH 03/12] Create .github/dependabot.yml --- .github/dependabot.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..4712878ee --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,28 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" + rebase-strategy: "disabled" + reviewers: + - "csi-reviewers" + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + rebase-strategy: "disabled" + reviewers: + - "csi-reviewers" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + rebase-strategy: "disabled" + reviewers: + - "csi-reviewers" From c8d0f8a268453f4b3885aa1288263071d1c34ad5 Mon Sep 17 00:00:00 2001 From: Matan Carmeli <45543087+matancarmeli7@users.noreply.github.com> Date: Tue, 21 Mar 2023 11:13:13 +0200 Subject: [PATCH 04/12] Update dependabot.yml --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4712878ee..c91009ca0 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,6 +12,8 @@ updates: rebase-strategy: "disabled" reviewers: - "csi-reviewers" + labels: + - go - package-ecosystem: "pip" directory: "/" schedule: @@ -19,6 +21,8 @@ updates: rebase-strategy: "disabled" reviewers: - "csi-reviewers" + labels: + - python - package-ecosystem: "github-actions" directory: "/" schedule: @@ -26,3 +30,5 @@ updates: rebase-strategy: "disabled" reviewers: - "csi-reviewers" + labels: + - testing From 8c6d7957da785046739b3afa8fa3316d0be64c5e Mon Sep 17 00:00:00 2001 From: Matan Carmeli <45543087+matancarmeli7@users.noreply.github.com> Date: Tue, 28 Mar 2023 11:21:52 +0300 Subject: [PATCH 05/12] Update dependabot.yml (#666) --- .github/dependabot.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c91009ca0..df91a4bfd 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -11,24 +11,27 @@ updates: interval: "weekly" rebase-strategy: "disabled" reviewers: - - "csi-reviewers" + - "IBM/csi-reviewers" labels: - go + - dependencies - package-ecosystem: "pip" directory: "/" schedule: interval: "weekly" rebase-strategy: "disabled" reviewers: - - "csi-reviewers" + - "IBM/csi-reviewers" labels: - python + - dependencies - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" rebase-strategy: "disabled" reviewers: - - "csi-reviewers" + - "IBM/csi-reviewers" labels: - - testing + - github_actions + - dependencies From 2846368b20b60409fd6dcf9c37b3012c87ec9525 Mon Sep 17 00:00:00 2001 From: matancarmeli7 Date: Thu, 16 Feb 2023 17:08:58 +0200 Subject: [PATCH 06/12] refactor hostDefiner classes Signed-off-by: matancarmeli7 --- controllers/common/settings.py | 2 + .../__init__.py | 0 .../definition_manager/definition.py | 125 +++++ .../definition_manager/request.py | 58 +++ controllers/servers/host_definer/globals.py | 2 + .../servers/host_definer/k8s/__init__.py | 0 controllers/servers/host_definer/k8s/api.py | 192 +++++++ .../kubernetes_manager/manager.py | 368 -------------- .../host_definer/resource_manager/__init__.py | 0 .../host_definer/resource_manager/csi_node.py | 51 ++ .../resource_manager/daemon_set.py | 36 ++ .../host_definer/resource_manager/event.py | 25 + .../resource_manager/host_definition.py | 169 ++++++ .../host_definer/resource_manager/node.py | 151 ++++++ .../resource_manager/resource_info.py | 108 ++++ .../host_definer/resource_manager/secret.py | 127 +++++ .../resource_manager/storage_class.py | 9 + controllers/servers/host_definer/types.py | 2 +- controllers/servers/host_definer/utils.py | 13 - .../servers/host_definer/utils/__init__.py | 0 .../host_definer/utils/manifest_utils.py | 58 +++ .../servers/host_definer/utils/utils.py | 154 ++++++ .../host_definer/watcher/csi_node_watcher.py | 96 ++-- .../watcher/host_definition_watcher.py | 96 +--- .../host_definer/watcher/node_watcher.py | 109 +--- .../host_definer/watcher/secret_watcher.py | 59 +-- .../watcher/storage_class_watcher.py | 97 +--- .../host_definer/watcher/watcher_helper.py | 450 +--------------- .../controller_server/host_definer/common.py | 20 - .../host_definer/csi_node_watcher_test.py | 232 --------- .../definition_manager/__init__.py | 0 .../definition_manager_test.py | 269 ++++++++++ .../request_manager_test.py | 66 +++ .../host_definer_utils_tests/__init__.py | 0 .../manifest_utils_test.py | 55 ++ .../host_definer_utils_tests/utils_test.py | 244 +++++++++ .../host_definition_watcher_test.py | 88 ---- .../host_definer/k8s/__init__.py | 0 .../host_definer/k8s/kubernetes_api_test.py | 257 ++++++++++ .../host_definer/node_watcher_test.py | 109 ---- .../host_definer/resource_manager/__init__.py | 0 .../resource_manager/base_resource_manager.py | 22 + .../resource_manager/csi_node_manager_test.py | 81 +++ .../daemon_set_manager_test.py | 44 ++ .../resource_manager/event_manager_test.py | 29 ++ .../host_definition_manager_test.py | 339 +++++++++++++ .../resource_manager/node_manager_test.py | 480 ++++++++++++++++++ .../resource_info_manager_test.py | 170 +++++++ .../resource_manager/secret_manager_test.py | 315 ++++++++++++ .../storage_class_manager_test.py | 23 + .../host_definer/secret_watcher_test.py | 60 --- .../host_definer/settings.py | 61 ++- .../storage_class_watcher_test.py | 94 ---- .../host_definer/utils/k8s_manifests_utils.py | 76 ++- .../host_definer/utils/test_utils.py | 230 ++++++--- .../host_definer/watchers/__init__.py | 0 .../watchers/csi_node_watcher_test.py | 342 +++++++++++++ .../watchers/host_definition_watcher_test.py | 200 ++++++++ .../watchers/node_watcher_test.py | 212 ++++++++ .../watchers/secret_watcher_test.py | 134 +++++ .../watchers/storage_class_watcher_test.py | 244 +++++++++ .../host_definer/watchers/watcher_base.py | 8 + 62 files changed, 5234 insertions(+), 1827 deletions(-) rename controllers/servers/host_definer/{kubernetes_manager => definition_manager}/__init__.py (100%) create mode 100644 controllers/servers/host_definer/definition_manager/definition.py create mode 100644 controllers/servers/host_definer/definition_manager/request.py create mode 100644 controllers/servers/host_definer/globals.py create mode 100644 controllers/servers/host_definer/k8s/__init__.py create mode 100644 controllers/servers/host_definer/k8s/api.py delete mode 100644 controllers/servers/host_definer/kubernetes_manager/manager.py create mode 100644 controllers/servers/host_definer/resource_manager/__init__.py create mode 100644 controllers/servers/host_definer/resource_manager/csi_node.py create mode 100644 controllers/servers/host_definer/resource_manager/daemon_set.py create mode 100644 controllers/servers/host_definer/resource_manager/event.py create mode 100644 controllers/servers/host_definer/resource_manager/host_definition.py create mode 100644 controllers/servers/host_definer/resource_manager/node.py create mode 100644 controllers/servers/host_definer/resource_manager/resource_info.py create mode 100644 controllers/servers/host_definer/resource_manager/secret.py create mode 100644 controllers/servers/host_definer/resource_manager/storage_class.py delete mode 100644 controllers/servers/host_definer/utils.py create mode 100644 controllers/servers/host_definer/utils/__init__.py create mode 100644 controllers/servers/host_definer/utils/manifest_utils.py create mode 100644 controllers/servers/host_definer/utils/utils.py delete mode 100644 controllers/tests/controller_server/host_definer/common.py delete mode 100644 controllers/tests/controller_server/host_definer/csi_node_watcher_test.py create mode 100644 controllers/tests/controller_server/host_definer/definition_manager/__init__.py create mode 100644 controllers/tests/controller_server/host_definer/definition_manager/definition_manager_test.py create mode 100644 controllers/tests/controller_server/host_definer/definition_manager/request_manager_test.py create mode 100644 controllers/tests/controller_server/host_definer/host_definer_utils_tests/__init__.py create mode 100644 controllers/tests/controller_server/host_definer/host_definer_utils_tests/manifest_utils_test.py create mode 100644 controllers/tests/controller_server/host_definer/host_definer_utils_tests/utils_test.py delete mode 100644 controllers/tests/controller_server/host_definer/host_definition_watcher_test.py create mode 100644 controllers/tests/controller_server/host_definer/k8s/__init__.py create mode 100644 controllers/tests/controller_server/host_definer/k8s/kubernetes_api_test.py delete mode 100644 controllers/tests/controller_server/host_definer/node_watcher_test.py create mode 100644 controllers/tests/controller_server/host_definer/resource_manager/__init__.py create mode 100644 controllers/tests/controller_server/host_definer/resource_manager/base_resource_manager.py create mode 100644 controllers/tests/controller_server/host_definer/resource_manager/csi_node_manager_test.py create mode 100644 controllers/tests/controller_server/host_definer/resource_manager/daemon_set_manager_test.py create mode 100644 controllers/tests/controller_server/host_definer/resource_manager/event_manager_test.py create mode 100644 controllers/tests/controller_server/host_definer/resource_manager/host_definition_manager_test.py create mode 100644 controllers/tests/controller_server/host_definer/resource_manager/node_manager_test.py create mode 100644 controllers/tests/controller_server/host_definer/resource_manager/resource_info_manager_test.py create mode 100644 controllers/tests/controller_server/host_definer/resource_manager/secret_manager_test.py create mode 100644 controllers/tests/controller_server/host_definer/resource_manager/storage_class_manager_test.py delete mode 100644 controllers/tests/controller_server/host_definer/secret_watcher_test.py delete mode 100644 controllers/tests/controller_server/host_definer/storage_class_watcher_test.py create mode 100644 controllers/tests/controller_server/host_definer/watchers/__init__.py create mode 100644 controllers/tests/controller_server/host_definer/watchers/csi_node_watcher_test.py create mode 100644 controllers/tests/controller_server/host_definer/watchers/host_definition_watcher_test.py create mode 100644 controllers/tests/controller_server/host_definer/watchers/node_watcher_test.py create mode 100644 controllers/tests/controller_server/host_definer/watchers/secret_watcher_test.py create mode 100644 controllers/tests/controller_server/host_definer/watchers/storage_class_watcher_test.py create mode 100644 controllers/tests/controller_server/host_definer/watchers/watcher_base.py diff --git a/controllers/common/settings.py b/controllers/common/settings.py index 686dbfbae..2bd7f95b1 100644 --- a/controllers/common/settings.py +++ b/controllers/common/settings.py @@ -28,6 +28,8 @@ NAMESPACE_FIELD = 'namespace' IO_GROUP_DELIMITER = ':' IO_GROUP_LABEL_PREFIX = 'hostdefiner.block.csi.ibm.com/io-group-' +RESOURCE_VERSION_FIELD = 'resource_version' +ITEMS_FIELD = 'items' EAR_VOLUME_FC_MAP_COUNT = "2" diff --git a/controllers/servers/host_definer/kubernetes_manager/__init__.py b/controllers/servers/host_definer/definition_manager/__init__.py similarity index 100% rename from controllers/servers/host_definer/kubernetes_manager/__init__.py rename to controllers/servers/host_definer/definition_manager/__init__.py diff --git a/controllers/servers/host_definer/definition_manager/definition.py b/controllers/servers/host_definer/definition_manager/definition.py new file mode 100644 index 000000000..bee0160c3 --- /dev/null +++ b/controllers/servers/host_definer/definition_manager/definition.py @@ -0,0 +1,125 @@ +from controllers.common.csi_logger import get_stdout_logger +from controllers.servers.host_definer.globals import NODES, MANAGED_SECRETS +import controllers.servers.host_definer.messages as messages +from controllers.servers.host_definer.utils import manifest_utils +from controllers.servers.host_definer.types import DefineHostResponse +from controllers.servers.host_definer.k8s.api import K8SApi +from controllers.servers.host_definer.resource_manager.secret import SecretManager +from controllers.servers.host_definer.resource_manager.host_definition import HostDefinitionManager +from controllers.servers.host_definer.definition_manager.request import RequestManager +from controllers.servers.host_definer.storage_manager.host_definer_server import HostDefinerServicer + +logger = get_stdout_logger() + + +class DefinitionManager: + def __init__(self): + self.k8s_api = K8SApi() + self.secret_manager = SecretManager() + self.request_manager = RequestManager() + self.host_definition_manager = HostDefinitionManager() + self.storage_host_servicer = HostDefinerServicer() + + def define_node_on_all_storages(self, node_name): + logger.info(messages.DEFINE_NODE_ON_ALL_MANAGED_SECRETS.format(node_name)) + for secret_info in MANAGED_SECRETS: + if secret_info.managed_storage_classes == 0: + continue + host_definition_info = self.host_definition_manager.get_host_definition_info_from_secret_and_node_name( + node_name, secret_info) + self.create_definition(host_definition_info) + + def delete_definition(self, host_definition_info): + response = DefineHostResponse() + if self.secret_manager.is_node_should_be_managed_on_secret( + host_definition_info.node_name, host_definition_info.secret_name, + host_definition_info.secret_namespace): + response = self.undefine_host(host_definition_info) + self.host_definition_manager.handle_k8s_host_definition_after_undefine_action(host_definition_info, + response) + + def undefine_node_definitions(self, node_name): + for secret_info in MANAGED_SECRETS: + host_definition_info = self.host_definition_manager.get_host_definition_info_from_secret_and_node_name( + node_name, secret_info) + self.delete_definition(host_definition_info) + + def undefine_host_after_pending(self, host_definition_info): + response = DefineHostResponse() + if self.secret_manager.is_node_should_be_managed_on_secret( + host_definition_info.node_name, host_definition_info.secret_name, + host_definition_info.secret_namespace): + response = self.undefine_host(host_definition_info) + return response + + def undefine_host(self, host_definition_info): + logger.info(messages.UNDEFINED_HOST.format(host_definition_info.node_name, + host_definition_info.secret_name, host_definition_info.secret_namespace)) + return self._ensure_definition_state(host_definition_info, self.storage_host_servicer.undefine_host) + + def define_host_after_pending(self, host_definition_info): + response = DefineHostResponse() + if self.secret_manager.is_node_should_be_managed_on_secret( + host_definition_info.node_name, host_definition_info.secret_name, + host_definition_info.secret_namespace): + response = self.define_host(host_definition_info) + self._update_host_definition_from_storage_response(host_definition_info.name, response) + else: + self.host_definition_manager.delete_host_definition(host_definition_info.name) + return response + + def _update_host_definition_from_storage_response(self, host_definition_name, response): + logger.info(messages.UPDATE_HOST_DEFINITION_FIELDS_FROM_STORAGE.format(host_definition_name, response)) + host_definition_manifest = manifest_utils.generate_host_definition_response_fields_manifest( + host_definition_name, response) + self.k8s_api.patch_host_definition(host_definition_manifest) + + def define_nodes_when_new_secret(self, secret_info): + managed_secret_info, index = self.secret_manager.get_matching_managed_secret_info(secret_info) + secret_info.managed_storage_classes = 1 + if index == -1: + MANAGED_SECRETS.append(secret_info) + self._define_nodes_from_secret_info(secret_info) + elif managed_secret_info.managed_storage_classes == 0: + MANAGED_SECRETS[index] = secret_info + self._define_nodes_from_secret_info(secret_info) + else: + secret_info.managed_storage_classes = managed_secret_info.managed_storage_classes + 1 + MANAGED_SECRETS[index] = secret_info + + def _define_nodes_from_secret_info(self, secret_info): + logger.info(messages.NEW_MANAGED_SECRET.format(secret_info.name, secret_info.namespace)) + host_definition_info = self.host_definition_manager.get_host_definition_info_from_secret(secret_info) + self.define_nodes(host_definition_info) + + def define_nodes(self, host_definition_info): + for node_name, _ in NODES.items(): + host_definition_info = self.host_definition_manager.add_name_to_host_definition_info( + node_name, host_definition_info) + self.create_definition(host_definition_info) + + def create_definition(self, host_definition_info): + if not self.secret_manager.is_node_should_be_managed_on_secret( + host_definition_info.node_name, host_definition_info.secret_name, + host_definition_info.secret_namespace): + return + host_definition_info = self.host_definition_manager.update_host_definition_info(host_definition_info) + response = self.define_host(host_definition_info) + current_host_definition_info_on_cluster = self.host_definition_manager.create_host_definition_if_not_exist( + host_definition_info, response) + self.host_definition_manager.set_status_to_host_definition_after_definition( + response.error_message, current_host_definition_info_on_cluster) + + def define_host(self, host_definition_info): + logger.info(messages.DEFINE_NODE_ON_SECRET.format(host_definition_info.node_name, + host_definition_info.secret_name, host_definition_info.secret_namespace)) + return self._ensure_definition_state(host_definition_info, self.storage_host_servicer.define_host) + + def _ensure_definition_state(self, host_definition_info, define_function): + request = self.request_manager.generate_request(host_definition_info) + if not request: + response = DefineHostResponse() + response.error_message = messages.FAILED_TO_GET_SECRET_EVENT.format( + host_definition_info.secret_name, host_definition_info.secret_namespace) + return response + return define_function(request) diff --git a/controllers/servers/host_definer/definition_manager/request.py b/controllers/servers/host_definer/definition_manager/request.py new file mode 100644 index 000000000..331956d6a --- /dev/null +++ b/controllers/servers/host_definer/definition_manager/request.py @@ -0,0 +1,58 @@ +from controllers.common.csi_logger import get_stdout_logger +from controllers.servers.host_definer.globals import NODES +from controllers.servers.host_definer import settings +from controllers.servers.host_definer.utils import utils +import controllers.servers.host_definer.messages as messages +from controllers.servers.host_definer.types import DefineHostRequest +from controllers.servers.host_definer.resource_manager.secret import SecretManager +from controllers.servers.host_definer.resource_manager.resource_info import ResourceInfoManager + +logger = get_stdout_logger() + + +class RequestManager: + def __init__(self): + self.secret_manager = SecretManager() + self.resource_info_manager = ResourceInfoManager() + + def generate_request(self, host_definition_info): + node_name = host_definition_info.node_name + logger.info(messages.GENERATE_REQUEST_FOR_NODE.format(node_name)) + node_info = self.resource_info_manager.get_node_info(node_name) + request = self._get_new_request(node_info.labels) + request = self._add_array_connectivity_info_to_request( + request, host_definition_info.secret_name, host_definition_info.secret_namespace, node_info.labels) + if request: + request.node_id_from_host_definition = host_definition_info.node_id + request.node_id_from_csi_node = self._get_node_id_by_node(host_definition_info) + request.io_group = self._get_io_group_by_node(host_definition_info.node_name) + return request + + def _get_new_request(self, labels): + request = DefineHostRequest() + connectivity_type_label_on_node = self._get_label_value(labels, settings.CONNECTIVITY_TYPE_LABEL) + request.prefix = utils.get_prefix() + request.connectivity_type_from_user = utils.get_connectivity_type_from_user(connectivity_type_label_on_node) + return request + + def _get_label_value(self, labels, label): + return labels.get(label) + + def _add_array_connectivity_info_to_request(self, request, secret_name, secret_namespace, labels): + request.array_connection_info = self.secret_manager.get_array_connection_info( + secret_name, secret_namespace, labels) + if request.array_connection_info: + return request + return None + + def _get_node_id_by_node(self, host_definition_info): + try: + return NODES[host_definition_info.node_name].node_id + except Exception: + return host_definition_info.node_id + + def _get_io_group_by_node(self, node_name): + try: + return NODES[node_name].io_group + except Exception: + return '' diff --git a/controllers/servers/host_definer/globals.py b/controllers/servers/host_definer/globals.py new file mode 100644 index 000000000..c65bf409b --- /dev/null +++ b/controllers/servers/host_definer/globals.py @@ -0,0 +1,2 @@ +MANAGED_SECRETS = [] +NODES = {} diff --git a/controllers/servers/host_definer/k8s/__init__.py b/controllers/servers/host_definer/k8s/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/controllers/servers/host_definer/k8s/api.py b/controllers/servers/host_definer/k8s/api.py new file mode 100644 index 000000000..1c9fb22c0 --- /dev/null +++ b/controllers/servers/host_definer/k8s/api.py @@ -0,0 +1,192 @@ +from kubernetes import client, config, dynamic, watch +from kubernetes.client import api_client +from kubernetes.client.rest import ApiException +from munch import Munch + +from controllers.common.csi_logger import get_stdout_logger +from controllers.servers.host_definer import settings +from controllers.servers.host_definer.utils import utils +import controllers.common.settings as common_settings +import controllers.servers.host_definer.messages as messages + +logger = get_stdout_logger() + + +class K8SApi(): + def __init__(self): + self._load_cluster_configuration() + self.dynamic_client = self._get_dynamic_client() + self.storage_api = client.StorageV1Api() + self.core_api = client.CoreV1Api() + self.custom_object_api = client.CustomObjectsApi() + self.apps_api = client.AppsV1Api() + self.csi_nodes_api = self._get_csi_nodes_api() + self.host_definitions_api = self._get_host_definitions_api() + + def _get_dynamic_client(self): + return dynamic.DynamicClient(api_client.ApiClient(configuration=self._load_cluster_configuration())) + + def _load_cluster_configuration(self): + return config.load_incluster_config() + + def _get_csi_nodes_api(self): + return self.dynamic_client.resources.get(api_version=settings.STORAGE_API_VERSION, + kind=settings.CSINODE_KIND) + + def _get_host_definitions_api(self): + return self.dynamic_client.resources.get(api_version=settings.CSI_IBM_API_VERSION, + kind=settings.HOST_DEFINITION_KIND) + + def get_csi_node(self, node_name): + try: + return self.csi_nodes_api.get(name=node_name) + except ApiException as ex: + if ex.status == 404: + logger.error(messages.CSI_NODE_DOES_NOT_EXIST.format(node_name)) + else: + logger.error(messages.FAILED_TO_GET_CSI_NODE.format(node_name, ex.body)) + return None + + def list_host_definition(self): + try: + return self.host_definitions_api.get() + except ApiException as ex: + logger.error(messages.FAILED_TO_GET_LIST_OF_HOST_DEFINITIONS.format(ex.body)) + return self._get_empty_k8s_list() + + def create_host_definition(self, host_definition_manifest): + try: + return self.host_definitions_api.create(body=host_definition_manifest) + except ApiException as ex: + if ex != 404: + logger.error(messages.FAILED_TO_CREATE_HOST_DEFINITION.format( + host_definition_manifest[settings.METADATA][common_settings.NAME_FIELD], ex.body)) + return None + + def patch_cluster_custom_object_status(self, group, version, plural, name, status): + try: + self.custom_object_api.patch_cluster_custom_object_status(group, version, plural, name, status) + except ApiException as ex: + if ex.status == 404: + logger.error(messages.HOST_DEFINITION_DOES_NOT_EXIST.format(name)) + else: + logger.error(messages.FAILED_TO_SET_HOST_DEFINITION_STATUS.format(name, ex.body)) + + def create_event(self, namespace, k8s_event): + try: + self.core_api.create_namespaced_event(namespace, k8s_event) + except ApiException as ex: + logger.error(messages.FAILED_TO_CREATE_EVENT_FOR_HOST_DEFINITION.format( + k8s_event.involved_object.name, ex.body)) + + def delete_host_definition(self, host_definition_name): + try: + return self.host_definitions_api.delete(name=host_definition_name, body={}) + except ApiException as ex: + if ex.status != 404: + logger.error(messages.FAILED_TO_DELETE_HOST_DEFINITION.format(host_definition_name, ex.body)) + return None + + def patch_host_definition(self, host_definition_manifest): + host_definition_name = host_definition_manifest[settings.METADATA][common_settings.NAME_FIELD] + logger.info(messages.PATCHING_HOST_DEFINITION.format(host_definition_name)) + try: + self.host_definitions_api.patch(body=host_definition_manifest, name=host_definition_name, + content_type='application/merge-patch+json') + return 200 + except ApiException as ex: + if ex.status == 404: + logger.error(messages.HOST_DEFINITION_DOES_NOT_EXIST.format(host_definition_name)) + else: + logger.error(messages.FAILED_TO_PATCH_HOST_DEFINITION.format(host_definition_name, ex.body)) + return ex.status + + def patch_node(self, node_name, body): + try: + self.core_api.patch_node(node_name, body) + except ApiException as ex: + logger.error(messages.FAILED_TO_UPDATE_NODE_LABEL.format( + node_name, settings.MANAGE_NODE_LABEL, ex.body)) + + def get_secret_data(self, secret_name, secret_namespace): + try: + return self.core_api.read_namespaced_secret(name=secret_name, namespace=secret_namespace).data + except ApiException as ex: + if ex.status == 404: + logger.error(messages.SECRET_DOES_NOT_EXIST.format(secret_name, secret_namespace)) + else: + logger.error(messages.FAILED_TO_GET_SECRET.format(secret_name, secret_namespace, ex.body)) + return {} + + def read_node(self, node_name): + try: + logger.info(messages.READ_NODE.format(node_name)) + return self.core_api.read_node(name=node_name) + except ApiException as ex: + logger.error(messages.FAILED_TO_GET_NODE.format(node_name, ex.body)) + return None + + def list_daemon_set_for_all_namespaces(self, label_selector): + try: + return self.apps_api.list_daemon_set_for_all_namespaces(label_selector=label_selector) + except ApiException as ex: + logger.error(messages.FAILED_TO_LIST_DAEMON_SETS.format(ex.body)) + return None + + def list_pod_for_all_namespaces(self, label_selector): + try: + return self.core_api.list_pod_for_all_namespaces(label_selector=label_selector) + except ApiException as ex: + logger.error(messages.FAILED_TO_LIST_PODS.format(ex.body)) + return None + + def get_storage_class_stream(self): + return self._get_basic_resource_stream(self.storage_api.list_storage_class, 5) + + def list_storage_class(self): + try: + return self.storage_api.list_storage_class() + except ApiException as ex: + logger.error(messages.FAILED_TO_GET_STORAGE_CLASSES.format(ex.body)) + return self._get_empty_k8s_list() + + def get_node_stream(self): + return self._get_basic_resource_stream(self.core_api.list_node, 5) + + def list_node(self): + try: + return self.core_api.list_node() + except ApiException as ex: + logger.error(messages.FAILED_TO_GET_NODES.format(ex.body)) + return self._get_empty_k8s_list() + + def get_secret_stream(self): + return self._get_basic_resource_stream(self.core_api.list_secret_for_all_namespaces, 5) + + def _get_basic_resource_stream(self, list_function, timeout): + resource_version = utils.get_k8s_object_resource_version(list_function()) + return watch.Watch().stream(list_function, resource_version=resource_version, timeout_seconds=timeout) + + def get_host_definition_stream(self, resource_version, timeout): + return self.host_definitions_api.watch(resource_version=resource_version, timeout=timeout) + + def get_csi_node_stream(self): + resource_version = utils.get_k8s_object_resource_version(self.list_csi_node()) + return self.csi_nodes_api.watch(resource_version=resource_version, timeout=5) + + def list_csi_node(self): + try: + return self.csi_nodes_api.get() + except ApiException as ex: + logger.error(messages.FAILED_TO_GET_CSI_NODES.format(ex.body)) + return self._get_empty_k8s_list() + + def _get_empty_k8s_list(self): + much_object = Munch.fromDict({ + common_settings.ITEMS_FIELD: [], + settings.METADATA: { + common_settings.RESOURCE_VERSION_FIELD + } + }) + much_object.items = [] + return much_object diff --git a/controllers/servers/host_definer/kubernetes_manager/manager.py b/controllers/servers/host_definer/kubernetes_manager/manager.py deleted file mode 100644 index cd6f80ad6..000000000 --- a/controllers/servers/host_definer/kubernetes_manager/manager.py +++ /dev/null @@ -1,368 +0,0 @@ -import ast -import datetime -import base64 - -from kubernetes import client, config, dynamic -from kubernetes.client import api_client -from kubernetes.client.rest import ApiException - -from controllers.common.csi_logger import get_stdout_logger -import controllers.servers.host_definer.messages as messages -from controllers.servers.host_definer import settings -import controllers.common.settings as common_settings -from controllers.servers.host_definer.types import ( - CsiNodeInfo, PodInfo, NodeInfo, StorageClassInfo, HostDefinitionInfo) - -logger = get_stdout_logger() - - -class KubernetesManager(): - def __init__(self): - self._load_cluster_configuration() - self.dynamic_client = self._get_dynamic_client() - self.storage_api = client.StorageV1Api() - self.core_api = client.CoreV1Api() - self.custom_object_api = client.CustomObjectsApi() - self.apps_api = client.AppsV1Api() - self.csi_nodes_api = self._get_csi_nodes_api() - self.host_definitions_api = self._get_host_definitions_api() - - def _get_dynamic_client(self): - return dynamic.DynamicClient(api_client.ApiClient(configuration=self._load_cluster_configuration())) - - def _load_cluster_configuration(self): - return config.load_incluster_config() - - def _get_csi_nodes_api(self): - return self.dynamic_client.resources.get(api_version=settings.STORAGE_API_VERSION, - kind=settings.CSINODE_KIND) - - def _get_host_definitions_api(self): - return self.dynamic_client.resources.get(api_version=settings.CSI_IBM_API_VERSION, - kind=settings.HOST_DEFINITION_KIND) - - def _get_csi_nodes_info_with_driver(self): - csi_nodes_info_with_driver = [] - k8s_csi_nodes = self._get_k8s_csi_nodes() - for k8s_csi_node in k8s_csi_nodes: - if self._is_k8s_csi_node_has_driver(k8s_csi_node): - csi_nodes_info_with_driver.append(self._generate_csi_node_info(k8s_csi_node)) - logger.info(messages.CSI_NODES_WITH_IBM_BLOCK_CSI_DRIVER.format(csi_nodes_info_with_driver)) - return csi_nodes_info_with_driver - - def _get_k8s_csi_nodes(self): - try: - return self.csi_nodes_api.get().items - except ApiException as ex: - logger.error(messages.FAILED_TO_GET_CSI_NODES.format(ex.body)) - return [] - - def _get_nodes_info(self): - try: - nodes_info = [] - for k8s_node in self.core_api.list_node().items: - k8s_node = self._generate_node_info(k8s_node) - nodes_info.append(k8s_node) - return nodes_info - except ApiException as ex: - logger.error(messages.FAILED_TO_GET_NODES.format(ex.body)) - return [] - - def _get_storage_classes_info(self): - try: - storage_classes_info = [] - for k8s_storage_class in self.storage_api.list_storage_class().items: - storage_class_info = self._generate_storage_class_info(k8s_storage_class) - storage_classes_info.append(storage_class_info) - return storage_classes_info - - except ApiException as ex: - logger.error(messages.FAILED_TO_GET_STORAGE_CLASSES.format(ex.body)) - return [] - - def _generate_storage_class_info(self, k8s_storage_class): - storage_class_info = StorageClassInfo() - storage_class_info.name = k8s_storage_class.metadata.name - storage_class_info.provisioner = k8s_storage_class.provisioner - storage_class_info.parameters = k8s_storage_class.parameters - return storage_class_info - - def _is_k8s_csi_node_has_driver(self, k8s_csi_node): - if k8s_csi_node.spec.drivers: - for driver in k8s_csi_node.spec.drivers: - if driver.name == settings.CSI_PROVISIONER_NAME: - return True - return False - - def _get_csi_node_info(self, node_name): - try: - k8s_csi_node = self.csi_nodes_api.get(name=node_name) - return self._generate_csi_node_info(k8s_csi_node) - except ApiException as ex: - if ex.status == 404: - logger.error(messages.CSI_NODE_DOES_NOT_EXIST.format(node_name)) - else: - logger.error(messages.FAILED_TO_GET_CSI_NODE.format(node_name, ex.body)) - return CsiNodeInfo() - - def _generate_csi_node_info(self, k8s_csi_node): - csi_node_info = CsiNodeInfo() - csi_node_info.name = k8s_csi_node.metadata.name - csi_node_info.node_id = self._get_node_id_from_k8s_csi_node(k8s_csi_node) - return csi_node_info - - def _get_node_id_from_k8s_csi_node(self, k8s_csi_node): - if k8s_csi_node.spec.drivers: - for driver in k8s_csi_node.spec.drivers: - if driver.name == settings.CSI_PROVISIONER_NAME: - return driver.nodeID - return '' - - def _get_matching_host_definition_info(self, node_name, secret_name, secret_namespace): - k8s_host_definitions = self._get_k8s_host_definitions() - for k8s_host_definition in k8s_host_definitions: - host_definition_info = self._generate_host_definition_info(k8s_host_definition) - if self._is_host_definition_matches(host_definition_info, node_name, secret_name, secret_namespace): - return host_definition_info - return None - - def _get_k8s_host_definitions(self): - try: - return self.host_definitions_api.get().items - - except ApiException as ex: - logger.error(messages.FAILED_TO_GET_LIST_OF_HOST_DEFINITIONS.format(ex.body)) - return [] - - def _generate_host_definition_info(self, k8s_host_definition): - host_definition_info = HostDefinitionInfo() - host_definition_info.name = k8s_host_definition.metadata.name - host_definition_info.resource_version = self._get_k8s_object_resource_version(k8s_host_definition) - host_definition_info.uid = k8s_host_definition.metadata.uid - host_definition_info.phase = self._get_host_definition_phase(k8s_host_definition) - host_definition_info.secret_name = self._get_attr_from_host_definition( - k8s_host_definition, settings.SECRET_NAME_FIELD) - host_definition_info.secret_namespace = self._get_attr_from_host_definition( - k8s_host_definition, settings.SECRET_NAMESPACE_FIELD) - host_definition_info.node_name = self._get_attr_from_host_definition( - k8s_host_definition, settings.NODE_NAME_FIELD) - host_definition_info.node_id = self._get_attr_from_host_definition( - k8s_host_definition, common_settings.HOST_DEFINITION_NODE_ID_FIELD) - host_definition_info.connectivity_type = self._get_attr_from_host_definition( - k8s_host_definition, settings.CONNECTIVITY_TYPE_FIELD) - return host_definition_info - - def _get_k8s_object_resource_version(self, k8s_object): - if k8s_object.metadata.resource_version: - return k8s_object.metadata.resource_version - return k8s_object.metadata.resourceVersion - - def _get_host_definition_phase(self, k8s_host_definition): - if k8s_host_definition.status: - return k8s_host_definition.status.phase - return '' - - def _get_attr_from_host_definition(self, k8s_host_definition, attribute): - if hasattr(k8s_host_definition.spec.hostDefinition, attribute): - return getattr(k8s_host_definition.spec.hostDefinition, attribute) - return '' - - def _is_host_definition_matches(self, host_definition_info, node_name, secret_name, secret_namespace): - return host_definition_info.node_name == node_name and \ - host_definition_info.secret_name == secret_name and \ - host_definition_info.secret_namespace == secret_namespace - - def _create_host_definition(self, host_definition_manifest): - try: - k8s_host_definition = self.host_definitions_api.create(body=host_definition_manifest) - logger.info(messages.CREATED_HOST_DEFINITION.format(k8s_host_definition.metadata.name)) - self._add_finalizer(k8s_host_definition.metadata.name) - return self._generate_host_definition_info(k8s_host_definition) - except ApiException as ex: - if ex != 404: - logger.error(messages.FAILED_TO_CREATE_HOST_DEFINITION.format( - host_definition_manifest[settings.METADATA][common_settings.NAME_FIELD], ex.body)) - - def _add_finalizer(self, host_definition_name): - logger.info(messages.ADD_FINALIZER_TO_HOST_DEFINITION.format(host_definition_name)) - self._update_finalizer(host_definition_name, [settings.CSI_IBM_FINALIZER, ]) - - def _set_host_definition_status(self, host_definition_name, host_definition_phase): - logger.info(messages.SET_HOST_DEFINITION_STATUS.format(host_definition_name, host_definition_phase)) - status = self._get_status_manifest(host_definition_phase) - try: - self.custom_object_api.patch_cluster_custom_object_status( - common_settings.CSI_IBM_GROUP, common_settings.VERSION, common_settings.HOST_DEFINITION_PLURAL, - host_definition_name, status) - except ApiException as ex: - if ex.status == 404: - logger.error(messages.HOST_DEFINITION_DOES_NOT_EXIST.format(host_definition_name)) - else: - logger.error(messages.FAILED_TO_SET_HOST_DEFINITION_STATUS.format(host_definition_name, ex.body)) - - def _get_status_manifest(self, host_definition_phase): - return { - settings.STATUS: { - settings.PHASE: host_definition_phase, - } - } - - def _generate_k8s_event(self, host_definition_info, message, action, message_type): - return client.CoreV1Event( - metadata=client.V1ObjectMeta(generate_name='{}.'.format(host_definition_info.name), ), - reporting_component=settings.HOST_DEFINER, reporting_instance=settings.HOST_DEFINER, action=action, - type=self._get_event_type(message_type), reason=message_type + action, message=str(message), - event_time=datetime.datetime.utcnow().isoformat(timespec='microseconds') + 'Z', - involved_object=client.V1ObjectReference( - api_version=settings.CSI_IBM_API_VERSION, kind=settings.HOST_DEFINITION_KIND, - name=host_definition_info.name, resource_version=host_definition_info.resource_version, - uid=host_definition_info.uid, )) - - def _get_event_type(self, message_type): - if message_type != settings.SUCCESSFUL_MESSAGE_TYPE: - return settings.WARNING_EVENT_TYPE - return settings.NORMAL_EVENT_TYPE - - def _create_k8s_event(self, namespace, k8s_event): - try: - self.core_api.create_namespaced_event(namespace, k8s_event) - except ApiException as ex: - logger.error(messages.FAILED_TO_CREATE_EVENT_FOR_HOST_DEFINITION.format( - k8s_event.involved_object.name, ex.body)) - - def _delete_host_definition(self, host_definition_name): - logger.info(messages.DELETE_HOST_DEFINITION.format(host_definition_name)) - try: - remove_finalizer_status_code = self._remove_finalizer(host_definition_name) - if remove_finalizer_status_code == 200: - self.host_definitions_api.delete(name=host_definition_name, body={}) - else: - logger.error(messages.FAILED_TO_DELETE_HOST_DEFINITION.format( - host_definition_name, messages.FAILED_TO_REMOVE_FINALIZER)) - except ApiException as ex: - if ex.status != 404: - logger.error(messages.FAILED_TO_DELETE_HOST_DEFINITION.format(host_definition_name, ex.body)) - - def _remove_finalizer(self, host_definition_name): - logger.info(messages.REMOVE_FINALIZER_TO_HOST_DEFINITION.format(host_definition_name)) - return self._update_finalizer(host_definition_name, []) - - def _update_finalizer(self, host_definition_name, finalizers): - finalizer_manifest = { - settings.METADATA: { - common_settings.NAME_FIELD: host_definition_name, - settings.FINALIZERS: finalizers, - } - } - return self._patch_host_definition(finalizer_manifest) - - def _patch_host_definition(self, host_definition_manifest): - host_definition_name = host_definition_manifest[settings.METADATA][common_settings.NAME_FIELD] - logger.info(messages.PATCHING_HOST_DEFINITION.format(host_definition_name)) - try: - self.host_definitions_api.patch(body=host_definition_manifest, name=host_definition_name, - content_type='application/merge-patch+json') - return 200 - except ApiException as ex: - if ex.status == 404: - logger.error(messages.HOST_DEFINITION_DOES_NOT_EXIST.format(host_definition_name)) - else: - logger.error(messages.FAILED_TO_PATCH_HOST_DEFINITION.format(host_definition_name, ex.body)) - return ex.status - - def _update_manage_node_label(self, node_name, label_value): - body = self._get_body_for_labels(label_value) - try: - self.core_api.patch_node(node_name, body) - except ApiException as ex: - logger.error(messages.FAILED_TO_UPDATE_NODE_LABEL.format( - node_name, settings.MANAGE_NODE_LABEL, ex.body)) - - def _get_body_for_labels(self, label_value): - body = { - settings.METADATA: { - settings.LABELS: { - settings.MANAGE_NODE_LABEL: label_value} - } - } - - return body - - def _get_secret_data(self, secret_name, secret_namespace): - try: - logger.info(messages.READ_SECRET.format(secret_name, secret_namespace)) - secret_data = self.core_api.read_namespaced_secret(name=secret_name, namespace=secret_namespace).data - return self._change_decode_base64_secret_config(secret_data) - except ApiException as ex: - if ex.status == 404: - logger.error(messages.SECRET_DOES_NOT_EXIST.format(secret_name, secret_namespace)) - else: - logger.error(messages.FAILED_TO_GET_SECRET.format(secret_name, secret_namespace, ex.body)) - return {} - - def _change_decode_base64_secret_config(self, secret_data): - if settings.SECRET_CONFIG_FIELD in secret_data.keys(): - secret_data[settings.SECRET_CONFIG_FIELD] = self._decode_base64_to_dict( - secret_data[settings.SECRET_CONFIG_FIELD]) - return secret_data - - def _decode_base64_to_dict(self, content_with_base64): - decoded_string_content = self._decode_base64_to_string(content_with_base64) - encoded_dict = str(decoded_string_content).encode('utf-8') - base64_dict = base64.b64encode(encoded_dict) - my_dict_again = ast.literal_eval(base64.b64decode(base64_dict)) - return my_dict_again - - def _decode_base64_to_string(self, content_with_base64): - try: - base64_bytes = content_with_base64.encode('ascii') - decoded_string_in_bytes = base64.b64decode(base64_bytes) - return decoded_string_in_bytes.decode('ascii') - except Exception: - return content_with_base64 - - def _get_node_info(self, node_name): - k8s_node = self._read_node(node_name) - if k8s_node: - return self._generate_node_info(k8s_node) - return NodeInfo('', {}) - - def _read_node(self, node_name): - try: - logger.info(messages.READ_NODE.format(node_name)) - return self.core_api.read_node(name=node_name) - except ApiException as ex: - logger.error(messages.FAILED_TO_GET_NODE.format(node_name, ex.body)) - return None - - def _generate_node_info(self, k8s_node): - return NodeInfo(k8s_node.metadata.name, k8s_node.metadata.labels) - - def _get_csi_daemon_set(self): - try: - daemon_sets = self.apps_api.list_daemon_set_for_all_namespaces(label_selector=settings.DRIVER_PRODUCT_LABEL) - if daemon_sets.items: - return daemon_sets.items[0] - return None - except ApiException as ex: - logger.error(messages.FAILED_TO_LIST_DAEMON_SETS.format(ex.body)) - return None - - def _get_csi_pods_info(self): - try: - pods_info = [] - k8s_pods = self.core_api.list_pod_for_all_namespaces(label_selector=settings.DRIVER_PRODUCT_LABEL) - for k8s_pod in k8s_pods.items: - pod_info = self._generate_pod_info(k8s_pod) - pods_info.append(pod_info) - return pods_info - - except ApiException as ex: - logger.error(messages.FAILED_TO_LIST_PODS.format(ex.body)) - return [] - - def _generate_pod_info(self, k8s_pod): - pod_info = PodInfo() - pod_info.name = k8s_pod.metadata.name - pod_info.node_name = k8s_pod.spec.node_name - return pod_info diff --git a/controllers/servers/host_definer/resource_manager/__init__.py b/controllers/servers/host_definer/resource_manager/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/controllers/servers/host_definer/resource_manager/csi_node.py b/controllers/servers/host_definer/resource_manager/csi_node.py new file mode 100644 index 000000000..89dc4d4e4 --- /dev/null +++ b/controllers/servers/host_definer/resource_manager/csi_node.py @@ -0,0 +1,51 @@ +from controllers.common.csi_logger import get_stdout_logger +from controllers.servers.host_definer import settings +import controllers.servers.host_definer.messages as messages +from controllers.servers.host_definer.k8s.api import K8SApi +from controllers.servers.host_definer.resource_manager.resource_info import ResourceInfoManager +from controllers.servers.host_definer.resource_manager.daemon_set import DaemonSetManager + +logger = get_stdout_logger() + + +class CSINodeManager: + def __init__(self): + self.k8s_api = K8SApi() + self.resource_info_manager = ResourceInfoManager() + self.daemon_set_manager = DaemonSetManager() + + def get_csi_nodes_info_with_driver(self): + csi_nodes_info_with_driver = [] + k8s_csi_nodes = self.k8s_api.list_csi_node().items + for k8s_csi_node in k8s_csi_nodes: + if self._is_k8s_csi_node_has_driver(k8s_csi_node): + csi_nodes_info_with_driver.append(self.resource_info_manager.generate_csi_node_info(k8s_csi_node)) + logger.info(messages.CSI_NODES_WITH_IBM_BLOCK_CSI_DRIVER.format(csi_nodes_info_with_driver)) + return csi_nodes_info_with_driver + + def _is_k8s_csi_node_has_driver(self, k8s_csi_node): + if k8s_csi_node.spec.drivers: + for driver in k8s_csi_node.spec.drivers: + if driver.name == settings.CSI_PROVISIONER_NAME: + return True + return False + + def is_host_part_of_update(self, worker): + logger.info(messages.CHECK_IF_NODE_IS_PART_OF_UPDATE.format(worker)) + daemon_set_name = self.daemon_set_manager.wait_until_all_daemon_set_pods_are_up_to_date() + if daemon_set_name: + return self._is_csi_node_pod_running_on_worker(worker, daemon_set_name) + return False + + def _is_csi_node_pod_running_on_worker(self, worker, daemon_set_name): + logger.info(messages.CHECK_IF_CSI_NODE_POD_IS_RUNNING.format(worker)) + csi_pods_info = self.resource_info_manager.get_csi_pods_info() + for pod_info in csi_pods_info: + if (pod_info.node_name == worker) and (daemon_set_name in pod_info.name): + return True + return False + + def is_node_id_changed(self, host_definition_node_id, csi_node_node_id): + if host_definition_node_id != csi_node_node_id and host_definition_node_id and csi_node_node_id: + return True + return False diff --git a/controllers/servers/host_definer/resource_manager/daemon_set.py b/controllers/servers/host_definer/resource_manager/daemon_set.py new file mode 100644 index 000000000..2ccb5d091 --- /dev/null +++ b/controllers/servers/host_definer/resource_manager/daemon_set.py @@ -0,0 +1,36 @@ +import time + +from controllers.common.csi_logger import get_stdout_logger +import controllers.servers.host_definer.messages as messages +from controllers.servers.host_definer import settings +from controllers.servers.host_definer.k8s.api import K8SApi + +logger = get_stdout_logger() + + +class DaemonSetManager(): + def __init__(self): + self.k8s_api = K8SApi() + + def wait_until_all_daemon_set_pods_are_up_to_date(self): + csi_daemon_set = self._get_csi_daemon_set() + if not csi_daemon_set: + return None + status = csi_daemon_set.status + while status.updated_number_scheduled != status.desired_number_scheduled: + logger.info(messages.UPDATED_CSI_NODE_VS_DESIRED.format( + status.updated_number_scheduled, status.desired_number_scheduled)) + if status.desired_number_scheduled == 0: + return None + csi_daemon_set = self._get_csi_daemon_set() + if not csi_daemon_set: + return None + status = csi_daemon_set.status + time.sleep(0.5) + return csi_daemon_set.metadata.name + + def _get_csi_daemon_set(self): + daemon_sets = self.k8s_api.list_daemon_set_for_all_namespaces(settings.DRIVER_PRODUCT_LABEL) + if daemon_sets and daemon_sets.items: + return daemon_sets.items[0] + return None diff --git a/controllers/servers/host_definer/resource_manager/event.py b/controllers/servers/host_definer/resource_manager/event.py new file mode 100644 index 000000000..08b7361b0 --- /dev/null +++ b/controllers/servers/host_definer/resource_manager/event.py @@ -0,0 +1,25 @@ +import datetime +from kubernetes import client + +from controllers.common.csi_logger import get_stdout_logger +from controllers.servers.host_definer import settings + +logger = get_stdout_logger() + + +class EventManager(): + def generate_k8s_event(self, host_definition_info, message, action, message_type): + return client.CoreV1Event( + metadata=client.V1ObjectMeta(generate_name='{}.'.format(host_definition_info.name), ), + reporting_component=settings.HOST_DEFINER, reporting_instance=settings.HOST_DEFINER, action=action, + type=self._get_event_type(message_type), reason=message_type + action, message=str(message), + event_time=datetime.datetime.utcnow().isoformat(timespec='microseconds') + 'Z', + involved_object=client.V1ObjectReference( + api_version=settings.CSI_IBM_API_VERSION, kind=settings.HOST_DEFINITION_KIND, + name=host_definition_info.name, resource_version=host_definition_info.resource_version, + uid=host_definition_info.uid, )) + + def _get_event_type(self, message_type): + if message_type != settings.SUCCESSFUL_MESSAGE_TYPE: + return settings.WARNING_EVENT_TYPE + return settings.NORMAL_EVENT_TYPE diff --git a/controllers/servers/host_definer/resource_manager/host_definition.py b/controllers/servers/host_definer/resource_manager/host_definition.py new file mode 100644 index 000000000..705b2457e --- /dev/null +++ b/controllers/servers/host_definer/resource_manager/host_definition.py @@ -0,0 +1,169 @@ +from controllers.common.csi_logger import get_stdout_logger +import controllers.common.settings as common_settings +from controllers.servers.host_definer import settings +from controllers.servers.host_definer.globals import NODES +from controllers.servers.host_definer.utils import utils +from controllers.servers.host_definer.utils import manifest_utils +from controllers.servers.host_definer.types import HostDefinitionInfo +import controllers.servers.host_definer.messages as messages +from controllers.servers.host_definer.k8s.api import K8SApi +from controllers.servers.host_definer.resource_manager.event import EventManager +from controllers.servers.host_definer.resource_manager.resource_info import ResourceInfoManager + +logger = get_stdout_logger() + + +class HostDefinitionManager: + def __init__(self): + self.k8s_api = K8SApi() + self.event_manager = EventManager() + self.resource_info_manager = ResourceInfoManager() + + def get_host_definition_info_from_secret_and_node_name(self, node_name, secret_info): + host_definition_info = self.get_host_definition_info_from_secret(secret_info) + host_definition_info = self.add_name_to_host_definition_info(node_name, host_definition_info) + return host_definition_info + + def get_host_definition_info_from_secret(self, secret_info): + host_definition_info = HostDefinitionInfo() + host_definition_info.secret_name = secret_info.name + host_definition_info.secret_namespace = secret_info.namespace + return host_definition_info + + def add_name_to_host_definition_info(self, node_name, host_definition_info): + host_definition_info.node_name = node_name + host_definition_info.node_id = NODES[node_name].node_id + host_definition_info.name = self._get_host_definition_name(node_name) + return host_definition_info + + def _get_host_definition_name(self, node_name): + return '{0}-{1}'.format(node_name, utils.get_random_string()).replace('_', '.') + + def update_host_definition_info(self, host_definition_info): + host_definition_info_on_cluster = self.get_matching_host_definition_info( + host_definition_info.node_name, host_definition_info.secret_name, host_definition_info.secret_namespace) + if host_definition_info_on_cluster: + host_definition_info.connectivity_type = host_definition_info_on_cluster.connectivity_type + host_definition_info.node_id = host_definition_info_on_cluster.node_id + return host_definition_info + + def create_host_definition_if_not_exist(self, host_definition_info, response): + node_id = NODES[host_definition_info.node_name].node_id + host_definition_manifest = manifest_utils.get_host_definition_manifest(host_definition_info, response, node_id) + current_host_definition_info_on_cluster = self.get_matching_host_definition_info( + host_definition_info.node_name, host_definition_info.secret_name, host_definition_info.secret_namespace) + if current_host_definition_info_on_cluster: + host_definition_manifest[settings.METADATA][ + common_settings.NAME_FIELD] = current_host_definition_info_on_cluster.name + self.k8s_api.patch_host_definition(host_definition_manifest) + return current_host_definition_info_on_cluster + else: + logger.info(messages.CREATING_NEW_HOST_DEFINITION.format(host_definition_info.name)) + return self.create_host_definition(host_definition_manifest) + + def create_host_definition(self, host_definition_manifest): + k8s_host_definition = self.k8s_api.create_host_definition(host_definition_manifest) + if k8s_host_definition: + logger.info(messages.CREATED_HOST_DEFINITION.format(k8s_host_definition.metadata.name)) + self._add_finalizer(k8s_host_definition.metadata.name) + return self.resource_info_manager.generate_host_definition_info(k8s_host_definition) + return HostDefinitionInfo() + + def _add_finalizer(self, host_definition_name): + logger.info(messages.ADD_FINALIZER_TO_HOST_DEFINITION.format(host_definition_name)) + self._update_finalizer(host_definition_name, [settings.CSI_IBM_FINALIZER, ]) + + def set_status_to_host_definition_after_definition(self, message_from_storage, host_definition_info): + if message_from_storage and host_definition_info: + self.set_host_definition_status(host_definition_info.name, + settings.PENDING_CREATION_PHASE) + self.create_k8s_event_for_host_definition( + host_definition_info, message_from_storage, settings.DEFINE_ACTION, settings.FAILED_MESSAGE_TYPE) + elif host_definition_info: + self.set_host_definition_status_to_ready(host_definition_info) + + def set_host_definition_status_to_ready(self, host_definition): + self.set_host_definition_status(host_definition.name, settings.READY_PHASE) + self.create_k8s_event_for_host_definition( + host_definition, settings.SUCCESS_MESSAGE, settings.DEFINE_ACTION, settings.SUCCESSFUL_MESSAGE_TYPE) + + def handle_k8s_host_definition_after_undefine_action(self, host_definition_info, response): + current_host_definition_info_on_cluster = self.get_matching_host_definition_info( + host_definition_info.node_name, host_definition_info.secret_name, host_definition_info.secret_namespace) + if current_host_definition_info_on_cluster: + self._handle_existing_k8s_host_definition_after_undefine_action( + response.error_message, current_host_definition_info_on_cluster) + + def _handle_existing_k8s_host_definition_after_undefine_action(self, message_from_storage, host_definition_info): + if message_from_storage and host_definition_info: + self.set_host_definition_status(host_definition_info.name, + settings.PENDING_DELETION_PHASE) + self.create_k8s_event_for_host_definition( + host_definition_info, message_from_storage, + settings.UNDEFINE_ACTION, settings.FAILED_MESSAGE_TYPE) + elif host_definition_info: + self.delete_host_definition(host_definition_info.name) + + def create_k8s_event_for_host_definition(self, host_definition_info, message, action, message_type): + logger.info(messages.CREATE_EVENT_FOR_HOST_DEFINITION.format(message, host_definition_info.name)) + k8s_event = self.event_manager.generate_k8s_event(host_definition_info, message, action, message_type) + self.k8s_api.create_event(settings.DEFAULT_NAMESPACE, k8s_event) + + def delete_host_definition(self, host_definition_name): + logger.info(messages.DELETE_HOST_DEFINITION.format(host_definition_name)) + remove_finalizer_status_code = self._remove_finalizer(host_definition_name) + if remove_finalizer_status_code == 200: + self.k8s_api.delete_host_definition(host_definition_name) + else: + logger.error(messages.FAILED_TO_DELETE_HOST_DEFINITION.format( + host_definition_name, messages.FAILED_TO_REMOVE_FINALIZER)) + + def _remove_finalizer(self, host_definition_name): + logger.info(messages.REMOVE_FINALIZER_TO_HOST_DEFINITION.format(host_definition_name)) + return self._update_finalizer(host_definition_name, []) + + def _update_finalizer(self, host_definition_name, finalizers): + finalizer_manifest = manifest_utils.get_finalizer_manifest(host_definition_name, finalizers) + return self.k8s_api.patch_host_definition(finalizer_manifest) + + def is_host_definition_in_pending_phase(self, phase): + return phase.startswith(settings.PENDING_PREFIX) + + def set_host_definition_phase_to_error(self, host_definition_info): + logger.info(messages.SET_HOST_DEFINITION_PHASE_TO_ERROR.format(host_definition_info.name)) + self.set_host_definition_status(host_definition_info.name, settings.ERROR_PHASE) + + def set_host_definition_status(self, host_definition_name, host_definition_phase): + logger.info(messages.SET_HOST_DEFINITION_STATUS.format(host_definition_name, host_definition_phase)) + status = manifest_utils.get_host_definition_status_manifest(host_definition_phase) + self.k8s_api.patch_cluster_custom_object_status( + common_settings.CSI_IBM_GROUP, common_settings.VERSION, common_settings.HOST_DEFINITION_PLURAL, + host_definition_name, status) + + def is_host_definition_not_pending(self, host_definition_info): + current_host_definition_info_on_cluster = self.get_matching_host_definition_info( + host_definition_info.node_name, host_definition_info.secret_name, host_definition_info.secret_namespace) + return not current_host_definition_info_on_cluster or \ + current_host_definition_info_on_cluster.phase == settings.READY_PHASE + + def get_matching_host_definition_info(self, node_name, secret_name, secret_namespace): + k8s_host_definitions = self.k8s_api.list_host_definition().items + for k8s_host_definition in k8s_host_definitions: + host_definition_info = self.resource_info_manager.generate_host_definition_info(k8s_host_definition) + if self._is_host_definition_matches(host_definition_info, node_name, secret_name, secret_namespace): + return host_definition_info + return None + + def _is_host_definition_matches(self, host_definition_info, node_name, secret_name, secret_namespace): + return host_definition_info.node_name == node_name and \ + host_definition_info.secret_name == secret_name and \ + host_definition_info.secret_namespace == secret_namespace + + def get_all_host_definitions_info_of_the_node(self, node_name): + node_host_definitions_info = [] + k8s_host_definitions = self.k8s_api.list_host_definition().items + for k8s_host_definition in k8s_host_definitions: + host_definition_info = self.resource_info_manager.generate_host_definition_info(k8s_host_definition) + if host_definition_info.node_name == node_name: + node_host_definitions_info.append(host_definition_info) + return node_host_definitions_info diff --git a/controllers/servers/host_definer/resource_manager/node.py b/controllers/servers/host_definer/resource_manager/node.py new file mode 100644 index 000000000..3d8a29cf4 --- /dev/null +++ b/controllers/servers/host_definer/resource_manager/node.py @@ -0,0 +1,151 @@ +from controllers.common.csi_logger import get_stdout_logger +from controllers.servers.utils import get_system_info_for_topologies +from controllers.servers.errors import ValidationException +from controllers.servers.host_definer import settings +from controllers.servers.host_definer.globals import NODES, MANAGED_SECRETS +from controllers.servers.host_definer.types import ManagedNode +import controllers.servers.host_definer.messages as messages +from controllers.servers.host_definer.utils import manifest_utils +from controllers.servers.host_definer.utils import utils +from controllers.servers.host_definer.k8s.api import K8SApi +from controllers.servers.host_definer.resource_manager.secret import SecretManager +from controllers.servers.host_definer.resource_manager.host_definition import HostDefinitionManager +from controllers.servers.host_definer.definition_manager.definition import DefinitionManager +from controllers.servers.host_definer.resource_manager.resource_info import ResourceInfoManager + +logger = get_stdout_logger() + + +class NodeManager: + def __init__(self): + self.k8s_api = K8SApi() + self.secret_manager = SecretManager() + self.host_definition_manager = HostDefinitionManager() + self.definition_manager = DefinitionManager() + self.resource_info_manager = ResourceInfoManager() + + def is_node_can_be_defined(self, node_name): + return utils.is_dynamic_node_labeling_allowed() or self.is_node_has_manage_node_label(node_name) + + def is_node_can_be_undefined(self, node_name): + return utils.is_host_definer_can_delete_hosts() and \ + self.is_node_has_manage_node_label(node_name) and \ + not self.is_node_has_forbid_deletion_label(node_name) + + def is_node_has_forbid_deletion_label(self, node_name): + return self._is_node_has_label_in_true(node_name, settings.FORBID_DELETION_LABEL) + + def add_node_to_nodes(self, csi_node_info): + logger.info(messages.NEW_KUBERNETES_NODE.format(csi_node_info.name)) + self._add_manage_node_label_to_node(csi_node_info.name) + NODES[csi_node_info.name] = self.generate_managed_node(csi_node_info) + + def _add_manage_node_label_to_node(self, node_name): + if self.is_node_has_manage_node_label(node_name): + return + logger.info(messages.ADD_LABEL_TO_NODE.format(settings.MANAGE_NODE_LABEL, node_name)) + self._update_manage_node_label(node_name, settings.TRUE_STRING) + + def generate_managed_node(self, csi_node_info): + node_info = self.resource_info_manager.get_node_info(csi_node_info.name) + return ManagedNode(csi_node_info, node_info.labels) + + def remove_manage_node_label(self, node_name): + if self._is_node_should_be_removed(node_name): + logger.info(messages.REMOVE_LABEL_FROM_NODE.format(settings.MANAGE_NODE_LABEL, node_name)) + self._update_manage_node_label(node_name, None) + + def _is_node_should_be_removed(self, node_name): + return utils.is_dynamic_node_labeling_allowed() and \ + not self._is_node_has_ibm_block_csi(node_name) and \ + not self.is_node_has_host_definitions(node_name) + + def _is_node_has_ibm_block_csi(self, node_name): + csi_node_info = self.resource_info_manager.get_csi_node_info(node_name) + return csi_node_info.node_id != '' + + def is_node_has_host_definitions(self, node_name): + host_definitions_info = self.host_definition_manager.get_all_host_definitions_info_of_the_node(node_name) + return host_definitions_info != [] + + def _update_manage_node_label(self, node_name, label_value): + body = manifest_utils.get_body_manifest_for_labels(label_value) + self.k8s_api.patch_node(node_name, body) + + def generate_nodes_with_system_id(self, secret_data): + nodes_with_system_id = {} + secret_config = utils.get_secret_config(secret_data) + nodes_info = self.get_nodes_info() + for node_info in nodes_info: + nodes_with_system_id[node_info.name] = self._get_system_id_for_node(node_info, secret_config) + return nodes_with_system_id + + def get_nodes_info(self): + nodes_info = [] + for k8s_node in self.k8s_api.list_node().items: + k8s_node = self.resource_info_manager.generate_node_info(k8s_node) + nodes_info.append(k8s_node) + return nodes_info + + def _get_system_id_for_node(self, node_info, secret_config): + node_topology_labels = self.secret_manager.get_topology_labels(node_info.labels) + try: + _, system_id = get_system_info_for_topologies(secret_config, node_topology_labels) + except ValidationException: + return '' + return system_id + + def is_node_has_new_manage_node_label(self, csi_node_info, unmanaged_csi_nodes_with_driver): + return not utils.is_dynamic_node_labeling_allowed() and \ + self.is_node_has_manage_node_label(csi_node_info.name) and \ + self._is_node_is_unmanaged_and_with_csi_node(csi_node_info, unmanaged_csi_nodes_with_driver) + + def is_node_has_manage_node_label(self, node_name): + return self._is_node_has_label_in_true(node_name, settings.MANAGE_NODE_LABEL) + + def _is_node_has_label_in_true(self, node_name, label): + node_info = self.resource_info_manager.get_node_info(node_name) + return node_info.labels.get(label) == settings.TRUE_STRING + + def _is_node_is_unmanaged_and_with_csi_node(self, csi_node_info, unmanaged_csi_nodes_with_driver): + if csi_node_info.name not in NODES and csi_node_info.node_id and \ + csi_node_info.name in unmanaged_csi_nodes_with_driver: + return True + return False + + def handle_node_topologies(self, node_info, watch_event_type): + if node_info.name not in NODES or watch_event_type != settings.MODIFIED_EVENT: + return + for index, managed_secret_info in enumerate(MANAGED_SECRETS): + if not managed_secret_info.system_ids_topologies: + continue + if self.secret_manager.is_node_should_managed_on_secret_info(node_info.name, managed_secret_info): + self._remove_node_if_topology_not_match(node_info, index, managed_secret_info) + elif self.secret_manager.is_node_labels_in_system_ids_topologies(managed_secret_info.system_ids_topologies, + node_info.labels): + self._define_host_with_new_topology(node_info, index, managed_secret_info) + + def _remove_node_if_topology_not_match(self, node_info, index, managed_secret_info): + if not self.secret_manager.is_node_labels_in_system_ids_topologies(managed_secret_info.system_ids_topologies, + node_info.labels): + managed_secret_info.nodes_with_system_id.pop(node_info.name, None) + MANAGED_SECRETS[index] = managed_secret_info + + def _define_host_with_new_topology(self, node_info, index, managed_secret_info): + node_name = node_info.name + system_id = self.secret_manager.get_system_id_for_node_labels( + managed_secret_info.system_ids_topologies, node_info.labels) + managed_secret_info.nodes_with_system_id[node_name] = system_id + MANAGED_SECRETS[index] = managed_secret_info + self.definition_manager.define_node_on_all_storages(node_name) + + def update_node_io_group(self, node_info): + io_group = utils.generate_io_group_from_labels(node_info.labels) + node_name = node_info.name + try: + if io_group != NODES[node_name].io_group: + logger.info(messages.IO_GROUP_CHANGED.format(node_name, io_group, NODES[node_name].io_group)) + NODES[node_name].io_group = io_group + self.definition_manager.define_node_on_all_storages(node_name) + except KeyError: + pass diff --git a/controllers/servers/host_definer/resource_manager/resource_info.py b/controllers/servers/host_definer/resource_manager/resource_info.py new file mode 100644 index 000000000..bb0498192 --- /dev/null +++ b/controllers/servers/host_definer/resource_manager/resource_info.py @@ -0,0 +1,108 @@ +from controllers.common.csi_logger import get_stdout_logger +from controllers.servers.host_definer import settings +import controllers.common.settings as common_settings +from controllers.servers.host_definer.k8s.api import K8SApi +from controllers.servers.host_definer.utils import utils +from controllers.servers.host_definer.types import ( + NodeInfo, CsiNodeInfo, StorageClassInfo, PodInfo, HostDefinitionInfo, SecretInfo) + + +logger = get_stdout_logger() + + +class ResourceInfoManager: + def __init__(self): + self.k8s_api = K8SApi() + + def get_node_info(self, node_name): + k8s_node = self.k8s_api.read_node(node_name) + if k8s_node: + return self.generate_node_info(k8s_node) + return NodeInfo('', {}) + + def generate_node_info(self, k8s_node): + return NodeInfo(k8s_node.metadata.name, k8s_node.metadata.labels) + + def get_csi_node_info(self, node_name): + k8s_csi_node = self.k8s_api.get_csi_node(node_name) + if k8s_csi_node: + return self.generate_csi_node_info(k8s_csi_node) + return CsiNodeInfo() + + def generate_csi_node_info(self, k8s_csi_node): + csi_node_info = CsiNodeInfo() + csi_node_info.name = k8s_csi_node.metadata.name + csi_node_info.node_id = self._get_node_id_from_k8s_csi_node(k8s_csi_node) + return csi_node_info + + def _get_node_id_from_k8s_csi_node(self, k8s_csi_node): + if k8s_csi_node.spec.drivers: + for driver in k8s_csi_node.spec.drivers: + if driver.name == settings.CSI_PROVISIONER_NAME: + return driver.nodeID + return '' + + def get_storage_classes_info(self): + storage_classes_info = [] + for k8s_storage_class in self.k8s_api.list_storage_class().items: + storage_class_info = self.generate_storage_class_info(k8s_storage_class) + storage_classes_info.append(storage_class_info) + return storage_classes_info + + def generate_storage_class_info(self, k8s_storage_class): + storage_class_info = StorageClassInfo() + storage_class_info.name = k8s_storage_class.metadata.name + storage_class_info.provisioner = k8s_storage_class.provisioner + storage_class_info.parameters = k8s_storage_class.parameters + return storage_class_info + + def get_csi_pods_info(self): + pods_info = [] + k8s_pods = self.k8s_api.list_pod_for_all_namespaces(settings.DRIVER_PRODUCT_LABEL) + if not k8s_pods: + return pods_info + for k8s_pod in k8s_pods.items: + pod_info = self._generate_pod_info(k8s_pod) + pods_info.append(pod_info) + return pods_info + + def _generate_pod_info(self, k8s_pod): + pod_info = PodInfo() + pod_info.name = k8s_pod.metadata.name + pod_info.node_name = k8s_pod.spec.node_name + return pod_info + + def generate_host_definition_info(self, k8s_host_definition): + host_definition_info = HostDefinitionInfo() + host_definition_info.name = k8s_host_definition.metadata.name + host_definition_info.resource_version = utils.get_k8s_object_resource_version(k8s_host_definition) + host_definition_info.uid = k8s_host_definition.metadata.uid + host_definition_info.phase = self._get_host_definition_phase(k8s_host_definition) + host_definition_info.secret_name = self._get_attr_from_host_definition( + k8s_host_definition, settings.SECRET_NAME_FIELD) + host_definition_info.secret_namespace = self._get_attr_from_host_definition( + k8s_host_definition, settings.SECRET_NAMESPACE_FIELD) + host_definition_info.node_name = self._get_attr_from_host_definition( + k8s_host_definition, settings.NODE_NAME_FIELD) + host_definition_info.node_id = self._get_attr_from_host_definition( + k8s_host_definition, common_settings.HOST_DEFINITION_NODE_ID_FIELD) + host_definition_info.connectivity_type = self._get_attr_from_host_definition( + k8s_host_definition, settings.CONNECTIVITY_TYPE_FIELD) + return host_definition_info + + def _get_host_definition_phase(self, k8s_host_definition): + if k8s_host_definition.status: + return k8s_host_definition.status.phase + return '' + + def _get_attr_from_host_definition(self, k8s_host_definition, attribute): + if hasattr(k8s_host_definition.spec.hostDefinition, attribute): + return getattr(k8s_host_definition.spec.hostDefinition, attribute) + return '' + + def generate_k8s_secret_to_secret_info(self, k8s_secret, nodes_with_system_id={}, system_ids_topologies={}): + return SecretInfo( + k8s_secret.metadata.name, k8s_secret.metadata.namespace, nodes_with_system_id, system_ids_topologies) + + def generate_secret_info(self, secret_name, secret_namespace, nodes_with_system_id={}, system_ids_topologies={}): + return SecretInfo(secret_name, secret_namespace, nodes_with_system_id, system_ids_topologies) diff --git a/controllers/servers/host_definer/resource_manager/secret.py b/controllers/servers/host_definer/resource_manager/secret.py new file mode 100644 index 000000000..b930b5b03 --- /dev/null +++ b/controllers/servers/host_definer/resource_manager/secret.py @@ -0,0 +1,127 @@ +from controllers.common.csi_logger import get_stdout_logger +from controllers.servers.settings import SECRET_SUPPORTED_TOPOLOGIES_PARAMETER +from controllers.servers.utils import is_topology_match +import controllers.common.settings as common_settings +from controllers.servers.host_definer.globals import MANAGED_SECRETS +from controllers.servers.host_definer import settings +from controllers.servers.host_definer.utils import utils +from controllers.servers.host_definer.types import SecretInfo +import controllers.servers.host_definer.messages as messages +from controllers.servers.host_definer.k8s.api import K8SApi +from controllers.servers.host_definer.resource_manager.resource_info import ResourceInfoManager + +logger = get_stdout_logger() + + +class SecretManager: + def __init__(self): + self.k8s_api = K8SApi() + self.resource_info_manager = ResourceInfoManager() + + def is_node_should_be_managed_on_secret(self, node_name, secret_name, secret_namespace): + logger.info(messages.CHECK_NODE_SHOULD_BE_MANAGED_BY_SECRET.format(node_name, secret_name, secret_namespace)) + secret_data = self.get_secret_data(secret_name, secret_namespace) + utils.validate_secret(secret_data) + managed_secret_info, _ = self._get_managed_secret_by_name_and_namespace(secret_name, secret_namespace) + if self.is_node_should_managed_on_secret_info(node_name, managed_secret_info): + logger.info(messages.NODE_SHOULD_BE_MANAGED_ON_SECRET.format(node_name, secret_name, secret_namespace)) + return True + logger.info(messages.NODE_SHOULD_NOT_BE_MANAGED_ON_SECRET.format(node_name, secret_name, secret_namespace)) + return False + + def _get_managed_secret_by_name_and_namespace(self, secret_name, secret_namespace): + secret_info = self.resource_info_manager.generate_secret_info(secret_name, secret_namespace) + managed_secret_info, index = self.get_matching_managed_secret_info(secret_info) + return managed_secret_info, index + + def is_node_should_managed_on_secret_info(self, node_name, secret_info): + if secret_info: + nodes_with_system_id = secret_info.nodes_with_system_id + if nodes_with_system_id and nodes_with_system_id.get(node_name): + return True + if nodes_with_system_id: + return False + return True + return False + + def is_node_labels_in_system_ids_topologies(self, system_ids_topologies, node_labels): + return self.get_system_id_for_node_labels(system_ids_topologies, node_labels) != '' + + def get_system_id_for_node_labels(self, system_ids_topologies, node_labels): + node_topology_labels = self.get_topology_labels(node_labels) + for system_id, system_topologies in system_ids_topologies.items(): + if is_topology_match(system_topologies, node_topology_labels): + return system_id + return '' + + def is_topology_secret(self, secret_data): + utils.validate_secret(secret_data) + if utils.get_secret_config(secret_data): + return True + return False + + def generate_secret_system_ids_topologies(self, secret_data): + system_ids_topologies = {} + secret_config = utils.get_secret_config(secret_data) + for system_id, system_info in secret_config.items(): + try: + system_ids_topologies[system_id] = (system_info.get(SECRET_SUPPORTED_TOPOLOGIES_PARAMETER)) + except AttributeError: + system_ids_topologies[system_id] = None + return system_ids_topologies + + def is_secret(self, parameter_name): + return parameter_name.endswith(settings.SECRET_NAME_SUFFIX) and \ + parameter_name.startswith(settings.CSI_PARAMETER_PREFIX) + + def get_secret_name_and_namespace(self, storage_class_info, parameter_name): + secret_name_suffix = settings.SECRET_NAME_SUFFIX + prefix = parameter_name.split(secret_name_suffix)[0] + return (storage_class_info.parameters[parameter_name], + storage_class_info.parameters[prefix + secret_name_suffix.replace( + common_settings.NAME_FIELD, common_settings.NAMESPACE_FIELD)]) + + def add_unique_secret_info_to_list(self, secret_info, secrets_info_list): + for secret_info_in_list in secrets_info_list: + if secret_info_in_list.name == secret_info.name and \ + secret_info_in_list.namespace == secret_info.namespace: + return secrets_info_list + secrets_info_list.append(secret_info) + return secrets_info_list + + def is_secret_can_be_changed(self, secret_info, watch_event_type): + return self._is_secret_managed(secret_info) and \ + not utils.is_watch_object_type_is_delete(watch_event_type) + + def _is_secret_managed(self, secret_info): + _, index = self.get_matching_managed_secret_info(secret_info) + if index != -1: + return True + return False + + def get_matching_managed_secret_info(self, secret_info): + for index, managed_secret_info in enumerate(MANAGED_SECRETS): + if managed_secret_info.name == secret_info.name and managed_secret_info.namespace == secret_info.namespace: + return managed_secret_info, index + return secret_info, -1 + + def get_array_connection_info(self, secret_name, secret_namespace, labels): + secret_data = self.get_secret_data(secret_name, secret_namespace) + if secret_data: + node_topology_labels = self.get_topology_labels(labels) + return utils.get_array_connection_info_from_secret_data(secret_data, node_topology_labels) + return {} + + def get_secret_data(self, secret_name, secret_namespace): + logger.info(messages.READ_SECRET.format(secret_name, secret_namespace)) + secret_data = self.k8s_api.get_secret_data(secret_name, secret_namespace) + if secret_data: + return utils.change_decode_base64_secret_config(secret_data) + return {} + + def get_topology_labels(self, labels): + topology_labels = {} + for label in labels: + if utils.is_topology_label(label): + topology_labels[label] = labels[label] + return topology_labels diff --git a/controllers/servers/host_definer/resource_manager/storage_class.py b/controllers/servers/host_definer/resource_manager/storage_class.py new file mode 100644 index 000000000..5f271902e --- /dev/null +++ b/controllers/servers/host_definer/resource_manager/storage_class.py @@ -0,0 +1,9 @@ +from controllers.common.csi_logger import get_stdout_logger +from controllers.servers.host_definer import settings + +logger = get_stdout_logger() + + +class StorageClassManager: + def is_storage_class_has_csi_as_a_provisioner(self, storage_class_info): + return storage_class_info.provisioner == settings.CSI_PROVISIONER_NAME diff --git a/controllers/servers/host_definer/types.py b/controllers/servers/host_definer/types.py index 0e5277106..90b40ec6c 100644 --- a/controllers/servers/host_definer/types.py +++ b/controllers/servers/host_definer/types.py @@ -1,6 +1,6 @@ from dataclasses import dataclass, field from controllers.servers.csi.controller_types import ArrayConnectionInfo -from controllers.servers.host_definer import utils +from controllers.servers.host_definer.utils import utils @dataclass diff --git a/controllers/servers/host_definer/utils.py b/controllers/servers/host_definer/utils.py deleted file mode 100644 index 0d7562a0a..000000000 --- a/controllers/servers/host_definer/utils.py +++ /dev/null @@ -1,13 +0,0 @@ -import controllers.servers.host_definer.settings as host_definer_settings -import controllers.common.settings as common_settings - - -def generate_io_group_from_labels(labels): - io_group = '' - for io_group_index in range(host_definer_settings.POSSIBLE_NUMBER_OF_IO_GROUP): - label_content = labels.get(common_settings.IO_GROUP_LABEL_PREFIX + str(io_group_index)) - if label_content == host_definer_settings.TRUE_STRING: - if io_group: - io_group += common_settings.IO_GROUP_DELIMITER - io_group += str(io_group_index) - return io_group diff --git a/controllers/servers/host_definer/utils/__init__.py b/controllers/servers/host_definer/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/controllers/servers/host_definer/utils/manifest_utils.py b/controllers/servers/host_definer/utils/manifest_utils.py new file mode 100644 index 000000000..37e123c5f --- /dev/null +++ b/controllers/servers/host_definer/utils/manifest_utils.py @@ -0,0 +1,58 @@ +from controllers.servers.host_definer import settings +import controllers.common.settings as common_settings + + +def get_host_definition_manifest(host_definition_info, response, node_id): + manifest = generate_host_definition_response_fields_manifest(host_definition_info.name, response) + manifest[settings.API_VERSION] = settings.CSI_IBM_API_VERSION + manifest[settings.KIND] = settings.HOST_DEFINITION_KIND + manifest[settings.SPEC][settings.HOST_DEFINITION_FIELD][settings.NODE_NAME_FIELD] = host_definition_info.node_name + manifest[settings.SPEC][settings.HOST_DEFINITION_FIELD][common_settings.HOST_DEFINITION_NODE_ID_FIELD] = node_id + manifest[settings.SPEC][settings.HOST_DEFINITION_FIELD][settings.SECRET_NAME_FIELD] = \ + host_definition_info.secret_name + manifest[settings.SPEC][settings.HOST_DEFINITION_FIELD][settings.SECRET_NAMESPACE_FIELD] = \ + host_definition_info.secret_namespace + return manifest + + +def get_host_definition_status_manifest(host_definition_phase): + return { + settings.STATUS: { + settings.PHASE: host_definition_phase, + } + } + + +def get_body_manifest_for_labels(label_value): + return { + settings.METADATA: { + settings.LABELS: { + settings.MANAGE_NODE_LABEL: label_value} + } + } + + +def get_finalizer_manifest(host_definition_name, finalizers): + return { + settings.METADATA: { + common_settings.NAME_FIELD: host_definition_name, + settings.FINALIZERS: finalizers, + } + } + + +def generate_host_definition_response_fields_manifest(host_definition_name, response): + return { + settings.METADATA: { + common_settings.NAME_FIELD: host_definition_name, + }, + settings.SPEC: { + settings.HOST_DEFINITION_FIELD: { + settings.CONNECTIVITY_TYPE_FIELD: response.connectivity_type, + settings.PORTS_FIELD: response.ports, + settings.NODE_NAME_ON_STORAGE_FIELD: response.node_name_on_storage, + settings.IO_GROUP_FIELD: response.io_group, + settings.MANAGEMENT_ADDRESS_FIELD: response.management_address + }, + }, + } diff --git a/controllers/servers/host_definer/utils/utils.py b/controllers/servers/host_definer/utils/utils.py new file mode 100644 index 000000000..9f2bfb05d --- /dev/null +++ b/controllers/servers/host_definer/utils/utils.py @@ -0,0 +1,154 @@ +import os +import ast +import base64 +import json +import random +import string +from munch import Munch + +from controllers.common.csi_logger import get_stdout_logger +from controllers.servers.utils import validate_secrets, get_array_connection_info_from_secrets +from controllers.servers.errors import ValidationException +import controllers.servers.host_definer.settings as host_definer_settings +import controllers.common.settings as common_settings +from controllers.servers.host_definer import settings + + +logger = get_stdout_logger() + + +def generate_io_group_from_labels(labels): + io_group = '' + for io_group_index in range(host_definer_settings.POSSIBLE_NUMBER_OF_IO_GROUP): + label_content = labels.get(common_settings.IO_GROUP_LABEL_PREFIX + str(io_group_index)) + if label_content == host_definer_settings.TRUE_STRING: + if io_group: + io_group += common_settings.IO_GROUP_DELIMITER + io_group += str(io_group_index) + return io_group + + +def get_k8s_object_resource_version(k8s_object): + if hasattr(k8s_object.metadata, 'resource_version'): + return k8s_object.metadata.resource_version + return k8s_object.metadata.resourceVersion + + +def change_decode_base64_secret_config(secret_data): + if settings.SECRET_CONFIG_FIELD in secret_data.keys(): + secret_data[settings.SECRET_CONFIG_FIELD] = _decode_base64_to_dict( + secret_data[settings.SECRET_CONFIG_FIELD]) + return secret_data + + +def _decode_base64_to_dict(content_with_base64): + decoded_string_content = decode_base64_to_string(content_with_base64) + my_dict_again = ast.literal_eval(decoded_string_content) + return my_dict_again + + +def get_secret_config(secret_data): + secret_data = _convert_secret_config_to_dict(secret_data) + return secret_data.get(settings.SECRET_CONFIG_FIELD, {}) + + +def _convert_secret_config_to_dict(secret_data): + if settings.SECRET_CONFIG_FIELD in secret_data.keys(): + if type(secret_data[settings.SECRET_CONFIG_FIELD]) is str: + secret_data[settings.SECRET_CONFIG_FIELD] = json.loads(secret_data[settings.SECRET_CONFIG_FIELD]) + return secret_data + + +def munch(watch_event): + return Munch.fromDict(watch_event) + + +def loop_forever(): + return True + + +def validate_secret(secret_data): + secret_data = _convert_secret_config_to_string(secret_data) + try: + validate_secrets(secret_data) + except ValidationException as ex: + logger.error(str(ex)) + + +def get_prefix(): + return os.getenv(settings.PREFIX_ENV_VAR) + + +def get_connectivity_type_from_user(connectivity_type_label_on_node): + if connectivity_type_label_on_node in settings.SUPPORTED_CONNECTIVITY_TYPES: + return connectivity_type_label_on_node + return os.getenv(settings.CONNECTIVITY_ENV_VAR) + + +def is_topology_label(label): + for prefix in settings.TOPOLOGY_PREFIXES: + if label.startswith(prefix): + return True + return False + + +def get_array_connection_info_from_secret_data(secret_data, labels): + try: + secret_data = _convert_secret_config_to_string(secret_data) + array_connection_info = get_array_connection_info_from_secrets(secret_data, labels) + return decode_array_connectivity_info(array_connection_info) + except ValidationException as ex: + logger.error(str(ex)) + return None + + +def _convert_secret_config_to_string(secret_data): + if settings.SECRET_CONFIG_FIELD in secret_data.keys(): + if type(secret_data[settings.SECRET_CONFIG_FIELD]) is dict: + secret_data[settings.SECRET_CONFIG_FIELD] = json.dumps(secret_data[settings.SECRET_CONFIG_FIELD]) + return secret_data + + +def decode_array_connectivity_info(array_connection_info): + array_connection_info.array_addresses = _decode_list_base64_to_list_string( + array_connection_info.array_addresses) + array_connection_info.user = decode_base64_to_string(array_connection_info.user) + array_connection_info.password = decode_base64_to_string(array_connection_info.password) + return array_connection_info + + +def _decode_list_base64_to_list_string(list_with_base64): + for index, base64_content in enumerate(list_with_base64): + list_with_base64[index] = decode_base64_to_string(base64_content) + return list_with_base64 + + +def decode_base64_to_string(content_with_base64): + try: + base64_bytes = content_with_base64.encode('ascii') + decoded_string_in_bytes = base64.b64decode(base64_bytes) + return decoded_string_in_bytes.decode('ascii') + except Exception: + return content_with_base64 + + +def get_random_string(): + return ''.join(random.choices(string.ascii_lowercase + string.digits, k=20)) + + +def is_watch_object_type_is_delete(watch_object_type): + return watch_object_type == settings.DELETED_EVENT + + +def is_host_definer_can_delete_hosts(): + return os.getenv(settings.ALLOW_DELETE_ENV_VAR) == settings.TRUE_STRING + + +def is_dynamic_node_labeling_allowed(): + return os.getenv(settings.DYNAMIC_NODE_LABELING_ENV_VAR) == settings.TRUE_STRING + + +def get_action(phase): + if phase == settings.PENDING_CREATION_PHASE: + return settings.DEFINE_ACTION + return settings.UNDEFINE_ACTION diff --git a/controllers/servers/host_definer/watcher/csi_node_watcher.py b/controllers/servers/host_definer/watcher/csi_node_watcher.py index 7c47eb58c..236f669d4 100644 --- a/controllers/servers/host_definer/watcher/csi_node_watcher.py +++ b/controllers/servers/host_definer/watcher/csi_node_watcher.py @@ -1,10 +1,11 @@ -import time from threading import Thread from controllers.common.csi_logger import get_stdout_logger -from controllers.servers.host_definer.watcher.watcher_helper import Watcher, NODES, MANAGED_SECRETS +from controllers.servers.host_definer.globals import MANAGED_SECRETS, NODES +from controllers.servers.host_definer.watcher.watcher_helper import Watcher import controllers.servers.host_definer.messages as messages from controllers.servers.host_definer import settings +from controllers.servers.host_definer.utils import utils logger = get_stdout_logger() @@ -12,18 +13,17 @@ class CsiNodeWatcher(Watcher): def add_initial_csi_nodes(self): - csi_nodes_info = self._get_csi_nodes_info_with_driver() + csi_nodes_info = self.csi_node.get_csi_nodes_info_with_driver() for csi_node_info in csi_nodes_info: - if self._is_host_can_be_defined(csi_node_info.name): - self._add_node_to_nodes(csi_node_info) + if self.node_manager.is_node_can_be_defined(csi_node_info.name): + self.node_manager.add_node_to_nodes(csi_node_info) def watch_csi_nodes_resources(self): - while self._loop_forever(): - resource_version = self._get_k8s_object_resource_version(self.csi_nodes_api.get()) - stream = self.csi_nodes_api.watch(resource_version=resource_version, timeout=5) + while utils.loop_forever(): + stream = self.k8s_api.get_csi_node_stream() for watch_event in stream: - watch_event = self._munch(watch_event) - csi_node_info = self._generate_csi_node_info(watch_event.object) + watch_event = utils.munch(watch_event) + csi_node_info = self.resource_info_manager.generate_csi_node_info(watch_event.object) if (watch_event.type == settings.DELETED_EVENT) and (csi_node_info.name in NODES): self._handle_deleted_csi_node_pod(csi_node_info) elif watch_event.type == settings.MODIFIED_EVENT: @@ -31,81 +31,43 @@ def watch_csi_nodes_resources(self): def _handle_modified_csi_node(self, csi_node_info): if self._is_new_csi_node(csi_node_info): - self._add_node_to_nodes(csi_node_info) - self._define_host_on_all_storages(csi_node_info.name) + self.node_manager.add_node_to_nodes(csi_node_info) + self.definition_manager.define_node_on_all_storages(csi_node_info.name) elif csi_node_info.name in NODES: self._handle_deleted_csi_node_pod(csi_node_info) def _is_new_csi_node(self, csi_node_info): - return csi_node_info.node_id and self._is_host_can_be_defined(csi_node_info.name) and \ + return csi_node_info.node_id and self.node_manager.is_node_can_be_defined(csi_node_info.name) and \ csi_node_info.name not in NODES def _handle_deleted_csi_node_pod(self, csi_node_info): - if self._is_node_has_manage_node_label(csi_node_info.name): + if self.node_manager.is_node_has_manage_node_label(csi_node_info.name): remove_host_thread = Thread(target=self._undefine_host_when_node_pod_is_deleted, args=(csi_node_info,)) remove_host_thread.start() def _undefine_host_when_node_pod_is_deleted(self, csi_node_info): node_name = csi_node_info.name - if self._is_host_part_of_update(node_name): + if self.csi_node.is_host_part_of_update(node_name): self._create_definitions_when_csi_node_changed(csi_node_info) - elif self._is_host_definer_can_delete_hosts() and \ - not self._is_node_has_forbid_deletion_label(node_name): - self._undefine_hosts(csi_node_info.name) + elif utils.is_host_definer_can_delete_hosts() and \ + not self.node_manager.is_node_has_forbid_deletion_label(node_name): + self._undefine_all_the_definitions_of_a_node(csi_node_info.name) else: NODES.pop(node_name, None) - def _is_host_part_of_update(self, worker): - logger.info(messages.CHECK_IF_NODE_IS_PART_OF_UPDATE.format(worker)) - daemon_set_name = self._wait_until_all_daemon_set_pods_are_up_to_date() - if daemon_set_name: - return self._is_csi_node_pod_running_on_worker(worker, daemon_set_name) - return False - - def _is_csi_node_pod_running_on_worker(self, worker, daemon_set_name): - logger.info(messages.CHECK_IF_CSI_NODE_POD_IS_RUNNING.format(worker)) - csi_pods_info = self._get_csi_pods_info() - for pod_info in csi_pods_info: - if (pod_info.node_name == worker) and (daemon_set_name in pod_info.name): - return True - return False - - def _wait_until_all_daemon_set_pods_are_up_to_date(self): - csi_daemon_set = self._get_csi_daemon_set() - if not csi_daemon_set: - return None - status = csi_daemon_set.status - while status.updated_number_scheduled != status.desired_number_scheduled: - logger.info(messages.UPDATED_CSI_NODE_VS_DESIRED.format( - status.updated_number_scheduled, status.desired_number_scheduled)) - if status.desired_number_scheduled == 0: - return None - csi_daemon_set = self._get_csi_daemon_set() - if not csi_daemon_set: - return None - status = csi_daemon_set.status - time.sleep(0.5) - return csi_daemon_set.metadata.name - def _create_definitions_when_csi_node_changed(self, csi_node_info): for secret_info in MANAGED_SECRETS: secret_name, secret_namespace = secret_info.name, secret_info.namespace - host_definition_info = self._get_matching_host_definition_info( + host_definition_info = self.host_definition_manager.get_matching_host_definition_info( csi_node_info.name, secret_name, secret_namespace) - if host_definition_info: - if self._is_node_id_changed(host_definition_info.node_id, csi_node_info.node_id): - logger.info(messages.NODE_ID_WAS_CHANGED.format(csi_node_info.name, - host_definition_info.node_id, csi_node_info.node_id)) - NODES[csi_node_info.name] = self._generate_managed_node(csi_node_info) - self._create_definition(host_definition_info) - - def _is_node_id_changed(self, host_definition_node_id, csi_node_node_id): - return host_definition_node_id != csi_node_node_id \ - and host_definition_node_id and csi_node_node_id - - def _undefine_hosts(self, node_name): - for secret_info in MANAGED_SECRETS: - host_definition_info = self._get_host_definition_info_from_secret_and_node_name(node_name, secret_info) - self._delete_definition(host_definition_info) - self._remove_manage_node_label(node_name) + if host_definition_info and self.csi_node.is_node_id_changed( + host_definition_info.node_id, csi_node_info.node_id): + logger.info(messages.NODE_ID_WAS_CHANGED.format(csi_node_info.name, + host_definition_info.node_id, csi_node_info.node_id)) + NODES[csi_node_info.name] = self.node_manager.generate_managed_node(csi_node_info) + self.definition_manager.create_definition(host_definition_info) + + def _undefine_all_the_definitions_of_a_node(self, node_name): + self.definition_manager.undefine_node_definitions(node_name) + self.node_manager.remove_manage_node_label(node_name) NODES.pop(node_name, None) diff --git a/controllers/servers/host_definer/watcher/host_definition_watcher.py b/controllers/servers/host_definer/watcher/host_definition_watcher.py index 652352449..7fd48084a 100644 --- a/controllers/servers/host_definer/watcher/host_definition_watcher.py +++ b/controllers/servers/host_definer/watcher/host_definition_watcher.py @@ -1,12 +1,12 @@ from threading import Thread from time import sleep +from controllers.servers.host_definer.utils import utils import controllers.servers.host_definer.messages as messages from controllers.common.csi_logger import get_stdout_logger from controllers.servers.host_definer.watcher.watcher_helper import Watcher from controllers.servers.host_definer.types import DefineHostResponse from controllers.servers.host_definer import settings -import controllers.common.settings as common_settings logger = get_stdout_logger() @@ -15,22 +15,19 @@ class HostDefinitionWatcher(Watcher): def watch_host_definitions_resources(self): self._watch_host_definition_with_timeout('') - while self._loop_forever(): - resource_version = self._get_k8s_object_resource_version(self.host_definitions_api.get()) + while utils.loop_forever(): + resource_version = utils.get_k8s_object_resource_version(self.k8s_api.list_host_definition()) self._watch_host_definition_with_timeout(resource_version) def _watch_host_definition_with_timeout(self, resource_version, timeout=5): - stream = self.host_definitions_api.watch(resource_version=resource_version, timeout=timeout) + stream = self.k8s_api.get_host_definition_stream(resource_version, timeout) for watch_event in stream: - watch_event = self._munch(watch_event) - host_definition_info = self._generate_host_definition_info(watch_event.object) - if self._is_host_definition_in_pending_phase(host_definition_info.phase) and \ - watch_event.type != settings.DELETED_EVENT: + watch_event = utils.munch(watch_event) + host_definition_info = self.resource_info_manager.generate_host_definition_info(watch_event.object) + if self.host_definition_manager.is_host_definition_in_pending_phase(host_definition_info.phase) and \ + not utils.is_watch_object_type_is_delete(watch_event.type): self._define_host_definition_after_pending_state(host_definition_info) - def _is_host_definition_in_pending_phase(self, phase): - return phase.startswith(settings.PENDING_PREFIX) - def _define_host_definition_after_pending_state(self, host_definition_info): logger.info(messages.FOUND_HOST_DEFINITION_IN_PENDING_STATE.format(host_definition_info.name)) remove_host_thread = Thread(target=self._define_host_using_exponential_backoff, @@ -44,7 +41,7 @@ def _define_host_using_exponential_backoff(self, host_definition_info): while retries > 0: logger.info(messages.VERIFY_HOST_DEFINITION_USING_EXPONENTIAL_BACKOFF.format( host_definition_info.name, retries)) - if self._is_host_definition_not_pending(host_definition_info) and \ + if self.host_definition_manager.is_host_definition_not_pending(host_definition_info) and \ retries != settings.HOST_DEFINITION_PENDING_RETRIES: logger.info(messages.HOST_DEFINITION_IS_NOT_PENDING.format(host_definition_info.name)) return @@ -53,84 +50,29 @@ def _define_host_using_exponential_backoff(self, host_definition_info): delay_in_seconds *= backoff_in_seconds sleep(delay_in_seconds) - self._set_host_definition_phase_to_error(host_definition_info) - - def _is_host_definition_not_pending(self, host_definition_info): - current_host_definition_info_on_cluster = self._get_matching_host_definition_info( - host_definition_info.node_name, host_definition_info.secret_name, host_definition_info.secret_namespace) - return not current_host_definition_info_on_cluster or \ - current_host_definition_info_on_cluster.phase == settings.READY_PHASE + self.host_definition_manager.set_host_definition_phase_to_error(host_definition_info) def _handle_pending_host_definition(self, host_definition_info): response = DefineHostResponse() phase = host_definition_info.phase - action = self._get_action(phase) + action = utils.get_action(phase) if phase == settings.PENDING_CREATION_PHASE: - response = self._define_host_after_pending(host_definition_info) + response = self.definition_manager.define_host_after_pending(host_definition_info) elif self._is_pending_for_deletion_need_to_be_handled(phase, host_definition_info.node_name): - response = self._undefine_host_after_pending(host_definition_info) + response = self.definition_manager.undefine_host_after_pending(host_definition_info) self._handle_message_from_storage( host_definition_info, response.error_message, action) - def _get_action(self, phase): - if phase == settings.PENDING_CREATION_PHASE: - return settings.DEFINE_ACTION - return settings.UNDEFINE_ACTION - - def _define_host_after_pending(self, host_definition_info): - response = DefineHostResponse() - if self._is_node_should_be_managed_on_secret( - host_definition_info.node_name, host_definition_info.secret_name, - host_definition_info.secret_namespace): - response = self._define_host(host_definition_info) - self._update_host_definition_from_storage_response(host_definition_info.name, response) - else: - self._delete_host_definition(host_definition_info.name) - return response - - def _update_host_definition_from_storage_response(self, host_definition_name, response): - logger.info(messages.UPDATE_HOST_DEFINITION_FIELDS_FROM_STORAGE.format(host_definition_name, response)) - host_definition_manifest = self._generate_host_definition_manifest(host_definition_name, response) - self._patch_host_definition(host_definition_manifest) - - def _generate_host_definition_manifest(self, host_definition_name, response): - return { - settings.METADATA: { - common_settings.NAME_FIELD: host_definition_name, - }, - settings.SPEC: { - settings.HOST_DEFINITION_FIELD: { - settings.CONNECTIVITY_TYPE_FIELD: response.connectivity_type, - settings.PORTS_FIELD: response.ports, - settings.NODE_NAME_ON_STORAGE_FIELD: response.node_name_on_storage, - settings.IO_GROUP_FIELD: response.io_group - }, - }, - } - - def _undefine_host_after_pending(self, host_definition_info): - response = DefineHostResponse() - if self._is_node_should_be_managed_on_secret( - host_definition_info.node_name, host_definition_info.secret_name, - host_definition_info.secret_namespace): - response = self._undefine_host(host_definition_info) - return response - def _handle_message_from_storage(self, host_definition_info, error_message, action): phase = host_definition_info.phase if error_message: - self._create_k8s_event_for_host_definition( - host_definition_info, str(error_message), - action, settings.FAILED_MESSAGE_TYPE) + self.host_definition_manager.create_k8s_event_for_host_definition(host_definition_info, str(error_message), + action, settings.FAILED_MESSAGE_TYPE) elif phase == settings.PENDING_CREATION_PHASE: - self._set_host_definition_status_to_ready(host_definition_info) + self.host_definition_manager.set_host_definition_status_to_ready(host_definition_info) elif self._is_pending_for_deletion_need_to_be_handled(phase, host_definition_info.node_name): - self._delete_host_definition(host_definition_info.name) - self._remove_manage_node_label(host_definition_info.node_name) + self.host_definition_manager.delete_host_definition(host_definition_info.name) + self.node_manager.remove_manage_node_label(host_definition_info.node_name) def _is_pending_for_deletion_need_to_be_handled(self, phase, node_name): - return phase == settings.PENDING_DELETION_PHASE and self._is_host_can_be_undefined(node_name) - - def _set_host_definition_phase_to_error(self, host_definition_info): - logger.info(messages.SET_HOST_DEFINITION_PHASE_TO_ERROR.format(host_definition_info.name)) - self._set_host_definition_status(host_definition_info.name, settings.ERROR_PHASE) + return phase == settings.PENDING_DELETION_PHASE and self.node_manager.is_node_can_be_undefined(node_name) diff --git a/controllers/servers/host_definer/watcher/node_watcher.py b/controllers/servers/host_definer/watcher/node_watcher.py index 354d3a022..acb503db2 100644 --- a/controllers/servers/host_definer/watcher/node_watcher.py +++ b/controllers/servers/host_definer/watcher/node_watcher.py @@ -1,10 +1,7 @@ -from kubernetes import watch - from controllers.common.csi_logger import get_stdout_logger -from controllers.servers.utils import is_topology_match -from controllers.servers.host_definer.watcher.watcher_helper import NODES, Watcher, MANAGED_SECRETS +from controllers.servers.host_definer.watcher.watcher_helper import Watcher from controllers.servers.host_definer import settings -from controllers.servers.host_definer import utils +from controllers.servers.host_definer.utils import utils from controllers.servers.host_definer import messages logger = get_stdout_logger() @@ -13,36 +10,45 @@ class NodeWatcher(Watcher): def add_initial_nodes(self): - nodes_info = self._get_nodes_info() + nodes_info = self.node_manager.get_nodes_info() for node_info in nodes_info: node_name = node_info.name - csi_node_info = self._get_csi_node_info(node_name) + csi_node_info = self.resource_info_manager.get_csi_node_info(node_name) if self._is_csi_node_pod_deleted_while_host_definer_was_down(csi_node_info): logger.info(messages.CSI_NODE_POD_DELETED_WHILE_HOST_DEFINER_WAS_DOWN.format(node_name)) self._delete_host_definitions(node_name) - self._remove_manage_node_label(node_name) + self.node_manager.remove_manage_node_label(node_name) if self._is_unmanaged_csi_node_has_driver(csi_node_info): logger.info(messages.DETECTED_UNMANAGED_CSI_NODE_WITH_IBM_BLOCK_CSI_DRIVER.format(csi_node_info.name)) unmanaged_csi_nodes_with_driver.add(csi_node_info.name) def _is_csi_node_pod_deleted_while_host_definer_was_down(self, csi_node_info): - return self._is_node_has_manage_node_label(csi_node_info.name) and \ - self._is_node_has_host_definitions(csi_node_info.name) and not csi_node_info.node_id + if self.node_manager.is_node_has_manage_node_label(csi_node_info.name) and \ + self.node_manager.is_node_has_host_definitions(csi_node_info.name) and not csi_node_info.node_id: + return True + return False + + def _delete_host_definitions(self, node_name): + if not self.node_manager.is_node_can_be_undefined(node_name): + return + host_definitions_info = self.host_definition_manager.get_all_host_definitions_info_of_the_node(node_name) + for host_definition_info in host_definitions_info: + self.definition_manager.delete_definition(host_definition_info) + self.node_manager.remove_manage_node_label(node_name) def watch_nodes_resources(self): - while self._loop_forever(): - resource_version = self._get_k8s_object_resource_version(self.core_api.list_node()) - stream = watch.Watch().stream(self.core_api.list_node, resource_version=resource_version, timeout_seconds=5) + while utils.loop_forever(): + stream = self.k8s_api.get_node_stream() for watch_event in stream: - watch_event = self._munch(watch_event) + watch_event = utils.munch(watch_event) node_name = watch_event.object.metadata.name - csi_node_info = self._get_csi_node_info(node_name) - node_info = self._generate_node_info(watch_event.object) + csi_node_info = self.resource_info_manager.get_csi_node_info(node_name) + node_info = self.resource_info_manager.generate_node_info(watch_event.object) self._add_new_unmanaged_nodes_with_ibm_csi_driver(watch_event, csi_node_info) self._define_new_managed_node(watch_event, node_name, csi_node_info) - self._handle_node_topologies(node_info, watch_event) - self._update_io_group(node_info) + self.node_manager.handle_node_topologies(node_info, watch_event.type) + self.node_manager.update_node_io_group(node_info) def _add_new_unmanaged_nodes_with_ibm_csi_driver(self, watch_event, csi_node_info): if watch_event.type in settings.MODIFIED_EVENT and \ @@ -51,71 +57,12 @@ def _add_new_unmanaged_nodes_with_ibm_csi_driver(self, watch_event, csi_node_inf unmanaged_csi_nodes_with_driver.add(csi_node_info.name) def _is_unmanaged_csi_node_has_driver(self, csi_node_info): - return csi_node_info.node_id and not self._is_host_can_be_defined(csi_node_info.name) + return csi_node_info.node_id and not self.node_manager.is_node_can_be_defined(csi_node_info.name) def _define_new_managed_node(self, watch_event, node_name, csi_node_info): if watch_event.type == settings.MODIFIED_EVENT and \ - self._is_node_has_new_manage_node_label(csi_node_info): + self.node_manager.is_node_has_new_manage_node_label(csi_node_info, unmanaged_csi_nodes_with_driver): logger.info(messages.DETECTED_NEW_MANAGED_CSI_NODE.format(node_name)) - self._add_node_to_nodes(csi_node_info) - self._define_host_on_all_storages(node_name) + self.node_manager.add_node_to_nodes(csi_node_info) + self.definition_manager.define_node_on_all_storages(node_name) unmanaged_csi_nodes_with_driver.remove(csi_node_info.name) - - def _delete_host_definitions(self, node_name): - if not self._is_host_can_be_undefined(node_name): - return - host_definitions_info = self._get_all_node_host_definitions_info(node_name) - for host_definition_info in host_definitions_info: - self._delete_definition(host_definition_info) - self._remove_manage_node_label(node_name) - - def _is_node_has_new_manage_node_label(self, csi_node_info): - return not self._is_dynamic_node_labeling_allowed() and \ - self._is_node_has_manage_node_label(csi_node_info.name) and \ - self._is_node_with_csi_ibm_csi_node_and_is_not_managed(csi_node_info) - - def _is_node_with_csi_ibm_csi_node_and_is_not_managed(self, csi_node_info): - return csi_node_info.name not in NODES and csi_node_info.node_id and \ - csi_node_info.name in unmanaged_csi_nodes_with_driver - - def _handle_node_topologies(self, node_info, watch_event): - if node_info.name not in NODES or watch_event.type != settings.MODIFIED_EVENT: - return - for index, managed_secret_info in enumerate(MANAGED_SECRETS): - if not managed_secret_info.system_ids_topologies: - continue - if self._is_node_should_managed_on_secret_info(node_info.name, managed_secret_info): - self._remove_node_if_topology_not_match(node_info, index, managed_secret_info) - elif self._is_node_in_system_ids_topologies(managed_secret_info.system_ids_topologies, node_info.labels): - self._define_host_with_new_topology(node_info, index, managed_secret_info) - - def _define_host_with_new_topology(self, node_info, index, managed_secret_info): - node_name = node_info.name - system_id = self._get_system_id_for_node_labels( - managed_secret_info.system_ids_topologies, node_info.labels) - managed_secret_info.nodes_with_system_id[node_name] = system_id - MANAGED_SECRETS[index] = managed_secret_info - self._define_host_on_all_storages(node_name) - - def _remove_node_if_topology_not_match(self, node_info, index, managed_secret_info): - if not self._is_node_in_system_ids_topologies(managed_secret_info.system_ids_topologies, node_info.labels): - managed_secret_info.nodes_with_system_id.pop(node_info.name, None) - MANAGED_SECRETS[index] = managed_secret_info - - def _is_node_in_system_ids_topologies(self, system_ids_topologies, node_labels): - return self._get_system_id_for_node_labels(system_ids_topologies, node_labels) != '' - - def _get_system_id_for_node_labels(self, system_ids_topologies, node_labels): - topology_labels = self._get_topology_labels(node_labels) - for system_id, system_topologies in system_ids_topologies.items(): - if is_topology_match(system_topologies, topology_labels): - return system_id - return '' - - def _update_io_group(self, node_info): - io_group = utils.generate_io_group_from_labels(node_info.labels) - node_name = node_info.name - if node_name in NODES and io_group != NODES[node_name].io_group: - logger.info(messages.IO_GROUP_CHANGED.format(node_name, io_group, NODES[node_name].io_group)) - NODES[node_name].io_group = io_group - self._define_host_on_all_storages(node_name) diff --git a/controllers/servers/host_definer/watcher/secret_watcher.py b/controllers/servers/host_definer/watcher/secret_watcher.py index 0b1b7fe22..7a3570553 100644 --- a/controllers/servers/host_definer/watcher/secret_watcher.py +++ b/controllers/servers/host_definer/watcher/secret_watcher.py @@ -1,10 +1,8 @@ -from kubernetes import watch - import controllers.servers.host_definer.messages as messages from controllers.common.csi_logger import get_stdout_logger -from controllers.servers.host_definer.watcher.watcher_helper import Watcher, MANAGED_SECRETS -from controllers.servers.host_definer.types import SecretInfo -from controllers.servers.host_definer import settings +from controllers.servers.host_definer.globals import MANAGED_SECRETS +from controllers.servers.host_definer.watcher.watcher_helper import Watcher +from controllers.servers.host_definer.utils import utils logger = get_stdout_logger() @@ -12,37 +10,34 @@ class SecretWatcher(Watcher): def watch_secret_resources(self): - while self._loop_forever(): - resource_version = self._get_k8s_object_resource_version(self.core_api.list_secret_for_all_namespaces()) - stream = watch.Watch().stream(self.core_api.list_secret_for_all_namespaces, - resource_version=resource_version, timeout_seconds=5) + while utils.loop_forever(): + stream = self.k8s_api.get_secret_stream() for watch_event in stream: - watch_event = self._munch(watch_event) - secret_info = self._generate_k8s_secret_to_secret_info(watch_event.object) - if self._is_secret_managed(secret_info): - secret_data = self._change_decode_base64_secret_config(watch_event.object.data) - if self._is_topology_secret(secret_data): - nodes_with_system_id = self._generate_nodes_with_system_id(secret_data) - system_ids_topologies = self._generate_secret_system_ids_topologies(secret_data) - secret_info = self._generate_k8s_secret_to_secret_info( - watch_event.object, nodes_with_system_id, system_ids_topologies) - else: - secret_info = self._generate_k8s_secret_to_secret_info(watch_event.object) - self._handle_storage_class_secret(secret_info, watch_event.type) - - def _generate_k8s_secret_to_secret_info(self, k8s_secret, nodes_with_system_id={}, system_ids_topologies={}): - return SecretInfo( - k8s_secret.metadata.name, k8s_secret.metadata.namespace, nodes_with_system_id, system_ids_topologies) - - def _handle_storage_class_secret(self, secret_info, watch_event_type): - managed_secret_info, index = self._get_matching_managed_secret_info(secret_info) - if watch_event_type in (settings.ADDED_EVENT, settings.MODIFIED_EVENT) and \ - managed_secret_info.managed_storage_classes > 0: + watch_event = utils.munch(watch_event) + secret_info = self.resource_info_manager.generate_k8s_secret_to_secret_info(watch_event.object) + if self.secret_manager.is_secret_can_be_changed(secret_info, watch_event.type): + secret_info = self._get_secret_info(watch_event.object) + self._handle_storage_class_secret(secret_info) + + def _get_secret_info(self, watch_event_object): + secret_data = utils.change_decode_base64_secret_config(watch_event_object.data) + if self.secret_manager.is_topology_secret(secret_data): + nodes_with_system_id = self.node_manager.generate_nodes_with_system_id(secret_data) + system_ids_topologies = self.secret_manager.generate_secret_system_ids_topologies(secret_data) + secret_info = self.resource_info_manager.generate_k8s_secret_to_secret_info( + watch_event_object, nodes_with_system_id, system_ids_topologies) + else: + secret_info = self.resource_info_manager.generate_k8s_secret_to_secret_info(watch_event_object) + return secret_info + + def _handle_storage_class_secret(self, secret_info): + managed_secret_info, index = self.secret_manager.get_matching_managed_secret_info(secret_info) + if managed_secret_info.managed_storage_classes > 0: secret_info.managed_storage_classes = managed_secret_info.managed_storage_classes MANAGED_SECRETS[index] = secret_info self._define_host_after_watch_event(secret_info) def _define_host_after_watch_event(self, secret_info): logger.info(messages.SECRET_HAS_BEEN_MODIFIED.format(secret_info.name, secret_info.namespace)) - host_definition_info = self._get_host_definition_info_from_secret(secret_info) - self._define_nodes(host_definition_info) + host_definition_info = self.host_definition_manager.get_host_definition_info_from_secret(secret_info) + self.definition_manager.define_nodes(host_definition_info) diff --git a/controllers/servers/host_definer/watcher/storage_class_watcher.py b/controllers/servers/host_definer/watcher/storage_class_watcher.py index 9ea9a08be..89d79ab5f 100644 --- a/controllers/servers/host_definer/watcher/storage_class_watcher.py +++ b/controllers/servers/host_definer/watcher/storage_class_watcher.py @@ -1,11 +1,9 @@ -import json -from kubernetes import watch - import controllers.servers.host_definer.messages as messages from controllers.common.csi_logger import get_stdout_logger -from controllers.servers.host_definer.watcher.watcher_helper import Watcher, MANAGED_SECRETS +from controllers.servers.host_definer.globals import MANAGED_SECRETS +from controllers.servers.host_definer.watcher.watcher_helper import Watcher from controllers.servers.host_definer import settings -import controllers.common.settings as common_settings +from controllers.servers.host_definer.utils import utils logger = get_stdout_logger() @@ -13,98 +11,59 @@ class StorageClassWatcher(Watcher): def add_initial_storage_classes(self): - storage_classes_info = self._get_storage_classes_info() + storage_classes_info = self.resource_info_manager.get_storage_classes_info() for storage_class_info in storage_classes_info: secrets_info = self._get_secrets_info_from_storage_class_with_driver_provisioner(storage_class_info) self._handle_added_watch_event(secrets_info, storage_class_info.name) def watch_storage_class_resources(self): - while self._loop_forever(): - resource_version = self._get_k8s_object_resource_version(self.storage_api.list_storage_class()) - stream = watch.Watch().stream(self.storage_api.list_storage_class, - resource_version=resource_version, timeout_seconds=5) + while utils.loop_forever(): + stream = self.k8s_api.get_storage_class_stream() for watch_event in stream: - watch_event = self._munch(watch_event) - storage_class_info = self._generate_storage_class_info(watch_event.object) + watch_event = utils.munch(watch_event) + storage_class_info = self.resource_info_manager.generate_storage_class_info(watch_event.object) secrets_info = self._get_secrets_info_from_storage_class_with_driver_provisioner(storage_class_info) if watch_event.type == settings.ADDED_EVENT: self._handle_added_watch_event(secrets_info, storage_class_info.name) - - if watch_event.type == settings.DELETED_EVENT: + elif utils.is_watch_object_type_is_delete(watch_event.type): self._handle_deleted_watch_event(secrets_info) def _get_secrets_info_from_storage_class_with_driver_provisioner(self, storage_class_info): - if self._is_storage_class_has_csi_as_a_provisioner(storage_class_info): + if self.storage_class_manager.is_storage_class_has_csi_as_a_provisioner(storage_class_info): return self._get_secrets_info_from_storage_class(storage_class_info) return [] - def _is_storage_class_has_csi_as_a_provisioner(self, storage_class_info): - return storage_class_info.provisioner == settings.CSI_PROVISIONER_NAME - def _get_secrets_info_from_storage_class(self, storage_class_info): secrets_info = [] for parameter_name in storage_class_info.parameters: - if self._is_secret(parameter_name): - secret_name, secret_namespace = self._get_secret_name_and_namespace(storage_class_info, parameter_name) - secret_data = self._get_secret_data(secret_name, secret_namespace) + if self.secret_manager.is_secret(parameter_name): + secret_name, secret_namespace = self.secret_manager.get_secret_name_and_namespace( + storage_class_info, parameter_name) logger.info(messages.SECRET_IS_BEING_USED_BY_STORAGE_CLASS.format( secret_name, secret_namespace, storage_class_info.name)) - if self._is_topology_secret(secret_data): - logger.info(messages.SECRET_IS_FROM_TOPOLOGY_TYPE.format(secret_name, secret_namespace)) - nodes_with_system_id = self._generate_nodes_with_system_id(secret_data) - system_ids_topologies = self._generate_secret_system_ids_topologies(secret_data) - secret_info = self._generate_secret_info( - secret_name, secret_namespace, nodes_with_system_id, system_ids_topologies) - secrets_info = self._add_secret_info_to_list(secret_info, secrets_info) - else: - secret_info = self._generate_secret_info(secret_name, secret_namespace) - secrets_info = self._add_secret_info_to_list(secret_info, secrets_info) + secret_info = self._get_secret_info(secret_name, secret_namespace) + secrets_info = self.secret_manager.add_unique_secret_info_to_list(secret_info, secrets_info) return list(filter(None, secrets_info)) - def _is_secret(self, parameter_name): - return parameter_name.endswith(settings.SECRET_NAME_SUFFIX) and \ - parameter_name.startswith(settings.CSI_PARAMETER_PREFIX) - - def _get_secret_name_and_namespace(self, storage_class_info, parameter_name): - secret_name_suffix = settings.SECRET_NAME_SUFFIX - prefix = parameter_name.split(secret_name_suffix)[0] - return (storage_class_info.parameters[parameter_name], - storage_class_info.parameters[prefix + secret_name_suffix.replace( - common_settings.NAME_FIELD, common_settings.NAMESPACE_FIELD)]) - - def _add_secret_info_to_list(self, secret_info, list_with_secrets_info): - for secret_info_in_list in list_with_secrets_info: - if secret_info_in_list.name == secret_info.name and \ - secret_info_in_list.namespace == secret_info.namespace: - return list_with_secrets_info - list_with_secrets_info.append(secret_info) - return list_with_secrets_info + def _get_secret_info(self, secret_name, secret_namespace): + secret_data = self.secret_manager.get_secret_data(secret_name, secret_namespace) + if self.secret_manager.is_topology_secret(secret_data): + logger.info(messages.SECRET_IS_FROM_TOPOLOGY_TYPE.format(secret_name, secret_namespace)) + nodes_with_system_id = self.node_manager.generate_nodes_with_system_id(secret_data) + system_ids_topologies = self.secret_manager.generate_secret_system_ids_topologies(secret_data) + secret_info = self.resource_info_manager.generate_secret_info( + secret_name, secret_namespace, nodes_with_system_id, system_ids_topologies) + else: + secret_info = self.resource_info_manager.generate_secret_info(secret_name, secret_namespace) + return secret_info def _handle_added_watch_event(self, secrets_info, storage_class_name): logger.info(messages.NEW_STORAGE_CLASS.format(storage_class_name)) for secret_info in secrets_info: if secret_info: - self._define_nodes_when_new_secret(secret_info) - - def _define_nodes_when_new_secret(self, secret_info): - managed_secret_info, index = self._get_matching_managed_secret_info(secret_info) - secret_info.managed_storage_classes = 1 - if index == -1: - MANAGED_SECRETS.append(secret_info) - self._define_nodes_from_secret_info(secret_info) - elif managed_secret_info.managed_storage_classes == 0: - MANAGED_SECRETS[index] = secret_info - self._define_nodes_from_secret_info(secret_info) - else: - secret_info.managed_storage_classes = managed_secret_info.managed_storage_classes + 1 - MANAGED_SECRETS[index] = secret_info - - def _define_nodes_from_secret_info(self, secret_info): - logger.info(messages.NEW_MANAGED_SECRET.format(secret_info.name, secret_info.namespace)) - host_definition_info = self._get_host_definition_info_from_secret(secret_info) - self._define_nodes(host_definition_info) + self.definition_manager.define_nodes_when_new_secret(secret_info) def _handle_deleted_watch_event(self, secrets_info): for secret_info in secrets_info: - _, index = self._get_matching_managed_secret_info(secret_info) + _, index = self.secret_manager.get_matching_managed_secret_info(secret_info) MANAGED_SECRETS[index].managed_storage_classes -= 1 diff --git a/controllers/servers/host_definer/watcher/watcher_helper.py b/controllers/servers/host_definer/watcher/watcher_helper.py index 3f4cb4eb5..d00200161 100644 --- a/controllers/servers/host_definer/watcher/watcher_helper.py +++ b/controllers/servers/host_definer/watcher/watcher_helper.py @@ -1,437 +1,21 @@ -import os -import random -import string -from munch import Munch -import json +from controllers.servers.host_definer.k8s.api import K8SApi +from controllers.servers.host_definer.resource_manager.host_definition import HostDefinitionManager +from controllers.servers.host_definer.resource_manager.secret import SecretManager +from controllers.servers.host_definer.resource_manager.node import NodeManager +from controllers.servers.host_definer.resource_manager.csi_node import CSINodeManager +from controllers.servers.host_definer.definition_manager.definition import DefinitionManager +from controllers.servers.host_definer.resource_manager.resource_info import ResourceInfoManager +from controllers.servers.host_definer.resource_manager.storage_class import StorageClassManager -from controllers.common.csi_logger import get_stdout_logger -from controllers.servers.settings import SECRET_SUPPORTED_TOPOLOGIES_PARAMETER -from controllers.servers.utils import ( - validate_secrets, get_array_connection_info_from_secrets, get_system_info_for_topologies) -from controllers.servers.errors import ValidationException -import controllers.servers.host_definer.messages as messages -from controllers.servers.host_definer.kubernetes_manager.manager import KubernetesManager -from controllers.servers.host_definer import settings -import controllers.common.settings as common_settings -from controllers.servers.host_definer.types import ( - DefineHostRequest, DefineHostResponse, HostDefinitionInfo, SecretInfo, ManagedNode) -from controllers.servers.host_definer.storage_manager.host_definer_server import HostDefinerServicer -MANAGED_SECRETS = [] -NODES = {} -logger = get_stdout_logger() - - -class Watcher(KubernetesManager): +class Watcher(): def __init__(self): super().__init__() - self.storage_host_servicer = HostDefinerServicer() - - def _define_host_on_all_storages(self, node_name): - logger.info(messages.DEFINE_NODE_ON_ALL_MANAGED_SECRETS.format(node_name)) - for secret_info in MANAGED_SECRETS: - if secret_info.managed_storage_classes == 0: - continue - host_definition_info = self._get_host_definition_info_from_secret_and_node_name(node_name, secret_info) - self._create_definition(host_definition_info) - - def _get_host_definition_info_from_secret_and_node_name(self, node_name, secret_info): - host_definition_info = self._get_host_definition_info_from_secret(secret_info) - host_definition_info = self._add_name_to_host_definition_info(node_name, host_definition_info) - return host_definition_info - - def _get_host_definition_info_from_secret(self, secret_info): - host_definition_info = HostDefinitionInfo() - host_definition_info.secret_name = secret_info.name - host_definition_info.secret_namespace = secret_info.namespace - return host_definition_info - - def _define_nodes(self, host_definition_info): - for node_name, _ in NODES.items(): - host_definition_info = self._add_name_to_host_definition_info(node_name, host_definition_info) - self._create_definition(host_definition_info) - - def _add_name_to_host_definition_info(self, node_name, host_definition_info): - host_definition_info.node_name = node_name - host_definition_info.node_id = NODES[node_name].node_id - host_definition_info.name = self._get_host_definition_name(node_name) - return host_definition_info - - def _create_definition(self, host_definition_info): - if not self._is_node_should_be_managed_on_secret( - host_definition_info.node_name, host_definition_info.secret_name, - host_definition_info.secret_namespace): - return - host_definition_info = self._update_host_definition_info(host_definition_info) - response = self._define_host(host_definition_info) - current_host_definition_info_on_cluster = self._create_host_definition_if_not_exist( - host_definition_info, response) - self._set_status_to_host_definition_after_definition( - response.error_message, current_host_definition_info_on_cluster) - - def _update_host_definition_info(self, host_definition_info): - host_definition_info_on_cluster = self._get_matching_host_definition_info( - host_definition_info.node_name, host_definition_info.secret_name, host_definition_info.secret_namespace) - if host_definition_info_on_cluster: - host_definition_info.connectivity_type = host_definition_info_on_cluster.connectivity_type - host_definition_info.node_id = host_definition_info_on_cluster.node_id - return host_definition_info - - def _define_host(self, host_definition_info): - logger.info(messages.DEFINE_NODE_ON_SECRET.format(host_definition_info.node_name, - host_definition_info.secret_name, host_definition_info.secret_namespace)) - return self._ensure_definition_state(host_definition_info, self.storage_host_servicer.define_host) - - def _create_host_definition_if_not_exist(self, host_definition_info, response): - host_definition_manifest = self._get_host_definition_manifest(host_definition_info, response) - current_host_definition_info_on_cluster = self._get_matching_host_definition_info( - host_definition_info.node_name, host_definition_info.secret_name, host_definition_info.secret_namespace) - if current_host_definition_info_on_cluster: - host_definition_manifest[settings.METADATA][ - common_settings.NAME_FIELD] = current_host_definition_info_on_cluster.name - self._patch_host_definition(host_definition_manifest) - return current_host_definition_info_on_cluster - else: - logger.info(messages.CREATING_NEW_HOST_DEFINITION.format(host_definition_info.name)) - return self._create_host_definition(host_definition_manifest) - - def _get_host_definition_manifest(self, host_definition_info, response): - return { - settings.API_VERSION: settings.CSI_IBM_API_VERSION, - settings.KIND: settings.HOST_DEFINITION_KIND, - settings.METADATA: { - common_settings.NAME_FIELD: host_definition_info.name, - }, - settings.SPEC: { - settings.HOST_DEFINITION_FIELD: { - settings.NODE_NAME_FIELD: host_definition_info.node_name, - common_settings.HOST_DEFINITION_NODE_ID_FIELD: NODES[host_definition_info.node_name].node_id, - settings.SECRET_NAME_FIELD: host_definition_info.secret_name, - settings.SECRET_NAMESPACE_FIELD: host_definition_info.secret_namespace, - settings.CONNECTIVITY_TYPE_FIELD: response.connectivity_type, - settings.PORTS_FIELD: response.ports, - settings.NODE_NAME_ON_STORAGE_FIELD: response.node_name_on_storage, - settings.IO_GROUP_FIELD: response.io_group, - settings.MANAGEMENT_ADDRESS_FIELD: response.management_address - }, - }, - } - - def _set_status_to_host_definition_after_definition(self, message_from_storage, host_definition_info): - if message_from_storage and host_definition_info: - self._set_host_definition_status(host_definition_info.name, - settings.PENDING_CREATION_PHASE) - self._create_k8s_event_for_host_definition( - host_definition_info, message_from_storage, settings.DEFINE_ACTION, settings.FAILED_MESSAGE_TYPE) - elif host_definition_info: - self._set_host_definition_status_to_ready(host_definition_info) - - def _delete_definition(self, host_definition_info): - response = DefineHostResponse() - if self._is_node_should_be_managed_on_secret(host_definition_info.node_name, host_definition_info.secret_name, - host_definition_info.secret_namespace): - response = self._undefine_host(host_definition_info) - self._handle_k8s_host_definition_after_undefine_action_if_exist(host_definition_info, response) - - def _is_node_should_be_managed_on_secret(self, node_name, secret_name, secret_namespace): - logger.info(messages.CHECK_NODE_SHOULD_BE_MANAGED_BY_SECRET.format(node_name, secret_name, secret_namespace)) - secret_data = self._get_secret_data(secret_name, secret_namespace) - self._validate_secret(secret_data) - managed_secret_info, _ = self._get_managed_secret_by_name_and_namespace(secret_name, secret_namespace) - if self._is_node_should_managed_on_secret_info(node_name, managed_secret_info): - logger.info(messages.NODE_SHOULD_BE_MANAGED_ON_SECRET.format(node_name, secret_name, secret_namespace)) - return True - logger.info(messages.NODE_SHOULD_NOT_BE_MANAGED_ON_SECRET.format(node_name, secret_name, secret_namespace)) - return False - - def _get_managed_secret_by_name_and_namespace(self, secret_name, secret_namespace): - secret_info = self._generate_secret_info(secret_name, secret_namespace) - managed_secret_info, index = self._get_matching_managed_secret_info(secret_info) - return managed_secret_info, index - - def _is_node_should_managed_on_secret_info(self, node_name, secret_info): - if secret_info: - nodes_with_system_id = secret_info.nodes_with_system_id - if nodes_with_system_id and nodes_with_system_id.get(node_name): - return True - if nodes_with_system_id: - return False - return True - return False - - def _is_topology_secret(self, secret_data): - self._validate_secret(secret_data) - if self._get_secret_secret_config(secret_data): - return True - return False - - def _validate_secret(self, secret_data): - secret_data = self._convert_secret_config_to_string(secret_data) - try: - validate_secrets(secret_data) - except ValidationException as ex: - logger.error(str(ex)) - - def _undefine_host(self, host_definition_info): - logger.info(messages.UNDEFINED_HOST.format(host_definition_info.node_name, - host_definition_info.secret_name, host_definition_info.secret_namespace)) - return self._ensure_definition_state(host_definition_info, self.storage_host_servicer.undefine_host) - - def _handle_k8s_host_definition_after_undefine_action_if_exist(self, host_definition_info, response): - current_host_definition_info_on_cluster = self._get_matching_host_definition_info( - host_definition_info.node_name, host_definition_info.secret_name, host_definition_info.secret_namespace) - if current_host_definition_info_on_cluster: - self._handle_k8s_host_definition_after_undefine_action( - response.error_message, current_host_definition_info_on_cluster) - - def _handle_k8s_host_definition_after_undefine_action(self, message_from_storage, host_definition_info): - if message_from_storage and host_definition_info: - self._set_host_definition_status(host_definition_info.name, - settings.PENDING_DELETION_PHASE) - self._create_k8s_event_for_host_definition( - host_definition_info, message_from_storage, - settings.UNDEFINE_ACTION, settings.FAILED_MESSAGE_TYPE) - elif host_definition_info: - self._delete_host_definition(host_definition_info.name) - - def _set_host_definition_status_to_ready(self, host_definition): - self._set_host_definition_status(host_definition.name, settings.READY_PHASE) - self._create_k8s_event_for_host_definition( - host_definition, settings.SUCCESS_MESSAGE, settings.DEFINE_ACTION, settings.SUCCESSFUL_MESSAGE_TYPE) - - def _create_k8s_event_for_host_definition(self, host_definition_info, message, action, message_type): - logger.info(messages.CREATE_EVENT_FOR_HOST_DEFINITION.format(message, host_definition_info.name)) - k8s_event = self._generate_k8s_event(host_definition_info, message, action, message_type) - self._create_k8s_event(settings.DEFAULT_NAMESPACE, k8s_event) - - def _is_host_can_be_defined(self, node_name): - return self._is_dynamic_node_labeling_allowed() or self._is_node_has_manage_node_label(node_name) - - def _is_dynamic_node_labeling_allowed(self): - return os.getenv(settings.DYNAMIC_NODE_LABELING_ENV_VAR) == settings.TRUE_STRING - - def _is_host_can_be_undefined(self, node_name): - return self._is_host_definer_can_delete_hosts() and \ - self._is_node_has_manage_node_label(node_name) and \ - not self._is_node_has_forbid_deletion_label(node_name) - - def _is_host_definer_can_delete_hosts(self): - return os.getenv(settings.ALLOW_DELETE_ENV_VAR) == settings.TRUE_STRING - - def _is_node_has_manage_node_label(self, node_name): - return self._is_host_has_label_in_true(node_name, settings.MANAGE_NODE_LABEL) - - def _is_node_has_forbid_deletion_label(self, node_name): - return self._is_host_has_label_in_true(node_name, settings.FORBID_DELETION_LABEL) - - def _is_host_has_label_in_true(self, node_name, label): - node_info = self._get_node_info(node_name) - return self._get_label_value(node_info.labels, label) == settings.TRUE_STRING - - def _ensure_definition_state(self, host_definition_info, define_function): - request = self._get_request_from_host_definition(host_definition_info) - if not request: - response = DefineHostResponse() - response.error_message = messages.FAILED_TO_GET_SECRET_EVENT.format( - host_definition_info.secret_name, host_definition_info.secret_namespace) - return response - return define_function(request) - - def _get_request_from_host_definition(self, host_definition_info): - node_name = host_definition_info.node_name - logger.info(messages.GENERATE_REQUEST_FOR_NODE.format(node_name)) - node_info = self._get_node_info(node_name) - request = self._get_new_request(node_info.labels) - request = self._add_array_connectivity_info_to_request( - request, host_definition_info.secret_name, host_definition_info.secret_namespace, node_info.labels) - if request: - request.node_id_from_host_definition = host_definition_info.node_id - request.node_id_from_csi_node = self._get_node_id_by_node(host_definition_info) - request.io_group = self._get_io_group_by_node(host_definition_info.node_name) - return request - - def _get_new_request(self, labels): - request = DefineHostRequest() - request.prefix = self._get_prefix() - request.connectivity_type_from_user = self._get_connectivity_type_from_user(labels) - return request - - def _get_prefix(self): - return os.getenv(settings.PREFIX_ENV_VAR) - - def _get_connectivity_type_from_user(self, labels): - connectivity_type_label_on_node = self._get_label_value(labels, settings.CONNECTIVITY_TYPE_LABEL) - if connectivity_type_label_on_node in settings.SUPPORTED_CONNECTIVITY_TYPES: - return connectivity_type_label_on_node - return os.getenv(settings.CONNECTIVITY_ENV_VAR) - - def _get_label_value(self, labels, label): - return labels.get(label) - - def _add_array_connectivity_info_to_request(self, request, secret_name, secret_namespace, labels): - request.array_connection_info = self._get_array_connection_info_from_secret( - secret_name, secret_namespace, labels) - if request.array_connection_info: - return request - return None - - def _get_array_connection_info_from_secret(self, secret_name, secret_namespace, labels): - secret_data = self._get_secret_data(secret_name, secret_namespace) - if secret_data: - node_topology_labels = self._get_topology_labels(labels) - return self._get_array_connection_info_from_secret_data(secret_data, node_topology_labels) - return {} - - def _get_array_connection_info_from_secret_data(self, secret_data, labels): - try: - secret_data = self._convert_secret_config_to_string(secret_data) - array_connection_info = get_array_connection_info_from_secrets(secret_data, labels) - return self._decode_array_connectivity_info(array_connection_info) - except ValidationException as ex: - logger.error(str(ex)) - return None - - def _convert_secret_config_to_string(self, secret_data): - if settings.SECRET_CONFIG_FIELD in secret_data.keys(): - if type(secret_data[settings.SECRET_CONFIG_FIELD]) is dict: - secret_data[settings.SECRET_CONFIG_FIELD] = json.dumps(secret_data[settings.SECRET_CONFIG_FIELD]) - return secret_data - - def _decode_array_connectivity_info(self, array_connection_info): - array_connection_info.array_addresses = self._decode_list_base64_to_list_string( - array_connection_info.array_addresses) - array_connection_info.user = self._decode_base64_to_string(array_connection_info.user) - array_connection_info.password = self._decode_base64_to_string(array_connection_info.password) - return array_connection_info - - def _decode_list_base64_to_list_string(self, list_with_base64): - for index, base64_content in enumerate(list_with_base64): - list_with_base64[index] = self._decode_base64_to_string(base64_content) - return list_with_base64 - - def _get_node_id_by_node(self, host_definition_info): - try: - return NODES[host_definition_info.node_name].node_id - except Exception: - return host_definition_info.node_id - - def _get_io_group_by_node(self, node_name): - try: - return NODES[node_name].io_group - except Exception: - return '' - - def _get_host_definition_name(self, node_name): - return '{0}-{1}'.format(node_name, self._get_random_string()).replace('_', '.') - - def _get_random_string(self): - return ''.join(random.choices(string.ascii_lowercase + string.digits, k=20)) - - def _add_node_to_nodes(self, csi_node_info): - logger.info(messages.NEW_KUBERNETES_NODE.format(csi_node_info.name)) - self._add_manage_node_label_to_node(csi_node_info.name) - NODES[csi_node_info.name] = self._generate_managed_node(csi_node_info) - - def _add_manage_node_label_to_node(self, node_name): - if self._is_node_has_manage_node_label(node_name): - return - logger.info(messages.ADD_LABEL_TO_NODE.format(settings.MANAGE_NODE_LABEL, node_name)) - self._update_manage_node_label(node_name, settings.TRUE_STRING) - - def _generate_managed_node(self, csi_node_info): - node_info = self._get_node_info(csi_node_info.name) - return ManagedNode(csi_node_info, node_info.labels) - - def _remove_manage_node_label(self, node_name): - if self._is_managed_by_host_definer_label_should_be_removed(node_name): - logger.info(messages.REMOVE_LABEL_FROM_NODE.format(settings.MANAGE_NODE_LABEL, node_name)) - self._update_manage_node_label(node_name, None) - - def _is_managed_by_host_definer_label_should_be_removed(self, node_name): - return self._is_dynamic_node_labeling_allowed() and \ - not self._is_node_has_ibm_block_csi(node_name) and \ - not self._is_node_has_host_definitions(node_name) - - def _is_node_has_ibm_block_csi(self, node_name): - csi_node_info = self._get_csi_node_info(node_name) - return csi_node_info.node_id != '' - - def _is_node_has_host_definitions(self, node_name): - host_definitions_info = self._get_all_node_host_definitions_info(node_name) - return host_definitions_info != [] - - def _get_all_node_host_definitions_info(self, node_name): - node_host_definitions_info = [] - k8s_host_definitions = self._get_k8s_host_definitions() - for k8s_host_definition in k8s_host_definitions: - host_definition_info = self._generate_host_definition_info(k8s_host_definition) - if host_definition_info.node_name == node_name: - node_host_definitions_info.append(host_definition_info) - return node_host_definitions_info - - def _munch(self, watch_event): - return Munch.fromDict(watch_event) - - def _loop_forever(self): - return True - - def _generate_secret_info(self, secret_name, secret_namespace, nodes_with_system_id={}, system_ids_topologies={}): - return SecretInfo(secret_name, secret_namespace, nodes_with_system_id, system_ids_topologies) - - def _is_secret_managed(self, secret_info): - _, index = self._get_matching_managed_secret_info(secret_info) - if index != -1: - return True - return False - - def _get_matching_managed_secret_info(self, secret_info): - for index, managed_secret_info in enumerate(MANAGED_SECRETS): - if managed_secret_info.name == secret_info.name and managed_secret_info.namespace == secret_info.namespace: - return managed_secret_info, index - return secret_info, -1 - - def _generate_nodes_with_system_id(self, secret_data): - nodes_with_system_id = {} - secret_config = self._get_secret_secret_config(secret_data) - nodes_info = self._get_nodes_info() - for node_info in nodes_info: - nodes_with_system_id[node_info.name] = self._get_system_id_for_node(node_info, secret_config) - return nodes_with_system_id - - def _get_system_id_for_node(self, node_info, secret_config): - node_topology_labels = self._get_topology_labels(node_info.labels) - try: - _, system_id = get_system_info_for_topologies(secret_config, node_topology_labels) - except ValidationException: - return '' - return system_id - - def _get_topology_labels(self, labels): - topology_labels = {} - for label in labels: - if self._is_topology_label(label): - topology_labels[label] = labels[label] - return topology_labels - - def _is_topology_label(self, label): - for prefix in settings.TOPOLOGY_PREFIXES: - if label.startswith(prefix): - return True - return False - - def _generate_secret_system_ids_topologies(self, secret_data): - system_ids_topologies = {} - secret_config = self._get_secret_secret_config(secret_data) - for system_id, system_info in secret_config.items(): - system_ids_topologies[system_id] = (system_info.get(SECRET_SUPPORTED_TOPOLOGIES_PARAMETER)) - return system_ids_topologies - - def _get_secret_secret_config(self, secret_data): - secret_data = self._convert_secret_config_to_dict(secret_data) - return secret_data.get(settings.SECRET_CONFIG_FIELD, {}) - - def _convert_secret_config_to_dict(self, secret_data): - if settings.SECRET_CONFIG_FIELD in secret_data.keys(): - if type(secret_data[settings.SECRET_CONFIG_FIELD]) is str: - secret_data[settings.SECRET_CONFIG_FIELD] = json.loads(secret_data[settings.SECRET_CONFIG_FIELD]) - return secret_data + self.k8s_api = K8SApi() + self.host_definition_manager = HostDefinitionManager() + self.secret_manager = SecretManager() + self.definition_manager = DefinitionManager() + self.node_manager = NodeManager() + self.csi_node = CSINodeManager() + self.resource_info_manager = ResourceInfoManager() + self.storage_class_manager = StorageClassManager() diff --git a/controllers/tests/controller_server/host_definer/common.py b/controllers/tests/controller_server/host_definer/common.py deleted file mode 100644 index 76de06b0f..000000000 --- a/controllers/tests/controller_server/host_definer/common.py +++ /dev/null @@ -1,20 +0,0 @@ -import unittest -from mock import patch -from kubernetes.client.rest import ApiException - -import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils -import controllers.tests.controller_server.host_definer.settings as test_settings - - -class BaseSetUp(unittest.TestCase): - def setUp(self): - test_utils.patch_kubernetes_manager_init() - self.os = patch('{}.os'.format(test_settings.WATCHER_HELPER_PATH)).start() - self.nodes_on_watcher_helper = test_utils.patch_nodes_global_variable(test_settings.WATCHER_HELPER_PATH) - self.managed_secrets_on_watcher_helper = test_utils.patch_managed_secrets_global_variable( - test_settings.WATCHER_HELPER_PATH) - self.k8s_node_with_manage_node_label = test_utils.get_fake_k8s_node(test_settings.MANAGE_NODE_LABEL) - self.k8s_node_with_fake_label = test_utils.get_fake_k8s_node(test_settings.FAKE_LABEL) - self.ready_k8s_host_definitions = test_utils.get_fake_k8s_host_definitions_items(test_settings.READY_PHASE) - self.http_resp = test_utils.get_error_http_resp() - self.fake_api_exception = ApiException(http_resp=self.http_resp) diff --git a/controllers/tests/controller_server/host_definer/csi_node_watcher_test.py b/controllers/tests/controller_server/host_definer/csi_node_watcher_test.py deleted file mode 100644 index a224e11b4..000000000 --- a/controllers/tests/controller_server/host_definer/csi_node_watcher_test.py +++ /dev/null @@ -1,232 +0,0 @@ -from kubernetes.client.rest import ApiException - -import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils -import controllers.tests.controller_server.host_definer.utils.k8s_manifests_utils as k8s_manifests_utils -import controllers.tests.controller_server.host_definer.settings as test_settings -import controllers.common.settings as common_settings -from controllers.tests.controller_server.host_definer.common import BaseSetUp -from controllers.servers.host_definer.types import DefineHostResponse -from controllers.servers.host_definer.watcher.csi_node_watcher import CsiNodeWatcher - - -class CsiNodeWatcherBase(BaseSetUp): - def setUp(self): - super().setUp() - self.csi_node_watcher = test_utils.get_class_mock(CsiNodeWatcher) - self.nodes_on_csi_node_watcher = test_utils.patch_nodes_global_variable(test_settings.CSI_NODE_WATCHER_PATH) - self.updated_daemon_set = test_utils.get_fake_k8s_daemon_set_items(1, 1) - self.not_updated_daemon_set = test_utils.get_fake_k8s_daemon_set_items(0, 1) - self.deleted_daemon_set = test_utils.get_fake_k8s_daemon_set_items(0, 0) - self.managed_secrets_on_csi_node_watcher = test_utils.patch_managed_secrets_global_variable( - test_settings.CSI_NODE_WATCHER_PATH) - - -class TestAddInitialCsiNodes(CsiNodeWatcherBase): - def test_host_not_defined_for_csi_node_without_ibm_block_provider(self): - self.csi_node_watcher.csi_nodes_api.get.return_value = test_utils.get_fake_k8s_csi_nodes( - test_settings.FAKE_CSI_PROVISIONER, 1) - self.csi_node_watcher.add_initial_csi_nodes() - self.assertEqual(0, len(self.nodes_on_watcher_helper)) - - def test_host_not_defined_for_node_without_labels_and_no_dynamic_labeling(self): - self._prepare_default_mocks_for_add_node() - self.os.getenv.return_value = '' - self.csi_node_watcher.core_api.read_node.return_value = self.k8s_node_with_fake_label - self.csi_node_watcher.add_initial_csi_nodes() - self.assertEqual(0, len(self.nodes_on_watcher_helper)) - - def test_host_defined_for_node_with_manage_label(self): - self._prepare_default_mocks_for_add_node() - self.os.getenv.return_value = '' - self.csi_node_watcher.add_initial_csi_nodes() - self.assertEqual(1, len(self.nodes_on_watcher_helper)) - - def test_host_defined_for_multiple_nodes_with_dynamic_labeling(self): - self._prepare_default_mocks_for_add_node() - self.csi_node_watcher.csi_nodes_api.get.return_value = test_utils.get_fake_k8s_csi_nodes( - test_settings.CSI_PROVISIONER_NAME, 2) - self.csi_node_watcher.add_initial_csi_nodes() - self.assertEqual(2, len(self.nodes_on_watcher_helper)) - - def test_add_node_not_update_labels(self): - self._prepare_default_mocks_for_add_node() - self.csi_node_watcher.add_initial_csi_nodes() - self.csi_node_watcher.core_api.patch_node.assert_not_called() - self.assertEqual(1, len(self.nodes_on_watcher_helper)) - - def test_add_node_update_labels(self): - self._prepare_default_mocks_for_add_node() - self.csi_node_watcher.core_api.read_node.return_value = self.k8s_node_with_fake_label - self.csi_node_watcher.add_initial_csi_nodes() - self.csi_node_watcher.core_api.patch_node.assert_called_once_with( - test_settings.FAKE_NODE_NAME + '-0', - k8s_manifests_utils.get_metadata_with_manage_node_labels_manifest(test_settings.TRUE_STRING)) - self.assertEqual(1, len(self.nodes_on_watcher_helper)) - - def test_update_node_label_fail(self): - self._prepare_default_mocks_for_add_node() - self.csi_node_watcher.core_api.read_node.return_value = self.k8s_node_with_fake_label - self.csi_node_watcher.core_api.patch_node.side_effect = self.fake_api_exception - self.csi_node_watcher.add_initial_csi_nodes() - self.assertEqual(1, len(self.nodes_on_watcher_helper)) - - def _prepare_default_mocks_for_add_node(self): - self.csi_node_watcher.csi_nodes_api.get.return_value = test_utils.get_fake_k8s_csi_nodes( - test_settings.CSI_PROVISIONER_NAME, 1) - self.os.getenv.return_value = test_settings.TRUE_STRING - self.csi_node_watcher.core_api.read_node.return_value = self.k8s_node_with_manage_node_label - - def test_get_csi_nodes_fail(self): - self.csi_node_watcher.csi_nodes_api.get.side_effect = self.fake_api_exception - self.csi_node_watcher.add_initial_csi_nodes() - self.assertEqual(0, len(self.nodes_on_watcher_helper)) - - -class TestWatchCsiNodesResources(CsiNodeWatcherBase): - - def test_updated_csi_node_not_removed(self): - self._prepare_mocks_for_updated_csi_node() - self.csi_node_watcher.host_definitions_api.get.return_value = test_utils.get_empty_k8s_host_definitions() - test_utils.run_function_with_timeout(self.csi_node_watcher.watch_csi_nodes_resources, 0.5) - self.assertEqual(1, len(self.nodes_on_csi_node_watcher)) - self.csi_node_watcher.storage_host_servicer.define_host.assert_not_called() - - def test_updated_node_id_of_csi_node(self): - self._prepare_mocks_for_updated_csi_node() - host_definitions = self.ready_k8s_host_definitions - host_definitions.items[0].spec.hostDefinition.nodeId = 'other_node_id' - self.csi_node_watcher.host_definitions_api.get.return_value = self.ready_k8s_host_definitions - self.csi_node_watcher.core_api.read_namespaced_secret.return_value = test_utils.get_fake_k8s_secret() - test_utils.run_function_with_timeout(self.csi_node_watcher.watch_csi_nodes_resources, 0.5) - self.assertEqual(1, len(self.nodes_on_csi_node_watcher)) - self.csi_node_watcher.storage_host_servicer.define_host.assert_called() - - def _prepare_mocks_for_updated_csi_node(self): - self.nodes_on_watcher_helper[test_settings.FAKE_NODE_NAME] = test_utils.get_fake_managed_node() - self.nodes_on_csi_node_watcher[test_settings.FAKE_NODE_NAME] = test_utils.get_fake_managed_node() - self.managed_secrets_on_csi_node_watcher.append(test_utils.get_fake_secret_info()) - self.csi_node_watcher.csi_nodes_api.watch.return_value = iter( - [test_utils.get_fake_csi_node_watch_event(test_settings.DELETED_EVENT_TYPE)]) - self.csi_node_watcher.core_api.read_node.return_value = self.k8s_node_with_manage_node_label - self.csi_node_watcher.apps_api.list_daemon_set_for_all_namespaces.side_effect = [ - self.not_updated_daemon_set, self.updated_daemon_set] - self.csi_node_watcher.core_api.list_pod_for_all_namespaces.return_value = test_utils.get_fake_k8s_pods_items() - - def test_delete_host_definition(self): - self._prepare_default_mocks_for_deletion() - test_utils.run_function_with_timeout(self.csi_node_watcher.watch_csi_nodes_resources, 0.5) - self.csi_node_watcher.host_definitions_api.delete.assert_called_once_with( - name=test_settings.FAKE_NODE_NAME, body={}) - self.assertEqual(0, len(self.nodes_on_csi_node_watcher)) - - def test_delete_host_from_storage_failed(self): - self._prepare_default_mocks_for_deletion() - self.csi_node_watcher.storage_host_servicer.undefine_host.return_value = DefineHostResponse( - error_message=test_settings.FAIL_MESSAGE_FROM_STORAGE) - self.csi_node_watcher.custom_object_api.patch_cluster_custom_object_status.return_value = None - test_utils.run_function_with_timeout(self.csi_node_watcher.watch_csi_nodes_resources, 0.5) - self.csi_node_watcher.core_api.create_namespaced_event.assert_called() - - def test_fail_to_get_host_definitions_delete_host_definition_not_called(self): - self._prepare_default_mocks_for_deletion() - self.csi_node_watcher.host_definitions_api.get.side_effect = self.fake_api_exception - test_utils.run_function_with_timeout(self.csi_node_watcher.watch_csi_nodes_resources, 0.5) - self.assertEqual(0, len(self.nodes_on_csi_node_watcher)) - self.csi_node_watcher.host_definitions_api.delete.assert_not_called() - - def test_remove_manage_node_label(self): - self._prepare_default_mocks_for_deletion() - self.csi_node_watcher.csi_nodes_api.get.return_value = test_utils.get_fake_k8s_csi_node( - test_settings.FAKE_CSI_PROVISIONER) - self.csi_node_watcher.host_definitions_api.get.return_value = test_utils.get_empty_k8s_host_definitions() - test_utils.run_function_with_timeout(self.csi_node_watcher.watch_csi_nodes_resources, 0.5) - self.assertEqual(0, len(self.nodes_on_csi_node_watcher)) - self.csi_node_watcher.core_api.patch_node.assert_called_once_with( - test_settings.FAKE_NODE_NAME, k8s_manifests_utils.get_metadata_with_manage_node_labels_manifest(None)) - - def test_nodes_global_variable_reduced_on_csi_node_deletion_and_definer_cannot_delete(self): - self._prepare_default_mocks_for_deletion() - self.os.getenv.return_value = '' - test_utils.run_function_with_timeout(self.csi_node_watcher.watch_csi_nodes_resources, 0.5) - self.assertEqual(0, len(self.nodes_on_csi_node_watcher)) - self.csi_node_watcher.storage_host_servicer.undefine_host.assert_not_called() - - def test_nodes_global_variable_reduced_on_failed_daemon_set_list(self): - self._prepare_default_mocks_for_deletion() - self.csi_node_watcher.apps_api.list_daemon_set_for_all_namespaces.side_effect = ApiException( - http_resp=self.http_resp) - test_utils.run_function_with_timeout(self.csi_node_watcher.watch_csi_nodes_resources, 0.5) - self.assertEqual(0, len(self.nodes_on_csi_node_watcher)) - - def test_failed_pods_list_log_message(self): - self._prepare_default_mocks_for_deletion() - self.csi_node_watcher.core_api.list_pod_for_all_namespaces.side_effect = ApiException( - http_resp=self.http_resp) - test_utils.run_function_with_timeout(self.csi_node_watcher.watch_csi_nodes_resources, 0.5) - self.assertEqual(0, len(self.nodes_on_csi_node_watcher)) - - def test_csi_node_deleted_with_modify_event(self): - self._prepare_default_mocks_for_deletion() - self.csi_node_watcher.csi_nodes_api.watch.return_value = iter( - [test_utils.get_fake_csi_node_watch_event(test_settings.MODIFIED_EVENT_TYPE)]) - test_utils.run_function_with_timeout(self.csi_node_watcher.watch_csi_nodes_resources, 0.5) - self.csi_node_watcher.host_definitions_api.delete.assert_called_once_with( - name=test_settings.FAKE_NODE_NAME, body={}) - - def _prepare_default_mocks_for_deletion(self): - self.nodes_on_watcher_helper[test_settings.FAKE_NODE_NAME] = test_utils.get_fake_managed_node() - self.nodes_on_csi_node_watcher[test_settings.FAKE_NODE_NAME] = test_utils.get_fake_managed_node() - self.csi_node_watcher.csi_nodes_api.watch.return_value = iter( - [test_utils.get_fake_csi_node_watch_event(test_settings.DELETED_EVENT_TYPE)]) - self.csi_node_watcher.core_api.read_node.side_effect = [ - self.k8s_node_with_manage_node_label, - self.k8s_node_with_fake_label, self.k8s_node_with_fake_label] - self.csi_node_watcher.apps_api.list_daemon_set_for_all_namespaces.return_value = self.deleted_daemon_set - self.csi_node_watcher.core_api.list_pod_for_all_namespaces.return_value = test_utils.get_empty_k8s_pods() - self.os.getenv.return_value = test_settings.TRUE_STRING - self.csi_node_watcher.storage_host_servicer.undefine_host.return_value = DefineHostResponse() - self.csi_node_watcher.host_definitions_api.get.return_value = self.ready_k8s_host_definitions - self.csi_node_watcher.core_api.read_namespaced_secret.return_value = test_utils.get_fake_k8s_secret() - self.csi_node_watcher.csi_nodes_api.get.return_value = test_utils.get_fake_k8s_csi_node( - test_settings.CSI_PROVISIONER_NAME) - self.managed_secrets_on_csi_node_watcher.append(test_utils.get_fake_secret_info()) - - def test_define_host_called_on_new_csi_node(self): - self._prepare_default_mocks_for_modified_event() - self.csi_node_watcher.host_definitions_api.get.side_effect = [ - test_utils.get_empty_k8s_host_definitions(), self.ready_k8s_host_definitions] - self.os.getenv.side_effect = [test_settings.TRUE_STRING, test_settings.FAKE_PREFIX, ''] - test_utils.run_function_with_timeout(self.csi_node_watcher.watch_csi_nodes_resources, 0.5) - self.assertEqual(1, len(self.nodes_on_watcher_helper)) - self.csi_node_watcher.storage_host_servicer.define_host.assert_called_once_with(test_utils.get_define_request( - prefix=test_settings.FAKE_PREFIX, node_id_from_host_definition=test_settings.FAKE_NODE_ID)) - - def test_define_host_not_called_on_new_csi_node_when_failed_to_get_secret(self): - self._prepare_default_mocks_for_modified_event() - self.csi_node_watcher.host_definitions_api.get.side_effect = [ - test_utils.get_empty_k8s_host_definitions(), self.ready_k8s_host_definitions] - self.csi_node_watcher.core_api.read_namespaced_secret.side_effect = self.fake_api_exception - test_utils.run_function_with_timeout(self.csi_node_watcher.watch_csi_nodes_resources, 0.5) - self.csi_node_watcher.storage_host_servicer.define_host.assert_not_called() - - def test_fail_define_host_on_storage(self): - self._prepare_default_mocks_for_modified_event() - self.csi_node_watcher.host_definitions_api.get.side_effect = [ - test_utils.get_empty_k8s_host_definitions(), self.ready_k8s_host_definitions] - self.csi_node_watcher.storage_host_servicer.define_host.return_value = DefineHostResponse( - error_message=test_settings.FAIL_MESSAGE_FROM_STORAGE) - test_utils.run_function_with_timeout(self.csi_node_watcher.watch_csi_nodes_resources, 0.5) - self.csi_node_watcher.custom_object_api.patch_cluster_custom_object_status.assert_called_with( - common_settings.CSI_IBM_GROUP, common_settings.VERSION, - common_settings.HOST_DEFINITION_PLURAL, test_settings.FAKE_NODE_NAME, - test_utils.get_pending_creation_status_manifest()) - - def _prepare_default_mocks_for_modified_event(self): - self.nodes_on_watcher_helper[test_settings.FAKE_NODE_NAME] = test_utils.get_fake_managed_node() - self.managed_secrets_on_watcher_helper.append(test_utils.get_fake_secret_info()) - self.csi_node_watcher.csi_nodes_api.watch.return_value = iter( - [test_utils.get_fake_csi_node_watch_event(test_settings.MODIFIED_EVENT_TYPE)]) - self.os.getenv.return_value = test_settings.TRUE_STRING - self.csi_node_watcher.core_api.read_namespaced_secret.return_value = test_utils.get_fake_k8s_secret() - self.csi_node_watcher.storage_host_servicer.define_host.return_value = DefineHostResponse() - self.csi_node_watcher.core_api.read_node.return_value = self.k8s_node_with_fake_label diff --git a/controllers/tests/controller_server/host_definer/definition_manager/__init__.py b/controllers/tests/controller_server/host_definer/definition_manager/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/controllers/tests/controller_server/host_definer/definition_manager/definition_manager_test.py b/controllers/tests/controller_server/host_definer/definition_manager/definition_manager_test.py new file mode 100644 index 000000000..478bbac14 --- /dev/null +++ b/controllers/tests/controller_server/host_definer/definition_manager/definition_manager_test.py @@ -0,0 +1,269 @@ +import unittest +from copy import deepcopy +from unittest.mock import MagicMock, Mock, patch + +import controllers.servers.host_definer.messages as messages +from controllers.servers.host_definer.types import DefineHostResponse +from controllers.servers.host_definer.k8s.api import K8SApi +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils +import controllers.tests.controller_server.host_definer.utils.k8s_manifests_utils as test_manifest_utils +import controllers.tests.controller_server.host_definer.settings as test_settings +from controllers.servers.host_definer.definition_manager.definition import DefinitionManager + + +class TestDefinitionManager(unittest.TestCase): + def setUp(self): + test_utils.patch_function(K8SApi, '_load_cluster_configuration') + test_utils.patch_function(K8SApi, '_get_dynamic_client') + self.manager = DefinitionManager() + self.manager.secret_manager = MagicMock() + self.manager.k8s_api = MagicMock() + self.manager.request_manager = MagicMock() + self.manager.host_definition_manager = MagicMock() + self.manager.storage_host_servicer = MagicMock() + self.global_managed_nodes = test_utils.patch_nodes_global_variable(test_settings.DEFINITION_MANAGER_PATH) + self.global_managed_secrets = test_utils.patch_managed_secrets_global_variable( + test_settings.DEFINITION_MANAGER_PATH) + self.fake_host_define_response = test_utils.get_fake_define_host_response() + self.fake_host_definition_info = test_utils.get_fake_host_definition_info() + self.fake_secret_info = test_utils.get_fake_secret_info() + self.fake_host_definition_manifest = test_manifest_utils.get_fake_k8s_host_definition_manifest() + self.secret_info_with_no_storage_classes = test_utils.get_fake_secret_info(0) + self.secret_info_with_storage_classes = test_utils.get_fake_secret_info(2) + + def test_define_host_on_all_storages_success(self): + self.global_managed_secrets.append(self.secret_info_with_no_storage_classes) + self.global_managed_secrets.append(self.secret_info_with_storage_classes) + self.manager.host_definition_manager.get_host_definition_info_from_secret_and_node_name.return_value = \ + self.fake_host_definition_info + self.manager.create_definition = Mock() + self.manager.define_node_on_all_storages(test_settings.FAKE_NODE_NAME) + self.manager.host_definition_manager.get_host_definition_info_from_secret_and_node_name.assert_called_once_with( + test_settings.FAKE_NODE_NAME, self.secret_info_with_storage_classes) + self.manager.create_definition.assert_called_once_with(self.fake_host_definition_info) + + def test_define_single_nodes(self): + self.global_managed_nodes[test_settings.FAKE_NODE_NAME] = test_utils.get_fake_managed_node() + host_definition_info = deepcopy(self.fake_host_definition_info) + host_definition_info.node_name = 'test_name' + self.manager.host_definition_manager.add_name_to_host_definition_info.return_value = \ + self.fake_host_definition_info + self.manager.create_definition = Mock() + self.manager.define_nodes(host_definition_info) + self.manager.host_definition_manager.add_name_to_host_definition_info.assert_called_once_with( + test_settings.FAKE_NODE_NAME, host_definition_info) + self.manager.create_definition.assert_called_once_with(self.fake_host_definition_info) + + def test_define_multiple_nodes(self): + self.global_managed_nodes[test_settings.FAKE_NODE_NAME] = test_utils.get_fake_managed_node() + self.global_managed_nodes[test_settings.FAKE_NODE_NAME + '2'] = test_utils.get_fake_managed_node() + self.manager.create_definition = Mock() + self.manager.define_nodes(self.fake_host_definition_info) + self.assertEqual(self.manager.create_definition.call_count, 2) + self.assertEqual(self.manager.host_definition_manager.add_name_to_host_definition_info.call_count, 2) + + def test_create_definition_success(self): + current_host_definition_info_on_cluster = self._prepare_create_definition(True) + self._test_create_host_definition() + + self.manager.host_definition_manager.update_host_definition_info.assert_called_once_with( + self.fake_host_definition_info) + self.manager.define_host.assert_called_once_with(self.fake_host_definition_info) + self.manager.host_definition_manager.create_host_definition_if_not_exist.assert_called_once_with( + self.fake_host_definition_info, self.fake_host_define_response) + self.manager.host_definition_manager.set_status_to_host_definition_after_definition.assert_called_once_with( + self.fake_host_define_response.error_message, current_host_definition_info_on_cluster) + + def test_do_not_create_definition_when_node_should_not_be_managed_by_secret(self): + self._prepare_create_definition(False) + self._test_create_host_definition() + self.manager.host_definition_manager.update_host_definition_info.assert_not_called() + self.manager.define_host.assert_not_called() + self.manager.host_definition_manager.create_host_definition_if_not_exist.assert_not_called() + self.manager.host_definition_manager.set_status_to_host_definition_after_definition.assert_not_called() + + def _prepare_create_definition(self, is_node_should_be_managed): + self.manager.secret_manager.is_node_should_be_managed_on_secret.return_value = is_node_should_be_managed + self.manager.define_host = Mock() + self.manager.define_host.return_value = self.fake_host_define_response + current_host_definition_info_on_cluster = deepcopy(self.fake_host_definition_info) + current_host_definition_info_on_cluster.node_name = 'current_host_on_cluster' + self.manager.host_definition_manager.update_host_definition_info.return_value = self.fake_host_definition_info + self.manager.host_definition_manager.create_host_definition_if_not_exist.return_value = \ + current_host_definition_info_on_cluster + return current_host_definition_info_on_cluster + + def _test_create_host_definition(self): + self.manager.create_definition(self.fake_host_definition_info) + self._assert_is_node_should_be_managed() + + def test_define_host_success(self): + self._test_define_host('request', 'response', self.manager.storage_host_servicer.define_host) + self.manager.storage_host_servicer.define_host.assert_called_once_with('request') + + def test_fail_to_generate_request_for_define_host(self): + expected_response = self._get_response_after_failing_to_generate_request() + self._test_define_host(None, expected_response, self.manager.storage_host_servicer.define_host) + self.manager.storage_host_servicer.define_host.assert_not_called() + + def _test_define_host(self, request, expected_response, define_function): + self._ensure_definition_state_function(request, expected_response, define_function) + result = self.manager.define_host(self.fake_host_definition_info) + self._assert_definition_state(expected_response, result) + + def test_undefine_host_success(self): + self._test_undefine_host('request', 'response', self.manager.storage_host_servicer.undefine_host) + self.manager.storage_host_servicer.undefine_host.assert_called_once_with('request') + + def test_fail_to_generate_request_for_undefine_host(self): + expected_response = self._get_response_after_failing_to_generate_request() + self._test_undefine_host(None, expected_response, self.manager.storage_host_servicer.undefine_host) + self.manager.storage_host_servicer.define_host.assert_not_called() + + def _get_response_after_failing_to_generate_request(self): + response = DefineHostResponse() + response.error_message = messages.FAILED_TO_GET_SECRET_EVENT.format( + test_settings.FAKE_SECRET, test_settings.FAKE_SECRET_NAMESPACE) + return response + + def _test_undefine_host(self, request, expected_response, define_function): + self._ensure_definition_state_function(request, expected_response, define_function) + result = self.manager.undefine_host(self.fake_host_definition_info) + self._assert_definition_state(expected_response, result) + + def _ensure_definition_state_function(self, request, expected_response, define_function): + self.manager.request_manager.generate_request.return_value = request + define_function.return_value = expected_response + + def _assert_definition_state(self, expected_response, result): + self.assertEqual(result, expected_response) + self.manager.request_manager.generate_request.assert_called_once_with(self.fake_host_definition_info) + + def test_delete_definition_success(self): + self._test_delete_definition(True, 'response') + self.manager.undefine_host.assert_called_once_with(self.fake_host_definition_info) + + def test_do_not_undefine_host_when_node_should_not_be_managed_by_secret(self): + self._test_delete_definition(False, DefineHostResponse()) + self.manager.undefine_host.assert_not_called() + + def _test_delete_definition(self, is_node_should_be_managed, expected_response): + self.manager.secret_manager.is_node_should_be_managed_on_secret.return_value = is_node_should_be_managed + self._prepare_undefine_host(expected_response) + self.manager.delete_definition(self.fake_host_definition_info) + self._assert_is_node_should_be_managed() + self.manager.host_definition_manager.handle_k8s_host_definition_after_undefine_action.assert_called_once_with( + self.fake_host_definition_info, expected_response) + + def test_undefine_multiple_node_definitions_success(self): + self.global_managed_secrets.append(self.secret_info_with_storage_classes) + self.global_managed_secrets.append(self.secret_info_with_storage_classes) + self._test_undefine_node_definitions() + self.assertEqual( + self.manager.host_definition_manager.get_host_definition_info_from_secret_and_node_name.call_count, 2) + self.assertEqual(self.manager.delete_definition.call_count, 2) + + def test_undefine_single_node_definition_success(self): + self.global_managed_secrets.append(self.secret_info_with_storage_classes) + self._test_undefine_node_definitions() + self.manager.host_definition_manager.get_host_definition_info_from_secret_and_node_name.assert_called_once_with( + test_settings.FAKE_NODE_NAME, self.secret_info_with_storage_classes) + self.manager.delete_definition.assert_called_once_with(self.fake_host_definition_info) + + def _test_undefine_node_definitions(self): + self.manager.delete_definition = Mock() + self.manager.host_definition_manager.get_host_definition_info_from_secret_and_node_name.return_value = \ + self.fake_host_definition_info + self.manager.undefine_node_definitions(test_settings.FAKE_NODE_NAME) + + def test_undefine_host_after_pending_success(self): + self._test_undefine_host_after_pending(True, 'response') + + def test_do_not_undefine_host_after_pending_when_node_should_not_be_managed_by_secret(self): + self._test_undefine_host_after_pending(False, DefineHostResponse()) + + def _test_undefine_host_after_pending(self, is_node_should_be_managed, expected_response): + self.manager.secret_manager.is_node_should_be_managed_on_secret.return_value = is_node_should_be_managed + self._prepare_undefine_host(expected_response) + result = self.manager.undefine_host_after_pending(self.fake_host_definition_info) + self.assertEqual(result, expected_response) + self._assert_is_node_should_be_managed() + + def _prepare_undefine_host(self, expected_response): + self.manager.undefine_host = Mock() + self.manager.undefine_host.return_value = expected_response + + @patch('{}.manifest_utils'.format(test_settings.DEFINITION_MANAGER_PATH)) + def test_define_host_after_pending_success(self, mock_manifest_utils): + self._test_define_host_after_pending(True, 'response', mock_manifest_utils) + mock_manifest_utils.generate_host_definition_response_fields_manifest.assert_called_once_with( + self.fake_host_definition_info.node_name, 'response') + self.manager.k8s_api.patch_host_definition.assert_called_once_with(self.fake_host_definition_manifest) + + @patch('{}.manifest_utils'.format(test_settings.DEFINITION_MANAGER_PATH)) + def test_do_not_define_host_after_pending_when_node_should_not_be_managed_by_secret(self, mock_manifest_utils): + self._test_define_host_after_pending(False, DefineHostResponse(), mock_manifest_utils) + mock_manifest_utils.generate_host_definition_response_fields_manifest.assert_not_called() + self.manager.k8s_api.patch_host_definition.assert_not_called() + + def _test_define_host_after_pending(self, is_node_should_be_managed, expected_response, mock_manifest_utils): + self.manager.secret_manager.is_node_should_be_managed_on_secret.return_value = is_node_should_be_managed + self._prepare_define_host(expected_response) + mock_manifest_utils.generate_host_definition_response_fields_manifest.return_value = \ + self.fake_host_definition_manifest + result = self.manager.define_host_after_pending(self.fake_host_definition_info) + self.assertEqual(result, expected_response) + self._assert_is_node_should_be_managed() + + def _prepare_define_host(self, expected_response): + self.manager.define_host = Mock() + self.manager.define_host.return_value = expected_response + + def _assert_is_node_should_be_managed(self): + self.manager.secret_manager.is_node_should_be_managed_on_secret.assert_called_once_with( + test_settings.FAKE_NODE_NAME, test_settings.FAKE_SECRET, test_settings.FAKE_SECRET_NAMESPACE) + + def test_define_nodes_when_new_secret_which_is_not_managed_yet(self): + secret_info = deepcopy(self.fake_secret_info) + self.manager.secret_manager.get_matching_managed_secret_info.return_value = ( + self.fake_secret_info, -1) + self._test_define_nodes_when_new_secret(secret_info, 1) + self._assert_define_nodes_from_secret_info_called(secret_info) + + def test_define_nodes_when_new_secret_which_is_managed_already_and_does_not_have_managed_storage_classes(self): + self.global_managed_secrets.append(self.secret_info_with_no_storage_classes) + secret_info = deepcopy(self.fake_secret_info) + self.manager.secret_manager.get_matching_managed_secret_info.return_value = ( + self.fake_secret_info, 0) + self._test_define_nodes_when_new_secret(secret_info, 1) + self._assert_define_nodes_from_secret_info_called(secret_info) + + def test_define_nodes_when_new_secret_which_is_managed_already_with_storage_classes(self): + self.global_managed_secrets.append(self.secret_info_with_storage_classes) + secret_info = deepcopy(self.fake_secret_info) + secret_info.managed_storage_classes = 2 + self.manager.secret_manager.get_matching_managed_secret_info.return_value = ( + secret_info, 0) + self._test_define_nodes_when_new_secret(secret_info, 3) + self.manager.host_definition_manager.get_host_definition_info_from_secret.assert_not_called() + self.manager.define_nodes.assert_not_called() + + def _test_define_nodes_when_new_secret(self, secret_info, expected_managed_storage_classes): + self._prepare_define_nodes_when_new_secret() + self.manager.define_nodes_when_new_secret(secret_info) + self.manager.secret_manager.get_matching_managed_secret_info.assert_called_once_with(secret_info) + self._assert_define_node_when_new_secret(secret_info, expected_managed_storage_classes) + + def _assert_define_node_when_new_secret(self, secret_info, expected_managed_storage_classes): + secret_info.managed_storage_classes = expected_managed_storage_classes + self.assertEqual(len(self.global_managed_secrets), 1) + self.assertEqual(self.global_managed_secrets, [secret_info]) + + def _prepare_define_nodes_when_new_secret(self): + self.manager.define_nodes = Mock() + self.manager.host_definition_manager.get_host_definition_info_from_secret.return_value = \ + self.fake_host_definition_info + + def _assert_define_nodes_from_secret_info_called(self, secret_info): + self.manager.host_definition_manager.get_host_definition_info_from_secret.assert_called_once_with(secret_info) + self.manager.define_nodes.assert_called_once_with(self.fake_host_definition_info) diff --git a/controllers/tests/controller_server/host_definer/definition_manager/request_manager_test.py b/controllers/tests/controller_server/host_definer/definition_manager/request_manager_test.py new file mode 100644 index 000000000..c97dbed14 --- /dev/null +++ b/controllers/tests/controller_server/host_definer/definition_manager/request_manager_test.py @@ -0,0 +1,66 @@ +import unittest +from copy import deepcopy +from unittest.mock import MagicMock, patch + +from controllers.servers.host_definer.k8s.api import K8SApi +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils +import controllers.tests.controller_server.host_definer.settings as test_settings +from controllers.servers.host_definer.definition_manager.request import RequestManager + + +class TestRequestManager(unittest.TestCase): + def setUp(self): + test_utils.patch_function(K8SApi, '_load_cluster_configuration') + test_utils.patch_function(K8SApi, '_get_dynamic_client') + self.request_manager = RequestManager() + self.request_manager.secret_manager = MagicMock() + self.request_manager.resource_info_manager = MagicMock() + self.mock_global_managed_nodes = test_utils.patch_nodes_global_variable( + test_settings.REQUEST_MANAGER_PATH) + self.fake_host_definition_info = test_utils.get_fake_host_definition_info() + self.fake_node_info = test_utils.get_fake_node_info() + self.fake_node_info.labels[test_settings.CONNECTIVITY_TYPE_LABEL] = test_settings.ISCSI_CONNECTIVITY_TYPE + self.array_connection_info = test_utils.get_array_connection_info() + self.define_request = test_utils.get_define_request( + test_settings.FAKE_PREFIX, test_settings.ISCSI_CONNECTIVITY_TYPE, test_settings.FAKE_NODE_ID) + + @patch('{}.utils'.format(test_settings.REQUEST_MANAGER_PATH)) + def test_generate_request_success(self, mock_utils): + self.mock_global_managed_nodes[test_settings.FAKE_NODE_NAME] = test_utils.get_fake_managed_node() + self._prepare_generate_request(self.array_connection_info, mock_utils) + result = self.request_manager.generate_request(self.fake_host_definition_info) + self._assert_called_generate_request(mock_utils) + self.assertEqual(result, self.define_request) + + @patch('{}.utils'.format(test_settings.REQUEST_MANAGER_PATH)) + def test_get_none_when_array_connection_info_is_empty_success(self, mock_utils): + self._prepare_generate_request(None, mock_utils) + result = self.request_manager.generate_request(self.fake_host_definition_info) + self._assert_called_generate_request(mock_utils) + self.assertEqual(result, None) + + @patch('{}.utils'.format(test_settings.REQUEST_MANAGER_PATH)) + def test_generate_request_when_node_is_not_in_managed_nodes_success(self, mock_utils): + host_definition_info = deepcopy(self.fake_host_definition_info) + host_definition_info.node_id = 'fake_new_node_id' + define_request = deepcopy(self.define_request) + define_request.node_id_from_csi_node = 'fake_new_node_id' + define_request.node_id_from_host_definition = 'fake_new_node_id' + define_request.io_group = '' + self._prepare_generate_request(self.array_connection_info, mock_utils) + result = self.request_manager.generate_request(host_definition_info) + self._assert_called_generate_request(mock_utils) + self.assertEqual(result, define_request) + + def _prepare_generate_request(self, array_connection_info, mock_utils): + self.request_manager.resource_info_manager.get_node_info.return_value = self.fake_node_info + mock_utils.get_prefix.return_value = test_settings.FAKE_PREFIX + mock_utils.get_connectivity_type_from_user.return_value = test_settings.ISCSI_CONNECTIVITY_TYPE + self.request_manager.secret_manager.get_array_connection_info.return_value = array_connection_info + + def _assert_called_generate_request(self, mock_utils): + self.request_manager.resource_info_manager.get_node_info.assert_called_once_with(test_settings.FAKE_NODE_NAME) + mock_utils.get_prefix.assert_called_once_with() + mock_utils.get_connectivity_type_from_user.assert_called_once_with(test_settings.ISCSI_CONNECTIVITY_TYPE) + self.request_manager.secret_manager.get_array_connection_info.assert_called_once_with( + test_settings.FAKE_SECRET, test_settings.FAKE_SECRET_NAMESPACE, self.fake_node_info.labels) diff --git a/controllers/tests/controller_server/host_definer/host_definer_utils_tests/__init__.py b/controllers/tests/controller_server/host_definer/host_definer_utils_tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/controllers/tests/controller_server/host_definer/host_definer_utils_tests/manifest_utils_test.py b/controllers/tests/controller_server/host_definer/host_definer_utils_tests/manifest_utils_test.py new file mode 100644 index 000000000..bcfcdd618 --- /dev/null +++ b/controllers/tests/controller_server/host_definer/host_definer_utils_tests/manifest_utils_test.py @@ -0,0 +1,55 @@ +import unittest +from unittest.mock import patch + +from controllers.servers.host_definer.utils import manifest_utils +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils +import controllers.tests.controller_server.host_definer.utils.k8s_manifests_utils as test_manifest_utils +import controllers.tests.controller_server.host_definer.settings as test_settings +import controllers.common.settings as common_settings + + +class TestManifestUtils(unittest.TestCase): + def setUp(self): + self.define_response = test_utils.get_fake_define_host_response() + self.fake_host_definition_info = test_utils.get_fake_host_definition_info() + + def test_get_host_definition_manifest_success(self): + mock_generate_host_definition_response_fields_manifest = patch.object( + manifest_utils, 'generate_host_definition_response_fields_manifest').start() + expected_manifest = test_manifest_utils.get_fake_k8s_host_definition_manifest() + mock_generate_host_definition_response_fields_manifest.return_value = \ + test_manifest_utils.get_fake_k8s_host_definition_response_fields_manifest() + result = manifest_utils.get_host_definition_manifest( + self.fake_host_definition_info, self.define_response, test_settings.FAKE_NODE_ID) + self.assertEqual(result[test_settings.SPEC_FIELD], expected_manifest[test_settings.SPEC_FIELD]) + self.assertEqual(result[test_settings.API_VERSION_FIELD], expected_manifest[test_settings.API_VERSION_FIELD]) + self.assertEqual(result[test_settings.KIND_FIELD], expected_manifest[test_settings.KIND_FIELD]) + self.assertEqual(result[test_settings.METADATA_FIELD][common_settings.NAME_FIELD], + test_settings.FAKE_NODE_NAME) + mock_generate_host_definition_response_fields_manifest.assert_called_once_with( + self.fake_host_definition_info.name, self.define_response) + + def test_get_host_definition_status_manifest_success(self): + fake_status_phase_manifest = test_manifest_utils.get_status_phase_manifest(test_settings.READY_PHASE) + result = manifest_utils.get_host_definition_status_manifest(test_settings.READY_PHASE) + self.assertEqual(result, fake_status_phase_manifest) + + def test_get_body_manifest_for_labels_success(self): + fake_labels_body = test_manifest_utils.get_metadata_with_manage_node_labels_manifest( + test_settings.TRUE_STRING) + result = manifest_utils.get_body_manifest_for_labels(test_settings.TRUE_STRING) + self.assertEqual(result, fake_labels_body) + + def test_get_finalizer_manifest_success(self): + fake_finalizers_manifest = test_manifest_utils.get_finalizers_manifest([test_settings.CSI_IBM_FINALIZER, ]) + result = manifest_utils.get_finalizer_manifest( + test_settings.FAKE_NODE_NAME, [test_settings.CSI_IBM_FINALIZER, ]) + self.assertEqual(result, fake_finalizers_manifest) + + def test_generate_host_definition_response_fields_manifest_success(self): + expected_manifest = test_manifest_utils.get_fake_k8s_host_definition_response_fields_manifest() + result = manifest_utils.generate_host_definition_response_fields_manifest( + self.fake_host_definition_info.name, self.define_response) + self.assertEqual(result[test_settings.SPEC_FIELD], expected_manifest[test_settings.SPEC_FIELD]) + self.assertEqual(result[test_settings.METADATA_FIELD][common_settings.NAME_FIELD], + test_settings.FAKE_NODE_NAME) diff --git a/controllers/tests/controller_server/host_definer/host_definer_utils_tests/utils_test.py b/controllers/tests/controller_server/host_definer/host_definer_utils_tests/utils_test.py new file mode 100644 index 000000000..3a9c30048 --- /dev/null +++ b/controllers/tests/controller_server/host_definer/host_definer_utils_tests/utils_test.py @@ -0,0 +1,244 @@ +import unittest +from copy import deepcopy +from unittest.mock import patch + +from controllers.servers.host_definer.utils import utils +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils +import controllers.tests.controller_server.host_definer.utils.k8s_manifests_utils as test_manifest_utils +import controllers.tests.controller_server.host_definer.settings as test_settings +import controllers.common.settings as common_settings +from controllers.servers.errors import ValidationException + + +class TestUtils(unittest.TestCase): + def setUp(self): + self.fake_k8s_metadata = test_utils.get_fake_k8s_metadata() + self.fake_array_connectivity_info = test_utils.get_fake_array_connectivity_info() + self.mock_validate_secrets = patch('{}.validate_secrets'.format(test_settings.UTILS_PATH)).start() + self.mock_get_array_connectivity_info = patch('{}.get_array_connection_info_from_secrets'.format( + test_settings.UTILS_PATH)).start() + self.mock_json = patch('{}.json'.format(test_settings.UTILS_PATH)).start() + self.mock_os = patch('{}.os'.format(test_settings.UTILS_PATH)).start() + + def test_generate_multiple_io_group_from_labels_success(self): + result = utils.generate_io_group_from_labels(test_utils.get_fake_io_group_labels(2)) + self.assertEqual(result, test_settings.FAKE_MULTIPLE_IO_GROUP_STRING) + + def test_generate_single_io_group_from_labels_success(self): + result = utils.generate_io_group_from_labels(test_utils.get_fake_io_group_labels(1)) + self.assertEqual(result, test_settings.FAKE_SINGLE_IO_GROUP_STRING) + + def test_get_k8s_object_resource_version_success(self): + result = utils.get_k8s_object_resource_version(self.fake_k8s_metadata) + self.assertEqual(result, test_settings.FAKE_RESOURCE_VERSION) + + def test_get_k8s_object_resource_version_with_not_default_resource_version_field_success(self): + self.fake_k8s_metadata.metadata.resourceVersion = self.fake_k8s_metadata.metadata.pop( + common_settings.RESOURCE_VERSION_FIELD) + result = utils.get_k8s_object_resource_version(self.fake_k8s_metadata) + self.assertEqual(result, test_settings.FAKE_RESOURCE_VERSION) + + @patch('{}.decode_base64_to_string'.format(test_settings.UTILS_PATH)) + def test_get_secret_config_encoded_in_base64_success(self, mock_decode_base64_to_string): + secret_data = deepcopy(test_settings.FAKE_ENCODED_CONFIG) + mock_decode_base64_to_string.return_value = test_settings.FAKE_DECODED_CONFIG_STRING[ + test_settings.CONFIG_FIELD] + result = utils.change_decode_base64_secret_config(secret_data) + self.assertEqual(result, test_settings.FAKE_DECODED_CONFIG) + mock_decode_base64_to_string.assert_called_once_with( + test_settings.FAKE_ENCODED_CONFIG[test_settings.CONFIG_FIELD]) + + @patch('{}.decode_base64_to_string'.format(test_settings.UTILS_PATH)) + def test_get_decoded_secret_not_encoded_success(self, mock_decode_base64_to_string): + secret_data = deepcopy(test_settings.FAKE_DECODED_CONFIG_STRING) + mock_decode_base64_to_string.return_value = test_settings.FAKE_DECODED_CONFIG_STRING[ + test_settings.CONFIG_FIELD] + result = utils.change_decode_base64_secret_config(secret_data) + self.assertEqual(result, test_settings.FAKE_DECODED_CONFIG) + mock_decode_base64_to_string.assert_called_once_with( + test_settings.FAKE_DECODED_CONFIG_STRING[test_settings.CONFIG_FIELD]) + + def test_get_secret_config_success(self): + secret_data = deepcopy(test_settings.FAKE_DECODED_CONFIG_STRING) + self.mock_json.loads.return_value = test_settings.FAKE_DECODED_CONFIG[test_settings.CONFIG_FIELD] + result = utils.get_secret_config(secret_data) + self.assertEqual(result, test_settings.FAKE_DECODED_CONFIG[test_settings.CONFIG_FIELD]) + self.mock_json.loads.assert_called_once_with( + test_settings.FAKE_DECODED_CONFIG_STRING[test_settings.CONFIG_FIELD]) + + def test_do_not_call_json_load_when_getting_dict_secret_config_success(self): + secret_data = deepcopy(test_settings.FAKE_DECODED_CONFIG) + result = utils.get_secret_config(secret_data) + self.assertEqual(result, test_settings.FAKE_DECODED_CONFIG[test_settings.CONFIG_FIELD]) + self.mock_json.loads.assert_not_called() + + def test_get_secret_config_from_secret_data_with_no_config_field_success(self): + secret_data = deepcopy(test_manifest_utils.get_fake_k8s_secret_manifest()[test_settings.SECRET_DATA_FIELD]) + result = utils.get_secret_config(secret_data) + self.assertEqual(result, {}) + self.mock_json.loads.assert_not_called() + + def test_munch_success(self): + result = utils.munch(test_manifest_utils.get_empty_k8s_list_manifest()) + self.assertEqual(result, test_utils.get_fake_empty_k8s_list()) + + def test_loop_forever_success(self): + result = utils.loop_forever() + self.assertEqual(result, True) + + def test_validate_secret_success(self): + secret_data = deepcopy(test_settings.FAKE_DECODED_CONFIG) + self.mock_json.dumps.return_value = test_settings.FAKE_DECODED_CONFIG_STRING[test_settings.CONFIG_FIELD] + utils.validate_secret(secret_data) + self.mock_validate_secrets.assert_called_once_with(test_settings.FAKE_DECODED_CONFIG_STRING) + self.mock_json.dumps.assert_called_once_with(test_settings.FAKE_DECODED_CONFIG[test_settings.CONFIG_FIELD]) + + def test_do_not_call_json_dumps_when_getting_string_secret_config_on_validate_secret_success(self): + secret_data = deepcopy(test_settings.FAKE_DECODED_CONFIG_STRING) + utils.validate_secret(secret_data) + self.mock_validate_secrets.assert_called_once_with(test_settings.FAKE_DECODED_CONFIG_STRING) + self.mock_json.dumps.assert_not_called() + + def test_validate_secret_from_secret_data_with_no_config_field_success(self): + secret_data = deepcopy(test_manifest_utils.get_fake_k8s_secret_manifest()[test_settings.SECRET_DATA_FIELD]) + utils.validate_secret(secret_data) + self.mock_validate_secrets.assert_called_once_with(secret_data) + self.mock_json.dumps.assert_not_called() + + def test_validate_secret_handle_validation_error_success(self): + secret_data = deepcopy(test_manifest_utils.get_fake_k8s_secret_manifest()[test_settings.SECRET_DATA_FIELD]) + self.mock_validate_secrets.side_effect = ValidationException('message') + utils.validate_secret(secret_data) + self.mock_validate_secrets.assert_called_once_with(secret_data) + self.mock_json.dumps.assert_not_called() + + def test_get_prefix_success(self): + self.mock_os.getenv.return_value = test_settings.TRUE_STRING + result = utils.get_prefix() + self.assertEqual(result, test_settings.TRUE_STRING) + self.mock_os.getenv.assert_called_once_with(test_settings.PREFIX_ENV_VAR) + + def test_get_connectivity_type_when_it_set_in_the_labels_success(self): + result = utils.get_connectivity_type_from_user(test_settings.ISCSI_CONNECTIVITY_TYPE) + self.assertEqual(result, test_settings.ISCSI_CONNECTIVITY_TYPE) + self.mock_os.getenv.assert_not_called() + + def test_get_connectivity_type_when_it_set_in_the_env_vars_success(self): + self.mock_os.getenv.return_value = test_settings.ISCSI_CONNECTIVITY_TYPE + result = utils.get_connectivity_type_from_user('') + self.assertEqual(result, test_settings.ISCSI_CONNECTIVITY_TYPE) + self.mock_os.getenv.assert_called_once_with(test_settings.CONNECTIVITY_ENV_VAR) + + def test_is_topology_label_true_success(self): + result = utils.is_topology_label(test_settings.FAKE_TOPOLOGY_LABEL) + self.assertEqual(result, True) + + def test_is_topology_label_false_success(self): + result = utils.is_topology_label(test_settings.FAKE_LABEL) + self.assertEqual(result, False) + + @patch('{}.decode_array_connectivity_info'.format(test_settings.UTILS_PATH)) + def test_get_array_connectivity_info_from_secret_config_success(self, mock_decode): + connectivity_info = 'connectivity_info' + secret_data = deepcopy(test_settings.FAKE_DECODED_CONFIG) + self.mock_json.dumps.return_value = test_settings.FAKE_DECODED_CONFIG_STRING[test_settings.CONFIG_FIELD] + self.mock_get_array_connectivity_info.return_value = connectivity_info + mock_decode.return_value = connectivity_info + result = utils.get_array_connection_info_from_secret_data(secret_data, []) + self.assertEqual(result, connectivity_info) + self.mock_get_array_connectivity_info.assert_called_once_with(secret_data, []) + mock_decode.assert_called_once_with(connectivity_info) + self.mock_json.dumps.assert_called_once_with(test_settings.FAKE_DECODED_CONFIG[test_settings.CONFIG_FIELD]) + + @patch('{}.decode_array_connectivity_info'.format(test_settings.UTILS_PATH)) + def test_do_not_call_json_dumps_when_getting_string_secret_config_on_get_array_info_success(self, mock_decode): + secret_data = deepcopy(test_settings.FAKE_DECODED_CONFIG_STRING) + self.mock_get_array_connectivity_info.return_value = None + mock_decode.return_value = None + result = utils.get_array_connection_info_from_secret_data(secret_data, []) + self.assertEqual(result, None) + self.mock_get_array_connectivity_info.assert_called_once_with(secret_data, []) + mock_decode.assert_called_once_with(None) + self.mock_json.dumps.assert_not_called() + + @patch('{}.decode_array_connectivity_info'.format(test_settings.UTILS_PATH)) + def test_get_array_info_from_secret_data_with_no_config_field_success(self, mock_decode): + secret_data = deepcopy(test_manifest_utils.get_fake_k8s_secret_manifest()[test_settings.SECRET_DATA_FIELD]) + self.mock_get_array_connectivity_info.return_value = None + mock_decode.return_value = None + result = utils.get_array_connection_info_from_secret_data(secret_data, []) + self.assertEqual(result, None) + self.mock_get_array_connectivity_info.assert_called_once_with(secret_data, []) + mock_decode.assert_called_once_with(None) + self.mock_json.dumps.assert_not_called() + + @patch('{}.decode_array_connectivity_info'.format(test_settings.UTILS_PATH)) + def test_get_array_connection_info_from_secret_data_handle_validation_error_success(self, mock_decode): + secret_data = deepcopy(test_manifest_utils.get_fake_k8s_secret_manifest()[test_settings.SECRET_DATA_FIELD]) + self.mock_get_array_connectivity_info.side_effect = ValidationException('message') + result = utils.get_array_connection_info_from_secret_data(secret_data, []) + self.assertEqual(result, None) + self.mock_get_array_connectivity_info.assert_called_once_with(secret_data, []) + mock_decode.assert_not_called() + self.mock_json.dumps.assert_not_called() + + @patch('{}.decode_base64_to_string'.format(test_settings.UTILS_PATH)) + def test_decode_array_connectivity_info_success(self, mock_decode): + mock_decode.side_effect = [test_settings.FAKE_SECRET_ARRAY, + test_settings.FAKE_SECRET_USER_NAME, test_settings.FAKE_SECRET_PASSWORD] + result = utils.decode_array_connectivity_info(self.fake_array_connectivity_info) + self.assertEqual(result, self.fake_array_connectivity_info) + self.assertEqual(mock_decode.call_count, 3) + + def test_decode_base64_to_string_success(self): + result = utils.decode_base64_to_string(test_settings.BASE64_STRING) + self.assertIn(test_settings.DECODED_BASE64_STRING, result) + + def test_decode_base64_to_string_handle_getting_decoded_string_success(self): + result = utils.decode_base64_to_string(test_settings.DECODED_BASE64_STRING) + self.assertEqual(result, test_settings.DECODED_BASE64_STRING) + + def test_get_random_string_success(self): + result = utils.get_random_string() + self.assertEqual(type(result), str) + self.assertEqual(len(result), 20) + + def test_return_true_when_watch_object_is_deleted(self): + result = utils.is_watch_object_type_is_delete(test_settings.DELETED_EVENT_TYPE) + self.assertTrue(result) + + def test_return_false_when_watch_object_is_not_deleted(self): + result = utils.is_watch_object_type_is_delete(test_settings.ADDED_EVENT) + self.assertFalse(result) + + def test_return_true_when_host_definer_can_delete_hosts_success(self): + self.mock_os.getenv.return_value = test_settings.TRUE_STRING + result = utils.is_host_definer_can_delete_hosts() + self.assertTrue(result) + self.mock_os.getenv.assert_called_once_with(test_settings.ALLOW_DELETE_ENV_VAR) + + def test_return_false_when_host_definer_cannot_delete_hosts_success(self): + self.mock_os.getenv.return_value = '' + result = utils.is_host_definer_can_delete_hosts() + self.assertFalse(result) + self.mock_os.getenv.assert_called_once_with(test_settings.ALLOW_DELETE_ENV_VAR) + + def test_return_true_when_dynamic_node_labeling_allowed_success(self): + self.mock_os.getenv.return_value = test_settings.TRUE_STRING + result = utils.is_dynamic_node_labeling_allowed() + self.assertTrue(result) + self.mock_os.getenv.assert_called_once_with(test_settings.DYNAMIC_NODE_LABELING_ENV_VAR) + + def test_return_false_when_dynamic_node_labeling_is_not_allowed_success(self): + self.mock_os.getenv.return_value = '' + result = utils.is_dynamic_node_labeling_allowed() + self.assertFalse(result) + self.mock_os.getenv.assert_called_once_with(test_settings.DYNAMIC_NODE_LABELING_ENV_VAR) + + def test_get_define_action_when_phase_is_pending_creation(self): + result = utils.get_action(test_settings.PENDING_CREATION_PHASE) + self.assertEqual(result, test_settings.DEFINE_ACTION) + + def test_get_undefine_action_when_phase_is_not_pending_creation(self): + result = utils.get_action(test_settings.PENDING_DELETION_PHASE) + self.assertEqual(result, test_settings.UNDEFINE_ACTION) diff --git a/controllers/tests/controller_server/host_definer/host_definition_watcher_test.py b/controllers/tests/controller_server/host_definer/host_definition_watcher_test.py deleted file mode 100644 index fa8696433..000000000 --- a/controllers/tests/controller_server/host_definer/host_definition_watcher_test.py +++ /dev/null @@ -1,88 +0,0 @@ -from unittest.mock import Mock - -import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils -import controllers.tests.controller_server.host_definer.settings as test_settings -import controllers.common.settings as common_settings -from controllers.tests.controller_server.host_definer.common import BaseSetUp -from controllers.servers.host_definer.types import DefineHostResponse -from controllers.servers.host_definer.watcher.host_definition_watcher import HostDefinitionWatcher - - -class HostDefinitionWatcherBase(BaseSetUp): - def setUp(self): - super().setUp() - self.host_definition_watcher = test_utils.get_class_mock(HostDefinitionWatcher) - self.nodes_on_watcher_helper[test_settings.FAKE_NODE_NAME] = test_utils.get_fake_managed_node() - - -class TestWatchHostDefinitionsResources(HostDefinitionWatcherBase): - def setUp(self): - super().setUp() - self.host_definition_watcher._get_k8s_object_resource_version = Mock() - self.host_definition_watcher._get_k8s_object_resource_version.return_value = test_settings.FAKE_RESOURCE_VERSION - - def test_events_on_host_definition_in_ready_state(self): - self.host_definition_watcher._define_host_definition_after_pending_state = Mock() - self.host_definition_watcher.host_definitions_api.watch.return_value = iter( - [test_utils.get_fake_host_definition_watch_event(test_settings.MODIFIED_EVENT_TYPE, - test_settings.READY_PHASE)]) - test_utils.run_function_with_timeout(self.host_definition_watcher.watch_host_definitions_resources, 0.5) - self.host_definition_watcher._define_host_definition_after_pending_state.assert_not_called() - - def test_pending_deletion_that_managed_to_be_deleted_log_messages(self): - self._prepare_default_mocks_for_pending_deletion() - test_utils.run_function_with_timeout(self.host_definition_watcher.watch_host_definitions_resources, 0.5) - self.host_definition_watcher.csi_nodes_api.get.assert_called_with(name=test_settings.FAKE_NODE_NAME) - - def test_set_error_event_on_pending_deletion(self): - self._prepare_default_mocks_for_pending_deletion() - self.host_definition_watcher.storage_host_servicer.undefine_host.return_value = DefineHostResponse( - error_message=test_settings.FAIL_MESSAGE_FROM_STORAGE) - test_utils.patch_pending_variables() - test_utils.run_function_with_timeout(self.host_definition_watcher.watch_host_definitions_resources, 0.5) - self.assertEqual(self.host_definition_watcher.storage_host_servicer.undefine_host.call_count, - test_settings.HOST_DEFINITION_PENDING_VARS['HOST_DEFINITION_PENDING_RETRIES']) - - def _prepare_default_mocks_for_pending_deletion(self): - self.host_definition_watcher.host_definitions_api.watch.return_value = iter( - [test_utils.get_fake_host_definition_watch_event(test_settings.MODIFIED_EVENT_TYPE, - test_settings.PENDING_DELETION_PHASE)]) - self._prepare_default_mocks_for_pending() - self.os.getenv.return_value = test_settings.TRUE_STRING - self.host_definition_watcher.core_api.read_node.return_value = self.k8s_node_with_manage_node_label - self.host_definition_watcher.storage_host_servicer.undefine_host.return_value = DefineHostResponse() - self.host_definition_watcher.csi_nodes_api.get.return_value = test_utils.get_fake_k8s_csi_node( - test_settings.CSI_PROVISIONER_NAME) - - def test_handle_pending_host_definition_that_became_ready(self): - self._prepare_default_mocks_for_pending_creation() - self.host_definition_watcher.host_definitions_api.get.return_value = self.ready_k8s_host_definitions - test_utils.patch_pending_variables() - self.host_definition_watcher.core_api.read_node.return_value = self.k8s_node_with_fake_label - test_utils.run_function_with_timeout(self.host_definition_watcher.watch_host_definitions_resources, 0.5) - self.host_definition_watcher.storage_host_servicer.define_host.assert_called_once_with( - test_utils.get_define_request(node_id_from_host_definition=test_settings.FAKE_NODE_ID)) - - def test_pending_creation_that_managed_to_be_created(self): - self._prepare_default_mocks_for_pending_creation() - self.host_definition_watcher._loop_forever = Mock() - self.host_definition_watcher._loop_forever.side_effect = [True, False] - test_utils.run_function_with_timeout(self.host_definition_watcher.watch_host_definitions_resources, 0.5) - self.host_definition_watcher.custom_object_api.patch_cluster_custom_object_status.assert_called_once_with( - common_settings.CSI_IBM_GROUP, common_settings.VERSION, - common_settings.HOST_DEFINITION_PLURAL, test_settings.FAKE_NODE_NAME, - test_utils.get_ready_status_manifest()) - - def _prepare_default_mocks_for_pending_creation(self): - self.host_definition_watcher.host_definitions_api.watch.return_value = iter( - [test_utils.get_fake_host_definition_watch_event(test_settings.MODIFIED_EVENT_TYPE, - test_settings.PENDING_CREATION_PHASE)]) - self.host_definition_watcher.storage_host_servicer.define_host.return_value = DefineHostResponse() - self.os.getenv.return_value = '' - self.host_definition_watcher.core_api.read_node.return_value = self.k8s_node_with_manage_node_label - self._prepare_default_mocks_for_pending() - - def _prepare_default_mocks_for_pending(self): - self.host_definition_watcher.core_api.read_namespaced_secret.return_value = test_utils.get_fake_k8s_secret() - self.host_definition_watcher.host_definitions_api.get.return_value = \ - test_utils.get_fake_k8s_host_definitions_items(test_settings.PENDING_DELETION_PHASE) diff --git a/controllers/tests/controller_server/host_definer/k8s/__init__.py b/controllers/tests/controller_server/host_definer/k8s/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/controllers/tests/controller_server/host_definer/k8s/kubernetes_api_test.py b/controllers/tests/controller_server/host_definer/k8s/kubernetes_api_test.py new file mode 100644 index 000000000..69d34dfae --- /dev/null +++ b/controllers/tests/controller_server/host_definer/k8s/kubernetes_api_test.py @@ -0,0 +1,257 @@ +import unittest +from unittest.mock import MagicMock, patch +from kubernetes.client.rest import ApiException +from kubernetes.watch import Watch + +from controllers.servers.host_definer.k8s.api import K8SApi +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils +import controllers.tests.controller_server.host_definer.utils.k8s_manifests_utils as test_manifest_utils +import controllers.tests.controller_server.host_definer.settings as test_settings +import controllers.common.settings as common_settings + + +class TestKubernetesApi(unittest.TestCase): + def setUp(self): + test_utils.patch_k8s_api_init() + self.k8s_api = K8SApi() + self.not_found_api_exception = ApiException(http_resp=test_utils.get_error_http_resp(404)) + self.general_api_exception = ApiException(http_resp=test_utils.get_error_http_resp(405)) + self.k8s_api.csi_nodes_api = MagicMock() + self.k8s_api.host_definitions_api = MagicMock() + self.k8s_api.custom_object_api = MagicMock() + self.k8s_api.core_api = MagicMock() + self.k8s_api.apps_api = MagicMock() + self.k8s_api.storage_api = MagicMock() + self.mock_stream = patch.object(Watch, 'stream').start() + + def test_get_csi_node_success(self): + self.k8s_api.csi_nodes_api.get.return_value = test_utils.get_fake_k8s_csi_node() + result = self.k8s_api.get_csi_node(test_settings.FAKE_NODE_NAME) + self.assertEqual(result, test_utils.get_fake_k8s_csi_node()) + self.k8s_api.csi_nodes_api.get.assert_called_once_with(name=test_settings.FAKE_NODE_NAME) + + def test_get_csi_node_not_found(self): + self.k8s_api.csi_nodes_api.get.side_effect = self.not_found_api_exception + result = self.k8s_api.get_csi_node(test_settings.FAKE_NODE_NAME) + self.assertEqual(result, None) + + def test_get_csi_node_failure(self): + self.k8s_api.csi_nodes_api.get.side_effect = self.general_api_exception + result = self.k8s_api.get_csi_node(test_settings.FAKE_NODE_NAME) + self.assertEqual(result, None) + + def test_list_host_definition_success(self): + self.k8s_api.host_definitions_api.get.return_value = test_utils.get_fake_k8s_host_definitions_items() + result = self.k8s_api.list_host_definition() + self.assertEqual(result, test_utils.get_fake_k8s_host_definitions_items()) + self.k8s_api.host_definitions_api.get.assert_called_once_with() + + def test_list_host_definition_failure(self): + self._test_list_k8s_resource_failure(self.k8s_api.list_host_definition, self.k8s_api.host_definitions_api.get) + + def test_create_host_definition_success(self): + self.k8s_api.host_definitions_api.create.return_value = test_utils.get_fake_empty_k8s_list() + result = self.k8s_api.create_host_definition(test_manifest_utils.get_fake_k8s_host_definition_manifest()) + self.assertEqual(result, test_utils.get_fake_empty_k8s_list()) + self.k8s_api.host_definitions_api.create.assert_called_once_with( + body=test_manifest_utils.get_fake_k8s_host_definition_manifest()) + + def test_create_host_definition_failure(self): + self.k8s_api.host_definitions_api.create.side_effect = self.general_api_exception + result = self.k8s_api.create_host_definition(test_manifest_utils.get_fake_k8s_host_definition_manifest()) + self.assertEqual(result, None) + + def test_patch_cluster_custom_object_status_success(self): + self.k8s_api.csi_nodes_api.get.return_value = None + self.k8s_api.patch_cluster_custom_object_status( + common_settings.CSI_IBM_GROUP, common_settings.VERSION, common_settings.HOST_DEFINITION_PLURAL, + test_settings.FAKE_NODE_NAME, test_settings.READY_PHASE) + self.k8s_api.custom_object_api.patch_cluster_custom_object_status.assert_called_with( + common_settings.CSI_IBM_GROUP, common_settings.VERSION, + common_settings.HOST_DEFINITION_PLURAL, test_settings.FAKE_NODE_NAME, + test_settings.READY_PHASE) + + def test_create_event_success(self): + self.k8s_api.core_api.create_namespaced_event.return_value = None + self.k8s_api.create_event(test_settings.FAKE_SECRET_NAMESPACE, test_utils.get_fake_empty_k8s_list()) + self.k8s_api.core_api.create_namespaced_event.assert_called_with( + test_settings.FAKE_SECRET_NAMESPACE, test_utils.get_fake_empty_k8s_list()) + + def test_delete_host_definition_success(self): + self.k8s_api.host_definitions_api.delete.return_value = test_utils.get_fake_k8s_host_definitions_items() + result = self.k8s_api.delete_host_definition(test_settings.FAKE_NODE_NAME) + self.assertEqual(result, test_utils.get_fake_k8s_host_definitions_items()) + self.k8s_api.host_definitions_api.delete.assert_called_once_with(name=test_settings.FAKE_NODE_NAME, body={}) + + def test_delete_host_definition_failure(self): + self.k8s_api.host_definitions_api.delete.side_effect = self.general_api_exception + result = self.k8s_api.delete_host_definition(test_settings.FAKE_NODE_NAME) + self.assertEqual(result, None) + + def test_patch_host_definition_success(self): + self.k8s_api.host_definitions_api.patch.return_value = None + result = self.k8s_api.patch_host_definition(test_manifest_utils.get_fake_k8s_host_definition_manifest()) + self.assertEqual(result, 200) + self.k8s_api.host_definitions_api.patch.assert_called_once_with( + name=test_settings.FAKE_NODE_NAME, body=test_manifest_utils.get_fake_k8s_host_definition_manifest(), + content_type='application/merge-patch+json') + + def test_patch_host_definition_failure(self): + self.k8s_api.host_definitions_api.patch.side_effect = self.not_found_api_exception + result = self.k8s_api.patch_host_definition(test_manifest_utils.get_fake_k8s_host_definition_manifest()) + self.assertEqual(result, 404) + + def test_patch_node_success(self): + self.k8s_api.core_api.patch_node.return_value = None + self.k8s_api.patch_node(test_settings.FAKE_NODE_NAME, test_manifest_utils.get_fake_k8s_node_manifest( + test_settings.MANAGE_NODE_LABEL)) + self.k8s_api.core_api.patch_node.assert_called_once_with( + test_settings.FAKE_NODE_NAME, test_manifest_utils.get_fake_k8s_node_manifest( + test_settings.MANAGE_NODE_LABEL)) + + def test_get_secret_data_success(self): + self.k8s_api.core_api.read_namespaced_secret.return_value = test_utils.get_fake_k8s_secret() + result = self.k8s_api.get_secret_data(test_settings.FAKE_SECRET, test_settings.FAKE_SECRET_NAMESPACE) + self.assertEqual(result, test_utils.get_fake_k8s_secret().data) + self.k8s_api.core_api.read_namespaced_secret.assert_called_once_with( + name=test_settings.FAKE_SECRET, namespace=test_settings.FAKE_SECRET_NAMESPACE) + + def test_get_secret_data_failure(self): + self.k8s_api.core_api.read_namespaced_secret.side_effect = self.not_found_api_exception + result = self.k8s_api.get_secret_data(test_settings.FAKE_SECRET, test_settings.FAKE_SECRET_NAMESPACE) + self.assertEqual(result, {}) + + def test_read_node_success(self): + self.k8s_api.core_api.read_node.return_value = test_utils.get_fake_k8s_node(test_settings.MANAGE_NODE_LABEL) + result = self.k8s_api.read_node(test_settings.FAKE_NODE_NAME) + self.assertEqual(result, test_utils.get_fake_k8s_node(test_settings.MANAGE_NODE_LABEL)) + self.k8s_api.core_api.read_node.assert_called_once_with(name=test_settings.FAKE_NODE_NAME) + + def test_read_node_failure(self): + self.k8s_api.core_api.read_node.side_effect = self.not_found_api_exception + result = self.k8s_api.read_node(test_settings.FAKE_NODE_NAME) + self.assertEqual(result, None) + + def test_list_daemon_set_for_all_namespaces_success(self): + self.k8s_api.apps_api.list_daemon_set_for_all_namespaces.return_value = \ + test_utils.get_fake_k8s_daemon_set_items(0, 0) + result = self.k8s_api.list_daemon_set_for_all_namespaces(test_settings.MANAGE_NODE_LABEL) + self.assertEqual(result, test_utils.get_fake_k8s_daemon_set_items(0, 0)) + self.k8s_api.apps_api.list_daemon_set_for_all_namespaces.assert_called_once_with( + label_selector=test_settings.MANAGE_NODE_LABEL) + + def test_list_daemon_set_for_all_namespaces_failure(self): + self.k8s_api.apps_api.list_daemon_set_for_all_namespaces.side_effect = self.general_api_exception + result = self.k8s_api.list_daemon_set_for_all_namespaces(test_settings.FAKE_NODE_NAME) + self.assertEqual(result, None) + + def test_list_pod_for_all_namespaces_success(self): + self.k8s_api.core_api.list_pod_for_all_namespaces.return_value = \ + test_utils.get_fake_k8s_daemon_set_items(0, 0) + result = self.k8s_api.list_pod_for_all_namespaces(test_settings.MANAGE_NODE_LABEL) + self.assertEqual(result, test_utils.get_fake_k8s_daemon_set_items(0, 0)) + self.k8s_api.core_api.list_pod_for_all_namespaces.assert_called_once_with( + label_selector=test_settings.MANAGE_NODE_LABEL) + + def test_list_pod_for_all_namespaces_failure(self): + self.k8s_api.core_api.list_pod_for_all_namespaces.side_effect = self.general_api_exception + result = self.k8s_api.list_pod_for_all_namespaces(test_settings.FAKE_NODE_NAME) + self.assertEqual(result, None) + + @patch('{}.utils'.format(test_settings.K8S_API_PATH)) + def test_get_storage_class_stream_success(self, mock_utils): + self._test_basic_resource_stream_success( + self.k8s_api.get_storage_class_stream, self.k8s_api.storage_api.list_storage_class, mock_utils) + + @patch('{}.utils'.format(test_settings.K8S_API_PATH)) + def test_get_node_stream_success(self, mock_utils): + self._test_basic_resource_stream_success(self.k8s_api.get_node_stream, + self.k8s_api.core_api.list_node, mock_utils) + + @patch('{}.utils'.format(test_settings.K8S_API_PATH)) + def test_get_secret_stream_success(self, mock_utils): + self._test_basic_resource_stream_success(self.k8s_api.get_secret_stream, + self.k8s_api.core_api.list_secret_for_all_namespaces, + mock_utils) + + def _test_basic_resource_stream_success(self, function_to_test, k8s_function, mock_utils): + mock_utils.get_k8s_object_resource_version.return_value = test_settings.FAKE_RESOURCE_VERSION + result = function_to_test() + k8s_function.assert_called_once() + self.mock_stream.assert_called_once_with(k8s_function, resource_version=test_settings.FAKE_RESOURCE_VERSION, + timeout_seconds=5) + self.assertEqual(result, self.mock_stream.return_value) + + def test_get_storage_class_stream_failure(self): + self._test_basic_resource_stream_failure(self.k8s_api.get_storage_class_stream) + + def test_get_node_stream_failure(self): + self._test_basic_resource_stream_failure(self.k8s_api.get_node_stream) + + def test_get_secret_stream_failure(self): + self._test_basic_resource_stream_failure(self.k8s_api.get_secret_stream) + + def _test_basic_resource_stream_failure(self, function_to_test): + self.mock_stream.side_effect = self.general_api_exception + with self.assertRaises(ApiException): + function_to_test() + + def test_list_storage_class_success(self): + self.k8s_api.storage_api.list_storage_class.return_value = \ + test_utils.get_fake_k8s_storage_class_items(test_settings.CSI_PROVISIONER_NAME) + result = self.k8s_api.list_storage_class() + self.assertEqual(result, test_utils.get_fake_k8s_storage_class_items(test_settings.CSI_PROVISIONER_NAME)) + + def test_list_storage_class_failure(self): + self._test_list_k8s_resource_failure(self.k8s_api.list_storage_class, + self.k8s_api.storage_api.list_storage_class) + + def test_list_node_success(self): + self.k8s_api.core_api.list_node.return_value = test_utils.get_fake_k8s_nodes_items() + result = self.k8s_api.list_node() + self.assertEqual(result, test_utils.get_fake_k8s_nodes_items()) + + def test_list_node_failure(self): + self._test_list_k8s_resource_failure(self.k8s_api.list_node, self.k8s_api.core_api.list_node) + + def test_list_csi_node_success(self): + self.k8s_api.csi_nodes_api.get.return_value = test_utils.get_fake_k8s_csi_node( + test_settings.CSI_PROVISIONER_NAME) + result = self.k8s_api.list_csi_node() + self.assertEqual(result, test_utils.get_fake_k8s_csi_node(test_settings.CSI_PROVISIONER_NAME)) + + def test_list_csi_node_failure(self): + self._test_list_k8s_resource_failure(self.k8s_api.list_csi_node, self.k8s_api.csi_nodes_api.get) + + def _test_list_k8s_resource_failure(self, function_to_test, k8s_function): + k8s_function.side_effect = self.general_api_exception + result = function_to_test() + self.assertEqual(result, test_utils.get_fake_empty_k8s_list()) + + def test_get_host_definition_stream_success(self): + expected_output = iter([]) + self.k8s_api.host_definitions_api.watch.return_value = expected_output + result = self.k8s_api.get_host_definition_stream(test_settings.FAKE_RESOURCE_VERSION, 5) + self.k8s_api.host_definitions_api.watch.assert_called_once_with( + resource_version=test_settings.FAKE_RESOURCE_VERSION, timeout=5) + self.assertEqual(result, expected_output) + + def test_get_host_definition_stream_failure(self): + self.k8s_api.host_definitions_api.watch.side_effect = self.general_api_exception + with self.assertRaises(ApiException): + self.k8s_api.get_host_definition_stream(test_settings.FAKE_RESOURCE_VERSION, 5) + + @patch('{}.utils'.format(test_settings.K8S_API_PATH)) + def test_get_csi_node_stream_success(self, mock_utils): + expected_output = iter([]) + mock_utils.get_k8s_object_resource_version.return_value = test_settings.FAKE_RESOURCE_VERSION + self.k8s_api.csi_nodes_api.watch.return_value = expected_output + result = self.k8s_api.get_csi_node_stream() + self.k8s_api.csi_nodes_api.watch.assert_called_once_with( + resource_version=test_settings.FAKE_RESOURCE_VERSION, timeout=5) + self.assertEqual(result, expected_output) + + def test_get_csi_node_stream_failure(self): + self.k8s_api.csi_nodes_api.watch.side_effect = self.general_api_exception + with self.assertRaises(ApiException): + self.k8s_api.get_csi_node_stream() diff --git a/controllers/tests/controller_server/host_definer/node_watcher_test.py b/controllers/tests/controller_server/host_definer/node_watcher_test.py deleted file mode 100644 index cc37725f0..000000000 --- a/controllers/tests/controller_server/host_definer/node_watcher_test.py +++ /dev/null @@ -1,109 +0,0 @@ -from unittest.mock import Mock, patch - -import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils -import controllers.tests.controller_server.host_definer.settings as test_settings -from controllers.tests.controller_server.host_definer.common import BaseSetUp -from controllers.servers.host_definer.watcher.node_watcher import NodeWatcher - - -class NodeWatcherBase(BaseSetUp): - def setUp(self): - super().setUp() - self.node_watcher = test_utils.get_class_mock(NodeWatcher) - self.unmanaged_csi_nodes_with_driver = patch( - '{}.unmanaged_csi_nodes_with_driver'.format(test_settings.NODES_WATCHER_PATH), set()).start() - self.expected_unmanaged_csi_nodes_with_driver = set() - self.expected_unmanaged_csi_nodes_with_driver.add(test_settings.FAKE_NODE_NAME) - self.nodes_on_node_watcher = test_utils.patch_nodes_global_variable(test_settings.NODES_WATCHER_PATH) - - -class TestAddInitialNodes(NodeWatcherBase): - def test_host_definer_does_not_delete_host_definitions_on_node_with_csi_node(self): - self._prepare_default_mocks_for_node() - self.node_watcher.add_initial_nodes() - self.node_watcher.storage_host_servicer.undefine_host.assert_not_called() - - def test_host_definer_deletes_host_definitions_on_node_with_csi_node(self): - self._prepare_default_mocks_for_node() - self.node_watcher.csi_nodes_api.get.return_value = test_utils.get_fake_k8s_csi_node( - test_settings.FAKE_CSI_PROVISIONER) - self.os.getenv.side_effect = [test_settings.TRUE_STRING, test_settings.FAKE_PREFIX, - '', test_settings.TRUE_STRING, test_settings.TRUE_STRING] - self.node_watcher.add_initial_nodes() - self.node_watcher.storage_host_servicer.undefine_host.assert_called_once_with(test_utils.get_define_request( - prefix=test_settings.FAKE_PREFIX, node_id_from_host_definition=test_settings.FAKE_NODE_ID)) - - def test_if_detect_unmanaged_node_with_csi_node(self): - self._prepare_default_mocks_for_node() - self.os.getenv.return_value = '' - self.node_watcher.core_api.read_node.return_value = self.k8s_node_with_fake_label - self.node_watcher.add_initial_nodes() - self.assertEqual(self.expected_unmanaged_csi_nodes_with_driver, self.unmanaged_csi_nodes_with_driver) - - def _prepare_default_mocks_for_node(self): - self.node_watcher.core_api.list_node.return_value = test_utils.get_fake_k8s_nodes_items() - self.node_watcher.csi_nodes_api.get.return_value = test_utils.get_fake_k8s_csi_node( - test_settings.CSI_PROVISIONER_NAME) - self.node_watcher.core_api.read_node.return_value = self.k8s_node_with_manage_node_label - self.node_watcher.host_definitions_api.get.return_value = self.ready_k8s_host_definitions - self.os.getenv.return_value = test_settings.TRUE_STRING - self.node_watcher.core_api.read_namespaced_secret.return_value = test_utils.get_fake_k8s_secret() - self.nodes_on_watcher_helper[test_settings.FAKE_NODE_NAME] = test_utils.get_fake_managed_node() - - -class TestWatchNodesResources(NodeWatcherBase): - def setUp(self): - super().setUp() - self.node_watcher._get_k8s_object_resource_version = Mock() - self.node_watcher._get_k8s_object_resource_version.return_value = test_settings.FAKE_RESOURCE_VERSION - self.nodes_stream = patch('{}.watch.Watch.stream'.format(test_settings.NODES_WATCHER_PATH)).start() - self.node_watcher._loop_forever = Mock() - self.node_watcher._loop_forever.side_effect = [True, False] - - def test_no_call_for_unmanaged_nodes_list_when_node_is_managed_already(self): - self._prepare_default_mocks_for_modified_event() - self.node_watcher.watch_nodes_resources() - self.expected_unmanaged_csi_nodes_with_driver.clear() - self.assertEqual(self.expected_unmanaged_csi_nodes_with_driver, self.unmanaged_csi_nodes_with_driver) - - def test_catch_node_with_new_manage_node_label(self): - self._prepare_default_mocks_for_modified_event() - self.managed_secrets_on_watcher_helper.append(test_utils.get_fake_secret_info()) - self.node_watcher.watch_nodes_resources() - self.assertEqual(1, len(self.nodes_on_watcher_helper)) - self.node_watcher.storage_host_servicer.define_host.assert_called_once_with( - test_utils.get_define_request(node_id_from_host_definition=test_settings.FAKE_NODE_ID)) - self.expected_unmanaged_csi_nodes_with_driver.clear() - self.assertEqual(self.expected_unmanaged_csi_nodes_with_driver, self.unmanaged_csi_nodes_with_driver) - - def test_do_not_create_host_definitions_on_modified_node_without_csi_node(self): - self._prepare_default_mocks_for_modified_event() - self.nodes_on_node_watcher[test_settings.FAKE_NODE_NAME] = test_utils.get_fake_managed_node() - self.node_watcher.watch_nodes_resources() - self.node_watcher.storage_host_servicer.define_host.assert_not_called() - self.assertEqual(self.expected_unmanaged_csi_nodes_with_driver, self.unmanaged_csi_nodes_with_driver) - - def test_do_not_create_host_definitions_on_modified_node_when_dynamic_node_labeling_enabled(self): - self._prepare_default_mocks_for_modified_event() - self.os.getenv.return_value = test_settings.TRUE_STRING - self.node_watcher.watch_nodes_resources() - self.node_watcher.storage_host_servicer.define_host.assert_not_called() - self.assertEqual(self.expected_unmanaged_csi_nodes_with_driver, self.unmanaged_csi_nodes_with_driver) - - def test_do_not_create_host_definitions_on_modified_node_with_no_manage_node_label(self): - self._prepare_default_mocks_for_modified_event() - self.node_watcher.core_api.read_node.return_value = self.k8s_node_with_fake_label - self.node_watcher.watch_nodes_resources() - self.node_watcher.storage_host_servicer.define_host.assert_not_called() - self.assertEqual(self.expected_unmanaged_csi_nodes_with_driver, self.unmanaged_csi_nodes_with_driver) - - def _prepare_default_mocks_for_modified_event(self): - self.nodes_stream.return_value = iter([test_utils.get_fake_node_watch_event( - test_settings.MODIFIED_EVENT_TYPE)]) - self.node_watcher.csi_nodes_api.get.return_value = test_utils.get_fake_k8s_csi_node( - test_settings.CSI_PROVISIONER_NAME) - self.os.getenv.return_value = '' - self.node_watcher.core_api.read_node.return_value = self.k8s_node_with_manage_node_label - self.node_watcher.host_definitions_api.get.return_value = test_utils.get_empty_k8s_host_definitions() - self.node_watcher.core_api.read_namespaced_secret.return_value = test_utils.get_fake_k8s_secret() - self.unmanaged_csi_nodes_with_driver.add(test_settings.FAKE_NODE_NAME) diff --git a/controllers/tests/controller_server/host_definer/resource_manager/__init__.py b/controllers/tests/controller_server/host_definer/resource_manager/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/controllers/tests/controller_server/host_definer/resource_manager/base_resource_manager.py b/controllers/tests/controller_server/host_definer/resource_manager/base_resource_manager.py new file mode 100644 index 000000000..00241a92d --- /dev/null +++ b/controllers/tests/controller_server/host_definer/resource_manager/base_resource_manager.py @@ -0,0 +1,22 @@ +import unittest + +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils + + +class BaseResourceManager(unittest.TestCase): + def setUp(self): + test_utils.patch_k8s_api_init() + + def _test_get_k8s_resources_info_success(self, function_to_test, k8s_function, + get_info_function, fake_resource_info, fake_k8s_items): + k8s_function.return_value = fake_k8s_items + get_info_function.return_value = fake_resource_info + result = function_to_test() + self.assertEqual(result, [fake_resource_info]) + get_info_function.assert_called_once_with(fake_k8s_items.items[0]) + + def _test_get_k8s_resources_info_empty_list_success(self, function_to_test, k8s_function, info_function): + k8s_function.return_value = test_utils.get_fake_empty_k8s_list() + result = function_to_test() + self.assertEqual(result, []) + info_function.assert_not_called() diff --git a/controllers/tests/controller_server/host_definer/resource_manager/csi_node_manager_test.py b/controllers/tests/controller_server/host_definer/resource_manager/csi_node_manager_test.py new file mode 100644 index 000000000..45575a905 --- /dev/null +++ b/controllers/tests/controller_server/host_definer/resource_manager/csi_node_manager_test.py @@ -0,0 +1,81 @@ +from unittest.mock import MagicMock +from copy import deepcopy + +from controllers.tests.controller_server.host_definer.resource_manager.base_resource_manager import BaseResourceManager +from controllers.servers.host_definer.resource_manager.csi_node import CSINodeManager +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils +import controllers.tests.controller_server.host_definer.settings as test_settings + + +class TestCSINodeManager(BaseResourceManager): + def setUp(self): + super().setUp() + self.csi_node = CSINodeManager() + self.csi_node.k8s_api = MagicMock() + self.csi_node.daemon_set_manager = MagicMock() + self.csi_node.resource_info_manager = MagicMock() + self.fake_csi_node_info = test_utils.get_fake_csi_node_info() + self.fake_pod_info = test_utils.get_fake_pod_info() + self.fake_k8s_csi_nodes_with_ibm_driver = test_utils.get_fake_k8s_csi_nodes( + test_settings.CSI_PROVISIONER_NAME, 1) + self.fake_k8s_csi_nodes_with_non_ibm_driver = test_utils.get_fake_k8s_csi_nodes( + test_settings.FAKE_CSI_PROVISIONER, 1) + + def test_get_csi_nodes_info_with_driver_success(self): + self._test_get_k8s_resources_info_success( + self.csi_node.get_csi_nodes_info_with_driver, self.csi_node.k8s_api.list_csi_node, + self.csi_node.resource_info_manager.generate_csi_node_info, self.fake_csi_node_info, + self.fake_k8s_csi_nodes_with_ibm_driver) + + def test_get_csi_nodes_info_with_driver_empty_list_success(self): + self._test_get_k8s_resources_info_empty_list_success( + self.csi_node.get_csi_nodes_info_with_driver, self.csi_node.k8s_api.list_csi_node, + self.csi_node.resource_info_manager.generate_csi_node_info) + + def test_get_csi_nodes_info_with_driver_non_ibm_driver_success(self): + self.csi_node.k8s_api.list_csi_node.return_value = self.fake_k8s_csi_nodes_with_non_ibm_driver + self.csi_node.resource_info_manager.generate_csi_node_info.return_value = self.fake_csi_node_info + result = self.csi_node.get_csi_nodes_info_with_driver() + self.assertEqual(result, []) + self.csi_node.resource_info_manager.generate_csi_node_info.assert_not_called() + + def test_host_is_part_of_update_when_the_node_has_matching_csi_node_pod(self): + self._test_is_host_part_of_update(True, test_settings.FAKE_DAEMON_SET_NAME, [self.fake_pod_info]) + self.csi_node.resource_info_manager.get_csi_pods_info.assert_called_once_with() + + def test_host_is_not_part_of_update_when_the_node_do_not_have_csi_node_pod_on_it(self): + pod_info = deepcopy(self.fake_pod_info) + pod_info.node_name = 'bad_node_name' + self._test_is_host_part_of_update(False, test_settings.FAKE_DAEMON_SET_NAME, [pod_info]) + self.csi_node.resource_info_manager.get_csi_pods_info.assert_called_once_with() + + def test_host_is_not_part_of_update_when_non_of_the_pods_has_the_daemon_set_name_in_their_name(self): + self._test_is_host_part_of_update(False, 'bad_daemon_set_name', [self.fake_pod_info, self.fake_pod_info]) + self.csi_node.resource_info_manager.get_csi_pods_info.assert_called_once_with() + + def test_host_is_not_part_of_update_when_fail_to_get_daemon_set(self): + self._test_is_host_part_of_update(False, None) + self.csi_node.resource_info_manager.get_csi_pods_info.assert_not_called() + + def _test_is_host_part_of_update(self, expected_result, daemon_set_name, pods_info=None): + self.csi_node.daemon_set_manager.wait_until_all_daemon_set_pods_are_up_to_date.return_value = daemon_set_name + self.csi_node.resource_info_manager.get_csi_pods_info.return_value = pods_info + result = self.csi_node.is_host_part_of_update(test_settings.FAKE_NODE_NAME) + self.assertEqual(result, expected_result) + self.csi_node.daemon_set_manager.wait_until_all_daemon_set_pods_are_up_to_date.assert_called_once_with() + + def test_return_true_when_node_id_changed(self): + self._test_is_node_id_changed(True, test_settings.FAKE_NODE_ID, 'different_node_id') + + def test_return_false_when_node_id_did_not_change(self): + self._test_is_node_id_changed(False, test_settings.FAKE_NODE_ID, test_settings.FAKE_NODE_ID) + + def test_return_false_when_host_definition_node_id_is_none(self): + self._test_is_node_id_changed(False, None, test_settings.FAKE_NODE_ID) + + def test_return_false_when_csi_node_node_id_is_none(self): + self._test_is_node_id_changed(False, test_settings.FAKE_NODE_ID, None) + + def _test_is_node_id_changed(self, expected_result, host_definition_node_id, csi_node_node_id): + result = self.csi_node.is_node_id_changed(host_definition_node_id, csi_node_node_id) + self.assertEqual(result, expected_result) diff --git a/controllers/tests/controller_server/host_definer/resource_manager/daemon_set_manager_test.py b/controllers/tests/controller_server/host_definer/resource_manager/daemon_set_manager_test.py new file mode 100644 index 000000000..a74ca3940 --- /dev/null +++ b/controllers/tests/controller_server/host_definer/resource_manager/daemon_set_manager_test.py @@ -0,0 +1,44 @@ +from unittest.mock import MagicMock + +from controllers.servers.host_definer.resource_manager.daemon_set import DaemonSetManager +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils +import controllers.tests.controller_server.host_definer.settings as test_settings +from controllers.tests.controller_server.host_definer.resource_manager.base_resource_manager import BaseResourceManager + + +class TestEventManagerTest(BaseResourceManager): + def setUp(self): + super().setUp() + self.daemon_set_manager = DaemonSetManager() + self.daemon_set_manager.k8s_api = MagicMock() + self.fake_updated_daemon_set_items = test_utils.get_fake_k8s_daemon_set_items(1, 1) + self.fake_not_updated_daemon_set_items = test_utils.get_fake_k8s_daemon_set_items(0, 1) + self.fake_updated_daemon_set = test_utils.get_fake_k8s_daemon_set(1, 1) + + def test_get_updated_daemon_set_so_it_will_return_the_daemon_set_name(self): + daemon_set_name = self.fake_updated_daemon_set.metadata.name + self._test_wait_for_updated_daemon_set_called_once(self.fake_updated_daemon_set_items, daemon_set_name) + + def test_get_none_when_fail_to_get_csi_daemon_set(self): + self._test_wait_for_updated_daemon_set_called_once(None, None) + + def _test_wait_for_updated_daemon_set_called_once(self, daemon_set, expected_result): + self.daemon_set_manager.k8s_api.list_daemon_set_for_all_namespaces.return_value = daemon_set + result = self.daemon_set_manager.wait_until_all_daemon_set_pods_are_up_to_date() + self.assertEqual(result, expected_result) + self.daemon_set_manager.k8s_api.list_daemon_set_for_all_namespaces.assert_called_once_with( + test_settings.DRIVER_PRODUCT_LABEL) + + def test_get_not_updated_daemon_and_wait_until_it_will_be_updated(self): + daemon_set_name = self.fake_updated_daemon_set.metadata.name + self._test_wait_for_updated_daemon_set_called_twice( + [self.fake_not_updated_daemon_set_items, self.fake_updated_daemon_set_items], daemon_set_name) + + def test_get_none_when_fail_to_get_csi_daemon_set_in_the_second_time(self): + self._test_wait_for_updated_daemon_set_called_twice([self.fake_not_updated_daemon_set_items, None], None) + + def _test_wait_for_updated_daemon_set_called_twice(self, daemon_sets, expected_result): + self.daemon_set_manager.k8s_api.list_daemon_set_for_all_namespaces.side_effect = daemon_sets + result = self.daemon_set_manager.wait_until_all_daemon_set_pods_are_up_to_date() + self.assertEqual(result, expected_result) + self.assertEqual(self.daemon_set_manager.k8s_api.list_daemon_set_for_all_namespaces.call_count, 2) diff --git a/controllers/tests/controller_server/host_definer/resource_manager/event_manager_test.py b/controllers/tests/controller_server/host_definer/resource_manager/event_manager_test.py new file mode 100644 index 000000000..533cfdc30 --- /dev/null +++ b/controllers/tests/controller_server/host_definer/resource_manager/event_manager_test.py @@ -0,0 +1,29 @@ +from controllers.tests.controller_server.host_definer.resource_manager.base_resource_manager import BaseResourceManager +from controllers.servers.host_definer.resource_manager.event import EventManager +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils +import controllers.tests.controller_server.host_definer.settings as test_settings + + +class TestEventManagerTest(BaseResourceManager): + def setUp(self): + self.event_manager = EventManager() + self.fake_host_definition_info = test_utils.get_fake_host_definition_info() + + def test_generate_k8s_normal_event_success(self): + self._test_generate_k8s_event_success(test_settings.SUCCESSFUL_MESSAGE_TYPE, test_settings.NORMAL_EVENT_TYPE) + + def test_generate_k8s_warning_event_success(self): + self._test_generate_k8s_event_success('unsuccessful message type', test_settings.WARNING_EVENT_TYPE) + + def _test_generate_k8s_event_success(self, message_type, expected_event_type): + result = self.event_manager.generate_k8s_event( + self.fake_host_definition_info, test_settings.MESSAGE, + test_settings.DEFINE_ACTION, message_type) + self.assertEqual(result.metadata, test_utils.get_event_object_metadata()) + self.assertEqual(result.reporting_component, test_settings.HOST_DEFINER) + self.assertEqual(result.reporting_instance, test_settings.HOST_DEFINER) + self.assertEqual(result.action, test_settings.DEFINE_ACTION) + self.assertEqual(result.type, expected_event_type) + self.assertEqual(result.reason, message_type + test_settings.DEFINE_ACTION) + self.assertEqual(result.message, test_settings.MESSAGE) + self.assertEqual(result.involved_object, test_utils.get_object_reference()) diff --git a/controllers/tests/controller_server/host_definer/resource_manager/host_definition_manager_test.py b/controllers/tests/controller_server/host_definer/resource_manager/host_definition_manager_test.py new file mode 100644 index 000000000..f7faacab2 --- /dev/null +++ b/controllers/tests/controller_server/host_definer/resource_manager/host_definition_manager_test.py @@ -0,0 +1,339 @@ +from copy import deepcopy +from unittest.mock import MagicMock, Mock, patch + +from controllers.tests.controller_server.host_definer.resource_manager.base_resource_manager import BaseResourceManager +from controllers.servers.host_definer.resource_manager.host_definition import HostDefinitionManager +import controllers.common.settings as common_settings +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils +import controllers.tests.controller_server.host_definer.utils.k8s_manifests_utils as test_manifest_utils +import controllers.tests.controller_server.host_definer.settings as test_settings + + +class TestHostDefinitionManager(BaseResourceManager): + def setUp(self): + self.host_definition_manager = HostDefinitionManager() + self.host_definition_manager.k8s_api = MagicMock() + self.host_definition_manager.event_manager = MagicMock() + self.host_definition_manager.resource_info_manager = MagicMock() + self.fake_host_definition_info = test_utils.get_fake_host_definition_info() + self.fake_secret_info = test_utils.get_fake_secret_info() + self.mock_global_managed_nodes = test_utils.patch_nodes_global_variable( + test_settings.HOST_DEFINITION_MANAGER_PATH) + self.mock_global_managed_nodes[test_settings.FAKE_NODE_NAME] = test_utils.get_fake_managed_node() + self.define_response = test_utils.get_fake_define_host_response() + + def test_get_host_definition_info_from_secret_success(self): + result = self.host_definition_manager.get_host_definition_info_from_secret(self.fake_secret_info) + self.assertEqual(result.secret_name, test_settings.FAKE_SECRET) + self.assertEqual(result.secret_namespace, test_settings.FAKE_SECRET_NAMESPACE) + + def test_get_matching_host_definition_info_success(self): + self._test_get_matching_host_definition_info_success( + test_settings.FAKE_NODE_NAME, test_settings.FAKE_SECRET, + test_settings.FAKE_SECRET_NAMESPACE, self.fake_host_definition_info) + + def test_get_none_when_host_definition_node_name_is_not_matched(self): + self._test_get_matching_host_definition_info_success( + 'bad_node_name', test_settings.FAKE_SECRET, test_settings.FAKE_SECRET_NAMESPACE) + + def test_get_none_when_host_definition_secret_name_is_not_matched(self): + self._test_get_matching_host_definition_info_success( + test_settings.FAKE_NODE_NAME, 'bad_secret_name', test_settings.FAKE_SECRET_NAMESPACE) + + def test_get_none_when_host_definition_secret_namespace_is_not_matched(self): + self._test_get_matching_host_definition_info_success( + test_settings.FAKE_NODE_NAME, test_settings.FAKE_SECRET, 'bad_secret_namespace') + + def _test_get_matching_host_definition_info_success( + self, node_name, secret_name, secret_namespace, expected_result=None): + self.host_definition_manager.k8s_api.list_host_definition.return_value = \ + test_utils.get_fake_k8s_host_definitions_items() + self.host_definition_manager.resource_info_manager.generate_host_definition_info.return_value = \ + self.fake_host_definition_info + result = self.host_definition_manager.get_matching_host_definition_info( + node_name, secret_name, secret_namespace) + self.assertEqual(result, expected_result) + self.host_definition_manager.resource_info_manager.generate_host_definition_info.assert_called_once_with( + test_utils.get_fake_k8s_host_definitions_items().items[0]) + self.host_definition_manager.k8s_api.list_host_definition.assert_called_once_with() + + def test_get_none_when_host_definition_list_empty_success(self): + self.host_definition_manager.k8s_api.list_host_definition.return_value = test_utils.get_fake_empty_k8s_list() + self.host_definition_manager.resource_info_manager.generate_host_definition_info.return_value = \ + self.fake_host_definition_info + result = self.host_definition_manager.get_matching_host_definition_info('', '', '') + self.assertEqual(result, None) + self.host_definition_manager.resource_info_manager.generate_host_definition_info.assert_not_called() + self.host_definition_manager.k8s_api.list_host_definition.assert_called_once_with() + + @patch('{}.manifest_utils'.format(test_settings.HOST_DEFINITION_MANAGER_PATH)) + def test_create_host_definition_success(self, mock_manifest): + finalizers_manifest = test_manifest_utils.get_finalizers_manifest([test_settings.CSI_IBM_FINALIZER, ]) + self.host_definition_manager.k8s_api.create_host_definition.return_value = \ + test_utils.get_fake_k8s_host_definition(test_settings.READY_PHASE) + mock_manifest.get_finalizer_manifest.return_value = finalizers_manifest + self.host_definition_manager.resource_info_manager.generate_host_definition_info.return_value = \ + self.fake_host_definition_info + result = self.host_definition_manager.create_host_definition( + test_manifest_utils.get_fake_k8s_host_definition_manifest()) + self.assertEqual(result, self.fake_host_definition_info) + self.host_definition_manager.resource_info_manager.generate_host_definition_info.assert_called_once_with( + test_utils.get_fake_k8s_host_definition(test_settings.READY_PHASE)) + mock_manifest.get_finalizer_manifest.assert_called_once_with( + test_settings.FAKE_NODE_NAME, [test_settings.CSI_IBM_FINALIZER, ]) + self.host_definition_manager.k8s_api.patch_host_definition.assert_called_once_with(finalizers_manifest) + self.host_definition_manager.k8s_api.create_host_definition.assert_called_once_with( + test_manifest_utils.get_fake_k8s_host_definition_manifest()) + + @patch('{}.manifest_utils'.format(test_settings.HOST_DEFINITION_MANAGER_PATH)) + def test_create_host_definition_failure(self, mock_manifest_utils): + self.host_definition_manager.k8s_api.create_host_definition.return_value = None + self.host_definition_manager.resource_info_manager.generate_host_definition_info.return_value = \ + self.fake_host_definition_info + result = self.host_definition_manager.create_host_definition( + test_manifest_utils.get_fake_k8s_host_definition_manifest()) + self.assertEqual(result.name, "") + self.assertEqual(result.node_id, "") + mock_manifest_utils.get_finalizer_manifest.assert_not_called() + self.host_definition_manager.resource_info_manager.generate_host_definition_info.assert_not_called() + self.host_definition_manager.k8s_api.patch_host_definition.assert_not_called() + self.host_definition_manager.k8s_api.create_host_definition.assert_called_once_with( + test_manifest_utils.get_fake_k8s_host_definition_manifest()) + + @patch('{}.manifest_utils'.format(test_settings.HOST_DEFINITION_MANAGER_PATH)) + def test_delete_host_definition_success(self, mock_manifest_utils): + self._test_delete_host_definition(200, mock_manifest_utils) + self.host_definition_manager.k8s_api.delete_host_definition.assert_called_once_with( + test_settings.FAKE_NODE_NAME) + + @patch('{}.manifest_utils'.format(test_settings.HOST_DEFINITION_MANAGER_PATH)) + def test_fail_to_delete_host_definition_because_the_finalizers_fails_to_be_deleted(self, mock_manifest_utils): + self._test_delete_host_definition(405, mock_manifest_utils) + self.host_definition_manager.k8s_api.delete_host_definition.assert_not_called() + + def _test_delete_host_definition(self, finalizers_status_code, mock_manifest_utils): + mock_manifest_utils.get_finalizer_manifest.return_value = test_manifest_utils.get_finalizers_manifest([]) + self.host_definition_manager.k8s_api.patch_host_definition.return_value = finalizers_status_code + self.host_definition_manager.delete_host_definition(test_settings.FAKE_NODE_NAME) + self.host_definition_manager.k8s_api.patch_host_definition.assert_called_once_with( + test_manifest_utils.get_finalizers_manifest([])) + + @patch('{}.manifest_utils'.format(test_settings.HOST_DEFINITION_MANAGER_PATH)) + def test_set_host_definition_status_success(self, mock_manifest_utils): + status_phase_manifest = test_manifest_utils.get_status_phase_manifest(test_settings.READY_PHASE) + mock_manifest_utils.get_host_definition_status_manifest.return_value = status_phase_manifest + self.host_definition_manager.set_host_definition_status(test_settings.FAKE_NODE_NAME, test_settings.READY_PHASE) + mock_manifest_utils.get_host_definition_status_manifest.assert_called_once_with(test_settings.READY_PHASE) + self.host_definition_manager.k8s_api.patch_cluster_custom_object_status.assert_called_once_with( + common_settings.CSI_IBM_GROUP, common_settings.VERSION, common_settings.HOST_DEFINITION_PLURAL, + test_settings.FAKE_NODE_NAME, status_phase_manifest) + + def test_get_host_definition_info_from_secret_and_node_name_success(self): + self.host_definition_manager.get_host_definition_info_from_secret = Mock() + self.host_definition_manager.add_name_to_host_definition_info = Mock() + self.host_definition_manager.get_host_definition_info_from_secret.return_value = self.fake_host_definition_info + self.host_definition_manager.add_name_to_host_definition_info.return_value = self.fake_host_definition_info + result = self.host_definition_manager.get_host_definition_info_from_secret_and_node_name( + test_settings.FAKE_NODE_NAME, self.fake_secret_info) + self.assertEqual(result, self.fake_host_definition_info) + self.host_definition_manager.get_host_definition_info_from_secret.assert_called_once_with( + self.fake_secret_info) + self.host_definition_manager.add_name_to_host_definition_info.assert_called_once_with( + test_settings.FAKE_NODE_NAME, self.fake_host_definition_info) + + @patch('{}.utils'.format(test_settings.HOST_DEFINITION_MANAGER_PATH)) + def test_add_name_to_host_definition_info_success(self, mock_utils): + random_string = '2049530945i3094i' + mock_utils.get_random_string.return_value = random_string + result = self.host_definition_manager.add_name_to_host_definition_info( + test_settings.FAKE_NODE_NAME, test_utils.get_fake_empty_host_definition_info()) + self.assertEqual(result.name, '{0}-{1}'.format(test_settings.FAKE_NODE_NAME, random_string).replace('_', '.')) + self.assertEqual(result.node_name, test_settings.FAKE_NODE_NAME) + self.assertEqual(result.node_id, test_settings.FAKE_NODE_ID) + + def test_update_host_definition_info_success(self): + result = self._test_update_host_definition_info(self.fake_host_definition_info) + self.assertEqual(result.connectivity_type, self.fake_host_definition_info.connectivity_type) + self.assertEqual(result.node_id, self.fake_host_definition_info.node_id) + + def test_do_not_update_not_found_host_definition_info_success(self): + result = self._test_update_host_definition_info(None) + self.assertEqual(result.connectivity_type, 'some_connectivity') + self.assertEqual(result.node_id, 'some_node_id') + + def _test_update_host_definition_info(self, matching_host_definition_info): + host_definition_info = deepcopy(self.fake_host_definition_info) + host_definition_info.connectivity_type = 'some_connectivity' + host_definition_info.node_id = 'some_node_id' + self._prepare_get_matching_host_definition_info_function_as_mock(matching_host_definition_info) + result = self.host_definition_manager.update_host_definition_info(host_definition_info) + self._assert_get_matching_host_definition_called_once_with() + return result + + @patch('{}.manifest_utils'.format(test_settings.HOST_DEFINITION_MANAGER_PATH)) + def test_create_exist_host_definition_success(self, mock_manifest_utils): + host_definition_manifest = self._test_create_host_definition_if_not_exist( + 'different_name', self.fake_host_definition_info, None, mock_manifest_utils) + host_definition_manifest[test_settings.METADATA_FIELD][common_settings.NAME_FIELD] = \ + test_settings.FAKE_NODE_NAME + self.host_definition_manager.k8s_api.patch_host_definition.assert_called_once_with(host_definition_manifest) + self.host_definition_manager.create_host_definition.assert_not_called() + + @patch('{}.manifest_utils'.format(test_settings.HOST_DEFINITION_MANAGER_PATH)) + def test_create_new_host_definition_success(self, mock_manifest_utils): + host_definition_manifest = self._test_create_host_definition_if_not_exist( + test_settings.FAKE_NODE_NAME, None, self.fake_host_definition_info, mock_manifest_utils) + self.host_definition_manager.k8s_api.patch_host_definition.assert_not_called() + self.host_definition_manager.create_host_definition.assert_called_once_with(host_definition_manifest) + + def _test_create_host_definition_if_not_exist(self, new_host_definition_name, matching_host_definition, + created_host_definition, mock_manifest_utils): + host_definition_manifest = deepcopy(test_manifest_utils.get_fake_k8s_host_definition_manifest()) + host_definition_manifest[test_settings.METADATA_FIELD][common_settings.NAME_FIELD] = new_host_definition_name + mock_manifest_utils.get_host_definition_manifest.return_value = host_definition_manifest + self._prepare_get_matching_host_definition_info_function_as_mock(matching_host_definition) + self.host_definition_manager.create_host_definition = Mock() + self.host_definition_manager.create_host_definition.return_value = created_host_definition + result = self.host_definition_manager.create_host_definition_if_not_exist( + self.fake_host_definition_info, self.define_response) + + self.assertEqual(result, self.fake_host_definition_info) + mock_manifest_utils.get_host_definition_manifest.assert_called_once_with( + self.fake_host_definition_info, self.define_response, test_settings.FAKE_NODE_ID) + self._assert_get_matching_host_definition_called_once_with() + return host_definition_manifest + + def test_set_host_definition_status_to_ready_success(self): + self.host_definition_manager.set_host_definition_status = Mock() + self.host_definition_manager.create_k8s_event_for_host_definition = Mock() + self.host_definition_manager.set_host_definition_status_to_ready(self.fake_host_definition_info) + self.host_definition_manager.set_host_definition_status.assert_called_once_with( + self.fake_host_definition_info.name, test_settings.READY_PHASE) + self.host_definition_manager.create_k8s_event_for_host_definition.assert_called_once_with( + self.fake_host_definition_info, test_settings.MESSAGE, + test_settings.DEFINE_ACTION, test_settings.SUCCESSFUL_MESSAGE_TYPE) + + def test_create_k8s_event_for_host_definition_success(self): + k8s_event = test_utils.get_event_object_metadata() + self.host_definition_manager.event_manager.generate_k8s_event.return_value = k8s_event + self.host_definition_manager.create_k8s_event_for_host_definition( + self.fake_host_definition_info, test_settings.MESSAGE, + test_settings.DEFINE_ACTION, test_settings.SUCCESSFUL_MESSAGE_TYPE) + self.host_definition_manager.event_manager.generate_k8s_event.assert_called_once_with( + self.fake_host_definition_info, test_settings.MESSAGE, + test_settings.DEFINE_ACTION, test_settings.SUCCESSFUL_MESSAGE_TYPE) + self.host_definition_manager.k8s_api.create_event.assert_called_once_with(test_settings.DEFAULT_NAMESPACE, + k8s_event) + + def test_set_host_definition_status_to_pending_and_create_event_after_failed_definition(self): + self._prepare_set_status_to_host_definition_after_definition(test_settings.MESSAGE) + self.host_definition_manager.set_host_definition_status.assert_called_once_with( + self.fake_host_definition_info.name, test_settings.PENDING_CREATION_PHASE) + self.host_definition_manager.create_k8s_event_for_host_definition.assert_called_once_with( + self.fake_host_definition_info, test_settings.MESSAGE, + test_settings.DEFINE_ACTION, test_settings.FAILED_MESSAGE_TYPE) + self.host_definition_manager.set_host_definition_status_to_ready.assert_not_called() + + def test_set_host_definition_status_to_ready_after_successful_definition(self): + self._prepare_set_status_to_host_definition_after_definition('') + self.host_definition_manager.set_host_definition_status.assert_not_called() + self.host_definition_manager.create_k8s_event_for_host_definition.assert_not_called() + self.host_definition_manager.set_host_definition_status_to_ready.assert_called_once_with( + self.fake_host_definition_info) + + def _prepare_set_status_to_host_definition_after_definition(self, message_from_storage): + self.host_definition_manager.set_host_definition_status = Mock() + self.host_definition_manager.create_k8s_event_for_host_definition = Mock() + self.host_definition_manager.set_host_definition_status_to_ready = Mock() + self.host_definition_manager.set_status_to_host_definition_after_definition( + message_from_storage, self.fake_host_definition_info) + + def test_handle_host_definition_after_failed_undefine_action_and_when_host_definition_exist(self): + self._test_handle_k8s_host_definition_after_undefine_action_if_exist( + self.fake_host_definition_info, self.define_response) + self.host_definition_manager.set_host_definition_status.assert_called_once_with( + self.fake_host_definition_info.name, test_settings.PENDING_DELETION_PHASE) + self.host_definition_manager.create_k8s_event_for_host_definition.assert_called_once_with( + self.fake_host_definition_info, self.define_response.error_message, test_settings.UNDEFINE_ACTION, + test_settings.FAILED_MESSAGE_TYPE) + self.host_definition_manager.delete_host_definition.assert_not_called() + + def test_handle_host_definition_after_successful_undefine_action_and_when_host_definition_exist(self): + define_response = deepcopy(self.define_response) + define_response.error_message = '' + self._test_handle_k8s_host_definition_after_undefine_action_if_exist( + self.fake_host_definition_info, define_response) + self.host_definition_manager.set_host_definition_status.assert_not_called() + self.host_definition_manager.create_k8s_event_for_host_definition.assert_not_called() + self.host_definition_manager.delete_host_definition.assert_called_once_with( + self.fake_host_definition_info.name) + + def test_handle_k8s_host_definition_after_undefine_action_when_not_exist(self): + self._test_handle_k8s_host_definition_after_undefine_action_if_exist(None, self.define_response) + self.host_definition_manager.set_host_definition_status.assert_not_called() + self.host_definition_manager.create_k8s_event_for_host_definition.assert_not_called() + self.host_definition_manager.delete_host_definition.assert_not_called() + + def _test_handle_k8s_host_definition_after_undefine_action_if_exist( + self, matching_host_definition, define_response): + self.host_definition_manager.set_host_definition_status = Mock() + self.host_definition_manager.create_k8s_event_for_host_definition = Mock() + self.host_definition_manager.delete_host_definition = Mock() + self._prepare_get_matching_host_definition_info_function_as_mock(matching_host_definition) + self.host_definition_manager.handle_k8s_host_definition_after_undefine_action( + self.fake_host_definition_info, define_response) + self._assert_get_matching_host_definition_called_once_with() + + def test_return_true_when_host_definition_phase_is_pending(self): + result = self.host_definition_manager.is_host_definition_in_pending_phase(test_settings.PENDING_CREATION_PHASE) + self.assertTrue(result) + + def test_return_false_when_host_definition_phase_is_not_pending(self): + result = self.host_definition_manager.is_host_definition_in_pending_phase(test_settings.READY_PHASE) + self.assertFalse(result) + + def test_set_host_definition_status_to_error_success(self): + self.host_definition_manager.set_host_definition_status = Mock() + self.host_definition_manager.set_host_definition_phase_to_error(self.fake_host_definition_info) + self.host_definition_manager.set_host_definition_status.assert_called_once_with( + self.fake_host_definition_info.name, test_settings.ERROR_PHASE) + + def test_return_true_when_host_definition_is_not_pending_and_exist(self): + result = self._test_is_host_definition_not_pending(self.fake_host_definition_info) + self.assertTrue(result) + + def test_return_true_when_host_definition_is_not_exist_after_it_was_pending(self): + result = self._test_is_host_definition_not_pending(None) + self.assertTrue(result) + + def test_return_false_when_host_definition_exist_but_still_pending(self): + host_definition_info = deepcopy(self.fake_host_definition_info) + host_definition_info.phase = test_settings.PENDING_CREATION_PHASE + result = self._test_is_host_definition_not_pending(host_definition_info) + self.assertFalse(result) + + def _test_is_host_definition_not_pending(self, matching_host_definition): + self._prepare_get_matching_host_definition_info_function_as_mock(matching_host_definition) + result = self.host_definition_manager.is_host_definition_not_pending(self.fake_host_definition_info) + self._assert_get_matching_host_definition_called_once_with() + return result + + def _prepare_get_matching_host_definition_info_function_as_mock(self, matching_host_definition): + self.host_definition_manager.get_matching_host_definition_info = Mock() + self.host_definition_manager.get_matching_host_definition_info.return_value = matching_host_definition + + def _assert_get_matching_host_definition_called_once_with(self): + self.host_definition_manager.get_matching_host_definition_info.assert_called_once_with( + self.fake_host_definition_info.name, self.fake_host_definition_info.secret_name, + self.fake_host_definition_info.secret_namespace) + + def test_get_all_host_definitions_info_of_the_node(self): + self.host_definition_manager.k8s_api.list_host_definition.return_value = \ + test_utils.get_fake_k8s_host_definitions_items() + self.host_definition_manager.resource_info_manager.generate_host_definition_info.return_value = \ + self.fake_host_definition_info + result = self.host_definition_manager.get_all_host_definitions_info_of_the_node(test_settings.FAKE_NODE_NAME) + self.assertEqual(result, [self.fake_host_definition_info]) + self.host_definition_manager.k8s_api.list_host_definition.assert_called_once_with() + self.host_definition_manager.resource_info_manager.generate_host_definition_info.assert_called_once_with( + test_utils.get_fake_k8s_host_definitions_items().items[0]) diff --git a/controllers/tests/controller_server/host_definer/resource_manager/node_manager_test.py b/controllers/tests/controller_server/host_definer/resource_manager/node_manager_test.py new file mode 100644 index 000000000..e71f5e861 --- /dev/null +++ b/controllers/tests/controller_server/host_definer/resource_manager/node_manager_test.py @@ -0,0 +1,480 @@ +from copy import deepcopy +from unittest.mock import MagicMock, Mock, patch + +from controllers.servers.host_definer.types import ManagedNode +from controllers.servers.errors import ValidationException +from controllers.servers.host_definer.resource_manager.node import NodeManager +from controllers.tests.controller_server.host_definer.resource_manager.base_resource_manager import BaseResourceManager +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils +import controllers.tests.controller_server.host_definer.utils.k8s_manifests_utils as test_manifest_utils +import controllers.tests.controller_server.host_definer.settings as test_settings + + +class TestNodeManager(BaseResourceManager): + def setUp(self): + super().setUp() + self.node_manager = NodeManager() + self.node_manager.k8s_api = MagicMock() + self.node_manager.host_definition_manager = MagicMock() + self.node_manager.secret_manager = MagicMock() + self.node_manager.definition_manager = MagicMock() + self.node_manager.resource_info_manager = MagicMock() + self.fake_node_info = test_utils.get_fake_node_info() + self.fake_csi_node_info = test_utils.get_fake_csi_node_info() + self.fake_managed_node = test_utils.get_fake_managed_node() + self.fake_host_definitions_info = test_utils.get_fake_k8s_host_definitions_items() + self.fake_secret_config = 'fake_secret_config' + self.fake_secret_data = test_utils.get_fake_k8s_secret().data + self.fake_secret_info = test_utils.get_fake_secret_info() + self.global_managed_nodes = test_utils.patch_nodes_global_variable(test_settings.NODE_MANAGER_PATH) + self.global_managed_secrets = test_utils.patch_managed_secrets_global_variable( + test_settings.NODE_MANAGER_PATH) + self.manage_node_labels_manifest = test_manifest_utils.get_metadata_with_manage_node_labels_manifest( + test_settings.MANAGE_NODE_LABEL) + self.mock_get_system_info_for_topologies = patch('{}.get_system_info_for_topologies'.format( + test_settings.NODE_MANAGER_PATH)).start() + + def test_get_nodes_info_success(self): + self._test_get_k8s_resources_info_success( + self.node_manager.get_nodes_info, self.node_manager.k8s_api.list_node, + self.node_manager.resource_info_manager.generate_node_info, self.fake_node_info, + test_utils.get_fake_k8s_nodes_items()) + + def test_get_nodes_info_empty_list_success(self): + self._test_get_k8s_resources_info_empty_list_success( + self.node_manager.get_nodes_info, self.node_manager.k8s_api.list_node, + self.node_manager.resource_info_manager.generate_node_info) + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_node_can_be_defined_when_dynamic_node_labeling_allowed(self, manifest_utils): + self._prepare_is_node_can_be_defined(True, manifest_utils) + self._test_is_node_can_be_defined(True, manifest_utils) + self.node_manager.is_node_has_manage_node_label.assert_not_called() + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_node_can_be_defined_when_node_has_manage_node_label(self, manifest_utils): + self._prepare_is_node_can_be_defined(False, manifest_utils) + self.node_manager.is_node_has_manage_node_label.return_value = True + self._test_is_node_can_be_defined(True, manifest_utils) + self.node_manager.is_node_has_manage_node_label.assert_called_once_with(test_settings.FAKE_NODE_NAME) + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_node_cannot_be_defined(self, manifest_utils): + self._prepare_is_node_can_be_defined(False, manifest_utils) + self.node_manager.is_node_has_manage_node_label.return_value = False + self._test_is_node_can_be_defined(False, manifest_utils) + self.node_manager.is_node_has_manage_node_label.assert_called_once_with(test_settings.FAKE_NODE_NAME) + + def _prepare_is_node_can_be_defined(self, is_dynamic_node_labeling_allowed, manifest_utils): + manifest_utils.is_dynamic_node_labeling_allowed.return_value = is_dynamic_node_labeling_allowed + self.node_manager.is_node_has_manage_node_label = Mock() + + def _test_is_node_can_be_defined(self, expected_result, manifest_utils): + result = self.node_manager.is_node_can_be_defined(test_settings.FAKE_NODE_NAME) + self.assertEqual(result, expected_result) + manifest_utils.is_dynamic_node_labeling_allowed.assert_called_once_with() + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_node_can_be_undefined(self, mock_utils): + self._prepare_is_node_can_be_undefined(mock_utils, True, True, False) + self._test_is_node_can_be_undefined(mock_utils, True) + self.node_manager.is_node_has_manage_node_label.assert_called_once_with(test_settings.FAKE_NODE_NAME) + self.node_manager.is_node_has_forbid_deletion_label.assert_called_once_with(test_settings.FAKE_NODE_NAME) + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_node_cannot_be_undefined_when_host_definer_cannot_delete_hosts(self, mock_utils): + self._prepare_is_node_can_be_undefined(mock_utils, False) + self._test_is_node_can_be_undefined(mock_utils, False) + self.node_manager.is_node_has_manage_node_label.assert_not_called() + self.node_manager.is_node_has_forbid_deletion_label.assert_not_called() + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_node_cannot_be_undefined_when_node_not_has_manage_node_label(self, mock_utils): + self._prepare_is_node_can_be_undefined(mock_utils, True, False) + self._test_is_node_can_be_undefined(mock_utils, False) + self.node_manager.is_node_has_manage_node_label.assert_called_once_with(test_settings.FAKE_NODE_NAME) + self.node_manager.is_node_has_forbid_deletion_label.assert_not_called() + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_node_cannot_be_undefined_when_node_has_forbid_deletion_label(self, mock_utils): + self._prepare_is_node_can_be_undefined(mock_utils, True, True, True) + self._test_is_node_can_be_undefined(mock_utils, False) + self.node_manager.is_node_has_manage_node_label.assert_called_once_with(test_settings.FAKE_NODE_NAME) + self.node_manager.is_node_has_forbid_deletion_label.assert_called_once_with(test_settings.FAKE_NODE_NAME) + + def _prepare_is_node_can_be_undefined( + self, mock_utils, is_dynamic_node_labeling_allowed=False, is_node_has_manage_node_label=False, + is_node_has_forbid_deletion_label=False): + mock_utils.is_host_definer_can_delete_hosts.return_value = is_dynamic_node_labeling_allowed + self._prepare_is_node_has_manage_node_label_mock(is_node_has_manage_node_label) + self.node_manager.is_node_has_forbid_deletion_label = Mock() + self.node_manager.is_node_has_forbid_deletion_label.return_value = is_node_has_forbid_deletion_label + + def _test_is_node_can_be_undefined(self, mock_utils, expected_result): + result = self.node_manager.is_node_can_be_undefined(test_settings.FAKE_NODE_NAME) + self.assertEqual(result, expected_result) + mock_utils.is_host_definer_can_delete_hosts.assert_called_once_with() + + def test_node_has_manage_node_label(self): + self._test_is_node_has_label(self.fake_node_info, True, self.node_manager.is_node_has_manage_node_label) + + def test_node_do_not_has_manage_node_label(self): + node_info = deepcopy(self.fake_node_info) + node_info.labels.pop(test_settings.MANAGE_NODE_LABEL) + self._test_is_node_has_label(node_info, False, self.node_manager.is_node_has_manage_node_label) + + def test_node_has_forbid_deletion_label(self): + node_info = deepcopy(self.fake_node_info) + node_info.labels[test_settings.FORBID_DELETION_LABEL] = test_settings.TRUE_STRING + self._test_is_node_has_label(node_info, True, self.node_manager.is_node_has_forbid_deletion_label) + + def test_node_do_not_has_forbid_deletion_label(self): + node_info_without_forbid_deletion_label = deepcopy(self.fake_node_info) + self._test_is_node_has_label(node_info_without_forbid_deletion_label, False, + self.node_manager.is_node_has_forbid_deletion_label) + + def _test_is_node_has_label(self, node_info, expected_result, function_to_run): + self.node_manager.resource_info_manager.get_node_info.return_value = node_info + result = function_to_run(test_settings.FAKE_NODE_NAME) + self.node_manager.resource_info_manager.get_node_info.assert_called_once_with(test_settings.FAKE_NODE_NAME) + self.assertEqual(result, expected_result) + + @patch('{}.manifest_utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_add_node_to_nodes_when_node_do_not_has_manage_node_label_success(self, mock_manifest_utils): + excepted_managed_node = {self.fake_csi_node_info.name: self.fake_managed_node} + self._prepare_add_node_to_nodes(False, mock_manifest_utils) + self.node_manager.add_node_to_nodes(self.fake_csi_node_info) + self._assert_add_node_to_nodes(excepted_managed_node) + self._assert_update_manage_node_label_called( + mock_manifest_utils, self.manage_node_labels_manifest, test_settings.TRUE_STRING) + + @patch('{}.manifest_utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_add_node_to_nodes_when_node_has_manage_node_label_success(self, mock_manifest_utils): + excepted_managed_node = {self.fake_csi_node_info.name: self.fake_managed_node} + self._prepare_add_node_to_nodes(True, mock_manifest_utils) + self.node_manager.add_node_to_nodes(self.fake_csi_node_info) + self._assert_add_node_to_nodes(excepted_managed_node) + self._assert_update_manage_node_label_not_called(mock_manifest_utils) + + def _prepare_add_node_to_nodes(self, is_node_has_manage_node_label, mock_manifest_utils): + self._prepare_is_node_has_manage_node_label_mock(is_node_has_manage_node_label) + mock_manifest_utils.get_body_manifest_for_labels.return_value = self.manage_node_labels_manifest + self.node_manager.generate_managed_node = Mock() + self.node_manager.generate_managed_node.return_value = self.fake_managed_node + + def _prepare_is_node_has_manage_node_label_mock(self, is_node_has_manage_node_label): + self.node_manager.is_node_has_manage_node_label = Mock() + self.node_manager.is_node_has_manage_node_label.return_value = is_node_has_manage_node_label + + def _assert_add_node_to_nodes(self, excepted_managed_node): + self.assertEqual(self.global_managed_nodes, excepted_managed_node) + self.node_manager.generate_managed_node.assert_called_once_with(self.fake_csi_node_info) + + @patch('{}.utils'.format(test_settings.TYPES_PATH)) + def test_generate_managed_node(self, mock_utils): + self.node_manager.resource_info_manager.get_node_info.return_value = self.fake_node_info + mock_utils.generate_io_group_from_labels.return_value = test_settings.IO_GROUP_NAMES + result = self.node_manager.generate_managed_node(self.fake_csi_node_info) + self.assertEqual(result.name, self.fake_csi_node_info.name) + self.assertEqual(result.node_id, self.fake_csi_node_info.node_id) + self.assertEqual(result.io_group, test_settings.IO_GROUP_NAMES) + self.assertEqual(type(result), ManagedNode) + self.node_manager.resource_info_manager.get_node_info.assert_called_once_with(self.fake_csi_node_info.name) + mock_utils.generate_io_group_from_labels.assert_called_once_with(self.fake_node_info.labels) + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + @patch('{}.manifest_utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_remove_manage_node_label_when_node_should_be_removed(self, mock_manifest_utils, mock_utils): + self._prepare_remove_manage_node_label(mock_manifest_utils, mock_utils, True, '', False) + self._test_remove_manage_node_label(mock_utils) + self.node_manager.resource_info_manager.get_csi_node_info.assert_called_once_with(test_settings.FAKE_NODE_NAME) + self.node_manager.is_node_has_host_definitions.assert_called_once_with(test_settings.FAKE_NODE_NAME) + self._assert_update_manage_node_label_called(mock_manifest_utils, self.manage_node_labels_manifest, None) + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + @patch('{}.manifest_utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_do_not_remove_manage_node_label_when_dynamic_node_labeling_is_not_allowed( + self, mock_manifest_utils, mock_utils): + self._prepare_remove_manage_node_label(mock_manifest_utils, mock_utils, False, '') + self._test_remove_manage_node_label(mock_utils) + self.node_manager.resource_info_manager.get_csi_node_info.assert_not_called() + self.node_manager.is_node_has_host_definitions.assert_not_called() + self._assert_update_manage_node_label_not_called(mock_manifest_utils) + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + @patch('{}.manifest_utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_do_not_remove_manage_node_label_when_node_has_ibm_block_csi(self, mock_manifest_utils, mock_utils): + self._prepare_remove_manage_node_label(mock_manifest_utils, mock_utils, True, 'something') + self._test_remove_manage_node_label(mock_utils) + self.node_manager.resource_info_manager.get_csi_node_info.assert_called_once_with(test_settings.FAKE_NODE_NAME) + self.node_manager.is_node_has_host_definitions.assert_not_called() + self._assert_update_manage_node_label_not_called(mock_manifest_utils) + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + @patch('{}.manifest_utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_do_not_remove_manage_node_label_when_node_has_host_definitions(self, mock_manifest_utils, mock_utils): + self._prepare_remove_manage_node_label(mock_manifest_utils, mock_utils, True, '', True) + self._test_remove_manage_node_label(mock_utils) + self.node_manager.resource_info_manager.get_csi_node_info.assert_called_once_with(test_settings.FAKE_NODE_NAME) + self.node_manager.is_node_has_host_definitions.assert_called_once_with(test_settings.FAKE_NODE_NAME) + self._assert_update_manage_node_label_not_called(mock_manifest_utils) + + def _prepare_remove_manage_node_label(self, mock_manifest_utils, mock_utils, is_dynamic_node_labeling_allowed, + node_id, is_node_has_host_definitions=False): + csi_node_info = deepcopy(self.fake_csi_node_info) + csi_node_info.node_id = node_id + mock_utils.is_dynamic_node_labeling_allowed.return_value = is_dynamic_node_labeling_allowed + self.node_manager.resource_info_manager.get_csi_node_info.return_value = csi_node_info + self.node_manager.is_node_has_host_definitions = Mock() + self.node_manager.is_node_has_host_definitions.return_value = is_node_has_host_definitions + mock_manifest_utils.get_body_manifest_for_labels.return_value = self.manage_node_labels_manifest + + def _test_remove_manage_node_label(self, mock_utils): + self.node_manager.remove_manage_node_label(test_settings.FAKE_NODE_NAME) + mock_utils.is_dynamic_node_labeling_allowed.assert_called_once_with() + + def _assert_update_manage_node_label_called(self, mock_manifest_utils, excepted_body, expected_label_value): + mock_manifest_utils.get_body_manifest_for_labels.assert_called_once_with(expected_label_value) + self.node_manager.k8s_api.patch_node.assert_called_once_with(test_settings.FAKE_NODE_NAME, excepted_body) + + def _assert_update_manage_node_label_not_called(self, mock_manifest_utils): + mock_manifest_utils.get_body_manifest_for_labels.assert_not_called() + self.node_manager.k8s_api.patch_node.assert_not_called() + + def test_return_true_when_node_has_host_definitions(self): + self._test_is_node_has_host_definitions(self.fake_host_definitions_info.items, True) + + def test_return_false_when_node_has_host_definitions(self): + self._test_is_node_has_host_definitions([], False) + + def _test_is_node_has_host_definitions(self, host_definitions, expected_result): + self.node_manager.host_definition_manager.get_all_host_definitions_info_of_the_node.return_value = \ + host_definitions + result = self.node_manager.is_node_has_host_definitions(test_settings.FAKE_NODE_NAME) + self.assertEqual(result, expected_result) + self.node_manager.host_definition_manager.get_all_host_definitions_info_of_the_node.assert_called_with( + test_settings.FAKE_NODE_NAME) + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_generate_single_node_with_system_id_success(self, mock_utils): + self._prepare_generate_nodes_with_system_id(mock_utils, [self.fake_node_info]) + result = self.node_manager.generate_nodes_with_system_id(self.fake_secret_data) + self.assertEqual(result, {self.fake_node_info.name: test_settings.FAKE_SYSTEM_ID}) + self._assert_generate_nodes_with_system_id(mock_utils) + self._assert_get_system_id_for_node_called_once() + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_generate_single_node_with_empty_system_id_when_fail_to_get_system_info(self, mock_utils): + self._prepare_generate_nodes_with_system_id(mock_utils, [self.fake_node_info]) + self.mock_get_system_info_for_topologies.side_effect = ValidationException('fail') + result = self.node_manager.generate_nodes_with_system_id(self.fake_secret_data) + self.assertEqual(result, {self.fake_node_info.name: ''}) + self._assert_generate_nodes_with_system_id(mock_utils) + self._assert_get_system_id_for_node_called_once() + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_generate_multiple_nodes_with_system_id_success(self, mock_utils): + second_node_info = deepcopy(self.fake_node_info) + second_node_info.name = 'second_node_info' + expected_result = {self.fake_node_info.name: test_settings.FAKE_SYSTEM_ID, + second_node_info.name: test_settings.FAKE_SYSTEM_ID} + self._prepare_generate_nodes_with_system_id(mock_utils, [self.fake_node_info, second_node_info]) + result = self.node_manager.generate_nodes_with_system_id(self.fake_secret_data) + self.assertEqual(result, expected_result) + self._assert_generate_nodes_with_system_id(mock_utils) + self.assertEqual(self.node_manager.secret_manager.get_topology_labels.call_count, 2) + self.assertEqual(self.mock_get_system_info_for_topologies.call_count, 2) + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_get_empty_dict_when_there_are_no_nodes_success(self, mock_utils): + self._prepare_generate_nodes_with_system_id(mock_utils, []) + result = self.node_manager.generate_nodes_with_system_id(self.fake_secret_data) + self.assertEqual(result, {}) + self._assert_generate_nodes_with_system_id(mock_utils) + self.node_manager.secret_manager.get_topology_labels.assert_not_called() + self.mock_get_system_info_for_topologies.assert_not_called() + + def _prepare_generate_nodes_with_system_id(self, mock_utils, nodes_info): + mock_utils.get_secret_config.return_value = self.fake_secret_config + self.node_manager.get_nodes_info = Mock() + self.node_manager.get_nodes_info.return_value = nodes_info + self.node_manager.secret_manager.get_topology_labels.return_value = test_settings.FAKE_TOPOLOGY_LABELS + self.mock_get_system_info_for_topologies.return_value = (None, test_settings.FAKE_SYSTEM_ID) + + def _assert_generate_nodes_with_system_id(self, mock_utils): + mock_utils.get_secret_config.assert_called_once_with(self.fake_secret_data) + self.node_manager.get_nodes_info.assert_called_once_with() + + def _assert_get_system_id_for_node_called_once(self): + self.node_manager.secret_manager.get_topology_labels.assert_called_once_with(self.fake_node_info.labels) + self.mock_get_system_info_for_topologies.assert_called_once_with( + self.fake_secret_config, test_settings.FAKE_TOPOLOGY_LABELS) + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_return_true_when_node_has_new_manage_node_label_success(self, manifest_utils): + self._prepare_is_node_has_new_manage_node_label(manifest_utils, False, True) + self._test_is_node_has_new_manage_node_label(True, self.fake_csi_node_info) + manifest_utils.is_dynamic_node_labeling_allowed.assert_called_once_with() + self.node_manager.is_node_has_manage_node_label.assert_called_once_with(self.fake_csi_node_info.name) + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_return_false_when_dynamic_node_labeling_allowed_success(self, manifest_utils): + self._prepare_is_node_has_new_manage_node_label(manifest_utils, True) + self._test_is_node_has_new_manage_node_label(False, self.fake_csi_node_info) + manifest_utils.is_dynamic_node_labeling_allowed.assert_called_once_with() + self.node_manager.is_node_has_manage_node_label.assert_not_called() + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_return_false_when_node_not_has_manage_node_label_success(self, manifest_utils): + self._prepare_is_node_has_new_manage_node_label(manifest_utils, False, False) + self._test_is_node_has_new_manage_node_label(False, self.fake_csi_node_info) + manifest_utils.is_dynamic_node_labeling_allowed.assert_called_once_with() + self.node_manager.is_node_has_manage_node_label.assert_called_once_with(self.fake_csi_node_info.name) + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_return_false_when_node_is_already_managed_success(self, manifest_utils): + self.global_managed_nodes[test_settings.FAKE_NODE_NAME] = self.fake_managed_node + self._prepare_is_node_has_new_manage_node_label(manifest_utils, False, True) + self._test_is_node_has_new_manage_node_label(False, self.fake_csi_node_info) + manifest_utils.is_dynamic_node_labeling_allowed.assert_called_once_with() + self.node_manager.is_node_has_manage_node_label.assert_called_once_with(self.fake_csi_node_info.name) + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_return_false_when_csi_node_do_not_have_node_id_success(self, manifest_utils): + csi_node_info = deepcopy(self.fake_csi_node_info) + csi_node_info.node_id = '' + self._prepare_is_node_has_new_manage_node_label(manifest_utils, False, True) + self._test_is_node_has_new_manage_node_label(False, csi_node_info) + manifest_utils.is_dynamic_node_labeling_allowed.assert_called_once_with() + self.node_manager.is_node_has_manage_node_label.assert_called_once_with(csi_node_info.name) + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_return_false_when_node_is_not_in_unmanaged_nodes_list_success(self, manifest_utils): + csi_node_info = deepcopy(self.fake_csi_node_info) + csi_node_info.name = 'bad-name' + self._prepare_is_node_has_new_manage_node_label(manifest_utils, False, True) + self._test_is_node_has_new_manage_node_label(False, csi_node_info) + manifest_utils.is_dynamic_node_labeling_allowed.assert_called_once_with() + self.node_manager.is_node_has_manage_node_label.assert_called_once_with(csi_node_info.name) + + def _prepare_is_node_has_new_manage_node_label( + self, manifest_utils, is_dynamic_node_labeling_allowed, is_node_has_manage_node_label=False): + manifest_utils.is_dynamic_node_labeling_allowed.return_value = is_dynamic_node_labeling_allowed + self.node_manager.is_node_has_manage_node_label = Mock() + self.node_manager.is_node_has_manage_node_label.return_value = is_node_has_manage_node_label + + def _test_is_node_has_new_manage_node_label(self, expected_result, csi_node_info): + result = self.node_manager.is_node_has_new_manage_node_label( + csi_node_info, [test_settings.FAKE_NODE_NAME]) + self.assertEqual(result, expected_result) + + def test_do_not_handle_node_topologies_when_node_is_not_managed(self): + self.global_managed_nodes = {} + self.node_manager.handle_node_topologies(self.fake_node_info, test_settings.MODIFIED_EVENT_TYPE) + self._assert_do_not_handle_node_topologies() + + def test_do_not_handle_node_topologies_when_watch_event_is_not_modified_type(self): + self.global_managed_nodes[test_settings.FAKE_NODE_NAME] = self.fake_managed_node + self.node_manager.handle_node_topologies(self.fake_node_info, test_settings.ADDED_EVENT) + self._assert_do_not_handle_node_topologies() + + def _assert_do_not_handle_node_topologies(self): + self.node_manager.secret_manager.is_node_should_managed_on_secret_info.assert_not_called() + self.node_manager.secret_manager.is_node_labels_in_system_ids_topologies.assert_not_called() + self.node_manager.secret_manager.get_system_id_for_node_labels.assert_not_called() + self.node_manager.definition_manager.define_node_on_all_storages.assert_not_called() + + def test_do_not_handle_node_topologies_when_node_is_not_in_secret_topologies(self): + self._prepare_handle_node_topologies(self.fake_secret_info, False, False) + global_managed_secrets = deepcopy(self.global_managed_secrets) + self.node_manager.handle_node_topologies(self.fake_node_info, test_settings.MODIFIED_EVENT_TYPE) + self.assertEqual(global_managed_secrets[0].nodes_with_system_id, + self.global_managed_secrets[0].nodes_with_system_id) + self.node_manager.secret_manager.is_node_should_managed_on_secret_info.assert_called_once_with( + self.fake_node_info.name, self.fake_secret_info) + self.node_manager.secret_manager.is_node_labels_in_system_ids_topologies.assert_called_once_with( + self.fake_secret_info.system_ids_topologies, self.fake_node_info.labels) + self.node_manager.secret_manager.get_system_id_for_node_labels.assert_not_called() + self.node_manager.definition_manager.define_node_on_all_storages.assert_not_called() + + def test_remove_node_from_secret_topology_fields_if_topology_not_match_anymore(self): + fake_secret_info = deepcopy(self.fake_secret_info) + self._prepare_handle_node_topologies(fake_secret_info, True, False) + self.node_manager.handle_node_topologies(self.fake_node_info, test_settings.MODIFIED_EVENT_TYPE) + self._assert_global_secrets_changed_as_wanted({}) + self._assert_called_remove_node_if_topology_not_match(fake_secret_info) + + def _assert_called_remove_node_if_topology_not_match(self, fake_secret_info): + self.node_manager.secret_manager.is_node_should_managed_on_secret_info.assert_called_once_with( + self.fake_node_info.name, fake_secret_info) + self.node_manager.secret_manager.is_node_labels_in_system_ids_topologies.assert_called_once_with( + fake_secret_info.system_ids_topologies, self.fake_node_info.labels) + self.node_manager.secret_manager.get_system_id_for_node_labels.assert_not_called() + self.node_manager.definition_manager.define_node_on_all_storages.assert_not_called() + + def test_define_host_with_new_topology(self): + fake_secret_info = deepcopy(self.fake_secret_info) + fake_secret_info.nodes_with_system_id = {} + self._prepare_handle_node_topologies(fake_secret_info, False, True) + self.node_manager.handle_node_topologies(self.fake_node_info, test_settings.MODIFIED_EVENT_TYPE) + self._assert_called_define_host_with_new_topology(fake_secret_info) + self._assert_global_secrets_changed_as_wanted(self.fake_secret_info.nodes_with_system_id) + + def _prepare_handle_node_topologies(self, fake_secret_info, is_node_should_managed, + is_node_labels_in_system_ids_topologies): + self.global_managed_nodes[test_settings.FAKE_NODE_NAME] = self.fake_managed_node + self.global_managed_secrets.append(fake_secret_info) + self.node_manager.secret_manager.is_node_should_managed_on_secret_info.return_value = is_node_should_managed + self.node_manager.secret_manager.is_node_labels_in_system_ids_topologies.return_value = \ + is_node_labels_in_system_ids_topologies + self.node_manager.secret_manager.get_system_id_for_node_labels.return_value = test_settings.FAKE_SYSTEM_ID + + def _assert_called_define_host_with_new_topology(self, fake_secret_info): + self.node_manager.secret_manager.is_node_should_managed_on_secret_info.assert_called_once_with( + self.fake_node_info.name, fake_secret_info) + self.node_manager.secret_manager.is_node_labels_in_system_ids_topologies.assert_called_once_with( + fake_secret_info.system_ids_topologies, self.fake_node_info.labels) + self.node_manager.secret_manager.get_system_id_for_node_labels.assert_called_once_with( + fake_secret_info.system_ids_topologies, self.fake_node_info.labels) + self.node_manager.definition_manager.define_node_on_all_storages.assert_called_once_with( + test_settings.FAKE_NODE_NAME) + + def _assert_global_secrets_changed_as_wanted(self, expected_nodes_with_system_id): + managed_secret_info = self.global_managed_secrets[0] + self.assertEqual(managed_secret_info.name, self.fake_secret_info.name) + self.assertEqual(managed_secret_info.namespace, self.fake_secret_info.namespace) + self.assertEqual(managed_secret_info.nodes_with_system_id, expected_nodes_with_system_id) + self.assertEqual(managed_secret_info.system_ids_topologies, self.fake_secret_info.system_ids_topologies) + self.assertEqual(managed_secret_info.managed_storage_classes, self.fake_secret_info.managed_storage_classes) + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_update_node_io_group_when_node_is_managed_and_his_io_group_was_changed(self, mock_utils): + fake_managed_node = deepcopy(self.fake_managed_node) + self._test_update_node_io_group_when_node_is_managed( + mock_utils, fake_managed_node, 'different_io_group', 'different_io_group') + self.node_manager.definition_manager.define_node_on_all_storages.assert_called_once_with( + test_settings.FAKE_NODE_NAME) + + @patch('{}.utils'.format(test_settings.NODE_MANAGER_PATH)) + def test_do_not_update_node_io_group_when_node_is_managed_and_his_io_group_was_not_changed(self, mock_utils): + fake_managed_node = deepcopy(self.fake_managed_node) + self._test_update_node_io_group_when_node_is_managed( + mock_utils, fake_managed_node, fake_managed_node.io_group, self.fake_managed_node.io_group) + + def _test_update_node_io_group_when_node_is_managed( + self, mock_utils, fake_managed_node, io_group_from_labels, expected_io_group): + self.global_managed_nodes[test_settings.FAKE_NODE_NAME] = fake_managed_node + mock_utils.generate_io_group_from_labels.return_value = io_group_from_labels + self.node_manager.update_node_io_group(self.fake_node_info) + managed_node = self.global_managed_nodes[test_settings.FAKE_NODE_NAME] + self.assertEqual(managed_node.name, self.fake_managed_node.name) + self.assertEqual(managed_node.node_id, self.fake_managed_node.node_id) + self.assertEqual(managed_node.io_group, expected_io_group) + + @patch('{}.utils'.format(test_settings.TYPES_PATH)) + def test_do_not_update_node_io_group_when_node_is_not_managed(self, mock_utils): + self.global_managed_nodes = [] + mock_utils.generate_io_group_from_labels.return_value = self.fake_managed_node.io_group + self.node_manager.update_node_io_group(self.fake_node_info) + self.assertEqual(len(self.global_managed_nodes), 0) + self.node_manager.definition_manager.define_node_on_all_storages.assert_not_called() diff --git a/controllers/tests/controller_server/host_definer/resource_manager/resource_info_manager_test.py b/controllers/tests/controller_server/host_definer/resource_manager/resource_info_manager_test.py new file mode 100644 index 000000000..a80ffa8c3 --- /dev/null +++ b/controllers/tests/controller_server/host_definer/resource_manager/resource_info_manager_test.py @@ -0,0 +1,170 @@ +from unittest.mock import MagicMock, Mock, patch + +from controllers.servers.host_definer.types import SecretInfo +from controllers.tests.controller_server.host_definer.resource_manager.base_resource_manager import BaseResourceManager +from controllers.servers.host_definer.resource_manager.resource_info import ResourceInfoManager +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils +import controllers.tests.controller_server.host_definer.settings as test_settings + + +class TestCSINodeManager(BaseResourceManager): + def setUp(self): + super().setUp() + self.resource_info_manager = ResourceInfoManager() + self.resource_info_manager.k8s_api = MagicMock() + self.fake_node_info = test_utils.get_fake_node_info() + self.fake_csi_node_info = test_utils.get_fake_csi_node_info() + self.fake_storage_class_info = test_utils.get_fake_storage_class_info() + self.fake_host_definition_info = test_utils.get_fake_host_definition_info() + self.fake_k8s_secret = test_utils.get_fake_k8s_secret() + + def test_get_node_info_seccess(self): + self.resource_info_manager.k8s_api.read_node.return_value = test_utils.get_fake_k8s_node( + test_settings.MANAGE_NODE_LABEL) + self.resource_info_manager.generate_node_info = Mock() + self.resource_info_manager.generate_node_info.return_value = self.fake_node_info + result = self.resource_info_manager.get_node_info(test_settings.MANAGE_NODE_LABEL) + self.assertEqual(result.name, self.fake_node_info.name) + self.assertEqual(result.labels, self.fake_node_info.labels) + self.resource_info_manager.k8s_api.read_node.assert_called_once_with(test_settings.MANAGE_NODE_LABEL) + self.resource_info_manager.generate_node_info.assert_called_once_with(test_utils.get_fake_k8s_node( + test_settings.MANAGE_NODE_LABEL)) + + def test_fail_to_get_node_info(self): + self.resource_info_manager.k8s_api.read_node.return_value = None + self.resource_info_manager.generate_node_info = Mock() + result = self.resource_info_manager.get_node_info(test_settings.MANAGE_NODE_LABEL) + self.assertEqual(result.name, '') + self.assertEqual(result.labels, {}) + self.resource_info_manager.k8s_api.read_node.assert_called_once_with(test_settings.MANAGE_NODE_LABEL) + self.resource_info_manager.generate_node_info.assert_not_called() + + def test_generate_node_info_success(self): + result = self.resource_info_manager.generate_node_info(test_utils.get_fake_k8s_node( + test_settings.MANAGE_NODE_LABEL)) + self.assertEqual(result.name, self.fake_node_info.name) + self.assertEqual(result.labels, test_utils.get_fake_k8s_node(test_settings.MANAGE_NODE_LABEL).metadata.labels) + + def test_get_csi_node_info_success(self): + self.resource_info_manager.k8s_api.get_csi_node.return_value = test_utils.get_fake_k8s_csi_node() + self.resource_info_manager.generate_csi_node_info = Mock() + self.resource_info_manager.generate_csi_node_info.return_value = self.fake_csi_node_info + result = self.resource_info_manager.get_csi_node_info(test_settings.FAKE_NODE_NAME) + self.assertEqual(result.name, self.fake_csi_node_info.name) + self.assertEqual(result.node_id, self.fake_csi_node_info.node_id) + self.resource_info_manager.k8s_api.get_csi_node.assert_called_once_with(test_settings.FAKE_NODE_NAME) + self.resource_info_manager.generate_csi_node_info.assert_called_once_with(test_utils.get_fake_k8s_csi_node()) + + def test_get_non_exist_csi_node_info_success(self): + self.resource_info_manager.k8s_api.get_csi_node.return_value = None + self.resource_info_manager.generate_csi_node_info = Mock() + result = self.resource_info_manager.get_csi_node_info(test_settings.FAKE_NODE_NAME) + self.assertEqual(result.name, "") + self.assertEqual(result.node_id, "") + self.resource_info_manager.k8s_api.get_csi_node.assert_called_once_with(test_settings.FAKE_NODE_NAME) + self.resource_info_manager.generate_csi_node_info.assert_not_called() + + def test_generate_csi_node_info_with_ibm_driver_success(self): + result = self.resource_info_manager.generate_csi_node_info( + test_utils.get_fake_k8s_csi_node(test_settings.CSI_PROVISIONER_NAME)) + self.assertEqual(result.name, self.fake_csi_node_info.name) + self.assertEqual(result.node_id, self.fake_csi_node_info.node_id) + + def test_generate_csi_node_info_with_non_ibm_driver_success(self): + result = self.resource_info_manager.generate_csi_node_info( + test_utils.get_fake_k8s_csi_node(test_settings.FAKE_CSI_PROVISIONER)) + self.assertEqual(result.name, self.fake_csi_node_info.name) + self.assertEqual(result.node_id, '') + + def test_get_storage_classes_info_success(self): + self.resource_info_manager.generate_storage_class_info = Mock() + self._test_get_k8s_resources_info_success( + self.resource_info_manager.get_storage_classes_info, self.resource_info_manager.k8s_api.list_storage_class, + self.resource_info_manager.generate_storage_class_info, self.fake_storage_class_info, + test_utils.get_fake_k8s_storage_class_items(test_settings.CSI_PROVISIONER_NAME)) + + def test_get_storage_classes_info_empty_list_success(self): + self.resource_info_manager.generate_storage_class_info = Mock() + self._test_get_k8s_resources_info_empty_list_success(self.resource_info_manager.get_storage_classes_info, + self.resource_info_manager.k8s_api.list_storage_class, + self.resource_info_manager.generate_storage_class_info) + + def test_generate_storage_class_info_success(self): + k8s_storage_class = test_utils.get_fake_k8s_storage_class(test_settings.CSI_PROVISIONER_NAME) + result = self.resource_info_manager.generate_storage_class_info(k8s_storage_class) + self.assertEqual(result.name, self.fake_storage_class_info.name) + self.assertEqual(result.provisioner, self.fake_storage_class_info.provisioner) + self.assertEqual(result.parameters, self.fake_storage_class_info.parameters) + + def test_get_csi_pods_info_success(self): + self._test_get_pods_info(1) + + def test_get_multiple_csi_pods_info_success(self): + self._test_get_pods_info(2) + + def _test_get_pods_info(self, number_of_pods): + self.resource_info_manager.k8s_api.list_pod_for_all_namespaces.return_value = \ + test_utils.get_fake_k8s_pods_items(number_of_pods) + result = self.resource_info_manager.get_csi_pods_info() + self.assertEqual(result[0].name, test_utils.get_fake_k8s_pods_items().items[0].metadata.name) + self.assertEqual(result[0].node_name, test_utils.get_fake_k8s_pods_items().items[0].spec.node_name) + self.assertEqual(len(result), number_of_pods) + self.resource_info_manager.k8s_api.list_pod_for_all_namespaces.assert_called_once_with( + test_settings.DRIVER_PRODUCT_LABEL) + + def test_get_none_when_fail_to_get_k8s_pods(self): + self.resource_info_manager.k8s_api.list_pod_for_all_namespaces.return_value = None + result = self.resource_info_manager.get_csi_pods_info() + self.assertEqual(result, []) + self.resource_info_manager.k8s_api.list_pod_for_all_namespaces.assert_called_once_with( + test_settings.DRIVER_PRODUCT_LABEL) + + @patch('{}.utils'.format(test_settings.RESOURCE_INFO_MANAGER_PATH)) + def test_generate_host_definition_info_success(self, mock_utils): + k8s_host_definition = test_utils.get_fake_k8s_host_definition(test_settings.READY_PHASE) + mock_utils.get_k8s_object_resource_version.return_value = self.fake_host_definition_info.resource_version + result = self.resource_info_manager.generate_host_definition_info(k8s_host_definition) + self.assertEqual(result.name, self.fake_host_definition_info.name) + self.assertEqual(result.resource_version, self.fake_host_definition_info.resource_version) + self.assertEqual(result.uid, self.fake_host_definition_info.uid) + self.assertEqual(result.phase, self.fake_host_definition_info.phase) + self.assertEqual(result.secret_name, self.fake_host_definition_info.secret_name) + self.assertEqual(result.secret_namespace, self.fake_host_definition_info.secret_namespace) + self.assertEqual(result.node_name, self.fake_host_definition_info.node_name) + self.assertEqual(result.node_id, self.fake_host_definition_info.node_id) + self.assertEqual(result.connectivity_type, self.fake_host_definition_info.connectivity_type) + mock_utils.get_k8s_object_resource_version.assert_called_once_with(k8s_host_definition) + + def test_generate_k8s_secret_to_secret_info_success(self): + result = self.resource_info_manager.generate_k8s_secret_to_secret_info(self.fake_k8s_secret, 'input1', 'input2') + self.assertEqual(result.name, test_settings.FAKE_SECRET) + self.assertEqual(result.namespace, test_settings.FAKE_SECRET_NAMESPACE) + self.assertEqual(result.nodes_with_system_id, 'input1') + self.assertEqual(result.system_ids_topologies, 'input2') + self.assertEqual(type(result), SecretInfo) + + def test_generate_k8s_secret_to_secret_info_defaults_success(self): + result = self.resource_info_manager.generate_k8s_secret_to_secret_info(self.fake_k8s_secret) + self.assertEqual(result.name, test_settings.FAKE_SECRET) + self.assertEqual(result.namespace, test_settings.FAKE_SECRET_NAMESPACE) + self.assertEqual(result.nodes_with_system_id, {}) + self.assertEqual(result.system_ids_topologies, {}) + self.assertEqual(type(result), SecretInfo) + + def test_generate_secret_info_success(self): + result = self.resource_info_manager.generate_secret_info( + test_settings.FAKE_SECRET, test_settings.FAKE_SECRET_NAMESPACE, 'input1', 'input2') + self.assertEqual(result.name, test_settings.FAKE_SECRET) + self.assertEqual(result.namespace, test_settings.FAKE_SECRET_NAMESPACE) + self.assertEqual(result.nodes_with_system_id, 'input1') + self.assertEqual(result.system_ids_topologies, 'input2') + self.assertEqual(type(result), SecretInfo) + + def test_generate_secret_info_defaults_success(self): + result = self.resource_info_manager.generate_secret_info(test_settings.FAKE_SECRET, + test_settings.FAKE_SECRET_NAMESPACE) + self.assertEqual(result.name, test_settings.FAKE_SECRET) + self.assertEqual(result.namespace, test_settings.FAKE_SECRET_NAMESPACE) + self.assertEqual(result.nodes_with_system_id, {}) + self.assertEqual(result.system_ids_topologies, {}) + self.assertEqual(type(result), SecretInfo) diff --git a/controllers/tests/controller_server/host_definer/resource_manager/secret_manager_test.py b/controllers/tests/controller_server/host_definer/resource_manager/secret_manager_test.py new file mode 100644 index 000000000..d30f08bfc --- /dev/null +++ b/controllers/tests/controller_server/host_definer/resource_manager/secret_manager_test.py @@ -0,0 +1,315 @@ +from copy import deepcopy +from unittest.mock import MagicMock, Mock, patch + +from controllers.tests.controller_server.host_definer.resource_manager.base_resource_manager import BaseResourceManager +from controllers.servers.host_definer.resource_manager.secret import SecretManager +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils +import controllers.tests.controller_server.host_definer.utils.k8s_manifests_utils as test_manifest_utils +import controllers.tests.controller_server.host_definer.settings as test_settings + + +class TestSecretManager(BaseResourceManager): + def setUp(self): + self.secret_manager = SecretManager() + self.secret_manager.k8s_api = MagicMock() + self.secret_manager.resource_info_manager = MagicMock() + self.fake_secret_info = test_utils.get_fake_secret_info() + self.fake_secret_data = test_utils.get_fake_k8s_secret().data + self.fake_k8s_secret = test_utils.get_fake_k8s_secret() + self.secret_config_with_system_info = test_manifest_utils.get_fake_secret_config_with_system_info_manifest() + self.expected_decode_secret_config = 'decoded secret config' + self.managed_secrets = patch('{}.MANAGED_SECRETS'.format(test_settings.SECRET_MANAGER_PATH), + [self.fake_secret_info]).start() + self.mock_is_topology_match = patch('{}.is_topology_match'.format(test_settings.SECRET_MANAGER_PATH)).start() + + @patch('{}.utils'.format(test_settings.SECRET_MANAGER_PATH)) + def test_get_secret_data_success(self, mock_utils): + result = self._test_get_secret_data(self.fake_secret_data, mock_utils) + mock_utils.change_decode_base64_secret_config.assert_called_once_with(self.fake_secret_data) + self.assertEqual(result, self.expected_decode_secret_config) + + @patch('{}.utils'.format(test_settings.SECRET_MANAGER_PATH)) + def test_get_empty_dict_when_there_is_no_secret_data(self, mock_utils): + result = self._test_get_secret_data({}, mock_utils) + mock_utils.change_decode_base64_secret_config.assert_not_called() + self.assertEqual(result, {}) + + def _test_get_secret_data(self, secret_data, mock_utils): + self.secret_manager.k8s_api.get_secret_data.return_value = secret_data + mock_utils.change_decode_base64_secret_config.return_value = self.expected_decode_secret_config + result = self.secret_manager.get_secret_data(test_settings.FAKE_SECRET, test_settings.FAKE_SECRET_NAMESPACE) + self.secret_manager.k8s_api.get_secret_data.assert_called_once_with(test_settings.FAKE_SECRET, + test_settings.FAKE_SECRET_NAMESPACE) + return result + + @patch('{}.utils'.format(test_settings.SECRET_MANAGER_PATH)) + def test_return_true_when_node_should_be_managed_on_secret(self, manifest_utils): + self._prepare_is_node_should_be_managed_on_secret() + result = self._test_is_node_should_be_managed_on_secret(True, manifest_utils) + self.assertTrue(result) + + @patch('{}.utils'.format(test_settings.SECRET_MANAGER_PATH)) + def test_return_false_when_node_should_be_managed_on_secret(self, manifest_utils): + self._prepare_is_node_should_be_managed_on_secret() + result = self._test_is_node_should_be_managed_on_secret(False, manifest_utils) + self.assertFalse(result) + + def _prepare_is_node_should_be_managed_on_secret(self): + self._prepare_get_secret_data(self.fake_secret_data) + self.secret_manager.resource_info_manager.generate_secret_info.return_value = self.fake_secret_info + self._prepare_get_matching_managed_secret_info(0) + self.secret_manager.is_node_should_managed_on_secret_info = Mock() + + def _test_is_node_should_be_managed_on_secret(self, is_node_should_be_managed, manifest_utils): + self.secret_manager.is_node_should_managed_on_secret_info.return_value = is_node_should_be_managed + result = self.secret_manager.is_node_should_be_managed_on_secret( + test_settings.FAKE_NODE_NAME, test_settings.FAKE_SECRET, test_settings.FAKE_SECRET_NAMESPACE) + self.secret_manager.get_secret_data.assert_called_once_with( + test_settings.FAKE_SECRET, test_settings.FAKE_SECRET_NAMESPACE) + manifest_utils.validate_secret.assert_called_once_with(self.fake_secret_data) + self.secret_manager.resource_info_manager.generate_secret_info.assert_called_once_with( + test_settings.FAKE_SECRET, test_settings.FAKE_SECRET_NAMESPACE) + self.secret_manager.get_matching_managed_secret_info.assert_called_once_with(self.fake_secret_info) + self.secret_manager.is_node_should_managed_on_secret_info.assert_called_once_with( + test_settings.FAKE_NODE_NAME, self.fake_secret_info) + return result + + def test_node_should_be_managed_when_node_name_in_nodes_with_system_id(self): + result = self.secret_manager.is_node_should_managed_on_secret_info( + test_settings.FAKE_NODE_NAME, self.fake_secret_info) + self.assertTrue(result) + + def test_node_should_be_managed_when_nodes_with_system_id_is_empty(self): + secret_info = deepcopy(self.fake_secret_info) + secret_info.nodes_with_system_id = {} + result = self.secret_manager.is_node_should_managed_on_secret_info( + test_settings.FAKE_NODE_NAME, self.fake_secret_info) + self.assertTrue(result) + + def test_node_should_not_be_managed_when_node_not_in_nodes_with_system_id(self): + result = self.secret_manager.is_node_should_managed_on_secret_info('bad_node', self.fake_secret_info) + self.assertFalse(result) + + def test_node_should_not_be_managed_on_empty_secret_info(self): + result = self.secret_manager.is_node_should_managed_on_secret_info(test_settings.FAKE_NODE_NAME, None) + self.assertFalse(result) + + def test_get_matching_managed_secret_info_success(self): + result = self.secret_manager.get_matching_managed_secret_info(self.fake_secret_info) + self.assertEqual(result, (self.fake_secret_info, 0)) + + def test_do_not_find_matching_secret_info(self): + secret_info = deepcopy(self.fake_secret_info) + secret_info.name = 'bad_name' + result = self.secret_manager.get_matching_managed_secret_info(secret_info) + self.assertEqual(result, (secret_info, -1)) + + def test_get_second_matching_managed_secret_info_success(self): + secret_info = deepcopy(self.fake_secret_info) + secret_info.name = 'name' + self.managed_secrets.append(secret_info) + result = self.secret_manager.get_matching_managed_secret_info(secret_info) + self.assertEqual(result, (secret_info, 1)) + + def test_return_true_when_node_in_system_ids_topologies(self): + result = self._test_is_node_in_system_ids_topologies(test_settings.FAKE_SYSTEM_ID) + self.assertTrue(result) + + def test_return_false_when_node_not_in_system_ids_topologies(self): + result = self._test_is_node_in_system_ids_topologies('') + self.assertFalse(result) + + def _test_is_node_in_system_ids_topologies(self, system_id): + node_labels = [test_settings.MANAGE_NODE_LABEL] + self.secret_manager.get_system_id_for_node_labels = Mock() + self.secret_manager.get_system_id_for_node_labels.return_value = system_id + result = self.secret_manager.is_node_labels_in_system_ids_topologies(self.fake_secret_info, node_labels) + self.secret_manager.get_system_id_for_node_labels.assert_called_once_with(self.fake_secret_info, node_labels) + return result + + def test_get_system_id_when_system_ids_topologies_with_multiple_system_ids(self): + system_ids_topologies = { + test_settings.FAKE_SYSTEM_ID + '1': test_settings.FAKE_TOPOLOGY_LABELS, + test_settings.FAKE_SYSTEM_ID + '2': test_settings.FAKE_TOPOLOGY_LABELS} + result = self._test_get_system_id_for_node_labels([False, True], system_ids_topologies) + self.assertEqual(result, test_settings.FAKE_SYSTEM_ID + '2') + self.assertEqual(self.mock_is_topology_match.call_count, 2) + + def test_get_system_id_when_node_topology_labels_match(self): + result = self._test_get_system_id_for_node_labels([True], test_settings.FAKE_SYSTEM_IDS_TOPOLOGIES) + self.assertEqual(result, test_settings.FAKE_SYSTEM_ID) + self.mock_is_topology_match.assert_called_once_with(test_settings.FAKE_TOPOLOGY_LABELS, + test_settings.FAKE_TOPOLOGY_LABELS) + + def test_get_empty_string_when_node_topology_labels_do_not_match(self): + result = self._test_get_system_id_for_node_labels([False], test_settings.FAKE_SYSTEM_IDS_TOPOLOGIES) + self.assertEqual(result, '') + self.mock_is_topology_match.assert_called_once_with(test_settings.FAKE_TOPOLOGY_LABELS, + test_settings.FAKE_TOPOLOGY_LABELS) + + def test_get_empty_string_when_system_ids_topologies_is_empty(self): + result = self._test_get_system_id_for_node_labels([False], {}) + self.assertEqual(result, '') + self.mock_is_topology_match.assert_not_called() + + def _test_get_system_id_for_node_labels(self, is_topology_match, system_ids_topologies): + self._prepare_get_topology_labels() + self.mock_is_topology_match.side_effect = is_topology_match + result = self.secret_manager.get_system_id_for_node_labels(system_ids_topologies, + test_settings.FAKE_TOPOLOGY_LABELS) + self.secret_manager.get_topology_labels.assert_called_once_with(test_settings.FAKE_TOPOLOGY_LABELS) + return result + + @patch('{}.utils'.format(test_settings.SECRET_MANAGER_PATH)) + def test_when_secret_have_config_field_it_is_considered_as_topology(self, mock_utils): + result = self._test_is_topology_secret('secret_config', mock_utils) + self.assertTrue(result) + + @patch('{}.utils'.format(test_settings.SECRET_MANAGER_PATH)) + def test_when_secret_do_not_have_config_field_it_is_do_not_considered_as_topology(self, mock_utils): + result = self._test_is_topology_secret({}, mock_utils) + self.assertFalse(result) + + def _test_is_topology_secret(self, secret_config, mock_utils): + mock_utils.get_secret_config.return_value = secret_config + result = self.secret_manager.is_topology_secret(self.fake_secret_data) + mock_utils.validate_secret.assert_called_once_with(self.fake_secret_data) + mock_utils.get_secret_config.assert_called_once_with(self.fake_secret_data) + return result + + @patch('{}.utils'.format(test_settings.SECRET_MANAGER_PATH)) + def test_get_only_first_label_when_it_is_a_topology(self, manifest_utils): + expected_result = {test_settings.FAKE_TOPOLOGY_LABEL + '1': test_settings.TRUE_STRING} + self._test_get_topology_labels([True, False], test_settings.FAKE_TOPOLOGY_LABELS, + expected_result, 2, manifest_utils) + + @patch('{}.utils'.format(test_settings.SECRET_MANAGER_PATH)) + def test_get_both_labels_when_they_are_topology(self, manifest_utils): + expected_result = test_settings.FAKE_TOPOLOGY_LABELS + self._test_get_topology_labels([True, True], test_settings.FAKE_TOPOLOGY_LABELS, + expected_result, 2, manifest_utils) + + @patch('{}.utils'.format(test_settings.SECRET_MANAGER_PATH)) + def test_get_empty_dict_when_non_of_the_labels_are_topology(self, manifest_utils): + expected_result = {} + self._test_get_topology_labels([False, False], test_settings.FAKE_TOPOLOGY_LABELS, + expected_result, 2, manifest_utils) + + @patch('{}.utils'.format(test_settings.SECRET_MANAGER_PATH)) + def test_get_empty_dict_when_getting_empty_dict_to_check(self, manifest_utils): + expected_result = {} + self._test_get_topology_labels([], {}, expected_result, 0, manifest_utils) + + def _test_get_topology_labels(self, is_topology_label, labels_to_check, expected_result, + expected_is_topology_call_count, manifest_utils): + manifest_utils.is_topology_label.side_effect = is_topology_label + result = self.secret_manager.get_topology_labels(labels_to_check) + self.assertEqual(manifest_utils.is_topology_label.call_count, expected_is_topology_call_count) + self.assertEqual(result, expected_result) + + @patch('{}.utils'.format(test_settings.SECRET_MANAGER_PATH)) + def test_generate_secret_system_ids_topologies_success(self, manifest_utils): + expected_result = { + 'system_id_with_supported_topologies' + '1': [test_settings.FAKE_TOPOLOGY_LABEL], + 'system_id_with_no_supported_topologies' + '2': None + } + self._test_generate_secret_system_ids_topologies( + self.secret_config_with_system_info, expected_result, manifest_utils) + + @patch('{}.utils'.format(test_settings.SECRET_MANAGER_PATH)) + def test_generate_empty_secret_system_ids_topologies(self, manifest_utils): + self._test_generate_secret_system_ids_topologies({}, {}, manifest_utils) + + def _test_generate_secret_system_ids_topologies(self, secret_config, expected_result, mock_utils): + mock_utils.get_secret_config.return_value = secret_config + result = self.secret_manager.generate_secret_system_ids_topologies(self.fake_secret_data) + self.assertEqual(result, expected_result) + mock_utils.get_secret_config.assert_called_once_with(self.fake_secret_data) + + def test_return_true_when_parameter_is_secret_success(self): + self._test_is_secret_success(test_settings.STORAGE_CLASS_SECRET_FIELD, True) + + def test_return_false_when_parameter_has_bad_suffix_is_secret_success(self): + self._test_is_secret_success('csi.storage.k8s.io/bad_suffix', False) + + def test_return_false_when_parameter_has_bad_prefix_is_secret_success(self): + self._test_is_secret_success('bad_prefix/secret-name', False) + + def _test_is_secret_success(self, parameter, expected_result): + result = self.secret_manager.is_secret(parameter) + self.assertEqual(result, expected_result) + + def test_get_secret_name_and_namespace_from_storage_class_success(self): + result = self.secret_manager.get_secret_name_and_namespace( + test_utils.get_fake_storage_class_info(), test_settings.STORAGE_CLASS_SECRET_FIELD) + self.assertEqual(result, (test_settings.FAKE_SECRET, test_settings.FAKE_SECRET_NAMESPACE)) + + def test_add_unique_secret_info_to_list_success(self): + self._test_add_unique_secret_info_to_list([]) + + def test_do_not_change_list_when_secret_is_already_there_success(self): + self._test_add_unique_secret_info_to_list([self.fake_secret_info]) + + def _test_add_unique_secret_info_to_list(self, secrets_info_list): + result = self.secret_manager.add_unique_secret_info_to_list(self.fake_secret_info, secrets_info_list) + self.assertEqual(result, [self.fake_secret_info]) + + @patch('{}.utils'.format(test_settings.SECRET_MANAGER_PATH)) + def test_secret_can_be_changed_when_secret_is_managed_and_the_watch_event_type_is_not_deleted(self, + manifest_utils): + manifest_utils.is_watch_object_type_is_delete.return_value = False + self._test_is_secret_can_be_changed(0, True) + manifest_utils.is_watch_object_type_is_delete.assert_called_once_with('event_type') + + @patch('{}.utils'.format(test_settings.SECRET_MANAGER_PATH)) + def test_secret_cannot_be_changed_when_secret_is_not_managed(self, manifest_utils): + self._test_is_secret_can_be_changed(-1, False) + manifest_utils.is_watch_object_type_is_delete.assert_not_called() + + @patch('{}.utils'.format(test_settings.SECRET_MANAGER_PATH)) + def test_secret_cannot_be_changed_when_watch_event_type_is_deleted(self, manifest_utils): + manifest_utils.is_watch_object_type_is_delete.return_value = True + self._test_is_secret_can_be_changed(0, False) + manifest_utils.is_watch_object_type_is_delete.assert_called_once_with('event_type') + + def _test_is_secret_can_be_changed(self, managed_secret_index, expected_result): + self._prepare_get_matching_managed_secret_info(managed_secret_index) + result = self.secret_manager.is_secret_can_be_changed(self.fake_secret_info, 'event_type') + self.secret_manager.get_matching_managed_secret_info.assert_called_once_with(self.fake_secret_info) + self.assertEqual(result, expected_result) + + def _prepare_get_matching_managed_secret_info(self, managed_secret_index): + self.secret_manager.get_matching_managed_secret_info = Mock() + self.secret_manager.get_matching_managed_secret_info.return_value = ( + self.fake_secret_info, managed_secret_index) + + @patch('{}.utils'.format(test_settings.SECRET_MANAGER_PATH)) + def test_get_array_connectivity_info_success(self, manifest_utils): + manifest_utils.get_array_connection_info_from_secret_data.return_value = 'fake_array_connectivity_info' + self._test_get_array_connectivity_info(self.fake_secret_data, 'fake_array_connectivity_info') + self.secret_manager.get_topology_labels.assert_called_once_with(test_settings.FAKE_TOPOLOGY_LABELS) + manifest_utils.get_array_connection_info_from_secret_data.assert_called_once_with( + self.fake_secret_data, test_settings.FAKE_TOPOLOGY_LABELS) + + @patch('{}.utils'.format(test_settings.SECRET_MANAGER_PATH)) + def test_get_empty_array_connectivity_info_when_secret_data_is_empty_success(self, manifest_utils): + self._test_get_array_connectivity_info(None, {}) + self.secret_manager.get_topology_labels.assert_not_called() + manifest_utils.get_array_connection_info_from_secret_data.assert_not_called() + + def _test_get_array_connectivity_info(self, secret_data, expected_result): + self._prepare_get_secret_data(secret_data) + self._prepare_get_topology_labels() + result = self.secret_manager.get_array_connection_info( + test_settings.FAKE_SECRET, test_settings.FAKE_SECRET_NAMESPACE, test_settings.FAKE_TOPOLOGY_LABELS) + self.assertEqual(result, expected_result) + self.secret_manager.get_secret_data.assert_called_once_with( + test_settings.FAKE_SECRET, test_settings.FAKE_SECRET_NAMESPACE) + + def _prepare_get_secret_data(self, secret_data): + self.secret_manager.get_secret_data = Mock() + self.secret_manager.get_secret_data.return_value = secret_data + + def _prepare_get_topology_labels(self): + self.secret_manager.get_topology_labels = Mock() + self.secret_manager.get_topology_labels.return_value = test_settings.FAKE_TOPOLOGY_LABELS diff --git a/controllers/tests/controller_server/host_definer/resource_manager/storage_class_manager_test.py b/controllers/tests/controller_server/host_definer/resource_manager/storage_class_manager_test.py new file mode 100644 index 000000000..b722f3606 --- /dev/null +++ b/controllers/tests/controller_server/host_definer/resource_manager/storage_class_manager_test.py @@ -0,0 +1,23 @@ +from copy import deepcopy + +from controllers.tests.controller_server.host_definer.resource_manager.base_resource_manager import BaseResourceManager +from controllers.servers.host_definer.resource_manager.storage_class import StorageClassManager +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils + + +class TestStorageClassManager(BaseResourceManager): + def setUp(self): + self.storage_class_manager = StorageClassManager() + self.fake_storage_class_info = test_utils.get_fake_storage_class_info() + + def test_return_true_when_storage_class_has_csi_as_a_provisioner(self): + self._test_is_storage_class_has_csi_as_a_provisioner(self.fake_storage_class_info, True) + + def test_return_false_when_storage_class_does_not_have_csi_as_a_provisioner(self): + storage_class_info = deepcopy(self.fake_storage_class_info) + storage_class_info.provisioner = 'some_provisioner' + self._test_is_storage_class_has_csi_as_a_provisioner(storage_class_info, False) + + def _test_is_storage_class_has_csi_as_a_provisioner(self, storage_class_info, expected_result): + result = self.storage_class_manager.is_storage_class_has_csi_as_a_provisioner(storage_class_info) + self.assertEqual(result, expected_result) diff --git a/controllers/tests/controller_server/host_definer/secret_watcher_test.py b/controllers/tests/controller_server/host_definer/secret_watcher_test.py deleted file mode 100644 index fc851d36b..000000000 --- a/controllers/tests/controller_server/host_definer/secret_watcher_test.py +++ /dev/null @@ -1,60 +0,0 @@ -from unittest.mock import Mock, patch - -import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils -import controllers.tests.controller_server.host_definer.settings as test_settings -from controllers.tests.controller_server.host_definer.common import BaseSetUp -from controllers.servers.host_definer.watcher.secret_watcher import SecretWatcher - - -class SecretWatcherBase(BaseSetUp): - def setUp(self): - super().setUp() - self.secret_watcher = test_utils.get_class_mock(SecretWatcher) - self.managed_secrets_on_secret_watcher = test_utils.patch_managed_secrets_global_variable( - test_settings.SECRET_WATCHER_PATH) - - -class TestWatchSecretResources(SecretWatcherBase): - def setUp(self): - super().setUp() - self.secret_stream = patch('{}.watch.Watch.stream'.format(test_settings.SECRET_WATCHER_PATH)).start() - self.secret_watcher._loop_forever = Mock() - self.secret_watcher._loop_forever.side_effect = [True, False] - self.secret_watcher.core_api.read_node.return_value = self.k8s_node_with_fake_label - - def test_create_definitions_managed_secret_was_modified(self): - self._prepare_default_mocks_for_secret() - self.nodes_on_watcher_helper[test_settings.FAKE_NODE_NAME] = test_utils.get_fake_managed_node() - self.managed_secrets_on_watcher_helper.append(test_utils.get_fake_secret_info()) - self.managed_secrets_on_secret_watcher.append(test_utils.get_fake_secret_info()) - self.secret_watcher.host_definitions_api.get.return_value = \ - test_utils.get_fake_k8s_host_definitions_items('not_ready') - self.secret_watcher.watch_secret_resources() - self.secret_watcher.storage_host_servicer.define_host.assert_called_once_with( - test_utils.get_define_request(node_id_from_host_definition=test_settings.FAKE_NODE_ID)) - - def test_ignore_deleted_events(self): - self._prepare_default_mocks_for_secret() - self.secret_stream.return_value = iter([test_utils.get_fake_secret_watch_event( - test_settings.DELETED_EVENT_TYPE)]) - self.secret_watcher.watch_secret_resources() - self.secret_watcher.storage_host_servicer.define_host.assert_not_called() - - def test_do_not_create_definitions_when_managed_secret_modified_but_no_managed_nodes(self): - self._prepare_default_mocks_for_secret() - self.secret_watcher.watch_secret_resources() - self.secret_watcher.storage_host_servicer.define_host.assert_not_called() - - def test_modified_secret_that_is_not_in_managed_secrets(self): - self._prepare_default_mocks_for_secret() - self.secret_watcher.watch_secret_resources() - self.secret_watcher.storage_host_servicer.define_host.assert_not_called() - - def _prepare_default_mocks_for_secret(self): - self.secret_stream.return_value = iter([test_utils.get_fake_secret_watch_event( - test_settings.MODIFIED_EVENT_TYPE)]) - self.secret_watcher.host_definitions_api.get.return_value = \ - test_utils.get_fake_k8s_host_definitions_items(test_settings.READY_PHASE) - self.secret_watcher.core_api.read_namespaced_secret.return_value = test_utils.get_fake_k8s_secret() - self.os.getenv.return_value = '' - self.secret_watcher.core_api.list_node.return_value = test_utils.get_fake_k8s_nodes_items() diff --git a/controllers/tests/controller_server/host_definer/settings.py b/controllers/tests/controller_server/host_definer/settings.py index 38ed3632b..5c20223ec 100644 --- a/controllers/tests/controller_server/host_definer/settings.py +++ b/controllers/tests/controller_server/host_definer/settings.py @@ -7,6 +7,8 @@ STORAGE_CLASS_DRIVERS_FIELD = 'drivers' CSI_NODE_NODE_ID_FIELD = 'nodeID' CSI_PROVISIONER_NAME = 'block.csi.ibm.com' +CSI_IBM_API_VERSION = 'csi.ibm.com/v1' +HOST_DEFINITION_KIND = 'HostDefinition' FAKE_SECRET = 'fake_secret' FAKE_SECRET_NAMESPACE = 'fake_secret_namespace' FAKE_NODE_NAME = 'fake_node_name' @@ -16,6 +18,8 @@ FAKE_SECRET_PASSWORD = 'fake_password' FAKE_SECRET_USER_NAME = 'fake_user_name' FAKE_STORAGE_CLASS = 'fake_storage_class' +FAKE_CONNECTIVITY_TYPE = 'fake_connectivity_type' +FAKE_SYSTEM_ID = 'fake_system_id' IQN = 'iqn.1994-05.com.redhat:686358c930fe' WWPN = '34859340583048' NQN = 'nqn.2014-08.org.nvmexpress:uuid:b57708c7-5bb6-46a0-b2af-9d824bf539e1' @@ -26,13 +30,22 @@ FAKE_LABEL = 'FAKE_LABEL' MANAGE_NODE_LABEL = 'hostdefiner.block.csi.ibm.com/manage-node' FORBID_DELETION_LABEL = 'hostdefiner.block.csi.ibm.com/do-not-delete-definition' -WATCHER_HELPER_PATH = 'controllers.servers.host_definer.watcher.watcher_helper' +FAKE_TOPOLOGY_LABEL = 'topology.block.csi.ibm.com/topology' NODES_WATCHER_PATH = 'controllers.servers.host_definer.watcher.node_watcher' SECRET_WATCHER_PATH = 'controllers.servers.host_definer.watcher.secret_watcher' CSI_NODE_WATCHER_PATH = 'controllers.servers.host_definer.watcher.csi_node_watcher' STORAGE_CLASS_WATCHER_PATH = 'controllers.servers.host_definer.watcher.storage_class_watcher' +HOST_DEFINITION_WATCHER_PATH = 'controllers.servers.host_definer.watcher.host_definition_watcher' +UTILS_PATH = 'controllers.servers.host_definer.utils.utils' +HOST_DEFINITION_MANAGER_PATH = 'controllers.servers.host_definer.resource_manager.host_definition' SETTINGS_PATH = 'controllers.servers.host_definer.settings' -METADATA_RESOURCE_VERSION_FIELD = 'resource_version' +SECRET_MANAGER_PATH = 'controllers.servers.host_definer.resource_manager.secret' +NODE_MANAGER_PATH = 'controllers.servers.host_definer.resource_manager.node' +RESOURCE_INFO_MANAGER_PATH = 'controllers.servers.host_definer.resource_manager.resource_info' +TYPES_PATH = 'controllers.servers.host_definer.types' +REQUEST_MANAGER_PATH = 'controllers.servers.host_definer.definition_manager.request' +DEFINITION_MANAGER_PATH = 'controllers.servers.host_definer.definition_manager.definition' +K8S_API_PATH = 'controllers.servers.host_definer.k8s.api' FAKE_RESOURCE_VERSION = '495873498573' FAKE_UID = '50345093486093' EVENT_TYPE_FIELD = 'type' @@ -47,15 +60,17 @@ METADATA_UID_FIELD = 'uid' STATUS_PHASE_FIELD = 'phase' READY_PHASE = 'Ready' +ERROR_PHASE = 'Error' HOST_DEFINITION_FIELD = 'hostDefinition' SECRET_NAME_FIELD = 'secretName' SECRET_NAMESPACE_FIELD = 'secretNamespace' HOST_DEFINITION_NODE_NAME_FIELD = 'nodeName' +NODE_NAME_ON_STORAGE_FIELD = 'nodeNameOnStorage' SECRET_DATA_FIELD = 'data' FAIL_MESSAGE_FROM_STORAGE = 'fail_from_storage' PENDING_CREATION_PHASE = 'PendingCreation' PENDING_DELETION_PHASE = 'PendingDeletion' -SUCCESS_MESSAGE = 'Host defined successfully on the array' +MESSAGE = 'Host defined successfully on the array' HOST_DEFINITION_PENDING_VARS = {'HOST_DEFINITION_PENDING_RETRIES': 3, 'HOST_DEFINITION_PENDING_EXPONENTIAL_BACKOFF_IN_SECONDS': 0.2, 'HOST_DEFINITION_PENDING_DELAY_IN_SECONDS': 0.2} @@ -68,3 +83,43 @@ IO_GROUP_IDS = ['0', '2'] IO_GROUP_NAMES = ['io_grp0', 'io_grp2'] FAKE_STRING_IO_GROUP = common_settings.IO_GROUP_DELIMITER.join(IO_GROUP_IDS) +FAKE_STORAGE_CLASS_PARAMETERS = { + STORAGE_CLASS_SECRET_FIELD: FAKE_SECRET, + STORAGE_CLASS_SECRET_NAMESPACE_FIELD: FAKE_SECRET_NAMESPACE +} +CSI_IBM_FINALIZER = common_settings.HOST_DEFINITION_PLURAL + '.' + common_settings.CSI_IBM_GROUP +FINALIZERS_FIELD = 'finalizers' +CONNECTIVITY_TYPE_FIELD = 'connectivityType' +DEFINE_ACTION = 'Define' +UNDEFINE_ACTION = 'Undefine' +SUCCESSFUL_MESSAGE_TYPE = 'Successful' +FAILED_MESSAGE_TYPE = 'Failed' +HOST_DEFINER = 'hostDefiner' +NORMAL_EVENT_TYPE = 'Normal' +WARNING_EVENT_TYPE = 'Warning' +DRIVER_PRODUCT_LABEL = 'product=ibm-block-csi-driver' +FAKE_FC_PORTS = ['532453845345', '532453845345'] +PORTS_FIELD = 'ports' +IO_GROUP_FIELD = 'ioGroups' +MANAGEMENT_ADDRESS_FIELD = 'managementAddress' +API_VERSION_FIELD = 'apiVersion' +KIND_FIELD = 'kind' +IO_GROUP_LABEL_PREFIX = 'hostdefiner.block.csi.ibm.com/io-group-' +FAKE_SINGLE_IO_GROUP_STRING = '0' +FAKE_MULTIPLE_IO_GROUP_STRING = '0:1' +BASE64_STRING = 'eydmYWtlX2tleSc6ICdmYWtlX3ZhbHVlJ30K' +DECODED_BASE64_STRING = "{'fake_key': 'fake_value'}" +FAKE_ENCODED_CONFIG = {"config": BASE64_STRING} +FAKE_DECODED_CONFIG_STRING = {"config": DECODED_BASE64_STRING} +FAKE_DECODED_CONFIG = {"config": {'fake_key': 'fake_value'}} +CONFIG_FIELD = 'config' +PREFIX_ENV_VAR = 'PREFIX' +CONNECTIVITY_ENV_VAR = 'CONNECTIVITY_TYPE' +ISCSI_CONNECTIVITY_TYPE = 'iscsi' +DEFAULT_NAMESPACE = 'default' +ALLOW_DELETE_ENV_VAR = 'ALLOW_DELETE' +DYNAMIC_NODE_LABELING_ENV_VAR = 'DYNAMIC_NODE_LABELING' +FAKE_TOPOLOGY_LABELS = {FAKE_TOPOLOGY_LABEL + '1': TRUE_STRING, FAKE_TOPOLOGY_LABEL + '2': TRUE_STRING} +FAKE_SYSTEM_IDS_TOPOLOGIES = {FAKE_SYSTEM_ID: FAKE_TOPOLOGY_LABELS} +SECRET_SUPPORTED_TOPOLOGIES_PARAMETER = "supported_topologies" +CONNECTIVITY_TYPE_LABEL = '{}/connectivity-type'.format(CSI_PROVISIONER_NAME) diff --git a/controllers/tests/controller_server/host_definer/storage_class_watcher_test.py b/controllers/tests/controller_server/host_definer/storage_class_watcher_test.py deleted file mode 100644 index 58a5619ee..000000000 --- a/controllers/tests/controller_server/host_definer/storage_class_watcher_test.py +++ /dev/null @@ -1,94 +0,0 @@ -from unittest.mock import Mock, patch - -import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils -import controllers.tests.controller_server.host_definer.settings as test_settings -from controllers.tests.controller_server.host_definer.common import BaseSetUp -from controllers.servers.host_definer.watcher.storage_class_watcher import StorageClassWatcher - - -class StorageClassWatcherBase(BaseSetUp): - def setUp(self): - super().setUp() - self.storage_class_watcher = test_utils.get_class_mock(StorageClassWatcher) - self.managed_secrets_on_storage_class_watcher = test_utils.patch_managed_secrets_global_variable( - test_settings.STORAGE_CLASS_WATCHER_PATH) - - -class TestAddInitialStorageClasses(StorageClassWatcherBase): - def setUp(self): - super().setUp() - self.storage_class_watcher.storage_api.list_storage_class.return_value = \ - test_utils.get_fake_k8s_storage_class_items(test_settings.CSI_PROVISIONER_NAME) - self.storage_class_watcher.host_definitions_api.get.return_value = \ - test_utils.get_fake_k8s_host_definitions_items(test_settings.READY_PHASE) - self.storage_class_watcher.core_api.read_namespaced_secret.return_value = test_utils.get_fake_k8s_secret() - self.os.getenv.return_value = '' - self.storage_class_watcher.core_api.read_node.return_value = self.k8s_node_with_fake_label - - def test_add_new_storage_class_with_new_secret(self): - self.storage_class_watcher.host_definitions_api.get.return_value = \ - test_utils.get_fake_k8s_host_definitions_items('not_ready') - self.nodes_on_watcher_helper[test_settings.FAKE_NODE_NAME] = test_utils.get_fake_managed_node() - self.storage_class_watcher.add_initial_storage_classes() - self.storage_class_watcher.storage_host_servicer.define_host.assert_called_once_with( - test_utils.get_define_request(node_id_from_host_definition=test_settings.FAKE_NODE_ID)) - - def test_add_new_storage_class_with_existing_secret(self): - self.managed_secrets_on_storage_class_watcher.append(test_utils.get_fake_secret_info()) - self.managed_secrets_on_watcher_helper.append(test_utils.get_fake_secret_info()) - self.storage_class_watcher.add_initial_storage_classes() - self.storage_class_watcher.storage_host_servicer.define_host.assert_not_called() - self.assertEqual(2, self.managed_secrets_on_storage_class_watcher[0].managed_storage_classes) - - def test_add_new_storage_class_without_ibm_csi_provisioner(self): - self.storage_class_watcher.storage_api.list_storage_class.return_value = \ - test_utils.get_fake_k8s_storage_class_items(test_settings.FAKE_CSI_PROVISIONER) - self.storage_class_watcher.add_initial_storage_classes() - self.assertEqual(0, len(self.managed_secrets_on_storage_class_watcher)) - self.storage_class_watcher.storage_host_servicer.define_host.assert_not_called() - - -class TestWatchStorageClassResources(StorageClassWatcherBase): - def setUp(self): - super().setUp() - self.storage_class_stream = patch('{}.watch.Watch.stream'.format(test_settings.SECRET_WATCHER_PATH)).start() - self.storage_class_stream.return_value = iter([test_utils.get_fake_secret_storage_event( - test_settings.ADDED_EVENT, test_settings.CSI_PROVISIONER_NAME)]) - self.storage_class_watcher.host_definitions_api.get.return_value = \ - test_utils.get_fake_k8s_host_definitions_items(test_settings.READY_PHASE) - self.storage_class_watcher.core_api.read_namespaced_secret.return_value = test_utils.get_fake_k8s_secret() - self.os.getenv.return_value = '' - self.storage_class_watcher._loop_forever = Mock() - self.storage_class_watcher._loop_forever.side_effect = [True, False] - self.storage_class_watcher.core_api.read_node.return_value = self.k8s_node_with_fake_label - - def test_add_new_storage_class_with_new_secret(self): - self.storage_class_watcher.host_definitions_api.get.return_value = \ - test_utils.get_fake_k8s_host_definitions_items('not_ready') - self.nodes_on_watcher_helper[test_settings.FAKE_NODE_NAME] = test_utils.get_fake_managed_node() - self.storage_class_watcher.watch_storage_class_resources() - self.storage_class_watcher.storage_host_servicer.define_host.assert_called_once_with( - test_utils.get_define_request(node_id_from_host_definition=test_settings.FAKE_NODE_ID)) - self.assertEqual(1, len(self.managed_secrets_on_storage_class_watcher)) - - def test_add_new_storage_class_with_existing_secret(self): - self.managed_secrets_on_storage_class_watcher.append(test_utils.get_fake_secret_info()) - self.managed_secrets_on_watcher_helper.append(test_utils.get_fake_secret_info()) - self.storage_class_watcher.watch_storage_class_resources() - self.storage_class_watcher.storage_host_servicer.define_host.assert_not_called() - self.assertEqual(2, self.managed_secrets_on_storage_class_watcher[0].managed_storage_classes) - - def test_add_new_storage_class_without_ibm_csi_provisioner(self): - self.storage_class_stream.return_value = iter([test_utils.get_fake_secret_storage_event( - test_settings.ADDED_EVENT, test_settings.FAKE_CSI_PROVISIONER)]) - self.storage_class_watcher.watch_storage_class_resources() - self.assertEqual(0, len(self.managed_secrets_on_storage_class_watcher)) - self.storage_class_watcher.storage_host_servicer.define_host.assert_not_called() - - def test_deleted_managed_storage_class(self): - self.storage_class_stream.return_value = iter([test_utils.get_fake_secret_storage_event( - test_settings.DELETED_EVENT_TYPE, test_settings.CSI_PROVISIONER_NAME)]) - self.managed_secrets_on_storage_class_watcher.append(test_utils.get_fake_secret_info()) - self.managed_secrets_on_watcher_helper.append(test_utils.get_fake_secret_info()) - self.storage_class_watcher.watch_storage_class_resources() - self.assertEqual(0, self.managed_secrets_on_storage_class_watcher[0].managed_storage_classes) diff --git a/controllers/tests/controller_server/host_definer/utils/k8s_manifests_utils.py b/controllers/tests/controller_server/host_definer/utils/k8s_manifests_utils.py index 1764b200c..88374a126 100644 --- a/controllers/tests/controller_server/host_definer/utils/k8s_manifests_utils.py +++ b/controllers/tests/controller_server/host_definer/utils/k8s_manifests_utils.py @@ -26,28 +26,48 @@ def get_fake_k8s_daemon_set_manifest(updated_pods, desired_updated_pods): return _generate_manifest(test_settings.FAKE_NODE_PODS_NAME, k8s_daemon_set_status) -def get_fake_k8s_pod_manifest(): +def get_fake_k8s_pod_manifest(pod_suffix=''): k8s_pod_spec = { test_settings.SPEC_FIELD: { test_settings.POD_NODE_NAME_FIELD: test_settings.FAKE_NODE_NAME }} - return _generate_manifest(test_settings.FAKE_NODE_PODS_NAME, k8s_pod_spec) + return _generate_manifest(test_settings.FAKE_NODE_PODS_NAME + pod_suffix, k8s_pod_spec) -def get_fake_k8s_host_definition_manifest(host_definition_phase): +def get_fake_k8s_host_definition_manifest(host_definition_phase='ready'): status_phase_manifest = get_status_phase_manifest(host_definition_phase) + fields_manifest = get_fake_k8s_host_definition_response_fields_manifest() k8s_host_definition_body = { + test_settings.API_VERSION_FIELD: test_settings.CSI_IBM_API_VERSION, + test_settings.KIND_FIELD: test_settings.HOST_DEFINITION_KIND, test_settings.SPEC_FIELD: { test_settings.HOST_DEFINITION_FIELD: { + test_settings.HOST_DEFINITION_NODE_NAME_FIELD: test_settings.FAKE_NODE_NAME, test_settings.SECRET_NAME_FIELD: test_settings.FAKE_SECRET, test_settings.SECRET_NAMESPACE_FIELD: test_settings.FAKE_SECRET_NAMESPACE, - test_settings.HOST_DEFINITION_NODE_NAME_FIELD: test_settings.FAKE_NODE_NAME, - common_settings.HOST_DEFINITION_NODE_ID_FIELD: test_settings.FAKE_NODE_ID + common_settings.HOST_DEFINITION_NODE_ID_FIELD: test_settings.FAKE_NODE_ID, } }} + k8s_host_definition_body[test_settings.SPEC_FIELD][test_settings.HOST_DEFINITION_FIELD].update( + fields_manifest[test_settings.SPEC_FIELD][test_settings.HOST_DEFINITION_FIELD]) return _generate_manifest(test_settings.FAKE_NODE_NAME, status_phase_manifest, k8s_host_definition_body) +def get_fake_k8s_host_definition_response_fields_manifest(): + manifest = { + test_settings.SPEC_FIELD: { + test_settings.HOST_DEFINITION_FIELD: { + test_settings.NODE_NAME_ON_STORAGE_FIELD: test_settings.FAKE_NODE_NAME, + test_settings.CONNECTIVITY_TYPE_FIELD: test_settings.FAKE_CONNECTIVITY_TYPE, + test_settings.PORTS_FIELD: test_settings.FAKE_FC_PORTS, + test_settings.IO_GROUP_FIELD: test_settings.IO_GROUP_IDS, + test_settings.MANAGEMENT_ADDRESS_FIELD: test_settings.FAKE_SECRET_ARRAY + } + } + } + return _generate_manifest(test_settings.FAKE_NODE_NAME, manifest) + + def get_status_phase_manifest(phase): return { test_settings.STATUS_FIELD: { @@ -80,15 +100,12 @@ def get_fake_k8s_secret_manifest(): def get_fake_k8s_storage_class_manifest(provisioner): k8s_storage_class_body = { test_settings.STORAGE_CLASS_PROVISIONER_FIELD: provisioner, - test_settings.STORAGE_CLASS_PARAMETERS_FIELD: { - test_settings.STORAGE_CLASS_SECRET_FIELD: test_settings.FAKE_SECRET, - test_settings.STORAGE_CLASS_SECRET_NAMESPACE_FIELD: test_settings.FAKE_SECRET_NAMESPACE - }} + test_settings.STORAGE_CLASS_PARAMETERS_FIELD: test_settings.FAKE_STORAGE_CLASS_PARAMETERS} return _generate_manifest(test_settings.FAKE_STORAGE_CLASS, k8s_storage_class_body) def _generate_manifest(object_name, *extra_dicts): - metadata_manifest = _get_metadata_manifest() + metadata_manifest = get_metadata_manifest() metadata_manifest[test_settings.METADATA_FIELD][common_settings.NAME_FIELD] = object_name if len(extra_dicts) > 0: merged_dicts = _merge_dicts(metadata_manifest, extra_dicts[0]) @@ -99,10 +116,10 @@ def _generate_manifest(object_name, *extra_dicts): return merged_dicts -def _get_metadata_manifest(): +def get_metadata_manifest(): return { test_settings.METADATA_FIELD: { - test_settings.METADATA_RESOURCE_VERSION_FIELD: test_settings.FAKE_RESOURCE_VERSION, + common_settings.RESOURCE_VERSION_FIELD: test_settings.FAKE_RESOURCE_VERSION, test_settings.METADATA_UID_FIELD: test_settings.FAKE_UID }} @@ -131,3 +148,38 @@ def get_host_io_group_manifest(): test_settings.IO_GROUP_ID_FIELD: test_settings.IO_GROUP_IDS, common_settings.NAME_FIELD: test_settings.IO_GROUP_NAMES } + + +def get_empty_k8s_list_manifest(): + return { + common_settings.ITEMS_FIELD: [], + test_settings.METADATA_FIELD: { + common_settings.RESOURCE_VERSION_FIELD + } + } + + +def get_finalizers_manifest(finalizers): + return { + test_settings.METADATA_FIELD: { + common_settings.NAME_FIELD: test_settings.FAKE_NODE_NAME, + test_settings.FINALIZERS_FIELD: finalizers, + } + } + + +def get_general_labels_manifest(labels): + return { + test_settings.METADATA_FIELD: { + test_settings.NODE_LABELS_FIELD: labels + } + } + + +def get_fake_secret_config_with_system_info_manifest(): + return { + 'system_id_with_supported_topologies' + '1': { + test_settings.SECRET_SUPPORTED_TOPOLOGIES_PARAMETER: [test_settings.FAKE_TOPOLOGY_LABEL] + }, + 'system_id_with_no_supported_topologies' + '2': [test_settings.FAKE_TOPOLOGY_LABEL] + } diff --git a/controllers/tests/controller_server/host_definer/utils/test_utils.py b/controllers/tests/controller_server/host_definer/utils/test_utils.py index afe09dd08..d5bd2797c 100644 --- a/controllers/tests/controller_server/host_definer/utils/test_utils.py +++ b/controllers/tests/controller_server/host_definer/utils/test_utils.py @@ -1,14 +1,15 @@ from dataclasses import dataclass, field import func_timeout from munch import Munch +from kubernetes import client from mock import patch, Mock -import controllers.tests.controller_server.host_definer.utils.k8s_manifests_utils as manifest_utils -import controllers.tests.controller_server.host_definer.settings as test_settings -from controllers.tests.common.test_settings import HOST_NAME, SECRET_MANAGEMENT_ADDRESS_VALUE -from controllers.servers.host_definer.kubernetes_manager.manager import KubernetesManager from controllers.servers.host_definer.types import DefineHostRequest, DefineHostResponse from controllers.servers.csi.controller_types import ArrayConnectionInfo +from controllers.servers.host_definer.k8s.api import K8SApi +from controllers.tests.common.test_settings import HOST_NAME, SECRET_MANAGEMENT_ADDRESS_VALUE +import controllers.tests.controller_server.host_definer.settings as test_settings +import controllers.tests.controller_server.host_definer.utils.k8s_manifests_utils as test_manifest_utils @dataclass @@ -29,84 +30,96 @@ def getheaders(self): def get_fake_k8s_csi_nodes(csi_provisioner_name, number_of_csi_nodes): k8s_csi_nodes = [] for csi_node_index in range(number_of_csi_nodes): - k8s_csi_node_manifest = manifest_utils.get_k8s_csi_node_manifest( + k8s_csi_node_manifest = test_manifest_utils.get_k8s_csi_node_manifest( csi_provisioner_name, '-{}'.format(csi_node_index)) k8s_csi_nodes.append(Munch.fromDict(k8s_csi_node_manifest)) return K8sResourceItems(k8s_csi_nodes) -def get_fake_k8s_csi_node(csi_provisioner_name): - csi_node_manifest = manifest_utils.get_k8s_csi_node_manifest(csi_provisioner_name) +def get_fake_k8s_csi_node(csi_provisioner_name=""): + csi_node_manifest = test_manifest_utils.get_k8s_csi_node_manifest(csi_provisioner_name) return Munch.fromDict(csi_node_manifest) def get_fake_csi_node_watch_event(event_type): - return manifest_utils.generate_watch_event(event_type, manifest_utils.get_k8s_csi_node_manifest( + return test_manifest_utils.generate_watch_event(event_type, test_manifest_utils.get_k8s_csi_node_manifest( test_settings.CSI_PROVISIONER_NAME)) def get_fake_k8s_node(label): - return Munch.fromDict(manifest_utils.get_fake_k8s_node_manifest(label)) + return Munch.fromDict(test_manifest_utils.get_fake_k8s_node_manifest(label)) def get_fake_k8s_daemon_set_items(updated_pods, desired_updated_pods): - k8s_daemon_set_manifest = manifest_utils.get_fake_k8s_daemon_set_manifest(updated_pods, desired_updated_pods) - return K8sResourceItems([Munch.fromDict(k8s_daemon_set_manifest)]) + return K8sResourceItems([get_fake_k8s_daemon_set(updated_pods, desired_updated_pods)]) + + +def get_fake_k8s_daemon_set(updated_pods, desired_updated_pods): + k8s_daemon_set_manifest = test_manifest_utils.get_fake_k8s_daemon_set_manifest(updated_pods, desired_updated_pods) + return Munch.fromDict(k8s_daemon_set_manifest) def get_empty_k8s_pods(): return K8sResourceItems() -def get_fake_k8s_pods_items(): - k8s_pod_manifest = manifest_utils.get_fake_k8s_pod_manifest() - return K8sResourceItems([Munch.fromDict(k8s_pod_manifest)]) +def get_fake_k8s_pods_items(number_of_pods=1): + k8s_pods = [] + for pod_index in range(number_of_pods): + k8s_pod_manifest = test_manifest_utils.get_fake_k8s_pod_manifest('-{}'.format(pod_index)) + k8s_pods.append(Munch.fromDict(k8s_pod_manifest)) + return K8sResourceItems(k8s_pods) def get_empty_k8s_host_definitions(): return K8sResourceItems() -def get_fake_k8s_host_definitions_items(host_definition_phase): - return K8sResourceItems([_get_fake_k8s_host_definitions(host_definition_phase)]) +def get_fake_k8s_host_definitions_items(host_definition_phase='ready'): + return K8sResourceItems([get_fake_k8s_host_definition(host_definition_phase)]) -def _get_fake_k8s_host_definitions(host_definition_phase): - return Munch.fromDict(manifest_utils.get_fake_k8s_host_definition_manifest(host_definition_phase)) +def get_fake_k8s_host_definition(host_definition_phase): + return Munch.fromDict(test_manifest_utils.get_fake_k8s_host_definition_manifest(host_definition_phase)) -def get_fake_host_definition_watch_event(event_type, host_definition_phase): - return manifest_utils.generate_watch_event( - event_type, manifest_utils.get_fake_k8s_host_definition_manifest(host_definition_phase)) +def get_fake_host_definition_watch_event(event_type): + return test_manifest_utils.generate_watch_event( + event_type, test_manifest_utils.get_fake_k8s_host_definition_manifest()) def get_fake_node_watch_event(event_type): - return manifest_utils.generate_watch_event(event_type, manifest_utils.get_fake_k8s_node_manifest( + return test_manifest_utils.generate_watch_event(event_type, test_manifest_utils.get_fake_k8s_node_manifest( test_settings.MANAGE_NODE_LABEL)) def get_fake_k8s_nodes_items(): - k8s_node_manifest = manifest_utils.get_fake_k8s_node_manifest(test_settings.MANAGE_NODE_LABEL) + k8s_node_manifest = test_manifest_utils.get_fake_k8s_node_manifest(test_settings.MANAGE_NODE_LABEL) return K8sResourceItems([Munch.fromDict(k8s_node_manifest)]) def get_fake_secret_watch_event(event_type): - return manifest_utils.generate_watch_event(event_type, - manifest_utils.get_fake_k8s_secret_manifest()) + return test_manifest_utils.generate_watch_event(event_type, + test_manifest_utils.get_fake_k8s_secret_manifest()) def get_fake_k8s_secret(): - return Munch.fromDict(manifest_utils.get_fake_k8s_secret_manifest()) + return Munch.fromDict(test_manifest_utils.get_fake_k8s_secret_manifest()) def get_fake_k8s_storage_class_items(provisioner): - k8s_storage_classes_manifest = manifest_utils.get_fake_k8s_storage_class_manifest(provisioner) + k8s_storage_classes_manifest = test_manifest_utils.get_fake_k8s_storage_class_manifest(provisioner) return K8sResourceItems([Munch.fromDict(k8s_storage_classes_manifest)]) -def get_fake_secret_storage_event(event_type, provisioner): - return manifest_utils.generate_watch_event(event_type, - manifest_utils.get_fake_k8s_storage_class_manifest(provisioner)) +def get_fake_k8s_storage_class(provisioner): + k8s_storage_classes_manifest = test_manifest_utils.get_fake_k8s_storage_class_manifest(provisioner) + return Munch.fromDict(k8s_storage_classes_manifest) + + +def get_fake_storage_class_watch_event(event_type, provisioner='provisioner'): + return test_manifest_utils.generate_watch_event( + event_type, test_manifest_utils.get_fake_k8s_storage_class_manifest(provisioner)) def patch_pending_variables(): @@ -115,42 +128,16 @@ def patch_pending_variables(): test_settings.SETTINGS_PATH, pending_var), value).start() -def patch_kubernetes_manager_init(): +def patch_k8s_api_init(): for function_to_patch in test_settings.KUBERNETES_MANAGER_INIT_FUNCTIONS_TO_PATCH: - _patch_function(KubernetesManager, function_to_patch) + patch_function(K8SApi, function_to_patch) -def _patch_function(class_type, function): +def patch_function(class_type, function): patcher = patch.object(class_type, function) patcher.start() -def get_class_mock(class_type): - class_type_dict = _get_class_dict(class_type) - class_mock = _get_class(class_type, class_type_dict) - return _mock_class_vars(class_mock) - - -def _get_class_dict(class_type): - class_type_copy = class_type.__dict__.copy() - return class_type_copy - - -def _get_class(class_type, class_type_dict): - return type(_get_dummy_class_name(class_type), (class_type,), class_type_dict) - - -def _get_dummy_class_name(class_type): - return 'dummy_{}'.format(class_type.__name__) - - -def _mock_class_vars(class_type): - class_instance = class_type() - for method in vars(class_instance): - class_instance.__dict__[method] = Mock() - return class_instance - - def run_function_with_timeout(function, max_wait): try: func_timeout.func_timeout(max_wait, function) @@ -158,8 +145,8 @@ def run_function_with_timeout(function, max_wait): pass -def get_error_http_resp(): - return HttpResp(405, 'some problem', 'some reason') +def get_error_http_resp(status_code): + return HttpResp(status_code, 'some problem', 'some reason') def patch_nodes_global_variable(module_path): @@ -171,11 +158,11 @@ def patch_managed_secrets_global_variable(module_path): def get_pending_creation_status_manifest(): - return manifest_utils.get_status_phase_manifest(test_settings.PENDING_CREATION_PHASE) + return test_manifest_utils.get_status_phase_manifest(test_settings.PENDING_CREATION_PHASE) def get_ready_status_manifest(): - return manifest_utils.get_status_phase_manifest(test_settings.READY_PHASE) + return test_manifest_utils.get_status_phase_manifest(test_settings.READY_PHASE) def get_array_connection_info(): @@ -197,12 +184,14 @@ def get_define_response(connectivity_type, ports): SECRET_MANAGEMENT_ADDRESS_VALUE) -def get_fake_secret_info(): - secret_info = Mock(spec_set=['name', 'namespace', 'nodes_with_system_id', 'managed_storage_classes']) +def get_fake_secret_info(managed_storage_classes=0): + secret_info = Mock(spec_set=['name', 'namespace', 'nodes_with_system_id', + 'system_ids_topologies', 'managed_storage_classes']) secret_info.name = test_settings.FAKE_SECRET secret_info.namespace = test_settings.FAKE_SECRET_NAMESPACE - secret_info.nodes_with_system_id = {} - secret_info.managed_storage_classes = 1 + secret_info.nodes_with_system_id = {test_settings.FAKE_NODE_NAME: test_settings.FAKE_SYSTEM_ID} + secret_info.system_ids_topologies = {test_settings.FAKE_NODE_NAME: test_settings.FAKE_TOPOLOGY_LABELS} + secret_info.managed_storage_classes = managed_storage_classes return secret_info @@ -212,7 +201,13 @@ def get_fake_host_io_group_id(): def get_fake_host_io_group(): - return Munch.fromDict(manifest_utils.get_host_io_group_manifest()) + return Munch.fromDict(test_manifest_utils.get_host_io_group_manifest()) + + +def get_fake_empty_k8s_list(): + much_object = Munch.fromDict(test_manifest_utils.get_empty_k8s_list_manifest()) + much_object.items = [] + return much_object def get_fake_managed_node(): @@ -221,3 +216,102 @@ def get_fake_managed_node(): managed_node.node_id = test_settings.FAKE_NODE_ID managed_node.io_group = test_settings.FAKE_STRING_IO_GROUP return managed_node + + +def get_fake_csi_node_info(): + csi_node_info = Mock(spec_set=['name', 'node_id']) + csi_node_info.name = test_settings.FAKE_NODE_NAME + csi_node_info.node_id = test_settings.FAKE_NODE_ID + return csi_node_info + + +def get_fake_node_info(): + node_info = Mock(spec_set=['name', 'labels']) + node_info.name = test_settings.FAKE_NODE_NAME + node_info.labels = {test_settings.MANAGE_NODE_LABEL: test_settings.TRUE_STRING} + return node_info + + +def get_fake_storage_class_info(): + storage_class_info = Mock(spec_set=['name', 'provisioner', 'parameters']) + storage_class_info.name = test_settings.FAKE_STORAGE_CLASS + storage_class_info.provisioner = test_settings.CSI_PROVISIONER_NAME + storage_class_info.parameters = test_settings.FAKE_STORAGE_CLASS_PARAMETERS + return storage_class_info + + +def get_fake_host_definition_info(): + host_definition_info = Mock(spec_set=['name', 'resource_version', 'uid', 'phase', 'secret_name', + 'secret_namespace', 'node_name', 'node_id', 'connectivity_type']) + host_definition_info.name = test_settings.FAKE_NODE_NAME + host_definition_info.resource_version = test_settings.FAKE_RESOURCE_VERSION + host_definition_info.uid = test_settings.FAKE_UID + host_definition_info.phase = test_settings.READY_PHASE + host_definition_info.secret_name = test_settings.FAKE_SECRET + host_definition_info.secret_namespace = test_settings.FAKE_SECRET_NAMESPACE + host_definition_info.node_name = test_settings.FAKE_NODE_NAME + host_definition_info.node_id = test_settings.FAKE_NODE_ID + host_definition_info.connectivity_type = test_settings.FAKE_CONNECTIVITY_TYPE + return host_definition_info + + +def get_fake_empty_host_definition_info(): + host_definition_info = Mock(spec_set=['name', 'node_name', 'node_id']) + host_definition_info.name = '' + host_definition_info.node_name = '' + host_definition_info.node_id = '' + return host_definition_info + + +def get_object_reference(): + return client.V1ObjectReference( + api_version=test_settings.CSI_IBM_API_VERSION, kind=test_settings.HOST_DEFINITION_KIND, + name=test_settings.FAKE_NODE_NAME, resource_version=test_settings.FAKE_RESOURCE_VERSION, + uid=test_settings.FAKE_UID, ) + + +def get_event_object_metadata(): + return client.V1ObjectMeta(generate_name='{}.'.format(test_settings.FAKE_NODE_NAME), ) + + +def get_fake_define_host_response(): + response = Mock(spec_set=['error_message', 'connectivity_type', 'ports', + 'node_name_on_storage', 'io_group', 'management_address']) + response.error_message = test_settings.MESSAGE + response.connectivity_type = test_settings.FAKE_CONNECTIVITY_TYPE + response.ports = test_settings.FAKE_FC_PORTS + response.node_name_on_storage = test_settings.FAKE_NODE_NAME + response.io_group = test_settings.IO_GROUP_IDS + response.management_address = test_settings.FAKE_SECRET_ARRAY + return response + + +def get_fake_io_group_labels(number_of_io_groups): + labels = {} + for index in range(number_of_io_groups): + labels[test_settings.IO_GROUP_LABEL_PREFIX + str(index)] = test_settings.TRUE_STRING + return labels + + +def get_fake_k8s_metadata(): + return Munch.fromDict(test_manifest_utils.get_metadata_manifest()) + + +def get_fake_array_connectivity_info(): + array_connectivity_info = Mock(spec_set=['array_addresses', 'user', 'password', 'system_id']) + array_connectivity_info.array_addresses = [test_settings.FAKE_SECRET_ARRAY] + array_connectivity_info.user = test_settings.FAKE_SECRET_USER_NAME + array_connectivity_info.password = test_settings.FAKE_SECRET_PASSWORD + array_connectivity_info.system_id = '2' + return array_connectivity_info + + +def get_fake_pod_info(): + pod_info = Mock(spec_set=['name', 'node_name']) + pod_info.name = test_settings.FAKE_NODE_PODS_NAME + pod_info.node_name = test_settings.FAKE_NODE_NAME + return pod_info + + +def convert_manifest_to_munch(manifest): + return Munch.fromDict(manifest) diff --git a/controllers/tests/controller_server/host_definer/watchers/__init__.py b/controllers/tests/controller_server/host_definer/watchers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/controllers/tests/controller_server/host_definer/watchers/csi_node_watcher_test.py b/controllers/tests/controller_server/host_definer/watchers/csi_node_watcher_test.py new file mode 100644 index 000000000..54eb88170 --- /dev/null +++ b/controllers/tests/controller_server/host_definer/watchers/csi_node_watcher_test.py @@ -0,0 +1,342 @@ +from unittest.mock import MagicMock, patch +from copy import deepcopy + +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils +import controllers.tests.controller_server.host_definer.settings as test_settings +from controllers.tests.controller_server.host_definer.watchers.watcher_base import WatcherBaseSetUp +from controllers.servers.host_definer.watcher.csi_node_watcher import CsiNodeWatcher + + +class CsiNodeWatcherBase(WatcherBaseSetUp): + def setUp(self): + super().setUp() + self.watcher = CsiNodeWatcher() + self.watcher.resource_info_manager = MagicMock() + self.watcher.node_manager = MagicMock() + self.watcher.definition_manager = MagicMock() + self.watcher.csi_node = MagicMock() + self.watcher.k8s_api = MagicMock() + self.watcher.host_definition_manager = MagicMock() + self.fake_csi_node_info = test_utils.get_fake_csi_node_info() + + +class TestAddInitialCsiNodes(CsiNodeWatcherBase): + def test_add_initial_csi_nodes(self): + self.watcher.csi_node.get_csi_nodes_info_with_driver.return_value = [self.fake_csi_node_info] + self.watcher.node_manager.is_node_can_be_defined.return_value = True + self.watcher.add_initial_csi_nodes() + self.watcher.csi_node.get_csi_nodes_info_with_driver.assert_called_once_with() + self.watcher.node_manager.is_node_can_be_defined.assert_called_once_with(self.fake_csi_node_info.name) + self.watcher.node_manager.add_node_to_nodes.assert_called_once_with(self.fake_csi_node_info) + + def test_do_not_add_initial_csi_nodes_that_cannot_be_defined(self): + self.watcher.csi_node.get_csi_nodes_info_with_driver.return_value = [self.fake_csi_node_info] + self.watcher.node_manager.is_node_can_be_defined.return_value = False + self.watcher.add_initial_csi_nodes() + self.watcher.csi_node.get_csi_nodes_info_with_driver.assert_called_once_with() + self.watcher.node_manager.is_node_can_be_defined.assert_called_once_with(self.fake_csi_node_info.name) + self.watcher.node_manager.add_node_to_nodes.assert_not_called() + + def test_do_not_add_empty_initial_csi_nodes(self): + self.watcher.csi_node.get_csi_nodes_info_with_driver.return_value = [] + self.watcher.add_initial_csi_nodes() + self.watcher.csi_node.get_csi_nodes_info_with_driver.assert_called_once_with() + self.watcher.node_manager.is_node_can_be_defined.assert_not_called() + self.watcher.node_manager.add_node_to_nodes.assert_not_called() + + +class TestWatchCsiNodesResources(CsiNodeWatcherBase): + def setUp(self): + super().setUp() + self.fake_managed_nodes = test_utils.get_fake_managed_node() + self.managed_node_with_different_node_id = deepcopy(self.fake_managed_nodes) + self.managed_node_with_different_node_id.node_id = 'different_node_id' + self.fake_secret_info = test_utils.get_fake_secret_info() + self.fake_host_definition_info = test_utils.get_fake_host_definition_info() + self.csi_node_modified_watch_manifest = test_utils.get_fake_node_watch_event(test_settings.MODIFIED_EVENT_TYPE) + self.csi_node_modified_watch_munch = test_utils.convert_manifest_to_munch( + self.csi_node_modified_watch_manifest) + self.csi_node_deleted_watch_manifest = test_utils.get_fake_node_watch_event(test_settings.DELETED_EVENT_TYPE) + self.csi_node_deleted_watch_munch = test_utils.convert_manifest_to_munch(self.csi_node_deleted_watch_manifest) + self.global_managed_nodes = test_utils.patch_nodes_global_variable( + test_settings.CSI_NODE_WATCHER_PATH) + self.global_managed_secret = test_utils.patch_managed_secrets_global_variable( + test_settings.CSI_NODE_WATCHER_PATH) + + @patch('{}.utils'.format(test_settings.CSI_NODE_WATCHER_PATH)) + def test_do_not_define_managed_csi_node_with_deleted_event_that_is_part_of_update_but_node_id_did_not_change( + self, mock_utils): + self._prepare_watch_csi_nodes_resources( + mock_utils, self.csi_node_deleted_watch_manifest, self.csi_node_deleted_watch_munch) + self._prepare_handle_deleted_csi_node_pod(mock_utils, True, True) + test_utils.run_function_with_timeout(self.watcher.watch_csi_nodes_resources, 0.2) + self._assert_watch_csi_nodes_resources( + mock_utils, self.csi_node_deleted_watch_manifest, self.csi_node_deleted_watch_munch) + self._assert_handle_deleted_csi_node_pod_called(mock_utils, True, True) + + @patch('{}.utils'.format(test_settings.CSI_NODE_WATCHER_PATH)) + def test_define_managed_csi_node_with_deleted_event_that_is_part_of_update_and_node_id_change(self, mock_utils): + self._prepare_watch_csi_nodes_resources( + mock_utils, self.csi_node_deleted_watch_manifest, self.csi_node_deleted_watch_munch) + self._prepare_handle_deleted_csi_node_pod(mock_utils, True, True, True) + test_utils.run_function_with_timeout(self.watcher.watch_csi_nodes_resources, 0.2) + self._assert_watch_csi_nodes_resources( + mock_utils, self.csi_node_deleted_watch_manifest, self.csi_node_deleted_watch_munch) + self._assert_handle_deleted_csi_node_pod_called(mock_utils, True, True, True) + + @patch('{}.utils'.format(test_settings.CSI_NODE_WATCHER_PATH)) + def test_undefine_managed_csi_node_with_deleted_event_that_is_not_part_of_update(self, mock_utils): + self._prepare_watch_csi_nodes_resources( + mock_utils, self.csi_node_deleted_watch_manifest, self.csi_node_deleted_watch_munch) + self._prepare_handle_deleted_csi_node_pod(mock_utils, True, False, False, True, False) + test_utils.run_function_with_timeout(self.watcher.watch_csi_nodes_resources, 0.2) + self._assert_watch_csi_nodes_resources( + mock_utils, self.csi_node_deleted_watch_manifest, self.csi_node_deleted_watch_munch) + self._assert_handle_deleted_csi_node_pod_called(mock_utils, True, False, False, True, False) + + @patch('{}.utils'.format(test_settings.CSI_NODE_WATCHER_PATH)) + def test_do_not_undefine_managed_csi_node_with_deleted_event_when_node_has_forbid_deletion_label(self, mock_utils): + self._prepare_watch_csi_nodes_resources( + mock_utils, self.csi_node_deleted_watch_manifest, self.csi_node_deleted_watch_munch) + self._prepare_handle_deleted_csi_node_pod(mock_utils, True, False, False, True, True) + test_utils.run_function_with_timeout(self.watcher.watch_csi_nodes_resources, 0.2) + self._assert_watch_csi_nodes_resources( + mock_utils, self.csi_node_deleted_watch_manifest, self.csi_node_deleted_watch_munch) + self._assert_handle_deleted_csi_node_pod_called(mock_utils, True, False, False, True, True) + + @patch('{}.utils'.format(test_settings.CSI_NODE_WATCHER_PATH)) + def test_do_not_undefine_managed_csi_node_with_deleted_event_when_host_definer_cannot_delete_hosts( + self, mock_utils): + self._prepare_watch_csi_nodes_resources( + mock_utils, self.csi_node_deleted_watch_manifest, self.csi_node_deleted_watch_munch) + self._prepare_handle_deleted_csi_node_pod(mock_utils, True, False, False, False, False) + test_utils.run_function_with_timeout(self.watcher.watch_csi_nodes_resources, 0.2) + self._assert_watch_csi_nodes_resources( + mock_utils, self.csi_node_deleted_watch_manifest, self.csi_node_deleted_watch_munch) + self._assert_handle_deleted_csi_node_pod_called(mock_utils, True, False, False, False, False) + + @patch('{}.utils'.format(test_settings.CSI_NODE_WATCHER_PATH)) + def test_do_not_handle_unmanaged_csi_node_with_deleted_event(self, mock_utils): + self._prepare_watch_csi_nodes_resources( + mock_utils, self.csi_node_deleted_watch_manifest, self.csi_node_deleted_watch_munch) + self._prepare_handle_deleted_csi_node_pod(mock_utils, False) + test_utils.run_function_with_timeout(self.watcher.watch_csi_nodes_resources, 0.2) + self._assert_watch_csi_nodes_resources( + mock_utils, self.csi_node_deleted_watch_manifest, self.csi_node_deleted_watch_munch) + self._assert_handle_deleted_csi_node_pod_called(mock_utils, False) + + @patch('{}.utils'.format(test_settings.CSI_NODE_WATCHER_PATH)) + def test_do_not_handle_csi_node_with_deleted_event_and_it_is_not_in_global_nodes(self, mock_utils): + self._prepare_watch_csi_nodes_resources( + mock_utils, self.csi_node_deleted_watch_manifest, self.csi_node_deleted_watch_munch) + self.global_managed_nodes.pop(self.fake_csi_node_info.name, None) + test_utils.run_function_with_timeout(self.watcher.watch_csi_nodes_resources, 0.2) + self._assert_watch_csi_nodes_resources( + mock_utils, self.csi_node_deleted_watch_manifest, self.csi_node_deleted_watch_munch) + self._assert_handle_deleted_csi_node_pod_not_called(mock_utils) + + @patch('{}.utils'.format(test_settings.CSI_NODE_WATCHER_PATH)) + def test_define_new_csi_node_with_ibm_block_csi_driver_with_modified_event(self, mock_utils): + self._prepare_watch_csi_nodes_resources( + mock_utils, self.csi_node_modified_watch_manifest, self.csi_node_modified_watch_munch) + self.global_managed_nodes.pop(self.fake_csi_node_info.name, None) + self.watcher.node_manager.is_node_can_be_defined.return_value = True + test_utils.run_function_with_timeout(self.watcher.watch_csi_nodes_resources, 0.2) + self._assert_watch_csi_nodes_resources( + mock_utils, self.csi_node_modified_watch_manifest, self.csi_node_modified_watch_munch) + self._assert_define_new_csi_node_called(mock_utils) + + @patch('{}.utils'.format(test_settings.CSI_NODE_WATCHER_PATH)) + def test_do_not_handle_new_csi_node_with_ibm_block_csi_driver_with_modified_event_but_cannot_be_defined( + self, mock_utils): + self._prepare_watch_csi_nodes_resources( + mock_utils, self.csi_node_modified_watch_manifest, self.csi_node_modified_watch_munch) + self.global_managed_nodes.pop(self.fake_csi_node_info.name, None) + self.watcher.node_manager.is_node_can_be_defined.return_value = False + test_utils.run_function_with_timeout(self.watcher.watch_csi_nodes_resources, 0.2) + self._assert_watch_csi_nodes_resources( + mock_utils, self.csi_node_modified_watch_manifest, self.csi_node_modified_watch_munch) + self._assert_define_new_csi_node_not_called() + self._assert_handle_deleted_csi_node_pod_not_called(mock_utils) + + @patch('{}.utils'.format(test_settings.CSI_NODE_WATCHER_PATH)) + def test_do_not_define_managed_csi_node_with_modified_event_that_is_part_of_update_but_node_id_did_not_change( + self, mock_utils): + self._prepare_watch_csi_nodes_resources( + mock_utils, self.csi_node_modified_watch_manifest, self.csi_node_modified_watch_munch) + self._prepare_handle_deleted_csi_node_pod(mock_utils, True, True) + test_utils.run_function_with_timeout(self.watcher.watch_csi_nodes_resources, 0.2) + self._assert_watch_csi_nodes_resources( + mock_utils, self.csi_node_modified_watch_manifest, self.csi_node_modified_watch_munch) + self._assert_define_new_csi_node_not_called() + self._assert_handle_deleted_csi_node_pod_called(mock_utils, True, True) + + @patch('{}.utils'.format(test_settings.CSI_NODE_WATCHER_PATH)) + def test_define_managed_csi_node_with_modified_event_that_is_part_of_update_and_node_id_change(self, mock_utils): + self._prepare_watch_csi_nodes_resources( + mock_utils, self.csi_node_modified_watch_manifest, self.csi_node_modified_watch_munch) + self._prepare_handle_deleted_csi_node_pod(mock_utils, True, True, True) + test_utils.run_function_with_timeout(self.watcher.watch_csi_nodes_resources, 0.2) + self._assert_watch_csi_nodes_resources( + mock_utils, self.csi_node_modified_watch_manifest, self.csi_node_modified_watch_munch) + self._assert_define_new_csi_node_not_called() + self._assert_handle_deleted_csi_node_pod_called(mock_utils, True, True, True) + + @patch('{}.utils'.format(test_settings.CSI_NODE_WATCHER_PATH)) + def test_undefine_managed_csi_node_with_modified_event_that_is_not_part_of_update(self, mock_utils): + self._prepare_watch_csi_nodes_resources( + mock_utils, self.csi_node_modified_watch_manifest, self.csi_node_modified_watch_munch) + self._prepare_handle_deleted_csi_node_pod(mock_utils, True, False, False, True, False) + test_utils.run_function_with_timeout(self.watcher.watch_csi_nodes_resources, 0.2) + self._assert_watch_csi_nodes_resources( + mock_utils, self.csi_node_modified_watch_manifest, self.csi_node_modified_watch_munch) + self._assert_define_new_csi_node_not_called() + self._assert_handle_deleted_csi_node_pod_called(mock_utils, True, False, False, True, False) + + @patch('{}.utils'.format(test_settings.CSI_NODE_WATCHER_PATH)) + def test_do_not_undefine_managed_csi_node_with_modified_event_when_node_has_forbid_deletion_label(self, mock_utils): + self._prepare_watch_csi_nodes_resources( + mock_utils, self.csi_node_modified_watch_manifest, self.csi_node_modified_watch_munch) + self._prepare_handle_deleted_csi_node_pod(mock_utils, True, False, False, True, True) + test_utils.run_function_with_timeout(self.watcher.watch_csi_nodes_resources, 0.2) + self._assert_watch_csi_nodes_resources( + mock_utils, self.csi_node_modified_watch_manifest, self.csi_node_modified_watch_munch) + self._assert_define_new_csi_node_not_called() + self._assert_handle_deleted_csi_node_pod_called(mock_utils, True, False, False, True, True) + + @patch('{}.utils'.format(test_settings.CSI_NODE_WATCHER_PATH)) + def test_do_not_undefine_managed_csi_node_with_modified_event_when_host_definer_cannot_delete_hosts( + self, mock_utils): + self._prepare_watch_csi_nodes_resources( + mock_utils, self.csi_node_modified_watch_manifest, self.csi_node_modified_watch_munch) + self._prepare_handle_deleted_csi_node_pod(mock_utils, True, False, False, False, False) + test_utils.run_function_with_timeout(self.watcher.watch_csi_nodes_resources, 0.2) + self._assert_watch_csi_nodes_resources( + mock_utils, self.csi_node_modified_watch_manifest, self.csi_node_modified_watch_munch) + self._assert_define_new_csi_node_not_called() + self._assert_handle_deleted_csi_node_pod_called(mock_utils, True, False, False, False, False) + + def _prepare_watch_csi_nodes_resources(self, mock_utils, csi_node_watch_manifest, csi_node_watch_munch): + self.global_managed_nodes[self.fake_csi_node_info.name] = self.fake_managed_nodes + self.watcher.k8s_api.get_csi_node_stream.return_value = iter([csi_node_watch_manifest]) + mock_utils.munch.return_value = csi_node_watch_munch + self.watcher.resource_info_manager.generate_csi_node_info.return_value = self.fake_csi_node_info + + def _prepare_handle_deleted_csi_node_pod( + self, mock_utils, is_node_has_manage_node_label, is_host_part_of_update=False, is_node_id_changed=False, + is_host_definer_can_delete_hosts=False, is_node_has_forbid_deletion_label=False): + self.watcher.node_manager.is_node_has_manage_node_label.return_value = is_node_has_manage_node_label + if is_node_has_manage_node_label: + self._prepare_undefine_host_when_node_pod_is_deleted( + mock_utils, is_host_part_of_update, is_node_id_changed, is_host_definer_can_delete_hosts, + is_node_has_forbid_deletion_label) + + def _prepare_undefine_host_when_node_pod_is_deleted( + self, mock_utils, is_host_part_of_update, is_node_id_changed, is_host_definer_can_delete_hosts, + is_node_has_forbid_deletion_label): + self.watcher.csi_node.is_host_part_of_update.return_value = is_host_part_of_update + if is_host_part_of_update: + self._prepare_create_definitions_when_csi_node_changed(is_node_id_changed) + else: + mock_utils.is_host_definer_can_delete_hosts.return_value = is_host_definer_can_delete_hosts + self.watcher.node_manager.is_node_has_forbid_deletion_label.return_value = is_node_has_forbid_deletion_label + + def _prepare_create_definitions_when_csi_node_changed(self, is_node_id_changed): + self.global_managed_secret.append(self.fake_secret_info) + self.watcher.host_definition_manager.get_matching_host_definition_info.return_value = \ + self.fake_host_definition_info + self.watcher.csi_node.is_node_id_changed.return_value = is_node_id_changed + if is_node_id_changed: + self.watcher.node_manager.generate_managed_node.return_value = self.managed_node_with_different_node_id + + def _assert_watch_csi_nodes_resources(self, mock_utils, csi_node_watch_manifest, csi_node_watch_munch): + mock_utils.loop_forever.assert_called() + self.watcher.k8s_api.get_csi_node_stream.assert_called_with() + mock_utils.munch.assert_called_once_with(csi_node_watch_manifest) + self.watcher.resource_info_manager.generate_csi_node_info.assert_called_once_with( + csi_node_watch_munch.object) + + def _assert_define_new_csi_node_called(self, mock_utils): + self.watcher.node_manager.is_node_can_be_defined.assert_called_once_with(self.fake_csi_node_info.name) + self.watcher.node_manager.add_node_to_nodes.assert_called_once_with(self.fake_csi_node_info) + self.watcher.definition_manager.define_node_on_all_storages.assert_called_once_with( + self.fake_csi_node_info.name) + self._assert_handle_deleted_csi_node_pod_not_called(mock_utils) + + def _assert_define_new_csi_node_not_called(self): + self.watcher.node_manager.is_node_can_be_defined.assert_called_once_with(self.fake_csi_node_info.name) + self.watcher.node_manager.add_node_to_nodes.assert_not_called() + self.watcher.definition_manager.define_node_on_all_storages.assert_not_called() + + def _assert_handle_deleted_csi_node_pod_called( + self, mock_utils, is_node_has_manage_node_label, is_host_part_of_update=False, is_node_id_changed=False, + is_host_definer_can_delete_hosts=False, is_node_has_forbid_deletion_label=False): + self.watcher.node_manager.is_node_has_manage_node_label.assert_called_once_with(self.fake_csi_node_info.name) + if is_node_has_manage_node_label: + self.watcher.csi_node.is_host_part_of_update.assert_called_once_with(self.fake_csi_node_info.name) + self._assert_undefine_host_when_node_pod_is_deleted_called( + mock_utils, is_host_part_of_update, is_node_id_changed, is_host_definer_can_delete_hosts, + is_node_has_forbid_deletion_label) + else: + self._assert_undefine_host_when_node_pod_is_deleted_not_called(mock_utils) + + def _assert_undefine_host_when_node_pod_is_deleted_called( + self, mock_utils, is_host_part_of_update, is_node_id_changed, is_host_definer_can_delete_hosts, + is_node_has_forbid_deletion_label): + self.watcher.csi_node.is_host_part_of_update.assert_called_once_with(self.fake_csi_node_info.name) + if is_host_part_of_update: + self._assert_create_definitions_when_csi_node_changed_called(is_node_id_changed) + mock_utils.is_host_definer_can_delete_hosts.assert_not_called() + self.watcher.node_manager.is_node_has_forbid_deletion_label.assert_not_called() + self._assert_undefine_all_the_definitions_of_a_node_not_called() + else: + self._assert_create_definitions_when_csi_node_changed_not_called() + mock_utils.is_host_definer_can_delete_hosts.assert_called_once_with() + if is_host_definer_can_delete_hosts: + self.watcher.node_manager.is_node_has_forbid_deletion_label.assert_called_once_with( + self.fake_csi_node_info.name) + if is_host_definer_can_delete_hosts and not is_node_has_forbid_deletion_label: + self._assert_undefine_all_the_definitions_of_a_node_called() + else: + self._assert_undefine_all_the_definitions_of_a_node_not_called() + self.assertEqual(self.global_managed_nodes, {}) + + def _assert_create_definitions_when_csi_node_changed_called(self, is_node_id_changed): + self.watcher.host_definition_manager.get_matching_host_definition_info.assert_called_once_with( + self.fake_csi_node_info.name, self.fake_secret_info.name, self.fake_secret_info.namespace) + self.watcher.csi_node.is_node_id_changed.assert_called_once_with(self.fake_host_definition_info.node_id, + self.fake_csi_node_info.node_id) + if is_node_id_changed: + self.watcher.node_manager.generate_managed_node.assert_called_once_with(self.fake_csi_node_info) + self.watcher.definition_manager.create_definition.assert_called_once_with(self.fake_host_definition_info) + self.assertEqual(self.global_managed_nodes[self.fake_csi_node_info.name], + self.managed_node_with_different_node_id) + else: + self.watcher.node_manager.generate_managed_node.assert_not_called() + self.watcher.definition_manager.create_definition.assert_not_called() + + def _assert_undefine_all_the_definitions_of_a_node_called(self): + self.watcher.definition_manager.undefine_node_definitions.assert_called_once_with(self.fake_csi_node_info.name) + self.watcher.node_manager.remove_manage_node_label.assert_called_once_with(self.fake_csi_node_info.name) + self.assertEqual(self.global_managed_nodes, {}) + + def _assert_handle_deleted_csi_node_pod_not_called(self, mock_utils): + self.watcher.node_manager.is_node_has_manage_node_label.assert_not_called() + self._assert_undefine_host_when_node_pod_is_deleted_not_called(mock_utils) + + def _assert_undefine_host_when_node_pod_is_deleted_not_called(self, mock_utils): + self.watcher.csi_node.is_host_part_of_update.assert_not_called() + mock_utils.is_host_definer_can_delete_hosts.assert_not_called() + self.watcher.node_manager.is_node_has_forbid_deletion_label.assert_not_called() + self._assert_create_definitions_when_csi_node_changed_not_called() + self._assert_undefine_all_the_definitions_of_a_node_not_called() + + def _assert_create_definitions_when_csi_node_changed_not_called(self): + self.watcher.host_definition_manager.get_matching_host_definition_info.assert_not_called() + self.watcher.csi_node.is_node_id_changed.assert_not_called() + self.watcher.node_manager.generate_managed_node.assert_not_called() + self.watcher.definition_manager.create_definition.assert_not_called() + + def _assert_undefine_all_the_definitions_of_a_node_not_called(self): + self.watcher.definition_manager.undefine_node_definitions.assert_not_called() + self.watcher.node_manager.remove_manage_node_label.assert_not_called() diff --git a/controllers/tests/controller_server/host_definer/watchers/host_definition_watcher_test.py b/controllers/tests/controller_server/host_definer/watchers/host_definition_watcher_test.py new file mode 100644 index 000000000..880be2dd3 --- /dev/null +++ b/controllers/tests/controller_server/host_definer/watchers/host_definition_watcher_test.py @@ -0,0 +1,200 @@ +from copy import deepcopy +from unittest.mock import patch, MagicMock + +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils +import controllers.tests.controller_server.host_definer.settings as test_settings +from controllers.tests.controller_server.host_definer.watchers.watcher_base import WatcherBaseSetUp +from controllers.servers.host_definer.watcher.host_definition_watcher import HostDefinitionWatcher + + +class TestWatchHostDefinitionsResources(WatcherBaseSetUp): + def setUp(self): + super().setUp() + self.watcher = HostDefinitionWatcher() + self.watcher.k8s_api = MagicMock() + self.watcher.resource_info_manager = MagicMock() + self.watcher.host_definition_manager = MagicMock() + self.watcher.definition_manager = MagicMock() + self.watcher.node_manager = MagicMock() + test_utils.patch_pending_variables() + self.fake_define_response = test_utils.get_fake_define_host_response() + self.fake_define_response.error_message = '' + self.fake_action = test_settings.DEFINE_ACTION + self.fake_host_definition_info = test_utils.get_fake_host_definition_info() + self.fake_pending_deletion_host_definition_info = deepcopy(self.fake_host_definition_info) + self.fake_pending_deletion_host_definition_info.phase = test_settings.PENDING_DELETION_PHASE + self.fake_pending_creation_host_definition_info = deepcopy(self.fake_host_definition_info) + self.fake_pending_creation_host_definition_info.phase = test_settings.PENDING_CREATION_PHASE + self.host_definition_deleted_watch_manifest = test_utils.get_fake_host_definition_watch_event( + test_settings.DELETED_EVENT_TYPE) + self.host_definition_deleted_watch_munch = test_utils.convert_manifest_to_munch( + self.host_definition_deleted_watch_manifest) + + @patch('{}.utils'.format(test_settings.HOST_DEFINITION_WATCHER_PATH)) + def test_define_pending_host_definition(self, mock_utils): + host_definition_info = self.fake_pending_creation_host_definition_info + self._prepare_watch_host_definition_resources(mock_utils, host_definition_info, True, False) + self._prepare_define_host_using_exponential_backoff(mock_utils, host_definition_info, [False, True]) + test_utils.run_function_with_timeout(self.watcher.watch_host_definitions_resources, 0.5) + self._assert_watch_host_definition_resources(mock_utils, host_definition_info, True) + self._assert_define_host_using_exponential_backoff_called(mock_utils, host_definition_info, 2, False) + + @patch('{}.utils'.format(test_settings.HOST_DEFINITION_WATCHER_PATH)) + def test_undefine_pending_host_definition(self, mock_utils): + host_definition_info = self.fake_pending_deletion_host_definition_info + self._prepare_watch_host_definition_resources(mock_utils, host_definition_info, True, False) + self._prepare_define_host_using_exponential_backoff(mock_utils, host_definition_info, [False, True], True) + test_utils.run_function_with_timeout(self.watcher.watch_host_definitions_resources, 0.5) + self._assert_watch_host_definition_resources(mock_utils, host_definition_info, True) + self._assert_define_host_using_exponential_backoff_called(mock_utils, host_definition_info, 2, False, True) + + @patch('{}.utils'.format(test_settings.HOST_DEFINITION_WATCHER_PATH)) + def test_do_not_handle_pending_deletion_host_definition_that_cannot_be_defined(self, mock_utils): + host_definition_info = self.fake_pending_deletion_host_definition_info + self._prepare_watch_host_definition_resources(mock_utils, host_definition_info, True, False) + self._prepare_define_host_using_exponential_backoff(mock_utils, host_definition_info, [False, True], False) + test_utils.run_function_with_timeout(self.watcher.watch_host_definitions_resources, 0.5) + self._assert_watch_host_definition_resources(mock_utils, host_definition_info, True) + self._assert_define_host_using_exponential_backoff_called(mock_utils, host_definition_info, 2, False, False) + + @patch('{}.utils'.format(test_settings.HOST_DEFINITION_WATCHER_PATH)) + def test_pending_host_definition_phase_to_error(self, mock_utils): + host_definition_info = self.fake_pending_deletion_host_definition_info + self._prepare_watch_host_definition_resources(mock_utils, host_definition_info, True, False) + self._prepare_define_host_using_exponential_backoff(mock_utils, host_definition_info, [False, False, False]) + test_utils.run_function_with_timeout(self.watcher.watch_host_definitions_resources, 0.5) + self._assert_watch_host_definition_resources(mock_utils, host_definition_info, True) + self._assert_define_host_using_exponential_backoff_called(mock_utils, host_definition_info, 3, True) + + @patch('{}.utils'.format(test_settings.HOST_DEFINITION_WATCHER_PATH)) + def test_create_event_when_failing_to_undefine_pending_deletion_host_definition(self, mock_utils): + host_definition_info = self.fake_pending_deletion_host_definition_info + self.fake_define_response.error_message = test_settings.MESSAGE + self._prepare_watch_host_definition_resources(mock_utils, host_definition_info, True, False) + self._prepare_define_host_using_exponential_backoff(mock_utils, host_definition_info, [False, True], True) + test_utils.run_function_with_timeout(self.watcher.watch_host_definitions_resources, 0.5) + self._assert_watch_host_definition_resources(mock_utils, host_definition_info, True) + self._assert_define_host_using_exponential_backoff_called( + mock_utils, host_definition_info, 2, False, True, True) + + @patch('{}.utils'.format(test_settings.HOST_DEFINITION_WATCHER_PATH)) + def test_do_not_handle_not_pending_host_definition(self, mock_utils): + host_definition_info = self.fake_host_definition_info + self._prepare_watch_host_definition_resources(mock_utils, host_definition_info, False) + test_utils.run_function_with_timeout(self.watcher.watch_host_definitions_resources, 0.5) + self._assert_watch_host_definition_resources(mock_utils, host_definition_info, False) + self._assert_define_host_using_exponential_backoff_not_called(mock_utils) + + @patch('{}.utils'.format(test_settings.HOST_DEFINITION_WATCHER_PATH)) + def test_do_not_handle_pending_host_definition_when_event_type_is_deletion(self, mock_utils): + host_definition_info = self.fake_host_definition_info + self._prepare_watch_host_definition_resources(mock_utils, host_definition_info, True, True) + test_utils.run_function_with_timeout(self.watcher.watch_host_definitions_resources, 0.5) + self._assert_watch_host_definition_resources(mock_utils, host_definition_info, True) + self._assert_define_host_using_exponential_backoff_not_called(mock_utils) + + def _prepare_watch_host_definition_resources( + self, mock_utils, host_definition_info, is_host_definition_in_pending_phase, + is_watch_object_type_is_delete=False): + self.watcher.k8s_api.list_host_definition.return_value = [host_definition_info] + mock_utils.get_k8s_object_resource_version.return_value = test_settings.FAKE_RESOURCE_VERSION + self.watcher.k8s_api.get_host_definition_stream.return_value = iter( + [self.host_definition_deleted_watch_manifest]) + mock_utils.munch.return_value = self.host_definition_deleted_watch_munch + self.watcher.resource_info_manager.generate_host_definition_info.return_value = host_definition_info + self.watcher.host_definition_manager.is_host_definition_in_pending_phase.return_value = \ + is_host_definition_in_pending_phase + mock_utils.is_watch_object_type_is_delete.return_value = is_watch_object_type_is_delete + + def _prepare_define_host_using_exponential_backoff( + self, mock_utils, host_definition_info, is_host_definition_not_pending, is_node_can_be_undefined=False): + self.watcher.host_definition_manager.is_host_definition_not_pending.side_effect = \ + is_host_definition_not_pending + self._prepare_handle_pending_host_definition(mock_utils, host_definition_info, is_node_can_be_undefined) + + def _prepare_handle_pending_host_definition(self, mock_utils, host_definition_info, is_node_can_be_undefined): + mock_utils.get_action.return_value = self.fake_action + if host_definition_info.phase == test_settings.PENDING_CREATION_PHASE: + self.watcher.definition_manager.define_host_after_pending.return_value = self.fake_define_response + else: + self.watcher.node_manager.is_node_can_be_undefined.return_value = is_node_can_be_undefined + if is_node_can_be_undefined: + self.watcher.definition_manager.undefine_host_after_pending.return_value = self.fake_define_response + + def _assert_watch_host_definition_resources( + self, mock_utils, host_definition_info, is_host_definition_in_pending_phase): + self.watcher.k8s_api.list_host_definition.assert_called_with() + mock_utils.get_k8s_object_resource_version.assert_called_with([host_definition_info]) + mock_utils.loop_forever.assert_called_with() + self.watcher.k8s_api.get_host_definition_stream.assert_called_with(test_settings.FAKE_RESOURCE_VERSION, 5) + mock_utils.munch.assert_called_once_with(self.host_definition_deleted_watch_manifest) + self.watcher.resource_info_manager.generate_host_definition_info.assert_called_once_with( + self.host_definition_deleted_watch_munch.object) + self.watcher.host_definition_manager.is_host_definition_in_pending_phase.assert_called_once_with( + host_definition_info.phase) + if is_host_definition_in_pending_phase: + mock_utils.is_watch_object_type_is_delete.assert_called_once_with( + self.host_definition_deleted_watch_munch.type) + + def _assert_define_host_using_exponential_backoff_called( + self, mock_utils, host_definition_info, host_definition_not_pending_call_count, set_phase_to_error=False, + is_node_can_be_undefined=False, is_error_message=False): + self.watcher.host_definition_manager.is_host_definition_not_pending.assert_called_with( + host_definition_info) + self.assertEqual(self.watcher.host_definition_manager.is_host_definition_not_pending.call_count, + host_definition_not_pending_call_count) + if set_phase_to_error: + self.watcher.host_definition_manager.set_host_definition_phase_to_error.assert_called_once_with( + host_definition_info) + else: + self._assert_called_handle_pending_host_definition( + mock_utils, host_definition_info, is_node_can_be_undefined, is_error_message) + + def _assert_called_handle_pending_host_definition( + self, mock_utils, host_definition_info, is_node_can_be_undefined, is_error_message): + mock_utils.get_action.assert_called_once_with(host_definition_info.phase) + if host_definition_info.phase == test_settings.PENDING_CREATION_PHASE: + self.watcher.definition_manager.define_host_after_pending.assert_called_once_with(host_definition_info) + self.watcher.node_manager.is_node_can_be_undefined.assert_not_called() + self.watcher.definition_manager.undefine_host_after_pending.assert_not_called() + else: + if is_error_message: + self.watcher.node_manager.is_node_can_be_undefined.assert_called_once_with( + host_definition_info.node_name) + else: + self.watcher.node_manager.is_node_can_be_undefined.assert_called_with(host_definition_info.node_name) + self.assertEqual(self.watcher.node_manager.is_node_can_be_undefined.call_count, 2) + + self.watcher.definition_manager.define_host_after_pending.assert_not_called() + if is_node_can_be_undefined: + self.watcher.definition_manager.undefine_host_after_pending.assert_called_once_with( + host_definition_info) + else: + self.watcher.definition_manager.undefine_host_after_pending.assert_not_called() + + self._assert_handle_message_from_storage(host_definition_info, is_node_can_be_undefined, is_error_message) + + def _assert_handle_message_from_storage(self, host_definition_info, is_node_can_be_undefined, is_error_message): + if is_error_message: + self.watcher.host_definition_manager.create_k8s_event_for_host_definition.assert_called_once_with( + host_definition_info, self.fake_define_response.error_message, self.fake_action, + test_settings.FAILED_MESSAGE_TYPE) + elif host_definition_info.phase == test_settings.PENDING_CREATION_PHASE: + self.watcher.host_definition_manager.set_host_definition_status_to_ready.assert_called_once_with( + host_definition_info) + elif is_node_can_be_undefined: + self.watcher.host_definition_manager.delete_host_definition.assert_called_once_with( + host_definition_info.name) + self.watcher.node_manager.remove_manage_node_label.assert_called_once_with(host_definition_info.name) + + def _assert_define_host_using_exponential_backoff_not_called(self, mock_utils): + self.watcher.host_definition_manager.is_host_definition_not_pending.assert_not_called() + self.watcher.host_definition_manager.set_host_definition_phase_to_error.assert_not_called() + self.watcher.host_definition_manager.create_k8s_event_for_host_definition.assert_not_called() + self.watcher.host_definition_manager.set_host_definition_status_to_ready.assert_not_called() + self.watcher.host_definition_manager.delete_host_definition.assert_not_called() + mock_utils.get_action.assert_not_called() + self.watcher.definition_manager.define_host_after_pending.assert_not_called() + self.watcher.definition_manager.undefine_host_after_pending.assert_not_called() + self.watcher.node_manager.remove_manage_node_label.assert_not_called() + self.watcher.node_manager.is_node_can_be_undefined.assert_not_called() diff --git a/controllers/tests/controller_server/host_definer/watchers/node_watcher_test.py b/controllers/tests/controller_server/host_definer/watchers/node_watcher_test.py new file mode 100644 index 000000000..8374e55d4 --- /dev/null +++ b/controllers/tests/controller_server/host_definer/watchers/node_watcher_test.py @@ -0,0 +1,212 @@ +from copy import deepcopy +from unittest.mock import patch, MagicMock + +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils +import controllers.tests.controller_server.host_definer.settings as test_settings +from controllers.tests.controller_server.host_definer.watchers.watcher_base import WatcherBaseSetUp +from controllers.servers.host_definer.watcher.node_watcher import NodeWatcher + + +class NodeWatcherBase(WatcherBaseSetUp): + def setUp(self): + super().setUp() + self.watcher = NodeWatcher() + self.watcher.k8s_api = MagicMock() + self.watcher.resource_info_manager = MagicMock() + self.watcher.node_manager = MagicMock() + self.watcher.host_definition_manager = MagicMock() + self.watcher.definition_manager = MagicMock() + self.fake_node_info = test_utils.get_fake_node_info() + self.fake_csi_node_info = test_utils.get_fake_csi_node_info() + self.fake_host_definition_info = test_utils.get_fake_host_definition_info() + self.unmanaged_csi_nodes_with_driver = patch( + '{}.unmanaged_csi_nodes_with_driver'.format(test_settings.NODES_WATCHER_PATH), set()).start() + self.expected_unmanaged_csi_nodes_with_driver = set() + + def _prepare_is_unmanaged_csi_node_has_driver(self, is_node_can_be_defined): + self.watcher.node_manager.is_node_can_be_defined.return_value = is_node_can_be_defined + + +class TestAddInitialNodes(NodeWatcherBase): + def test_add_initial_unmanaged_node_with_ibm_block_csi_driver(self): + self.expected_unmanaged_csi_nodes_with_driver.add(test_settings.FAKE_NODE_NAME) + self._prepare_add_initial_nodes(self.fake_csi_node_info) + self._prepare_is_unmanaged_csi_node_has_driver(False) + self.watcher.add_initial_nodes() + self._assert_add_initial_nodes() + self._assert_delete_host_definitions_not_called() + self.watcher.node_manager.is_node_can_be_defined.assert_called_once_with(self.fake_csi_node_info.name) + + def test_do_not_add_initial_unmanaged_node_with_ibm_block_csi_driver_because_it_can_be_defined(self): + self._prepare_add_initial_nodes(self.fake_csi_node_info) + self._prepare_is_unmanaged_csi_node_has_driver(True) + self.watcher.add_initial_nodes() + self._assert_add_initial_nodes() + self._assert_delete_host_definitions_not_called() + self.watcher.node_manager.is_node_can_be_defined.assert_called_once_with(self.fake_csi_node_info.name) + + def test_undefined_initial_managed_node_that_do_not_have_ibm_block_csi_driver_anymore(self): + csi_node_info = deepcopy(self.fake_csi_node_info) + csi_node_info.node_id = '' + self._prepare_add_initial_nodes(csi_node_info) + self._prepare_csi_node_pod_deleted_while_host_definer_was_down(True, True) + self._prepare_delete_host_definitions_called(True) + self.watcher.add_initial_nodes() + self._assert_add_initial_nodes() + self._assert_delete_host_definitions_called(True) + self.watcher.node_manager.is_node_has_manage_node_label.assert_called_once_with(csi_node_info.name) + self.watcher.node_manager.is_node_has_host_definitions.assert_called_once_with(csi_node_info.name) + self.watcher.node_manager.is_node_can_be_defined.assert_not_called() + + def _prepare_delete_host_definitions_called(self, is_node_can_be_undefined): + self.watcher.node_manager.is_node_can_be_undefined.return_value = is_node_can_be_undefined + if is_node_can_be_undefined: + self.watcher.host_definition_manager.get_all_host_definitions_info_of_the_node.return_value = \ + [self.fake_host_definition_info] + + def _assert_delete_host_definitions_called(self, is_node_can_be_undefined): + self.watcher.node_manager.is_node_can_be_undefined.assert_called_once_with(self.fake_node_info.name) + if is_node_can_be_undefined: + self.watcher.host_definition_manager.get_all_host_definitions_info_of_the_node.assert_called_once_with( + self.fake_node_info.name) + self.watcher.definition_manager.delete_definition.assert_called_once_with(self.fake_host_definition_info) + self.assertEqual(self.watcher.node_manager.remove_manage_node_label.call_count, 2) + else: + self.watcher.host_definition_manager.get_all_host_definitions_info_of_the_node.assert_not_called() + self.watcher.definition_manager.delete_definition.assert_not_called() + self.watcher.node_manager.remove_manage_node_label.assert_called_once_with(self.fake_node_info.name) + + def test_do_not_undefined_initial_that_do_not_have_ibm_block_csi_driver_because_it_is_not_managed(self): + csi_node_info = deepcopy(self.fake_csi_node_info) + csi_node_info.node_id = '' + self._prepare_add_initial_nodes(csi_node_info) + self._prepare_csi_node_pod_deleted_while_host_definer_was_down(False, True) + self.watcher.add_initial_nodes() + self._assert_add_initial_nodes() + self._assert_delete_host_definitions_not_called() + self.watcher.node_manager.is_node_has_manage_node_label.assert_called_once_with(csi_node_info.name) + self.watcher.node_manager.is_node_has_host_definitions.assert_not_called() + self.watcher.node_manager.is_node_can_be_defined.assert_not_called() + + def test_do_not_undefined_initial_that_do_not_have_ibm_block_csi_driver_because_it_is_not_have_host_definitions( + self): + csi_node_info = deepcopy(self.fake_csi_node_info) + csi_node_info.node_id = '' + self._prepare_add_initial_nodes(csi_node_info) + self._prepare_csi_node_pod_deleted_while_host_definer_was_down(True, False) + self.watcher.add_initial_nodes() + self._assert_add_initial_nodes() + self._assert_delete_host_definitions_not_called() + self.watcher.node_manager.is_node_has_manage_node_label.assert_called_once_with(csi_node_info.name) + self.watcher.node_manager.is_node_has_host_definitions.assert_called_once_with(csi_node_info.name) + self.watcher.node_manager.is_node_can_be_defined.assert_not_called() + + def _prepare_add_initial_nodes(self, csi_node_info): + self.watcher.node_manager.get_nodes_info.return_value = [self.fake_node_info] + self.watcher.resource_info_manager.get_csi_node_info.return_value = csi_node_info + + def _prepare_csi_node_pod_deleted_while_host_definer_was_down( + self, is_node_has_manage_node_label, is_node_has_host_definitions): + self.watcher.node_manager.is_node_has_manage_node_label.return_value = is_node_has_manage_node_label + self.watcher.node_manager.is_node_has_host_definitions.return_value = is_node_has_host_definitions + + def _assert_add_initial_nodes(self): + self.watcher.node_manager.get_nodes_info.assert_called_once() + self.watcher.resource_info_manager.get_csi_node_info.assert_called_once_with(self.fake_node_info.name) + self.assertEqual(self.unmanaged_csi_nodes_with_driver, self.expected_unmanaged_csi_nodes_with_driver) + + def _assert_delete_host_definitions_not_called(self): + self.watcher.node_manager.is_node_can_be_undefined.assert_not_called() + self.watcher.host_definition_manager.get_all_host_definitions_info_of_the_node.assert_not_called() + self.watcher.definition_manager.delete_definition.assert_not_called() + self.watcher.node_manager.remove_manage_node_label.assert_not_called() + + +class TestWatchNodesResources(NodeWatcherBase): + def setUp(self): + super().setUp() + self.node_modified_watch_manifest = test_utils.get_fake_node_watch_event(test_settings.MODIFIED_EVENT_TYPE) + self.node_modified_watch_munch = test_utils.convert_manifest_to_munch(self.node_modified_watch_manifest) + self.node_added_watch_manifest = test_utils.get_fake_node_watch_event(test_settings.ADDED_EVENT) + self.node_added_watch_munch = test_utils.convert_manifest_to_munch(self.node_added_watch_manifest) + + @patch('{}.utils'.format(test_settings.NODES_WATCHER_PATH)) + def test_watch_and_add_unmanaged_node_with_ibm_block_csi_driver_but_do_not_define_it(self, mock_utils): + self.expected_unmanaged_csi_nodes_with_driver.add(test_settings.FAKE_NODE_NAME) + self._prepare_watch_nodes_resources(self.node_modified_watch_manifest, + self.node_modified_watch_munch, mock_utils) + self._prepare_is_unmanaged_csi_node_has_driver(False) + self.watcher.node_manager.is_node_has_new_manage_node_label.return_value = False + self.watcher.watch_nodes_resources() + self._assert_watch_nodes_resources(self.node_modified_watch_manifest, + self.node_modified_watch_munch, mock_utils) + self._assert_define_node_not_called() + self.watcher.node_manager.is_node_has_new_manage_node_label.assert_called_once_with( + self.fake_csi_node_info, self.unmanaged_csi_nodes_with_driver) + + @patch('{}.utils'.format(test_settings.NODES_WATCHER_PATH)) + def test_watch_and_add_unmanaged_node_with_ibm_block_csi_driver_and_define_it(self, mock_utils): + self._prepare_watch_nodes_resources(self.node_modified_watch_manifest, + self.node_modified_watch_munch, mock_utils) + self._prepare_is_unmanaged_csi_node_has_driver(False) + self.watcher.node_manager.is_node_has_new_manage_node_label.return_value = True + self.watcher.watch_nodes_resources() + self._assert_watch_nodes_resources(self.node_modified_watch_manifest, + self.node_modified_watch_munch, mock_utils) + self._assert_define_node_called() + self.watcher.node_manager.is_node_has_new_manage_node_label.assert_called_once_with( + self.fake_csi_node_info, self.unmanaged_csi_nodes_with_driver) + + @patch('{}.utils'.format(test_settings.NODES_WATCHER_PATH)) + def test_watch_and_do_not_add_unmanaged_node_with_ibm_block_csi_driver_and_define_it(self, mock_utils): + self.unmanaged_csi_nodes_with_driver.add(test_settings.FAKE_NODE_NAME) + self.expected_unmanaged_csi_nodes_with_driver = set() + self._prepare_watch_nodes_resources(self.node_modified_watch_manifest, + self.node_modified_watch_munch, mock_utils) + self._prepare_is_unmanaged_csi_node_has_driver(True) + self.watcher.node_manager.is_node_has_new_manage_node_label.return_value = True + self.watcher.watch_nodes_resources() + self._assert_watch_nodes_resources(self.node_modified_watch_manifest, + self.node_modified_watch_munch, mock_utils) + self._assert_define_node_called() + self.watcher.node_manager.is_node_has_new_manage_node_label.assert_called_once_with( + self.fake_csi_node_info, self.unmanaged_csi_nodes_with_driver) + + @patch('{}.utils'.format(test_settings.NODES_WATCHER_PATH)) + def test_watch_and_do_not_add_unmanaged_node_and_do_not_define_it_when_the_event_type_is_not_modified( + self, mock_utils): + self.expected_unmanaged_csi_nodes_with_driver = set() + self._prepare_watch_nodes_resources(self.node_added_watch_manifest, self.node_added_watch_munch, mock_utils) + self._prepare_is_unmanaged_csi_node_has_driver(True) + self.watcher.watch_nodes_resources() + self._assert_watch_nodes_resources(self.node_added_watch_manifest, self.node_added_watch_munch, mock_utils) + self._assert_define_node_not_called() + self.watcher.node_manager.is_node_has_new_manage_node_label.assert_not_called() + self.watcher.node_manager.is_node_can_be_defined.assert_not_called() + + def _prepare_watch_nodes_resources(self, node_watch_manifest, node_watch_munch, mock_utils): + mock_utils.loop_forever.side_effect = [True, False] + self.watcher.k8s_api.get_node_stream.return_value = iter([node_watch_manifest]) + mock_utils.munch.return_value = node_watch_munch + self.watcher.resource_info_manager.get_csi_node_info.return_value = self.fake_csi_node_info + self.watcher.resource_info_manager.generate_node_info.return_value = self.fake_node_info + + def _assert_watch_nodes_resources(self, node_watch_manifest, node_watch_munch, mock_utils): + self.watcher.k8s_api.get_node_stream.assert_called_once_with() + mock_utils.munch.assert_called_once_with(node_watch_manifest) + self.watcher.resource_info_manager.get_csi_node_info.assert_called_once_with(test_settings.FAKE_NODE_NAME) + self.watcher.resource_info_manager.generate_node_info.assert_called_once_with( + node_watch_munch.object) + self.watcher.node_manager.handle_node_topologies.assert_called_once_with( + self.fake_node_info, node_watch_munch.type) + self.watcher.node_manager.update_node_io_group.assert_called_once_with(self.fake_node_info) + self.assertEqual(self.unmanaged_csi_nodes_with_driver, self.expected_unmanaged_csi_nodes_with_driver) + + def _assert_define_node_not_called(self): + self.watcher.node_manager.add_node_to_nodes.assert_not_called() + self.watcher.definition_manager.define_node_on_all_storages.assert_not_called() + + def _assert_define_node_called(self): + self.watcher.node_manager.add_node_to_nodes.assert_called_once_with(self.fake_csi_node_info) + self.watcher.definition_manager.define_node_on_all_storages.assert_called_once_with( + test_settings.FAKE_NODE_NAME) diff --git a/controllers/tests/controller_server/host_definer/watchers/secret_watcher_test.py b/controllers/tests/controller_server/host_definer/watchers/secret_watcher_test.py new file mode 100644 index 000000000..807b1c5ab --- /dev/null +++ b/controllers/tests/controller_server/host_definer/watchers/secret_watcher_test.py @@ -0,0 +1,134 @@ +from copy import deepcopy +from unittest.mock import patch, MagicMock + +from controllers.servers.host_definer.watcher.secret_watcher import SecretWatcher +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils +import controllers.tests.controller_server.host_definer.settings as test_settings +from controllers.tests.controller_server.host_definer.watchers.watcher_base import WatcherBaseSetUp + + +class TestWatchSecretResources(WatcherBaseSetUp): + def setUp(self): + super().setUp() + self.watcher = SecretWatcher() + self.watcher.k8s_api = MagicMock() + self.watcher.secret_manager = MagicMock() + self.watcher.host_definition_manager = MagicMock() + self.watcher.definition_manager = MagicMock() + self.watcher.node_manager = MagicMock() + self.watcher.resource_info_manager = MagicMock() + self.fake_secret_info = test_utils.get_fake_secret_info() + self.fake_secret_data = test_utils.get_fake_k8s_secret().data + self.fake_node_info = test_utils.get_fake_node_info() + self.fake_host_definition_info = test_utils.get_fake_host_definition_info() + self.global_managed_secrets = test_utils.patch_managed_secrets_global_variable( + test_settings.SECRET_WATCHER_PATH) + self.secret_modified_watch_manifest = test_utils.get_fake_secret_watch_event(test_settings.MODIFIED_EVENT_TYPE) + self.secret_modified_watch_munch = test_utils.convert_manifest_to_munch(self.secret_modified_watch_manifest) + self.fake_nodes_with_system_id = {self.fake_node_info.name: test_settings.FAKE_SYSTEM_ID} + + @patch('{}.utils'.format(test_settings.SECRET_WATCHER_PATH)) + def test_watch_and_define_changed_topology_secret_topology(self, mock_utils): + self._prepare_watch_secret_resource(True, mock_utils) + self._prepare_get_secret_info(True, mock_utils) + self._prepare_handle_storage_class_secret(2) + self._test_watch_secret_resources(2, mock_utils) + self._assert_get_secret_info_called(True, mock_utils) + self._assert_handle_storage_class_secret_called() + self.watcher.host_definition_manager.get_host_definition_info_from_secret.assert_called_once_with( + self.fake_secret_info) + self.watcher.definition_manager.define_nodes.assert_called_once_with(self.fake_host_definition_info) + self.assertEqual(self.global_managed_secrets[0], self.fake_secret_info) + + @patch('{}.utils'.format(test_settings.SECRET_WATCHER_PATH)) + def test_watch_and_define_changed_non_topology_secret_topology(self, mock_utils): + self._prepare_watch_secret_resource(True, mock_utils) + self._prepare_get_secret_info(False, mock_utils) + self._prepare_handle_storage_class_secret(2) + self._test_watch_secret_resources(2, mock_utils) + self._assert_get_secret_info_called(False, mock_utils) + self._assert_handle_storage_class_secret_called() + self.watcher.host_definition_manager.get_host_definition_info_from_secret.assert_called_once_with( + self.fake_secret_info) + self.watcher.definition_manager.define_nodes.assert_called_once_with(self.fake_host_definition_info) + self.assertEqual(self.global_managed_secrets[0], self.fake_secret_info) + + @patch('{}.utils'.format(test_settings.SECRET_WATCHER_PATH)) + def test_watch_and_do_not_define_changed_secret_that_is_not_used_by_storage_class(self, mock_utils): + self._prepare_watch_secret_resource(True, mock_utils) + self._prepare_get_secret_info(True, mock_utils) + self._prepare_handle_storage_class_secret(0) + self._test_watch_secret_resources(2, mock_utils) + self._assert_get_secret_info_called(True, mock_utils) + self._assert_handle_storage_class_secret_called() + self.watcher.host_definition_manager.get_host_definition_info_from_secret.assert_not_called() + self.watcher.definition_manager.define_nodes.assert_not_called() + + @patch('{}.utils'.format(test_settings.SECRET_WATCHER_PATH)) + def test_watch_and_do_not_define_unchanged_secret(self, mock_utils): + self._prepare_watch_secret_resource(False, mock_utils) + self._test_watch_secret_resources(1, mock_utils) + self._assert_get_secret_info_not_called(mock_utils) + self._assert_handle_storage_class_secret_not_called() + self.watcher.resource_info_manager.generate_k8s_secret_to_secret_info.assert_called_once_with( + self.secret_modified_watch_munch.object) + + def _prepare_watch_secret_resource(self, is_secret_can_be_changed, mock_utils): + mock_utils.loop_forever.side_effect = [True, False] + self.watcher.k8s_api.get_secret_stream.return_value = iter([self.secret_modified_watch_manifest]) + mock_utils.munch.return_value = self.secret_modified_watch_munch + self.watcher.resource_info_manager.generate_k8s_secret_to_secret_info.return_value = self.fake_secret_info + self.watcher.secret_manager.is_secret_can_be_changed.return_value = is_secret_can_be_changed + + def _prepare_get_secret_info(self, is_topology_secret, mock_utils): + mock_utils.change_decode_base64_secret_config.return_value = self.fake_secret_data + self.watcher.secret_manager.is_topology_secret.return_value = is_topology_secret + if is_topology_secret: + self.watcher.node_manager.generate_nodes_with_system_id.return_value = self.fake_nodes_with_system_id + self.watcher.secret_manager.generate_secret_system_ids_topologies.return_value = \ + test_settings.FAKE_SYSTEM_IDS_TOPOLOGIES + self.watcher.resource_info_manager.generate_k8s_secret_to_secret_info.return_value = self.fake_secret_info + + def _prepare_handle_storage_class_secret(self, managed_storage_classes): + secret_info_with_storage_classes = test_utils.get_fake_secret_info(managed_storage_classes) + copy_secret_info = deepcopy(secret_info_with_storage_classes) + self.global_managed_secrets.append(copy_secret_info) + self.watcher.secret_manager.get_matching_managed_secret_info.return_value = ( + secret_info_with_storage_classes, 0) + if managed_storage_classes > 0: + self.watcher.host_definition_manager.get_host_definition_info_from_secret.return_value = \ + self.fake_host_definition_info + + def _test_watch_secret_resources(self, generate_secret_info_call_count, mock_utils): + self.watcher.watch_secret_resources() + mock_utils.loop_forever.assert_called() + self.watcher.k8s_api.get_secret_stream.assert_called_once_with() + mock_utils.munch.assert_called_once_with(self.secret_modified_watch_manifest) + self.assertEqual( + self.watcher.resource_info_manager.generate_k8s_secret_to_secret_info.call_count, + generate_secret_info_call_count) + self.watcher.secret_manager.is_secret_can_be_changed.assert_called_once_with( + self.fake_secret_info, self.secret_modified_watch_munch.type) + + def _assert_get_secret_info_called(self, is_topology_secret, mock_utils): + mock_utils.change_decode_base64_secret_config.assert_called_once_with( + self.secret_modified_watch_munch.object.data) + self.watcher.secret_manager.is_topology_secret.assert_called_once_with(self.fake_secret_data) + if is_topology_secret: + self.watcher.node_manager.generate_nodes_with_system_id.assert_called_once_with(self.fake_secret_data) + self.watcher.secret_manager.generate_secret_system_ids_topologies.assert_called_once_with( + self.fake_secret_data) + + def _assert_get_secret_info_not_called(self, mock_utils): + mock_utils.change_decode_base64_secret_config.assert_not_called() + self.watcher.secret_manager.is_topology_secret.assert_not_called() + self.watcher.node_manager.generate_nodes_with_system_id.assert_not_called() + self.watcher.secret_manager.generate_secret_system_ids_topologies.assert_not_called() + + def _assert_handle_storage_class_secret_called(self): + self.watcher.secret_manager.get_matching_managed_secret_info.assert_called_once_with(self.fake_secret_info) + + def _assert_handle_storage_class_secret_not_called(self): + self.watcher.secret_manager.get_matching_managed_secret_info.assert_not_called() + self.watcher.host_definition_manager.get_host_definition_info_from_secret.assert_not_called() + self.watcher.definition_manager.define_nodes.assert_not_called() diff --git a/controllers/tests/controller_server/host_definer/watchers/storage_class_watcher_test.py b/controllers/tests/controller_server/host_definer/watchers/storage_class_watcher_test.py new file mode 100644 index 000000000..b77a52378 --- /dev/null +++ b/controllers/tests/controller_server/host_definer/watchers/storage_class_watcher_test.py @@ -0,0 +1,244 @@ +from copy import deepcopy +from unittest.mock import MagicMock, patch + +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils +import controllers.tests.controller_server.host_definer.settings as test_settings +from controllers.tests.controller_server.host_definer.watchers.watcher_base import WatcherBaseSetUp +from controllers.servers.host_definer.watcher.storage_class_watcher import StorageClassWatcher + + +class StorageClassWatcherBase(WatcherBaseSetUp): + def setUp(self): + super().setUp() + self.watcher = StorageClassWatcher() + self.watcher.k8s_api = MagicMock() + self.watcher.storage_class_manager = MagicMock() + self.watcher.secret_manager = MagicMock() + self.watcher.node_manager = MagicMock() + self.watcher.resource_info_manager = MagicMock() + self.watcher.definition_manager = MagicMock() + self.fake_storage_class_info = test_utils.get_fake_storage_class_info() + self.fake_storage_class_info.parameters = {test_settings.STORAGE_CLASS_SECRET_FIELD: test_settings.FAKE_SECRET} + self.fake_secret_info = test_utils.get_fake_secret_info() + self.fake_node_info = test_utils.get_fake_node_info() + self.fake_secret_data = test_utils.get_fake_k8s_secret().data + self.managed_secrets_on_storage_class_watcher = test_utils.patch_managed_secrets_global_variable( + test_settings.STORAGE_CLASS_WATCHER_PATH) + self.fake_nodes_with_system_id = {self.fake_node_info.name: test_settings.FAKE_SYSTEM_ID} + + def _prepare_get_secrets_info_from_storage_class_with_driver_provisioner( + self, is_sc_has_csi_provisioner, is_secret, is_topology_secret=False): + self.watcher.storage_class_manager.is_storage_class_has_csi_as_a_provisioner.return_value = \ + is_sc_has_csi_provisioner + if is_sc_has_csi_provisioner: + self._prepare_get_secrets_info_from_storage_class(is_secret, is_topology_secret) + + def _prepare_get_secrets_info_from_storage_class(self, is_secret, is_topology_secret): + self.watcher.secret_manager.is_secret.return_value = is_secret + if is_secret: + self.watcher.secret_manager.get_secret_name_and_namespace.return_value = ( + test_settings.FAKE_SECRET, test_settings.FAKE_SECRET_NAMESPACE) + self.watcher.secret_manager.get_secret_data.return_value = self.fake_secret_data + self._prepare_get_secret_info(is_topology_secret) + self.watcher.secret_manager.add_unique_secret_info_to_list.return_value = [self.fake_secret_info] + + def _prepare_get_secret_info(self, is_topology_secret): + self.watcher.secret_manager.is_topology_secret.return_value = is_topology_secret + self.watcher.resource_info_manager.generate_secret_info.return_value = self.fake_secret_info + if is_topology_secret: + self.watcher.node_manager.generate_nodes_with_system_id.return_value = self.fake_nodes_with_system_id + self.watcher.secret_manager.generate_secret_system_ids_topologies.return_value = \ + test_settings.FAKE_SYSTEM_IDS_TOPOLOGIES + + def _assert_called_get_secrets_info_from_storage_class_called(self, is_secret, is_topology_secret=False): + self.watcher.secret_manager.is_secret.assert_called_once_with(test_settings.STORAGE_CLASS_SECRET_FIELD) + if is_secret: + self.watcher.secret_manager.get_secret_name_and_namespace.assert_called_once_with( + self.fake_storage_class_info, test_settings.STORAGE_CLASS_SECRET_FIELD) + self.watcher.secret_manager.get_secret_data.assert_called_once_with( + test_settings.FAKE_SECRET, test_settings.FAKE_SECRET_NAMESPACE) + self.watcher.secret_manager.is_topology_secret.assert_called_once_with(self.fake_secret_data) + self.watcher.secret_manager.add_unique_secret_info_to_list.assert_called_once_with( + self.fake_secret_info, []) + self._assert_get_secret_info(is_topology_secret) + else: + self._assert_get_secret_info_from_parameter_not_called() + + def _assert_get_secret_info(self, is_topology_secret): + if is_topology_secret: + self.watcher.node_manager.generate_nodes_with_system_id.assert_called_once_with(self.fake_secret_data) + self.watcher.secret_manager.generate_secret_system_ids_topologies.assert_called_once_with( + self.fake_secret_data) + self.watcher.resource_info_manager.generate_secret_info.assert_called_once_with( + test_settings.FAKE_SECRET, test_settings.FAKE_SECRET_NAMESPACE, self.fake_nodes_with_system_id, + test_settings.FAKE_SYSTEM_IDS_TOPOLOGIES) + else: + self.watcher.node_manager.generate_nodes_with_system_id.assert_not_called() + self.watcher.secret_manager.generate_secret_system_ids_topologies.assert_not_called() + self.watcher.resource_info_manager.generate_secret_info.assert_called_once_with( + test_settings.FAKE_SECRET, test_settings.FAKE_SECRET_NAMESPACE) + + def _assert_called_get_secrets_info_from_storage_class_not_called(self): + self.watcher.secret_manager.is_secret.assert_not_called() + self.watcher.secret_manager.get_secret_name_and_namespace.assert_not_called() + self.watcher.secret_manager.get_secret_data.assert_not_called() + self.watcher.secret_manager.is_topology_secret.assert_not_called() + self.watcher.secret_manager.add_unique_secret_info_to_list.assert_not_called() + self._assert_get_secret_info_from_parameter_not_called() + + def _assert_get_secret_info_from_parameter_not_called(self): + self.watcher.secret_manager.get_secret_name_and_namespace.assert_not_called() + self.watcher.secret_manager.get_secret_data.assert_not_called() + self.watcher.secret_manager.is_topology_secret.assert_not_called() + self.watcher.secret_manager.add_unique_secret_info_to_list.assert_not_called() + self.watcher.node_manager.generate_nodes_with_system_id.assert_not_called() + self.watcher.secret_manager.generate_secret_system_ids_topologies.assert_not_called() + self.watcher.resource_info_manager.generate_secret_info.assert_not_called() + + +class TestAddInitialStorageClasses(StorageClassWatcherBase): + + def test_define_initial_storage_class_with_secret_parameter(self): + self._prepare_add_initial_storage_classes([self.fake_storage_class_info], True, True, False) + self.watcher.add_initial_storage_classes() + self._assert_add_initial_storage_classes(True, True, False) + self.watcher.storage_class_manager.is_storage_class_has_csi_as_a_provisioner.assert_called_once_with( + self.fake_storage_class_info) + self.watcher.definition_manager.define_nodes_when_new_secret.assert_called_once_with(self.fake_secret_info) + + def test_do_not_define_initial_storage_class_with_non_secret_parameter(self): + self._prepare_add_initial_storage_classes([self.fake_storage_class_info], True, False) + self.watcher.add_initial_storage_classes() + self._assert_add_initial_storage_classes(True, False) + self.watcher.storage_class_manager.is_storage_class_has_csi_as_a_provisioner.assert_called_once_with( + self.fake_storage_class_info) + self.watcher.definition_manager.define_nodes_when_new_secret.assert_not_called() + + def test_do_not_define_initial_storage_class_with_no_ibm_block_csi_provisioner(self): + self._prepare_add_initial_storage_classes([self.fake_storage_class_info], False) + self.watcher.add_initial_storage_classes() + self._assert_add_initial_storage_classes(False) + self.watcher.storage_class_manager.is_storage_class_has_csi_as_a_provisioner.assert_called_once_with( + self.fake_storage_class_info) + self.watcher.definition_manager.define_nodes_when_new_secret.assert_not_called() + + def test_define_initial_storage_class_with_secret_topology_parameter(self): + self._prepare_add_initial_storage_classes([self.fake_storage_class_info], True, True, True) + self.watcher.add_initial_storage_classes() + self._assert_add_initial_storage_classes(True, True, True) + self.watcher.storage_class_manager.is_storage_class_has_csi_as_a_provisioner.assert_called_once_with( + self.fake_storage_class_info) + self.watcher.definition_manager.define_nodes_when_new_secret.assert_called_once_with(self.fake_secret_info) + + def test_do_not_define_initial_empty_storage_classes(self): + self._prepare_add_initial_storage_classes([]) + self.watcher.add_initial_storage_classes() + self._assert_add_initial_storage_classes(False) + self.watcher.storage_class_manager.is_storage_class_has_csi_as_a_provisioner.assert_not_called() + self.watcher.definition_manager.define_nodes_when_new_secret.assert_not_called() + + def _prepare_add_initial_storage_classes(self, storage_classes_info, is_sc_has_csi_provisioner=False, + is_secret=False, is_topology_secret=False): + self.watcher.resource_info_manager.get_storage_classes_info.return_value = storage_classes_info + self._prepare_get_secrets_info_from_storage_class_with_driver_provisioner( + is_sc_has_csi_provisioner, is_secret, is_topology_secret) + + def _assert_add_initial_storage_classes(self, is_sc_has_csi_provisioner=False, + is_secret=False, is_topology_secret=False): + self.watcher.resource_info_manager.get_storage_classes_info.assert_called_once_with() + if is_sc_has_csi_provisioner: + self._assert_called_get_secrets_info_from_storage_class_called(is_secret, is_topology_secret) + else: + self._assert_called_get_secrets_info_from_storage_class_not_called() + + +class TestWatchStorageClassResources(StorageClassWatcherBase): + def setUp(self): + super().setUp() + self.secret_info_with_storage_classes = test_utils.get_fake_secret_info(2) + self.copy_secret_info_with_storage_classes = deepcopy(self.secret_info_with_storage_classes) + self.storage_class_added_watch_manifest = test_utils.get_fake_storage_class_watch_event( + test_settings.ADDED_EVENT) + self.storage_class_added_watch_munch = test_utils.convert_manifest_to_munch( + self.storage_class_added_watch_manifest) + self.storage_class_deleted_watch_manifest = test_utils.get_fake_storage_class_watch_event( + test_settings.DELETED_EVENT_TYPE) + self.storage_class_deleted_watch_munch = test_utils.convert_manifest_to_munch( + self.storage_class_deleted_watch_manifest) + self.global_managed_secrets = patch('{}.MANAGED_SECRETS'.format(test_settings.STORAGE_CLASS_WATCHER_PATH), + [self.copy_secret_info_with_storage_classes]).start() + + @patch('{}.utils'.format(test_settings.STORAGE_CLASS_WATCHER_PATH)) + def test_define_new_storage_class_with_topology_secret_parameter(self, mock_utils): + mock_utils.munch.return_value = self.storage_class_added_watch_munch + self._prepare_get_secrets_info_from_storage_class_with_driver_provisioner(True, True, True) + self._test_watch_storage_class_resources(self.storage_class_added_watch_manifest, mock_utils) + self._assert_called_get_secrets_info_from_storage_class_called(True, True) + self.watcher.definition_manager.define_nodes_when_new_secret.assert_called_once_with(self.fake_secret_info) + self.watcher.secret_manager.get_matching_managed_secret_info.assert_not_called() + + @patch('{}.utils'.format(test_settings.STORAGE_CLASS_WATCHER_PATH)) + def test_define_new_storage_class_with_non_topology_secret_parameter(self, mock_utils): + mock_utils.munch.return_value = self.storage_class_added_watch_munch + self._prepare_get_secrets_info_from_storage_class_with_driver_provisioner(True, True, False) + self._test_watch_storage_class_resources(self.storage_class_added_watch_manifest, mock_utils) + self._assert_called_get_secrets_info_from_storage_class_called(True, False) + self.watcher.definition_manager.define_nodes_when_new_secret.assert_called_once_with(self.fake_secret_info) + self.watcher.secret_manager.get_matching_managed_secret_info.assert_not_called() + + @patch('{}.utils'.format(test_settings.STORAGE_CLASS_WATCHER_PATH)) + def test_do_not_define_new_storage_class_with_non_secret_parameter(self, mock_utils): + mock_utils.munch.return_value = self.storage_class_added_watch_munch + self._prepare_get_secrets_info_from_storage_class_with_driver_provisioner(True, False) + self._test_watch_storage_class_resources(self.storage_class_added_watch_manifest, mock_utils) + self._assert_called_get_secrets_info_from_storage_class_called(False) + self.watcher.definition_manager.define_nodes_when_new_secret.assert_not_called() + self.watcher.secret_manager.get_matching_managed_secret_info.assert_not_called() + + @patch('{}.utils'.format(test_settings.STORAGE_CLASS_WATCHER_PATH)) + def test_undefine_new_storage_class_with_topology_secret_parameter(self, mock_utils): + self.global_managed_secrets.append(self.secret_info_with_storage_classes) + mock_utils.munch.return_value = self.storage_class_deleted_watch_munch + self._prepare_get_secrets_info_from_storage_class_with_driver_provisioner(True, True, True) + self.watcher.secret_manager.get_matching_managed_secret_info.return_value = (None, 0) + self._test_watch_storage_class_resources(self.storage_class_deleted_watch_manifest, mock_utils) + self._assert_called_get_secrets_info_from_storage_class_called(True, True) + self.watcher.secret_manager.get_matching_managed_secret_info.assert_called_once_with(self.fake_secret_info) + self.watcher.definition_manager.define_nodes_when_new_secret.assert_not_called() + self.assertEqual(self.global_managed_secrets[0].managed_storage_classes, + self.secret_info_with_storage_classes.managed_storage_classes - 1) + + @patch('{}.utils'.format(test_settings.STORAGE_CLASS_WATCHER_PATH)) + def test_undefine_new_storage_class_with_non_topology_secret_parameter(self, mock_utils): + self.global_managed_secrets.append(self.secret_info_with_storage_classes) + mock_utils.munch.return_value = self.storage_class_deleted_watch_munch + self._prepare_get_secrets_info_from_storage_class_with_driver_provisioner(True, True, False) + self.watcher.secret_manager.get_matching_managed_secret_info.return_value = (None, 0) + self._test_watch_storage_class_resources(self.storage_class_deleted_watch_manifest, mock_utils) + self._assert_called_get_secrets_info_from_storage_class_called(True, False) + self.watcher.secret_manager.get_matching_managed_secret_info.assert_called_once_with(self.fake_secret_info) + self.watcher.definition_manager.define_nodes_when_new_secret.assert_not_called() + self.assertEqual(self.global_managed_secrets[0].managed_storage_classes, + self.secret_info_with_storage_classes.managed_storage_classes - 1) + + @patch('{}.utils'.format(test_settings.STORAGE_CLASS_WATCHER_PATH)) + def test_do_not_undefine_new_storage_class_with_non_secret_parameter(self, mock_utils): + self.global_managed_secrets.append(self.secret_info_with_storage_classes) + mock_utils.munch.return_value = self.storage_class_deleted_watch_munch + self._prepare_get_secrets_info_from_storage_class_with_driver_provisioner(True, False) + self.watcher.secret_manager.get_matching_managed_secret_info.return_value = (None, 0) + self._test_watch_storage_class_resources(self.storage_class_deleted_watch_manifest, mock_utils) + self._assert_called_get_secrets_info_from_storage_class_called(False) + self.watcher.definition_manager.define_nodes_when_new_secret.assert_not_called() + self.watcher.secret_manager.get_matching_managed_secret_info.assert_not_called() + self.assertEqual(self.global_managed_secrets[0].managed_storage_classes, + self.secret_info_with_storage_classes.managed_storage_classes) + + def _test_watch_storage_class_resources(self, watch_manifest, mock_utils): + mock_utils.loop_forever.side_effect = [True, False] + self.watcher.k8s_api.get_storage_class_stream.return_value = iter([watch_manifest]) + self.watcher.resource_info_manager.generate_storage_class_info.return_value = self.fake_storage_class_info + self.watcher.watch_storage_class_resources() + mock_utils.munch.assert_called_once_with(watch_manifest) + self.watcher.storage_class_manager.is_storage_class_has_csi_as_a_provisioner.assert_called_once_with( + self.fake_storage_class_info) diff --git a/controllers/tests/controller_server/host_definer/watchers/watcher_base.py b/controllers/tests/controller_server/host_definer/watchers/watcher_base.py new file mode 100644 index 000000000..5c63dcc91 --- /dev/null +++ b/controllers/tests/controller_server/host_definer/watchers/watcher_base.py @@ -0,0 +1,8 @@ +import unittest + +import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils + + +class WatcherBaseSetUp(unittest.TestCase): + def setUp(self): + test_utils.patch_k8s_api_init() From 776206a2e154d464cf58df3a34ad79172993c862 Mon Sep 17 00:00:00 2001 From: Arbel Nathan Date: Mon, 1 May 2023 15:31:06 +0300 Subject: [PATCH 07/12] CSI-5443 fix mend issues (#667) --- Dockerfile-csi-node | 2 +- Dockerfile-csi-node.test | 5 +- controllers/servers/csi/requirements.txt | 2 +- go.mod | 67 ++- go.sum | 537 ++++++++--------------- 5 files changed, 231 insertions(+), 382 deletions(-) diff --git a/Dockerfile-csi-node b/Dockerfile-csi-node index bb240f316..f0c932a13 100644 --- a/Dockerfile-csi-node +++ b/Dockerfile-csi-node @@ -13,7 +13,7 @@ # limitations under the License. # Build stage -FROM golang:1.13 as builder +FROM golang:1.19 as builder WORKDIR /go/src/github.com/ibm/ibm-block-csi-driver ENV GO111MODULE=on diff --git a/Dockerfile-csi-node.test b/Dockerfile-csi-node.test index a14a18261..261e2bde2 100644 --- a/Dockerfile-csi-node.test +++ b/Dockerfile-csi-node.test @@ -13,7 +13,7 @@ # limitations under the License. # Build stage -FROM golang:1.13 as builder +FROM golang:1.19 as builder WORKDIR /go/src/github.com/ibm/ibm-block-csi-driver ENV GO111MODULE=on @@ -24,6 +24,9 @@ COPY go.sum . RUN go mod download RUN go get github.com/tebeka/go2xunit # when GO111MODULE=on the module will not become executable, so get it here to run it as binary. RUN go get github.com/golang/mock/gomock +RUN go get github.com/golang/mock/mockgen +RUN go install github.com/tebeka/go2xunit +RUN go install github.com/golang/mock/gomock RUN go install github.com/golang/mock/mockgen COPY . . diff --git a/controllers/servers/csi/requirements.txt b/controllers/servers/csi/requirements.txt index 55810667d..880399592 100644 --- a/controllers/servers/csi/requirements.txt +++ b/controllers/servers/csi/requirements.txt @@ -3,7 +3,7 @@ grpcio-tools==1.41.1 protobuf==3.18.3 pyyaml==6 munch==2.3.2 -retry==0.9.2 +retry2==0.9.5 packaging==20.1 base58==2.0.0 diff --git a/go.mod b/go.mod index dc40cd2e6..640699192 100644 --- a/go.mod +++ b/go.mod @@ -1,21 +1,56 @@ module github.com/ibm/ibm-block-csi-driver -go 1.13 +go 1.19 require ( - github.com/container-storage-interface/spec v1.5.0 - github.com/golang/mock v1.3.1 - github.com/gophercloud/gophercloud v0.1.0 // indirect - github.com/kubernetes-csi/csi-lib-utils v0.9.1 - github.com/sirupsen/logrus v1.6.0 - golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e - golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 - google.golang.org/grpc v1.29.0 - gopkg.in/yaml.v2 v2.2.8 - k8s.io/apimachinery v0.19.0 - k8s.io/client-go v0.19.0 - k8s.io/klog v1.0.0 // indirect - k8s.io/mount-utils v0.20.13 - k8s.io/utils v0.0.0-20201110183641-67b214c5f920 - sigs.k8s.io/structured-merge-diff/v3 v3.0.0 // indirect + github.com/container-storage-interface/spec v1.8.0 + github.com/golang/mock v1.6.0 + github.com/kubernetes-csi/csi-lib-utils v0.13.0 + github.com/sirupsen/logrus v1.9.0 + golang.org/x/sync v0.1.0 + golang.org/x/sys v0.7.0 + google.golang.org/grpc v1.54.0 + gopkg.in/yaml.v2 v2.4.0 + k8s.io/apimachinery v0.27.1 + k8s.io/client-go v0.27.1 + k8s.io/mount-utils v0.27.1 + k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.10.2 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/gnostic v0.6.9 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/moby/sys/mountinfo v0.6.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + golang.org/x/net v0.9.0 // indirect + golang.org/x/oauth2 v0.7.0 // indirect + golang.org/x/term v0.7.0 // indirect + golang.org/x/text v0.9.0 // indirect + golang.org/x/time v0.3.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect + google.golang.org/protobuf v1.30.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.27.1 // indirect + k8s.io/klog/v2 v2.90.1 // indirect + k8s.io/kube-openapi v0.0.0-20230327201221-f5883ff37f0c // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index fc71d6b1a..94028d318 100644 --- a/go.sum +++ b/go.sum @@ -1,100 +1,50 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/container-storage-interface/spec v1.2.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4= -github.com/container-storage-interface/spec v1.5.0 h1:lvKxe3uLgqQeVQcrnL2CPQKISoKjTJxojEs9cBk+HXo= -github.com/container-storage-interface/spec v1.5.0/go.mod h1:8K96oQNkJ7pFcC2R9Z1ynGGBB1I93kcS6PGg3SsOk8s= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/container-storage-interface/spec v1.8.0 h1:D0vhF3PLIZwlwZEf2eNbpujGCNwspwTYf2idJRJx4xI= +github.com/container-storage-interface/spec v1.8.0/go.mod h1:ROLik+GhPslwwWRNFF1KasPzroNARibH2rfz1rkg4H0= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE= +github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= @@ -103,292 +53,182 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= +github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.1.0 h1:rVsPeBmXbYv4If/cumu1AzZPwV58q433hvONV1UEZoI= -github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I= -github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kubernetes-csi/csi-lib-utils v0.9.1 h1:sGq6ifVujfMSkfTsMZip44Ttv8SDXvsBlFk9GdYl/b8= -github.com/kubernetes-csi/csi-lib-utils v0.9.1/go.mod h1:8E2jVUX9j3QgspwHXa6LwyN7IHQDjW9jX3kwoWnSC+M= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kubernetes-csi/csi-lib-utils v0.13.0 h1:QrTdZVZbHlaSUBN9ReayBPnnF1N0edFIpUKBwVIBW3w= +github.com/kubernetes-csi/csi-lib-utils v0.13.0/go.mod h1:JS9eDIZmSjx4F9o0bLTVK/qfhIIOifdjEfVXzxWapfE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= +github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= +github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo= -golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= +golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8= -golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.29.0 h1:2pJjwYOdkZ9HlN4sWRYBg9ttH5bCOlsueaM+b/oYjwo= -google.golang.org/grpc v1.29.0/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -397,75 +237,46 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -k8s.io/api v0.18.0 h1:lwYk8Vt7rsVTwjRU6pzEsa9YNhThbmbocQlKvNBB4EQ= -k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8= -k8s.io/api v0.19.0 h1:XyrFIJqTYZJ2DU7FBE/bSPz7b1HvbVBuBf07oeo6eTc= -k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw= -k8s.io/apimachinery v0.18.0 h1:fuPfYpk3cs1Okp/515pAf0dNhL66+8zk8RLbSX+EgAE= -k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= -k8s.io/apimachinery v0.19.0 h1:gjKnAda/HZp5k4xQYjL0K/Yb66IvNqjthCb03QlKpaQ= -k8s.io/apimachinery v0.19.0/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= -k8s.io/client-go v0.18.0 h1:yqKw4cTUQraZK3fcVCMeSa+lqKwcjZ5wtcOIPnxQno4= -k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8= -k8s.io/client-go v0.19.0 h1:1+0E0zfWFIWeyRhQYWzimJOyAk2UT7TiARaLNwJCf7k= -k8s.io/client-go v0.19.0/go.mod h1:H9E/VT95blcFQnlyShFgnFT9ZnJOAceiUHM3MlRC+mU= -k8s.io/component-base v0.19.0/go.mod h1:dKsY8BxkA+9dZIAh2aWJLL/UdASFDNtGYTCItL4LM7Y= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/klog/v2 v2.0.0 h1:Foj74zO6RbjjP4hBEKjnYtjjAhGg4jNynUdYF6fJrok= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ= -k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= -k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= -k8s.io/mount-utils v0.20.13 h1:IaaCihes2VCR5B6ZYZ/tynCHkJSiidQG0SNQoh8vrg4= -k8s.io/mount-utils v0.20.13/go.mod h1:Jv9NRZ5L2LF87A17GaGlArD+r3JAJdZFvo4XD1cG4Kc= -k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20200912215256-4140de9c8800 h1:9ZNvfPvVIEsp/T1ez4GQuzCcCTEQWhovSofhqR73A6g= -k8s.io/utils v0.0.0-20200912215256-4140de9c8800/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSqqhKxQ0xVbA= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +k8s.io/api v0.27.1 h1:Z6zUGQ1Vd10tJ+gHcNNNgkV5emCyW+v2XTmn+CLjSd0= +k8s.io/api v0.27.1/go.mod h1:z5g/BpAiD+f6AArpqNjkY+cji8ueZDU/WV1jcj5Jk4E= +k8s.io/apimachinery v0.27.1 h1:EGuZiLI95UQQcClhanryclaQE6xjg1Bts6/L3cD7zyc= +k8s.io/apimachinery v0.27.1/go.mod h1:5ikh59fK3AJ287GUvpUsryoMFtH9zj/ARfWCo3AyXTM= +k8s.io/client-go v0.27.1 h1:oXsfhW/qncM1wDmWBIuDzRHNS2tLhK3BZv512Nc59W8= +k8s.io/client-go v0.27.1/go.mod h1:f8LHMUkVb3b9N8bWturc+EDtVVVwZ7ueTVquFAJb2vA= +k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= +k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20230327201221-f5883ff37f0c h1:EFfsozyzZ/pggw5qNx7ftTVZdp7WZl+3ih89GEjYEK8= +k8s.io/kube-openapi v0.0.0-20230327201221-f5883ff37f0c/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= +k8s.io/mount-utils v0.27.1 h1:RSd0wslbIuwLRaGGNAGMZ3m9FLcvukxJ3FWlOm76W2A= +k8s.io/mount-utils v0.27.1/go.mod h1:vmcjYdi2Vg1VTWY7KkhvwJVY6WDHxb/QQhiQKkR8iNs= +k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= +k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= From c2ef58779c3dc76cf1c2d9608768df07795ee92b Mon Sep 17 00:00:00 2001 From: Arbel Nathan Date: Mon, 1 May 2023 16:20:30 +0300 Subject: [PATCH 08/12] csi 5503 update base image (#669) --- Dockerfile-controllers.test | 4 ++-- Dockerfile-csi-controller | 4 ++-- Dockerfile-csi-host-definer | 4 ++-- Dockerfile-csi-node | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Dockerfile-controllers.test b/Dockerfile-controllers.test index ccb8da4c4..8cf52ecc1 100644 --- a/Dockerfile-controllers.test +++ b/Dockerfile-controllers.test @@ -16,7 +16,7 @@ # This Dockerfile.test is for running the csi controller local tests inside a container. # Its similar to the Dockerfile, but with additional requirements-tests.txt and ENTRYPOINT to run the local tests. -FROM registry.access.redhat.com/ubi8/python-38:1-115.1669838006 as builder +FROM registry.access.redhat.com/ubi8/python-38:1-125.1682304659 as builder USER root RUN if [[ "$(uname -m)" != "x86"* ]]; then yum install -y rust-toolset; fi USER default @@ -39,7 +39,7 @@ COPY controllers/tests/requirements.txt ./requirements-tests.txt RUN pip3 install -r ./requirements-tests.txt -FROM registry.access.redhat.com/ubi8/python-38:1-115.1669838006 +FROM registry.access.redhat.com/ubi8/python-38:1-125.1682304659 COPY --from=builder /opt/app-root /opt/app-root COPY ./common /driver/common diff --git a/Dockerfile-csi-controller b/Dockerfile-csi-controller index 5adb217b0..a2bc5d8ab 100644 --- a/Dockerfile-csi-controller +++ b/Dockerfile-csi-controller @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM registry.access.redhat.com/ubi8/python-38:1-115.1669838006 as builder +FROM registry.access.redhat.com/ubi8/python-38:1-125.1682304659 as builder USER root RUN if [[ "$(uname -m)" != "x86"* ]]; then yum install -y rust-toolset; fi USER default @@ -30,7 +30,7 @@ RUN ./csi_pb2.sh RUN pip3 install . -FROM registry.access.redhat.com/ubi8/python-38:1-115.1669838006 +FROM registry.access.redhat.com/ubi8/python-38:1-125.1682304659 MAINTAINER IBM Storage ARG VERSION=1.12.0 diff --git a/Dockerfile-csi-host-definer b/Dockerfile-csi-host-definer index 1dc48f875..3e72fed84 100644 --- a/Dockerfile-csi-host-definer +++ b/Dockerfile-csi-host-definer @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi8/python-38:1-115.1669838006 as builder +FROM registry.access.redhat.com/ubi8/python-38:1-125.1682304659 as builder USER root RUN if [[ "$(uname -m)" != "x86"* ]]; then yum install -y rust-toolset; fi @@ -17,7 +17,7 @@ COPY controllers/scripts/csi_general . RUN ./csi_pb2.sh RUN pip3 install . -FROM registry.access.redhat.com/ubi8/python-38:1-115.1669838006 +FROM registry.access.redhat.com/ubi8/python-38:1-125.1682304659 ARG VERSION=1.12.0 ARG BUILD_NUMBER=0 diff --git a/Dockerfile-csi-node b/Dockerfile-csi-node index f0c932a13..4cdcf9d13 100644 --- a/Dockerfile-csi-node +++ b/Dockerfile-csi-node @@ -27,7 +27,7 @@ COPY . . RUN make ibm-block-csi-driver # Final stage -FROM registry.access.redhat.com/ubi8/ubi-minimal:8.7-1049 +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.7-1107 MAINTAINER IBM Storage ARG VERSION=1.12.0 From d09aa28aa10997a2db03d0614eee0bc313b3ddf5 Mon Sep 17 00:00:00 2001 From: Matan Carmeli <45543087+matancarmeli7@users.noreply.github.com> Date: Wed, 17 May 2023 15:43:38 +0300 Subject: [PATCH 09/12] add pavariables to common settings (#673) Signed-off-by: matancarmeli7 --- controllers/common/settings.py | 53 +++++++++++++ .../definition_manager/request.py | 3 +- controllers/servers/host_definer/k8s/api.py | 12 +-- controllers/servers/host_definer/messages.py | 4 +- .../host_definer/resource_manager/csi_node.py | 4 +- .../resource_manager/daemon_set.py | 3 +- .../host_definer/resource_manager/event.py | 24 +++--- .../resource_manager/host_definition.py | 24 +++--- .../host_definer/resource_manager/node.py | 16 ++-- .../resource_manager/resource_info.py | 12 +-- .../host_definer/resource_manager/secret.py | 7 +- .../resource_manager/storage_class.py | 4 +- controllers/servers/host_definer/settings.py | 54 +------------ .../host_definer/utils/manifest_utils.py | 46 +++++------ .../servers/host_definer/utils/utils.py | 40 +++++----- .../host_definer/watcher/csi_node_watcher.py | 5 +- .../watcher/host_definition_watcher.py | 13 ++-- .../host_definer/watcher/node_watcher.py | 5 +- .../watcher/storage_class_watcher.py | 4 +- .../request_manager_test.py | 3 +- .../manifest_utils_test.py | 25 +++--- .../host_definer_utils_tests/utils_test.py | 60 ++++++++------- .../host_definer/k8s/kubernetes_api_test.py | 28 +++---- .../resource_manager/csi_node_manager_test.py | 3 +- .../daemon_set_manager_test.py | 4 +- .../resource_manager/event_manager_test.py | 16 ++-- .../host_definition_manager_test.py | 50 ++++++------ .../resource_manager/node_manager_test.py | 19 ++--- .../resource_info_manager_test.py | 29 +++---- .../resource_manager/secret_manager_test.py | 5 +- .../host_definer/settings.py | 76 +++++-------------- .../host_definer/utils/k8s_manifests_utils.py | 71 ++++++++--------- .../host_definer/utils/test_utils.py | 21 ++--- .../watchers/csi_node_watcher_test.py | 6 +- .../watchers/host_definition_watcher_test.py | 17 +++-- .../watchers/node_watcher_test.py | 5 +- .../watchers/secret_watcher_test.py | 4 +- .../watchers/storage_class_watcher_test.py | 5 +- 38 files changed, 392 insertions(+), 388 deletions(-) diff --git a/controllers/common/settings.py b/controllers/common/settings.py index 2bd7f95b1..b93551c6a 100644 --- a/controllers/common/settings.py +++ b/controllers/common/settings.py @@ -20,6 +20,7 @@ SPACE_EFFICIENCY_THICK = "thick" SPACE_EFFICIENCY_NONE = "none" +# hostDefiner HOST_DEFINITION_PLURAL = 'hostdefinitions' CSI_IBM_GROUP = 'csi.ibm.com' VERSION = 'v1' @@ -30,6 +31,58 @@ IO_GROUP_LABEL_PREFIX = 'hostdefiner.block.csi.ibm.com/io-group-' RESOURCE_VERSION_FIELD = 'resource_version' ITEMS_FIELD = 'items' +CSI_PROVISIONER_NAME = 'block.csi.ibm.com' +CSI_IBM_API_VERSION = 'csi.ibm.com/v1' +HOST_DEFINITION_KIND = 'HostDefinition' +MANAGE_NODE_LABEL = 'hostdefiner.block.csi.ibm.com/manage-node' +FORBID_DELETION_LABEL = 'hostdefiner.block.csi.ibm.com/do-not-delete-definition' +TRUE_STRING = 'true' +PREFIX_ENV_VAR = 'PREFIX' +CONNECTIVITY_ENV_VAR = 'CONNECTIVITY_TYPE' +DYNAMIC_NODE_LABELING_ENV_VAR = 'DYNAMIC_NODE_LABELING' +ALLOW_DELETE_ENV_VAR = 'ALLOW_DELETE' +DEFINE_ACTION = 'Define' +UNDEFINE_ACTION = 'Undefine' +FAILED_MESSAGE_TYPE = 'Failed' +NORMAL_EVENT_TYPE = 'Normal' +WARNING_EVENT_TYPE = 'Warning' +CSI_IBM_FINALIZER = HOST_DEFINITION_PLURAL + '.' + CSI_IBM_GROUP +TOPOLOGY_IBM_BLOCK_PREFIX = 'topology.block.csi.ibm.com' +ADDED_EVENT_TYPE = 'ADDED' +MODIFIED_EVENT_TYPE = 'MODIFIED' +DELETED_EVENT_TYPE = 'DELETED' +PENDING_CREATION_PHASE = 'PendingCreation' +PENDING_DELETION_PHASE = 'PendingDeletion' +DRIVER_PRODUCT_LABEL = 'product=ibm-block-csi-driver' +CONNECTIVITY_TYPE_LABEL = '{}/connectivity-type'.format(CSI_PROVISIONER_NAME) +SUCCESSFUL_MESSAGE_TYPE = 'Successful' +HOST_DEFINER = 'hostDefiner' +STORAGE_API_GROUP = 'storage.k8s.io' +CSI_PARAMETER_PREFIX = "csi.{}/".format(STORAGE_API_GROUP) +SECRET_NAME_SUFFIX = 'secret-name' +READY_PHASE = 'Ready' +ERROR_PHASE = 'Error' +DEFAULT_NAMESPACE = 'default' + +# hostDefiner fields +API_VERSION_FIELD = 'apiVersion' +KIND_FIELD = 'kind' +SPEC_FIELD = 'spec' +METADATA_FIELD = 'metadata' +STATUS_FIELD = 'status' +HOST_DEFINITION_NODE_NAME_FIELD = 'nodeName' +SECRET_NAME_FIELD = 'secretName' +SECRET_NAMESPACE_FIELD = 'secretNamespace' +CONNECTIVITY_TYPE_FIELD = 'connectivityType' +PORTS_FIELD = 'ports' +NODE_NAME_ON_STORAGE_FIELD = 'nodeNameOnStorage' +IO_GROUP_FIELD = 'ioGroups' +MANAGEMENT_ADDRESS_FIELD = 'managementAddress' +STATUS_PHASE_FIELD = 'phase' +LABELS_FIELD = 'labels' +FINALIZERS_FIELD = 'finalizers' +SECRET_CONFIG_FIELD = 'config' +HOST_DEFINITION_FIELD = 'hostDefinition' EAR_VOLUME_FC_MAP_COUNT = "2" diff --git a/controllers/servers/host_definer/definition_manager/request.py b/controllers/servers/host_definer/definition_manager/request.py index 331956d6a..49ce0d604 100644 --- a/controllers/servers/host_definer/definition_manager/request.py +++ b/controllers/servers/host_definer/definition_manager/request.py @@ -1,3 +1,4 @@ +import controllers.common.settings as common_settings from controllers.common.csi_logger import get_stdout_logger from controllers.servers.host_definer.globals import NODES from controllers.servers.host_definer import settings @@ -30,7 +31,7 @@ def generate_request(self, host_definition_info): def _get_new_request(self, labels): request = DefineHostRequest() - connectivity_type_label_on_node = self._get_label_value(labels, settings.CONNECTIVITY_TYPE_LABEL) + connectivity_type_label_on_node = self._get_label_value(labels, common_settings.CONNECTIVITY_TYPE_LABEL) request.prefix = utils.get_prefix() request.connectivity_type_from_user = utils.get_connectivity_type_from_user(connectivity_type_label_on_node) return request diff --git a/controllers/servers/host_definer/k8s/api.py b/controllers/servers/host_definer/k8s/api.py index 1c9fb22c0..d4db2d87c 100644 --- a/controllers/servers/host_definer/k8s/api.py +++ b/controllers/servers/host_definer/k8s/api.py @@ -34,8 +34,8 @@ def _get_csi_nodes_api(self): kind=settings.CSINODE_KIND) def _get_host_definitions_api(self): - return self.dynamic_client.resources.get(api_version=settings.CSI_IBM_API_VERSION, - kind=settings.HOST_DEFINITION_KIND) + return self.dynamic_client.resources.get(api_version=common_settings.CSI_IBM_API_VERSION, + kind=common_settings.HOST_DEFINITION_KIND) def get_csi_node(self, node_name): try: @@ -60,7 +60,7 @@ def create_host_definition(self, host_definition_manifest): except ApiException as ex: if ex != 404: logger.error(messages.FAILED_TO_CREATE_HOST_DEFINITION.format( - host_definition_manifest[settings.METADATA][common_settings.NAME_FIELD], ex.body)) + host_definition_manifest[common_settings.METADATA_FIELD][common_settings.NAME_FIELD], ex.body)) return None def patch_cluster_custom_object_status(self, group, version, plural, name, status): @@ -88,7 +88,7 @@ def delete_host_definition(self, host_definition_name): return None def patch_host_definition(self, host_definition_manifest): - host_definition_name = host_definition_manifest[settings.METADATA][common_settings.NAME_FIELD] + host_definition_name = host_definition_manifest[common_settings.METADATA_FIELD][common_settings.NAME_FIELD] logger.info(messages.PATCHING_HOST_DEFINITION.format(host_definition_name)) try: self.host_definitions_api.patch(body=host_definition_manifest, name=host_definition_name, @@ -106,7 +106,7 @@ def patch_node(self, node_name, body): self.core_api.patch_node(node_name, body) except ApiException as ex: logger.error(messages.FAILED_TO_UPDATE_NODE_LABEL.format( - node_name, settings.MANAGE_NODE_LABEL, ex.body)) + node_name, common_settings.MANAGE_NODE_LABEL, ex.body)) def get_secret_data(self, secret_name, secret_namespace): try: @@ -184,7 +184,7 @@ def list_csi_node(self): def _get_empty_k8s_list(self): much_object = Munch.fromDict({ common_settings.ITEMS_FIELD: [], - settings.METADATA: { + common_settings.METADATA_FIELD: { common_settings.RESOURCE_VERSION_FIELD } }) diff --git a/controllers/servers/host_definer/messages.py b/controllers/servers/host_definer/messages.py index a4e3e8beb..79c91fd1e 100644 --- a/controllers/servers/host_definer/messages.py +++ b/controllers/servers/host_definer/messages.py @@ -1,4 +1,4 @@ -from controllers.servers.host_definer import settings +import controllers.common.settings as common_settings SECRET_DOES_NOT_EXIST = 'Secret {} in namespace {} does not exist' FAILED_TO_GET_SECRET = 'Failed to get Secret {} in namespace {}, go this error: {}' @@ -37,7 +37,7 @@ DELETE_HOST_DEFINITION = 'Deleting host definition {}' ADD_FINALIZER_TO_HOST_DEFINITION = 'Adding finalizer to host definition {}' REMOVE_FINALIZER_TO_HOST_DEFINITION = 'Removing finalizer from host definition {}' -FAILED_TO_REMOVE_FINALIZER = 'Failed to remove {} finalizer from node'.format(settings.CSI_IBM_FINALIZER) +FAILED_TO_REMOVE_FINALIZER = 'Failed to remove {} finalizer from node'.format(common_settings.CSI_IBM_FINALIZER) NODE_ID_WAS_CHANGED = "NodeId was changed for {} node, updating his ports in his definitions,"\ "old NodeId [{}], new NodeId [{}]" UPDATE_HOST_DEFINITION_FIELDS_FROM_STORAGE = 'Update host definition {} host from storage fields with {}' diff --git a/controllers/servers/host_definer/resource_manager/csi_node.py b/controllers/servers/host_definer/resource_manager/csi_node.py index 89dc4d4e4..d85d2c6ae 100644 --- a/controllers/servers/host_definer/resource_manager/csi_node.py +++ b/controllers/servers/host_definer/resource_manager/csi_node.py @@ -1,5 +1,5 @@ from controllers.common.csi_logger import get_stdout_logger -from controllers.servers.host_definer import settings +import controllers.common.settings as common_settings import controllers.servers.host_definer.messages as messages from controllers.servers.host_definer.k8s.api import K8SApi from controllers.servers.host_definer.resource_manager.resource_info import ResourceInfoManager @@ -26,7 +26,7 @@ def get_csi_nodes_info_with_driver(self): def _is_k8s_csi_node_has_driver(self, k8s_csi_node): if k8s_csi_node.spec.drivers: for driver in k8s_csi_node.spec.drivers: - if driver.name == settings.CSI_PROVISIONER_NAME: + if driver.name == common_settings.CSI_PROVISIONER_NAME: return True return False diff --git a/controllers/servers/host_definer/resource_manager/daemon_set.py b/controllers/servers/host_definer/resource_manager/daemon_set.py index 2ccb5d091..be97ca252 100644 --- a/controllers/servers/host_definer/resource_manager/daemon_set.py +++ b/controllers/servers/host_definer/resource_manager/daemon_set.py @@ -1,5 +1,6 @@ import time +import controllers.common.settings as common_settings from controllers.common.csi_logger import get_stdout_logger import controllers.servers.host_definer.messages as messages from controllers.servers.host_definer import settings @@ -30,7 +31,7 @@ def wait_until_all_daemon_set_pods_are_up_to_date(self): return csi_daemon_set.metadata.name def _get_csi_daemon_set(self): - daemon_sets = self.k8s_api.list_daemon_set_for_all_namespaces(settings.DRIVER_PRODUCT_LABEL) + daemon_sets = self.k8s_api.list_daemon_set_for_all_namespaces(common_settings.DRIVER_PRODUCT_LABEL) if daemon_sets and daemon_sets.items: return daemon_sets.items[0] return None diff --git a/controllers/servers/host_definer/resource_manager/event.py b/controllers/servers/host_definer/resource_manager/event.py index 08b7361b0..652ee6675 100644 --- a/controllers/servers/host_definer/resource_manager/event.py +++ b/controllers/servers/host_definer/resource_manager/event.py @@ -3,23 +3,27 @@ from controllers.common.csi_logger import get_stdout_logger from controllers.servers.host_definer import settings +import controllers.common.settings as common_settings logger = get_stdout_logger() class EventManager(): def generate_k8s_event(self, host_definition_info, message, action, message_type): - return client.CoreV1Event( - metadata=client.V1ObjectMeta(generate_name='{}.'.format(host_definition_info.name), ), - reporting_component=settings.HOST_DEFINER, reporting_instance=settings.HOST_DEFINER, action=action, - type=self._get_event_type(message_type), reason=message_type + action, message=str(message), + return client.CoreV1Event(metadata=client.V1ObjectMeta( + generate_name='{}.'.format(host_definition_info.name),), + reporting_component=common_settings.HOST_DEFINER, + reporting_instance=common_settings.HOST_DEFINER, action=action, + type=self._get_event_type(message_type), + reason=message_type + action, message=str(message), event_time=datetime.datetime.utcnow().isoformat(timespec='microseconds') + 'Z', involved_object=client.V1ObjectReference( - api_version=settings.CSI_IBM_API_VERSION, kind=settings.HOST_DEFINITION_KIND, - name=host_definition_info.name, resource_version=host_definition_info.resource_version, - uid=host_definition_info.uid, )) + api_version=common_settings.CSI_IBM_API_VERSION, + kind=common_settings.HOST_DEFINITION_KIND, name=host_definition_info.name, + resource_version=host_definition_info.resource_version, + uid=host_definition_info.uid,)) def _get_event_type(self, message_type): - if message_type != settings.SUCCESSFUL_MESSAGE_TYPE: - return settings.WARNING_EVENT_TYPE - return settings.NORMAL_EVENT_TYPE + if message_type != common_settings.SUCCESSFUL_MESSAGE_TYPE: + return common_settings.WARNING_EVENT_TYPE + return common_settings.NORMAL_EVENT_TYPE diff --git a/controllers/servers/host_definer/resource_manager/host_definition.py b/controllers/servers/host_definer/resource_manager/host_definition.py index 705b2457e..61d14a85f 100644 --- a/controllers/servers/host_definer/resource_manager/host_definition.py +++ b/controllers/servers/host_definer/resource_manager/host_definition.py @@ -53,7 +53,7 @@ def create_host_definition_if_not_exist(self, host_definition_info, response): current_host_definition_info_on_cluster = self.get_matching_host_definition_info( host_definition_info.node_name, host_definition_info.secret_name, host_definition_info.secret_namespace) if current_host_definition_info_on_cluster: - host_definition_manifest[settings.METADATA][ + host_definition_manifest[common_settings.METADATA_FIELD][ common_settings.NAME_FIELD] = current_host_definition_info_on_cluster.name self.k8s_api.patch_host_definition(host_definition_manifest) return current_host_definition_info_on_cluster @@ -71,21 +71,23 @@ def create_host_definition(self, host_definition_manifest): def _add_finalizer(self, host_definition_name): logger.info(messages.ADD_FINALIZER_TO_HOST_DEFINITION.format(host_definition_name)) - self._update_finalizer(host_definition_name, [settings.CSI_IBM_FINALIZER, ]) + self._update_finalizer(host_definition_name, [common_settings.CSI_IBM_FINALIZER, ]) def set_status_to_host_definition_after_definition(self, message_from_storage, host_definition_info): if message_from_storage and host_definition_info: self.set_host_definition_status(host_definition_info.name, - settings.PENDING_CREATION_PHASE) + common_settings.PENDING_CREATION_PHASE) self.create_k8s_event_for_host_definition( - host_definition_info, message_from_storage, settings.DEFINE_ACTION, settings.FAILED_MESSAGE_TYPE) + host_definition_info, message_from_storage, common_settings.DEFINE_ACTION, + common_settings.FAILED_MESSAGE_TYPE) elif host_definition_info: self.set_host_definition_status_to_ready(host_definition_info) def set_host_definition_status_to_ready(self, host_definition): - self.set_host_definition_status(host_definition.name, settings.READY_PHASE) + self.set_host_definition_status(host_definition.name, common_settings.READY_PHASE) self.create_k8s_event_for_host_definition( - host_definition, settings.SUCCESS_MESSAGE, settings.DEFINE_ACTION, settings.SUCCESSFUL_MESSAGE_TYPE) + host_definition, settings.SUCCESS_MESSAGE, common_settings.DEFINE_ACTION, + common_settings.SUCCESSFUL_MESSAGE_TYPE) def handle_k8s_host_definition_after_undefine_action(self, host_definition_info, response): current_host_definition_info_on_cluster = self.get_matching_host_definition_info( @@ -97,17 +99,17 @@ def handle_k8s_host_definition_after_undefine_action(self, host_definition_info, def _handle_existing_k8s_host_definition_after_undefine_action(self, message_from_storage, host_definition_info): if message_from_storage and host_definition_info: self.set_host_definition_status(host_definition_info.name, - settings.PENDING_DELETION_PHASE) + common_settings.PENDING_DELETION_PHASE) self.create_k8s_event_for_host_definition( host_definition_info, message_from_storage, - settings.UNDEFINE_ACTION, settings.FAILED_MESSAGE_TYPE) + common_settings.UNDEFINE_ACTION, common_settings.FAILED_MESSAGE_TYPE) elif host_definition_info: self.delete_host_definition(host_definition_info.name) def create_k8s_event_for_host_definition(self, host_definition_info, message, action, message_type): logger.info(messages.CREATE_EVENT_FOR_HOST_DEFINITION.format(message, host_definition_info.name)) k8s_event = self.event_manager.generate_k8s_event(host_definition_info, message, action, message_type) - self.k8s_api.create_event(settings.DEFAULT_NAMESPACE, k8s_event) + self.k8s_api.create_event(common_settings.DEFAULT_NAMESPACE, k8s_event) def delete_host_definition(self, host_definition_name): logger.info(messages.DELETE_HOST_DEFINITION.format(host_definition_name)) @@ -131,7 +133,7 @@ def is_host_definition_in_pending_phase(self, phase): def set_host_definition_phase_to_error(self, host_definition_info): logger.info(messages.SET_HOST_DEFINITION_PHASE_TO_ERROR.format(host_definition_info.name)) - self.set_host_definition_status(host_definition_info.name, settings.ERROR_PHASE) + self.set_host_definition_status(host_definition_info.name, common_settings.ERROR_PHASE) def set_host_definition_status(self, host_definition_name, host_definition_phase): logger.info(messages.SET_HOST_DEFINITION_STATUS.format(host_definition_name, host_definition_phase)) @@ -144,7 +146,7 @@ def is_host_definition_not_pending(self, host_definition_info): current_host_definition_info_on_cluster = self.get_matching_host_definition_info( host_definition_info.node_name, host_definition_info.secret_name, host_definition_info.secret_namespace) return not current_host_definition_info_on_cluster or \ - current_host_definition_info_on_cluster.phase == settings.READY_PHASE + current_host_definition_info_on_cluster.phase == common_settings.READY_PHASE def get_matching_host_definition_info(self, node_name, secret_name, secret_namespace): k8s_host_definitions = self.k8s_api.list_host_definition().items diff --git a/controllers/servers/host_definer/resource_manager/node.py b/controllers/servers/host_definer/resource_manager/node.py index 3d8a29cf4..ab7dbb28c 100644 --- a/controllers/servers/host_definer/resource_manager/node.py +++ b/controllers/servers/host_definer/resource_manager/node.py @@ -1,7 +1,7 @@ from controllers.common.csi_logger import get_stdout_logger from controllers.servers.utils import get_system_info_for_topologies from controllers.servers.errors import ValidationException -from controllers.servers.host_definer import settings +import controllers.common.settings as common_settings from controllers.servers.host_definer.globals import NODES, MANAGED_SECRETS from controllers.servers.host_definer.types import ManagedNode import controllers.servers.host_definer.messages as messages @@ -33,7 +33,7 @@ def is_node_can_be_undefined(self, node_name): not self.is_node_has_forbid_deletion_label(node_name) def is_node_has_forbid_deletion_label(self, node_name): - return self._is_node_has_label_in_true(node_name, settings.FORBID_DELETION_LABEL) + return self._is_node_has_label_in_true(node_name, common_settings.FORBID_DELETION_LABEL) def add_node_to_nodes(self, csi_node_info): logger.info(messages.NEW_KUBERNETES_NODE.format(csi_node_info.name)) @@ -43,8 +43,8 @@ def add_node_to_nodes(self, csi_node_info): def _add_manage_node_label_to_node(self, node_name): if self.is_node_has_manage_node_label(node_name): return - logger.info(messages.ADD_LABEL_TO_NODE.format(settings.MANAGE_NODE_LABEL, node_name)) - self._update_manage_node_label(node_name, settings.TRUE_STRING) + logger.info(messages.ADD_LABEL_TO_NODE.format(common_settings.MANAGE_NODE_LABEL, node_name)) + self._update_manage_node_label(node_name, common_settings.TRUE_STRING) def generate_managed_node(self, csi_node_info): node_info = self.resource_info_manager.get_node_info(csi_node_info.name) @@ -52,7 +52,7 @@ def generate_managed_node(self, csi_node_info): def remove_manage_node_label(self, node_name): if self._is_node_should_be_removed(node_name): - logger.info(messages.REMOVE_LABEL_FROM_NODE.format(settings.MANAGE_NODE_LABEL, node_name)) + logger.info(messages.REMOVE_LABEL_FROM_NODE.format(common_settings.MANAGE_NODE_LABEL, node_name)) self._update_manage_node_label(node_name, None) def _is_node_should_be_removed(self, node_name): @@ -101,11 +101,11 @@ def is_node_has_new_manage_node_label(self, csi_node_info, unmanaged_csi_nodes_w self._is_node_is_unmanaged_and_with_csi_node(csi_node_info, unmanaged_csi_nodes_with_driver) def is_node_has_manage_node_label(self, node_name): - return self._is_node_has_label_in_true(node_name, settings.MANAGE_NODE_LABEL) + return self._is_node_has_label_in_true(node_name, common_settings.MANAGE_NODE_LABEL) def _is_node_has_label_in_true(self, node_name, label): node_info = self.resource_info_manager.get_node_info(node_name) - return node_info.labels.get(label) == settings.TRUE_STRING + return node_info.labels.get(label) == common_settings.TRUE_STRING def _is_node_is_unmanaged_and_with_csi_node(self, csi_node_info, unmanaged_csi_nodes_with_driver): if csi_node_info.name not in NODES and csi_node_info.node_id and \ @@ -114,7 +114,7 @@ def _is_node_is_unmanaged_and_with_csi_node(self, csi_node_info, unmanaged_csi_n return False def handle_node_topologies(self, node_info, watch_event_type): - if node_info.name not in NODES or watch_event_type != settings.MODIFIED_EVENT: + if node_info.name not in NODES or watch_event_type != common_settings.MODIFIED_EVENT_TYPE: return for index, managed_secret_info in enumerate(MANAGED_SECRETS): if not managed_secret_info.system_ids_topologies: diff --git a/controllers/servers/host_definer/resource_manager/resource_info.py b/controllers/servers/host_definer/resource_manager/resource_info.py index bb0498192..8d999e58a 100644 --- a/controllers/servers/host_definer/resource_manager/resource_info.py +++ b/controllers/servers/host_definer/resource_manager/resource_info.py @@ -38,7 +38,7 @@ def generate_csi_node_info(self, k8s_csi_node): def _get_node_id_from_k8s_csi_node(self, k8s_csi_node): if k8s_csi_node.spec.drivers: for driver in k8s_csi_node.spec.drivers: - if driver.name == settings.CSI_PROVISIONER_NAME: + if driver.name == common_settings.CSI_PROVISIONER_NAME: return driver.nodeID return '' @@ -58,7 +58,7 @@ def generate_storage_class_info(self, k8s_storage_class): def get_csi_pods_info(self): pods_info = [] - k8s_pods = self.k8s_api.list_pod_for_all_namespaces(settings.DRIVER_PRODUCT_LABEL) + k8s_pods = self.k8s_api.list_pod_for_all_namespaces(common_settings.DRIVER_PRODUCT_LABEL) if not k8s_pods: return pods_info for k8s_pod in k8s_pods.items: @@ -79,15 +79,15 @@ def generate_host_definition_info(self, k8s_host_definition): host_definition_info.uid = k8s_host_definition.metadata.uid host_definition_info.phase = self._get_host_definition_phase(k8s_host_definition) host_definition_info.secret_name = self._get_attr_from_host_definition( - k8s_host_definition, settings.SECRET_NAME_FIELD) + k8s_host_definition, common_settings.SECRET_NAME_FIELD) host_definition_info.secret_namespace = self._get_attr_from_host_definition( - k8s_host_definition, settings.SECRET_NAMESPACE_FIELD) + k8s_host_definition, common_settings.SECRET_NAMESPACE_FIELD) host_definition_info.node_name = self._get_attr_from_host_definition( - k8s_host_definition, settings.NODE_NAME_FIELD) + k8s_host_definition, common_settings.HOST_DEFINITION_NODE_NAME_FIELD) host_definition_info.node_id = self._get_attr_from_host_definition( k8s_host_definition, common_settings.HOST_DEFINITION_NODE_ID_FIELD) host_definition_info.connectivity_type = self._get_attr_from_host_definition( - k8s_host_definition, settings.CONNECTIVITY_TYPE_FIELD) + k8s_host_definition, common_settings.CONNECTIVITY_TYPE_FIELD) return host_definition_info def _get_host_definition_phase(self, k8s_host_definition): diff --git a/controllers/servers/host_definer/resource_manager/secret.py b/controllers/servers/host_definer/resource_manager/secret.py index b930b5b03..15285f57c 100644 --- a/controllers/servers/host_definer/resource_manager/secret.py +++ b/controllers/servers/host_definer/resource_manager/secret.py @@ -5,7 +5,6 @@ from controllers.servers.host_definer.globals import MANAGED_SECRETS from controllers.servers.host_definer import settings from controllers.servers.host_definer.utils import utils -from controllers.servers.host_definer.types import SecretInfo import controllers.servers.host_definer.messages as messages from controllers.servers.host_definer.k8s.api import K8SApi from controllers.servers.host_definer.resource_manager.resource_info import ResourceInfoManager @@ -71,11 +70,11 @@ def generate_secret_system_ids_topologies(self, secret_data): return system_ids_topologies def is_secret(self, parameter_name): - return parameter_name.endswith(settings.SECRET_NAME_SUFFIX) and \ - parameter_name.startswith(settings.CSI_PARAMETER_PREFIX) + return parameter_name.endswith(common_settings.SECRET_NAME_SUFFIX) and \ + parameter_name.startswith(common_settings.CSI_PARAMETER_PREFIX) def get_secret_name_and_namespace(self, storage_class_info, parameter_name): - secret_name_suffix = settings.SECRET_NAME_SUFFIX + secret_name_suffix = common_settings.SECRET_NAME_SUFFIX prefix = parameter_name.split(secret_name_suffix)[0] return (storage_class_info.parameters[parameter_name], storage_class_info.parameters[prefix + secret_name_suffix.replace( diff --git a/controllers/servers/host_definer/resource_manager/storage_class.py b/controllers/servers/host_definer/resource_manager/storage_class.py index 5f271902e..31d6386d2 100644 --- a/controllers/servers/host_definer/resource_manager/storage_class.py +++ b/controllers/servers/host_definer/resource_manager/storage_class.py @@ -1,9 +1,9 @@ from controllers.common.csi_logger import get_stdout_logger -from controllers.servers.host_definer import settings +import controllers.common.settings as common_settings logger = get_stdout_logger() class StorageClassManager: def is_storage_class_has_csi_as_a_provisioner(self, storage_class_info): - return storage_class_info.provisioner == settings.CSI_PROVISIONER_NAME + return storage_class_info.provisioner == common_settings.CSI_PROVISIONER_NAME diff --git a/controllers/servers/host_definer/settings.py b/controllers/servers/host_definer/settings.py index 8a293c33b..4ebdce727 100644 --- a/controllers/servers/host_definer/settings.py +++ b/controllers/servers/host_definer/settings.py @@ -1,62 +1,14 @@ -from controllers.common.settings import HOST_DEFINITION_PLURAL, CSI_IBM_GROUP +from controllers.common.settings import TOPOLOGY_IBM_BLOCK_PREFIX, STORAGE_API_GROUP import controllers.array_action.settings as array_config -STORAGE_API_VERSION = 'storage.k8s.io/v1' -CSI_PARAMETER_PREFIX = "csi.storage.k8s.io/" +STORAGE_API_VERSION = '{}/v1'.format(STORAGE_API_GROUP) CSINODE_KIND = 'CSINode' -CSI_IBM_API_VERSION = 'csi.ibm.com/v1' -HOST_DEFINITION_KIND = 'HostDefinition' -SECRET_NAME_SUFFIX = 'secret-name' -CSI_PROVISIONER_NAME = 'block.csi.ibm.com' -ADDED_EVENT = 'ADDED' -DELETED_EVENT = 'DELETED' -MODIFIED_EVENT = 'MODIFIED' PENDING_PREFIX = 'Pending' -PENDING_CREATION_PHASE = 'PendingCreation' -PENDING_DELETION_PHASE = 'PendingDeletion' -ERROR_PHASE = 'Error' -READY_PHASE = 'Ready' -DRIVER_PRODUCT_LABEL = 'product=ibm-block-csi-driver' -DEFAULT_NAMESPACE = 'default' -HOST_DEFINER = 'hostDefiner' -MANAGE_NODE_LABEL = 'hostdefiner.block.csi.ibm.com/manage-node' -FORBID_DELETION_LABEL = 'hostdefiner.block.csi.ibm.com/do-not-delete-definition' -CONNECTIVITY_TYPE_LABEL = 'block.csi.ibm.com/connectivity-type' SUPPORTED_CONNECTIVITY_TYPES = [array_config.ISCSI_CONNECTIVITY_TYPE, array_config.FC_CONNECTIVITY_TYPE, array_config.NVME_OVER_FC_CONNECTIVITY_TYPE] -NODE_NAME_FIELD = 'nodeName' -SECRET_NAME_FIELD = 'secretName' -SECRET_NAMESPACE_FIELD = 'secretNamespace' -CONNECTIVITY_TYPE_FIELD = 'connectivityType' -PORTS_FIELD = 'ports' -NODE_NAME_ON_STORAGE_FIELD = 'nodeNameOnStorage' -IO_GROUP_FIELD = 'ioGroups' -MANAGEMENT_ADDRESS_FIELD = 'managementAddress' -API_VERSION = 'apiVersion' -KIND = 'kind' -METADATA = 'metadata' -SPEC = 'spec' -HOST_DEFINITION_FIELD = 'hostDefinition' -PREFIX_ENV_VAR = 'PREFIX' -CONNECTIVITY_ENV_VAR = 'CONNECTIVITY_TYPE' -STATUS = 'status' -PHASE = 'phase' -LABELS = 'labels' -TRUE_STRING = 'true' -DYNAMIC_NODE_LABELING_ENV_VAR = 'DYNAMIC_NODE_LABELING' -ALLOW_DELETE_ENV_VAR = 'ALLOW_DELETE' -DEFINE_ACTION = 'Define' -UNDEFINE_ACTION = 'Undefine' SUCCESS_MESSAGE = 'Host defined successfully on the array' -FAILED_MESSAGE_TYPE = 'Failed' -SUCCESSFUL_MESSAGE_TYPE = 'Successful' -NORMAL_EVENT_TYPE = 'Normal' -WARNING_EVENT_TYPE = 'Warning' -FINALIZERS = 'finalizers' -CSI_IBM_FINALIZER = HOST_DEFINITION_PLURAL + '.' + CSI_IBM_GROUP HOST_DEFINITION_PENDING_RETRIES = 5 HOST_DEFINITION_PENDING_EXPONENTIAL_BACKOFF_IN_SECONDS = 3 HOST_DEFINITION_PENDING_DELAY_IN_SECONDS = 3 -SECRET_CONFIG_FIELD = 'config' -TOPOLOGY_PREFIXES = ['topology.block.csi.ibm.com'] +TOPOLOGY_PREFIXES = [TOPOLOGY_IBM_BLOCK_PREFIX] POSSIBLE_NUMBER_OF_IO_GROUP = 4 diff --git a/controllers/servers/host_definer/utils/manifest_utils.py b/controllers/servers/host_definer/utils/manifest_utils.py index 37e123c5f..baa0f9eba 100644 --- a/controllers/servers/host_definer/utils/manifest_utils.py +++ b/controllers/servers/host_definer/utils/manifest_utils.py @@ -4,55 +4,57 @@ def get_host_definition_manifest(host_definition_info, response, node_id): manifest = generate_host_definition_response_fields_manifest(host_definition_info.name, response) - manifest[settings.API_VERSION] = settings.CSI_IBM_API_VERSION - manifest[settings.KIND] = settings.HOST_DEFINITION_KIND - manifest[settings.SPEC][settings.HOST_DEFINITION_FIELD][settings.NODE_NAME_FIELD] = host_definition_info.node_name - manifest[settings.SPEC][settings.HOST_DEFINITION_FIELD][common_settings.HOST_DEFINITION_NODE_ID_FIELD] = node_id - manifest[settings.SPEC][settings.HOST_DEFINITION_FIELD][settings.SECRET_NAME_FIELD] = \ + manifest[common_settings.API_VERSION_FIELD] = common_settings.CSI_IBM_API_VERSION + manifest[common_settings.KIND_FIELD] = common_settings.HOST_DEFINITION_KIND + manifest[common_settings.SPEC_FIELD][common_settings.HOST_DEFINITION_FIELD][ + common_settings.HOST_DEFINITION_NODE_NAME_FIELD] = host_definition_info.node_name + manifest[common_settings.SPEC_FIELD][common_settings.HOST_DEFINITION_FIELD][ + common_settings.HOST_DEFINITION_NODE_ID_FIELD] = node_id + manifest[common_settings.SPEC_FIELD][common_settings.HOST_DEFINITION_FIELD][common_settings.SECRET_NAME_FIELD] = \ host_definition_info.secret_name - manifest[settings.SPEC][settings.HOST_DEFINITION_FIELD][settings.SECRET_NAMESPACE_FIELD] = \ - host_definition_info.secret_namespace + manifest[common_settings.SPEC_FIELD][common_settings.HOST_DEFINITION_FIELD][ + common_settings.SECRET_NAMESPACE_FIELD] = host_definition_info.secret_namespace return manifest def get_host_definition_status_manifest(host_definition_phase): return { - settings.STATUS: { - settings.PHASE: host_definition_phase, + common_settings.STATUS_FIELD: { + common_settings.STATUS_PHASE_FIELD: host_definition_phase, } } def get_body_manifest_for_labels(label_value): return { - settings.METADATA: { - settings.LABELS: { - settings.MANAGE_NODE_LABEL: label_value} + common_settings.METADATA_FIELD: { + common_settings.LABELS_FIELD: { + common_settings.MANAGE_NODE_LABEL: label_value} } } def get_finalizer_manifest(host_definition_name, finalizers): return { - settings.METADATA: { + common_settings.METADATA_FIELD: { common_settings.NAME_FIELD: host_definition_name, - settings.FINALIZERS: finalizers, + common_settings.FINALIZERS_FIELD: finalizers, } } def generate_host_definition_response_fields_manifest(host_definition_name, response): return { - settings.METADATA: { + common_settings.METADATA_FIELD: { common_settings.NAME_FIELD: host_definition_name, }, - settings.SPEC: { - settings.HOST_DEFINITION_FIELD: { - settings.CONNECTIVITY_TYPE_FIELD: response.connectivity_type, - settings.PORTS_FIELD: response.ports, - settings.NODE_NAME_ON_STORAGE_FIELD: response.node_name_on_storage, - settings.IO_GROUP_FIELD: response.io_group, - settings.MANAGEMENT_ADDRESS_FIELD: response.management_address + common_settings.SPEC_FIELD: { + common_settings.HOST_DEFINITION_FIELD: { + common_settings.CONNECTIVITY_TYPE_FIELD: response.connectivity_type, + common_settings.PORTS_FIELD: response.ports, + common_settings.NODE_NAME_ON_STORAGE_FIELD: response.node_name_on_storage, + common_settings.IO_GROUP_FIELD: response.io_group, + common_settings.MANAGEMENT_ADDRESS_FIELD: response.management_address }, }, } diff --git a/controllers/servers/host_definer/utils/utils.py b/controllers/servers/host_definer/utils/utils.py index 9f2bfb05d..0cb367431 100644 --- a/controllers/servers/host_definer/utils/utils.py +++ b/controllers/servers/host_definer/utils/utils.py @@ -21,7 +21,7 @@ def generate_io_group_from_labels(labels): io_group = '' for io_group_index in range(host_definer_settings.POSSIBLE_NUMBER_OF_IO_GROUP): label_content = labels.get(common_settings.IO_GROUP_LABEL_PREFIX + str(io_group_index)) - if label_content == host_definer_settings.TRUE_STRING: + if label_content == common_settings.TRUE_STRING: if io_group: io_group += common_settings.IO_GROUP_DELIMITER io_group += str(io_group_index) @@ -35,9 +35,9 @@ def get_k8s_object_resource_version(k8s_object): def change_decode_base64_secret_config(secret_data): - if settings.SECRET_CONFIG_FIELD in secret_data.keys(): - secret_data[settings.SECRET_CONFIG_FIELD] = _decode_base64_to_dict( - secret_data[settings.SECRET_CONFIG_FIELD]) + if common_settings.SECRET_CONFIG_FIELD in secret_data.keys(): + secret_data[common_settings.SECRET_CONFIG_FIELD] = _decode_base64_to_dict( + secret_data[common_settings.SECRET_CONFIG_FIELD]) return secret_data @@ -49,13 +49,14 @@ def _decode_base64_to_dict(content_with_base64): def get_secret_config(secret_data): secret_data = _convert_secret_config_to_dict(secret_data) - return secret_data.get(settings.SECRET_CONFIG_FIELD, {}) + return secret_data.get(common_settings.SECRET_CONFIG_FIELD, {}) def _convert_secret_config_to_dict(secret_data): - if settings.SECRET_CONFIG_FIELD in secret_data.keys(): - if type(secret_data[settings.SECRET_CONFIG_FIELD]) is str: - secret_data[settings.SECRET_CONFIG_FIELD] = json.loads(secret_data[settings.SECRET_CONFIG_FIELD]) + if common_settings.SECRET_CONFIG_FIELD in secret_data.keys(): + if type(secret_data[common_settings.SECRET_CONFIG_FIELD]) is str: + secret_data[common_settings.SECRET_CONFIG_FIELD] = json.loads( + secret_data[common_settings.SECRET_CONFIG_FIELD]) return secret_data @@ -76,13 +77,13 @@ def validate_secret(secret_data): def get_prefix(): - return os.getenv(settings.PREFIX_ENV_VAR) + return os.getenv(common_settings.PREFIX_ENV_VAR) def get_connectivity_type_from_user(connectivity_type_label_on_node): if connectivity_type_label_on_node in settings.SUPPORTED_CONNECTIVITY_TYPES: return connectivity_type_label_on_node - return os.getenv(settings.CONNECTIVITY_ENV_VAR) + return os.getenv(common_settings.CONNECTIVITY_ENV_VAR) def is_topology_label(label): @@ -103,9 +104,10 @@ def get_array_connection_info_from_secret_data(secret_data, labels): def _convert_secret_config_to_string(secret_data): - if settings.SECRET_CONFIG_FIELD in secret_data.keys(): - if type(secret_data[settings.SECRET_CONFIG_FIELD]) is dict: - secret_data[settings.SECRET_CONFIG_FIELD] = json.dumps(secret_data[settings.SECRET_CONFIG_FIELD]) + if common_settings.SECRET_CONFIG_FIELD in secret_data.keys(): + if type(secret_data[common_settings.SECRET_CONFIG_FIELD]) is dict: + secret_data[common_settings.SECRET_CONFIG_FIELD] = json.dumps( + secret_data[common_settings.SECRET_CONFIG_FIELD]) return secret_data @@ -137,18 +139,18 @@ def get_random_string(): def is_watch_object_type_is_delete(watch_object_type): - return watch_object_type == settings.DELETED_EVENT + return watch_object_type == common_settings.DELETED_EVENT_TYPE def is_host_definer_can_delete_hosts(): - return os.getenv(settings.ALLOW_DELETE_ENV_VAR) == settings.TRUE_STRING + return os.getenv(common_settings.ALLOW_DELETE_ENV_VAR) == common_settings.TRUE_STRING def is_dynamic_node_labeling_allowed(): - return os.getenv(settings.DYNAMIC_NODE_LABELING_ENV_VAR) == settings.TRUE_STRING + return os.getenv(common_settings.DYNAMIC_NODE_LABELING_ENV_VAR) == common_settings.TRUE_STRING def get_action(phase): - if phase == settings.PENDING_CREATION_PHASE: - return settings.DEFINE_ACTION - return settings.UNDEFINE_ACTION + if phase == common_settings.PENDING_CREATION_PHASE: + return common_settings.DEFINE_ACTION + return common_settings.UNDEFINE_ACTION diff --git a/controllers/servers/host_definer/watcher/csi_node_watcher.py b/controllers/servers/host_definer/watcher/csi_node_watcher.py index 236f669d4..262cb62c7 100644 --- a/controllers/servers/host_definer/watcher/csi_node_watcher.py +++ b/controllers/servers/host_definer/watcher/csi_node_watcher.py @@ -1,5 +1,6 @@ from threading import Thread +import controllers.common.settings as common_settings from controllers.common.csi_logger import get_stdout_logger from controllers.servers.host_definer.globals import MANAGED_SECRETS, NODES from controllers.servers.host_definer.watcher.watcher_helper import Watcher @@ -24,9 +25,9 @@ def watch_csi_nodes_resources(self): for watch_event in stream: watch_event = utils.munch(watch_event) csi_node_info = self.resource_info_manager.generate_csi_node_info(watch_event.object) - if (watch_event.type == settings.DELETED_EVENT) and (csi_node_info.name in NODES): + if (watch_event.type == common_settings.DELETED_EVENT_TYPE) and (csi_node_info.name in NODES): self._handle_deleted_csi_node_pod(csi_node_info) - elif watch_event.type == settings.MODIFIED_EVENT: + elif watch_event.type == common_settings.MODIFIED_EVENT_TYPE: self._handle_modified_csi_node(csi_node_info) def _handle_modified_csi_node(self, csi_node_info): diff --git a/controllers/servers/host_definer/watcher/host_definition_watcher.py b/controllers/servers/host_definer/watcher/host_definition_watcher.py index 7fd48084a..d8a73b6cb 100644 --- a/controllers/servers/host_definer/watcher/host_definition_watcher.py +++ b/controllers/servers/host_definer/watcher/host_definition_watcher.py @@ -1,6 +1,7 @@ from threading import Thread from time import sleep +import controllers.common.settings as common_settings from controllers.servers.host_definer.utils import utils import controllers.servers.host_definer.messages as messages from controllers.common.csi_logger import get_stdout_logger @@ -56,7 +57,7 @@ def _handle_pending_host_definition(self, host_definition_info): response = DefineHostResponse() phase = host_definition_info.phase action = utils.get_action(phase) - if phase == settings.PENDING_CREATION_PHASE: + if phase == common_settings.PENDING_CREATION_PHASE: response = self.definition_manager.define_host_after_pending(host_definition_info) elif self._is_pending_for_deletion_need_to_be_handled(phase, host_definition_info.node_name): response = self.definition_manager.undefine_host_after_pending(host_definition_info) @@ -66,13 +67,15 @@ def _handle_pending_host_definition(self, host_definition_info): def _handle_message_from_storage(self, host_definition_info, error_message, action): phase = host_definition_info.phase if error_message: - self.host_definition_manager.create_k8s_event_for_host_definition(host_definition_info, str(error_message), - action, settings.FAILED_MESSAGE_TYPE) - elif phase == settings.PENDING_CREATION_PHASE: + self.host_definition_manager.create_k8s_event_for_host_definition( + host_definition_info, str(error_message), + action, common_settings.FAILED_MESSAGE_TYPE) + elif phase == common_settings.PENDING_CREATION_PHASE: self.host_definition_manager.set_host_definition_status_to_ready(host_definition_info) elif self._is_pending_for_deletion_need_to_be_handled(phase, host_definition_info.node_name): self.host_definition_manager.delete_host_definition(host_definition_info.name) self.node_manager.remove_manage_node_label(host_definition_info.node_name) def _is_pending_for_deletion_need_to_be_handled(self, phase, node_name): - return phase == settings.PENDING_DELETION_PHASE and self.node_manager.is_node_can_be_undefined(node_name) + return phase == common_settings.PENDING_DELETION_PHASE and \ + self.node_manager.is_node_can_be_undefined(node_name) diff --git a/controllers/servers/host_definer/watcher/node_watcher.py b/controllers/servers/host_definer/watcher/node_watcher.py index acb503db2..9ff8975af 100644 --- a/controllers/servers/host_definer/watcher/node_watcher.py +++ b/controllers/servers/host_definer/watcher/node_watcher.py @@ -1,4 +1,5 @@ from controllers.common.csi_logger import get_stdout_logger +import controllers.common.settings as common_settings from controllers.servers.host_definer.watcher.watcher_helper import Watcher from controllers.servers.host_definer import settings from controllers.servers.host_definer.utils import utils @@ -51,7 +52,7 @@ def watch_nodes_resources(self): self.node_manager.update_node_io_group(node_info) def _add_new_unmanaged_nodes_with_ibm_csi_driver(self, watch_event, csi_node_info): - if watch_event.type in settings.MODIFIED_EVENT and \ + if watch_event.type in common_settings.MODIFIED_EVENT_TYPE and \ self._is_unmanaged_csi_node_has_driver(csi_node_info): logger.info(messages.DETECTED_UNMANAGED_CSI_NODE_WITH_IBM_BLOCK_CSI_DRIVER.format(csi_node_info.name)) unmanaged_csi_nodes_with_driver.add(csi_node_info.name) @@ -60,7 +61,7 @@ def _is_unmanaged_csi_node_has_driver(self, csi_node_info): return csi_node_info.node_id and not self.node_manager.is_node_can_be_defined(csi_node_info.name) def _define_new_managed_node(self, watch_event, node_name, csi_node_info): - if watch_event.type == settings.MODIFIED_EVENT and \ + if watch_event.type == common_settings.MODIFIED_EVENT_TYPE and \ self.node_manager.is_node_has_new_manage_node_label(csi_node_info, unmanaged_csi_nodes_with_driver): logger.info(messages.DETECTED_NEW_MANAGED_CSI_NODE.format(node_name)) self.node_manager.add_node_to_nodes(csi_node_info) diff --git a/controllers/servers/host_definer/watcher/storage_class_watcher.py b/controllers/servers/host_definer/watcher/storage_class_watcher.py index 89d79ab5f..584137fbc 100644 --- a/controllers/servers/host_definer/watcher/storage_class_watcher.py +++ b/controllers/servers/host_definer/watcher/storage_class_watcher.py @@ -1,8 +1,8 @@ +import controllers.common.settings as common_settings import controllers.servers.host_definer.messages as messages from controllers.common.csi_logger import get_stdout_logger from controllers.servers.host_definer.globals import MANAGED_SECRETS from controllers.servers.host_definer.watcher.watcher_helper import Watcher -from controllers.servers.host_definer import settings from controllers.servers.host_definer.utils import utils logger = get_stdout_logger() @@ -23,7 +23,7 @@ def watch_storage_class_resources(self): watch_event = utils.munch(watch_event) storage_class_info = self.resource_info_manager.generate_storage_class_info(watch_event.object) secrets_info = self._get_secrets_info_from_storage_class_with_driver_provisioner(storage_class_info) - if watch_event.type == settings.ADDED_EVENT: + if watch_event.type == common_settings.ADDED_EVENT_TYPE: self._handle_added_watch_event(secrets_info, storage_class_info.name) elif utils.is_watch_object_type_is_delete(watch_event.type): self._handle_deleted_watch_event(secrets_info) diff --git a/controllers/tests/controller_server/host_definer/definition_manager/request_manager_test.py b/controllers/tests/controller_server/host_definer/definition_manager/request_manager_test.py index c97dbed14..0fde1dd88 100644 --- a/controllers/tests/controller_server/host_definer/definition_manager/request_manager_test.py +++ b/controllers/tests/controller_server/host_definer/definition_manager/request_manager_test.py @@ -2,6 +2,7 @@ from copy import deepcopy from unittest.mock import MagicMock, patch +import controllers.common.settings as common_settings from controllers.servers.host_definer.k8s.api import K8SApi import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils import controllers.tests.controller_server.host_definer.settings as test_settings @@ -19,7 +20,7 @@ def setUp(self): test_settings.REQUEST_MANAGER_PATH) self.fake_host_definition_info = test_utils.get_fake_host_definition_info() self.fake_node_info = test_utils.get_fake_node_info() - self.fake_node_info.labels[test_settings.CONNECTIVITY_TYPE_LABEL] = test_settings.ISCSI_CONNECTIVITY_TYPE + self.fake_node_info.labels[common_settings.CONNECTIVITY_TYPE_LABEL] = test_settings.ISCSI_CONNECTIVITY_TYPE self.array_connection_info = test_utils.get_array_connection_info() self.define_request = test_utils.get_define_request( test_settings.FAKE_PREFIX, test_settings.ISCSI_CONNECTIVITY_TYPE, test_settings.FAKE_NODE_ID) diff --git a/controllers/tests/controller_server/host_definer/host_definer_utils_tests/manifest_utils_test.py b/controllers/tests/controller_server/host_definer/host_definer_utils_tests/manifest_utils_test.py index bcfcdd618..73b6d2ae9 100644 --- a/controllers/tests/controller_server/host_definer/host_definer_utils_tests/manifest_utils_test.py +++ b/controllers/tests/controller_server/host_definer/host_definer_utils_tests/manifest_utils_test.py @@ -21,35 +21,36 @@ def test_get_host_definition_manifest_success(self): test_manifest_utils.get_fake_k8s_host_definition_response_fields_manifest() result = manifest_utils.get_host_definition_manifest( self.fake_host_definition_info, self.define_response, test_settings.FAKE_NODE_ID) - self.assertEqual(result[test_settings.SPEC_FIELD], expected_manifest[test_settings.SPEC_FIELD]) - self.assertEqual(result[test_settings.API_VERSION_FIELD], expected_manifest[test_settings.API_VERSION_FIELD]) - self.assertEqual(result[test_settings.KIND_FIELD], expected_manifest[test_settings.KIND_FIELD]) - self.assertEqual(result[test_settings.METADATA_FIELD][common_settings.NAME_FIELD], + self.assertEqual(result[common_settings.SPEC_FIELD], expected_manifest[common_settings.SPEC_FIELD]) + self.assertEqual(result[common_settings.API_VERSION_FIELD], + expected_manifest[common_settings.API_VERSION_FIELD]) + self.assertEqual(result[common_settings.KIND_FIELD], expected_manifest[common_settings.KIND_FIELD]) + self.assertEqual(result[common_settings.METADATA_FIELD][common_settings.NAME_FIELD], test_settings.FAKE_NODE_NAME) mock_generate_host_definition_response_fields_manifest.assert_called_once_with( self.fake_host_definition_info.name, self.define_response) def test_get_host_definition_status_manifest_success(self): - fake_status_phase_manifest = test_manifest_utils.get_status_phase_manifest(test_settings.READY_PHASE) - result = manifest_utils.get_host_definition_status_manifest(test_settings.READY_PHASE) + fake_status_phase_manifest = test_manifest_utils.get_status_phase_manifest(common_settings.READY_PHASE) + result = manifest_utils.get_host_definition_status_manifest(common_settings.READY_PHASE) self.assertEqual(result, fake_status_phase_manifest) def test_get_body_manifest_for_labels_success(self): fake_labels_body = test_manifest_utils.get_metadata_with_manage_node_labels_manifest( - test_settings.TRUE_STRING) - result = manifest_utils.get_body_manifest_for_labels(test_settings.TRUE_STRING) + common_settings.TRUE_STRING) + result = manifest_utils.get_body_manifest_for_labels(common_settings.TRUE_STRING) self.assertEqual(result, fake_labels_body) def test_get_finalizer_manifest_success(self): - fake_finalizers_manifest = test_manifest_utils.get_finalizers_manifest([test_settings.CSI_IBM_FINALIZER, ]) + fake_finalizers_manifest = test_manifest_utils.get_finalizers_manifest([common_settings.CSI_IBM_FINALIZER, ]) result = manifest_utils.get_finalizer_manifest( - test_settings.FAKE_NODE_NAME, [test_settings.CSI_IBM_FINALIZER, ]) + test_settings.FAKE_NODE_NAME, [common_settings.CSI_IBM_FINALIZER, ]) self.assertEqual(result, fake_finalizers_manifest) def test_generate_host_definition_response_fields_manifest_success(self): expected_manifest = test_manifest_utils.get_fake_k8s_host_definition_response_fields_manifest() result = manifest_utils.generate_host_definition_response_fields_manifest( self.fake_host_definition_info.name, self.define_response) - self.assertEqual(result[test_settings.SPEC_FIELD], expected_manifest[test_settings.SPEC_FIELD]) - self.assertEqual(result[test_settings.METADATA_FIELD][common_settings.NAME_FIELD], + self.assertEqual(result[common_settings.SPEC_FIELD], expected_manifest[common_settings.SPEC_FIELD]) + self.assertEqual(result[common_settings.METADATA_FIELD][common_settings.NAME_FIELD], test_settings.FAKE_NODE_NAME) diff --git a/controllers/tests/controller_server/host_definer/host_definer_utils_tests/utils_test.py b/controllers/tests/controller_server/host_definer/host_definer_utils_tests/utils_test.py index 3a9c30048..4dfa2bbde 100644 --- a/controllers/tests/controller_server/host_definer/host_definer_utils_tests/utils_test.py +++ b/controllers/tests/controller_server/host_definer/host_definer_utils_tests/utils_test.py @@ -42,34 +42,34 @@ def test_get_k8s_object_resource_version_with_not_default_resource_version_field def test_get_secret_config_encoded_in_base64_success(self, mock_decode_base64_to_string): secret_data = deepcopy(test_settings.FAKE_ENCODED_CONFIG) mock_decode_base64_to_string.return_value = test_settings.FAKE_DECODED_CONFIG_STRING[ - test_settings.CONFIG_FIELD] + common_settings.SECRET_CONFIG_FIELD] result = utils.change_decode_base64_secret_config(secret_data) self.assertEqual(result, test_settings.FAKE_DECODED_CONFIG) mock_decode_base64_to_string.assert_called_once_with( - test_settings.FAKE_ENCODED_CONFIG[test_settings.CONFIG_FIELD]) + test_settings.FAKE_ENCODED_CONFIG[common_settings.SECRET_CONFIG_FIELD]) @patch('{}.decode_base64_to_string'.format(test_settings.UTILS_PATH)) def test_get_decoded_secret_not_encoded_success(self, mock_decode_base64_to_string): secret_data = deepcopy(test_settings.FAKE_DECODED_CONFIG_STRING) mock_decode_base64_to_string.return_value = test_settings.FAKE_DECODED_CONFIG_STRING[ - test_settings.CONFIG_FIELD] + common_settings.SECRET_CONFIG_FIELD] result = utils.change_decode_base64_secret_config(secret_data) self.assertEqual(result, test_settings.FAKE_DECODED_CONFIG) mock_decode_base64_to_string.assert_called_once_with( - test_settings.FAKE_DECODED_CONFIG_STRING[test_settings.CONFIG_FIELD]) + test_settings.FAKE_DECODED_CONFIG_STRING[common_settings.SECRET_CONFIG_FIELD]) def test_get_secret_config_success(self): secret_data = deepcopy(test_settings.FAKE_DECODED_CONFIG_STRING) - self.mock_json.loads.return_value = test_settings.FAKE_DECODED_CONFIG[test_settings.CONFIG_FIELD] + self.mock_json.loads.return_value = test_settings.FAKE_DECODED_CONFIG[common_settings.SECRET_CONFIG_FIELD] result = utils.get_secret_config(secret_data) - self.assertEqual(result, test_settings.FAKE_DECODED_CONFIG[test_settings.CONFIG_FIELD]) + self.assertEqual(result, test_settings.FAKE_DECODED_CONFIG[common_settings.SECRET_CONFIG_FIELD]) self.mock_json.loads.assert_called_once_with( - test_settings.FAKE_DECODED_CONFIG_STRING[test_settings.CONFIG_FIELD]) + test_settings.FAKE_DECODED_CONFIG_STRING[common_settings.SECRET_CONFIG_FIELD]) def test_do_not_call_json_load_when_getting_dict_secret_config_success(self): secret_data = deepcopy(test_settings.FAKE_DECODED_CONFIG) result = utils.get_secret_config(secret_data) - self.assertEqual(result, test_settings.FAKE_DECODED_CONFIG[test_settings.CONFIG_FIELD]) + self.assertEqual(result, test_settings.FAKE_DECODED_CONFIG[common_settings.SECRET_CONFIG_FIELD]) self.mock_json.loads.assert_not_called() def test_get_secret_config_from_secret_data_with_no_config_field_success(self): @@ -88,10 +88,12 @@ def test_loop_forever_success(self): def test_validate_secret_success(self): secret_data = deepcopy(test_settings.FAKE_DECODED_CONFIG) - self.mock_json.dumps.return_value = test_settings.FAKE_DECODED_CONFIG_STRING[test_settings.CONFIG_FIELD] + self.mock_json.dumps.return_value = test_settings.FAKE_DECODED_CONFIG_STRING[ + common_settings.SECRET_CONFIG_FIELD] utils.validate_secret(secret_data) self.mock_validate_secrets.assert_called_once_with(test_settings.FAKE_DECODED_CONFIG_STRING) - self.mock_json.dumps.assert_called_once_with(test_settings.FAKE_DECODED_CONFIG[test_settings.CONFIG_FIELD]) + self.mock_json.dumps.assert_called_once_with( + test_settings.FAKE_DECODED_CONFIG[common_settings.SECRET_CONFIG_FIELD]) def test_do_not_call_json_dumps_when_getting_string_secret_config_on_validate_secret_success(self): secret_data = deepcopy(test_settings.FAKE_DECODED_CONFIG_STRING) @@ -113,10 +115,10 @@ def test_validate_secret_handle_validation_error_success(self): self.mock_json.dumps.assert_not_called() def test_get_prefix_success(self): - self.mock_os.getenv.return_value = test_settings.TRUE_STRING + self.mock_os.getenv.return_value = common_settings.TRUE_STRING result = utils.get_prefix() - self.assertEqual(result, test_settings.TRUE_STRING) - self.mock_os.getenv.assert_called_once_with(test_settings.PREFIX_ENV_VAR) + self.assertEqual(result, common_settings.TRUE_STRING) + self.mock_os.getenv.assert_called_once_with(common_settings.PREFIX_ENV_VAR) def test_get_connectivity_type_when_it_set_in_the_labels_success(self): result = utils.get_connectivity_type_from_user(test_settings.ISCSI_CONNECTIVITY_TYPE) @@ -127,7 +129,7 @@ def test_get_connectivity_type_when_it_set_in_the_env_vars_success(self): self.mock_os.getenv.return_value = test_settings.ISCSI_CONNECTIVITY_TYPE result = utils.get_connectivity_type_from_user('') self.assertEqual(result, test_settings.ISCSI_CONNECTIVITY_TYPE) - self.mock_os.getenv.assert_called_once_with(test_settings.CONNECTIVITY_ENV_VAR) + self.mock_os.getenv.assert_called_once_with(common_settings.CONNECTIVITY_ENV_VAR) def test_is_topology_label_true_success(self): result = utils.is_topology_label(test_settings.FAKE_TOPOLOGY_LABEL) @@ -141,14 +143,16 @@ def test_is_topology_label_false_success(self): def test_get_array_connectivity_info_from_secret_config_success(self, mock_decode): connectivity_info = 'connectivity_info' secret_data = deepcopy(test_settings.FAKE_DECODED_CONFIG) - self.mock_json.dumps.return_value = test_settings.FAKE_DECODED_CONFIG_STRING[test_settings.CONFIG_FIELD] + self.mock_json.dumps.return_value = test_settings.FAKE_DECODED_CONFIG_STRING[ + common_settings.SECRET_CONFIG_FIELD] self.mock_get_array_connectivity_info.return_value = connectivity_info mock_decode.return_value = connectivity_info result = utils.get_array_connection_info_from_secret_data(secret_data, []) self.assertEqual(result, connectivity_info) self.mock_get_array_connectivity_info.assert_called_once_with(secret_data, []) mock_decode.assert_called_once_with(connectivity_info) - self.mock_json.dumps.assert_called_once_with(test_settings.FAKE_DECODED_CONFIG[test_settings.CONFIG_FIELD]) + self.mock_json.dumps.assert_called_once_with( + test_settings.FAKE_DECODED_CONFIG[common_settings.SECRET_CONFIG_FIELD]) @patch('{}.decode_array_connectivity_info'.format(test_settings.UTILS_PATH)) def test_do_not_call_json_dumps_when_getting_string_secret_config_on_get_array_info_success(self, mock_decode): @@ -204,41 +208,41 @@ def test_get_random_string_success(self): self.assertEqual(len(result), 20) def test_return_true_when_watch_object_is_deleted(self): - result = utils.is_watch_object_type_is_delete(test_settings.DELETED_EVENT_TYPE) + result = utils.is_watch_object_type_is_delete(common_settings.DELETED_EVENT_TYPE) self.assertTrue(result) def test_return_false_when_watch_object_is_not_deleted(self): - result = utils.is_watch_object_type_is_delete(test_settings.ADDED_EVENT) + result = utils.is_watch_object_type_is_delete(common_settings.ADDED_EVENT_TYPE) self.assertFalse(result) def test_return_true_when_host_definer_can_delete_hosts_success(self): - self.mock_os.getenv.return_value = test_settings.TRUE_STRING + self.mock_os.getenv.return_value = common_settings.TRUE_STRING result = utils.is_host_definer_can_delete_hosts() self.assertTrue(result) - self.mock_os.getenv.assert_called_once_with(test_settings.ALLOW_DELETE_ENV_VAR) + self.mock_os.getenv.assert_called_once_with(common_settings.ALLOW_DELETE_ENV_VAR) def test_return_false_when_host_definer_cannot_delete_hosts_success(self): self.mock_os.getenv.return_value = '' result = utils.is_host_definer_can_delete_hosts() self.assertFalse(result) - self.mock_os.getenv.assert_called_once_with(test_settings.ALLOW_DELETE_ENV_VAR) + self.mock_os.getenv.assert_called_once_with(common_settings.ALLOW_DELETE_ENV_VAR) def test_return_true_when_dynamic_node_labeling_allowed_success(self): - self.mock_os.getenv.return_value = test_settings.TRUE_STRING + self.mock_os.getenv.return_value = common_settings.TRUE_STRING result = utils.is_dynamic_node_labeling_allowed() self.assertTrue(result) - self.mock_os.getenv.assert_called_once_with(test_settings.DYNAMIC_NODE_LABELING_ENV_VAR) + self.mock_os.getenv.assert_called_once_with(common_settings.DYNAMIC_NODE_LABELING_ENV_VAR) def test_return_false_when_dynamic_node_labeling_is_not_allowed_success(self): self.mock_os.getenv.return_value = '' result = utils.is_dynamic_node_labeling_allowed() self.assertFalse(result) - self.mock_os.getenv.assert_called_once_with(test_settings.DYNAMIC_NODE_LABELING_ENV_VAR) + self.mock_os.getenv.assert_called_once_with(common_settings.DYNAMIC_NODE_LABELING_ENV_VAR) def test_get_define_action_when_phase_is_pending_creation(self): - result = utils.get_action(test_settings.PENDING_CREATION_PHASE) - self.assertEqual(result, test_settings.DEFINE_ACTION) + result = utils.get_action(common_settings.PENDING_CREATION_PHASE) + self.assertEqual(result, common_settings.DEFINE_ACTION) def test_get_undefine_action_when_phase_is_not_pending_creation(self): - result = utils.get_action(test_settings.PENDING_DELETION_PHASE) - self.assertEqual(result, test_settings.UNDEFINE_ACTION) + result = utils.get_action(common_settings.PENDING_DELETION_PHASE) + self.assertEqual(result, common_settings.UNDEFINE_ACTION) diff --git a/controllers/tests/controller_server/host_definer/k8s/kubernetes_api_test.py b/controllers/tests/controller_server/host_definer/k8s/kubernetes_api_test.py index 69d34dfae..fd3903524 100644 --- a/controllers/tests/controller_server/host_definer/k8s/kubernetes_api_test.py +++ b/controllers/tests/controller_server/host_definer/k8s/kubernetes_api_test.py @@ -65,11 +65,11 @@ def test_patch_cluster_custom_object_status_success(self): self.k8s_api.csi_nodes_api.get.return_value = None self.k8s_api.patch_cluster_custom_object_status( common_settings.CSI_IBM_GROUP, common_settings.VERSION, common_settings.HOST_DEFINITION_PLURAL, - test_settings.FAKE_NODE_NAME, test_settings.READY_PHASE) + test_settings.FAKE_NODE_NAME, common_settings.READY_PHASE) self.k8s_api.custom_object_api.patch_cluster_custom_object_status.assert_called_with( common_settings.CSI_IBM_GROUP, common_settings.VERSION, common_settings.HOST_DEFINITION_PLURAL, test_settings.FAKE_NODE_NAME, - test_settings.READY_PHASE) + common_settings.READY_PHASE) def test_create_event_success(self): self.k8s_api.core_api.create_namespaced_event.return_value = None @@ -104,10 +104,10 @@ def test_patch_host_definition_failure(self): def test_patch_node_success(self): self.k8s_api.core_api.patch_node.return_value = None self.k8s_api.patch_node(test_settings.FAKE_NODE_NAME, test_manifest_utils.get_fake_k8s_node_manifest( - test_settings.MANAGE_NODE_LABEL)) + common_settings.MANAGE_NODE_LABEL)) self.k8s_api.core_api.patch_node.assert_called_once_with( test_settings.FAKE_NODE_NAME, test_manifest_utils.get_fake_k8s_node_manifest( - test_settings.MANAGE_NODE_LABEL)) + common_settings.MANAGE_NODE_LABEL)) def test_get_secret_data_success(self): self.k8s_api.core_api.read_namespaced_secret.return_value = test_utils.get_fake_k8s_secret() @@ -122,9 +122,9 @@ def test_get_secret_data_failure(self): self.assertEqual(result, {}) def test_read_node_success(self): - self.k8s_api.core_api.read_node.return_value = test_utils.get_fake_k8s_node(test_settings.MANAGE_NODE_LABEL) + self.k8s_api.core_api.read_node.return_value = test_utils.get_fake_k8s_node(common_settings.MANAGE_NODE_LABEL) result = self.k8s_api.read_node(test_settings.FAKE_NODE_NAME) - self.assertEqual(result, test_utils.get_fake_k8s_node(test_settings.MANAGE_NODE_LABEL)) + self.assertEqual(result, test_utils.get_fake_k8s_node(common_settings.MANAGE_NODE_LABEL)) self.k8s_api.core_api.read_node.assert_called_once_with(name=test_settings.FAKE_NODE_NAME) def test_read_node_failure(self): @@ -135,10 +135,10 @@ def test_read_node_failure(self): def test_list_daemon_set_for_all_namespaces_success(self): self.k8s_api.apps_api.list_daemon_set_for_all_namespaces.return_value = \ test_utils.get_fake_k8s_daemon_set_items(0, 0) - result = self.k8s_api.list_daemon_set_for_all_namespaces(test_settings.MANAGE_NODE_LABEL) + result = self.k8s_api.list_daemon_set_for_all_namespaces(common_settings.MANAGE_NODE_LABEL) self.assertEqual(result, test_utils.get_fake_k8s_daemon_set_items(0, 0)) self.k8s_api.apps_api.list_daemon_set_for_all_namespaces.assert_called_once_with( - label_selector=test_settings.MANAGE_NODE_LABEL) + label_selector=common_settings.MANAGE_NODE_LABEL) def test_list_daemon_set_for_all_namespaces_failure(self): self.k8s_api.apps_api.list_daemon_set_for_all_namespaces.side_effect = self.general_api_exception @@ -148,10 +148,10 @@ def test_list_daemon_set_for_all_namespaces_failure(self): def test_list_pod_for_all_namespaces_success(self): self.k8s_api.core_api.list_pod_for_all_namespaces.return_value = \ test_utils.get_fake_k8s_daemon_set_items(0, 0) - result = self.k8s_api.list_pod_for_all_namespaces(test_settings.MANAGE_NODE_LABEL) + result = self.k8s_api.list_pod_for_all_namespaces(common_settings.MANAGE_NODE_LABEL) self.assertEqual(result, test_utils.get_fake_k8s_daemon_set_items(0, 0)) self.k8s_api.core_api.list_pod_for_all_namespaces.assert_called_once_with( - label_selector=test_settings.MANAGE_NODE_LABEL) + label_selector=common_settings.MANAGE_NODE_LABEL) def test_list_pod_for_all_namespaces_failure(self): self.k8s_api.core_api.list_pod_for_all_namespaces.side_effect = self.general_api_exception @@ -198,9 +198,9 @@ def _test_basic_resource_stream_failure(self, function_to_test): def test_list_storage_class_success(self): self.k8s_api.storage_api.list_storage_class.return_value = \ - test_utils.get_fake_k8s_storage_class_items(test_settings.CSI_PROVISIONER_NAME) + test_utils.get_fake_k8s_storage_class_items(common_settings.CSI_PROVISIONER_NAME) result = self.k8s_api.list_storage_class() - self.assertEqual(result, test_utils.get_fake_k8s_storage_class_items(test_settings.CSI_PROVISIONER_NAME)) + self.assertEqual(result, test_utils.get_fake_k8s_storage_class_items(common_settings.CSI_PROVISIONER_NAME)) def test_list_storage_class_failure(self): self._test_list_k8s_resource_failure(self.k8s_api.list_storage_class, @@ -216,9 +216,9 @@ def test_list_node_failure(self): def test_list_csi_node_success(self): self.k8s_api.csi_nodes_api.get.return_value = test_utils.get_fake_k8s_csi_node( - test_settings.CSI_PROVISIONER_NAME) + common_settings.CSI_PROVISIONER_NAME) result = self.k8s_api.list_csi_node() - self.assertEqual(result, test_utils.get_fake_k8s_csi_node(test_settings.CSI_PROVISIONER_NAME)) + self.assertEqual(result, test_utils.get_fake_k8s_csi_node(common_settings.CSI_PROVISIONER_NAME)) def test_list_csi_node_failure(self): self._test_list_k8s_resource_failure(self.k8s_api.list_csi_node, self.k8s_api.csi_nodes_api.get) diff --git a/controllers/tests/controller_server/host_definer/resource_manager/csi_node_manager_test.py b/controllers/tests/controller_server/host_definer/resource_manager/csi_node_manager_test.py index 45575a905..c57b0e5ba 100644 --- a/controllers/tests/controller_server/host_definer/resource_manager/csi_node_manager_test.py +++ b/controllers/tests/controller_server/host_definer/resource_manager/csi_node_manager_test.py @@ -5,6 +5,7 @@ from controllers.servers.host_definer.resource_manager.csi_node import CSINodeManager import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils import controllers.tests.controller_server.host_definer.settings as test_settings +import controllers.common.settings as common_settings class TestCSINodeManager(BaseResourceManager): @@ -17,7 +18,7 @@ def setUp(self): self.fake_csi_node_info = test_utils.get_fake_csi_node_info() self.fake_pod_info = test_utils.get_fake_pod_info() self.fake_k8s_csi_nodes_with_ibm_driver = test_utils.get_fake_k8s_csi_nodes( - test_settings.CSI_PROVISIONER_NAME, 1) + common_settings.CSI_PROVISIONER_NAME, 1) self.fake_k8s_csi_nodes_with_non_ibm_driver = test_utils.get_fake_k8s_csi_nodes( test_settings.FAKE_CSI_PROVISIONER, 1) diff --git a/controllers/tests/controller_server/host_definer/resource_manager/daemon_set_manager_test.py b/controllers/tests/controller_server/host_definer/resource_manager/daemon_set_manager_test.py index a74ca3940..f32dc16da 100644 --- a/controllers/tests/controller_server/host_definer/resource_manager/daemon_set_manager_test.py +++ b/controllers/tests/controller_server/host_definer/resource_manager/daemon_set_manager_test.py @@ -1,8 +1,8 @@ from unittest.mock import MagicMock +import controllers.common.settings as common_settings from controllers.servers.host_definer.resource_manager.daemon_set import DaemonSetManager import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils -import controllers.tests.controller_server.host_definer.settings as test_settings from controllers.tests.controller_server.host_definer.resource_manager.base_resource_manager import BaseResourceManager @@ -27,7 +27,7 @@ def _test_wait_for_updated_daemon_set_called_once(self, daemon_set, expected_res result = self.daemon_set_manager.wait_until_all_daemon_set_pods_are_up_to_date() self.assertEqual(result, expected_result) self.daemon_set_manager.k8s_api.list_daemon_set_for_all_namespaces.assert_called_once_with( - test_settings.DRIVER_PRODUCT_LABEL) + common_settings.DRIVER_PRODUCT_LABEL) def test_get_not_updated_daemon_and_wait_until_it_will_be_updated(self): daemon_set_name = self.fake_updated_daemon_set.metadata.name diff --git a/controllers/tests/controller_server/host_definer/resource_manager/event_manager_test.py b/controllers/tests/controller_server/host_definer/resource_manager/event_manager_test.py index 533cfdc30..4383c8fd4 100644 --- a/controllers/tests/controller_server/host_definer/resource_manager/event_manager_test.py +++ b/controllers/tests/controller_server/host_definer/resource_manager/event_manager_test.py @@ -1,3 +1,4 @@ +import controllers.common.settings as common_settings from controllers.tests.controller_server.host_definer.resource_manager.base_resource_manager import BaseResourceManager from controllers.servers.host_definer.resource_manager.event import EventManager import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils @@ -10,20 +11,21 @@ def setUp(self): self.fake_host_definition_info = test_utils.get_fake_host_definition_info() def test_generate_k8s_normal_event_success(self): - self._test_generate_k8s_event_success(test_settings.SUCCESSFUL_MESSAGE_TYPE, test_settings.NORMAL_EVENT_TYPE) + self._test_generate_k8s_event_success(common_settings.SUCCESSFUL_MESSAGE_TYPE, + common_settings.NORMAL_EVENT_TYPE) def test_generate_k8s_warning_event_success(self): - self._test_generate_k8s_event_success('unsuccessful message type', test_settings.WARNING_EVENT_TYPE) + self._test_generate_k8s_event_success('unsuccessful message type', common_settings.WARNING_EVENT_TYPE) def _test_generate_k8s_event_success(self, message_type, expected_event_type): result = self.event_manager.generate_k8s_event( self.fake_host_definition_info, test_settings.MESSAGE, - test_settings.DEFINE_ACTION, message_type) + common_settings.DEFINE_ACTION, message_type) self.assertEqual(result.metadata, test_utils.get_event_object_metadata()) - self.assertEqual(result.reporting_component, test_settings.HOST_DEFINER) - self.assertEqual(result.reporting_instance, test_settings.HOST_DEFINER) - self.assertEqual(result.action, test_settings.DEFINE_ACTION) + self.assertEqual(result.reporting_component, common_settings.HOST_DEFINER) + self.assertEqual(result.reporting_instance, common_settings.HOST_DEFINER) + self.assertEqual(result.action, common_settings.DEFINE_ACTION) self.assertEqual(result.type, expected_event_type) - self.assertEqual(result.reason, message_type + test_settings.DEFINE_ACTION) + self.assertEqual(result.reason, message_type + common_settings.DEFINE_ACTION) self.assertEqual(result.message, test_settings.MESSAGE) self.assertEqual(result.involved_object, test_utils.get_object_reference()) diff --git a/controllers/tests/controller_server/host_definer/resource_manager/host_definition_manager_test.py b/controllers/tests/controller_server/host_definer/resource_manager/host_definition_manager_test.py index f7faacab2..573dca516 100644 --- a/controllers/tests/controller_server/host_definer/resource_manager/host_definition_manager_test.py +++ b/controllers/tests/controller_server/host_definer/resource_manager/host_definition_manager_test.py @@ -1,9 +1,9 @@ from copy import deepcopy from unittest.mock import MagicMock, Mock, patch +import controllers.common.settings as common_settings from controllers.tests.controller_server.host_definer.resource_manager.base_resource_manager import BaseResourceManager from controllers.servers.host_definer.resource_manager.host_definition import HostDefinitionManager -import controllers.common.settings as common_settings import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils import controllers.tests.controller_server.host_definer.utils.k8s_manifests_utils as test_manifest_utils import controllers.tests.controller_server.host_definer.settings as test_settings @@ -68,9 +68,9 @@ def test_get_none_when_host_definition_list_empty_success(self): @patch('{}.manifest_utils'.format(test_settings.HOST_DEFINITION_MANAGER_PATH)) def test_create_host_definition_success(self, mock_manifest): - finalizers_manifest = test_manifest_utils.get_finalizers_manifest([test_settings.CSI_IBM_FINALIZER, ]) + finalizers_manifest = test_manifest_utils.get_finalizers_manifest([common_settings.CSI_IBM_FINALIZER, ]) self.host_definition_manager.k8s_api.create_host_definition.return_value = \ - test_utils.get_fake_k8s_host_definition(test_settings.READY_PHASE) + test_utils.get_fake_k8s_host_definition(common_settings.READY_PHASE) mock_manifest.get_finalizer_manifest.return_value = finalizers_manifest self.host_definition_manager.resource_info_manager.generate_host_definition_info.return_value = \ self.fake_host_definition_info @@ -78,9 +78,9 @@ def test_create_host_definition_success(self, mock_manifest): test_manifest_utils.get_fake_k8s_host_definition_manifest()) self.assertEqual(result, self.fake_host_definition_info) self.host_definition_manager.resource_info_manager.generate_host_definition_info.assert_called_once_with( - test_utils.get_fake_k8s_host_definition(test_settings.READY_PHASE)) + test_utils.get_fake_k8s_host_definition(common_settings.READY_PHASE)) mock_manifest.get_finalizer_manifest.assert_called_once_with( - test_settings.FAKE_NODE_NAME, [test_settings.CSI_IBM_FINALIZER, ]) + test_settings.FAKE_NODE_NAME, [common_settings.CSI_IBM_FINALIZER, ]) self.host_definition_manager.k8s_api.patch_host_definition.assert_called_once_with(finalizers_manifest) self.host_definition_manager.k8s_api.create_host_definition.assert_called_once_with( test_manifest_utils.get_fake_k8s_host_definition_manifest()) @@ -120,10 +120,11 @@ def _test_delete_host_definition(self, finalizers_status_code, mock_manifest_uti @patch('{}.manifest_utils'.format(test_settings.HOST_DEFINITION_MANAGER_PATH)) def test_set_host_definition_status_success(self, mock_manifest_utils): - status_phase_manifest = test_manifest_utils.get_status_phase_manifest(test_settings.READY_PHASE) + status_phase_manifest = test_manifest_utils.get_status_phase_manifest(common_settings.READY_PHASE) mock_manifest_utils.get_host_definition_status_manifest.return_value = status_phase_manifest - self.host_definition_manager.set_host_definition_status(test_settings.FAKE_NODE_NAME, test_settings.READY_PHASE) - mock_manifest_utils.get_host_definition_status_manifest.assert_called_once_with(test_settings.READY_PHASE) + self.host_definition_manager.set_host_definition_status( + test_settings.FAKE_NODE_NAME, common_settings.READY_PHASE) + mock_manifest_utils.get_host_definition_status_manifest.assert_called_once_with(common_settings.READY_PHASE) self.host_definition_manager.k8s_api.patch_cluster_custom_object_status.assert_called_once_with( common_settings.CSI_IBM_GROUP, common_settings.VERSION, common_settings.HOST_DEFINITION_PLURAL, test_settings.FAKE_NODE_NAME, status_phase_manifest) @@ -174,7 +175,7 @@ def _test_update_host_definition_info(self, matching_host_definition_info): def test_create_exist_host_definition_success(self, mock_manifest_utils): host_definition_manifest = self._test_create_host_definition_if_not_exist( 'different_name', self.fake_host_definition_info, None, mock_manifest_utils) - host_definition_manifest[test_settings.METADATA_FIELD][common_settings.NAME_FIELD] = \ + host_definition_manifest[common_settings.METADATA_FIELD][common_settings.NAME_FIELD] = \ test_settings.FAKE_NODE_NAME self.host_definition_manager.k8s_api.patch_host_definition.assert_called_once_with(host_definition_manifest) self.host_definition_manager.create_host_definition.assert_not_called() @@ -189,7 +190,7 @@ def test_create_new_host_definition_success(self, mock_manifest_utils): def _test_create_host_definition_if_not_exist(self, new_host_definition_name, matching_host_definition, created_host_definition, mock_manifest_utils): host_definition_manifest = deepcopy(test_manifest_utils.get_fake_k8s_host_definition_manifest()) - host_definition_manifest[test_settings.METADATA_FIELD][common_settings.NAME_FIELD] = new_host_definition_name + host_definition_manifest[common_settings.METADATA_FIELD][common_settings.NAME_FIELD] = new_host_definition_name mock_manifest_utils.get_host_definition_manifest.return_value = host_definition_manifest self._prepare_get_matching_host_definition_info_function_as_mock(matching_host_definition) self.host_definition_manager.create_host_definition = Mock() @@ -208,30 +209,30 @@ def test_set_host_definition_status_to_ready_success(self): self.host_definition_manager.create_k8s_event_for_host_definition = Mock() self.host_definition_manager.set_host_definition_status_to_ready(self.fake_host_definition_info) self.host_definition_manager.set_host_definition_status.assert_called_once_with( - self.fake_host_definition_info.name, test_settings.READY_PHASE) + self.fake_host_definition_info.name, common_settings.READY_PHASE) self.host_definition_manager.create_k8s_event_for_host_definition.assert_called_once_with( self.fake_host_definition_info, test_settings.MESSAGE, - test_settings.DEFINE_ACTION, test_settings.SUCCESSFUL_MESSAGE_TYPE) + common_settings.DEFINE_ACTION, common_settings.SUCCESSFUL_MESSAGE_TYPE) def test_create_k8s_event_for_host_definition_success(self): k8s_event = test_utils.get_event_object_metadata() self.host_definition_manager.event_manager.generate_k8s_event.return_value = k8s_event self.host_definition_manager.create_k8s_event_for_host_definition( self.fake_host_definition_info, test_settings.MESSAGE, - test_settings.DEFINE_ACTION, test_settings.SUCCESSFUL_MESSAGE_TYPE) + common_settings.DEFINE_ACTION, common_settings.SUCCESSFUL_MESSAGE_TYPE) self.host_definition_manager.event_manager.generate_k8s_event.assert_called_once_with( self.fake_host_definition_info, test_settings.MESSAGE, - test_settings.DEFINE_ACTION, test_settings.SUCCESSFUL_MESSAGE_TYPE) - self.host_definition_manager.k8s_api.create_event.assert_called_once_with(test_settings.DEFAULT_NAMESPACE, + common_settings.DEFINE_ACTION, common_settings.SUCCESSFUL_MESSAGE_TYPE) + self.host_definition_manager.k8s_api.create_event.assert_called_once_with(common_settings.DEFAULT_NAMESPACE, k8s_event) def test_set_host_definition_status_to_pending_and_create_event_after_failed_definition(self): self._prepare_set_status_to_host_definition_after_definition(test_settings.MESSAGE) self.host_definition_manager.set_host_definition_status.assert_called_once_with( - self.fake_host_definition_info.name, test_settings.PENDING_CREATION_PHASE) + self.fake_host_definition_info.name, common_settings.PENDING_CREATION_PHASE) self.host_definition_manager.create_k8s_event_for_host_definition.assert_called_once_with( self.fake_host_definition_info, test_settings.MESSAGE, - test_settings.DEFINE_ACTION, test_settings.FAILED_MESSAGE_TYPE) + common_settings.DEFINE_ACTION, common_settings.FAILED_MESSAGE_TYPE) self.host_definition_manager.set_host_definition_status_to_ready.assert_not_called() def test_set_host_definition_status_to_ready_after_successful_definition(self): @@ -252,10 +253,10 @@ def test_handle_host_definition_after_failed_undefine_action_and_when_host_defin self._test_handle_k8s_host_definition_after_undefine_action_if_exist( self.fake_host_definition_info, self.define_response) self.host_definition_manager.set_host_definition_status.assert_called_once_with( - self.fake_host_definition_info.name, test_settings.PENDING_DELETION_PHASE) + self.fake_host_definition_info.name, common_settings.PENDING_DELETION_PHASE) self.host_definition_manager.create_k8s_event_for_host_definition.assert_called_once_with( - self.fake_host_definition_info, self.define_response.error_message, test_settings.UNDEFINE_ACTION, - test_settings.FAILED_MESSAGE_TYPE) + self.fake_host_definition_info, self.define_response.error_message, common_settings.UNDEFINE_ACTION, + common_settings.FAILED_MESSAGE_TYPE) self.host_definition_manager.delete_host_definition.assert_not_called() def test_handle_host_definition_after_successful_undefine_action_and_when_host_definition_exist(self): @@ -285,18 +286,19 @@ def _test_handle_k8s_host_definition_after_undefine_action_if_exist( self._assert_get_matching_host_definition_called_once_with() def test_return_true_when_host_definition_phase_is_pending(self): - result = self.host_definition_manager.is_host_definition_in_pending_phase(test_settings.PENDING_CREATION_PHASE) + result = self.host_definition_manager.is_host_definition_in_pending_phase( + common_settings.PENDING_CREATION_PHASE) self.assertTrue(result) def test_return_false_when_host_definition_phase_is_not_pending(self): - result = self.host_definition_manager.is_host_definition_in_pending_phase(test_settings.READY_PHASE) + result = self.host_definition_manager.is_host_definition_in_pending_phase(common_settings.READY_PHASE) self.assertFalse(result) def test_set_host_definition_status_to_error_success(self): self.host_definition_manager.set_host_definition_status = Mock() self.host_definition_manager.set_host_definition_phase_to_error(self.fake_host_definition_info) self.host_definition_manager.set_host_definition_status.assert_called_once_with( - self.fake_host_definition_info.name, test_settings.ERROR_PHASE) + self.fake_host_definition_info.name, common_settings.ERROR_PHASE) def test_return_true_when_host_definition_is_not_pending_and_exist(self): result = self._test_is_host_definition_not_pending(self.fake_host_definition_info) @@ -308,7 +310,7 @@ def test_return_true_when_host_definition_is_not_exist_after_it_was_pending(self def test_return_false_when_host_definition_exist_but_still_pending(self): host_definition_info = deepcopy(self.fake_host_definition_info) - host_definition_info.phase = test_settings.PENDING_CREATION_PHASE + host_definition_info.phase = common_settings.PENDING_CREATION_PHASE result = self._test_is_host_definition_not_pending(host_definition_info) self.assertFalse(result) diff --git a/controllers/tests/controller_server/host_definer/resource_manager/node_manager_test.py b/controllers/tests/controller_server/host_definer/resource_manager/node_manager_test.py index e71f5e861..61c26b740 100644 --- a/controllers/tests/controller_server/host_definer/resource_manager/node_manager_test.py +++ b/controllers/tests/controller_server/host_definer/resource_manager/node_manager_test.py @@ -8,6 +8,7 @@ import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils import controllers.tests.controller_server.host_definer.utils.k8s_manifests_utils as test_manifest_utils import controllers.tests.controller_server.host_definer.settings as test_settings +import controllers.common.settings as common_settings class TestNodeManager(BaseResourceManager): @@ -30,7 +31,7 @@ def setUp(self): self.global_managed_secrets = test_utils.patch_managed_secrets_global_variable( test_settings.NODE_MANAGER_PATH) self.manage_node_labels_manifest = test_manifest_utils.get_metadata_with_manage_node_labels_manifest( - test_settings.MANAGE_NODE_LABEL) + common_settings.MANAGE_NODE_LABEL) self.mock_get_system_info_for_topologies = patch('{}.get_system_info_for_topologies'.format( test_settings.NODE_MANAGER_PATH)).start() @@ -120,12 +121,12 @@ def test_node_has_manage_node_label(self): def test_node_do_not_has_manage_node_label(self): node_info = deepcopy(self.fake_node_info) - node_info.labels.pop(test_settings.MANAGE_NODE_LABEL) + node_info.labels.pop(common_settings.MANAGE_NODE_LABEL) self._test_is_node_has_label(node_info, False, self.node_manager.is_node_has_manage_node_label) def test_node_has_forbid_deletion_label(self): node_info = deepcopy(self.fake_node_info) - node_info.labels[test_settings.FORBID_DELETION_LABEL] = test_settings.TRUE_STRING + node_info.labels[common_settings.FORBID_DELETION_LABEL] = common_settings.TRUE_STRING self._test_is_node_has_label(node_info, True, self.node_manager.is_node_has_forbid_deletion_label) def test_node_do_not_has_forbid_deletion_label(self): @@ -146,7 +147,7 @@ def test_add_node_to_nodes_when_node_do_not_has_manage_node_label_success(self, self.node_manager.add_node_to_nodes(self.fake_csi_node_info) self._assert_add_node_to_nodes(excepted_managed_node) self._assert_update_manage_node_label_called( - mock_manifest_utils, self.manage_node_labels_manifest, test_settings.TRUE_STRING) + mock_manifest_utils, self.manage_node_labels_manifest, common_settings.TRUE_STRING) @patch('{}.manifest_utils'.format(test_settings.NODE_MANAGER_PATH)) def test_add_node_to_nodes_when_node_has_manage_node_label_success(self, mock_manifest_utils): @@ -370,12 +371,12 @@ def _test_is_node_has_new_manage_node_label(self, expected_result, csi_node_info def test_do_not_handle_node_topologies_when_node_is_not_managed(self): self.global_managed_nodes = {} - self.node_manager.handle_node_topologies(self.fake_node_info, test_settings.MODIFIED_EVENT_TYPE) + self.node_manager.handle_node_topologies(self.fake_node_info, common_settings.MODIFIED_EVENT_TYPE) self._assert_do_not_handle_node_topologies() def test_do_not_handle_node_topologies_when_watch_event_is_not_modified_type(self): self.global_managed_nodes[test_settings.FAKE_NODE_NAME] = self.fake_managed_node - self.node_manager.handle_node_topologies(self.fake_node_info, test_settings.ADDED_EVENT) + self.node_manager.handle_node_topologies(self.fake_node_info, common_settings.ADDED_EVENT_TYPE) self._assert_do_not_handle_node_topologies() def _assert_do_not_handle_node_topologies(self): @@ -387,7 +388,7 @@ def _assert_do_not_handle_node_topologies(self): def test_do_not_handle_node_topologies_when_node_is_not_in_secret_topologies(self): self._prepare_handle_node_topologies(self.fake_secret_info, False, False) global_managed_secrets = deepcopy(self.global_managed_secrets) - self.node_manager.handle_node_topologies(self.fake_node_info, test_settings.MODIFIED_EVENT_TYPE) + self.node_manager.handle_node_topologies(self.fake_node_info, common_settings.MODIFIED_EVENT_TYPE) self.assertEqual(global_managed_secrets[0].nodes_with_system_id, self.global_managed_secrets[0].nodes_with_system_id) self.node_manager.secret_manager.is_node_should_managed_on_secret_info.assert_called_once_with( @@ -400,7 +401,7 @@ def test_do_not_handle_node_topologies_when_node_is_not_in_secret_topologies(sel def test_remove_node_from_secret_topology_fields_if_topology_not_match_anymore(self): fake_secret_info = deepcopy(self.fake_secret_info) self._prepare_handle_node_topologies(fake_secret_info, True, False) - self.node_manager.handle_node_topologies(self.fake_node_info, test_settings.MODIFIED_EVENT_TYPE) + self.node_manager.handle_node_topologies(self.fake_node_info, common_settings.MODIFIED_EVENT_TYPE) self._assert_global_secrets_changed_as_wanted({}) self._assert_called_remove_node_if_topology_not_match(fake_secret_info) @@ -416,7 +417,7 @@ def test_define_host_with_new_topology(self): fake_secret_info = deepcopy(self.fake_secret_info) fake_secret_info.nodes_with_system_id = {} self._prepare_handle_node_topologies(fake_secret_info, False, True) - self.node_manager.handle_node_topologies(self.fake_node_info, test_settings.MODIFIED_EVENT_TYPE) + self.node_manager.handle_node_topologies(self.fake_node_info, common_settings.MODIFIED_EVENT_TYPE) self._assert_called_define_host_with_new_topology(fake_secret_info) self._assert_global_secrets_changed_as_wanted(self.fake_secret_info.nodes_with_system_id) diff --git a/controllers/tests/controller_server/host_definer/resource_manager/resource_info_manager_test.py b/controllers/tests/controller_server/host_definer/resource_manager/resource_info_manager_test.py index a80ffa8c3..47bd7c6da 100644 --- a/controllers/tests/controller_server/host_definer/resource_manager/resource_info_manager_test.py +++ b/controllers/tests/controller_server/host_definer/resource_manager/resource_info_manager_test.py @@ -5,6 +5,7 @@ from controllers.servers.host_definer.resource_manager.resource_info import ResourceInfoManager import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils import controllers.tests.controller_server.host_definer.settings as test_settings +import controllers.common.settings as common_settings class TestCSINodeManager(BaseResourceManager): @@ -20,30 +21,30 @@ def setUp(self): def test_get_node_info_seccess(self): self.resource_info_manager.k8s_api.read_node.return_value = test_utils.get_fake_k8s_node( - test_settings.MANAGE_NODE_LABEL) + common_settings.MANAGE_NODE_LABEL) self.resource_info_manager.generate_node_info = Mock() self.resource_info_manager.generate_node_info.return_value = self.fake_node_info - result = self.resource_info_manager.get_node_info(test_settings.MANAGE_NODE_LABEL) + result = self.resource_info_manager.get_node_info(common_settings.MANAGE_NODE_LABEL) self.assertEqual(result.name, self.fake_node_info.name) self.assertEqual(result.labels, self.fake_node_info.labels) - self.resource_info_manager.k8s_api.read_node.assert_called_once_with(test_settings.MANAGE_NODE_LABEL) + self.resource_info_manager.k8s_api.read_node.assert_called_once_with(common_settings.MANAGE_NODE_LABEL) self.resource_info_manager.generate_node_info.assert_called_once_with(test_utils.get_fake_k8s_node( - test_settings.MANAGE_NODE_LABEL)) + common_settings.MANAGE_NODE_LABEL)) def test_fail_to_get_node_info(self): self.resource_info_manager.k8s_api.read_node.return_value = None self.resource_info_manager.generate_node_info = Mock() - result = self.resource_info_manager.get_node_info(test_settings.MANAGE_NODE_LABEL) + result = self.resource_info_manager.get_node_info(common_settings.MANAGE_NODE_LABEL) self.assertEqual(result.name, '') self.assertEqual(result.labels, {}) - self.resource_info_manager.k8s_api.read_node.assert_called_once_with(test_settings.MANAGE_NODE_LABEL) + self.resource_info_manager.k8s_api.read_node.assert_called_once_with(common_settings.MANAGE_NODE_LABEL) self.resource_info_manager.generate_node_info.assert_not_called() def test_generate_node_info_success(self): result = self.resource_info_manager.generate_node_info(test_utils.get_fake_k8s_node( - test_settings.MANAGE_NODE_LABEL)) + common_settings.MANAGE_NODE_LABEL)) self.assertEqual(result.name, self.fake_node_info.name) - self.assertEqual(result.labels, test_utils.get_fake_k8s_node(test_settings.MANAGE_NODE_LABEL).metadata.labels) + self.assertEqual(result.labels, test_utils.get_fake_k8s_node(common_settings.MANAGE_NODE_LABEL).metadata.labels) def test_get_csi_node_info_success(self): self.resource_info_manager.k8s_api.get_csi_node.return_value = test_utils.get_fake_k8s_csi_node() @@ -66,7 +67,7 @@ def test_get_non_exist_csi_node_info_success(self): def test_generate_csi_node_info_with_ibm_driver_success(self): result = self.resource_info_manager.generate_csi_node_info( - test_utils.get_fake_k8s_csi_node(test_settings.CSI_PROVISIONER_NAME)) + test_utils.get_fake_k8s_csi_node(common_settings.CSI_PROVISIONER_NAME)) self.assertEqual(result.name, self.fake_csi_node_info.name) self.assertEqual(result.node_id, self.fake_csi_node_info.node_id) @@ -81,7 +82,7 @@ def test_get_storage_classes_info_success(self): self._test_get_k8s_resources_info_success( self.resource_info_manager.get_storage_classes_info, self.resource_info_manager.k8s_api.list_storage_class, self.resource_info_manager.generate_storage_class_info, self.fake_storage_class_info, - test_utils.get_fake_k8s_storage_class_items(test_settings.CSI_PROVISIONER_NAME)) + test_utils.get_fake_k8s_storage_class_items(common_settings.CSI_PROVISIONER_NAME)) def test_get_storage_classes_info_empty_list_success(self): self.resource_info_manager.generate_storage_class_info = Mock() @@ -90,7 +91,7 @@ def test_get_storage_classes_info_empty_list_success(self): self.resource_info_manager.generate_storage_class_info) def test_generate_storage_class_info_success(self): - k8s_storage_class = test_utils.get_fake_k8s_storage_class(test_settings.CSI_PROVISIONER_NAME) + k8s_storage_class = test_utils.get_fake_k8s_storage_class(common_settings.CSI_PROVISIONER_NAME) result = self.resource_info_manager.generate_storage_class_info(k8s_storage_class) self.assertEqual(result.name, self.fake_storage_class_info.name) self.assertEqual(result.provisioner, self.fake_storage_class_info.provisioner) @@ -110,18 +111,18 @@ def _test_get_pods_info(self, number_of_pods): self.assertEqual(result[0].node_name, test_utils.get_fake_k8s_pods_items().items[0].spec.node_name) self.assertEqual(len(result), number_of_pods) self.resource_info_manager.k8s_api.list_pod_for_all_namespaces.assert_called_once_with( - test_settings.DRIVER_PRODUCT_LABEL) + common_settings.DRIVER_PRODUCT_LABEL) def test_get_none_when_fail_to_get_k8s_pods(self): self.resource_info_manager.k8s_api.list_pod_for_all_namespaces.return_value = None result = self.resource_info_manager.get_csi_pods_info() self.assertEqual(result, []) self.resource_info_manager.k8s_api.list_pod_for_all_namespaces.assert_called_once_with( - test_settings.DRIVER_PRODUCT_LABEL) + common_settings.DRIVER_PRODUCT_LABEL) @patch('{}.utils'.format(test_settings.RESOURCE_INFO_MANAGER_PATH)) def test_generate_host_definition_info_success(self, mock_utils): - k8s_host_definition = test_utils.get_fake_k8s_host_definition(test_settings.READY_PHASE) + k8s_host_definition = test_utils.get_fake_k8s_host_definition(common_settings.READY_PHASE) mock_utils.get_k8s_object_resource_version.return_value = self.fake_host_definition_info.resource_version result = self.resource_info_manager.generate_host_definition_info(k8s_host_definition) self.assertEqual(result.name, self.fake_host_definition_info.name) diff --git a/controllers/tests/controller_server/host_definer/resource_manager/secret_manager_test.py b/controllers/tests/controller_server/host_definer/resource_manager/secret_manager_test.py index d30f08bfc..a0304b05b 100644 --- a/controllers/tests/controller_server/host_definer/resource_manager/secret_manager_test.py +++ b/controllers/tests/controller_server/host_definer/resource_manager/secret_manager_test.py @@ -6,6 +6,7 @@ import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils import controllers.tests.controller_server.host_definer.utils.k8s_manifests_utils as test_manifest_utils import controllers.tests.controller_server.host_definer.settings as test_settings +import controllers.common.settings as common_settings class TestSecretManager(BaseResourceManager): @@ -120,7 +121,7 @@ def test_return_false_when_node_not_in_system_ids_topologies(self): self.assertFalse(result) def _test_is_node_in_system_ids_topologies(self, system_id): - node_labels = [test_settings.MANAGE_NODE_LABEL] + node_labels = [common_settings.MANAGE_NODE_LABEL] self.secret_manager.get_system_id_for_node_labels = Mock() self.secret_manager.get_system_id_for_node_labels.return_value = system_id result = self.secret_manager.is_node_labels_in_system_ids_topologies(self.fake_secret_info, node_labels) @@ -179,7 +180,7 @@ def _test_is_topology_secret(self, secret_config, mock_utils): @patch('{}.utils'.format(test_settings.SECRET_MANAGER_PATH)) def test_get_only_first_label_when_it_is_a_topology(self, manifest_utils): - expected_result = {test_settings.FAKE_TOPOLOGY_LABEL + '1': test_settings.TRUE_STRING} + expected_result = {test_settings.FAKE_TOPOLOGY_LABEL + '1': common_settings.TRUE_STRING} self._test_get_topology_labels([True, False], test_settings.FAKE_TOPOLOGY_LABELS, expected_result, 2, manifest_utils) diff --git a/controllers/tests/controller_server/host_definer/settings.py b/controllers/tests/controller_server/host_definer/settings.py index 5c20223ec..2406071f9 100644 --- a/controllers/tests/controller_server/host_definer/settings.py +++ b/controllers/tests/controller_server/host_definer/settings.py @@ -1,14 +1,9 @@ +from controllers.common.settings import TOPOLOGY_IBM_BLOCK_PREFIX, CSI_PARAMETER_PREFIX, SECRET_NAME_SUFFIX from controllers.tests.common.test_settings import HOST_NAME import controllers.common.settings as common_settings -SPEC_FIELD = 'spec' -METADATA_FIELD = 'metadata' -STATUS_FIELD = 'status' STORAGE_CLASS_DRIVERS_FIELD = 'drivers' CSI_NODE_NODE_ID_FIELD = 'nodeID' -CSI_PROVISIONER_NAME = 'block.csi.ibm.com' -CSI_IBM_API_VERSION = 'csi.ibm.com/v1' -HOST_DEFINITION_KIND = 'HostDefinition' FAKE_SECRET = 'fake_secret' FAKE_SECRET_NAMESPACE = 'fake_secret_namespace' FAKE_NODE_NAME = 'fake_node_name' @@ -25,23 +20,22 @@ NQN = 'nqn.2014-08.org.nvmexpress:uuid:b57708c7-5bb6-46a0-b2af-9d824bf539e1' FAKE_NODE_ID = '{};;;{}'.format(HOST_NAME, IQN) FAKE_CSI_PROVISIONER = 'fake_csi_provisioner' -TRUE_STRING = 'true' -NODE_LABELS_FIELD = 'labels' FAKE_LABEL = 'FAKE_LABEL' -MANAGE_NODE_LABEL = 'hostdefiner.block.csi.ibm.com/manage-node' -FORBID_DELETION_LABEL = 'hostdefiner.block.csi.ibm.com/do-not-delete-definition' -FAKE_TOPOLOGY_LABEL = 'topology.block.csi.ibm.com/topology' -NODES_WATCHER_PATH = 'controllers.servers.host_definer.watcher.node_watcher' -SECRET_WATCHER_PATH = 'controllers.servers.host_definer.watcher.secret_watcher' -CSI_NODE_WATCHER_PATH = 'controllers.servers.host_definer.watcher.csi_node_watcher' -STORAGE_CLASS_WATCHER_PATH = 'controllers.servers.host_definer.watcher.storage_class_watcher' -HOST_DEFINITION_WATCHER_PATH = 'controllers.servers.host_definer.watcher.host_definition_watcher' +FAKE_TOPOLOGY_LABEL = '{}/topology'.format(TOPOLOGY_IBM_BLOCK_PREFIX) +HOST_DEFINER_PATH = 'controllers.servers.host_definer' +HOST_DEFINER_WATCHER_PATH = '{}.watcher'.format(HOST_DEFINER_PATH) +HOST_DEFINER_RESOURCE_MANAGER_PATH = '{}.resource_manager'.format(HOST_DEFINER_PATH) +NODES_WATCHER_PATH = '{}.node_watcher'.format(HOST_DEFINER_WATCHER_PATH) +SECRET_WATCHER_PATH = '{}.secret_watcher'.format(HOST_DEFINER_WATCHER_PATH) +CSI_NODE_WATCHER_PATH = '{}.csi_node_watcher'.format(HOST_DEFINER_WATCHER_PATH) +STORAGE_CLASS_WATCHER_PATH = '{}.storage_class_watcher'.format(HOST_DEFINER_WATCHER_PATH) +HOST_DEFINITION_WATCHER_PATH = '{}.host_definition_watcher'.format(HOST_DEFINER_WATCHER_PATH) UTILS_PATH = 'controllers.servers.host_definer.utils.utils' -HOST_DEFINITION_MANAGER_PATH = 'controllers.servers.host_definer.resource_manager.host_definition' SETTINGS_PATH = 'controllers.servers.host_definer.settings' -SECRET_MANAGER_PATH = 'controllers.servers.host_definer.resource_manager.secret' -NODE_MANAGER_PATH = 'controllers.servers.host_definer.resource_manager.node' -RESOURCE_INFO_MANAGER_PATH = 'controllers.servers.host_definer.resource_manager.resource_info' +HOST_DEFINITION_MANAGER_PATH = '{}.host_definition'.format(HOST_DEFINER_RESOURCE_MANAGER_PATH) +SECRET_MANAGER_PATH = '{}.secret'.format(HOST_DEFINER_RESOURCE_MANAGER_PATH) +NODE_MANAGER_PATH = '{}.node'.format(HOST_DEFINER_RESOURCE_MANAGER_PATH) +RESOURCE_INFO_MANAGER_PATH = '{}.resource_info'.format(HOST_DEFINER_RESOURCE_MANAGER_PATH) TYPES_PATH = 'controllers.servers.host_definer.types' REQUEST_MANAGER_PATH = 'controllers.servers.host_definer.definition_manager.request' DEFINITION_MANAGER_PATH = 'controllers.servers.host_definer.definition_manager.definition' @@ -54,30 +48,17 @@ UPDATED_PODS = 'updated_number_scheduled' POD_NODE_NAME_FIELD = 'node_name' DESIRED_UPDATED_PODS = 'desired_number_scheduled' -DELETED_EVENT_TYPE = 'DELETED' -MODIFIED_EVENT_TYPE = 'MODIFIED' -ADDED_EVENT = 'ADDED' METADATA_UID_FIELD = 'uid' -STATUS_PHASE_FIELD = 'phase' -READY_PHASE = 'Ready' -ERROR_PHASE = 'Error' -HOST_DEFINITION_FIELD = 'hostDefinition' -SECRET_NAME_FIELD = 'secretName' -SECRET_NAMESPACE_FIELD = 'secretNamespace' -HOST_DEFINITION_NODE_NAME_FIELD = 'nodeName' -NODE_NAME_ON_STORAGE_FIELD = 'nodeNameOnStorage' SECRET_DATA_FIELD = 'data' FAIL_MESSAGE_FROM_STORAGE = 'fail_from_storage' -PENDING_CREATION_PHASE = 'PendingCreation' -PENDING_DELETION_PHASE = 'PendingDeletion' MESSAGE = 'Host defined successfully on the array' HOST_DEFINITION_PENDING_VARS = {'HOST_DEFINITION_PENDING_RETRIES': 3, 'HOST_DEFINITION_PENDING_EXPONENTIAL_BACKOFF_IN_SECONDS': 0.2, 'HOST_DEFINITION_PENDING_DELAY_IN_SECONDS': 0.2} STORAGE_CLASS_PROVISIONER_FIELD = 'provisioner' STORAGE_CLASS_PARAMETERS_FIELD = 'parameters' -STORAGE_CLASS_SECRET_FIELD = 'csi.storage.k8s.io/secret-name' -STORAGE_CLASS_SECRET_NAMESPACE_FIELD = 'csi.storage.k8s.io/secret-namespace' +STORAGE_CLASS_SECRET_FIELD = '{}{}'.format(CSI_PARAMETER_PREFIX, SECRET_NAME_SUFFIX) +STORAGE_CLASS_SECRET_NAMESPACE_FIELD = '{}secret-namespace'.format(CSI_PARAMETER_PREFIX) FAKE_PREFIX = 'fake-prefix' IO_GROUP_ID_FIELD = 'id' IO_GROUP_IDS = ['0', '2'] @@ -87,23 +68,8 @@ STORAGE_CLASS_SECRET_FIELD: FAKE_SECRET, STORAGE_CLASS_SECRET_NAMESPACE_FIELD: FAKE_SECRET_NAMESPACE } -CSI_IBM_FINALIZER = common_settings.HOST_DEFINITION_PLURAL + '.' + common_settings.CSI_IBM_GROUP -FINALIZERS_FIELD = 'finalizers' CONNECTIVITY_TYPE_FIELD = 'connectivityType' -DEFINE_ACTION = 'Define' -UNDEFINE_ACTION = 'Undefine' -SUCCESSFUL_MESSAGE_TYPE = 'Successful' -FAILED_MESSAGE_TYPE = 'Failed' -HOST_DEFINER = 'hostDefiner' -NORMAL_EVENT_TYPE = 'Normal' -WARNING_EVENT_TYPE = 'Warning' -DRIVER_PRODUCT_LABEL = 'product=ibm-block-csi-driver' FAKE_FC_PORTS = ['532453845345', '532453845345'] -PORTS_FIELD = 'ports' -IO_GROUP_FIELD = 'ioGroups' -MANAGEMENT_ADDRESS_FIELD = 'managementAddress' -API_VERSION_FIELD = 'apiVersion' -KIND_FIELD = 'kind' IO_GROUP_LABEL_PREFIX = 'hostdefiner.block.csi.ibm.com/io-group-' FAKE_SINGLE_IO_GROUP_STRING = '0' FAKE_MULTIPLE_IO_GROUP_STRING = '0:1' @@ -112,14 +78,8 @@ FAKE_ENCODED_CONFIG = {"config": BASE64_STRING} FAKE_DECODED_CONFIG_STRING = {"config": DECODED_BASE64_STRING} FAKE_DECODED_CONFIG = {"config": {'fake_key': 'fake_value'}} -CONFIG_FIELD = 'config' -PREFIX_ENV_VAR = 'PREFIX' -CONNECTIVITY_ENV_VAR = 'CONNECTIVITY_TYPE' ISCSI_CONNECTIVITY_TYPE = 'iscsi' -DEFAULT_NAMESPACE = 'default' -ALLOW_DELETE_ENV_VAR = 'ALLOW_DELETE' -DYNAMIC_NODE_LABELING_ENV_VAR = 'DYNAMIC_NODE_LABELING' -FAKE_TOPOLOGY_LABELS = {FAKE_TOPOLOGY_LABEL + '1': TRUE_STRING, FAKE_TOPOLOGY_LABEL + '2': TRUE_STRING} +FAKE_TOPOLOGY_LABELS = {FAKE_TOPOLOGY_LABEL + '1': common_settings.TRUE_STRING, + FAKE_TOPOLOGY_LABEL + '2': common_settings.TRUE_STRING} FAKE_SYSTEM_IDS_TOPOLOGIES = {FAKE_SYSTEM_ID: FAKE_TOPOLOGY_LABELS} SECRET_SUPPORTED_TOPOLOGIES_PARAMETER = "supported_topologies" -CONNECTIVITY_TYPE_LABEL = '{}/connectivity-type'.format(CSI_PROVISIONER_NAME) diff --git a/controllers/tests/controller_server/host_definer/utils/k8s_manifests_utils.py b/controllers/tests/controller_server/host_definer/utils/k8s_manifests_utils.py index 88374a126..7755445eb 100644 --- a/controllers/tests/controller_server/host_definer/utils/k8s_manifests_utils.py +++ b/controllers/tests/controller_server/host_definer/utils/k8s_manifests_utils.py @@ -7,7 +7,7 @@ def get_k8s_csi_node_manifest(csi_provisioner_name, csi_node_suffix=''): k8s_csi_node_spec = { - test_settings.SPEC_FIELD: { + common_settings.SPEC_FIELD: { test_settings.STORAGE_CLASS_DRIVERS_FIELD: [{ common_settings.NAME_FIELD: csi_provisioner_name, test_settings.CSI_NODE_NODE_ID_FIELD: test_settings.FAKE_NODE_ID @@ -19,7 +19,7 @@ def get_k8s_csi_node_manifest(csi_provisioner_name, csi_node_suffix=''): def get_fake_k8s_daemon_set_manifest(updated_pods, desired_updated_pods): k8s_daemon_set_status = { - test_settings.STATUS_FIELD: { + common_settings.STATUS_FIELD: { test_settings.UPDATED_PODS: updated_pods, test_settings.DESIRED_UPDATED_PODS: desired_updated_pods, }} @@ -28,7 +28,7 @@ def get_fake_k8s_daemon_set_manifest(updated_pods, desired_updated_pods): def get_fake_k8s_pod_manifest(pod_suffix=''): k8s_pod_spec = { - test_settings.SPEC_FIELD: { + common_settings.SPEC_FIELD: { test_settings.POD_NODE_NAME_FIELD: test_settings.FAKE_NODE_NAME }} return _generate_manifest(test_settings.FAKE_NODE_PODS_NAME + pod_suffix, k8s_pod_spec) @@ -38,30 +38,30 @@ def get_fake_k8s_host_definition_manifest(host_definition_phase='ready'): status_phase_manifest = get_status_phase_manifest(host_definition_phase) fields_manifest = get_fake_k8s_host_definition_response_fields_manifest() k8s_host_definition_body = { - test_settings.API_VERSION_FIELD: test_settings.CSI_IBM_API_VERSION, - test_settings.KIND_FIELD: test_settings.HOST_DEFINITION_KIND, - test_settings.SPEC_FIELD: { - test_settings.HOST_DEFINITION_FIELD: { - test_settings.HOST_DEFINITION_NODE_NAME_FIELD: test_settings.FAKE_NODE_NAME, - test_settings.SECRET_NAME_FIELD: test_settings.FAKE_SECRET, - test_settings.SECRET_NAMESPACE_FIELD: test_settings.FAKE_SECRET_NAMESPACE, + common_settings.API_VERSION_FIELD: common_settings.CSI_IBM_API_VERSION, + common_settings.KIND_FIELD: common_settings.HOST_DEFINITION_KIND, + common_settings.SPEC_FIELD: { + common_settings.HOST_DEFINITION_FIELD: { + common_settings.HOST_DEFINITION_NODE_NAME_FIELD: test_settings.FAKE_NODE_NAME, + common_settings.SECRET_NAME_FIELD: test_settings.FAKE_SECRET, + common_settings.SECRET_NAMESPACE_FIELD: test_settings.FAKE_SECRET_NAMESPACE, common_settings.HOST_DEFINITION_NODE_ID_FIELD: test_settings.FAKE_NODE_ID, } }} - k8s_host_definition_body[test_settings.SPEC_FIELD][test_settings.HOST_DEFINITION_FIELD].update( - fields_manifest[test_settings.SPEC_FIELD][test_settings.HOST_DEFINITION_FIELD]) + k8s_host_definition_body[common_settings.SPEC_FIELD][common_settings.HOST_DEFINITION_FIELD].update( + fields_manifest[common_settings.SPEC_FIELD][common_settings.HOST_DEFINITION_FIELD]) return _generate_manifest(test_settings.FAKE_NODE_NAME, status_phase_manifest, k8s_host_definition_body) def get_fake_k8s_host_definition_response_fields_manifest(): manifest = { - test_settings.SPEC_FIELD: { - test_settings.HOST_DEFINITION_FIELD: { - test_settings.NODE_NAME_ON_STORAGE_FIELD: test_settings.FAKE_NODE_NAME, - test_settings.CONNECTIVITY_TYPE_FIELD: test_settings.FAKE_CONNECTIVITY_TYPE, - test_settings.PORTS_FIELD: test_settings.FAKE_FC_PORTS, - test_settings.IO_GROUP_FIELD: test_settings.IO_GROUP_IDS, - test_settings.MANAGEMENT_ADDRESS_FIELD: test_settings.FAKE_SECRET_ARRAY + common_settings.SPEC_FIELD: { + common_settings.HOST_DEFINITION_FIELD: { + common_settings.NODE_NAME_ON_STORAGE_FIELD: test_settings.FAKE_NODE_NAME, + common_settings.CONNECTIVITY_TYPE_FIELD: test_settings.FAKE_CONNECTIVITY_TYPE, + common_settings.PORTS_FIELD: test_settings.FAKE_FC_PORTS, + common_settings.IO_GROUP_FIELD: test_settings.IO_GROUP_IDS, + common_settings.MANAGEMENT_ADDRESS_FIELD: test_settings.FAKE_SECRET_ARRAY } } } @@ -70,18 +70,18 @@ def get_fake_k8s_host_definition_response_fields_manifest(): def get_status_phase_manifest(phase): return { - test_settings.STATUS_FIELD: { - test_settings.STATUS_PHASE_FIELD: phase + common_settings.STATUS_FIELD: { + common_settings.STATUS_PHASE_FIELD: phase } } def get_fake_k8s_node_manifest(label): node_manifest = _generate_manifest(test_settings.FAKE_NODE_NAME) - node_manifest[test_settings.METADATA_FIELD][test_settings.NODE_LABELS_FIELD] = { - label: test_settings.TRUE_STRING, - common_settings.IO_GROUP_LABEL_PREFIX + str(0): test_settings.TRUE_STRING, - common_settings.IO_GROUP_LABEL_PREFIX + str(2): test_settings.TRUE_STRING} + node_manifest[common_settings.METADATA_FIELD][common_settings.LABELS_FIELD] = { + label: common_settings.TRUE_STRING, + common_settings.IO_GROUP_LABEL_PREFIX + str(0): common_settings.TRUE_STRING, + common_settings.IO_GROUP_LABEL_PREFIX + str(2): common_settings.TRUE_STRING} return node_manifest @@ -93,7 +93,8 @@ def get_fake_k8s_secret_manifest(): SECRET_USERNAME_PARAMETER: test_settings.FAKE_SECRET_USER_NAME }} secret_manifest = _generate_manifest(test_settings.FAKE_SECRET, secret_data_manifest) - secret_manifest[test_settings.METADATA_FIELD][common_settings.NAMESPACE_FIELD] = test_settings.FAKE_SECRET_NAMESPACE + secret_manifest[common_settings.METADATA_FIELD][common_settings.NAMESPACE_FIELD] = \ + test_settings.FAKE_SECRET_NAMESPACE return secret_manifest @@ -106,7 +107,7 @@ def get_fake_k8s_storage_class_manifest(provisioner): def _generate_manifest(object_name, *extra_dicts): metadata_manifest = get_metadata_manifest() - metadata_manifest[test_settings.METADATA_FIELD][common_settings.NAME_FIELD] = object_name + metadata_manifest[common_settings.METADATA_FIELD][common_settings.NAME_FIELD] = object_name if len(extra_dicts) > 0: merged_dicts = _merge_dicts(metadata_manifest, extra_dicts[0]) else: @@ -118,7 +119,7 @@ def _generate_manifest(object_name, *extra_dicts): def get_metadata_manifest(): return { - test_settings.METADATA_FIELD: { + common_settings.METADATA_FIELD: { common_settings.RESOURCE_VERSION_FIELD: test_settings.FAKE_RESOURCE_VERSION, test_settings.METADATA_UID_FIELD: test_settings.FAKE_UID }} @@ -137,8 +138,8 @@ def generate_watch_event(event_type, object_function): def get_metadata_with_manage_node_labels_manifest(label_value): return { - test_settings.METADATA_FIELD: { - test_settings.NODE_LABELS_FIELD: {test_settings.MANAGE_NODE_LABEL: label_value} + common_settings.METADATA_FIELD: { + common_settings.LABELS_FIELD: {common_settings.MANAGE_NODE_LABEL: label_value} } } @@ -153,7 +154,7 @@ def get_host_io_group_manifest(): def get_empty_k8s_list_manifest(): return { common_settings.ITEMS_FIELD: [], - test_settings.METADATA_FIELD: { + common_settings.METADATA_FIELD: { common_settings.RESOURCE_VERSION_FIELD } } @@ -161,17 +162,17 @@ def get_empty_k8s_list_manifest(): def get_finalizers_manifest(finalizers): return { - test_settings.METADATA_FIELD: { + common_settings.METADATA_FIELD: { common_settings.NAME_FIELD: test_settings.FAKE_NODE_NAME, - test_settings.FINALIZERS_FIELD: finalizers, + common_settings.FINALIZERS_FIELD: finalizers, } } def get_general_labels_manifest(labels): return { - test_settings.METADATA_FIELD: { - test_settings.NODE_LABELS_FIELD: labels + common_settings.METADATA_FIELD: { + common_settings.LABELS_FIELD: labels } } diff --git a/controllers/tests/controller_server/host_definer/utils/test_utils.py b/controllers/tests/controller_server/host_definer/utils/test_utils.py index d5bd2797c..54fa18572 100644 --- a/controllers/tests/controller_server/host_definer/utils/test_utils.py +++ b/controllers/tests/controller_server/host_definer/utils/test_utils.py @@ -9,6 +9,7 @@ from controllers.servers.host_definer.k8s.api import K8SApi from controllers.tests.common.test_settings import HOST_NAME, SECRET_MANAGEMENT_ADDRESS_VALUE import controllers.tests.controller_server.host_definer.settings as test_settings +import controllers.common.settings as common_settings import controllers.tests.controller_server.host_definer.utils.k8s_manifests_utils as test_manifest_utils @@ -43,7 +44,7 @@ def get_fake_k8s_csi_node(csi_provisioner_name=""): def get_fake_csi_node_watch_event(event_type): return test_manifest_utils.generate_watch_event(event_type, test_manifest_utils.get_k8s_csi_node_manifest( - test_settings.CSI_PROVISIONER_NAME)) + common_settings.CSI_PROVISIONER_NAME)) def get_fake_k8s_node(label): @@ -90,11 +91,11 @@ def get_fake_host_definition_watch_event(event_type): def get_fake_node_watch_event(event_type): return test_manifest_utils.generate_watch_event(event_type, test_manifest_utils.get_fake_k8s_node_manifest( - test_settings.MANAGE_NODE_LABEL)) + common_settings.MANAGE_NODE_LABEL)) def get_fake_k8s_nodes_items(): - k8s_node_manifest = test_manifest_utils.get_fake_k8s_node_manifest(test_settings.MANAGE_NODE_LABEL) + k8s_node_manifest = test_manifest_utils.get_fake_k8s_node_manifest(common_settings.MANAGE_NODE_LABEL) return K8sResourceItems([Munch.fromDict(k8s_node_manifest)]) @@ -158,11 +159,11 @@ def patch_managed_secrets_global_variable(module_path): def get_pending_creation_status_manifest(): - return test_manifest_utils.get_status_phase_manifest(test_settings.PENDING_CREATION_PHASE) + return test_manifest_utils.get_status_phase_manifest(common_settings.PENDING_CREATION_PHASE) def get_ready_status_manifest(): - return test_manifest_utils.get_status_phase_manifest(test_settings.READY_PHASE) + return test_manifest_utils.get_status_phase_manifest(common_settings.READY_PHASE) def get_array_connection_info(): @@ -228,14 +229,14 @@ def get_fake_csi_node_info(): def get_fake_node_info(): node_info = Mock(spec_set=['name', 'labels']) node_info.name = test_settings.FAKE_NODE_NAME - node_info.labels = {test_settings.MANAGE_NODE_LABEL: test_settings.TRUE_STRING} + node_info.labels = {common_settings.MANAGE_NODE_LABEL: common_settings.TRUE_STRING} return node_info def get_fake_storage_class_info(): storage_class_info = Mock(spec_set=['name', 'provisioner', 'parameters']) storage_class_info.name = test_settings.FAKE_STORAGE_CLASS - storage_class_info.provisioner = test_settings.CSI_PROVISIONER_NAME + storage_class_info.provisioner = common_settings.CSI_PROVISIONER_NAME storage_class_info.parameters = test_settings.FAKE_STORAGE_CLASS_PARAMETERS return storage_class_info @@ -246,7 +247,7 @@ def get_fake_host_definition_info(): host_definition_info.name = test_settings.FAKE_NODE_NAME host_definition_info.resource_version = test_settings.FAKE_RESOURCE_VERSION host_definition_info.uid = test_settings.FAKE_UID - host_definition_info.phase = test_settings.READY_PHASE + host_definition_info.phase = common_settings.READY_PHASE host_definition_info.secret_name = test_settings.FAKE_SECRET host_definition_info.secret_namespace = test_settings.FAKE_SECRET_NAMESPACE host_definition_info.node_name = test_settings.FAKE_NODE_NAME @@ -265,7 +266,7 @@ def get_fake_empty_host_definition_info(): def get_object_reference(): return client.V1ObjectReference( - api_version=test_settings.CSI_IBM_API_VERSION, kind=test_settings.HOST_DEFINITION_KIND, + api_version=common_settings.CSI_IBM_API_VERSION, kind=common_settings.HOST_DEFINITION_KIND, name=test_settings.FAKE_NODE_NAME, resource_version=test_settings.FAKE_RESOURCE_VERSION, uid=test_settings.FAKE_UID, ) @@ -289,7 +290,7 @@ def get_fake_define_host_response(): def get_fake_io_group_labels(number_of_io_groups): labels = {} for index in range(number_of_io_groups): - labels[test_settings.IO_GROUP_LABEL_PREFIX + str(index)] = test_settings.TRUE_STRING + labels[test_settings.IO_GROUP_LABEL_PREFIX + str(index)] = common_settings.TRUE_STRING return labels diff --git a/controllers/tests/controller_server/host_definer/watchers/csi_node_watcher_test.py b/controllers/tests/controller_server/host_definer/watchers/csi_node_watcher_test.py index 54eb88170..c3be5ebbd 100644 --- a/controllers/tests/controller_server/host_definer/watchers/csi_node_watcher_test.py +++ b/controllers/tests/controller_server/host_definer/watchers/csi_node_watcher_test.py @@ -1,6 +1,7 @@ from unittest.mock import MagicMock, patch from copy import deepcopy +import controllers.common.settings as common_settings import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils import controllers.tests.controller_server.host_definer.settings as test_settings from controllers.tests.controller_server.host_definer.watchers.watcher_base import WatcherBaseSetUp @@ -53,10 +54,11 @@ def setUp(self): self.managed_node_with_different_node_id.node_id = 'different_node_id' self.fake_secret_info = test_utils.get_fake_secret_info() self.fake_host_definition_info = test_utils.get_fake_host_definition_info() - self.csi_node_modified_watch_manifest = test_utils.get_fake_node_watch_event(test_settings.MODIFIED_EVENT_TYPE) + self.csi_node_modified_watch_manifest = test_utils.get_fake_node_watch_event( + common_settings.MODIFIED_EVENT_TYPE) self.csi_node_modified_watch_munch = test_utils.convert_manifest_to_munch( self.csi_node_modified_watch_manifest) - self.csi_node_deleted_watch_manifest = test_utils.get_fake_node_watch_event(test_settings.DELETED_EVENT_TYPE) + self.csi_node_deleted_watch_manifest = test_utils.get_fake_node_watch_event(common_settings.DELETED_EVENT_TYPE) self.csi_node_deleted_watch_munch = test_utils.convert_manifest_to_munch(self.csi_node_deleted_watch_manifest) self.global_managed_nodes = test_utils.patch_nodes_global_variable( test_settings.CSI_NODE_WATCHER_PATH) diff --git a/controllers/tests/controller_server/host_definer/watchers/host_definition_watcher_test.py b/controllers/tests/controller_server/host_definer/watchers/host_definition_watcher_test.py index 880be2dd3..e13e06f27 100644 --- a/controllers/tests/controller_server/host_definer/watchers/host_definition_watcher_test.py +++ b/controllers/tests/controller_server/host_definer/watchers/host_definition_watcher_test.py @@ -1,6 +1,7 @@ from copy import deepcopy from unittest.mock import patch, MagicMock +import controllers.common.settings as common_settings import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils import controllers.tests.controller_server.host_definer.settings as test_settings from controllers.tests.controller_server.host_definer.watchers.watcher_base import WatcherBaseSetUp @@ -19,14 +20,14 @@ def setUp(self): test_utils.patch_pending_variables() self.fake_define_response = test_utils.get_fake_define_host_response() self.fake_define_response.error_message = '' - self.fake_action = test_settings.DEFINE_ACTION + self.fake_action = common_settings.DEFINE_ACTION self.fake_host_definition_info = test_utils.get_fake_host_definition_info() self.fake_pending_deletion_host_definition_info = deepcopy(self.fake_host_definition_info) - self.fake_pending_deletion_host_definition_info.phase = test_settings.PENDING_DELETION_PHASE + self.fake_pending_deletion_host_definition_info.phase = common_settings.PENDING_DELETION_PHASE self.fake_pending_creation_host_definition_info = deepcopy(self.fake_host_definition_info) - self.fake_pending_creation_host_definition_info.phase = test_settings.PENDING_CREATION_PHASE + self.fake_pending_creation_host_definition_info.phase = common_settings.PENDING_CREATION_PHASE self.host_definition_deleted_watch_manifest = test_utils.get_fake_host_definition_watch_event( - test_settings.DELETED_EVENT_TYPE) + common_settings.DELETED_EVENT_TYPE) self.host_definition_deleted_watch_munch = test_utils.convert_manifest_to_munch( self.host_definition_deleted_watch_manifest) @@ -114,7 +115,7 @@ def _prepare_define_host_using_exponential_backoff( def _prepare_handle_pending_host_definition(self, mock_utils, host_definition_info, is_node_can_be_undefined): mock_utils.get_action.return_value = self.fake_action - if host_definition_info.phase == test_settings.PENDING_CREATION_PHASE: + if host_definition_info.phase == common_settings.PENDING_CREATION_PHASE: self.watcher.definition_manager.define_host_after_pending.return_value = self.fake_define_response else: self.watcher.node_manager.is_node_can_be_undefined.return_value = is_node_can_be_undefined @@ -153,7 +154,7 @@ def _assert_define_host_using_exponential_backoff_called( def _assert_called_handle_pending_host_definition( self, mock_utils, host_definition_info, is_node_can_be_undefined, is_error_message): mock_utils.get_action.assert_called_once_with(host_definition_info.phase) - if host_definition_info.phase == test_settings.PENDING_CREATION_PHASE: + if host_definition_info.phase == common_settings.PENDING_CREATION_PHASE: self.watcher.definition_manager.define_host_after_pending.assert_called_once_with(host_definition_info) self.watcher.node_manager.is_node_can_be_undefined.assert_not_called() self.watcher.definition_manager.undefine_host_after_pending.assert_not_called() @@ -178,8 +179,8 @@ def _assert_handle_message_from_storage(self, host_definition_info, is_node_can_ if is_error_message: self.watcher.host_definition_manager.create_k8s_event_for_host_definition.assert_called_once_with( host_definition_info, self.fake_define_response.error_message, self.fake_action, - test_settings.FAILED_MESSAGE_TYPE) - elif host_definition_info.phase == test_settings.PENDING_CREATION_PHASE: + common_settings.FAILED_MESSAGE_TYPE) + elif host_definition_info.phase == common_settings.PENDING_CREATION_PHASE: self.watcher.host_definition_manager.set_host_definition_status_to_ready.assert_called_once_with( host_definition_info) elif is_node_can_be_undefined: diff --git a/controllers/tests/controller_server/host_definer/watchers/node_watcher_test.py b/controllers/tests/controller_server/host_definer/watchers/node_watcher_test.py index 8374e55d4..133c4a644 100644 --- a/controllers/tests/controller_server/host_definer/watchers/node_watcher_test.py +++ b/controllers/tests/controller_server/host_definer/watchers/node_watcher_test.py @@ -1,6 +1,7 @@ from copy import deepcopy from unittest.mock import patch, MagicMock +import controllers.common.settings as common_settings import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils import controllers.tests.controller_server.host_definer.settings as test_settings from controllers.tests.controller_server.host_definer.watchers.watcher_base import WatcherBaseSetUp @@ -125,9 +126,9 @@ def _assert_delete_host_definitions_not_called(self): class TestWatchNodesResources(NodeWatcherBase): def setUp(self): super().setUp() - self.node_modified_watch_manifest = test_utils.get_fake_node_watch_event(test_settings.MODIFIED_EVENT_TYPE) + self.node_modified_watch_manifest = test_utils.get_fake_node_watch_event(common_settings.MODIFIED_EVENT_TYPE) self.node_modified_watch_munch = test_utils.convert_manifest_to_munch(self.node_modified_watch_manifest) - self.node_added_watch_manifest = test_utils.get_fake_node_watch_event(test_settings.ADDED_EVENT) + self.node_added_watch_manifest = test_utils.get_fake_node_watch_event(common_settings.ADDED_EVENT_TYPE) self.node_added_watch_munch = test_utils.convert_manifest_to_munch(self.node_added_watch_manifest) @patch('{}.utils'.format(test_settings.NODES_WATCHER_PATH)) diff --git a/controllers/tests/controller_server/host_definer/watchers/secret_watcher_test.py b/controllers/tests/controller_server/host_definer/watchers/secret_watcher_test.py index 807b1c5ab..6cdbeba66 100644 --- a/controllers/tests/controller_server/host_definer/watchers/secret_watcher_test.py +++ b/controllers/tests/controller_server/host_definer/watchers/secret_watcher_test.py @@ -1,6 +1,7 @@ from copy import deepcopy from unittest.mock import patch, MagicMock +import controllers.common.settings as common_settings from controllers.servers.host_definer.watcher.secret_watcher import SecretWatcher import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils import controllers.tests.controller_server.host_definer.settings as test_settings @@ -23,7 +24,8 @@ def setUp(self): self.fake_host_definition_info = test_utils.get_fake_host_definition_info() self.global_managed_secrets = test_utils.patch_managed_secrets_global_variable( test_settings.SECRET_WATCHER_PATH) - self.secret_modified_watch_manifest = test_utils.get_fake_secret_watch_event(test_settings.MODIFIED_EVENT_TYPE) + self.secret_modified_watch_manifest = test_utils.get_fake_secret_watch_event( + common_settings.MODIFIED_EVENT_TYPE) self.secret_modified_watch_munch = test_utils.convert_manifest_to_munch(self.secret_modified_watch_manifest) self.fake_nodes_with_system_id = {self.fake_node_info.name: test_settings.FAKE_SYSTEM_ID} diff --git a/controllers/tests/controller_server/host_definer/watchers/storage_class_watcher_test.py b/controllers/tests/controller_server/host_definer/watchers/storage_class_watcher_test.py index b77a52378..25e7cf86a 100644 --- a/controllers/tests/controller_server/host_definer/watchers/storage_class_watcher_test.py +++ b/controllers/tests/controller_server/host_definer/watchers/storage_class_watcher_test.py @@ -1,6 +1,7 @@ from copy import deepcopy from unittest.mock import MagicMock, patch +import controllers.common.settings as common_settings import controllers.tests.controller_server.host_definer.utils.test_utils as test_utils import controllers.tests.controller_server.host_definer.settings as test_settings from controllers.tests.controller_server.host_definer.watchers.watcher_base import WatcherBaseSetUp @@ -158,11 +159,11 @@ def setUp(self): self.secret_info_with_storage_classes = test_utils.get_fake_secret_info(2) self.copy_secret_info_with_storage_classes = deepcopy(self.secret_info_with_storage_classes) self.storage_class_added_watch_manifest = test_utils.get_fake_storage_class_watch_event( - test_settings.ADDED_EVENT) + common_settings.ADDED_EVENT_TYPE) self.storage_class_added_watch_munch = test_utils.convert_manifest_to_munch( self.storage_class_added_watch_manifest) self.storage_class_deleted_watch_manifest = test_utils.get_fake_storage_class_watch_event( - test_settings.DELETED_EVENT_TYPE) + common_settings.DELETED_EVENT_TYPE) self.storage_class_deleted_watch_munch = test_utils.convert_manifest_to_munch( self.storage_class_deleted_watch_manifest) self.global_managed_secrets = patch('{}.MANAGED_SECRETS'.format(test_settings.STORAGE_CLASS_WATCHER_PATH), From 62160c82cf539b0e483fb6d38667e925251ff1c4 Mon Sep 17 00:00:00 2001 From: Matan Carmeli <45543087+matancarmeli7@users.noreply.github.com> Date: Wed, 17 May 2023 15:46:23 +0300 Subject: [PATCH 10/12] add not found error to add/remove ports svc functions (#675) Signed-off-by: matancarmeli7 --- .../array_action/array_mediator_svc.py | 10 ++-- .../svc/array_mediator_svc_test.py | 54 ++++++++++++------- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/controllers/array_action/array_mediator_svc.py b/controllers/array_action/array_mediator_svc.py index ea9e216f6..e7366983c 100644 --- a/controllers/array_action/array_mediator_svc.py +++ b/controllers/array_action/array_mediator_svc.py @@ -1831,11 +1831,16 @@ def _raise_error_when_no_ports_added(self, host_name): self.delete_host(host_name) raise array_errors.NoPortIsValid(host_name) + def _raise_error_when_host_not_found(self, host_name, error_message): + if OBJ_NOT_FOUND in error_message: + raise array_errors.HostNotFoundError(host_name) + def _addhostport(self, host_name, connectivity_type, port): cli_kwargs = build_host_port_command_kwargs(host_name, connectivity_type, port) try: self.client.svctask.addhostport(**cli_kwargs) except (svc_errors.CommandExecutionError, CLIFailureError) as ex: + self._raise_error_when_host_not_found(host_name, ex.my_message) if not self._is_port_invalid(ex.my_message): if is_warning_message(ex.my_message): logger.warning("exception encountered during adding port {} to host {} : {}".format( @@ -1854,6 +1859,7 @@ def _rmhostport(self, host_name, connectivity_type, port): try: self.client.svctask.rmhostport(**cli_kwargs) except (svc_errors.CommandExecutionError, CLIFailureError) as ex: + self._raise_error_when_host_not_found(host_name, ex.my_message) if not self._is_port_invalid(ex.my_message): if is_warning_message(ex.my_message): logger.warning("exception encountered during removing port {} from host {} : {}".format( @@ -1950,10 +1956,6 @@ def get_host_io_group(self, host_name): logger.info(svc_messages.HOST_IO_GROUP_IDS.format(host_name, io_group.id)) return io_group - def _raise_error_when_host_not_found(self, host_name, error_message): - if OBJ_NOT_FOUND in error_message: - raise array_errors.HostNotFoundError(host_name) - def _raise_unsupported_parameter_error(self, error_message, parameter): if NOT_SUPPORTED_PARAMETER in error_message: raise array_errors.UnSupportedParameter(parameter) diff --git a/controllers/tests/array_action/svc/array_mediator_svc_test.py b/controllers/tests/array_action/svc/array_mediator_svc_test.py index f555f4a06..457079e2e 100644 --- a/controllers/tests/array_action/svc/array_mediator_svc_test.py +++ b/controllers/tests/array_action/svc/array_mediator_svc_test.py @@ -1395,19 +1395,19 @@ def _prepare_mocks_for_get_host_by_identifiers_slow(self, svc_response, custom_h self._prepare_mocks_for_get_host_by_identifiers_no_hosts() host_1 = self._get_host_as_munch(array_settings.DUMMY_HOST_ID1, array_settings.DUMMY_HOST_NAME1, nqn_list=[ array_settings.DUMMY_NVME_NQN1], - wwpns_list=[array_settings.DUMMY_FC_WWN1], - iscsi_names_list=[array_settings.DUMMY_NODE1_IQN]) + wwpns_list=[array_settings.DUMMY_FC_WWN1], + iscsi_names_list=[array_settings.DUMMY_NODE1_IQN]) host_2 = self._get_host_as_munch(array_settings.DUMMY_HOST_ID2, array_settings.DUMMY_HOST_NAME2, nqn_list=[ array_settings.DUMMY_NVME_NQN2], - wwpns_list=[array_settings.DUMMY_FC_WWN2], - iscsi_names_list=[array_settings.DUMMY_NODE2_IQN]) + wwpns_list=[array_settings.DUMMY_FC_WWN2], + iscsi_names_list=[array_settings.DUMMY_NODE2_IQN]) if custom_host: host_3 = custom_host else: host_3 = self._get_host_as_munch(array_settings.DUMMY_HOST_ID3, array_settings.DUMMY_HOST_NAME3, nqn_list=[ array_settings.DUMMY_NVME_NQN3], - wwpns_list=[array_settings.DUMMY_FC_WWN3], iscsi_names_list=[ - array_settings.DUMMY_NODE3_IQN]) + wwpns_list=[array_settings.DUMMY_FC_WWN3], iscsi_names_list=[ + array_settings.DUMMY_NODE3_IQN]) hosts = [host_1, host_2, host_3] self.svc.client.svcinfo.lshost = Mock() self.svc.client.svcinfo.lshost.return_value = self._get_hosts_list_result(hosts) @@ -1548,13 +1548,13 @@ def test_get_host_by_identifiers_slow_no_other_ports_return_nvme_host(self, svc_ def test_get_host_by_identifiers_slow_return_fc_host(self, svc_response): host_1 = self._get_host_as_munch(array_settings.DUMMY_HOST_ID1, array_settings.DUMMY_HOST_NAME1, wwpns_list=[ array_settings.DUMMY_FC_WWN1], - iscsi_names_list=[]) + iscsi_names_list=[]) host_2 = self._get_host_as_munch(array_settings.DUMMY_HOST_ID2, array_settings.DUMMY_HOST_NAME2, wwpns_list=[ array_settings.DUMMY_FC_WWN2], - iscsi_names_list=[]) + iscsi_names_list=[]) host_3 = self._get_host_as_munch(array_settings.DUMMY_HOST_ID3, array_settings.DUMMY_HOST_NAME3, wwpns_list=[ array_settings.DUMMY_FC_WWN3, array_settings.DUMMY_FC_WWN4], - iscsi_names_list=[array_settings.DUMMY_NODE3_IQN]) + iscsi_names_list=[array_settings.DUMMY_NODE3_IQN]) hosts = [host_1, host_2, host_3] self._prepare_mocks_for_get_host_by_identifiers_slow(svc_response) hostname, connectivity_types = self.svc.get_host_by_host_identifiers( @@ -1604,13 +1604,13 @@ def test_get_host_by_identifiers_no_other_ports_return_fc_host(self): def test_get_host_by_identifiers_slow_with_wrong_fc_iscsi_raise_not_found(self, svc_response): host_1 = self._get_host_as_munch(array_settings.DUMMY_HOST_ID1, array_settings.DUMMY_HOST_NAME1, wwpns_list=[ array_settings.DUMMY_FC_WWN1], - iscsi_names_list=[]) + iscsi_names_list=[]) host_2 = self._get_host_as_munch(array_settings.DUMMY_HOST_ID2, array_settings.DUMMY_HOST_NAME2, wwpns_list=[ array_settings.DUMMY_FC_WWN3], - iscsi_names_list=[array_settings.DUMMY_NODE2_IQN]) + iscsi_names_list=[array_settings.DUMMY_NODE2_IQN]) host_3 = self._get_host_as_munch(array_settings.DUMMY_HOST_ID3, array_settings.DUMMY_HOST_NAME3, wwpns_list=[ array_settings.DUMMY_FC_WWN3], - iscsi_names_list=[array_settings.DUMMY_NODE3_IQN]) + iscsi_names_list=[array_settings.DUMMY_NODE3_IQN]) hosts = [host_1, host_2, host_3] self._prepare_mocks_for_get_host_by_identifiers_slow(svc_response) with self.assertRaises(array_errors.HostNotFoundError): @@ -1764,8 +1764,8 @@ def _test_map_volume_mkvdiskhostmap_error(self, client_error, expected_error, mo self._test_mediator_method_client_error(self.svc.map_volume, ( common_settings.VOLUME_UID, common_settings.HOST_NAME, array_settings.DUMMY_CONNECTIVITY_TYPE), - self.svc.client.svctask.mkvdiskhostmap, client_error, - expected_error) + self.svc.client.svctask.mkvdiskhostmap, client_error, + expected_error) def test_map_volume_mkvdiskhostmap_errors(self): self._test_map_volume_mkvdiskhostmap_error(svc_errors.CommandExecutionError("CMMVC5804E"), @@ -1806,8 +1806,8 @@ def test_map_volume_nvme_success(self): def _test_unmap_volume_rmvdiskhostmap_error(self, client_error, expected_error): self._test_mediator_method_client_error(self.svc.unmap_volume, ( common_settings.VOLUME_UID, common_settings.HOST_NAME), - self.svc.client.svctask.rmvdiskhostmap, client_error, - expected_error) + self.svc.client.svctask.rmvdiskhostmap, client_error, + expected_error) def test_unmap_volume_rmvdiskhostmap_errors(self): self._test_unmap_volume_rmvdiskhostmap_error(svc_errors.CommandExecutionError("CMMVC5753E"), @@ -1828,9 +1828,9 @@ def test_unmap_volume_success(self): def _prepare_mocks_for_get_iscsi_targets(self, portset_id=None): host = self._get_host_as_munch(array_settings.DUMMY_HOST_ID1, common_settings.HOST_NAME, wwpns_list=[ array_settings.DUMMY_FC_WWN1], - iscsi_names_list=[array_settings.DUMMY_NODE1_IQN, - array_settings.DUMMY_NODE2_IQN], - portset_id=portset_id) + iscsi_names_list=[array_settings.DUMMY_NODE1_IQN, + array_settings.DUMMY_NODE2_IQN], + portset_id=portset_id) self.svc.client.svcinfo.lshost = Mock() self.svc.client.svcinfo.lshost.return_value = Mock(as_single_element=host) @@ -2199,6 +2199,14 @@ def test_add_ports_to_host_falied(self): array_settings.ISCSI_CONNECTIVITY_TYPE), self.svc.client.svctask.addhostport, Exception("Failed"), Exception) + def test_add_ports_to_not_exist_host_falied(self): + self.svc.client.svctask.addhostport.side_effect = [CLIFailureError('CMMVC5753E')] + with self.assertRaises(array_errors.HostNotFoundError): + self.svc.add_ports_to_host(common_settings.HOST_NAME, + Initiators([], [array_settings.DUMMY_FC_WWN1, array_settings.DUMMY_FC_WWN2], []), + array_settings.FC_CONNECTIVITY_TYPE) + self.assertEqual(self.svc.client.svctask.addhostport.call_count, 1) + def test_remove_nvme_ports_from_host_success(self): self.svc.remove_ports_from_host(common_settings.HOST_NAME, [array_settings.DUMMY_NVME_NQN1], @@ -2235,6 +2243,14 @@ def test_remove_ports_from_host_falied(self): array_settings.ISCSI_CONNECTIVITY_TYPE), self.svc.client.svctask.rmhostport, Exception("Failed"), Exception) + def test_remove_ports_from_not_exist_host_falied(self): + self.svc.client.svctask.rmhostport.side_effect = [CLIFailureError('CMMVC5753E')] + with self.assertRaises(array_errors.HostNotFoundError): + self.svc.remove_ports_from_host(common_settings.HOST_NAME, + [array_settings.DUMMY_FC_WWN1, array_settings.DUMMY_FC_WWN2], + array_settings.ISCSI_CONNECTIVITY_TYPE) + self.assertEqual(self.svc.client.svctask.rmhostport.call_count, 1) + def _prepare_mocks_for_get_host_with_ports(self, attribute_name): self.svc.client.svcinfo.lshost = Mock() self.svc.client.svcinfo.lshost.return_value = Mock(as_single_element=Munch({ From fd9c675f8a2f9217aca24415e672db107cf77950 Mon Sep 17 00:00:00 2001 From: Matan Carmeli <45543087+matancarmeli7@users.noreply.github.com> Date: Thu, 8 Jun 2023 09:50:13 +0300 Subject: [PATCH 11/12] Feature/csi 5609 basic flow call home (#677) * add register plugin Signed-off-by: matancarmeli7 * add new empty line Signed-off-by: matancarmeli7 * add unit tests Signed-off-by: matancarmeli7 * PR Signed-off-by: matancarmeli7 * PR Signed-off-by: matancarmeli7 --------- Signed-off-by: matancarmeli7 --- .../array_action/array_mediator_ds8k.py | 3 + .../array_action/array_mediator_interface.py | 17 +++++ .../array_action/array_mediator_svc.py | 72 +++++++++++++++++++ .../array_action/array_mediator_xiv.py | 3 + .../array_action/registration_cache.py | 1 + controllers/array_action/registration_maps.py | 38 ++++++++++ controllers/array_action/settings.py | 5 ++ .../servers/csi/csi_controller_server.py | 2 + controllers/servers/csi/decorators.py | 16 ++++- controllers/servers/settings.py | 1 + .../svc/array_mediator_svc_test.py | 35 +++++++++ .../csi_controller_server_test.py | 5 ++ 12 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 controllers/array_action/registration_cache.py create mode 100644 controllers/array_action/registration_maps.py diff --git a/controllers/array_action/array_mediator_ds8k.py b/controllers/array_action/array_mediator_ds8k.py index 4bd251af5..7703a2498 100644 --- a/controllers/array_action/array_mediator_ds8k.py +++ b/controllers/array_action/array_mediator_ds8k.py @@ -726,3 +726,6 @@ def get_host_io_group(self, host_name): def change_host_protocol(self, host_name, protocol): raise NotImplementedError + + def register_plugin(self, unique_key, metadata): + return None diff --git a/controllers/array_action/array_mediator_interface.py b/controllers/array_action/array_mediator_interface.py index d56858303..223cbd37e 100644 --- a/controllers/array_action/array_mediator_interface.py +++ b/controllers/array_action/array_mediator_interface.py @@ -633,6 +633,23 @@ def change_host_protocol(self, host_name, protocol): """ raise NotImplementedError + @abstractmethod + def register_plugin(self, unique_key, metadata): + """ + This function should register CSI plugin with unique_key and metadata accordingly to the feature it used. + + Args: + unique_key : a unique key that will represent a feature + metadata : a metadata that will add some information + + Returns: + None + + Raises: + None + """ + raise NotImplementedError + @property @abstractmethod def identifier(self): diff --git a/controllers/array_action/array_mediator_svc.py b/controllers/array_action/array_mediator_svc.py index e7366983c..73e567125 100644 --- a/controllers/array_action/array_mediator_svc.py +++ b/controllers/array_action/array_mediator_svc.py @@ -1,6 +1,7 @@ from collections import defaultdict from io import StringIO from random import choice +from datetime import datetime, timedelta from packaging.version import Version from pysvc import errors as svc_errors @@ -8,10 +9,13 @@ from pysvc.unified.response import CLIFailureError, SVCResponse from retry import retry +from controllers.common.config import config import controllers.array_action.errors as array_errors import controllers.array_action.settings as array_settings +from controllers.array_action.registration_cache import SVC_REGISTRATION_CACHE from controllers.array_action import svc_messages import controllers.servers.settings as controller_settings +from controllers.servers.csi.decorators import register_csi_plugin from controllers.array_action.array_action_types import Volume, Snapshot, Replication, Host, VolumeGroup, ThinVolume from controllers.array_action.array_mediator_abstract import ArrayMediatorAbstract from controllers.array_action.utils import ClassProperty, convert_scsi_id_to_nguid @@ -19,6 +23,7 @@ from controllers.common import settings as common_settings from controllers.common.csi_logger import get_stdout_logger from controllers.servers.utils import get_connectivity_type_ports, split_string +from controllers.servers.settings import UNIQUE_KEY_KEY array_connections_dict = {} logger = get_stdout_logger() @@ -193,6 +198,16 @@ def build_change_host_protocol_kwargs(host_name, protocol): } +def build_register_plugin_kwargs(unique_key, metadata): + cli_kwargs = { + UNIQUE_KEY_KEY: unique_key, + array_settings.VERSION_KEY: config.identity.version + } + if metadata: + cli_kwargs[array_settings.METADATA_KEY] = metadata + return cli_kwargs + + def _get_cli_volume_space_efficiency_aliases(cli_volume): space_efficiency_aliases = {common_settings.SPACE_EFFICIENCY_THICK, ''} if cli_volume.se_copy == YES: @@ -666,6 +681,7 @@ def _create_cli_volume_from_source(self, name, pool, io_group, volume_group, sou def _is_vdisk_support_addsnapshot(self, vdisk_uid): return self._is_addsnapshot_supported() and not self._is_vdisk_has_fcmaps(vdisk_uid) + @register_csi_plugin() def create_volume(self, name, size_in_bytes, space_efficiency, pool, io_group, volume_group, source_ids, source_type, is_virt_snap_func): if is_virt_snap_func and source_ids: @@ -692,6 +708,7 @@ def _rmvolume(self, volume_id_or_name, not_exist_err=True): raise array_errors.ObjectNotFoundError(volume_id_or_name) raise ex + @register_csi_plugin() def delete_volume(self, volume_id): logger.info("Deleting volume with id : {0}".format(volume_id)) self._delete_volume(volume_id) @@ -904,6 +921,7 @@ def _get_cli_volume_in_pool_site(self, volume_name, pool_name): return other_cli_volume raise RuntimeError('could not find a volume for {} in site {}'.format(volume_name, pool_site_name)) + @register_csi_plugin() def create_snapshot(self, volume_id, snapshot_name, space_efficiency, pool, is_virt_snap_func): logger.info("creating snapshot '{0}' from volume '{1}'".format(snapshot_name, volume_id)) source_volume_name = self._get_volume_name_by_wwn(volume_id) @@ -933,6 +951,7 @@ def _rmsnapshot(self, internal_snapshot_id): raise array_errors.ObjectNotFoundError(internal_snapshot_id) raise ex + @register_csi_plugin() def delete_snapshot(self, snapshot_id, internal_snapshot_id): logger.info("Deleting snapshot with id : {0}".format(snapshot_id)) if self._is_addsnapshot_supported() and not snapshot_id: @@ -1148,6 +1167,7 @@ def _raise_error_when_host_not_exist_or_not_meet_the_rules(self, host_name, erro if NAME_NOT_EXIST_OR_MEET_RULES in error_message: raise array_errors.HostNotFoundError(host_name) + @register_csi_plugin() def map_volume(self, volume_id, host_name, connectivity_type): logger.debug("mapping volume : {0} to host : " "{1}".format(volume_id, host_name)) @@ -1181,6 +1201,7 @@ def map_volume(self, volume_id, host_name, connectivity_type): return str(lun) + @register_csi_plugin() def unmap_volume(self, volume_id, host_name): logger.debug("unmapping volume : {0} from host : " "{1}".format(volume_id, host_name)) @@ -1481,6 +1502,7 @@ def _start_rcrelationship(self, rcrelationship_id, primary_endpoint_type=None, f else: logger.warning("failed to start rcrelationship '{}': {}".format(rcrelationship_id, ex)) + @register_csi_plugin() def create_replication(self, replication_request): if replication_request.replication_type == array_settings.REPLICATION_TYPE_MIRROR: self._create_replication(replication_request) @@ -1531,6 +1553,7 @@ def _delete_rcrelationship(self, rcrelationship_id): else: logger.warning("failed to delete rcrelationship '{0}': {1}".format(rcrelationship_id, ex)) + @register_csi_plugin() def delete_replication(self, replication): if replication.replication_type == array_settings.REPLICATION_TYPE_MIRROR: self._delete_replication(replication.name) @@ -1581,6 +1604,7 @@ def _ensure_endpoint_is_primary(self, rcrelationship, endpoint_type): self._start_rcrelationship(rcrelationship.id, primary_endpoint_type=other_endpoint_type, force=True) self._promote_replication_endpoint(endpoint_type, rcrelationship.name) + @register_csi_plugin() def promote_replication_volume(self, replication): if replication.replication_type == array_settings.REPLICATION_TYPE_MIRROR: self._promote_replication_volume(replication.name) @@ -1612,6 +1636,7 @@ def _promote_ear_replication_volume(self, volume_group_id): else: logger.info("Can't be promoted because the local volume group is not an independent copy") + @register_csi_plugin() def demote_replication_volume(self, replication): if replication.replication_type == array_settings.REPLICATION_TYPE_MIRROR: self._demote_replication_volume(replication.name) @@ -1800,6 +1825,7 @@ def _mkhost(self, host_name, connectivity_type, port, io_group): logger.warning("exception encountered during host {} creation : {}".format(host_name, ex.my_message)) raise ex + @register_csi_plugin() def create_host(self, host_name, initiators, connectivity_type, io_group): ports = get_connectivity_type_ports(initiators, connectivity_type) for port in ports: @@ -1821,6 +1847,7 @@ def _rmhost(self, host_name): return raise ex + @register_csi_plugin() def delete_host(self, host_name): logger.info(svc_messages.DELETE_HOST.format(host_name)) self._rmhost(host_name) @@ -1847,6 +1874,7 @@ def _addhostport(self, host_name, connectivity_type, port): port, host_name, ex.my_message)) raise ex + @register_csi_plugin() def add_ports_to_host(self, host_name, initiators, connectivity_type): ports = get_connectivity_type_ports(initiators, connectivity_type) for port in ports: @@ -1866,6 +1894,7 @@ def _rmhostport(self, host_name, connectivity_type, port): port, host_name, ex.my_message)) raise ex + @register_csi_plugin() def remove_ports_from_host(self, host_name, ports, connectivity_type): for port in ports: logger.info(svc_messages.REMOVE_HOST_PORT.format(port, host_name)) @@ -2005,6 +2034,7 @@ def _generate_volume_group_response(self, cli_volume_group): internal_id=cli_volume_group.id, volumes=volumes) + @register_csi_plugin() def create_volume_group(self, name): volume_group_id = self._create_volume_group(name) cli_volume_group = self._lsvolumegroup(volume_group_id) @@ -2016,10 +2046,12 @@ def get_volume_group(self, volume_group_id): raise array_errors.ObjectNotFoundError(volume_group_id) return self._generate_volume_group_response(cli_volume_group) + @register_csi_plugin() def delete_volume_group(self, volume_group_id): self._lsvolumegroup(volume_group_id, not_exist_err=True) self._rmvolumegroup(volume_group_id) + @register_csi_plugin() def add_volume_to_volume_group(self, volume_group_id, volume_id): volume_name = self._get_volume_name_by_wwn(volume_id) cli_volume = self._get_cli_volume(volume_name) @@ -2027,6 +2059,46 @@ def add_volume_to_volume_group(self, volume_group_id, volume_id): raise array_errors.VolumeAlreadyInVolumeGroup(volume_id, cli_volume.volume_group_name) self._change_volume_group(cli_volume.id, volume_group_id) + @register_csi_plugin() def remove_volume_from_volume_group(self, volume_id): cli_volume = self._get_cli_volume_by_wwn(volume_id, not_exist_err=True) self._change_volume_group(cli_volume.id, None) + + def register_plugin(self, unique_key, metadata): + if self._is_registerplugin_supported() and self._is_plugin_needs_to_be_registered(unique_key): + self._register_plugin(unique_key, metadata) + + def _is_registerplugin_supported(self): + return hasattr(self.client.svctask, "registerplugin") + + def _is_plugin_needs_to_be_registered(self, unique_key): + current_time = datetime.now() + endpoint_cache = SVC_REGISTRATION_CACHE.get(self.endpoint) + if not endpoint_cache: + return True + last_registration_time = endpoint_cache.get(unique_key) + if last_registration_time: + time_difference = current_time - last_registration_time + return time_difference >= timedelta(hours=array_settings.MINIMUM_HOURS_BETWEEN_REGISTRATIONS) + return True + + def _register_plugin(self, unique_key, metadata): + self._update_registration_cache(unique_key) + self._registerplugin(unique_key, metadata) + + def _update_registration_cache(self, unique_key): + endpoint_cache = SVC_REGISTRATION_CACHE.get(self.endpoint) + if not endpoint_cache: + SVC_REGISTRATION_CACHE[self.endpoint] = {} + SVC_REGISTRATION_CACHE[self.endpoint][unique_key] = datetime.now() + + def _registerplugin(self, unique_key, metadata): + logger.info("Registering {} plugin, using {} unique key with [{}] metadata".format( + array_settings.REGISTRATION_PLUGIN, unique_key, metadata)) + cli_kwargs = build_register_plugin_kwargs(unique_key, metadata) + try: + self.client.svctask.registerplugin(name='{}'.format(array_settings.REGISTRATION_PLUGIN), **cli_kwargs) + except Exception as ex: + logger.error("exception encountered during" + "registering {} plugin using {} unique key with [{}] metadata: {}".format( + array_settings.REGISTRATION_PLUGIN, unique_key, metadata, ex)) diff --git a/controllers/array_action/array_mediator_xiv.py b/controllers/array_action/array_mediator_xiv.py index f785ec854..32c5ee4a3 100644 --- a/controllers/array_action/array_mediator_xiv.py +++ b/controllers/array_action/array_mediator_xiv.py @@ -558,3 +558,6 @@ def get_host_io_group(self, host_name): def change_host_protocol(self, host_name, protocol): raise NotImplementedError + + def register_plugin(self, unique_key, metadata): + return None diff --git a/controllers/array_action/registration_cache.py b/controllers/array_action/registration_cache.py new file mode 100644 index 000000000..80f1da495 --- /dev/null +++ b/controllers/array_action/registration_cache.py @@ -0,0 +1 @@ +SVC_REGISTRATION_CACHE = {} diff --git a/controllers/array_action/registration_maps.py b/controllers/array_action/registration_maps.py new file mode 100644 index 000000000..cdf677161 --- /dev/null +++ b/controllers/array_action/registration_maps.py @@ -0,0 +1,38 @@ +from controllers.array_action.settings import METADATA_KEY +from controllers.servers.settings import UNIQUE_KEY_KEY + + +def _generate_plugin_type(unique_key, metadata=''): + return { + UNIQUE_KEY_KEY: unique_key, + METADATA_KEY: metadata + } + + +basic_plugin_type = _generate_plugin_type('basic') +replication_plugin_type = _generate_plugin_type('replication') +volume_group_plugin_type = _generate_plugin_type('volume_group') +snapshot_plugin_type = _generate_plugin_type('snapshot') +host_definition_plugin_type = _generate_plugin_type('host_definition') + + +REGISTRATION_MAP = { + 'create_volume': basic_plugin_type, + 'delete_volume': basic_plugin_type, + 'map_volume': basic_plugin_type, + 'unmap_volume': basic_plugin_type, + 'create_replication': replication_plugin_type, + 'delete_replication': replication_plugin_type, + 'promote_replication_volume': replication_plugin_type, + 'demote_replication_volume': replication_plugin_type, + 'create_volume_group': volume_group_plugin_type, + 'delete_volume_group': volume_group_plugin_type, + 'add_volume_to_volume_group': volume_group_plugin_type, + 'remove_volume_from_volume_group': volume_group_plugin_type, + 'create_snapshot': snapshot_plugin_type, + 'delete_snapshot': snapshot_plugin_type, + 'create_host': host_definition_plugin_type, + 'delete_host': host_definition_plugin_type, + 'add_ports_to_host': host_definition_plugin_type, + 'remove_ports_from_host': host_definition_plugin_type, +} diff --git a/controllers/array_action/settings.py b/controllers/array_action/settings.py index 33611e527..2d84aa4c3 100644 --- a/controllers/array_action/settings.py +++ b/controllers/array_action/settings.py @@ -23,3 +23,8 @@ NGUID_OUI_END = 22 WWN_VENDOR_IDENTIFIER_END = 16 VENDOR_IDENTIFIER_LENGTH = 9 + +METADATA_KEY = 'metadata' +VERSION_KEY = 'version' +REGISTRATION_PLUGIN = 'block.csi.ibm.com' +MINIMUM_HOURS_BETWEEN_REGISTRATIONS = 2 diff --git a/controllers/servers/csi/csi_controller_server.py b/controllers/servers/csi/csi_controller_server.py index 6c8f06adb..dfa047143 100755 --- a/controllers/servers/csi/csi_controller_server.py +++ b/controllers/servers/csi/csi_controller_server.py @@ -99,6 +99,8 @@ def CreateVolume(self, request, context): "volume was not found. creating a new volume with parameters: {0}".format(request.parameters)) array_mediator.validate_supported_space_efficiency(space_efficiency) + if topologies: + array_mediator.register_plugin('topology', '') volume = array_mediator.create_volume(volume_final_name, required_bytes, space_efficiency, pool, volume_parameters.io_group, volume_parameters.volume_group, source_ids, source_type, is_virt_snap_func) diff --git a/controllers/servers/csi/decorators.py b/controllers/servers/csi/decorators.py index 819416dce..199b8c170 100644 --- a/controllers/servers/csi/decorators.py +++ b/controllers/servers/csi/decorators.py @@ -4,7 +4,10 @@ from controllers.common.csi_logger import get_stdout_logger from controllers.common.utils import set_current_thread_name from controllers.servers.errors import ObjectAlreadyProcessingError -from controllers.servers.settings import VOLUME_TYPE_NAME, VOLUME_GROUP_TYPE_NAME, LOCK_REPLICATION_REQUEST_ATTR +from controllers.servers.settings import (VOLUME_TYPE_NAME, VOLUME_GROUP_TYPE_NAME, + LOCK_REPLICATION_REQUEST_ATTR, UNIQUE_KEY_KEY) +from controllers.array_action.settings import METADATA_KEY +from controllers.array_action.registration_maps import REGISTRATION_MAP from controllers.servers.csi.exception_handler import handle_exception, handle_common_exceptions from controllers.servers.csi.sync_lock import SyncLock @@ -50,3 +53,14 @@ def _set_sync_lock(lock_id, lock_request_attribute, error_response_type, return handle_exception(ex, context, grpc.StatusCode.ABORTED, error_response_type) logger.info("finished {}".format(controller_method_name)) return response + + +def register_csi_plugin(): + @decorator + def call_csi_plugin_registration(mediator_method, mediator_class, *args): + plugin_fields = REGISTRATION_MAP.get(mediator_method.__name__, {}) + if plugin_fields: + mediator_class.register_plugin(plugin_fields[UNIQUE_KEY_KEY], plugin_fields[METADATA_KEY]) + return mediator_method(mediator_class, *args) + + return call_csi_plugin_registration diff --git a/controllers/servers/settings.py b/controllers/servers/settings.py index 77e056432..d67020443 100644 --- a/controllers/servers/settings.py +++ b/controllers/servers/settings.py @@ -62,3 +62,4 @@ MINIMUM_VOLUME_ID_PARTS = 2 MAXIMUM_VOLUME_ID_PARTS = 3 +UNIQUE_KEY_KEY = 'uniquekey' diff --git a/controllers/tests/array_action/svc/array_mediator_svc_test.py b/controllers/tests/array_action/svc/array_mediator_svc_test.py index 457079e2e..57977a3d9 100644 --- a/controllers/tests/array_action/svc/array_mediator_svc_test.py +++ b/controllers/tests/array_action/svc/array_mediator_svc_test.py @@ -1,11 +1,13 @@ import unittest from unittest.mock import MagicMock +from datetime import datetime, timedelta from mock import patch, Mock, call, PropertyMock from munch import Munch from pysvc import errors as svc_errors from pysvc.unified.response import CLIFailureError, SVCResponse +from controllers.common.config import config import controllers.array_action.errors as array_errors import controllers.tests.array_action.svc.test_settings as svc_settings import controllers.tests.array_action.test_settings as array_settings @@ -2478,3 +2480,36 @@ def test_remove_volume_from_volume_group_success(self): self.svc.client.svctask.chvdisk.assert_called_once_with(vdisk_id=common_settings.INTERNAL_VOLUME_ID, novolumegroup=True) + + @patch('controllers.array_action.array_mediator_svc.SVC_REGISTRATION_CACHE') + def test_register_plugin_when_there_is_no_registered_storage_success(self, mock_cache): + self._test_register_plugin_success(mock_cache, {}, True) + + @patch('controllers.array_action.array_mediator_svc.SVC_REGISTRATION_CACHE') + def test_register_plugin_when_unique_key_is_registered_more_than_two_hours_ago_success(self, mock_cache): + current_time = datetime.now() + three_hours_ago = current_time - timedelta(hours=3) + self._test_register_plugin_success(mock_cache, {'test_key': three_hours_ago}, True) + + @patch('controllers.array_action.array_mediator_svc.SVC_REGISTRATION_CACHE') + def test_do_not_register_plugin_when_unique_key_is_registered_less_than_two_hours_ago_success(self, mock_cache): + current_time = datetime.now() + one_hours_ago = current_time - timedelta(hours=1) + self._test_register_plugin_success(mock_cache, {'test_key': one_hours_ago}, False) + + @patch('controllers.array_action.array_mediator_svc.SVC_REGISTRATION_CACHE') + def test_assert_no_exception_when_register_fail_success(self, mock_cache): + self.svc.client.svctask.registerplugin.side_effect = [Exception] + self._test_register_plugin_success(mock_cache, {}, True) + + def _test_register_plugin_success(self, mock_cache, mock_cache_return_value, should_register): + mock_cache.get.return_value = mock_cache_return_value + self.svc.register_plugin('test_key', 'some_metadata') + + if should_register: + self.svc.client.svctask.registerplugin.assert_called_once_with(name='block.csi.ibm.com', + uniquekey='test_key', + version=config.identity.version, + metadata='some_metadata') + else: + self.svc.client.svctask.registerplugin.not_called() diff --git a/controllers/tests/controller_server/csi_controller_server_test.py b/controllers/tests/controller_server/csi_controller_server_test.py index 51ae5ac62..25222131b 100644 --- a/controllers/tests/controller_server/csi_controller_server_test.py +++ b/controllers/tests/controller_server/csi_controller_server_test.py @@ -447,8 +447,12 @@ def test_create_volume_already_processing(self): def test_create_volume_succeeds(self): self._test_create_volume_succeeds('xiv:{};{}'.format(INTERNAL_VOLUME_ID, VOLUME_UID)) + self.mediator.register_plugin.not_called() def test_create_volume_with_topologies_succeeds(self): + self._test_create_volume_with_topologies_succeeds() + + def _test_create_volume_with_topologies_succeeds(self): self.request.secrets = utils.get_fake_secret_config(system_id="u2", supported_topologies=[ {"topology.block.csi.ibm.com/test": "topology_value"}]) self.request.accessibility_requirements.preferred = [ @@ -460,6 +464,7 @@ def test_create_volume_with_topologies_succeeds(self): {"u1": self.request.parameters, "u2": second_system_parameters})} self._test_create_volume_succeeds('xiv:u2:{};{}'.format(INTERNAL_VOLUME_ID, VOLUME_UID), expected_pool=DUMMY_POOL2) + self.mediator.register_plugin.assert_called_once_with('topology', '') def test_create_volume_with_space_efficiency_succeeds(self): self._prepare_create_volume_mocks() From d8b5ee7a6afb646efbee0e1d6d39fd8648564aa3 Mon Sep 17 00:00:00 2001 From: Matan Carmeli <45543087+matancarmeli7@users.noreply.github.com> Date: Sun, 11 Jun 2023 10:07:44 +0300 Subject: [PATCH 12/12] Feature/csi 5610 add an option to enable/disable call home (#678) * add register plugin Signed-off-by: matancarmeli7 * add new empty line Signed-off-by: matancarmeli7 * add unit tests Signed-off-by: matancarmeli7 * add the option to enable/disable call home Signed-off-by: matancarmeli7 * add new empty line Signed-off-by: matancarmeli7 * PR Signed-off-by: matancarmeli7 * PR Signed-off-by: matancarmeli7 * add default value to ENABLE_CALL_HOME_ENV_VAR variable Signed-off-by: matancarmeli7 * fix unit test Signed-off-by: matancarmeli7 --------- Signed-off-by: matancarmeli7 --- .../array_action/array_mediator_svc.py | 5 ++- controllers/servers/settings.py | 1 + controllers/servers/utils.py | 5 +++ .../svc/array_mediator_svc_test.py | 45 ++++++++++++++----- .../tests/controller_server/utils_test.py | 13 ++++++ 5 files changed, 57 insertions(+), 12 deletions(-) diff --git a/controllers/array_action/array_mediator_svc.py b/controllers/array_action/array_mediator_svc.py index 73e567125..4d684b634 100644 --- a/controllers/array_action/array_mediator_svc.py +++ b/controllers/array_action/array_mediator_svc.py @@ -22,7 +22,7 @@ from controllers.array_action.volume_group_interface import VolumeGroupInterface from controllers.common import settings as common_settings from controllers.common.csi_logger import get_stdout_logger -from controllers.servers.utils import get_connectivity_type_ports, split_string +from controllers.servers.utils import get_connectivity_type_ports, split_string, is_call_home_enabled from controllers.servers.settings import UNIQUE_KEY_KEY array_connections_dict = {} @@ -2065,7 +2065,8 @@ def remove_volume_from_volume_group(self, volume_id): self._change_volume_group(cli_volume.id, None) def register_plugin(self, unique_key, metadata): - if self._is_registerplugin_supported() and self._is_plugin_needs_to_be_registered(unique_key): + if is_call_home_enabled() and self._is_registerplugin_supported() and \ + self._is_plugin_needs_to_be_registered(unique_key): self._register_plugin(unique_key, metadata) def _is_registerplugin_supported(self): diff --git a/controllers/servers/settings.py b/controllers/servers/settings.py index d67020443..15909f021 100644 --- a/controllers/servers/settings.py +++ b/controllers/servers/settings.py @@ -62,4 +62,5 @@ MINIMUM_VOLUME_ID_PARTS = 2 MAXIMUM_VOLUME_ID_PARTS = 3 +ENABLE_CALL_HOME_ENV_VAR = 'ENABLE_CALL_HOME' UNIQUE_KEY_KEY = 'uniquekey' diff --git a/controllers/servers/utils.py b/controllers/servers/utils.py index 8fc6602db..2cc7b7ddf 100644 --- a/controllers/servers/utils.py +++ b/controllers/servers/utils.py @@ -1,3 +1,4 @@ +from os import getenv import json import re from hashlib import sha256 @@ -846,3 +847,7 @@ def get_replication_object_type_and_id_info(request): raise ValidationException(messages.UNSUPPORTED_REPLICATION_SOURCE_TYPE_MESSAGE) object_id_info = get_object_id_info(object_id, object_type) return object_type, object_id_info + + +def is_call_home_enabled(): + return getenv(servers_settings.ENABLE_CALL_HOME_ENV_VAR, 'true') == 'true' diff --git a/controllers/tests/array_action/svc/array_mediator_svc_test.py b/controllers/tests/array_action/svc/array_mediator_svc_test.py index 57977a3d9..b07ec8362 100644 --- a/controllers/tests/array_action/svc/array_mediator_svc_test.py +++ b/controllers/tests/array_action/svc/array_mediator_svc_test.py @@ -2481,29 +2481,54 @@ def test_remove_volume_from_volume_group_success(self): self.svc.client.svctask.chvdisk.assert_called_once_with(vdisk_id=common_settings.INTERNAL_VOLUME_ID, novolumegroup=True) + @patch('{}.is_call_home_enabled'.format('controllers.array_action.array_mediator_svc')) @patch('controllers.array_action.array_mediator_svc.SVC_REGISTRATION_CACHE') - def test_register_plugin_when_there_is_no_registered_storage_success(self, mock_cache): - self._test_register_plugin_success(mock_cache, {}, True) + def test_register_plugin_when_there_is_no_registered_storage_success(self, mock_cache, is_enabled_mock): + mock_cache.get.return_value = {} + is_enabled_mock.return_value = True + self._test_register_plugin_success(True) + + @patch('{}.is_call_home_enabled'.format('controllers.array_action.array_mediator_svc')) @patch('controllers.array_action.array_mediator_svc.SVC_REGISTRATION_CACHE') - def test_register_plugin_when_unique_key_is_registered_more_than_two_hours_ago_success(self, mock_cache): + def test_register_plugin_when_unique_key_is_registered_more_than_two_hours_ago_success( + self, mock_cache, is_enabled_mock): current_time = datetime.now() three_hours_ago = current_time - timedelta(hours=3) - self._test_register_plugin_success(mock_cache, {'test_key': three_hours_ago}, True) + mock_cache.get.return_value = {'test_key': three_hours_ago} + is_enabled_mock.return_value = True + + self._test_register_plugin_success(True) + @patch('{}.is_call_home_enabled'.format('controllers.array_action.array_mediator_svc')) @patch('controllers.array_action.array_mediator_svc.SVC_REGISTRATION_CACHE') - def test_do_not_register_plugin_when_unique_key_is_registered_less_than_two_hours_ago_success(self, mock_cache): + def test_do_not_register_plugin_when_unique_key_is_registered_less_than_two_hours_ago_success( + self, mock_cache, is_enabled_mock): current_time = datetime.now() one_hours_ago = current_time - timedelta(hours=1) - self._test_register_plugin_success(mock_cache, {'test_key': one_hours_ago}, False) + mock_cache.get.return_value = {'test_key': one_hours_ago} + is_enabled_mock.return_value = True + self._test_register_plugin_success(False) + + @patch('{}.is_call_home_enabled'.format('controllers.array_action.array_mediator_svc')) @patch('controllers.array_action.array_mediator_svc.SVC_REGISTRATION_CACHE') - def test_assert_no_exception_when_register_fail_success(self, mock_cache): + def test_assert_no_exception_when_register_fail_success(self, mock_cache, is_enabled_mock): self.svc.client.svctask.registerplugin.side_effect = [Exception] - self._test_register_plugin_success(mock_cache, {}, True) + mock_cache.get.return_value = {} + is_enabled_mock.return_value = True + + self._test_register_plugin_success(True) + + @patch('{}.is_call_home_enabled'.format('controllers.array_action.array_mediator_svc')) + @patch('controllers.array_action.array_mediator_svc.SVC_REGISTRATION_CACHE') + def test_do_not_register_plugin_when_call_home_is_not_enabled_success(self, mock_cache, is_enabled_mock): + is_enabled_mock.return_value = False + + self._test_register_plugin_success(False) + self.assertEqual(mock_cache.get.call_count, 0) - def _test_register_plugin_success(self, mock_cache, mock_cache_return_value, should_register): - mock_cache.get.return_value = mock_cache_return_value + def _test_register_plugin_success(self, should_register): self.svc.register_plugin('test_key', 'some_metadata') if should_register: diff --git a/controllers/tests/controller_server/utils_test.py b/controllers/tests/controller_server/utils_test.py index 04382d48e..4e6dc4ad5 100644 --- a/controllers/tests/controller_server/utils_test.py +++ b/controllers/tests/controller_server/utils_test.py @@ -534,3 +534,16 @@ def test_validate_parameters_match_volume_prefix_success(self): self._test_validate_parameters_match_volume(volume_field="name", volume_value="prefix_vol", parameter_field=controller_config.PARAMETERS_VOLUME_NAME_PREFIX, parameter_value="prefix") + + def test_is_call_home_enabled_true(self): + self._test_is_call_home_enabled('true', True) + + def test_is_call_home_enabled_false(self): + self._test_is_call_home_enabled('false', False) + + def _test_is_call_home_enabled(self, get_env_return_value, expected_result): + mock_getenv = patch('{}.getenv'.format('controllers.servers.utils')).start() + mock_getenv.return_value = get_env_return_value + result = utils.is_call_home_enabled() + self.assertEqual(result, expected_result) + mock_getenv.assert_called_once_with(controller_config.ENABLE_CALL_HOME_ENV_VAR, 'true')