From de5efe8aa7053e5bf0e0c266dc6d1d279113f5ea Mon Sep 17 00:00:00 2001 From: Benedith Mulongo Date: Fri, 20 Sep 2024 14:06:44 +0200 Subject: [PATCH] Add setup_cosmos_modules to the repo --- .../overlay/etc/puppet/setup_cosmos_modules | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 global/overlay/etc/puppet/setup_cosmos_modules diff --git a/global/overlay/etc/puppet/setup_cosmos_modules b/global/overlay/etc/puppet/setup_cosmos_modules new file mode 100644 index 0000000..4732c8d --- /dev/null +++ b/global/overlay/etc/puppet/setup_cosmos_modules @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +""" Write out a puppet cosmos-modules.conf """ + +import hashlib +import os +import os.path +import platform +import sys + +from debian import debian_support + +try: + from configobj import ConfigObj + + OS_INFO = ConfigObj("/etc/os-release") +except (IOError, ModuleNotFoundError): + OS_INFO = None + + +def get_file_hash(modulesfile): + """ + Based on https://github.com/python/cpython/pull/31930: should use + hashlib.file_digest() but it is only available in python 3.11 + """ + try: + with open(modulesfile, "rb") as fileobj: + digestobj = hashlib.sha256() + _bufsize = 2**18 + buf = bytearray(_bufsize) # Reusable buffer to reduce allocations. + view = memoryview(buf) + while True: + size = fileobj.readinto(buf) + if size == 0: + break # EOF + digestobj.update(view[:size]) + except FileNotFoundError: + return "" + + return digestobj.hexdigest() + + +def get_list_hash(file_lines): + """Get hash of list contents""" + + file_lines_hash = hashlib.sha256() + for line in file_lines: + file_lines_hash.update(line) + + return file_lines_hash.hexdigest() + + +def create_file_content(modules): + """ + Write out the expected file contents to a list so we can check the + expected checksum before writing anything + """ + file_lines = [] + file_lines.append( + "# Generated by {}\n".format( # pylint: disable=consider-using-f-string + os.path.basename(sys.argv[0]) + ).encode("utf-8") + ) + for key in modules: + file_lines.append( + "{0:11} {1} {2} {3}\n".format( # pylint: disable=consider-using-f-string + key, + modules[key]["repo"], + modules[key]["upgrade"], + modules[key]["tag"], + ).encode("utf-8") + ) + + return file_lines + + +def main(): + """Starting point of the program""" + + modulesfile: str = "/etc/puppet/cosmos-modules.conf" + modulesfile_tmp: str = modulesfile + ".tmp" + + modules: dict = { + "sunet": { + "repo": "https://github.com/SUNET/puppet-sunet.git", + "upgrade": "yes", + "tag": "stable-2023v1-2*", + }, + "augeas": { + "repo": "https://github.com/SUNET/puppet-augeas.git", + "upgrade": "yes", + "tag": "sunet-2*", + }, + "concat": { + "repo": "https://github.com/SUNET/puppetlabs-concat.git", + "upgrade": "yes", + "tag": "sunet-2*", + }, + "stdlib": { + "repo": "https://github.com/SUNET/puppetlabs-stdlib.git", + "upgrade": "yes", + "tag": "sunet-2*", + }, + "apt": { + "repo": "https://github.com/SUNET/puppetlabs-apt.git", + "upgrade": "yes", + "tag": "sunet-2*", + }, + "docker": { + "repo": "https://github.com/SUNET/garethr-docker.git", + "upgrade": "yes", + "tag": "sunet-2*", + }, + } + + if OS_INFO: + # Provided by Debian/Ubuntu via + # https://github.com/SUNET/multiverse/blob/main/global/pre-tasks.d/030puppet + + if (OS_INFO["ID"] == "debian" and int(OS_INFO["VERSION_ID"]) >= 12) or ( + OS_INFO["ID"] == "ubuntu" + and debian_support.version_compare(OS_INFO["VERSION_ID"], "24.04") >= 0 + ): + del modules["augeas"] + del modules["apt"] + del modules["concat"] + del modules["stdlib"] + + # Make test machines use special test branch of puppet-sunet: + if OS_INFO["ID"] == "ubuntu": + nodename = platform.node() + name_parts = nodename.split("-") + if len(name_parts) > 1: + if name_parts[1] == "test": + modules["sunet"]["tag"] = "testing-2*" + + # Build list of expected file content + file_lines = create_file_content(modules) + + # Get hash of the list + list_hash = get_list_hash(file_lines) + + # Get hash of the existing file on disk + file_hash = get_file_hash(modulesfile) + + # Update the file if necessary + if list_hash != file_hash: + # Since we are reading the file with 'rb' when computing our hash use 'wb' when + # writing so we dont end up creating a file that does not match the + # expected hash + with open(modulesfile_tmp, "wb") as fileobj: + for line in file_lines: + fileobj.write(line) + + # Rename it in place so the update is atomic for anything else trying to + # read the file + os.rename(modulesfile_tmp, modulesfile) + + +if __name__ == "__main__": + main()