Adding comments + doc. Starting the work on git packages.

This commit is contained in:
antux18 2024-07-10 12:23:07 +02:00
parent ba1303aa97
commit 6a6ae9370d
3 changed files with 111 additions and 7 deletions

110
ecg.py
View File

@ -1,5 +1,13 @@
#!/bin/python3 #!/bin/python3
"""
ECG is program that automates software environment checking
in scientific artifacts.
It is meant to be executed periodically to analyze variations in the
software environment of the artifact through time.
"""
import subprocess import subprocess
import json import json
import yaml import yaml
@ -10,20 +18,42 @@ import requests
import zipfile import zipfile
import io import io
import tarfile import tarfile
import pathlib import pathlib
HEREPATH = pathlib.Path(__file__).parent.absolute() import logging
# Paths:
HEREPATH = pathlib.Path(__file__).parent.absolute()
# Where to store list of installed packages: # Where to store list of installed packages:
PKGLISTS = "./pkglists/" PKGLISTS = "./pkglists/"
# Commands to list installed packages along with their versions and the name of the package manager, depending on the packages manager: # Commands to list installed packages along with their versions and the name
pkgmgr_cmd = {"dpkg":"dpkg -l | awk 'NR>5 {print $2 \",\" $3 \",\" \"dpkg\"}'", "rpm":"rpm -qa --queryformat '%{NAME},%{VERSION},rpm\\n'", "pacman":"pacman -Q | awk '{print $0 \",\" $1 \",pacman\"}'", "pip":"pip freeze | sed 's/==/,/g' | awk '{print $0 \",pip\"}'", "conda":"/root/.conda/bin/conda list -e | sed 's/=/ /g' | awk 'NR>3 {print $1 \",\" $2 \",conda\"}'"} # of the package manager, depending on the package managers:
pkgmgr_cmd = {
"dpkg":"dpkg -l | awk 'NR>5 {print $2 \",\" $3 \",\" \"dpkg\"}'", \
"rpm":"rpm -qa --queryformat '%{NAME},%{VERSION},rpm\\n'", \
"pacman":"pacman -Q | awk '{print $0 \",\" $1 \",pacman\"}'", \
"pip":"pip freeze | sed 's/==/,/g' | awk '{print $0 \",pip\"}'", \
"conda":"/root/.conda/bin/conda list -e | sed 's/=/ /g' | awk 'NR>3 {print $1 \",\" $2 \",conda\"}'", \
"git":""
}
import logging # Enables logging:
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO) logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO)
def download_sources(config): def download_sources(config):
"""
Downloads the source of the artifact in 'config'.
Parameters
----------
config: dict
Parsed YAML config file.
Returns
-------
tempdir: tempfile.TemporaryDirectory
The directory where the artifact is downloaded to.
"""
url = config["artifact_url"] url = config["artifact_url"]
logging.info(f"Downloading sources from {url}") logging.info(f"Downloading sources from {url}")
temp_dir = tempfile.TemporaryDirectory() temp_dir = tempfile.TemporaryDirectory()
@ -37,6 +67,23 @@ def download_sources(config):
return temp_dir return temp_dir
def build_image(config, src_dir): def build_image(config, src_dir):
"""
Builds the given Docker image in 'config'.
Parameters
----------
config: dict
Part of the parsed YAML config file concerning the Docker image
to build.
src_dir: tempfile.TemporaryDirectory
The directory where the artifact is stored.
Returns
-------
return_code: int
Return code of the Docker 'build' command.
"""
name = config["name"] name = config["name"]
logging.info(f"Starting building image {name}") logging.info(f"Starting building image {name}")
path = os.path.join(src_dir, config["location"]) path = os.path.join(src_dir, config["location"])
@ -49,6 +96,24 @@ def build_image(config, src_dir):
return return_code == 0 return return_code == 0
def check_env(config, src_dir): def check_env(config, src_dir):
"""
Builds a list of all software packages installed in the
Docker image given in 'config', depending on the package managers
specified in the configuration, then stores it in a CSV file.
Parameters
----------
config: dict
Part of the parsed YAML config file concerning the Docker image
where to check the environment.
src_dir: tempfile.TemporaryDirectory
The directory where the artifact is stored.
Returns
-------
None
"""
pathlib.Path(PKGLISTS).mkdir(parents=True, exist_ok=True) pathlib.Path(PKGLISTS).mkdir(parents=True, exist_ok=True)
path = os.path.join(src_dir, config["location"]) path = os.path.join(src_dir, config["location"])
pkglist_file = open(PKGLISTS + "pkglist.csv", "w") pkglist_file = open(PKGLISTS + "pkglist.csv", "w")
@ -59,14 +124,45 @@ def check_env(config, src_dir):
pkglist = check_process.stdout.decode("utf-8") pkglist = check_process.stdout.decode("utf-8")
print(pkglist) print(pkglist)
pkglist_file.write(pkglist) pkglist_file.write(pkglist)
if "git_packages" in config.keys():
print(config["git_packages"])
pkglist_file.close() pkglist_file.close()
def remove_image(config): def remove_image(config):
"""
Removes the Docker image given in 'config'.
Parameters
----------
config: dict
Part of the parsed YAML config file concerning the Docker image
to remove.
Returns
-------
None
"""
name = config["name"] name = config["name"]
logging.info(f"Removing image '{name}'") logging.info(f"Removing image '{name}'")
subprocess.run(["docker", "rmi", name]) subprocess.run(["docker", "rmi", name])
def build_images(config, src_dir): def build_images(config, src_dir):
"""
Builds all Docker images specified in 'config', checks software
environment if build is successful, then removes the images.
Parameters
----------
config: dict
Parsed YAML config file.
src_dir: tempfile.TemporaryDirectory
The directory where the artifact is stored.
Returns
-------
None
"""
for image in config["dockerfiles"]: for image in config["dockerfiles"]:
successful_build = build_image(image, src_dir) successful_build = build_image(image, src_dir)
if successful_build: if successful_build:
@ -74,13 +170,15 @@ def build_images(config, src_dir):
remove_image(image) remove_image(image)
def main(): def main():
# Command line arguments parser:
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
prog='ecg', prog='ecg',
description='Check if a dockerfile is still buildable') description='Check if a dockerfile is still buildable')
parser.add_argument('config') parser.add_argument('config')
parser.add_argument('-v', '--verbose', action='store_true') parser.add_argument('-v', '--verbose', action='store_true')
args = parser.parse_args() args = parser.parse_args()
# Parsing the input YAML file including the configuration of the artifact:
config = None config = None
with open(args.config, "r") as config_file: with open(args.config, "r") as config_file:
config = yaml.safe_load(config_file) config = yaml.safe_load(config_file)

View File

@ -5,7 +5,10 @@ dockerfiles:
- name: "image1:version" - name: "image1:version"
location: "path/to/docker/folder" location: "path/to/docker/folder"
package_managers: package_managers:
- "dpkg" # Possible values: dpkg, pip, conda - "dpkg" # Possible values: dpkg, rpm, pacman, pip, conda
git_packages:
- name: "pkg1"
location: "path/to/git/package"
- name: "image2:version" - name: "image2:version"
location: "path/to/docker/folder" location: "path/to/docker/folder"
package_managers: package_managers:

View File

@ -7,3 +7,6 @@ dockerfiles:
package_managers: package_managers:
- "dpkg" - "dpkg"
- "pip" - "pip"
git_packages:
- name: "pkg1"
location: "./pkg1"