diff --git a/fuelclient/cli/error.py b/fuelclient/cli/error.py index a878242..e7e24b8 100644 --- a/fuelclient/cli/error.py +++ b/fuelclient/cli/error.py @@ -92,6 +92,14 @@ class LabelEmptyKeyError(BadDataException): """Should be raised when user provides labels with empty key.""" +class InvalidDirectoryException(FuelClientException): + pass + + +class InvalidFileException(FuelClientException): + pass + + def exceptions_decorator(func): """Handles HTTP errors and expected exceptions that may occur in methods of APIClient class diff --git a/fuelclient/cli/serializers.py b/fuelclient/cli/serializers.py index f3b1f07..33ad6e8 100644 --- a/fuelclient/cli/serializers.py +++ b/fuelclient/cli/serializers.py @@ -88,16 +88,25 @@ class Serializer(object): def write_to_path(self, path, data): full_path = self.prepare_path(path) - with open(full_path, "w+") as file_to_write: - self.write_to_file(file_to_write, data) + try: + with open(full_path, "w") as file_to_write: + self.write_to_file(file_to_write, data) + except IOError as e: + raise error.InvalidFileException( + "Can't write to file '{0}': {1}.".format( + full_path, e.strerror)) return full_path def read_from_file(self, path): return self.read_from_full_path(self.prepare_path(path)) def read_from_full_path(self, full_path): - with open(full_path, "r") as file_to_read: - return self.serializer["r"](file_to_read.read()) + try: + with open(full_path, "r") as file_to_read: + return self.serializer["r"](file_to_read.read()) + except IOError as e: + raise error.InvalidFileException( + "Can't open file '{0}': {1}.".format(full_path, e.strerror)) def write_to_file(self, file_obj, data): """Writes to opened file or file like object diff --git a/fuelclient/objects/environment.py b/fuelclient/objects/environment.py index 7c4bc1d..50bcefc 100644 --- a/fuelclient/objects/environment.py +++ b/fuelclient/objects/environment.py @@ -17,6 +17,7 @@ import os import shutil from fuelclient.cli.error import ActionException +from fuelclient.cli.error import InvalidDirectoryException from fuelclient.cli.error import ServerDataException from fuelclient.cli.serializers import listdir_without_extensions from fuelclient.objects.base import BaseObject @@ -156,6 +157,7 @@ class Environment(BaseObject): def write_network_data(self, network_data, directory=os.curdir, serializer=None): + self._check_dir(directory) return (serializer or self.serializer).write_to_path( self.get_network_data_path(directory), network_data @@ -163,6 +165,7 @@ class Environment(BaseObject): def write_settings_data(self, settings_data, directory=os.curdir, serializer=None): + self._check_dir(directory) return (serializer or self.serializer).write_to_path( self.get_settings_data_path(directory), settings_data @@ -171,6 +174,7 @@ class Environment(BaseObject): def write_vmware_settings_data(self, settings_data, directory=None, serializer=None): directory = directory or os.curdir + self._check_dir(directory) return (serializer or self.serializer).write_to_path( self.get_vmware_settings_data_path(directory), settings_data @@ -186,22 +190,34 @@ class Environment(BaseObject): def read_network_data(self, directory=os.curdir, serializer=None): + self._check_dir(directory) network_file_path = self.get_network_data_path(directory) return (serializer or self.serializer).read_from_file( network_file_path) def read_settings_data(self, directory=os.curdir, serializer=None): + self._check_dir(directory) settings_file_path = self.get_settings_data_path(directory) return (serializer or self.serializer).read_from_file( settings_file_path) + def _check_dir(self, directory): + if not os.path.exists(directory): + raise InvalidDirectoryException( + "Directory '{0}' doesn't exist.".format(directory)) + if not os.path.isdir(directory): + raise InvalidDirectoryException( + "Error: '{0}' is not a directory.".format(directory)) + def read_vmware_settings_data(self, directory=os.curdir, serializer=None): + self._check_dir(directory) return (serializer or self.serializer).read_from_file( self.get_vmware_settings_data_path(directory)) def read_network_template_data(self, directory=os.curdir, serializer=None): """Used by 'fuel' command line utility.""" + self._check_dir(directory) network_template_file_path = self.get_network_template_data_path( directory) return self.read_network_template_data_from_file( @@ -366,7 +382,9 @@ class Environment(BaseObject): def read_deployment_info(self, fact_type, directory=os.path.curdir, serializer=None): + self._check_dir(directory) dir_name = self._get_fact_dir_name(fact_type, directory=directory) + self._check_dir(dir_name) return map( lambda f: (serializer or self.serializer).read_from_file(f), [os.path.join(dir_name, json_file) diff --git a/fuelclient/tests/functional/v1/test_client.py b/fuelclient/tests/functional/v1/test_client.py index c7a799b..02e597b 100644 --- a/fuelclient/tests/functional/v1/test_client.py +++ b/fuelclient/tests/functional/v1/test_client.py @@ -435,3 +435,80 @@ class TestDeployChanges(base.BaseTestCase): add_node = "--env-id=1 node set --node 1 --role=controller" deploy_changes = "deploy-changes --env 1" self.run_cli_commands((env_create, add_node, deploy_changes)) + + +class TestDirectoryDoesntExistErrorMessages(base.BaseTestCase): + + def test_settings_upload(self): + self.check_for_stderr( + "settings --upload --dir /foo/bar/baz --env 1", + "Directory '/foo/bar/baz' doesn't exist.\n", + check_errors=False + ) + + def test_deployment_upload(self): + self.check_for_stderr( + "deployment --upload --dir /foo/bar/baz --env 1", + "Directory '/foo/bar/baz' doesn't exist.\n", + check_errors=False + ) + + def test_net_upload(self): + self.check_for_stderr( + "net --upload --dir /foo/bar/baz --env 1 network", + "Directory '/foo/bar/baz' doesn't exist.\n", + check_errors=False + ) + + def test_env_download(self): + self.load_data_to_nailgun_server() + release_id = self.get_first_deployable_release_id() + self.run_cli_commands(( + "env create --name=NewEnv --release={0}".format(release_id), + "--env-id=1 node set --node 2 --role=controller" + )) + self.check_for_stderr( + "network --download --dir /foo/bar/baz --env 1", + "Directory '/foo/bar/baz' doesn't exist.\n", + check_errors=False + ) + + def test_download_network_configuration(self): + self.load_data_to_nailgun_server() + release_id = self.get_first_deployable_release_id() + self.run_cli_commands(( + "env create --name=NewEnv --release={0}".format(release_id), + "--env-id=1 node set --node 2 --role=controller" + )) + self.check_for_stderr( + "--env 1 network --download --dir /foo/bar/baz", + "Directory '/foo/bar/baz' doesn't exist.\n", + check_errors=False + ) + + def test_download_default_settings(self): + self.load_data_to_nailgun_server() + release_id = self.get_first_deployable_release_id() + self.run_cli_commands(( + "env create --name=NewEnv --release={0}".format(release_id), + "--env-id=1 node set --node 2 --role=controller" + )) + self.check_for_stderr( + "--env 1 settings --default --dir /foo/bar/baz", + "Directory '/foo/bar/baz' doesn't exist.\n", + check_errors=False + ) + + def test_upload_network_configuration(self): + self.check_for_stderr( + "--env 1 network --upload --dir /foo/bar/baz", + "Directory '/foo/bar/baz' doesn't exist.\n", + check_errors=False + ) + + def test_upload_network_template(self): + self.check_for_stderr( + "--env 1 network-template --upload --dir /foo/bar/baz", + "Directory '/foo/bar/baz' doesn't exist.\n", + check_errors=False + ) diff --git a/fuelclient/tests/unit/common/test_serializers.py b/fuelclient/tests/unit/common/test_serializers.py index 64d73b5..c6f8f5d 100644 --- a/fuelclient/tests/unit/common/test_serializers.py +++ b/fuelclient/tests/unit/common/test_serializers.py @@ -18,6 +18,7 @@ import json import mock import yaml +from fuelclient.cli import error from fuelclient.cli.serializers import Serializer from fuelclient.tests import base @@ -55,3 +56,11 @@ class TestSerializers(base.UnitTestCase): serialized = serialize(self.DATA) deserialized = Serializer(format).deserialize(serialized) self.assertEqual(self.DATA, deserialized) + + def test_write_to_path_invalid_file_exception(self): + serializer = Serializer('json') + with self.assertRaises(error.InvalidFileException): + mo = mock.mock_open() + with mock.patch('__main__.open', mo, create=True) as mocked_open: + mocked_open.side_effect = IOError() + serializer.write_to_path('/foo/bar/baz', self.DATA)