Blame cloudinit/cmd/devel/make_mime.py

Packit Service 751c4a
# This file is part of cloud-init. See LICENSE file for license information.
Packit Service 751c4a
Packit Service 751c4a
"""Generate multi-part mime messages for user-data """
Packit Service 751c4a
Packit Service 751c4a
import argparse
Packit Service 751c4a
import sys
Packit Service 751c4a
from email.mime.multipart import MIMEMultipart
Packit Service 751c4a
from email.mime.text import MIMEText
Packit Service 751c4a
Packit Service 751c4a
from cloudinit import log
Packit Service 751c4a
from cloudinit.handlers import INCLUSION_TYPES_MAP
Packit Service 751c4a
from . import addLogHandlerCLI
Packit Service 751c4a
Packit Service 751c4a
NAME = 'make-mime'
Packit Service 751c4a
LOG = log.getLogger(NAME)
Packit Service 751c4a
EPILOG = ("Example: make-mime -a config.yaml:cloud-config "
Packit Service 751c4a
          "-a script.sh:x-shellscript > user-data")
Packit Service 751c4a
Packit Service 751c4a
Packit Service 751c4a
def file_content_type(text):
Packit Service 751c4a
    """ Return file content type by reading the first line of the input. """
Packit Service 751c4a
    try:
Packit Service 751c4a
        filename, content_type = text.split(":", 1)
Packit Service 751c4a
        return (open(filename, 'r'), filename, content_type.strip())
Packit Service 751c4a
    except ValueError as e:
Packit Service 751c4a
        raise argparse.ArgumentError(
Packit Service 751c4a
            text, "Invalid value for %r" % (text)
Packit Service 751c4a
        ) from e
Packit Service 751c4a
Packit Service 751c4a
Packit Service 751c4a
def get_parser(parser=None):
Packit Service 751c4a
    """Build or extend and arg parser for make-mime utility.
Packit Service 751c4a
Packit Service 751c4a
    @param parser: Optional existing ArgumentParser instance representing the
Packit Service 751c4a
        subcommand which will be extended to support the args of this utility.
Packit Service 751c4a
Packit Service 751c4a
    @returns: ArgumentParser with proper argument configuration.
Packit Service 751c4a
    """
Packit Service 751c4a
    if not parser:
Packit Service 751c4a
        parser = argparse.ArgumentParser()
Packit Service 751c4a
    # update the parser's doc and add an epilog to show an example
Packit Service 751c4a
    parser.description = __doc__
Packit Service 751c4a
    parser.epilog = EPILOG
Packit Service 751c4a
    parser.add_argument("-a", "--attach", dest="files", type=file_content_type,
Packit Service 751c4a
                        action='append', default=[],
Packit Service 751c4a
                        metavar="<file>:<content-type>",
Packit Service 751c4a
                        help=("attach the given file as the specified "
Packit Service 751c4a
                              "content-type"))
Packit Service 751c4a
    parser.add_argument('-l', '--list-types', action='store_true',
Packit Service 751c4a
                        default=False,
Packit Service 751c4a
                        help='List support cloud-init content types.')
Packit Service 751c4a
    parser.add_argument('-f', '--force', action='store_true',
Packit Service 751c4a
                        default=False,
Packit Service 751c4a
                        help='Ignore unknown content-type warnings')
Packit Service 751c4a
    return parser
Packit Service 751c4a
Packit Service 751c4a
Packit Service 751c4a
def get_content_types(strip_prefix=False):
Packit Service 751c4a
    """ Return a list of cloud-init supported content types.  Optionally
Packit Service 751c4a
        strip out the leading 'text/' of the type if strip_prefix=True.
Packit Service 751c4a
    """
Packit Service 751c4a
    return sorted([ctype.replace("text/", "") if strip_prefix else ctype
Packit Service 751c4a
                   for ctype in INCLUSION_TYPES_MAP.values()])
Packit Service 751c4a
Packit Service 751c4a
Packit Service 751c4a
def handle_args(name, args):
Packit Service 751c4a
    """Create a multi-part MIME archive for use as user-data.  Optionally
Packit Service 751c4a
       print out the list of supported content types of cloud-init.
Packit Service 751c4a
Packit Service 751c4a
    Also setup CLI log handlers to report to stderr since this is a development
Packit Service 751c4a
    utility which should be run by a human on the CLI.
Packit Service 751c4a
Packit Service 751c4a
    @return 0 on success, 1 on failure.
Packit Service 751c4a
    """
Packit Service 751c4a
    addLogHandlerCLI(LOG, log.DEBUG if args.debug else log.WARNING)
Packit Service 751c4a
    if args.list_types:
Packit Service 751c4a
        print("\n".join(get_content_types(strip_prefix=True)))
Packit Service 751c4a
        return 0
Packit Service 751c4a
Packit Service 751c4a
    sub_messages = []
Packit Service 751c4a
    errors = []
Packit Service 751c4a
    for i, (fh, filename, format_type) in enumerate(args.files):
Packit Service 751c4a
        contents = fh.read()
Packit Service 751c4a
        sub_message = MIMEText(contents, format_type, sys.getdefaultencoding())
Packit Service 751c4a
        sub_message.add_header('Content-Disposition',
Packit Service 751c4a
                               'attachment; filename="%s"' % (filename))
Packit Service 751c4a
        content_type = sub_message.get_content_type().lower()
Packit Service 751c4a
        if content_type not in get_content_types():
Packit Service 751c4a
            level = "WARNING" if args.force else "ERROR"
Packit Service 751c4a
            msg = (level + ": content type %r for attachment %s "
Packit Service 751c4a
                   "may be incorrect!") % (content_type, i + 1)
Packit Service 751c4a
            sys.stderr.write(msg + '\n')
Packit Service 751c4a
            errors.append(msg)
Packit Service 751c4a
        sub_messages.append(sub_message)
Packit Service 751c4a
    if len(errors) and not args.force:
Packit Service 751c4a
        sys.stderr.write("Invalid content-types, override with --force\n")
Packit Service 751c4a
        return 1
Packit Service 751c4a
    combined_message = MIMEMultipart()
Packit Service 751c4a
    for msg in sub_messages:
Packit Service 751c4a
        combined_message.attach(msg)
Packit Service 751c4a
    print(combined_message)
Packit Service 751c4a
    return 0
Packit Service 751c4a
Packit Service 751c4a
Packit Service 751c4a
def main():
Packit Service 751c4a
    args = get_parser().parse_args()
Packit Service 751c4a
    return(handle_args(NAME, args))
Packit Service 751c4a
Packit Service 751c4a
Packit Service 751c4a
if __name__ == '__main__':
Packit Service 751c4a
    sys.exit(main())
Packit Service 751c4a
Packit Service 751c4a
Packit Service 751c4a
# vi: ts=4 expandtab