>From 8af5bac2039459124b0084ab9fba445c3f56733e Mon Sep 17 00:00:00 2001
From: Marcus Lauer <marcuslauer@nyc.rr.com>
Date: Mon, 3 Sep 2012 13:38:25 -0400
Subject: [PATCH 1/4] Make copyfile write to a tempfile on the minion then
 move the file at the end

---
 func/minion/modules/copyfile.py   |   85 +++++++++++++++++++++++++++++--------
 func/overlord/modules/copyfile.py |    6 ++-
 2 files changed, 71 insertions(+), 20 deletions(-)

diff --git a/func/minion/modules/copyfile.py b/func/minion/modules/copyfile.py
index 61c6a4f..8281747 100644
--- a/func/minion/modules/copyfile.py
+++ b/func/minion/modules/copyfile.py
@@ -26,12 +26,12 @@ import time
 import shutil
 
 import func_module
-
+from func import utils as func_utils
 
 class CopyFile(func_module.FuncModule):
 
     version = "0.0.1"
-    api_version = "0.0.2"
+    api_version = "0.0.3"
     description = "Allows for smart copying of a file."
 
     def _checksum_blob(self, blob):
@@ -40,7 +40,6 @@ class CopyFile(func_module.FuncModule):
         return thissum.hexdigest()
 
     def checksum(self, thing):
-
         CHUNK=2**16
         thissum = hashlib.new('sha1')
         if os.path.exists(thing):
@@ -62,7 +61,9 @@ class CopyFile(func_module.FuncModule):
         if not os.path.exists(dirpath):
             os.makedirs(dirpath)
 
-        # Create empty file
+        # Create an empty tempfile in the target directory.
+	writetarget = filepath + '.functmp'
+
         try:
             fo = open(filepath, 'w')
             fo.close()
@@ -71,33 +72,81 @@ class CopyFile(func_module.FuncModule):
             # XXX logger output here
             return -1
 
-        try:
-            # we could intify the mode here if it's a string
-            if mode:
-                os.chmod(filepath, mode)
-            if uid != -1 or gid != -1:
-                os.chown(filepath, uid, gid)
-        except (IOError, OSError), e:
-            return -1
+        """
+        Set the mode and ownership of the tempfile if requested.
+	If not set them to match the target file if the target 
+	file exists.
+	"""
+	if mode or uid != -1 or gid != -1:
+            try:
+                # we could intify the mode here if it's a string
+                if mode:
+                    os.chmod(filepath, mode)
+                if uid != -1 or gid != -1:
+                    os.chown(filepath, uid, gid)
+            except (IOError, OSError), e:
+		return -1
+	elif os.path.exists(filepath):
+            # Copy the mode of the existing file.
+            shutil.copystat(filepath,writetarget)
 
-        return filepath
+            # Copy the uid/gid of the existing file.
+            fileinfo = os.stat(filepath)
+            os.chown(writetarget,fileinfo[4],fileinfo[5])
 
-    def append(self, filepath, filebuf):
-        if not os.path.exists(filepath):
-            # file disaperead
+            # Copy the SELinux attribs of the existing file.
+            import subprocess
+	    try:
+		subprocess.check_call(["chcon", "--reference=" + filepath, writetarget])
+            except (OSError), e:
+                # Do nothing. Presumably SELinux is not enabled on this system.
+
+        return writetarget
+
+    def append(self, open_result, filebuf):
+        hostname = func_utils.get_hostname_by_route()
+        writetarget = open_result[hostname]
+
+	# If the result of the open function was 0 (or -1), do not append, return the same value.
+        if type(writetarget) == type(0):
+            return writetarget
+
+        if not os.path.exists(writetarget):
+            # file disappeared
             return -1
 
         try:
-            fo = open(filepath, 'a')
+            fo = open(writetarget, 'a')
             fo.write(filebuf.data)
             fo.close()
             del fo
         except (IOError, OSError), e:
-            # XXX logger output here
+            # Clean up by removing the tempfile, then return an error.
+            os.unlink(writetarget)
             return -1
 
         return 1
 
+    def move(self, filepath, append_result)
+        hostname = func_utils.get_hostname_by_route()
+	writetarget = append_result[hostname]
+
+        # If the result of the append function was 0 (or -1), just return the same value.
+	if type(filepath) == type(0):
+	    return filepath
+
+        if not os.path.exists(filepath):
+            # file disappeared
+            return -1
+
+        try:
+	    shutil.move(writetarget,filepath)
+	except (IOError, OSError), e:
+	    # Clean up by removing the tempfile, then return an error.
+	    os.unlink(writetarget)
+            return -1
+
+	return 1
 
     def copyfile(self, filepath, filebuf, mode=0644, uid=0, gid=0, force=None):
         # -1 = problem file was not copied
