Multi env support and Kube client integration

Kube friendly Beta

Package versions supports Kube env

Added:
  - Env type detection
  - New option: --use-env, for selecting env
    when function supports multiple detected envs
  - Updated config loading
  - Each module and command type has supported env check
    and stops execution if it is on unsupported env
  - Functions can support multiple envs
  - Kubernetes dependency
  - Kubenernetes API detection: local and remote
  - Package checking class hierachy for using Salt or Kube
  - Remote pod execution routine
  - Flexible SSH/SSH Forwarder classes: with, ssh,do(), etc
  - Multithreaded SSH script execution
  - Number of workers parameter, default 5

Fixed:
  - Config dependency
  - Command loading with supported envs list
  - Unittests structure and execution flow updated
  - Unittests fixes
  - Fixed debug mode handling
  - Unified command type/support routine
  - Nested attrs getter/setter

Change-Id: I3ade693ac21536e2b5dcee4b24d511749dc72759
Related-PROD: PROD-35811
diff --git a/tests/mocks.py b/tests/mocks.py
index 863def5..9d9c534 100644
--- a/tests/mocks.py
+++ b/tests/mocks.py
@@ -6,6 +6,7 @@
 
 # Prepare fake filenames and files
 _res_dir = os.path.join(tests_dir, 'res')
+_fake_kube_config_path = os.path.join(_res_dir, "_fake_kube_config.env")
 
 
 # preload file from res
diff --git a/tests/res/_fake_kube_config.yaml b/tests/res/_fake_kube_config.yaml
new file mode 100644
index 0000000..5311671
--- /dev/null
+++ b/tests/res/_fake_kube_config.yaml
@@ -0,0 +1,19 @@
+apiVersion: v1
+clusters:
+- cluster:
+    certificate-authority-data: U3VwZXIgc2VjcmV0IGZha2UgY2VydGlmaWNhdGUgYXV0aG9yaXR5IQo=
+    server: https://192.168.0.1:6443
+  name: ucp_192.168.0.1:6443_admin
+contexts:
+- context:
+    cluster: ucp_192.168.0.1:6443_admin
+    user: ucp_192.168.0.1:6443_admin
+  name: ucp_192.168.0.1:6443_admin
+current-context: ucp_192.168.0.1:6443_admin
+kind: Config
+preferences: {}
+users:
+- name: ucp_192.168.0.1:6443_admin
+  user:
+    client-certificate-data: U3VwZXIgc2VjcmV0IGZha2UgY2VydGlmaWNhdGUhCg==
+    client-key-data: U3VwZXIgc2VjcmV0IGZha2Uga2V5IQo=
\ No newline at end of file
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 74928e3..9d1bd32 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -1,9 +1,22 @@
+import os
+
 from unittest import mock
 
+from tests.mocks import _fake_kube_config_path
 from tests.test_base import CfgCheckerTestBase
 
 
+os.environ['MCP_TYPE_FORCE'] = 'SALT'
+
+
 class TestCliCommands(CfgCheckerTestBase):
+    def setUp(self):
+        # force env type to salt
+        os.environ['MCP_TYPE_FORCE'] = 'SALT'
+
+    def tearDown(self):
+        del os.environ['MCP_TYPE_FORCE']
+
     def test_do_cli_main_command(self):
         _module_name = 'cfg_checker.cfg_check'
         _m = self._try_import(_module_name)
@@ -104,8 +117,13 @@
         _m = self._try_import(_module_name)
         _fake_args = mock.MagicMock(name="FakeArgsClass")
         _command = "unknowncommand"
+        _config = None
         with self.redirect_output():
