Index: /branches/ipp-132_automate_jira_conf_backups/backups/backup_atlassian_applications.py
===================================================================
--- /branches/ipp-132_automate_jira_conf_backups/backups/backup_atlassian_applications.py	(revision 40896)
+++ /branches/ipp-132_automate_jira_conf_backups/backups/backup_atlassian_applications.py	(revision 40897)
@@ -13,8 +13,8 @@
 # a restructure would be needed
 # Ideally, the utils should be added to the path.
-import utils.subprocess_utils as sub_utils
-
-
-DEFAULT_CONFIG_FILE = path.abspath('./atlassian_backups.config')
+import subprocess_utils as sub_utils
+
+
+DEFAULT_CONFIG_FILE = path.join(path.dirname(__file__), 'atlassian_backups.config')
 
 
@@ -236,5 +236,5 @@
         jira_tar_name = datetime.date.today().strftime("%Y_%m_%d") + "_jira_attachments.tar"
         jira_tar_filepath = path.join(latest_paths[0], jira_tar_name)
-        jira_tar_args = ["-cf", jira_tar_filepath, self.jira_attachments_path]
+        jira_tar_args = ["-C", "/", "-cf", jira_tar_filepath, self.jira_attachments_path.strip('/')]
         sub_utils.tar_wrapper(jira_tar_args)
         sub_utils.chmod_wrapper(['774', jira_tar_filepath])
@@ -246,5 +246,5 @@
         confluence_tar_name = datetime.date.today().strftime("%Y_%m_%d") + "_confluence_attachments.tar"
         confluence_tar_filepath = path.join(latest_paths[0], confluence_tar_name)
-        confluence_tar_args = ["-cf", confluence_tar_filepath, self.conf_attachments_path]
+        confluence_tar_args = ["-C", "/", "-cf", confluence_tar_filepath, self.conf_attachments_path.strip('/')]
         sub_utils.tar_wrapper(confluence_tar_args)
         sub_utils.chmod_wrapper(['774', confluence_tar_filepath])
Index: /branches/ipp-132_automate_jira_conf_backups/backups/backup_atlassian_applications_test.py
===================================================================
--- /branches/ipp-132_automate_jira_conf_backups/backups/backup_atlassian_applications_test.py	(revision 40896)
+++ /branches/ipp-132_automate_jira_conf_backups/backups/backup_atlassian_applications_test.py	(revision 40897)
@@ -8,9 +8,9 @@
 
 import full_atlassian_backup_test as fab_test
-import utils.subprocess_utils as sub_utils
+import subprocess_utils as sub_utils
 
 from backup_atlassian_applications import AtlassianBackups
 
-TEST_DEFAULT_CONFIG_FILE = "./testing/test.config"
+TEST_DEFAULT_CONFIG_FILE = os.path.join(os.path.dirname(__file__), './testing/test.config')
 
 
@@ -47,5 +47,6 @@
 
     def test_default_config_loads_with_no_args(self):
-        assert os.path.exists("./atlassian_backups.config")
+        default_config = baa.DEFAULT_CONFIG_FILE
+        assert os.path.exists(default_config)
         ab = AtlassianBackups()
         assert ab.host_backup_path      == "/export/ippops4.0/atlassian_backups"
Index: /branches/ipp-132_automate_jira_conf_backups/backups/full_atlassian_backup_test.py
===================================================================
--- /branches/ipp-132_automate_jira_conf_backups/backups/full_atlassian_backup_test.py	(revision 40896)
+++ /branches/ipp-132_automate_jira_conf_backups/backups/full_atlassian_backup_test.py	(revision 40897)
@@ -6,7 +6,4 @@
 
 from backup_atlassian_applications import AtlassianBackups
-
-TEST_DEFAULT_CONFIG_FILE = "./testing/test.config"
-
 
 def create_all_paths_and_folders(ab_class: AtlassianBackups, tmpdir) -> AtlassianBackups:
@@ -140,5 +137,7 @@
 
     def test_entire_process(self, tmpdir):
