← Back to team overview

cloud-init-dev team mailing list archive

[Merge] lp:~harlowja/cloud-init/cloud-file-write into lp:cloud-init

 

Scott Moser has proposed merging lp:~harlowja/cloud-init/cloud-file-write into lp:cloud-init.

Requested reviews:
  cloud init development team (cloud-init-dev)

For more details, see:
https://code.launchpad.net/~harlowja/cloud-init/cloud-file-write/+merge/114417

cloud-config support for file writing

Fixes LP: #1012854 by implementing file writing, adjusts
other code to have user/group parsing in util instead
of in stages.py, renames decomp_str to decomp_gzip since
it is more meaningful when named that (as thats all it can
decompress).

-- 
https://code.launchpad.net/~harlowja/cloud-init/cloud-file-write/+merge/114417
Your team cloud init development team is requested to review the proposed merge of lp:~harlowja/cloud-init/cloud-file-write into lp:cloud-init.
=== added file 'cloudinit/config/cc_write_files.py'
--- cloudinit/config/cc_write_files.py	1970-01-01 00:00:00 +0000
+++ cloudinit/config/cc_write_files.py	2012-07-11 13:22:49 +0000
@@ -0,0 +1,69 @@
+# vi: ts=4 expandtab
+#
+#    Copyright (C) 2012 Yahoo! Inc.
+#
+#    Author: Joshua Harlow <harlowja@xxxxxxxxxxxxx>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License version 3, as
+#    published by the Free Software Foundation.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import base64
+import os
+
+from cloudinit import util
+from cloudinit.settings import PER_INSTANCE
+
+frequency = PER_INSTANCE
+
+DEFAULT_PERMS = 0644
+
+
+def handle(name, cfg, _cloud, log, _args):
+    files = cfg.get('files')
+    if not files:
+        log.debug(("Skipping module named %s,"
+                   " no/empty 'files' key in configuration"), name)
+        return
+    write_files(name, files, log)
+
+
+def write_files(name, files, log):
+    if not files:
+        return
+
+    for (i, f_info) in enumerate(files):
+        path = f_info.get('path')
+        if not path:
+            log.warn("No path provided to write for entry %s in module %s",
+                     i + 1, name)
+            continue
+        path = os.path.abspath(path)
+        contents = decode_string(f_info.get('content', ''),
+                                 f_info.get('compression'))
+        (u, g) = util.extract_usergroup(f_info.get('owner'))
+        perms = safe_int(f_info.get('permissions'), DEFAULT_PERMS)
+        util.write_file(path, contents, mode=perms)
+        util.chownbyname(path, u, g)
+
+
+def safe_int(text, default):
+    try:
+        return int(text)
+    except (TypeError, ValueError):
+        return default
+
+
+def decode_string(contents, content_type):
+    if util.is_true(content_type, addons=['gzip', 'gz']):
+        contents_dec = base64.b64decode(contents)
+        contents = util.decomp_gzip(contents_dec, quiet=False)
+    return contents

=== modified file 'cloudinit/stages.py'
--- cloudinit/stages.py	2012-07-09 19:08:27 +0000
+++ cloudinit/stages.py	2012-07-11 13:22:49 +0000
@@ -133,18 +133,7 @@
         if log_file:
             util.ensure_file(log_file)
             if perms:
-                perms_parted = perms.split(':', 1)
-                u = perms_parted[0]
-                if len(perms_parted) == 2:
-                    g = perms_parted[1]
-                else:
-                    g = ''
-                u = u.strip()
-                g = g.strip()
-                if u == "-1" or u.lower() == "none":
-                    u = None
-                if g == "-1" or g.lower() == "none":
-                    g = None
+                u, g = util.extract_usergroup(perms)
                 try:
                     util.chownbyname(log_file, u, g)
                 except OSError:

=== modified file 'cloudinit/user_data.py'
--- cloudinit/user_data.py	2012-06-23 05:04:37 +0000
+++ cloudinit/user_data.py	2012-07-11 13:22:49 +0000
@@ -227,7 +227,7 @@
         raw_data = ''
     if not headers:
         headers = {}
-    data = util.decomp_str(raw_data)
+    data = util.decomp_gzip(raw_data)
     if "mime-version:" in data[0:4096].lower():
         msg = email.message_from_string(data)
         for (key, val) in headers.iteritems():

=== modified file 'cloudinit/util.py'
--- cloudinit/util.py	2012-07-10 21:47:02 +0000
+++ cloudinit/util.py	2012-07-11 13:22:49 +0000
@@ -159,6 +159,10 @@
     pass
 
 
+class DecompressionError(Exception):
+    pass
+
+
 def ExtendedTemporaryFile(**kwargs):
     fh = tempfile.NamedTemporaryFile(**kwargs)
     # Replace its unlink with a quiet version
@@ -256,13 +260,32 @@
     return fn
 
 
-def decomp_str(data):
+def decomp_gzip(data, quiet=True):
     try:
         buf = StringIO(str(data))
         with contextlib.closing(gzip.GzipFile(None, "rb", 1, buf)) as gh:
             return gh.read()
-    except:
-        return data
+    except Exception as e:
+        if quiet:
+            return data
+        else:
+            raise DecompressionError(str(e))
+
+
+def extract_usergroup(ug_pair):
+    if not ug_pair:
+        return (None, None)
+    ug_parted = ug_pair.split(':', 1)
+    u = ug_parted[0].strip()
+    if len(ug_parted) == 2:
+        g = ug_parted[1].strip()
+    else:
+        g = None
+    if not u or u == "-1" or u.lower() == "none":
+        u = None
+    if not g or g == "-1" or g.lower() == "none":
+        g = None
+    return (u, g)
 
 
 def find_modules(root_dir):


Follow ups