summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordd <dd@wx.tnyzeq.icu>2024-10-07 11:55:45 +0200
committerdd <dd@wx.tnyzeq.icu>2024-10-07 12:20:03 +0200
commit7bc109ed9a701123bea0fbfecdd9fe09f2a525bc (patch)
tree9ee462505be59dde59841938be17cd932b2aa8d1
parentcf95baa691c82d21f0c78bb037a8ab9343db3a0a (diff)
downloadvyos-jenkins-7bc109ed9a701123bea0fbfecdd9fe09f2a525bc.tar.gz
vyos-jenkins-7bc109ed9a701123bea0fbfecdd9fe09f2a525bc.zip
circinus refactoring: added shared docker logic and improved logging by whole lot
-rwxr-xr-xnew/image_builder.py94
-rw-r--r--new/lib/docker.py72
-rw-r--r--new/lib/helpers.py128
-rwxr-xr-xnew/package_builder.py90
4 files changed, 254 insertions, 130 deletions
diff --git a/new/image_builder.py b/new/image_builder.py
index df2e1be..817353a 100755
--- a/new/image_builder.py
+++ b/new/image_builder.py
@@ -12,8 +12,9 @@ from time import monotonic
import netifaces
+from lib.docker import Docker
from lib.git import Git
-from lib.helpers import setup_logging, execute, quote_all, refuse_root
+from lib.helpers import setup_logging, refuse_root, project_dir, get_my_log_file
class ImageBuilder:
@@ -21,13 +22,14 @@ class ImageBuilder:
"sagitta": "1.4.x",
"circinus": "1.5.x",
}
- docker_image = None
vyos_build_repo = None
+ docker = None
- def __init__(self, branch, vyos_build_git, vyos_mirror, extra_options, flavor, build_by, version, bind_addr,
- bind_port, keep_build):
+ def __init__(self, branch, vyos_build_git, vyos_build_docker, vyos_mirror, extra_options,
+ flavor, build_by, version, bind_addr, bind_port, keep_build):
self.branch = branch
self.vyos_build_git = vyos_build_git
+ self.vyos_build_docker = vyos_build_docker
self.vyos_mirror = vyos_mirror
self.extra_options = extra_options
self.flavor = flavor
@@ -37,32 +39,28 @@ class ImageBuilder:
self.bind_port = bind_port
self.keep_build = keep_build
- self.project_dir: str = os.path.realpath(os.path.dirname(__file__))
self.cwd = os.getcwd()
def build(self):
begin = monotonic()
if self.vyos_mirror == "local":
- vyos_mirror = self.start_local_webserver()
+ vyos_mirror = self.start_local_apt_webserver()
logging.info("Starting local APT repository at %s" % vyos_mirror)
else:
vyos_mirror = self.vyos_mirror
logging.info("Using supplied APT repository at %s" % vyos_mirror)
+ self.vyos_build_repo = os.path.join(project_dir, "build", "%s-image-build" % self.branch)
+
logging.info("Pulling vyos-build docker image")
- self.docker_image = "vyos/vyos-build:%s" % self.branch
- execute("docker pull %s" % quote_all(self.docker_image), passthrough=True)
+ self.docker = Docker(self.vyos_build_docker, self.branch, self.vyos_build_repo)
+ self.docker.pull()
- self.vyos_build_repo = os.path.join(self.project_dir, "build", "%s-image-build" % self.branch)
git = Git(self.vyos_build_repo)
if not self.keep_build:
if git.exists():
- try:
- shutil.rmtree(self.vyos_build_repo)
- except PermissionError:
- # Unfortunately the docker container creates some files as root, and thus we don't have choice...
- self.docker_run("bash -c %s" % quote("sudo rm -rf /vyos/*"), log=False)
- shutil.rmtree(self.vyos_build_repo)
+ # We want to delete original vyos-build repo and do fresh clone to clean cached build files.
+ self.docker.rmtree(self.vyos_build_repo)
if not git.exists():
git.clone(self.vyos_build_git, self.branch)
@@ -102,7 +100,18 @@ class ImageBuilder:
logging.info("Using build image command: '%s'" % build_image_command)
logging.info("Executing image build now...")
- self.docker_run(build_image_command)
+
+ extra_mounts = []
+ if self.vyos_mirror == "local":
+ apt_key_path = os.path.join(project_dir, "apt", "apt.gpg.key")
+ extra_mounts.append((apt_key_path, "/opt/apt.gpg.key"))
+
+ self.docker.run(
+ command=build_image_command,
+ work_dir="/vyos",
+ extra_mounts=extra_mounts,
+ log_command="IMAGE_BUILD_COMMAND"
+ )
image_path = None
build_dir = os.path.join(self.vyos_build_repo, "build")
@@ -117,7 +126,9 @@ class ImageBuilder:
if not os.path.exists(image_path):
logging.error(
- "Build failed (image not found), see log above for reason why, inspect build here: %s" % build_dir
+ "Build failed (image not found), see log above for reason why"
+ ", inspect build here: %s"
+ ", log file: %s" % (build_dir, get_my_log_file())
)
exit(1)
@@ -128,47 +139,16 @@ class ImageBuilder:
elapsed = round(monotonic() - begin, 3)
logging.info("Done in %s seconds, image is available here: %s" % (elapsed, new_image_path))
- def docker_run(self, command, log=True):
- docker_pieces: list = [
- "docker run --rm -it",
- "-v %s:/vyos" % quote(self.vyos_build_repo),
- ]
-
- if self.vyos_mirror == "local":
- apt_key_path = os.path.join(self.project_dir, "apt", "apt.gpg.key")
- docker_pieces.extend([
- "-v %s:/opt/apt.gpg.key" % quote(apt_key_path),
- ])
-
- docker_pieces.extend([
- "-w /vyos --privileged --sysctl net.ipv6.conf.lo.disable_ipv6=0",
- "-e GOSU_UID=%s -e GOSU_GID=%s" % (os.getuid(), os.getgid()),
- self.docker_image,
- ])
-
- if log:
- visual_docker_pieces = docker_pieces.copy()
- visual_docker_pieces.append("IMAGE_BUILD_COMMAND")
- logging.info("Using docker run command: '%s'" % " ".join(visual_docker_pieces))
-
- docker_pieces.extend([
- command,
- ])
- docker_command = " ".join(docker_pieces)
-
- execute(docker_command, passthrough=True)
-
- def start_local_webserver(self):
+ def start_local_apt_webserver(self):
address = self.get_local_ip() if not self.bind_addr else self.bind_addr
port = self.get_free_port(address) if not self.bind_port else self.bind_port
- thread = Thread(target=self.serve_webserver, args=(address, port), name="LocalWebServer", daemon=True)
+ thread = Thread(target=self.serve_apt, args=(address, port), name="LocalWebServer", daemon=True)
thread.start()
return "http://%s:%s/%s" % (address, "" if port == 80 else port, self.branch)
- def serve_webserver(self, address, port):
- os.chdir(os.path.join(self.project_dir, "apt"))
+ def serve_apt(self, address, port):
# noinspection PyTypeChecker
- server = ThreadingHTTPServer((address, port), WebServerHandler)
+ server = ThreadingHTTPServer((address, port), AptWebServerHandler)
server.serve_forever()
def get_free_port(self, address):
@@ -207,13 +187,16 @@ class ImageBuilder:
return selected_address
-class WebServerHandler(SimpleHTTPRequestHandler):
+class AptWebServerHandler(SimpleHTTPRequestHandler):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, directory=os.path.join(project_dir, "apt"), **kwargs)
+
def log_message(self, format, *args):
pass
if __name__ == "__main__":
- setup_logging()
+ setup_logging(name="image_builder")
try:
refuse_root()
@@ -223,6 +206,8 @@ if __name__ == "__main__":
parser.add_argument("--vyos-build-git", default="https://github.com/vyos/vyos-build.git",
help="Git URL of vyos-build")
parser.add_argument("--vyos-mirror", default="local", help="VyOS package repository (URL or 'local')")
+ parser.add_argument("--vyos-build-docker", default="vyos/vyos-build",
+ help="Default option uses vyos/vyos-build from dockerhub")
parser.add_argument("--extra-options", help="Extra options for the build-vyos-image")
parser.add_argument("--flavor", default="generic")
parser.add_argument("--build-by", default="myself@localhost")
@@ -239,4 +224,5 @@ if __name__ == "__main__":
exit(1)
except Exception as e:
logging.exception(e)
+ logging.error("Something went wrong, log file: %s" % get_my_log_file())
exit(1)
diff --git a/new/lib/docker.py b/new/lib/docker.py
new file mode 100644
index 0000000..916bb82
--- /dev/null
+++ b/new/lib/docker.py
@@ -0,0 +1,72 @@
+import logging
+import os
+from shlex import quote
+import shutil
+
+from lib.helpers import execute, quote_all, project_dir
+
+
+class Docker:
+ def __init__(self, image_name, branch, vyos_mount_dir):
+ self.image_name = image_name
+ self.branch = branch
+ self.vyos_mount_dir = vyos_mount_dir
+
+ def get_full_image_name(self):
+ return "%s:%s" % (self.image_name, self.branch)
+
+ def pull(self, passthrough=True):
+ docker_image = self.get_full_image_name()
+ execute("docker ")
+ execute("docker pull %s" % quote_all(docker_image), passthrough=passthrough)
+
+ def rmtree(self, target):
+ # This is sanity check, we really don't want to rm -rf something that isn't ours by mistake.
+ target = os.path.realpath(target)
+ if not target.startswith(project_dir):
+ raise Exception("Delete of %s DENIED, target is outside project_dir (%s)" % (target, project_dir))
+
+ try:
+ shutil.rmtree(target)
+ except PermissionError:
+ # I know, this is privilege escalation, but there is no other way.
+ # Unfortunately the docker container creates some files as root, and thus we don't have a choice.
+ # What the container messes up, the container needs to clean up.
+ # Here you can see the inherent security issue if container has root privileges.
+ # Any regular user with docker access can leverage the container to do anything as root.
+ # But this container needs to run as root in order to do its job so this is necessary evil.
+ # Ideally the container should be made not to leave behind files owned by root, tell this to the VyOS team.
+ logging.info("Deleting '%s' by force (privilege escalation)" % target)
+ self.run("bash -c %s" % quote("sudo rm -rf /delete-me/*"), extra_mounts=[
+ (target, "/delete-me")
+ ])
+ shutil.rmtree(target)
+
+ def run(self, command, work_dir="/vyos", extra_mounts=None, passthrough=True, log_command=None):
+ pieces: list = [
+ "docker run --rm -it",
+ ]
+
+ if os.path.exists(self.vyos_mount_dir):
+ pieces.append("-v %s:/vyos" % quote(self.vyos_mount_dir))
+
+ if extra_mounts is not None:
+ for mount in extra_mounts:
+ pieces.append("-v %s:%s" % quote_all(*mount))
+
+ pieces.extend([
+ "-w %s --privileged --sysctl net.ipv6.conf.lo.disable_ipv6=0" % quote(work_dir),
+ "-e GOSU_UID=%s -e GOSU_GID=%s" % (os.getuid(), os.getgid()),
+ quote(self.get_full_image_name()),
+ ])
+
+ if log_command:
+ placeholder = command if log_command is True else log_command
+ visual_pieces = pieces.copy()
+ visual_pieces.append(placeholder)
+ logging.info("Using docker run command: '%s'" % " ".join(visual_pieces))
+
+ pieces.append(command)
+
+ docker_run_command = " ".join(pieces)
+ return execute(docker_run_command, passthrough=passthrough, passthrough_prefix="DOCKER: ")
diff --git a/new/lib/helpers.py b/new/lib/helpers.py
index 327a88d..89237c0 100644
--- a/new/lib/helpers.py
+++ b/new/lib/helpers.py
@@ -1,15 +1,27 @@
import logging
+from logging.handlers import RotatingFileHandler
import os
+import re
import shlex
import subprocess
import sys
+from time import monotonic
+project_dir: str = os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))
-def execute(command, passthrough=False, timeout=None, **kwargs):
+
+def quote_all(*args):
+ quoted = []
+ for arg in args:
+ quoted.append(shlex.quote(arg))
+ return tuple(quoted)
+
+
+def execute(command, timeout: int = None, passthrough=False, passthrough_prefix=None, **kwargs):
if passthrough:
- kwargs["stdout"] = sys.stdout
- kwargs["stderr"] = sys.stderr
+ kwargs["stdout"] = subprocess.PIPE
+ kwargs["stderr"] = subprocess.STDOUT
if "stdout" not in kwargs:
kwargs["stdout"] = subprocess.PIPE
@@ -19,7 +31,32 @@ def execute(command, passthrough=False, timeout=None, **kwargs):
kwargs["shell"] = True
process = subprocess.Popen(command, **kwargs)
- process.wait(timeout)
+ if passthrough:
+ file_log_handler = find_file_log_handler()
+ buffer = TerminalLineBuffer()
+ stdout = process.stdout
+ deadline = monotonic() + timeout if timeout is not None else None
+ while process.poll() is None and (deadline is None or deadline < monotonic()):
+ # noinspection PyTypeChecker
+ value: bytes = stdout.read(1)
+ sys.stdout.buffer.write(value)
+
+ if file_log_handler is not None:
+ buffer.feed(value)
+ if buffer.is_complete():
+ line = buffer.get_line()
+ file_log_handler.handle(create_stdout_log_record(line, passthrough_prefix))
+
+ if file_log_handler is not None:
+ line = buffer.get_line()
+ if line:
+ file_log_handler.handle(create_stdout_log_record(line, passthrough_prefix))
+
+ if deadline is not None and deadline >= monotonic() and process.poll() is None:
+ process.kill()
+ raise subprocess.TimeoutExpired(process.args, timeout)
+ else:
+ process.wait(timeout)
exit_code = process.returncode
if exit_code != 0:
@@ -40,11 +77,34 @@ class ProcessException(Exception):
pass
-def quote_all(*args):
- quoted = []
- for arg in args:
- quoted.append(shlex.quote(arg))
- return tuple(quoted)
+class TerminalLineBuffer:
+ last_value: bytes
+
+ def __init__(self):
+ self.line_buffer = b""
+ # ANSI & carriage return
+ self.control_sequences_regex = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])|\x0D")
+
+ def feed(self, value: bytes):
+ self.last_value = value
+ self.line_buffer += value
+
+ def is_complete(self):
+ return self.last_value == b"\n"
+
+ def get_line(self):
+ line = self.line_buffer.decode("utf-8")
+ self.line_buffer = b""
+ line = line.replace("\r\n", "\n")
+ line = self.control_sequences_regex.sub("", line)
+ return line
+
+
+def create_stdout_log_record(message, passthrough_prefix=None, level=logging.INFO):
+ message = message.rstrip()
+ if passthrough_prefix is not None:
+ message = "%s%s" % (passthrough_prefix, message)
+ return logging.LogRecord("root", level, "", 0, message, None, None, None)
class LessThanLevelFilter(logging.Filter):
@@ -56,23 +116,51 @@ class LessThanLevelFilter(logging.Filter):
return 1 if record.levelno < self.maximum_level else 0
-def setup_logging():
+def setup_logging(name="test"):
logger = logging.getLogger()
logger.setLevel(logging.INFO)
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
stderr_level = logging.WARNING
- stdout = logging.StreamHandler(sys.stdout)
- stdout.setLevel(logging.INFO)
- stdout.addFilter(LessThanLevelFilter(stderr_level))
- stdout.setFormatter(formatter)
- logger.addHandler(stdout)
-
- stderr = logging.StreamHandler(sys.stderr)
- stderr.setLevel(stderr_level)
- stderr.setFormatter(formatter)
- logger.addHandler(stderr)
+ stdout_handler = logging.StreamHandler(sys.stdout)
+ stdout_handler.setLevel(logging.INFO)
+ stdout_handler.addFilter(LessThanLevelFilter(stderr_level))
+ stdout_handler.setFormatter(formatter)
+ logger.addHandler(stdout_handler)
+
+ stderr_handler = logging.StreamHandler(sys.stderr)
+ stderr_handler.setLevel(stderr_level)
+ stderr_handler.setFormatter(formatter)
+ logger.addHandler(stderr_handler)
+
+ log_file = os.path.join(project_dir, "build", "%s.log" % name)
+ file_handler = RotatingFileHandler(
+ log_file,
+ maxBytes=1048576 * 10,
+ backupCount=5,
+ encoding="utf-8",
+ )
+ file_handler.log_file = log_file
+ file_handler.setFormatter(formatter)
+ file_handler.setLevel(logging.INFO)
+ logger.addHandler(file_handler)
+
+
+def find_file_log_handler():
+ file_log_handler = None
+ for handler in logging.getLogger().handlers:
+ if isinstance(handler, RotatingFileHandler):
+ file_log_handler = handler
+ break
+ return file_log_handler
+
+
+def get_my_log_file():
+ file_log_handler = find_file_log_handler()
+ if file_log_handler is not None and hasattr(file_log_handler, "log_file"):
+ return file_log_handler.log_file
+ return "can't find it"
def refuse_root():
diff --git a/new/package_builder.py b/new/package_builder.py
index 2721f09..be40de7 100755
--- a/new/package_builder.py
+++ b/new/package_builder.py
@@ -3,26 +3,27 @@ import argparse
import logging
import os.path
from shlex import quote
-import shutil
from time import time, monotonic
import pendulum
from lib.apt import Apt
from lib.cache import Cache
+from lib.docker import Docker
from lib.git import Git
from lib.github import GitHub
-from lib.helpers import setup_logging, quote_all, execute, ProcessException, refuse_root
+from lib.helpers import setup_logging, ProcessException, refuse_root, project_dir, get_my_log_file
class Builder:
- directory = None
+ build_dir = None
docker_image = None
updated_repos = None
apt = None
+ docker = None
- def __init__(self, branch, single_package, dirty_build, ignore_missing_binaries, skip_build,
- skip_apt, force_build):
+ def __init__(self, branch, single_package, dirty_build, ignore_missing_binaries,
+ skip_build, skip_apt, force_build, vyos_build_docker):
self.branch = branch
self.single_package = single_package
self.dirty_build = dirty_build
@@ -30,10 +31,10 @@ class Builder:
self.skip_build = skip_build
self.skip_apt = skip_apt
self.force_build = force_build
+ self.vyos_build_docker = vyos_build_docker
- self.project_dir: str = os.path.realpath(os.path.dirname(__file__))
self.github = GitHub()
- self.cache = Cache(os.path.join(self.project_dir, "build", "builder-cache-%s.json" % self.branch), dict, {})
+ self.cache = Cache(os.path.join(project_dir, "build", "builder-cache-%s.json" % self.branch), dict, {})
def build(self):
begin = monotonic()
@@ -43,15 +44,16 @@ class Builder:
logging.info("Building packages for %s" % self.branch)
packages = self.get_packages_metadata()
- self.directory = os.path.join(self.project_dir, "build", self.branch)
- if not os.path.exists(self.directory):
- os.makedirs(self.directory)
+ self.build_dir = os.path.join(project_dir, "build", self.branch)
+ if not os.path.exists(self.build_dir):
+ os.makedirs(self.build_dir)
- self.apt = Apt(self.project_dir, self.branch, self.directory)
+ self.apt = Apt(project_dir, self.branch, self.build_dir)
logging.info("Pulling vyos-build docker image")
- self.docker_image = "vyos/vyos-build:%s" % self.branch
- execute("docker pull %s" % quote_all(self.docker_image), passthrough=True)
+ vyos_build_repo = os.path.join(os.path.join(self.build_dir, "vyos-build"))
+ self.docker = Docker(self.vyos_build_docker, self.branch, vyos_build_repo)
+ self.docker.pull()
self.updated_repos = []
for package in packages.values():
@@ -71,7 +73,7 @@ class Builder:
if "hash" not in my_state:
my_state["hash"] = None
- repo_path = os.path.join(self.directory, repo_name)
+ repo_path = os.path.join(self.build_dir, repo_name)
parent_path = repo_path
if package["build_type"] == "dpkg-buildpackage":
@@ -87,7 +89,7 @@ class Builder:
return
except ProcessException as e:
if "not a git repository" in str(e):
- self.rmtree(parent_path)
+ self.docker.rmtree(parent_path)
else:
raise
@@ -96,7 +98,8 @@ class Builder:
self.updated_repos.append(repo_name)
if os.path.exists(parent_path) and not self.dirty_build:
- self.rmtree(parent_path)
+ # We want to delete original repo and do fresh clone to clean cached build files.
+ self.docker.rmtree(parent_path)
if not os.path.exists(repo_path):
logging.info("Cloning repository %s" % package["git_url"])
@@ -109,15 +112,17 @@ class Builder:
logging.info("Using shared repository %s" % package["git_url"])
if package["build_type"] == "build.py":
- my_directory = os.path.join(self.directory, "vyos-build", package["path"])
+ my_directory = os.path.join(self.build_dir, "vyos-build", package["path"])
if not self.skip_build or new:
- self.docker_run("bash -i -c 'python3 ./build.py'", "/vyos/%s" % package["path"])
+ # It's important to run bash in interactive mode, non-interactive shell breaks dependency on .bashrc.
+ # It's also required to call python explicitly since some scripts don't have correct shebang.
+ self.docker.run("bash -i -c 'python3 ./build.py'", work_dir="/vyos/%s" % package["path"])
elif package["build_type"] == "dpkg-buildpackage":
- my_directory = os.path.join(self.directory, repo_name)
+ my_directory = os.path.join(self.build_dir, repo_name)
virtual_dir = "/vyos-%s" % package["package_name"]
- scripts_dir = os.path.join(self.project_dir, "scripts")
+ scripts_dir = os.path.join(project_dir, "scripts")
virtual_scripts = "%s-scripts" % virtual_dir
build_script = "generic-build-script.sh"
@@ -127,7 +132,9 @@ class Builder:
sources_dir = os.path.join(virtual_dir, "sources")
if not self.skip_build or new:
- self.docker_run("bash -i %s/%s" % (virtual_scripts, build_script), sources_dir, extra_mounts=[
+ # Again, interactive shell is essential.
+ virtual_build_script = os.path.join(virtual_scripts, build_script)
+ self.docker.run("bash -i %s" % quote(virtual_build_script), work_dir=sources_dir, extra_mounts=[
(my_directory, virtual_dir),
(scripts_dir, virtual_scripts),
])
@@ -139,6 +146,8 @@ class Builder:
dsc_files, binary_files = self.apt.scan_for_dist_files(my_directory)
if len(binary_files) == 0:
message = "%s: something is wrong, no binary files found" % package["package_name"]
+ message += ", build dir: %s," % my_directory
+ message += ", log file: %s" % get_my_log_file()
if self.ignore_missing_binaries:
logging.error(message)
else:
@@ -151,40 +160,6 @@ class Builder:
self.cache.set(package["package_name"], my_state)
- def docker_run(self, command, work_dir, extra_mounts=None):
- pieces = [
- "docker run --rm -it",
- "-v %s:/vyos" % quote(os.path.join(self.directory, "vyos-build")),
- ]
-
- if extra_mounts is not None:
- for mount in extra_mounts:
- pieces.append("-v %s:%s" % quote_all(*mount))
-
- pieces.extend([
- "-w %s --privileged --sysctl net.ipv6.conf.lo.disable_ipv6=0" % quote(work_dir),
- "-e GOSU_UID=%s -e GOSU_GID=%s" % (os.getuid(), os.getgid()),
- self.docker_image,
- command,
- ])
-
- command = " ".join(pieces)
- return execute(command, passthrough=True)
-
- def rmtree(self, directory):
- # sanity check
- if not directory.startswith(self.project_dir):
- raise Exception("Delete of %s denied, target is outside project_dir (%s)" % (directory, self.project_dir))
-
- try:
- shutil.rmtree(directory)
- except PermissionError:
- # Unfortunately the docker container creates some files as root, and thus we don't have choice...
- self.docker_run("bash -c %s" % quote("sudo rm -rf /delete-me/*"), "/vyos", extra_mounts=[
- (directory, "/delete-me")
- ])
- shutil.rmtree(directory)
-
def get_packages_metadata(self):
packages_timestamp = self.cache.get("packages_timestamp")
packages = self.cache.get("packages")
@@ -207,7 +182,7 @@ class Builder:
if __name__ == "__main__":
- setup_logging()
+ setup_logging(name="package_builder")
try:
refuse_root()
@@ -221,6 +196,8 @@ if __name__ == "__main__":
parser.add_argument("--skip-build", action="store_true")
parser.add_argument("--skip-apt", action="store_true")
parser.add_argument("--force-build", action="store_true")
+ parser.add_argument("--vyos-build-docker", default="vyos/vyos-build",
+ help="Default option uses vyos/vyos-build from dockerhub")
args = parser.parse_args()
builder = Builder(**vars(args))
@@ -230,4 +207,5 @@ if __name__ == "__main__":
exit(1)
except Exception as e:
logging.exception(e)
+ logging.error("Something went wrong, log file: %s" % get_my_log_file())
exit(1)