From f2cbb5f2f23fcfd244941924b5cd10cc91a7d4d2 Mon Sep 17 00:00:00 2001 From: Tomas Tomecek Date: Mar 29 2017 14:05:11 +0000 Subject: [PATCH 1/2] PoC: add post {un,}install scripts for install profiles Signed-off-by: Tomas Tomecek --- diff --git a/spec.yaml b/spec.yaml index bce4314..9e50853 100644 --- a/spec.yaml +++ b/spec.yaml @@ -78,6 +78,33 @@ data: - bar - bar-extras - baz + # actions to run after all packages of the install profile are installed, optional + post_install_script: + # URL wthin production dist-git + # + # workflow: + # 1. dnf installs all packages of this profile + # 2. dnf downloads script specified in `url` + # 3. dnf runs downloaded script, as root + # + # requirements: + # * file needs to be INSIDE production dist-git repository of this module + # * file needs to be references by commit hash, this commit needs to be on either master or f26 branch + # * the script contains shebang (so it can be executed as `$ ./run_me`) + url: http://pkgs.fedoraproject.org/cgit/modules/nginx.git/plain/nginx.yaml?id=840a83995d5b12ac886a88635a15c01be7b2d004 + post_uninstall_script: + # URL wthin production dist-git + # + # workflow: + # 1. dnf uninstalls all packages of this profile + # 2. dnf downloads script specified in `url` + # 3. dnf runs downloaded script, as root + # + # requirements: + # * file needs to be INSIDE production dist-git repository of this module + # * file needs to be references by commit hash, this commit needs to be on either master or f26 branch + # * the script contains shebang (so it can be executed as `$ ./run_me`) + url: http://pkgs.fedoraproject.org/cgit/modules/nginx.git/plain/nginx.yaml?id=840a83995d5b12ac886a88635a15c01be7b2d004 # An example minimal profile only installing one component. # Optional, just like any other profile. # XXX: What happens if the system wants to use an undefined From 861555906a2f50a1dc2e12973697224020ec2e70 Mon Sep 17 00:00:00 2001 From: Tomas Tomecek Date: Mar 31 2017 10:12:08 +0000 Subject: [PATCH 2/2] iteration 2 Signed-off-by: Tomas Tomecek --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 9fc79f9..a8121fa 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -51,7 +51,7 @@ from modulemd.components.module import ModuleComponentModule from modulemd.components.rpm import ModuleComponentRPM from modulemd.api import ModuleAPI from modulemd.filter import ModuleFilter -from modulemd.profile import ModuleProfile +from modulemd.profile import ModuleProfile, PostScript supported_mdversions = ( 1, ) @@ -187,13 +187,24 @@ class ModuleMetadata(object): if ("profiles" in yml["data"] and isinstance(yml["data"]["profiles"], dict)): for profile in yml["data"]["profiles"].keys(): + profile_data = yml["data"]["profiles"][profile] self.profiles[profile] = ModuleProfile() - if "description" in yml["data"]["profiles"][profile]: + if "description" in profile_data: self.profiles[profile].description = \ - str(yml["data"]["profiles"][profile]["description"]) - if "rpms" in yml["data"]["profiles"][profile]: + str(profile_data["description"]) + if "rpms" in profile_data: self.profiles[profile].rpms = \ - set(yml["data"]["profiles"][profile]["rpms"]) + set(profile_data["rpms"]) + if "post_install_script" in profile_data: + self.profiles[profile].post_install_script = \ + PostScript("post_install_script") + self.profiles[profile].post_install_script.set_values( + profile_data["post_install_script"]) + if "post_uninstall_script" in profile_data: + self.profiles[profile].post_uninstall_script = \ + PostScript("post_uninstall_script") + self.profiles[profile].post_uninstall_script.set_values( + profile_data["post_uninstall_script"]) if ("api" in yml["data"] and isinstance(yml["data"]["api"], dict)): self.api = ModuleAPI() @@ -298,6 +309,13 @@ class ModuleMetadata(object): data["data"]["profiles"][profile] = dict() data["data"]["profiles"][profile]["rpms"] = \ list(self.profiles[profile].rpms) + if self.profiles[profile].post_install_script: + data["data"]["profiles"][profile]["post_install_script"] = \ + self.profiles[profile].post_install_script.dump() + if self.profiles[profile].post_uninstall_script: + data["data"]["profiles"][profile]["post_uninstall_script"] = \ + self.profiles[profile].post_uninstall_script.dump() + if self.api: data["data"]["api"] = dict() if self.api.rpms: diff --git a/modulemd/profile.py b/modulemd/profile.py index a307e11..0a1d154 100644 --- a/modulemd/profile.py +++ b/modulemd/profile.py @@ -25,6 +25,69 @@ supported_content = ( "rpms", ) + +class PostScript(object): + """ Base class for post install and uninstall scripts. """ + def __init__(self, key): + """ + @param str key: 'post_install_script' or 'post_uninstall_script' + """ + self.key = key + self.rationale = None + self.repository = None + self.ref = None + self.path = None + self.content = None + + def __repr__(self): + return ("").format( + repr(self.key), + repr(self.rationale), + repr(self.repository), + repr(self.ref), + repr(self.path), + repr(self.content)) + + def set_values(self, yaml_data): + if "rationale" not in yaml_data: + raise ValueError("Script %s requires rationale", self.key) + if "path" not in yaml_data: + raise ValueError("Script %s requires path", self.key) + self.rationale = yaml_data["rationale"] + self.path = yaml_data["path"] + if "repository" in yaml_data: + self.repository = yaml_data["repository"] + if "ref" in yaml_data: + if not self.repository: + raise ValueError("Ref for %s can be specified only when repository is." % self.key) + self.ref = yaml_data["ref"] + if "content" in yaml_data: + self.content = yaml_data["content"] + + def dump(self): + """ + return dict for serialization + """ + def set_if_set(k, v): + if v: + d[k] = v + + d = { + "rationale": self.rationale, + "path": self.path + } + set_if_set("repository", self.repository) + set_if_set("ref", self.ref) + set_if_set("content", self.ref) + return d + + class ModuleProfile(object): """Class representing a particular module profile.""" @@ -32,6 +95,8 @@ class ModuleProfile(object): """Creates a new ModuleProfile instance.""" self.description = "" self.rpms = set() + self._post_install_script = None + self._post_uninstall_script = None def __repr__(self): return ("