diff --git a/func/overlord/modules/copyfile.py b/func/overlord/modules/copyfile.py
index 9f385a9..9a2bf2b 100644
--- a/func/overlord/modules/copyfile.py
+++ b/func/overlord/modules/copyfile.py
@@ -19,13 +19,15 @@ class copyfile(overlord_module.BaseModule):
         uid = st.st_uid
         gid = st.st_gid
 
-        self.parent.run("copyfile", "open", [remotepath, mode, uid, gid])
+        open_result = self.parent.run("copyfile", "open", [remotepath, mode, uid, gid])
 
         while True:
             data=f.read(bufsize)
             if data:
-                self.parent.run("copyfile", "append", [remotepath, xmlrpclib.Binary(data)])
+                append_result = self.parent.run("copyfile", "append", [open_result, xmlrpclib.Binary(data)])
             else:
                 break
 
+	self.parent.run("copyfile", "move", [remotepath, append_result])
+
         return True
-- 
1.7.7


>From 1f16288e1f47e20ef4e35029eda8dd7d6fdab28d Mon Sep 17 00:00:00 2001
From: Marcus Lauer <marcuslauer@nyc.rr.com>
Date: Mon, 3 Sep 2012 13:40:15 -0400
Subject: [PATCH 2/4] Make copyfile write to a tempfile on the minion then
 move the file at the end

---
 func/minion/modules/copyfile.py   |   13 +++++++------
 func/overlord/modules/copyfile.py |    1 +
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/func/minion/modules/copyfile.py b/func/minion/modules/copyfile.py
index 8281747..b8e6d99 100644
--- a/func/minion/modules/copyfile.py
+++ b/func/minion/modules/copyfile.py
@@ -65,7 +65,7 @@ class CopyFile(func_module.FuncModule):
 	writetarget = filepath + '.functmp'
 
         try:
-            fo = open(filepath, 'w')
+            fo = open(writetarget, 'w')
             fo.close()
             del fo
         except (IOError, OSError), e:
@@ -100,6 +100,7 @@ class CopyFile(func_module.FuncModule):
 		subprocess.check_call(["chcon", "--reference=" + filepath, writetarget])
             except (OSError), e:
                 # Do nothing. Presumably SELinux is not enabled on this system.
+		pass
 
         return writetarget
 
@@ -125,17 +126,17 @@ class CopyFile(func_module.FuncModule):
             os.unlink(writetarget)
             return -1
 
-        return 1
+        return writetarget
 
-    def move(self, filepath, append_result)
+    def move(self, filepath, append_result):
         hostname = func_utils.get_hostname_by_route()
 	writetarget = append_result[hostname]
 
         # If the result of the append function was 0 (or -1), just return the same value.
-	if type(filepath) == type(0):
-	    return filepath
+	if type(writetarget) == type(0):
+	    return writetarget
 
-        if not os.path.exists(filepath):
+        if not os.path.exists(writetarget):
             # file disappeared
             return -1
 
diff --git a/func/overlord/modules/copyfile.py b/func/overlord/modules/copyfile.py
index 9a2bf2b..148d5b1 100644
--- a/func/overlord/modules/copyfile.py
+++ b/func/overlord/modules/copyfile.py
@@ -5,6 +5,7 @@ import sys
 import xmlrpclib
 
 from func.overlord import overlord_module
+import func.minion.modules.copyfile as copyfile
 
 class copyfile(overlord_module.BaseModule):
     def send(self, localpath, remotepath, bufsize=60000):
-- 
1.7.7


>From 67c89de4ec644f4f8ad695750f05abd4b883d4e2 Mon Sep 17 00:00:00 2001
From: Marcus Lauer <marcuslauer@nyc.rr.com>
Date: Mon, 3 Sep 2012 15:49:12 -0400
Subject: [PATCH 3/4] More work on making copyfile write to a tempfile on the
 minion

---
 func/minion/modules/copyfile.py       |   46 +++++++++++++++------------------
 func/overlord/cmd_modules/copyfile.py |    4 ++-
 func/overlord/modules/copyfile.py     |    4 +-
 3 files changed, 26 insertions(+), 28 deletions(-)

diff --git a/func/minion/modules/copyfile.py b/func/minion/modules/copyfile.py
index b8e6d99..582e21b 100644
--- a/func/minion/modules/copyfile.py
+++ b/func/minion/modules/copyfile.py
@@ -56,7 +56,7 @@ class CopyFile(func_module.FuncModule):
 
         return thissum.hexdigest()
 
-    def open(self, filepath, mode=None, uid=-1, gid=-1):
+    def open(self, filepath, retainperms, mode=None, uid=-1, gid=-1):
         dirpath = os.path.dirname(filepath)
         if not os.path.exists(dirpath):
             os.makedirs(dirpath)
@@ -72,35 +72,31 @@ class CopyFile(func_module.FuncModule):
             # XXX logger output here
             return -1
 