-        ab = AtlassianBackups(config_file=TEST_DEFAULT_CONFIG_FILE)
+        test_config_file = os.path.join(os.path.dirname(__file__), 'testing', 'test.config')
+
+        ab = AtlassianBackups(config_file=test_config_file)
         ab = create_all_paths_and_folders(ab, tmpdir)
 
Index: anches/ipp-132_automate_jira_conf_backups/backups/utils
===================================================================
--- /branches/ipp-132_automate_jira_conf_backups/backups/utils	(revision 40896)
+++ 	(revision )
@@ -1,1 +1,0 @@
-link ../utils/python
Index: /branches/ipp-132_automate_jira_conf_backups/utils/python/generic_utils.py
===================================================================
--- /branches/ipp-132_automate_jira_conf_backups/utils/python/generic_utils.py	(revision 40896)
+++ /branches/ipp-132_automate_jira_conf_backups/utils/python/generic_utils.py	(revision 40897)
@@ -16,19 +16,16 @@
 
     if source_file is None or destination_path is None:
-        print("Error: source for copy cannot be None type")
-        raise ValidationError
+        raise ValidationError("Error: source for copy cannot be None type")
     if destination_path is None:
-        print("Error: copy destination cannot be None type")
-        raise ValidationError
+        raise ValidationError("Error: copy destination cannot be None type")
     if not path.exists(source_file):
-        print("Error: Copy Failure: source does not exist: ", source_file)
-        raise ValidationError
+        raise ValidationError(f"Error: Copy Failure: source does not exist: {source_file}")
     if not path.exists(destination_path) or not path.isdir(destination_path):
         print("Error: Copy Failure: destination path does not exist: ", destination_path)
-        raise ValidationError
+        raise ValidationError(f"Error: Copy Failure: destination path does not exist: {destination_path}")
 
     try:
         if verbose:
-            print("Copying: {0} to {1}".format(source_file, destination_path))
+            print(f"Copying: {source_file} to {destination_path}")
         if path.isdir(source_file):
             copy_tree(source_file, destination_path)
@@ -36,7 +33,6 @@
             shutil.copy2(source_file, destination_path)
     except IOError as e:
-        print("Error: copy failure.\n  copying: {0}\n  to: {1}".format(e.filename, e.filename2))
-        print(e)
-        raise IOError
+        msg = f"Error: copy failure.\n  copying: {source_file}\n  to: {destination_path}"
+        raise IOError(msg) from e
     return 0
 
@@ -48,23 +44,21 @@
 
     if source_paths is None:
-        print("Error: source for copy cannot be None type")
-        raise ValidationError
+        raise ValidationError("Error: source for copy cannot be None type")
     if destination_paths is None:
-        print("Error: copy destination cannot be None type")
-        raise ValidationError
+        raise ValidationError("Error: copy destination cannot be None type")
 
     if type(source_paths) is not list or len(source_paths) == 0:
-        print("Error: sources must be given as a list with at least one entry")
-        raise ValidationError
+        raise ValidationError("Error: sources must be given as a list with at least one entry")
     if type(destination_paths) is not list or len(destination_paths) == 0:
-        print("Error: destinations must be given as a list with at least one entry")
-        raise ValidationError
+        raise ValidationError("Error: destinations must be given as a list with at least one entry")
 
     for dpath in destination_paths:
         if not path.exists(dpath):
-            print("Copy Warning: target path does not exist (skipping): ", dpath)
+            if verbose:
+                print("Copy Warning: target path does not exist (skipping): ", dpath)
             continue
         if not path.isdir(dpath):
-            print("Copy Warning: target path is not a directory (skipping): ", )
+            if verbose:
+                print("Copy Warning: target path is not a directory (skipping): ", )
             continue
 
Index: /branches/ipp-132_automate_jira_conf_backups/utils/python/subprocess_utils.py
===================================================================
--- /branches/ipp-132_automate_jira_conf_backups/utils/python/subprocess_utils.py	(revision 40896)
+++ /branches/ipp-132_automate_jira_conf_backups/utils/python/subprocess_utils.py	(revision 40897)
@@ -2,4 +2,5 @@
 
 # Plans are to flesh this out a bit more to make some smarter copying etc