-            _r_value = _m.cli.command.execute_command(_fake_args, _command)
+            _r_value = _m.cli.command.execute_command(
+                _fake_args,
+                _command,
+                _config
+            )
 
         self.assertEqual(
             _r_value,
@@ -118,8 +136,9 @@
         _m = self._try_import(_module_name)
         _type = {}
         _command = "unknowncommand"
+        _config = None
         with self.redirect_output():
-            _r_value = _m.cli.command.execute_command(_type, _command)
+            _r_value = _m.cli.command.execute_command(_type, _command, _config)
 
         self.assertEqual(
             _r_value,
@@ -131,9 +150,18 @@
         _module_name = 'cfg_checker.cli.command'
         _m = self._try_import(_module_name)
         _fake_args = mock.MagicMock(name="FakeArgsClass")
+        _fake_args.kube_config_path = _fake_kube_config_path
         _command = "reclass"
+
+        from cfg_checker.common.settings import CheckerConfiguration
+        _config = CheckerConfiguration(_fake_args)
+
         with self.redirect_output():
-            _r_value = _m.cli.command.execute_command(_fake_args, _command)
+            _r_value = _m.cli.command.execute_command(
+                _fake_args,
+                _command,
+                _config
+            )
 
         self.assertEqual(
             _r_value,
diff --git a/tests/test_common.py b/tests/test_common.py
index 65159a1..42f2988 100644
--- a/tests/test_common.py
+++ b/tests/test_common.py
@@ -30,6 +30,13 @@
 
 
 class TestCommonModules(CfgCheckerTestBase):
+    def setUp(self):
+        # force env type to salt
+        os.environ['MCP_TYPE_FORCE'] = 'SALT'
+
+    def tearDown(self):
+        del os.environ['MCP_TYPE_FORCE']
+
     def test_exceptions(self):
         _m = self._try_import("cfg_checker.common.exception")
         # Get all classes from the exceptions module
diff --git a/tests/test_entrypoints.py b/tests/test_entrypoints.py
index 6cbee3d..7c420d5 100644
--- a/tests/test_entrypoints.py
+++ b/tests/test_entrypoints.py
@@ -1,7 +1,16 @@
+import os
+
 from tests.test_base import CfgCheckerTestBase
 
 
 class TestEntrypoints(CfgCheckerTestBase):
+    def setUp(self):
+        # force env type to salt
+        os.environ['MCP_TYPE_FORCE'] = 'SALT'
+
+    def tearDown(self):
+        del os.environ['MCP_TYPE_FORCE']
+
     def test_entry_mcp_checker(self):
         _module_name = 'cfg_checker.cfg_check'
         with self.redirect_output():
diff --git a/tests/test_network.py b/tests/test_network.py
index 2dc07b3..3a1867b 100644
--- a/tests/test_network.py
+++ b/tests/test_network.py
@@ -1,4 +1,5 @@
 import os
+import shutil
 
 from unittest.mock import patch
 
@@ -19,6 +20,21 @@
 
 
 class TestNetworkModule(CfgCheckerTestBase):
+    def setUp(self):
+        # force env type to salt
+        os.environ['MCP_TYPE_FORCE'] = 'SALT'
+
+    def tearDown(self):
+        del os.environ['MCP_TYPE_FORCE']
+
+    @classmethod
+    def tearDownClass(cls):
+        _ferr = os.path.join(_res_dir, "fakeerrors")
+        if os.path.exists(_ferr):
+            shutil.rmtree(_ferr)
+
+        return super().tearDownClass()
+
     @patch('requests.get', side_effect=mocked_salt_get)
     @patch('requests.post', side_effect=mocked_salt_post)
     @patch(_shell_salt_path, side_effect=mocked_shell)
diff --git a/tests/test_packages.py b/tests/test_packages.py
index 52ec23a..6d8990d 100644
--- a/tests/test_packages.py
+++ b/tests/test_packages.py
@@ -23,6 +23,13 @@
 
 
 class TestPackageModule(CfgCheckerTestBase):
+    def setUp(self):
+        # force env type to salt
+        os.environ['MCP_TYPE_FORCE'] = 'SALT'
+
+    def tearDown(self):
+        del os.environ['MCP_TYPE_FORCE']
+
     @patch('requests.get', side_effect=mocked_package_get)
     @patch(_ReposInfo_path, new=_fakeReposInfo)
     @patch(_RepoManager_path, new=_fakeRepoManager)
diff --git a/tests/test_reclass_comparer.py b/tests/test_reclass_comparer.py
index 07ff9a6..a1a94b3 100644
--- a/tests/test_reclass_comparer.py
+++ b/tests/test_reclass_comparer.py
@@ -5,6 +5,13 @@
 
 
 class TestReclassModule(CfgCheckerTestBase):
+    def setUp(self):
+        # force env type to salt
+        os.environ['MCP_TYPE_FORCE'] = 'SALT'
+
+    def tearDown(self):
+        del os.environ['MCP_TYPE_FORCE']
+
     def test_reclass_list(self):
         _models_dir = os.path.join(_res_dir, "models")
         _args = ["list", "-p", _models_dir]