]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Add file support to cfn-init
authorAngus Salkeld <asalkeld@redhat.com>
Thu, 26 Apr 2012 04:07:12 +0000 (14:07 +1000)
committerAngus Salkeld <asalkeld@redhat.com>
Thu, 26 Apr 2012 04:07:39 +0000 (14:07 +1000)
Signed-off-by: Angus Salkeld <asalkeld@redhat.com>
heat/cfntools/cfn_helper.py
heat/tests/test_cfn.py

index 475d1e7e1629544e26b3584961194ac107cec77e..3b6e491a452438171036b5817de3d6614ba3366b 100644 (file)
@@ -30,9 +30,13 @@ Not implemented yet:
 """
 
 import ConfigParser
+import errno
+import grp
 import json
 import logging
 import os
+import os.path
+import pwd
 import rpmUtils.updates as rpmupdates
 import rpmUtils.miscutils as rpmutils
 import subprocess
@@ -451,6 +455,8 @@ class PackagesHandler(object):
           * apt
           * yum
         """
+        if not self._packages:
+            return
         packages = sorted(self._packages.iteritems(), PackagesHandler._pkgsort)
 
         for manager, package_entries in packages:
@@ -460,6 +466,58 @@ class PackagesHandler(object):
             else:
                 handler(self, package_entries)
 
+class FilesHandler(object):
+    def __init__(self, files):
+        self._files = files
+
+    def apply_files(self):
+        if not self._files:
+            return
+        for fdest, meta in self._files.iteritems():
+            dest = fdest.encode()
+            try:
+                os.makedirs(os.path.dirname(dest))
+            except OSError as e:
+                if e.errno == errno.EEXIST:
+                    logging.debug(str(e))
+                else:
+                    logging.exception(e)
+
+            if 'content' in meta:
+                if isinstance(meta['content'], basestring):
+                    f = open(dest, 'w')
+                    f.write(meta['content'])
+                    f.close()
+                else:
+                    f = open(dest, 'w')
+                    f.write(json.dumps(meta['content'], indent=4))
+                    f.close()
+            elif 'source' in meta:
+                CommandRunner('wget -O %s %s' % (dest, meta['source'])).run()
+            else:
+                logging.error('%s %s' % (dest, str(meta)))
+                continue
+
+            uid = -1
+            gid = -1
+            if 'owner' in meta:
+                try:
+                    user_info = pwd.getpwnam(meta['owner'])
+                    uid = user_info[2]
+                except KeyError as ex:
+                    pass
+
+            if 'group' in meta:
+                try:
+                    group_info = grp.getgrnam(meta['group'])
+                    gid = group_info[2]
+                except KeyError as ex:
+                    pass
+
+            os.chown(dest, uid, gid)
+            if 'mode' in meta:
+                os.chmod(dest, int(meta['mode'], 8))
+
 
 class ServicesHandler(object):
     _services = {}
@@ -566,6 +624,8 @@ class ServicesHandler(object):
         """
         Starts, stops, enables, disables services
         """
+        if not self._services:
+            return
         for manager, service_entries in self._services.iteritems():
             handler = self._service_handler(manager)
             if not handler:
@@ -577,6 +637,8 @@ class ServicesHandler(object):
         """
         Restarts failed services, and runs hooks.
         """
+        if not self._services:
+            return
         for manager, service_entries in self._services.iteritems():
             handler = self._service_handler(manager)
             if not handler:
@@ -603,13 +665,17 @@ class Metadata(object):
         self._is_local_metadata = True
         self._metadata = None
 
-    def retrieve(self):
+    def retrieve(self, meta_str=None):
         """
-        Read the metadata from the given filename and return the string
+        Read the metadata from the given filename
         """
-        f = open("/var/lib/cloud/data/cfn-init-data")
-        self._data = f.read()
-        f.close()
+        if meta_str:
+            self._data = meta_str
+        else:
+            f = open("/var/lib/cloud/data/cfn-init-data")
+            self._data = f.read()
+            f.close()
+
         if isinstance(self._data, str):
             self._metadata = json.loads(self._data)
         else:
@@ -633,7 +699,7 @@ class Metadata(object):
           * sources (not yet)
           * users (not yet)
           * groups (not yet)
-          * files (not yet)
+          * files
           * commands (not yet)
           * services
         """
@@ -643,7 +709,7 @@ class Metadata(object):
         #FIXME: handle sources
         #FIXME: handle users
         #FIXME: handle groups
-        #FIXME: handle files
+        FilesHandler(self._config.get("files")).apply_files()
         #FIXME: handle commands
         ServicesHandler(self._config.get("services")).apply_services()
 
index 6f0a08c74bd0999ccf52147bde8006a9a3b75f24..1f5c62e8855f192eed7f1d50f2d3ef9cc23ad4ef 100644 (file)
@@ -9,6 +9,7 @@ import sys
 import nose
 from nose.plugins.attrib import attr
 from nose import with_setup
+import shutil
 
 from heat.cfntools.cfn_helper import *
 
@@ -97,6 +98,58 @@ runas=root
     assert(c.hooks['bla'].runas == 'root')
 
 
+def tearDown_metadata_files():
+    shutil.rmtree('/tmp/_files_test_', ignore_errors=True)
+
+
+@with_setup(None, tearDown_metadata_files)
+@attr(tag=['unit', 'cfn-metadata'])
+@attr(speed='fast')
+def test_metadata_files():
+
+    j = ''' {
+        "AWS::CloudFormation::Init" : {
+          "config" : {
+            "files" : {
+              "/tmp/_files_test_/epel.repo" : {
+                "source" : "https://raw.github.com/heat-api/heat/master/README.rst",
+                "mode"   : "000644"
+              },
+              "/tmp/_files_test_/_with/some/dirs/to/make/small.conf" : {
+                "content" : "not much really",
+                "mode"    : "000777"
+              },
+              "/tmp/_files_test_/node.json": {
+                  "content": {
+                      "myapp": {
+                          "db": {
+                              "database": "RefDBName",
+                              "user": "RefDBUser",
+                              "host": "Fn::GetAttDBInstance.Endpoint.Address",
+                              "password": "RefDBPassword"
+                          }
+                      },
+                      "run_list": ["recipe[wordpress]", "bla"]
+                  },
+                  "mode": "000600"
+              }
+            }
+          }
+        }
+    }
+'''
+
+    metadata = Metadata('tester',
+                        'ronald')
+    metadata.retrieve(j)
+    metadata.cfn_init()
+
+    # mask out the file type
+    mask = int('007777', 8)
+    assert(os.stat('/tmp/_files_test_/node.json').st_mode & mask == 0600)
+    assert(os.stat('/tmp/_files_test_/epel.repo').st_mode & mask == 0644)
+    assert(os.stat('/tmp/_files_test_/_with/some/dirs/to/make/small.conf').st_mode & mask == 0777)
+
 if __name__ == '__main__':
     sys.argv.append(__file__)
     nose.main()