summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDevan Carpenter <devan@taler.net>2023-06-18 16:42:10 -0400
committerDevan Carpenter <devan@taler.net>2023-07-11 22:41:44 -0400
commit878bf9502613b69ceeb83dbdf19faae6819c640d (patch)
tree2eba531cbd83e09dd47c7ea4f821be560d50dbfe
parent178216208c44281b5177267de53fea937ff1c5e6 (diff)
downloaddeployment-878bf9502613b69ceeb83dbdf19faae6819c640d.tar.gz
deployment-878bf9502613b69ceeb83dbdf19faae6819c640d.tar.bz2
deployment-878bf9502613b69ceeb83dbdf19faae6819c640d.zip
buildbot: add support for container job configs
jobs can have configurations to specify paramaters, such as container image, failure conditions, etc. restructure container job loop to support generating jobs using the built-in buildbot functions. split logic into functions for improved clarity and composibility.
-rw-r--r--buildbot/master.cfg211
1 files changed, 175 insertions, 36 deletions
diff --git a/buildbot/master.cfg b/buildbot/master.cfg
index 1fddae2..418ddfe 100644
--- a/buildbot/master.cfg
+++ b/buildbot/master.cfg
@@ -24,9 +24,13 @@
# @author ng0
# @author Christian Grothoff
# @author Devan Carpenter
+import ast
+import configparser
+import glob
import os
import pathlib
import re
+import subprocess
from buildbot.changes.pb import PBChangeSource
from buildbot.steps.source.git import Git
@@ -35,8 +39,10 @@ from buildbot.plugins import reporters
from buildbot.plugins import schedulers
from buildbot.plugins import steps
from buildbot.plugins import util
+from buildbot.process import buildstep, logobserver
from buildbot.reporters.generators.build import BuildStatusGenerator
from buildbot.worker import Worker
+from twisted.internet import defer
# This is a sample buildmaster config file. It must be
# installed as 'master.cfg' in your buildmaster's base
@@ -174,24 +180,47 @@ def update_deployment(factory):
# Convenience function that builds and runs a container.
-def container_run_step(stepName, factory, WORK_DIR, containerName,
+def container_add_step(HALT_ON_FAILURE,
+ WARN_ON_FAILURE,
+ CONTAINER_BUILD,
+ CONTAINER_NAME,
+ factory,
+ WORK_DIR,
+ stepName,
jobCmd="/workdir/ci/ci.sh",
containerFile="ci/Containerfile"):
- factory.addStep(steps.ShellSequence(
- name=stepName,
- commands=[
- util.ShellArg(command=["podman", "build", "-t", containerName,
- "-f", containerFile, "."],
- logname='build container', haltOnFailure=True),
- util.ShellArg(command=["podman", "run", "-ti", "--rm",
- "--volume", f"{WORK_DIR}:/workdir",
- "--workdir", "/workdir",
- containerName, jobCmd],
- logname='run inside container', haltOnFailure=True),
- ],
- haltOnFailure=True,
- workdir=WORK_DIR
- ))
+ print(f"HALT_ON_FAILURE: {HALT_ON_FAILURE}, WARN_ON_FAILURE: {WARN_ON_FAILURE}, CONTAINER_BUILD: {CONTAINER_BUILD}, CONTAINER_NAME: {CONTAINER_NAME}")
+ if not CONTAINER_BUILD:
+ return steps.ShellSequence(
+ name=stepName,
+ commands=[
+ util.ShellArg(command=["podman", "run", "-ti", "--rm",
+ "--volume", f"{WORK_DIR}:/workdir",
+ "--workdir", "/workdir",
+ CONTAINER_NAME, jobCmd],
+ logname='run inside container',
+ haltOnFailure=HALT_ON_FAILURE),
+ ],
+ haltOnFailure=HALT_ON_FAILURE,
+ workdir=WORK_DIR
+ )
+ else:
+ return steps.ShellSequence(
+ name=stepName,
+ commands=[
+ util.ShellArg(command=["podman", "build", "-t", CONTAINER_NAME,
+ "-f", containerFile, "."],
+ logname='build container', haltOnFailure=True),
+ util.ShellArg(command=["podman", "run", "-ti", "--rm",
+ "--volume", f"{WORK_DIR}:/workdir",
+ "--workdir", "/workdir",
+ CONTAINER_NAME, jobCmd],
+ logname='run inside container',
+ haltOnFailure=HALT_ON_FAILURE),
+ ],
+ haltOnFailure=HALT_ON_FAILURE,
+ workdir=WORK_DIR
+ )
##################################################################
######################## JOBS ####################################
@@ -1208,13 +1237,125 @@ WORKERS.append(Worker("container-worker", "container-pass"))
# "merchant", "deployment", "twister", "sync",
# "help", "taler-merchant-demos", "challenger"]
#
-container_repos = ["wallet-core"]
+
+
+# Container Job Generator Functions
+# Parse config file and save values in a dict
+def ingest_job_config(configPath, jobName):
+ configDict = {jobName: {}}
+ print(configDict)
+ ini.read_string(configPath)
+ for key in ini["build"]:
+ value = ini['build'][key]
+ configDict[jobName][key] = value
+ print(configDict)
+ configDict.update(configDict)
+ print(configDict)
+ return configDict
+
+
+# Search for configs, and ingest
+def handle_job_config(jobDirPath, jobName, repoName, configPath, configExist):
+ print(configPath)
+ if configExist == 0:
+ print(f"Ingesting Job Config: {configPath}")
+ configDict = ingest_job_config(configPath, jobName)
+ print(configDict)
+ return configDict
+ else:
+ print("No job config; Using default params")
+ # Set default job config parameters
+ configDict = {jobName: {"HALT_ON_FAILURE": True,
+ "WARN_ON_FAILURE": False,
+ "CONTAINER_BUILD": True,
+ "CONTAINER_NAME": repoName}}
+ return configDict
+
+
+class GenerateStagesCommand(buildstep.ShellMixin, steps.BuildStep):
+
+ def __init__(self, reponame, **kwargs):
+ self.reponame = reponame
+ kwargs = self.setupShellMixin(kwargs)
+ super().__init__(**kwargs)
+ self.observer = logobserver.BufferLogObserver()
+ self.addLogObserver('stdio', self.observer)
+
+ def extract_stages(self, stdout):
+ stages = []
+ for line in stdout.split('\n'):
+ stage = str(line.strip())
+ if stage:
+ stages.append(stage)
+ return stages
+
+ @defer.inlineCallbacks
+ def run(self):
+ CONTAINER_WORKDIR = f"/home/container-worker/workspace/{self.reponame}"
+ CI_JOBS_PATH = f"{CONTAINER_WORKDIR}/ci/jobs"
+ # run 'ls <project_root>/ci/jobs/' to get the list of stages
+ cmd = yield self.makeRemoteShellCommand()
+ yield self.runCommand(cmd)
+ jobDirs = []
+
+ # if the command passes extract the list of stages
+ result = cmd.results()
+ if result == util.SUCCESS:
+ jobDirs = self.extract_stages(self.observer.getStdout())
+ print(f"this is jobDirs list: {jobDirs}")
+ self.configDict = {}
+ print(f"Remote cmd stdout: {self.observer.getStdout()}")
+ print(f"cmd.results: {cmd.results()}")
+ for stage in jobDirs:
+ jobDirPath = f"{CI_JOBS_PATH}/{stage}"
+ observer = logobserver.BufferLogObserver()
+ self.addLogObserver('stdio', observer)
+ cmd1 = yield self.makeRemoteShellCommand(
+ command=["cat", f"{jobDirPath}/config.ini"])
+ yield self.runCommand(cmd1)
+ print(f"cmd1.results: {cmd1.results()}")
+ print(f"Second command stdout: {observer.getStdout()}")
+ print(f"Current stage: {stage}")
+ print(jobDirPath)
+ self.configDict.update(
+ handle_job_config(
+ jobDirPath, stage, self.reponame,
+ observer.getStdout(), cmd1.results()))
+ print(self.configDict)
+ # create a container step for each stage and
+ # add them to the build
+ convstr2bool = ast.literal_eval
+ self.build.addStepsAfterCurrentStep([
+ container_add_step(
+ convstr2bool(
+ str(self.configDict[stage]["HALT_ON_FAILURE"])),
+ convstr2bool(
+ str(self.configDict[stage]["WARN_ON_FAILURE"])),
+ convstr2bool(
+ str(self.configDict[stage]["CONTAINER_BUILD"])),
+ self.configDict[stage]["CONTAINER_NAME"],
+ container_factory,
+ CONTAINER_WORKDIR,
+ stage,
+ f"ci/jobs/{stage}/job.sh")
+ for stage in jobDirs
+ ])
+
+ return result
+
+
+container_repos = ["wallet-core", "merchant"]
for reponame in container_repos:
- ##
+
+ # Prepare to read job configs
+ ini = configparser.ConfigParser()
+ ini.optionxform = str
+
# Factory-wide variables
REPO_URL = "https://git.taler.net/" + reponame + ".git"
CONTAINER_WORKDIR = f"/home/container-worker/workspace/{reponame}"
+ CI_JOBS_PATH = f"{CONTAINER_WORKDIR}/ci/jobs"
# Create a factory
container_factory = util.BuildFactory()
@@ -1237,37 +1378,35 @@ for reponame in container_repos:
container_factory.addStep(Git(
name="git",
repourl=REPO_URL,
+ branch="dev/devan-carpenter/container-ci",
mode='full',
method='fresh',
haltOnFailure=True,
))
- # Run container step with default commands
- CI_JOBS_PATH = f"{CONTAINER_WORKDIR}/ci/jobs"
- if os.path.exists(CI_JOBS_PATH):
- for parentDir, dirNames, fileNames in os.walk(CI_JOBS_PATH):
- dirNames.sort()
- fileNames.sort()
- for filename in fileNames:
- if filename.endswith('.sh'):
- basedir = pathlib.PurePath(parentDir).name
- container_run_step(basedir,
- container_factory,
- CONTAINER_WORKDIR, reponame,
- f"ci/jobs/{basedir}/{filename}")
- else:
- print("No jobs found")
- else:
- print("Cannot find jobs directory")
+ container_factory.addStep(GenerateStagesCommand(
+ reponame,
+ name="Generate build stages",
+ command=["ls", CI_JOBS_PATH],
+ haltOnFailure=True))
BUILDERS.append(util.BuilderConfig(
name=f"{reponame}-builder",
workernames=["container-worker"],
factory=container_factory
))
+
+ # Only enable this scheduler for debugging!
+ # Will run builders with 1 minute of waiting inbetween builds
+ # SCHEDULERS.append(schedulers.Periodic(
+ # name=f"{reponame}-minutely",
+ # builderNames=[f"{reponame}-builder"],
+ # periodicBuildTimer=60
+ # ))
+
# Buildmaster is notified whenever deployment.git changes
SCHEDULERS.append(schedulers.SingleBranchScheduler(
- name="container-scheduler",
+ name=f"{reponame}-container-scheduler",
change_filter=util.ChangeFilter(
branch="dev/devan-carpenter/container-ci",
project_re=f"({reponame})"