sailesh1993 / rpms / cloud-init

Forked from rpms/cloud-init a year ago
Clone
20a859
From 29ed6e1c54a6ffbc3017660af5e2a81850e46b43 Mon Sep 17 00:00:00 2001
20a859
From: Lars Kellogg-Stedman <lars@redhat.com>
20a859
Date: Mon, 10 Apr 2017 15:52:37 -0400
20a859
Subject: [PATCH] util: teach write_file about copy_mode option
20a859
20a859
On centos/fedora/rhel/derivatives, /etc/ssh/sshd_config has mode 0600,
20a859
but cloud-init unilaterally sets file modes to 0644 when no explicit
20a859
mode is passed to util.write_file. On ubuntu/debian, this file has
20a859
mode 0644.  With this patch, write_file learns about the copy_mode
20a859
option, which will cause it to use the mode of the existing file by
20a859
default, falling back to the explicit mode parameter if the file does
20a859
not exist.
20a859
20a859
LP: #1644064
20a859
Resolves: rhbz#1295984
20a859
(cherry picked from commit 721348a622a660b65acfdf7fdf53203b47f80748)
20a859
---
20a859
 cloudinit/atomic_helper.py           | 12 +++++++++++-
20a859
 cloudinit/config/cc_set_passwords.py |  3 ++-
20a859
 cloudinit/util.py                    | 10 +++++++++-
20a859
 tests/unittests/test_util.py         | 33 +++++++++++++++++++++++++++++++--
20a859
 4 files changed, 53 insertions(+), 5 deletions(-)
20a859
20a859
diff --git a/cloudinit/atomic_helper.py b/cloudinit/atomic_helper.py
20a859
index fb2df8d..587b994 100644
20a859
--- a/cloudinit/atomic_helper.py
20a859
+++ b/cloudinit/atomic_helper.py
20a859
@@ -2,13 +2,23 @@
20a859
 
20a859
 import json
20a859
 import os
20a859
+import stat
20a859
 import tempfile
20a859
 
20a859
 _DEF_PERMS = 0o644
20a859
 
20a859
 
20a859
-def write_file(filename, content, mode=_DEF_PERMS, omode="wb"):
20a859
+def write_file(filename, content, mode=_DEF_PERMS,
20a859
+               omode="wb", copy_mode=False):
20a859
     # open filename in mode 'omode', write content, set permissions to 'mode'
20a859
+
20a859
+    if copy_mode:
20a859
+        try:
20a859
+            file_stat = os.stat(filename)
20a859
+            mode = stat.S_IMODE(file_stat.st_mode)
20a859
+        except OSError:
20a859
+            pass
20a859
+
20a859
     tf = None
20a859
     try:
20a859
         tf = tempfile.NamedTemporaryFile(dir=os.path.dirname(filename),
20a859
diff --git a/cloudinit/config/cc_set_passwords.py b/cloudinit/config/cc_set_passwords.py
20a859
index cf1f59e..2745df8 100755
20a859
--- a/cloudinit/config/cc_set_passwords.py
20a859
+++ b/cloudinit/config/cc_set_passwords.py
20a859
@@ -174,7 +174,8 @@ def handle(_name, cfg, cloud, log, args):
20a859
                                                      pw_auth))
20a859
 
20a859
         lines = [str(l) for l in new_lines]
20a859
-        util.write_file(ssh_util.DEF_SSHD_CFG, "\n".join(lines))
20a859
+        util.write_file(ssh_util.DEF_SSHD_CFG, "\n".join(lines),
20a859
+                        copy_mode=True)
20a859
 
20a859
         try:
20a859
             cmd = cloud.distro.init_cmd  # Default service
20a859
diff --git a/cloudinit/util.py b/cloudinit/util.py
20a859
index 5725129..f90653d 100644
20a859
--- a/cloudinit/util.py
20a859
+++ b/cloudinit/util.py
20a859
@@ -1732,7 +1732,7 @@ def chmod(path, mode):
20a859
             os.chmod(path, real_mode)
20a859
 
20a859
 
20a859
-def write_file(filename, content, mode=0o644, omode="wb"):
20a859
+def write_file(filename, content, mode=0o644, omode="wb", copy_mode=False):
20a859
     """
20a859
     Writes a file with the given content and sets the file mode as specified.
20a859
     Resotres the SELinux context if possible.
20a859
@@ -1742,6 +1742,14 @@ def write_file(filename, content, mode=0o644, omode="wb"):
20a859
     @param mode: The filesystem mode to set on the file.
20a859
     @param omode: The open mode used when opening the file (w, wb, a, etc.)
20a859
     """
20a859
+
20a859
+    if copy_mode:
20a859
+        try:
20a859
+            file_stat = os.stat(filename)
20a859
+            mode = stat.S_IMODE(file_stat.st_mode)
20a859
+        except OSError:
20a859
+            pass
20a859
+
20a859
     ensure_dir(os.path.dirname(filename))
20a859
     if 'b' in omode.lower():
20a859
         content = encode_text(content)
20a859
diff --git a/tests/unittests/test_util.py b/tests/unittests/test_util.py
20a859
index ab74311..5d21b4b 100644
20a859
--- a/tests/unittests/test_util.py
20a859
+++ b/tests/unittests/test_util.py
20a859
@@ -103,8 +103,8 @@ class TestWriteFile(helpers.TestCase):
20a859
         self.assertTrue(os.path.isdir(dirname))
20a859
         self.assertTrue(os.path.isfile(path))
20a859
 
20a859
-    def test_custom_mode(self):
20a859
-        """Verify custom mode works properly."""
20a859
+    def test_explicit_mode(self):
20a859
+        """Verify explicit file mode works properly."""
20a859
         path = os.path.join(self.tmp, "NewFile.txt")
20a859
         contents = "Hey there"
20a859
 
20a859
@@ -115,6 +115,35 @@ class TestWriteFile(helpers.TestCase):
20a859
         file_stat = os.stat(path)
20a859
         self.assertEqual(0o666, stat.S_IMODE(file_stat.st_mode))
20a859
 
20a859
+    def test_copy_mode_no_existing(self):
20a859
+        """Verify that file is created with mode 0o644 if copy_mode
20a859
+        is true and there is no prior existing file."""
20a859
+        path = os.path.join(self.tmp, "NewFile.txt")
20a859
+        contents = "Hey there"
20a859
+
20a859
+        util.write_file(path, contents, copy_mode=True)
20a859
+
20a859
+        self.assertTrue(os.path.exists(path))
20a859
+        self.assertTrue(os.path.isfile(path))
20a859
+        file_stat = os.stat(path)
20a859
+        self.assertEqual(0o644, stat.S_IMODE(file_stat.st_mode))
20a859
+
20a859
+    def test_copy_mode_with_existing(self):
20a859
+        """Verify that file is created using mode of existing file
20a859
+        if copy_mode is true."""
20a859
+        path = os.path.join(self.tmp, "NewFile.txt")
20a859
+        contents = "Hey there"
20a859
+
20a859
+        open(path, 'w').close()
20a859
+        os.chmod(path, 0o666)
20a859
+
20a859
+        util.write_file(path, contents, copy_mode=True)
20a859
+
20a859
+        self.assertTrue(os.path.exists(path))
20a859
+        self.assertTrue(os.path.isfile(path))
20a859
+        file_stat = os.stat(path)
20a859
+        self.assertEqual(0o666, stat.S_IMODE(file_stat.st_mode))
20a859
+
20a859
     def test_custom_omode(self):
20a859
         """Verify custom omode works properly."""
20a859
         path = os.path.join(self.tmp, "NewFile.txt")