def test_is_deploy_supported_inv(self): """Test invalid host OS platform deployments returns False""" self.assertFalse(EdgeDefault.is_deployment_supported( 'windows', 'blah')) self.assertFalse(EdgeDefault.is_deployment_supported('windows', '')) self.assertFalse(EdgeDefault.is_deployment_supported('blah', 'docker')) self.assertFalse(EdgeDefault.is_deployment_supported('', 'docker'))
def _get_host_config_dir(): edge_config_dir = None result = None host = platform.system() log.debug('Searching Edge config dir in env var %s', EC.ENV_EDGECONFIGDIR) env_config_dir = os.getenv(EC.ENV_EDGECONFIGDIR, None) if env_config_dir and env_config_dir.strip() != '': edge_config_dir = os.path.realpath(env_config_dir) else: meta_config_file_path = EdgeDefault.get_meta_conf_file_path() log.debug('Searching Edge config dir in config file %s', meta_config_file_path) if meta_config_file_path and os.path.exists(meta_config_file_path): data = EdgeHostPlatform._read_json_config_file(meta_config_file_path) config_dir = data[EC.CONFIG_DIR_KEY] if config_dir != '': edge_config_dir = os.path.realpath(config_dir) else: edge_config_dir = os.path.realpath(EdgeDefault.get_config_dir(host)) log.debug('Using default Edge config dir %s', edge_config_dir) if edge_config_dir and os.path.isdir(edge_config_dir): log.debug('Found config directory: %s', edge_config_dir) result = edge_config_dir return result
def test_runtime_log_levels_valid(self): """Test to validate the log level settings and default setting""" test_levels = EdgeDefault.get_runtime_log_levels() self.assertIsNotNone(test_levels) self.assertTrue(len(test_levels)) test_level = EdgeDefault.get_default_runtime_log_level() self.assertIn(test_level, test_levels)
def test_is_deploy_supported_valid(self): """Test valid host OS platform deployments returns True""" self.assertTrue( EdgeDefault.is_deployment_supported('windows', 'docker')) self.assertTrue(EdgeDefault.is_deployment_supported('linux', 'Docker')) self.assertTrue(EdgeDefault.is_deployment_supported( 'Darwin', 'docker'))
def test_get_deployments_invalid(self): """" Test empty deployments list for invalid host OS platforms """ test_deployments = EdgeDefault.get_supported_deployments('blah') self.assertIsNotNone(test_deployments) self.assertEqual(0, len(test_deployments)) test_deployments = EdgeDefault.get_supported_deployments('') self.assertIsNotNone(test_deployments) self.assertEqual(0, len(test_deployments))
def test_docker_uri_invalid(self): """ Test invalid host and container types returns None""" test_uri = EdgeDefault.get_docker_uri('blah', 'linux') self.assertIsNone(test_uri) test_uri = EdgeDefault.get_docker_uri('linux', 'blah') self.assertIsNone(test_uri) test_uri = EdgeDefault.get_docker_uri('linux', 'windows') self.assertIsNone(test_uri)
def test_is_deploy_supported_except(self): """ Test None as host OS and deployment_type raises edgectl.errors.EdgeInvalidArgument """ with self.assertRaises(edgectl.errors.EdgeInvalidArgument): EdgeDefault.is_deployment_supported('Linux', None) with self.assertRaises(edgectl.errors.EdgeInvalidArgument): EdgeDefault.is_deployment_supported(None, 'docker')
def test_get_home_dir_valid(self): """Test valid host OS platform returns a non empty string""" path = EdgeDefault.get_home_dir('linux') self.assertIsNotNone(path) self.assertGreater(len(path), 0) path = EdgeDefault.get_home_dir('darwin') self.assertIsNotNone(path) self.assertGreater(len(path), 0) path = EdgeDefault.get_home_dir('windows') self.assertIsNotNone(path) self.assertGreater(len(path), 0)
def test_def_settings_json_except(self): """ Test if exceptions raised when unable to read the JSON config or parse it. """ with patch('edgectl.config.EdgeDefault.get_default_settings_file_path', MagicMock(return_value='bad_file.json')): with self.assertRaises(edgectl.errors.EdgeFileAccessError): EdgeDefault.get_default_settings_json() with patch('json.load', MagicMock(return_value=None)) as json_load_mock: json_load_mock.side_effect = ValueError() with self.assertRaises(edgectl.errors.EdgeFileParseError): EdgeDefault.get_default_settings_json()
def test_get_config_dir_valid(self): """Test valid host OS platform returns a non empty directory""" dir_name = EdgeDefault.get_config_dir('windows') self.assertIsNotNone(dir_name) self.assertGreater(len(dir_name), 0) dir_name = EdgeDefault.get_config_dir('linux') self.assertIsNotNone(dir_name) self.assertGreater(len(dir_name), 0) dir_name = EdgeDefault.get_config_dir('darwin') self.assertIsNotNone(dir_name) self.assertGreater(len(dir_name), 0)
def test_get_deployments_valid(self): """" Test deployments list for valid host OS platforms """ test_deployments = EdgeDefault.get_supported_deployments('linux') self.assertIsNotNone(test_deployments) self.assertIn(EdgeConstants.DEPLOYMENT_DOCKER_KEY, test_deployments) test_deployments = EdgeDefault.get_supported_deployments('darwin') self.assertIsNotNone(test_deployments) self.assertIn(EdgeConstants.DEPLOYMENT_DOCKER_KEY, test_deployments) test_deployments = EdgeDefault.get_supported_deployments('windows') self.assertIsNotNone(test_deployments) self.assertIn(EdgeConstants.DEPLOYMENT_DOCKER_KEY, test_deployments)
def test_get_edge_diag_path_valid(self): """Test valid host OS platform returns a non empty string""" path = EdgeDefault.get_edge_ctl_diagnostic_path('windows') self.assertIsNotNone(path) self.assertEqual('%%USERPROFILE%%\\.iotedgectl', path) path = EdgeDefault.get_edge_ctl_diagnostic_path('linux') self.assertIsNotNone(path) self.assertEqual('$HOME/.iotedgectl', path) self.assertGreater(len(path), 0) path = EdgeDefault.get_edge_ctl_diagnostic_path('darwin') self.assertIsNotNone(path) self.assertEqual('$HOME/.iotedgectl', path)
def test_get_docker_ctr_type_valid(self): """Test valid host OS platform docker container types""" types = EdgeDefault.get_docker_container_types('windows') self.assertTrue(len(types) == 2) self.assertTrue('linux' in types) self.assertTrue('windows' in types) types = EdgeDefault.get_docker_container_types('darwin') self.assertTrue(len(types) == 1) self.assertTrue('linux' in types) types = EdgeDefault.get_docker_container_types('linux') self.assertTrue(len(types) == 1) self.assertTrue('linux' in types)
def _setup_meta_edge_config_dir(edge_config_dir): meta_config_dir = EdgeDefault.get_edge_ctl_config_dir() if os.path.isdir(meta_config_dir) is False: log.info('Meta config directory does not exist.' \ 'Creating directory: %s', meta_config_dir) EdgeHostPlatform._create_dir(meta_config_dir, 'Edge meta config') if edge_config_dir is None: edge_config_dir = '' meta_config_dict = {EC.CONFIG_DIR_KEY: edge_config_dir} json_data = json.dumps(meta_config_dict, indent=2, sort_keys=True) meta_config_file_path = EdgeDefault.get_meta_conf_file_path() EdgeHostPlatform._create_config_file(meta_config_file_path, json_data, 'Edge meta config file')
def test_docker_uri_valid(self): """ Get the docker URI for valid host and container types """ test_uri = EdgeDefault.get_docker_uri('linux', 'linux') self.assertIsNotNone(test_uri) linux_test_uri = test_uri self.assertEqual('unix:///var/run/docker.sock', linux_test_uri) test_uri = EdgeDefault.get_docker_uri('darwin', 'linux') self.assertIsNotNone(test_uri) self.assertEqual(linux_test_uri, test_uri) test_uri = EdgeDefault.get_docker_uri('Windows', 'linux') self.assertIsNotNone(test_uri) self.assertEqual(linux_test_uri, test_uri) test_uri = EdgeDefault.get_docker_uri('Windows', 'Windows') self.assertIsNotNone(test_uri) self.assertEqual('npipe://./pipe/docker_engine', test_uri)
def test_self_signed_certificate_subject_missing_fields_valid(self): """ Tests getter and setter for certificate subject field. Tests if certificate subject field is not set then expected defaults are chosen. This test applicable for the auto generated self signed certificate flow. """ test_cases = [] subj_dict = { EC.SUBJECT_COUNTRY_KEY: 'TC', EC.SUBJECT_STATE_KEY: 'Test State', EC.SUBJECT_LOCALITY_KEY: 'Test Locality', EC.SUBJECT_ORGANIZATION_KEY: 'Test Organization', EC.SUBJECT_ORGANIZATION_UNIT_KEY: 'Test Unit', EC.SUBJECT_COMMON_NAME_KEY: 'Test CommonName' } default_subj_dict = EdgeDefault.get_certificate_subject_dict() all_subj_keys = [ EC.SUBJECT_COUNTRY_KEY, EC.SUBJECT_STATE_KEY, EC.SUBJECT_LOCALITY_KEY, EC.SUBJECT_ORGANIZATION_KEY, EC.SUBJECT_ORGANIZATION_UNIT_KEY, EC.SUBJECT_COMMON_NAME_KEY ] for subj_key in all_subj_keys: subj_ip_dict = subj_dict.copy() del subj_ip_dict[subj_key] subj_op_dict = subj_dict.copy() subj_op_dict[subj_key] = default_subj_dict[subj_key] self._add_subj_test_case(test_cases, False, EC.SELFSIGNED_KEY, subj_ip_dict, subj_op_dict) self._valid_test_cases_helper('cert subj missing field', test_cases)
def execute_user_command(self): """This is the main function that implements the CLI. Overall flow: 1. Read, validate and process the input arguments. 2. Update configuration files on the host based on user input. 3. Construct the deployment specific command object to execute the the user input command. 4. Log any status, progress and errors along the way to stdout 5. Return to caller with an error code. 0 -- Success Non Zero -- Error """ error_code = 1 host = platform.system() if EdgeDefault.is_host_supported(host) is False: log.error('Unsupported host platform: %s.', host) elif EdgeHostPlatform.is_deployment_supported(EC.DEPLOYMENT_DOCKER) is False: log.error('Docker is not installed or is unavailable. Please ensure docker is installed and is up and running.') else: try: if self._process_cli_args(): self._execute_command() error_code = 0 except edgectl.errors.EdgeError: log.debug('Errors observed running %s command.', self._prog()) if error_code != 0: log.error('Exiting with errors. Return code: %s', str(error_code)) return error_code
def _get_edge_ctl_config_dir_helper(self, host, dirpath, test_config_file=None): test_path = None if dirpath: test_path = os.path.join(dirpath, '.iotedgectl') with patch('platform.system', MagicMock(return_value=host)): with patch('os.getenv', MagicMock(return_value=dirpath)): result_path = EdgeDefault.get_edge_ctl_config_dir() self.assertIsNotNone(result_path) self.assertEqual(test_path, result_path) if dirpath and test_config_file: test_path = os.path.join(test_path, test_config_file) result_path = EdgeDefault.get_meta_conf_file_path() self.assertEqual(test_path, result_path)
def test_self_signed_default_certificate_subject_chosen_valid(self): """ Tests getter and setter for certificate subject. Tests if certificate subject is None that expected defaults are chosen. This test applicable for the auto generated self signed certificate flow. """ test_cases = [] self._add_subj_test_case(test_cases, False, EC.SELFSIGNED_KEY, None, EdgeDefault.get_certificate_subject_dict()) self._valid_test_cases_helper('cert subj defaults', test_cases)
def get_host_config_file_path(): result = None edge_config_dir = EdgeHostPlatform._get_host_config_dir() if edge_config_dir: edge_config_file_path = os.path.join(edge_config_dir, EdgeDefault.get_config_file_name()) if os.path.exists(edge_config_file_path): result = edge_config_file_path return result
def _validation_helper_certificate_subject(self, result_config_object, test_case, msg=None): op_subj_dict = test_case[TEST_OP_KEY][EC.CERTS_SUBJECT_KEY] if op_subj_dict is None: op_subj_dict = EdgeDefault.get_certificate_subject_dict() result_subj_dict = result_config_object.certificate_subject_dict self.assertEqual(op_subj_dict, result_subj_dict, msg)
def test_is_host_supported_valid(self): """Test valid host OS platforms returns True""" self.assertTrue(EdgeDefault.is_host_supported('windows')) self.assertTrue(EdgeDefault.is_host_supported('Windows')) self.assertTrue(EdgeDefault.is_host_supported('Linux')) self.assertTrue(EdgeDefault.is_host_supported('linux')) self.assertTrue(EdgeDefault.is_host_supported('Darwin')) self.assertTrue(EdgeDefault.is_host_supported('darwin'))
def test_preinstalled_certificate_subject_valid(self): """ Tests getter and setter for certificate subject. This test applicable for the preinstalled certificate flow. Regardless of setting a certificate subject, its getter should return None. """ test_cases = [] ip_kwargs = { KWARG_OWNER_CERT: OWNER_CERT_FILE_NAME, KWARG_DCA_CERT: DEVICE_CA_CERT_FILE_NAME, KWARG_DCA_CHAIN: DEVICE_CA_CHAIN_CERT_FILE_NAME, KWARG_DCA_PK: DEVICE_CA_PRIVATE_KEY_FILE_NAME } op_kwargs = { KWARG_OWNER_CERT: OWNER_CERT_FILE_NAME, KWARG_DCA_CERT: DEVICE_CA_CERT_FILE_NAME, KWARG_DCA_CHAIN: DEVICE_CA_CHAIN_CERT_FILE_NAME, KWARG_DCA_PK: DEVICE_CA_PRIVATE_KEY_FILE_NAME, KWARG_DCA_PASS: None, KWARG_AGT_PASS: None, KWARG_DCA_PASS_FILE: None, KWARG_AGT_PASS_FILE: None } subj_dict = { EC.SUBJECT_COUNTRY_KEY: 'TC', EC.SUBJECT_STATE_KEY: 'Test State', EC.SUBJECT_LOCALITY_KEY: 'Test Locality', EC.SUBJECT_ORGANIZATION_KEY: 'Test Organization', EC.SUBJECT_ORGANIZATION_UNIT_KEY: 'Test Unit', EC.SUBJECT_COMMON_NAME_KEY: 'Test CommonName' } self._add_subj_test_case(test_cases, False, EC.PREINSTALL_KEY, subj_dict, subj_dict, ip_kwargs_dict=ip_kwargs, op_kwargs_dict=op_kwargs) default_subj_dict = EdgeDefault.get_certificate_subject_dict() self._add_subj_test_case(test_cases, False, EC.PREINSTALL_KEY, None, default_subj_dict, ip_kwargs_dict=ip_kwargs, op_kwargs_dict=op_kwargs) with patch('edgectl.utils.EdgeUtils.check_if_file_exists', MagicMock(return_value=True)): self._valid_test_cases_helper('cert subj', test_cases)
def _parse_setup_options(self, args): cmd = args.subparser_name log.debug('Command: ' + cmd) is_valid = False if args.config_file is not None: # we are using options specified in the config file try: is_valid = self._parse_and_validate_user_input_config_file( args) except edgectl.errors.EdgeValueError as ex_value: log.error('Please check the configuration values in the config file' \ ' and re-run \'%s setup\'', EdgeCLI._prog()) raise edgectl.errors.EdgeError( 'Error when parsing configuration data', ex_value) except edgectl.errors.EdgeFileParseError as ex_parse: log.error('Please check the configuration in config file: %s' \ ' and re-run \'%s setup\'', ex_parse.file_name, EdgeCLI._prog()) raise edgectl.errors.EdgeError( 'Error when parsing configuration data', ex_parse) else: # we are using cli options, validate all the supplied args try: is_valid = self._parse_and_validate_user_input(args) except edgectl.errors.EdgeValueError as ex_value: log.error( 'Please fix any input values and re-run \'%s setup\'', EdgeCLI._prog()) raise edgectl.errors.EdgeError('Incorrect input options', ex_value) except edgectl.errors.EdgeFileParseError as ex_parse: log.critical( 'Please restore the config file or reinstall the %s utility.', EdgeCLI._prog()) raise edgectl.errors.EdgeError( 'Error when parsing configuration data', ex_parse) except edgectl.errors.EdgeFileAccessError as ex_access: if ex_access.file_name == EdgeDefault.get_default_settings_file_path( ): log.critical( 'Please restore the config file or reinstall the %s utility.', EdgeCLI._prog()) raise edgectl.errors.EdgeError('Filesystem access errors', ex_access) if is_valid: config_file = EdgeHostPlatform.get_host_config_file_path() log.info('The runtime configuration file %s was updated with' \ ' the ''setup'' options.', config_file) return is_valid
def _setup_edge_config_dir(edge_config): edge_config_dir = edge_config.config_dir if edge_config.config_dir_source == EdgeConfigDirInputSource.USER_PROVIDED: EdgeHostPlatform._setup_meta_edge_config_dir(edge_config_dir) else: EdgeHostPlatform._clear_edge_meta_config_dir() if os.path.exists(edge_config_dir) is False: log.info('IoT Edge Config directory does not exist.' \ 'Creating directory: %s', edge_config_dir) EdgeHostPlatform._create_dir(edge_config_dir, 'Edge config') edge_config_file_path = os.path.join(edge_config_dir, EdgeDefault.get_config_file_name()) return edge_config_file_path
def test_cert_subject_dict_valid(self): """ Test if the certificate subject dict is valid and as expected""" test_dict = EdgeDefault.get_certificate_subject_dict() self.assertIn(EdgeConstants.SUBJECT_COUNTRY_KEY, test_dict) self.assertIsNotNone(test_dict[EdgeConstants.SUBJECT_COUNTRY_KEY]) self.assertIn(EdgeConstants.SUBJECT_STATE_KEY, test_dict) self.assertIsNotNone(test_dict[EdgeConstants.SUBJECT_STATE_KEY]) self.assertIn(EdgeConstants.SUBJECT_LOCALITY_KEY, test_dict) self.assertIsNotNone(test_dict[EdgeConstants.SUBJECT_LOCALITY_KEY]) self.assertIn(EdgeConstants.SUBJECT_ORGANIZATION_KEY, test_dict) self.assertIsNotNone(test_dict[EdgeConstants.SUBJECT_ORGANIZATION_KEY]) self.assertIn(EdgeConstants.SUBJECT_ORGANIZATION_UNIT_KEY, test_dict) self.assertIsNotNone( test_dict[EdgeConstants.SUBJECT_ORGANIZATION_UNIT_KEY]) self.assertIn(EdgeConstants.SUBJECT_COMMON_NAME_KEY, test_dict) self.assertIsNotNone(test_dict[EdgeConstants.SUBJECT_COMMON_NAME_KEY])
def choose_platform_config_dir(user_input_path, user_input_option): """ Utility function that chooses a Edge config directory in the precedence order of: 1) Env variable EDGECONFIGDIR 2) User input via user_input_path 3) Default Path Args: user_input_path: (string) A user supplied config dir path. Can be None or ''. user_input_option: (enum EdgeConfigDirInputSource member): Use NONE when user_input_path is None or empty Return: Tuple: [0]: Path to the Edge config dir. [1]: An EdgeConfigDirInputSource enum member indicating which dir was chosen """ edge_config_dir = None choice = None env_config_dir = os.getenv(EC.ENV_EDGECONFIGDIR, None) if env_config_dir and env_config_dir.strip() != '': edge_config_dir = os.path.realpath(env_config_dir) log.info('Using environment variable %s as IoT Edge configuration dir: %s', EC.ENV_EDGECONFIGDIR, edge_config_dir) choice = EdgeConfigDirInputSource.ENV elif user_input_path and user_input_path.strip() != '': edge_config_dir = os.path.realpath(user_input_path) log.info('Using user configured IoT Edge configuration dir: %s', edge_config_dir) choice = user_input_option else: edge_config_dir = os.path.realpath(EdgeDefault.get_config_dir(platform.system())) log.info('Using default IoT Edge configuration dir: %s', edge_config_dir) choice = EdgeConfigDirInputSource.DEFAULT return (edge_config_dir, choice)
def parse(self, ignored=None): args = self._input_args try: defaults_json = EdgeDefault.get_default_settings_json() cs = args.connection_string if cs is None or len(cs) == 0: raise ValueError('Please specify the device connection string' \ ' using the --connection-string option') config = EdgeHostConfig() config.schema_version = defaults_json[EC.SCHEMA_KEY] config.connection_string = cs cfg_src = EdgeConfigDirInputSource.USER_PROVIDED cfg_dir_opt = EdgeHostPlatform.choose_platform_config_dir(args.edge_config_dir, cfg_src) config.config_dir = cfg_dir_opt[0] config.config_dir_source = cfg_dir_opt[1] home_dir = args.edge_home_dir if home_dir is None: home_dir = EdgeHostPlatform.get_home_dir() config.home_dir = home_dir hostname = args.edge_hostname if hostname is None: hostname = EdgeUtils.get_hostname() config.hostname = hostname log_level = args.runtime_log_level if log_level is None: log_level = EdgeDefault.get_default_runtime_log_level() config.log_level = log_level upstream_protocol = args.upstream_protocol if upstream_protocol is None: config.upstream_protocol = EdgeUpstreamProtocol.NONE else: config.upstream_protocol = upstream_protocol deploy_cfg = None if self._deployment_type == EC.DEPLOYMENT_DOCKER: deploy_cfg = EdgeDeploymentConfigDocker() docker_deploy_data = \ defaults_json[EC.DEPLOYMENT_KEY][EC.DEPLOYMENT_DOCKER_KEY] registries = args.docker_registries if registries is None: registries = docker_deploy_data[EC.REGISTRIES_KEY] for registry in registries: deploy_cfg.add_registry(registry[EC.REGISTRY_ADDRESS_KEY], registry[EC.REGISTRY_USERNAME_KEY], registry[EC.REGISTRY_PASSWORD_KEY]) else: idx = 0 address = '' username = '' password = '' for item in registries: if idx == 0: address = item elif idx == 1: username = item else: password = item deploy_cfg.add_registry(address, username, password) idx = (idx + 1) % 3 image = args.image if image is None: image = docker_deploy_data[EC.EDGE_RUNTIME_IMAGE_KEY] deploy_cfg.edge_image = image uri = args.docker_uri if uri is None: uri = EdgeHostPlatform.get_docker_uri() deploy_cfg.uri = uri docker_log_cfg = docker_deploy_data[EC.DOCKER_LOGGING_OPTS_KEY] deploy_cfg.logging_driver = \ docker_log_cfg[EC.DOCKER_LOGGING_DRIVER_KEY] driver_log_opts = \ docker_log_cfg[EC.DOCKER_LOGGING_DRIVER_OPTS_KEY] for opt_key, opt_val in list(driver_log_opts.items()): deploy_cfg.add_logging_option(opt_key, opt_val) if deploy_cfg is None: raise ValueError('Unsupported deployment type: %s', self._deployment_type) config.deployment_config = deploy_cfg subj_dict = {} if args.country: subj_dict[EC.SUBJECT_COUNTRY_KEY] = args.country if args.state: subj_dict[EC.SUBJECT_STATE_KEY] = args.state if args.locality: subj_dict[EC.SUBJECT_LOCALITY_KEY] = args.locality if args.organization: subj_dict[EC.SUBJECT_ORGANIZATION_KEY] = args.organization if args.organization_unit: subj_dict[EC.SUBJECT_ORGANIZATION_UNIT_KEY] = args.organization_unit if args.common_name: subj_dict[EC.SUBJECT_COMMON_NAME_KEY] = args.common_name cert_config = EdgeCertConfig() cert_config.set_options(args.auto_cert_gen_force_no_passwords, subj_dict, owner_ca_cert_file=args.owner_ca_cert_file, device_ca_cert_file=args.device_ca_cert_file, device_ca_chain_cert_file=args.device_ca_chain_cert_file, device_ca_private_key_file=args.device_ca_private_key_file, device_ca_passphrase=args.device_ca_passphrase, device_ca_passphrase_file=args.device_ca_passphrase_file, agent_ca_passphrase=args.agent_ca_passphrase, agent_ca_passphrase_file=args.agent_ca_passphrase_file) config.certificate_config = cert_config return config except ValueError as ex_value: log.error('Error parsing user input data: %s.', str(ex_value)) raise edgectl.errors.EdgeValueError('Error parsing user input data')
def test_get_config_dir_invalid(self): """Test invalid host OS platform returns None""" dir_name = EdgeDefault.get_config_dir('blah') self.assertIsNone(dir_name) dir_name = EdgeDefault.get_config_dir('') self.assertIsNone(dir_name)
def test_get_docker_ctr_type_except(self): """Test None as host OS raises edgectl.errors.EdgeInvalidArgument""" with self.assertRaises(edgectl.errors.EdgeInvalidArgument): EdgeDefault.get_docker_container_types(None)