TOOL-30719 build packages in a container bootstrapped for the target suite - #404
TOOL-30719 build packages in a container bootstrapped for the target suite#404prakashsurya wants to merge 4 commits into
Conversation
c9c0415 to
ffb19e0
Compare
ffb19e0 to
f6c2b8e
Compare
74ca494 to
f14e21c
Compare
…suite Package builds ran natively on the build host's root filesystem: `setup.sh` rewrote `/etc/apt/sources.list` to the target suite's mirror, and `mk-build-deps --install` then put that suite's build dependencies onto the host. That coupled the build host's OS to the OS being built, which is why each Ubuntu LTS upgrade began by hand-bootstrapping a temporary buildserver from a stock Ubuntu AMI of the new release; the Delphix buildserver is itself an appliance image assembled from the very packages that need building. `setup.sh` now has two phases. On the host it bootstraps a root filesystem for `$UBUNTU_DISTRIBUTION` with `debootstrap --variant=buildd` and imports it as `linux-pkg-build:$UBUNTU_DISTRIBUTION`; inside the container it configures apt and installs the build tooling as before. Each entrypoint re-execs itself into that image with the checkout bind-mounted at the same absolute path, so the existing build code runs unchanged against a disposable root and a host running one LTS can build packages for the next. Consequences worth knowing about: - `UBUNTU_DISTRIBUTION` is now environment-overridable rather than a hard literal, so a build can target a suite other than the branch default. Any environment exporting that name steers the suite, the image tag, and the apt sources. - The secondary mirror's key is named with `signed-by=` on its own source line instead of being installed through `apt-key`, which does not exist in 26.04 and would otherwise break the in-container setup on the new suite. Scoping it there also drops the authority `apt-key` granted it over every other source in the file, the primary Ubuntu mirror included. - The host safety check asks the appliance image what it is, through `get-appliance-platform` and `get-appliance-variant`, rather than inferring disposability from AWS instance metadata answering. Metadata answers on any AWS instance, a developer's own cloud workstation included, so it never distinguished a buildserver from the machines the check exists to protect. A host that is not an aws/internal-buildserver image, a plain Ubuntu AWS instance included, now needs `DISABLE_SYSTEM_CHECK=true`. - Environment crossing the boundary is an explicit allowlist, so a variable the build needs and nobody listed fails loudly rather than silently changing what gets built. - `virtualization` receives the host's docker socket for its `-Ddockerize` step, with `--group-add` derived from the socket's own gid, since docker does not propagate the host's supplementary groups. - `LINUX_PKG_NO_CONTAINER=true` runs in place on the host, and `./buildpkg.sh -S <pkg>` opens a shell with a build's mounts and environment for reproducing a failure. The image is rebuilt when the resolved mirror snapshot, the invoking user's uid, or the Dockerfile's content changes. apt is repointed at the current snapshot on every run, so a stale rootfs would reintroduce the toolchain drift this is meant to remove; and the reuse check returns before `docker build` is reached, so docker's own layer cache never gets to notice a Dockerfile edit. README's System Requirements and Getting Started are rewritten accordingly, and `.gitignore` picks up `.superpowers/`, the per-plan scratch directory the agent workflow writes locally and that is never part of the repository. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The buildserver supplied a UTF-8 locale to every package build, ambiently, because every Delphix appliance image installs the locales package. A debootstrapped root has no generated locale at all, so glibc falls back to C/POSIX, javac's default source encoding becomes US-ASCII rather than UTF-8, and a build dies once ant reaches a file whose comments contain UTF-8 punctuation: GetTimezone.java:23: error: unmappable character (0xE2) for encoding US-ASCII 9 errors BUILD FAILED That took 33 minutes to surface in 'virtualization', which is what makes it worth a comment: unlike a missing binary, a wrong locale does not fail at dependency-install time. This belongs to the environment rather than to any one package's config.sh, and the evidence is that the host gave it to all 38 packages equally. windows-connector never exports LANG, yet its javac compiles the same UTF-8 source comments cleanly on the host and emits the diagnostic above in a container. Only 'virtualization' happens to export LANG, and on the host that export was redundant. Requiring each package to ask for a locale would also diverge silently rather than loudly, since a wrong locale changes encoding, sort order and date formatting instead of erroring. Generation and selection are kept together in the Dockerfile so they cannot drift: generating a locale nothing selects leaves glibc on C/POSIX and fixes nothing. Editing the Dockerfile changes the content hash the image cache is keyed on, so existing images rebuild rather than silently keeping the old environment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The swap that absorbs a package build's memory spikes belongs to the build host. delphix-platform provisions 8G on /dev/nvme1n1 for the internal-buildserver variant, and check_host_is_disposable() requires the host to be an aws/internal-buildserver image, so it is always present. Measured on a fresh clone of dlpx-internal-buildserver-develop: $ swapon --show NAME TYPE SIZE USED PRIO /dev/nvme1n1 partition 8G 0B -2 $ free -h Mem: 3.2Gi Swap: 8.0Gi The ansible arranging it is guarded by an "ansible-done" marker, so the obvious worry is that a clone skips it and comes up with nothing; the marker is absent from the image and written at each clone's first boot, so the swap is there every time. A host reached through DISABLE_SYSTEM_CHECK=true gets whatever swap it came with, which is consistent with the rest of what that flag opts out of. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
136e890 to
8b94a1f
Compare
| # | ||
| # The suite package builds run against. This must stay overridable via the | ||
| # environment, not a hard literal: a build needs to be able to target a suite | ||
| # other than the branch's default, which is the whole point of the container | ||
| # (see lib/container.sh). | ||
| # |
There was a problem hiding this comment.
[nit] perhaps could shorten this to "The suite package builds run against, which may be different than the branch's default (see lib/container.sh)."
|
|
||
| if [[ -z "$primary_url" ]] || [[ -z "$secondary_url" ]]; then | ||
| delphix_version="$DELPHIX_RELEASE_VERSION" | ||
| if compare_versions "$delphix_version" eq "9999.0.0.0" || |
There was a problem hiding this comment.
just for my understanding, what is this "9999.0.0.0" ? some special version value we use for something?
There was a problem hiding this comment.
it comes from here: https://github.com/delphix/linux-pkg/blob/develop/setup.sh#L36 .. I'm not introducing it, just moving the location..
doing a bit of digging, I think it originates from #338 and even before that, but we might have lost that history, if it was on a project branch..
it looks like when palash was previously doing the os-upgrade work, he set the product version to 999.0.0.0 since we didn't know what actual delphix version the new LTS would land on.. and in context of that new LTS, we were using a new package mirror host.. so we needed some way for new LTS builds to use the new package mirror host, and he settled on using a "fictional" delphix version, that was practically not going to collide with any real delphix version (we were still making actual delphis releases and version bumps, while that os-upgrade project branch was being worked on)..
we can likely revisit this decision, and probably remove this, since currently, we'll use the same package mirror host for develop, as well as the next LTS work for 26.04..
for now, I'll leave this as is, but open a follow up JIRA ticket to track the removal of this cruft.. I'm also unsure off the top of my head, what delphix version the os-upgrade branch will have for the 26.04 work.. I'm guessing it's still set to 9999.0.0.0, but I'll need to double check..
There was a problem hiding this comment.
yes.. looking at branch_schemas.toml in devops-gate, os-upgrade is set to 9999.0.0.0..
34 [[branch_schemas.branches]]
35 name = "os-upgrade"
36 active = true
37 version = "9999.0.0.0"
that's where this version value is coming from.. i.e. it has a backed in assumption about what the delphix version will be for the os-upgrade branch.. which is not obvious or intuitive when looking at this logic in isolation..
| # SECRET_DB_JUMP_BOX_PRIVATE_KEY holds a path to an SSH key on the host, so | ||
| # the path has to resolve inside the container as well. Docker's classic -v | ||
| # syntax does not refuse a missing host source; it silently creates an empty, | ||
| # root-owned directory there instead (confirmed in the Task 1 spike), so |
There was a problem hiding this comment.
should this Task 1 ref be removed? ie if it's a reference to the implementation/test process.
There was a problem hiding this comment.
yes, thanks.. I'll do that.
Each of these builds only because the buildserver is itself an appliance image, and so already has something the package never asked for. A debootstrapped root has none of it. - gdb-python build-depends on libkdumpfile in debian/control, which linux-pkg builds rather than the Ubuntu archive publishing, so apt cannot resolve it and mk-build-deps fails. It is declared in PACKAGE_DEPENDENCIES and installed from DEPDIR before mk-build-deps runs, which is what drgn already does for the same dependency. - zfs installs its systemd units only when configure decides systemd is wanted, and configure decides that by probing for the systemctl binary (config/user-systemd.m4). A host running systemd always has one; a debootstrapped root carries libsystemd0 and libudev1 but not systemd itself, so configure resolves to '--without systemd', the unit and preset files are never generated, and dh_install aborts on the paths debian/*.install lists unconditionally; e.g. 'zfs-zed missing files: lib/systemd/system/zfs-zed.service'. The only signal configure gives before that is a single line, 'checking for systemd support... no'. - docker-python-image runs 'docker pull' from debian/rules' override_dh_install to fetch the image it repackages, and containerized-masking reaches :tools:docker:buildLocalDockerImage, which builds one. Both need a docker daemon and say so through PACKAGE_NEEDS_DOCKER. Without the socket the latter misdirects, reporting 'Connect to http://127.0.0.1:2375 failed: Connection refused' rather than anything about docker being absent. - containerized-masking installs no JDK of its own, leaving the java-8 JAVA_HOME its build() exports pointing at nothing. It now installs the same build-dependencies list from dms-core-gate that the masking package installs. That list carries openjdk-17-jdk, and dms-core-gate's gradlew resets JAVA_HOME to 17 once a 17 JDK is present, which is why masking builds with the identical JAVA_HOME. Those JAVA_HOME lines are left alone, so containerized-masking and masking stay consistent rather than one of them changing which JDK builds the product. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
8b94a1f to
2c79feb
Compare
TOOL-30719
Why
Package builds run natively on the build host's root filesystem.
setup.shrewrites/etc/apt/sources.listto the target suite's mirror, andmk-build-deps --installthen puts that suite's build dependencies onto the host itself. A build is therefore not isolated from the machine running it in either direction: it mutates that machine, and it reads whatever that machine already happens to have.That couples the buildserver's OS to the OS being built, and the coupling bites hardest exactly when an Ubuntu LTS upgrade starts. The Delphix buildserver is itself an appliance image, assembled from the packages linux-pkg builds, so at the beginning of a cycle no machine exists that can build packages for the new suite: you need a buildserver on the new release to build the packages, and the buildserver is built from those packages. The standing workaround is to hand-bootstrap a throwaway one from a stock Ubuntu AMI of the new release (
dc image import, clone a VM, runbootstrap/playbook.ymland expect install failures, expand root to 200G, add thedockergroup, snapshot with a long expiry) purely to get a single machine able to build once. Nothing exercises that path between cycles, so it is re-derived from scratch each time, and since an LTS upgrade comes around every year or two it is usually re-derived by someone who has not done it before.The live instance is the 24.04 (
noble) to 26.04 (resolute) cycle now in progress. What this change buys there is that the existingnoblebuildserver can buildresolutepackages directly, by pointingUBUNTU_DISTRIBUTIONat the new suite, with no stock-AMI step at all. That variable is set per-branch inlib/common.shand theos-upgradebranch is where a cycle bumps it, so the suite a container is bootstrapped for follows the branch being built rather than the machine doing the building. One prerequisite is unchanged and still ordered ahead of this: the new suite has to be published in the internal package mirror first, since that mirror is whatdebootstrappulls the rootfs from.The second half of the coupling — that a build silently reads from its host — is what the Jenkins validation below spent most of its time on. Four packages turned out to be taking build inputs from the appliance image without declaring them, and a clean root is what made that visible.
What changes
setup.shnow has two phases. On the host it bootstraps a root filesystem for$UBUNTU_DISTRIBUTIONwithdebootstrap --variant=builddand imports it aslinux-pkg-build:$UBUNTU_DISTRIBUTION; inside the container it configures apt and installs the build tooling as before. Each entrypoint (buildpkg.sh,checkupdates.sh,sync-with-upstream.sh,push-merge.sh) re-execs itself into that image with the checkout bind-mounted at the same absolute path, so existing build code runs unchanged against a disposable root.sbuildandpbuilderwere considered and rejected. Their unit of work is a Debian source package built offline with dependencies declared indebian/control, whereas linux-pkg's unit is aconfig.shwith arbitrary hooks:virtualizationrunsantand needs a docker daemon,zfscross-builds modules against several kernel header trees fetched from S3, several packages declare build dependencies imperatively inprepare(), and builds need the network mid-build. Adopting either would mean re-expressing every package, which is a rewrite of the framework rather than a change to its build isolation.Consequences worth knowing about:
UBUNTU_DISTRIBUTIONis environment-overridable rather than a hard literal. This is a deliberate behavior change beyond the original scope, and it is load-bearing: every cross-suite test depends on it, and while it was a literal any override was silently a no-op. Any environment exporting that name steers the suite, the image tag, and the apt sources.aws/internal-buildserverimage now needsDISABLE_SYSTEM_CHECK=true, a plain Ubuntu AWS instance included. The host safety check asks the appliance image what it is, throughget-appliance-platformandget-appliance-variant. Inferring this from AWS instance metadata cannot work, since metadata answers on any AWS instance, a developer's own cloud workstation included.docker run. A variable is forgeable, and setting it on a host would skip the safety check and sendsetup.shdown the host path, rewriting that host's/etc/apt/sources.listand installing build packages onto it.PACKAGE_NEEDS_DOCKERand receive the host's socket, with--group-addderived from the socket's own gid, because docker does not propagate the host's supplementary groups.virtualization,docker-python-imageandcontainerized-maskingset it.signed-by=on its own source line. Installing it globally would give it authority over every source in the file, the primary Ubuntu mirror included;apt-keyis also absent from 26.04. Verified against the live mirror: the key gives a good signature on the secondaryppasrepo and cannot verify the primaryubunturepo at all.en_US.UTF-8locale. The buildserver supplies one ambiently, via thelocalespackage every appliance image installs, and a debootstrapped root has none. Generation and selection sit together in the Dockerfile so they cannot drift apart.zfsdeclaressystemdas a build dependency. Itsconfiguredecides whether to install unit files by probing for thesystemctlbinary, which is present on any host running systemd and absent from a debootstrapped root. Without itconfigureresolves to--without systemd, the units are never generated, anddh_installaborts. That collision is the only reason this was loud:debian/*.installlists the unit paths unconditionally, so a package that globbed them would have shipped without its units instead of failing.setup.shno longer arranges swap. It previously added a/swapfile, but returned early whenever the root filesystem was ZFS, which it always is on an appliance, so on every sanctioned build host it did nothing. delphix-platform provisions swap for theinternal-buildservervariant. That provisioning was unreliable, which is what TOOL-30723 below was about; it is fixed ondevelopand waiting on a buildserver image roll.docker buildis reached, so docker's own layer cache never gets to notice a Dockerfile edit.LINUX_PKG_NO_CONTAINER=trueruns in place on the host, and./buildpkg.sh -S <pkg>opens a shell with a build's mounts and environment for reproducing a failure.The container's tool contract was discovered empirically rather than designed:
wget,git,fakeroot,bc,sbsigntool,kmod,python3-pipand a pinnedawscligo intosetup.sh's in-container provisioning, each added in response to a specific observed failure and named in a comment next to it.gnupgis kept in the image despite having no known consumer, because confirming it is unnecessary needs a full build from an image built without it.Testing
Where this stands
build-packagesgreen for the appliance setcombine-packagescombine-packages→appliance-build→ image bootsvirtualizationandzfsin-container with Jenkins credentialssync-with-upstream.sh/push-merge.shin-containersync-with-upstream.shgreen.push-merge.shunreachable from this controller; gap documented belowFour container-attributable failures were found and fixed. Everything still red is pre-existing on
developor environmental, each with a control run to prove it.Verified locally, before Jenkins
On a DCoA buildserver VM (
noble, ZFS root, docker 29.7.1):resolute(26.04) root filesystem bootstraps and imports on anoblehost whosedebootstraphas noresolutescript until the symlink step creates oneptools,host-jdksandconnstatbuild end to end in-container,connstatincluding cross-compiled and signed kernel modules for five flavorsptoolsbuilds end to end inside aresolutecontainer on thatnoblehostdpkg -lis unchanged by a containerized build, which is the property the change exists forworkdir/artifactsowned by the invoking user, so the Publish stage can read themdocker runper invocation, i.e. the re-exec cannot recurseLINUX_PKG_NO_CONTAINER=truestill builds on the host path--group-add, and a container started that way reaches the daemon. Without--group-addthe same container getspermission denied-dbgsymDWARF recordsrustc 1.93.1where the host caps at1.75.0. Thelibc6floor was identical on both suites and is a dead end rather than evidenceshlibsdifferences between host-built and container-built for all three packages; container-vs-container builds are byte-identicalmake checkcleanVerified on Jenkins
Run on the selfservice controller against this branch. Where a package failed, the same job was re-run against
developas a control, so that "the container caused this" is measured rather than inferred.The headline run is
build-packages/pre-push#7329: 31 of 31 child builds green, no failures, 6h24m. It requested the appliance set minuswindows-connectorplusupgrade-verify, so every package in the Delphix Appliance exceptwindows-connectoris container-built in that one run.build-packages/pre-push#7329connstat+delphix-kernel+zfsbuild-kernel/pre-push#2969, folded into #7329combine-packagescombine-packages/pre-push#6007, from #7329buildpkg.sh, hardest packagebuild-package/virtualization#2343--group-add, Jenkins credentials, localebuildpkg.sh,zfsstandalonebuild-package/zfs#3558sync-with-upstream.shupdate-package/drgn#3combine-packages→appliance-build→ boot → testsappliance-build-orchestrator-pre-push#14722, fromupdate-package/drgn#3checkupdates.shcheck-updates/main#2appliance-build-orchestrator-pre-push#14725, from #6007Green in-container, all 36 requested packages:
bcc,challenge-response,cloud-init,connstat,crash-python,crypt-blowfish,delphix-go,delphix-kernel,delphix-platform,delphix-rust,delphix-sso-app,docker-python-image,drgn,dwarves,fluentd-gems,gdb-python,grub2,host-jdks,libkdumpfile,linux-kernel-aws,linux-kernel-azure,linux-kernel-gcp,linux-kernel-generic,linux-kernel-oracle,makedumpfile,masking,misc-debs,nfs-utils,performance-diagnostics,ptools,python-rtslib-fb,savedump,sdb,targetcli-fb,virtualization,zfs. Plusupgrade-verify, which is not built by linux-pkg but is part of the combined set.Item 2: how far the chain is proven
update-package/drgn#3 carried its own downstream all the way through, which is the first time anything pastcombine-packageshas run on this branch:So
combine-packages→appliance-build→ boot → integration tests all work. What that run does not show is an appliance assembled from container-built packages: pre-push builds skip the S3latestlink update (UPDATE_LATEST_LINK,linux_pkg_build_package.groovy:249, logged asSkipping update of 'latest' link), so #7326's combined set is host-builtlatestartifacts plus container-builtdrgnalone, 1 of 37.#14725 closes that gap. It is the same job with the same parameters, fed #7329's
combine-packagesoutput instead, where all 37 are container-built.Failures, and who owns each
gdb-pythondeveloppassesdocker-python-imagedevelopfails earlier, for an unrelated pre-existing reasonvirtualizationzfssystemdbuild input, soconfiguresilently disabled its unit files anddh_installabortedcontainerized-maskingdms-core-gatedefect. Control ondevelopalso failswindows-connectordevelopfails identicallydlpx-app-gate; not fixable from linux-pkgcheck-updates3h timeoutdeveloptoo, nightly since 2026-07-28zfs — undeclared
systemdbuild input, found on the re-runThe earlier
zfsfailure was an OOM, which masked this one: the build died before it ever reacheddh_install. With swap present it gets much further, through thelibzpoollink and the Rust object agent, and then:One line of
configureoutput explains it, and it is the only signal given:config/user-systemd.m4leavesenable_systemdatcheckand resolves it by probing for thesystemctlbinary. A host running systemd always has one; a--variant=buildddebootstrap root haslibsystemd0andlibudev1but notsystemd. The probe fails,configureresolves to--without systemd, the units are never generated, anddebian/*.installlists them unconditionally, sodh_installaborts.prepare()now installssystemd.configurereportschecking for systemd support... yes, and every path from the error list is back in the package:zfs-zed.service,zfs-scrub@/zfs-trim@and their weekly and monthly timers,zfs-volume-wait.service,zfs-volumes.target,zoa-chaos-monkey.service,lib/systemd/system-preset/50-zfs.preset,usr/lib/systemd/system-generators/zfs-mount-generator, andlib/modules-load.d/zfs.conf. Bothzfsutils-linuxandzfs-zedpostinst carry theirsystemctl enablesnippets, so the units are still enabled on install.gdb-python — undeclared
libkdumpfilebuild dependencylibkdumpfileis listed in the repo'sdebian/controlBuild-Depends, but it is built by linux-pkg rather than published in the Ubuntu archive, andconfig.shdeclared noPACKAGE_DEPENDENCIES, so nothing fetched it. On the host it resolved silently against the copy the appliance image already carried.drgnhas the same dependency and already handles it correctly, sogdb-pythonnow does the same thing.docker-python-image — needs the docker socket, and is already broken on
developThe control on
developfails earlier, for an unrelated reason: the buildserver carries docker-ce (docker-ce-cli29.7.1,containerd.io), whichConflictswithdocker.io/docker-cli/containerd, somk-build-depscannot install thedocker.ioitsdebian/controlasks for at all. In a debootstrapped root there is no docker-ce,docker.ioinstalls cleanly, and the build gets further before hitting the real missing input:debian/rules'override_dh_installneeds a docker daemon, so the package now declaresPACKAGE_NEEDS_DOCKER. Theinvoke-rc.d: unknown initscript, /etc/init.d/docker not foundline in the log is a red herring: that isdocker.io's postinst in a container with no init system, andpreparecompletes after it.virtualization — the container had no generated locale
The socket and
--group-addworked andpreparecompleted, butantthen ran for 33 minutes beforejavacfailed on UTF-8 punctuation in source comments:packages/virtualization'sbuild()already exportsLANG=en_US.UTF-8, but exporting a locale is inert unless it has been generated, and a debootstrapped root has none: glibc falls back to C/POSIX and javac's default source encoding becomes US-ASCII.This belongs to the environment rather than to any one package, and the evidence is that the host supplied it to every package equally —
windows-connectornever exportsLANG, yet itsjavaccompiles the same UTF-8 comments cleanly on the host and emits the diagnostic above in a container. A wrong locale is also silent rather than loud, changing encoding, sort order and date formatting instead of failing, which is the wrong thing to make each package remember to ask for. The Dockerfile installslocales, runslocale-gen en_US.UTF-8, and setsENV LANG.The fix is visible in #2343's log as
locale-gen en_US.UTF-8... doneandStep 6/12 : ENV LANG=en_US.UTF-8, with nounmappable characterdiagnostics and everyant/javacphase reportingBUILD SUCCESSFUL.containerized-masking — two container gaps in front of an upstream defect
It has no
prepare()stage, so no JDK is installed and the java-8JAVA_HOMEitsbuild()exports points at nothing. Worth being precise about what this is not:maskingsets the identical java-8JAVA_HOMEand builds fine, becausedms-core-gate's owngradlewlogsSetting JAVA_HOME to Java 17 for buildand overrides it once it finds theopenjdk-17-jdkthatmasking'sprepare()installs.containerized-maskingruns a different task in the same repo without installing that list, so the wrapper has nothing to switch to. It now installs the same list, and also declaresPACKAGE_NEEDS_DOCKER, since it reaches:tools:docker:buildLocalDockerImage. Both staleJAVA_HOMElines are left alone, so neither package quietly changes which JDK builds the product.Neither fix makes it green, because it fails on
developtoo. Both fixes are confirmed working in the latest run:openjdk-17-jdkinstalls, the wrapper logsSetting JAVA_HOME to Java 17 for build, and the build reaches and completes the docker image it was asked for. It then dies indms-core-gate'stools/docker/build.gradle:98:The fixes are still worth having: without the socket the container failed earlier and misleadingly, reporting
Connect to http://127.0.0.1:2375 failed: Connection refusedrather than anything about docker being absent. This package is in neitherbuild/main.pkgsnorbuild/kernel-modules.pkgs, so it gates nothing here.windows-connector — pre-existing, not fixable from linux-pkg
Fails on both branches at the same place,
appliance/host/windows/build.gradle:253in:host:windows:buildDelphixConnectorService, which shells out to its ownapt-get installfor the mono toolchain and exits 100. Only apt's proximate complaint differs: on the control it isE: Error, pkgProblemResolver::Resolve generated breakswithpython3unmet; in-container it isE: Packages were downgraded and -y was used without --allow-downgradesontzdata. Thatapt-getlives indlpx-app-gate, not in a packageconfig.sh. linux-pkg's owninstall_pkgs()does pass--allow-downgrades; this build bypasses that helper.Because this failure stands,
build-packagescannot report green withBUILD_POLICY=ALL, and its pre-pushcombine-packagesstage runs after the build stage, so one failing package aborts the pipeline before anyCOMBINED_PACKAGES_S3_URLis produced. #7329 therefore requests the appliance set minuswindows-connector, which is why it is 36 packages rather than 37.check-updates — a 3h job timeout that
develophits toocheck-updates/main#2 went UNSTABLE at the 3 hour mark. This is not a container problem and not a hang, both of which were earlier readings of it.The nightly production job on the ops controller, host path, identical hardcoded timeout and identical package list, has timed out on every run since 2026-07-28:
Only the five
linux-kernel-*packages cost anything; every other package finishes its stages in single-digit seconds.fetch_repo_from_gittakes a deep path whenDO_UPDATE_PACKAGEis true, fetchingrepo-HEADandupstreams/developin full, because detecting an upstream update needs history a--depth=1clone does not have. Those fetches grow with the repositories:genericis what breaks the run: 35 min on 07-26, 43 min on 07-27, and still fetching after 101.5 min on 08-04.That the container is not implicated is measurable rather than assumed. The same repo and branch fetched by a
build-packagejob, which uses--depth=1, takes 78 s in-container against 93 s on the host, i.e. the container is marginally faster. Comparing the same day's deep fetches,awsis 1253 s in-container against 1277 s on the host.Filed as TOOL-30727 with a timeout bump in devops-gate#4682. The operational consequence is worth knowing independently of this PR:
UPDATE_PACKAGESis true in production, solinux-kernel-genericandlinux-kernel-oraclehave had no upstream update detection for eight days, and UNSTABLE rather than FAILURE meant nothing flagged it.One correction to an earlier claim in this PR:
check-updatesdoes not run the fullsetup.shonce per package.debootstrapand the image build each happen exactly once. What repeats is the container start plus the in-container apt install, 13 times at roughly a minute each, so baking the tool install into the image would recover about 13 minutes, not hours.What the container costs in build latency
Measured across 19 packages with both a container-built and a host-built run. The overhead is close to constant per build rather than proportional to build size, so the ratio is large for trivial packages and small for the ones that set wall clock.
Summed over the 19: 374 min host against 472 min container, 1.26x. Median absolute delta +4.5 min, range +0.5 to +18.1.
maskingis +0.5,virtualization+4.4,delphix-rust+3.8.Of that, about 2.7 min is fixed setup, measured from the logs as
debootstraparound 70 s,docker buildaround 40 s, and the in-container tooling install around 45 s. The remainder is each package's build dependencies, which the buildserver already had installed and a clean root must fetch. That part is not waste; it is the coupling this change removes.It does not amortize in CI. Every
build-packagejob runsdc clone-latestonto a fresh DCenter VM, so there is no local image cache to hit anddebootstrapruns once per build. The reuse check helps repeated builds on one machine, i.e. a developer's, and never CI. Removing that cost in CI would mean publishing the image to a registry or baking it into the buildserver image.Pipeline latency grows by less than 1.26x, because
build-packagesbatches in parallel and its wall clock is set by the longest chain,build-kernelat 1.11x. #7329 took 6h24m, of whichbuild-kernelwas 5h22m.grub2is excluded from the table as non-comparable: its host runs predateDLPX-97018 grub2 package doesn't need to be rebuilt, so its apparent speedup is a package change rather than a container effect.systemd units still ship, and are still enabled
zfslosing its units raised the obvious question of whether other packages lose theirs silently, since a package that globbed those paths would have shipped without them rather than failing. Audited by runningdpkg-deb -cover the container-built artifacts of every package in the appliance set.Only builds that probe the environment are exposed.
zfswas the only one.nfs-utilspasses--with-systemdexplicitly, so there is nothing to detect;delphix-platform, app-gate andmaskingcopy static unit files.delphix-platformnfs-utilsnfs-server,nfs-mountd,nfs-idmapd,rpc-statd,nfs-client.target,proc-fs-nfsd.mountvirtualizationdelphix-mgmt,delphix-nginx,delphix-postgres@,delphix-statcloud-initgrub2,targetcli-fb,python-rtslib-fb,maskingperformance-diagnostics,delphix-sso-appdelphix-telegraf.service,delphix-sso-app.serviceNineteen other packages ship no units at all, which is correct rather than a regression:
savedumpis a Python script, the rest are libraries and tools. Not audited:linux-kernel-*, which are kernel images,windows-connector, which does not build and ships no Linux units, andupgrade-verify, which linux-pkg does not build.Enablement is structurally unaffected.
dh_installsystemdgenerates the postinst and prerm snippets from the presence of.servicefiles indebian/tmp, and the enabling itself runsdeb-systemd-helperon the target at install time. Neither needs systemd in the build root.Still to verify
combine-packagesoutput. The chain itself is already green through boot and integration tests via #14722.zfsreliability rather thanzfscorrectness. Its greens were all obtained on a buildserver image that predates the TOOL-30723 swap fix, so they represent the device-enumeration coin flip landing favourably, three times. The systemd fix is what the greens prove; robustness waits on the image roll.Not proven
http://archive.ubuntu.com/ubuntu, because the Delphixresolutemirror does not exist yet. The code path is identical and only the URL differs, but the production mirror configuration for a new suite is unexercised, including the secondary PPA source.push-merge.shnever runs in-container. Theupdate-packagejobs on selfservice hardcodeDRYRUN=true, and the pipeline only callspush-merge.shwhenDRYRUNis false, which exists only on ops and pushes a real upstream merge onto a package'sdevelopbranch. Reaching it was judged not worth mutating a shared repo. The untested delta is narrow: the container boundary, theDRYRUNandPUSH_GIT_TOKENallowlist entries, andpush_to_remoteagainst a real remote are all covered by thesync-with-upstream.shrun, leaving onlygit ls-remoteand the final push target unexercised.check-updateswere about twice as slow in-container as the same day's production run (azure3504 s against 1742 s,gcp2595 s against 1377 s) whileawsmatched within 2%. The most likely explanation is contention, since the container run overlapped three concurrentbuild-packagesruns while production runs at 03:06 on a quiet controller, but that is not demonstrated and a quiet-period control would settle it.Consequently the LTS runbook's stock-AMI step is reframed as conditional rather than deleted, in both
topics/ubuntu-lts-upgrade-runbook.mdand the 26.04 project page.Related
check-updateshas timed out nightly since 2026-07-28, solinux-kernel-genericandlinux-kernel-oracleupstream updates are silently skipped. Pre-existing and reproducing ondevelop; surfaced by this work. Timeout bump in devops-gate#4682./dev/nvme1n1, which is sometimes the root pool disk, so a build VM could come up with no swap. Filed from this work, independent of it. Fixed ondevelopby delphix-platform#566, waiting on a buildserver image roll.sign_modules()swallows a failedkmodsignbecause its loop runs in a subshell, so a signing failure would ship an unsigned module while reporting success. Pre-existing, deliberately not fixed here.🤖 Generated with Claude Code