+from generic_utils import ValidationError
 
 
@@ -10,9 +11,7 @@
 def _args_check(args, function_name):
     if args is None:
-        print("Error: no arguments provided in ", function_name)
-        return 2
+        raise ValidationError(f"Error: no arguments provided in {function_name}")
     if type(args) != list or len(args) == 0:
-        print("Error: at least 1 arg must be given for ", function_name)
-        return 2
+        raise ValidationError(f"Error: no arguments provided in {function_name}")
     return 0
 
@@ -20,5 +19,5 @@
 def cp_wrapper(args: [str]):
     if args is None:
-        return 2
+        raise ValidationError(f"Error: no arguments provided to {cp_wrapper.__name__}")
     command_and_args = ["cp"] + args
     return simple_unix_wrapper(command_and_args)
@@ -27,5 +26,5 @@
 def tar_wrapper(args: [str]):
     if args is None:
-        return 2
+        raise ValidationError(f"Error: no arguments provided to {tar_wrapper.__name__}")
     command_and_args = ["tar"] + args
     return simple_unix_wrapper(command_and_args)
@@ -34,5 +33,5 @@
 def chmod_wrapper(args: [str]):
     if args is None:
-        return 2
+        raise ValidationError(f"Error: no arguments provided to {chmod_wrapper.__name__}")
     command_and_args = ["chmod"] + args
     return simple_unix_wrapper(command_and_args)