-        """
-        Set the mode and ownership of the tempfile if requested.
-	If not set them to match the target file if the target 
-	file exists.
-	"""
-	if mode or uid != -1 or gid != -1:
+	if retainperms:
+            # Make the attributes of the tempfile match the target file.
+            if os.path.isfile(filepath):
+                # Copy the mode of the existing file.
+                shutil.copystat(filepath,writetarget)
+
+                # Copy the uid/gid of the existing file.
+                fileinfo = os.stat(filepath)
+                os.chown(writetarget,fileinfo[4],fileinfo[5])
+
+                # Copy the SELinux attribs of the existing file.
+                import subprocess
+                refstring = "--reference=" + filepath
+                selinx_result = subprocess.call(["chcon", refstring, writetarget], stdout=None, stderr=None)
+
+        else:
+            # Set the mode and ownership of the file to match the server.
             try:
                 # we could intify the mode here if it's a string
                 if mode:
-                    os.chmod(filepath, mode)
+                    os.chmod(writetarget, mode)
                 if uid != -1 or gid != -1:
-                    os.chown(filepath, uid, gid)
+                    os.chown(writetarget, uid, gid)
             except (IOError, OSError), e:
-		return -1
-	elif os.path.exists(filepath):
-            # Copy the mode of the existing file.
-            shutil.copystat(filepath,writetarget)
-
-            # Copy the uid/gid of the existing file.
-            fileinfo = os.stat(filepath)
-            os.chown(writetarget,fileinfo[4],fileinfo[5])
-
-            # Copy the SELinux attribs of the existing file.
-            import subprocess
-	    try:
-		subprocess.check_call(["chcon", "--reference=" + filepath, writetarget])
-            except (OSError), e:
-                # Do nothing. Presumably SELinux is not enabled on this system.
-		pass
+                return -1
 
         return writetarget
 
diff --git a/func/overlord/cmd_modules/copyfile.py b/func/overlord/cmd_modules/copyfile.py
index 3f2263d..3796515 100644
--- a/func/overlord/cmd_modules/copyfile.py
+++ b/func/overlord/cmd_modules/copyfile.py
@@ -38,6 +38,8 @@ class CopyFile(base_command.BaseCommand):
                                action="store_true")
         self.parser.add_option("-v", "--verbose", dest="verbose",
                                action="store_true")
+        self.parser.add_option("", "--retainperms", dest="retainperms",
+                               action="store_true")
 
     def handleOptions(self, options):
         self.verbose = self.options.verbose
@@ -50,4 +52,4 @@ class CopyFile(base_command.BaseCommand):
         self.server_spec = self.parentCommand.server_spec
         self.getOverlord()
 
-        return self.overlord_obj.local.copyfile.send(self.options.filename, self.options.remotepath)
+        return self.overlord_obj.local.copyfile.send(self.options.filename, self.options.remotepath, self.options.retainperms)
diff --git a/func/overlord/modules/copyfile.py b/func/overlord/modules/copyfile.py
index 148d5b1..bef3740 100644
--- a/func/overlord/modules/copyfile.py
+++ b/func/overlord/modules/copyfile.py
@@ -8,7 +8,7 @@ from func.overlord import overlord_module
 import func.minion.modules.copyfile as copyfile
 
 class copyfile(overlord_module.BaseModule):
-    def send(self, localpath, remotepath, bufsize=60000):
+    def send(self, localpath, remotepath, retainperms, bufsize=60000):
         try:
             f = open(localpath, "r")
         except IOError, e:
@@ -20,7 +20,7 @@ class copyfile(overlord_module.BaseModule):
         uid = st.st_uid
         gid = st.st_gid
 
-        open_result = self.parent.run("copyfile", "open", [remotepath, mode, uid, gid])
+        open_result = self.parent.run("copyfile", "open", [remotepath, retainperms, mode, uid, gid])
 
         while True:
             data=f.read(bufsize)
-- 
1.7.7


>From 9988429a94e613dae6f33c7383797cc7ec922a05 Mon Sep 17 00:00:00 2001
From: Marcus Lauer <marcuslauer@nyc.rr.com>
Date: Mon, 3 Sep 2012 16:02:04 -0400
Subject: [PATCH 4/4] Make False the default for new retainperms flag in
 copyfile

---
 func/overlord/modules/copyfile.py |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/func/overlord/modules/copyfile.py b/func/overlord/modules/copyfile.py
index bef3740..6ac97a6 100644
--- a/func/overlord/modules/copyfile.py
+++ b/func/overlord/modules/copyfile.py
@@ -8,7 +8,7 @@ from func.overlord import overlord_module
 import func.minion.modules.copyfile as copyfile
 
 class copyfile(overlord_module.BaseModule):
-    def send(self, localpath, remotepath, retainperms, bufsize=60000):
+    def send(self, localpath, remotepath, retainperms=False, bufsize=60000):
         try:
             f = open(localpath, "r")
         except IOError, e:
-- 
1.7.7

