2

Can write_files be merged? I can't seem to get the merge_how syntax correct. Only files in the last write_files are being created.

Thanks

Charles
  • 81
  • 8

1 Answers1

4

The answer is yes.

When working with multi-part MIME, you must add the following to the header of each part.

Merge-Type: list(append)+dict(no_replace,recurse_list)+str()

Below is a modified version of the helper script provided in the cloud-init documentation.

#!/usr/bin/env python3

import click
import sys
import gzip as gz

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


@click.command()
@click.option('--gzip', is_flag=True,
              help='gzip MIME message to current directory')
@click.argument('message', nargs=-1)
def main(message: str, gzip: bool) -> None:
    """This script creates a multi part MIME suitable for cloud-init

    A MESSAGE has following format <filename>:<type> where <filename> is the
    file that contains the data to add to the MIME message. <type> is the 
    MIME content-type.

    MIME content-type may be on of the following:
    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

    EXAMPLE:
    write-mime-multipart afile.cfg:cloud-config bfile.sh:x-shellscript
    """
    combined_message = MIMEMultipart()
    for message_part in message:
        (filename, format_type) = message_part.split(':', 1)
        with open(filename) as fh:
            contents = fh.read()
        sub_message = MIMEText(contents, format_type, sys.getdefaultencoding())
        sub_message.add_header('Content-Disposition',
                               'attachment; filename="{}"'.format(filename))
        sub_message.add_header('Merge-Type',
                               'list(append)+dict(no_replace,recurse_list)+str()')
        combined_message.attach(sub_message)

    if gzip:
        with gz.open('combined-userdata.txt.gz', 'wb') as fd:
            fd.write(bytes(combined_message))
    else:
        print(combined_message)


if __name__ == '__main__':
    main()
Charles
  • 81
  • 8
  • Just a lesson learned the hard way: do not ignore the part that says *of each part.* According to the cloud-init docs, the merge-type works like a stack and the first one defined becomes the default. However, in practice, on Ubuntu 22.04, this did not work and I had to indeed specify `merge_type` on each `write_files` part (in Terraform). – SvenAelterman Aug 18 '23 at 04:47