@@ -44,13 +43,11 @@
 Only a single command should be called.
     """
-    arg_check = _args_check(command_and_args, simple_unix_wrapper.__name__)
-    if arg_check != 0:
-        return arg_check
+    _args_check(command_and_args, simple_unix_wrapper.__name__)
 
     try:
         subprocess.check_call(command_and_args)
-    except subprocess.CalledProcessError as e:
-        print("CalledProcessError in python subprocess: ")
-        print(e)
-        return 2
+    except (subprocess.CalledProcessError) as cpe:
+        raise SubprocessError(f"{type(cpe)} in python simple_unix_wrapper") from cpe
+    except (ValidationError) as ve:
+        raise ValidationError(f"{type(ve)} in python simple_unix_wrapper") from ve
     return 0
Index: /branches/ipp-132_automate_jira_conf_backups/utils/python/subprocess_utils_test.py
===================================================================
--- /branches/ipp-132_automate_jira_conf_backups/utils/python/subprocess_utils_test.py	(revision 40896)
+++ /branches/ipp-132_automate_jira_conf_backups/utils/python/subprocess_utils_test.py	(revision 40897)
@@ -1,6 +1,9 @@
 import subprocess_utils as utils
 import os
+import pytest
+import stat
 
 from pathlib import Path
+from generic_utils import ValidationError
 
 
@@ -15,11 +18,15 @@
 class TestCopyWrapper(object):
 
+    def test_copy_raises_validation_error_with_no_args(self):
+        with pytest.raises(ValidationError):
+            utils.cp_wrapper(None)
+
     def test_copy_fails_with_invalid_args(self, tmpdir):
-        result = utils.cp_wrapper(["not a real file", "not a valid copy_location"])
-        assert result == 2
+        with pytest.raises(utils.SubprocessError):
+            utils.cp_wrapper(["not a real file", "not a valid copy_location"])
 
     def test_copy_fails_if_source_does_not_exist(self, tmpdir):
-        result = utils.cp_wrapper(["not a real file", tmpdir])
-        assert result == 2
+        with pytest.raises(utils.SubprocessError):
+            utils.cp_wrapper(["not a real file", tmpdir])
 
     def test_simple_copy(self, tmpdir):
@@ -68,28 +75,31 @@
         assert not os.path.exists(expected_filepath_b)
 
-        utils.cp_wrapper([subdir, destination_dir])
+        # fails without -r arg:
+        with pytest.raises(utils.SubprocessError):
+            utils.cp_wrapper([subdir, destination_dir])
         assert not os.path.exists(expected_filepath_a)
         assert not os.path.exists(expected_filepath_b)
+
+        utils.cp_wrapper(["-r", subdir, destination_dir])
+        assert os.path.exists(expected_filepath_a)
+        assert os.path.exists(expected_filepath_b)
 
 
 class TestTarWrapper(object):
 
-    def test_tar_returns_safely_with_none_args(self):
-
-        result = utils.tar_wrapper(None)
-        assert result == 2
+    def test_tar_raise_validation_error_with_none_args(self):
+        with pytest.raises(ValidationError):
+            utils.tar_wrapper(None)
 
     def test_tar_returns_safely_with_too_few_args(self):
-
-        result = utils.tar_wrapper(["only one arg"])
-        assert result == 2
+        with pytest.raises(utils.SubprocessError):
+            utils.tar_wrapper(["only one arg"])
 
     def test_tar_return_error_if_invalid_args_given(self, tmpdir):
-
         tar_filename = os.path.join(tmpdir, "tar_file.tar")
         fake_file = os.path.join(tmpdir, "fake_file.txt")
         assert not os.path.exists(fake_file)
-        result = utils.tar_wrapper(["-cf", tar_filename, fake_file])
-        assert result == 2
+        with pytest.raises(utils.SubprocessError):
+            utils.tar_wrapper(["-cf", tar_filename, fake_file])
 
     def test_simple_tar_of_files(self, tmpdir):
@@ -102,4 +112,54 @@
         args = ["-cf", expected_file, file_a, file_b]
         utils.tar_wrapper(args)
+        assert os.path.exists(expected_file)
 
-        assert os.path.exists(expected_file)
+
+class TestChmodWrapper(object):
+
+    def test_chmod_gives_validation_error_when_no_args(self):
+        with pytest.raises(utils.ValidationError):
+            utils.chmod_wrapper(None)
+
+    def test_chmod_wrapper_raises_error_with_invalid_args(self):
+        with pytest.raises(utils.SubprocessError):
+            utils.chmod_wrapper(["only one arg"])
+
+    def test_chmod_fails_if_no_permissions_given(self, tmpdir):
+        real_file = make_a_real_file(tmpdir, 'mr_file')
+
+        with pytest.raises(utils.SubprocessError):
+            utils.chmod_wrapper([real_file])
+
+        with pytest.raises(utils.SubprocessError):
+            utils.chmod_wrapper(["dumb_arg", real_file])
+
+    def test_can_chmod_file(self, tmpdir):
+        real_file = make_a_real_file(tmpdir, 'mr_file')
+
+        # check initial permissions
+        org_stats = os.stat(real_file)
+        # Default should be: rw-rw-r--
+        assert bool(org_stats.st_mode & stat.S_IRUSR) is True
+        assert bool(org_stats.st_mode & stat.S_IWUSR) is True
+        assert bool(org_stats.st_mode & stat.S_IXUSR) is False
+        assert bool(org_stats.st_mode & stat.S_IRGRP) is True
+        assert bool(org_stats.st_mode & stat.S_IWGRP) is True
+        assert bool(org_stats.st_mode & stat.S_IXGRP) is False
+        assert bool(org_stats.st_mode & stat.S_IROTH) is True
+        assert bool(org_stats.st_mode & stat.S_IWOTH) is False
+        assert bool(org_stats.st_mode & stat.S_IXOTH) is False
+
+        # change them
+        utils.chmod_wrapper(["777", real_file])
+
+        # check they changed
+        new_stats = os.stat(real_file)
+        assert bool(new_stats.st_mode & stat.S_IRUSR) is True
+        assert bool(new_stats.st_mode & stat.S_IWUSR) is True
+        assert bool(new_stats.st_mode & stat.S_IXUSR) is True
+        assert bool(new_stats.st_mode & stat.S_IRGRP) is True
+        assert bool(new_stats.st_mode & stat.S_IWGRP) is True
+        assert bool(new_stats.st_mode & stat.S_IXGRP) is True
+        assert bool(new_stats.st_mode & stat.S_IROTH) is True
+        assert bool(new_stats.st_mode & stat.S_IWOTH) is True
+        assert bool(new_stats.st_mode & stat.S_IXOTH) is True
