debian-forge-composer/tools/gen-user-data
Lars Karlitski 2f40265844 tools/gen-user-data: don't depend on python3-pyyaml
Instead, append `write_files: <JSON>` to the end of the file. This
works, because JSON is valid YAML.

For two reasons:

1. The generated user-data was hard to read, because python3-pyyaml
   outputs weird syntax. Keeping the file as written makes it easier to
   recognize when debugging an issue.

2. The tool now only depends on modules that python3 ships, making it
   easier to run on a pristine system.
2020-11-24 13:08:44 +01:00

72 lines
2.3 KiB
Python
Executable file

#!/usr/bin/python3
"""
gen-user-data
This tool generates a cloud-config user-data file from a directory containing
configuration. Its main purpose is to make it easy to include files in the
user-data, which need to be encoded in base64.
It writes the assembled user-data to standard out.
The configuration directory may contain:
* user-data.yml -- a base user-data. Anything that exists in this file will be
transferred as-is. Any additional configuration is appended
to already existing configuration.
* files/ -- a directory containing additional files to include. The
file's path on the target system mirrors its path relative
to this directore (`files/etc/hosts` → `/etc/hosts`). Its
permissions are copied over, but the owner will always be
root:root. Empty directories are ignored.
"""
import argparse
import base64
import json
import os
import stat
import sys
def octal_mode_string(mode):
"""Convert stat.st_mode to the format cloud-init expects.
cloud-init's write_files plugin expects file permissions in the format
returned by python2's oct() function, for example '0644'. In python3, oct()
returns a string in the new octal notation, '0o644'.
"""
return "0" + oct(stat.S_IMODE(mode))[2:]
def main():
p = argparse.ArgumentParser(description="Generate cloud-config user-data")
p.add_argument("configdir", metavar="CONFIGDIR", help="input directory")
args = p.parse_args()
write_files = []
filesdir = f"{args.configdir}/files"
for directory, dirs, files in os.walk(filesdir, followlinks=True):
for name in files:
path = f"{directory}/{name}"
with open(path, "rb") as f:
content = base64.b64encode(f.read()).decode("utf-8")
write_files.append({
"path": "/" + os.path.relpath(path, filesdir),
"encoding": "b64",
"content": content,
"permissions": octal_mode_string(os.lstat(path).st_mode)
})
with open(f"{args.configdir}/user-data.yml") as f:
sys.stdout.write(f.read())
sys.stdout.write("write_files: ")
json.dump(write_files, sys.stdout)
sys.stdout.write("\n")
if __name__ == "__main__":
sys.exit(main())