diff options
-rw-r--r-- | cloudinit/UserDataHandler.py | 51 | ||||
-rw-r--r-- | doc/examples/cloud-config-archive.txt | 16 |
2 files changed, 61 insertions, 6 deletions
diff --git a/cloudinit/UserDataHandler.py b/cloudinit/UserDataHandler.py index feb67149..fbb000fc 100644 --- a/cloudinit/UserDataHandler.py +++ b/cloudinit/UserDataHandler.py @@ -19,7 +19,9 @@ import email from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText - +from email.mime.base import MIMEBase +from email import encoders +import yaml starts_with_mappings={ '#include' : 'text/x-include-url', @@ -27,7 +29,8 @@ starts_with_mappings={ '#cloud-config' : 'text/cloud-config', '#upstart-job' : 'text/upstart-job', '#part-handler' : 'text/part-handler', - '#cloud-boothook' : 'text/cloud-boothook' + '#cloud-boothook' : 'text/cloud-boothook', + '#cloud-config-archive' : 'text/cloud-config-archive', } # if 'str' is compressed return decompressed otherwise return it @@ -43,12 +46,47 @@ def decomp_str(str): def do_include(str,parts): import urllib # is just a list of urls, one per line + # also support '#include <url here>' for line in str.splitlines(): if line == "#include": continue + if line.startswith("#include"): + line = line[len("#include"):].lstrip() if line.startswith("#"): continue content = urllib.urlopen(line).read() process_includes(email.message_from_string(decomp_str(content)),parts) +def explode_cc_archive(archive,parts): + for ent in yaml.load(archive): + # ent can be one of: + # dict { 'filename' : 'value' , 'content' : 'value', 'type' : 'value' } + # filename and type not be present + # or + # scalar(payload) + filename = 'part-%03d' % len(parts['content']) + def_type = "text/cloud-config" + if isinstance(ent,str): + content = ent + mtype = type_from_startswith(content,def_type) + else: + content = ent.get('content', '') + filename = ent.get('filename', filename) + mtype = ent.get('type', None) + if mtype == None: + mtype = type_from_startswith(payload,def_type) + + print "adding %s,%s" % (filename, mtype) + parts['content'].append(content) + parts['names'].append(filename) + parts['types'].append(mtype) + +def type_from_startswith(payload, default=None): + # slist is sorted longest first + slist = sorted(starts_with_mappings.keys(), key=lambda e: 0-len(e)) + for sstr in slist: + if payload.startswith(sstr): + return(starts_with_mappings[sstr]) + return default + def process_includes(msg,parts): # parts is a dictionary of arrays # parts['content'] @@ -67,10 +105,7 @@ def process_includes(msg,parts): ctype = None ctype_orig = part.get_content_type() if ctype_orig == "text/plain": - for sstr, gtype in starts_with_mappings.items(): - if payload.startswith(sstr): - ctype = gtype - break + ctype = type_from_startswith(payload) if ctype is None: ctype = ctype_orig @@ -79,6 +114,10 @@ def process_includes(msg,parts): do_include(payload,parts) continue + if ctype == "text/cloud-config-archive": + explode_cc_archive(payload,parts) + continue + filename = part.get_filename() if not filename: filename = 'part-%03d' % len(parts['content']) diff --git a/doc/examples/cloud-config-archive.txt b/doc/examples/cloud-config-archive.txt new file mode 100644 index 00000000..23b1024c --- /dev/null +++ b/doc/examples/cloud-config-archive.txt @@ -0,0 +1,16 @@ +#cloud-config-archive +- type: foo/wark + filename: bar + content: | + This is my payload + hello +- this is also payload +- | + multi line payload + here +- + type: text/upstart-job + filename: my-upstart.conf + content: | + whats this, yo? + |