Add file support to cfn-init

Signed-off-by: Angus Salkeld <asalkeld@redhat.com>
This commit is contained in:
Angus Salkeld 2012-04-26 14:07:12 +10:00
parent c227c4581a
commit 6d2f82aca0
2 changed files with 126 additions and 7 deletions

View 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()

View 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()