Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions cloudinit/cmd/devel/make_mime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# This file is part of cloud-init. See LICENSE file for license information.

"""Generate multi-part mime messages for user-data """

import argparse
import sys
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

from cloudinit import log
from cloudinit.handlers import INCLUSION_TYPES_MAP
from . import addLogHandlerCLI

NAME = 'make-mime'
LOG = log.getLogger(NAME)
EPILOG = ("Example: make-mime -a config.yaml:cloud-config "
"-a script.sh:x-shellscript > user-data")


def file_content_type(text):
""" Return file content type by reading the first line of the input. """
try:
filename, content_type = text.split(":", 1)
return (open(filename, 'r'), filename, content_type.strip())
except ValueError:
raise argparse.ArgumentError(text, "Invalid value for %r" % (text))


def get_parser(parser=None):
"""Build or extend and arg parser for make-mime utility.

@param parser: Optional existing ArgumentParser instance representing the
subcommand which will be extended to support the args of this utility.

@returns: ArgumentParser with proper argument configuration.
"""
if not parser:
parser = argparse.ArgumentParser()
# update the parser's doc and add an epilog to show an example
parser.description = __doc__
parser.epilog = EPILOG
parser.add_argument("-a", "--attach", dest="files", type=file_content_type,
action='append', default=[],
metavar="<file>:<content-type>",
help=("attach the given file as the specified "
"content-type"))
parser.add_argument('-l', '--list-types', action='store_true',
default=False,
help='List support cloud-init content types.')
parser.add_argument('-f', '--force', action='store_true',
default=False,
help='Ignore unknown content-type warnings')
return parser


def get_content_types(strip_prefix=False):
""" Return a list of cloud-init supported content types. Optionally
strip out the leading 'text/' of the type if strip_prefix=True.
"""
return sorted([ctype.replace("text/", "") if strip_prefix else ctype
for ctype in INCLUSION_TYPES_MAP.values()])


def handle_args(name, args):
"""Create a multi-part MIME archive for use as user-data. Optionally
print out the list of supported content types of cloud-init.

Also setup CLI log handlers to report to stderr since this is a development
utility which should be run by a human on the CLI.

@return 0 on success, 1 on failure.
"""
addLogHandlerCLI(LOG, log.DEBUG if args.debug else log.WARNING)
if args.list_types:
print("\n".join(get_content_types(strip_prefix=True)))
return 0

sub_messages = []
errors = []
for i, (fh, filename, format_type) in enumerate(args.files):
contents = fh.read()
sub_message = MIMEText(contents, format_type, sys.getdefaultencoding())
sub_message.add_header('Content-Disposition',
'attachment; filename="%s"' % (filename))
content_type = sub_message.get_content_type().lower()
if content_type not in get_content_types():
level = "WARNING" if args.force else "ERROR"
msg = (level + ": content type %r for attachment %s "
"may be incorrect!") % (content_type, i + 1)
sys.stderr.write(msg + '\n')
errors.append(msg)
sub_messages.append(sub_message)
if len(errors) and not args.force:
sys.stderr.write("Invalid content-types, override with --force\n")
return 1
combined_message = MIMEMultipart()
for msg in sub_messages:
combined_message.attach(msg)
print(combined_message)
return 0


def main():
args = get_parser().parse_args()
return(handle_args(NAME, args))


if __name__ == '__main__':
sys.exit(main())


# vi: ts=4 expandtab
5 changes: 4 additions & 1 deletion cloudinit/cmd/devel/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from . import net_convert
from . import render
from . import make_mime


def get_parser(parser=None):
Expand All @@ -25,7 +26,9 @@ def get_parser(parser=None):
(net_convert.NAME, net_convert.__doc__,
net_convert.get_parser, net_convert.handle_args),
(render.NAME, render.__doc__,
render.get_parser, render.handle_args)
render.get_parser, render.handle_args),
(make_mime.NAME, make_mime.__doc__,
make_mime.get_parser, make_mime.handle_args),
]
for (subcmd, helpmsg, get_parser, handler) in subcmds:
parser = subparsers.add_parser(subcmd, help=helpmsg)
Expand Down
44 changes: 23 additions & 21 deletions doc/rtd/topics/format.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,33 +23,35 @@ Using a mime-multi part file, the user can specify more than one type of data.
For example, both a user data script and a cloud-config type could be
specified.

Supported content-types:
Supported content-types are listed from the cloud-init subcommand make-mime::

- text/cloud-boothook
- text/cloud-config
- text/cloud-config-archive
- text/jinja2
- text/part-handler
- text/upstart-job
- text/x-include-once-url
- text/x-include-url
- text/x-shellscript
% cloud-init devel make-mime --list-types
cloud-boothook
cloud-config
cloud-config-archive
cloud-config-jsonp
jinja2
part-handler
upstart-job
x-include-once-url
x-include-url
x-shellscript

Helper script to generate mime messages
---------------------------------------

The cloud-init codebase includes a helper script to generate MIME multi-part
files: `make-mime.py`_.
Helper subcommand to generate mime messages
-------------------------------------------

``make-mime.py`` takes pairs of (filename, "text/" mime subtype) separated by
a colon (e.g. ``config.yaml:cloud-config``) and emits a MIME multipart
message to stdout. An example invocation, assuming you have your cloud config
in ``config.yaml`` and a shell script in ``script.sh`` and want to store the
multipart message in ``user-data``::
The cloud-init subcommand can generate MIME multi-part files: `make-mime`_.

./tools/make-mime.py -a config.yaml:cloud-config -a script.sh:x-shellscript > user-data
``make-mime`` subcommand takes pairs of (filename, "text/" mime subtype)
separated by a colon (e.g. ``config.yaml:cloud-config``) and emits a MIME
multipart message to stdout. An example invocation, assuming you have your
cloud config in ``config.yaml`` and a shell script in ``script.sh`` and want
to store the multipart message in ``user-data``::

.. _make-mime.py: https://github.com/canonical/cloud-init/blob/master/tools/make-mime.py
% cloud-init devel make-mime -a config.yaml:cloud-config -a script.sh:x-shellscript > user-data

.. _make-mime: https://github.com/canonical/cloud-init/blob/master/cloudinit/cmd/devel/make_mime.py


User-Data Script
Expand Down
62 changes: 0 additions & 62 deletions tools/make-mime.py

This file was deleted.