summaryrefslogtreecommitdiff
path: root/cloudinit
diff options
context:
space:
mode:
authorRyan Harper <ryan.harper@canonical.com>2020-08-17 11:06:20 -0500
committerGitHub <noreply@github.com>2020-08-17 12:06:20 -0400
commita4b6b96f30bdd994ab535b222cf4b4bf09f20668 (patch)
treeb663314d2bc85a48460306d215e107bb4af3787c /cloudinit
parentef041fd822a2cf3a4022525e942ce988b1f95180 (diff)
downloadcloud-init-git-a4b6b96f30bdd994ab535b222cf4b4bf09f20668.tar.gz
cli: add devel make-mime subcommand (#518)
* cli: add devel make-mime subcommand Cloud-init documents an in-source-tree tool, make-mime.py used to help users create multi-part mime user-data. This tool is not shipped in the cloud-init install and unavailable at runtime. This patch takes tools/make-mime.py and makes the functionality available via the devel subcommand. The primary interface of --attach file:content-type is still present. The cli now adds: -l, --list-types Print out a list of supported content-types -f, --force Ignore errors for unsupported content-types The tool will now raise a RunTime error if the supplied content-type is not supported (or more likely a typo: x-shell-script vs. x-shellscript) * make-mime: write to stderr and exit 1 instead of raising RuntimeError * Update example to match docs * Update docs for make-mime subcommand * Remove tools/make-mime.py; replaced by cloud-init devel make-mime Co-authored-by: Rick Harding <rharding@mitechie.com>
Diffstat (limited to 'cloudinit')
-rwxr-xr-xcloudinit/cmd/devel/make_mime.py112
-rw-r--r--cloudinit/cmd/devel/parser.py5
2 files changed, 116 insertions, 1 deletions
diff --git a/cloudinit/cmd/devel/make_mime.py b/cloudinit/cmd/devel/make_mime.py
new file mode 100755
index 00000000..77e10540
--- /dev/null
+++ b/cloudinit/cmd/devel/make_mime.py
@@ -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
diff --git a/cloudinit/cmd/devel/parser.py b/cloudinit/cmd/devel/parser.py
index 99a234ce..1a3c46a4 100644
--- a/cloudinit/cmd/devel/parser.py
+++ b/cloudinit/cmd/devel/parser.py
@@ -9,6 +9,7 @@ from cloudinit.config import schema
from . import net_convert
from . import render
+from . import make_mime
def get_parser(parser=None):
@@ -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)