Add support for using pre-built images#6737
Add support for using pre-built images#6737arrrnas wants to merge 14 commits intotilt-dev:masterfrom
Conversation
4d647ce to
c4a8590
Compare
|
here's an example of using a generic ➜ tilt-initial-sync-example cat Tiltfile
# Example: using initial_sync to serve local files with a stock nginx image.
# No Dockerfile needed — just sync local files into the running container.
allow_k8s_contexts(k8s_context())
k8s_custom_deploy(
'nginx',
apply_cmd='kubectl apply -f k8s.yaml -o yaml',
delete_cmd='kubectl delete -f k8s.yaml --ignore-not-found',
deps=['site'],
container_selector='nginx',
live_update=[
initial_sync(),
sync('site', '/usr/share/nginx/html'),
],
)
k8s_resource('nginx', port_forwards='8080:80')➜ tilt-initial-sync-example cat k8s.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80➜ tilt-initial-sync-example cat site/index.html
<!DOCTYPE html>
<html>
<head><title>Hello from Tilt</title></head>
<body>
<h1>Hello from Tilt initial_sync!</h1>
<p>Edit this file and watch it update in the container.</p>
</body>
</html> |
|
high-level comments: overall i like the approach! i don't understand what problem you're trying to solve with the ignore arguments to initial_sync. in all the cases i can think of, this will put users in weird and inconsistent states? for example, it looks like the tests are broken? is that why the pr is still in draft? or are there other things you want to iterate on before review? |
|
hey @nicks and thanks for looking into this, apologies for the broken state of the PR, didn't have enough time to update it yet... regarding the ignore filter - our use case might be a bit contrived but the reason to ignore
this was the main use case for us with the ignore filter, but think there might be more - mono-repo where you might want just a subset of the repo to get synced when using a generic image (like in the nginx example). There's one main optimization that's missing from this, reconciler.go changes can be optimized I think, to simplify collecting all of the files that need to be synced, otherwise the structure of the PR shouldn't change much. |
Signed-off-by: arnas dundulis <arnas@vinted.com>
Signed-off-by: arnas dundulis <arnas@vinted.com>
Signed-off-by: arnas dundulis <arnas@vinted.com>
Without this, maybeSync is gated behind hasChangesToSync which requires file change events or container state changes. On a fresh tilt up, the container starts with no file changes detected, so maybeSync is never entered and isInitialSync is never checked. This means initial_sync silently does nothing. Fix: when InitialSync is configured on the spec, always set hasChangesToSync=true so the reconciler enters maybeSync. The isInitialSync check inside maybeSync still correctly gates on whether the container is new (lastFileTimeSynced is zero). Signed-off-by: arnas dundulis <arnas@vinted.com>
Two pre-existing test bugs: 1. Test containers use Image "frontend-image" which doesn't match the ImageMap selector (expects "local-registry:12345/frontend-image:my-tag"). Tests were actually syncing to the setupFrontend fixture's "main-id" container, not the test's "container-1". Assertions only checked call count so this was hidden. 2. f.Update() and f.kdUpdateStatus() auto-reconcile via MustReconcile, so initial sync fires during setup before the test's explicit MustReconcile call. Tests now clear f.cu.Calls before the assertion window and account for the correct reconcile sequence. Signed-off-by: arnas dundulis <arnas@vinted.com>
During initial sync, instead of collecting all individual file paths, converting them to PathMappings via FilesToPathMappings (O(files x syncs)), statting each for existence via MissingLocalPaths, and then creating a tar from individual file mappings — build the tar archive directly from directory-level sync mappings with the ignore filter passed to TarArchiveForPaths. This means a single directory walk produces the tar stream, skipping the redundant FilesToPathMappings conversion and MissingLocalPaths stat calls. collectAllSyncedFiles still runs for BoilRuns trigger matching. Benchmarks at 14k files: old (individual files): 189ms, 33MB allocs new (tar batched): 164ms, 21MB allocs (14% faster, 37% less memory) Also adds: - buildInitialSyncFilter: composes per-sync-path ignore matchers into a single filter for TarArchiveForPaths - Warnings when sync paths don't exist or dockerignore path has no .dockerignore file during initial sync Signed-off-by: arnas dundulis <arnas@vinted.com>
Add 6 tests that inspect the actual tar archive produced by initial_sync: - TarBatching_IgnoresFilteredFiles: IgnorePaths exclude files from tar - TarBatching_DockerignoreExcludesFromTar: .dockerignore patterns applied - TarBatching_MultipleSyncPathsInSingleTar: files from all syncs in one tar - TarBatching_NoDeletesDuringInitialSync: ToDelete is always empty - TarBatching_GlobPatternExcludesFromTar: **/testdata glob works - TarBatching_LargeFileTree: 1000 files with 100 ignored, correct count Signed-off-by: arnas dundulis <arnas@vinted.com>
- Rename noMoreInitialSync → seenInitialSync in liveUpdateFromSteps for clarity (the variable tracks whether we've seen an initial_sync step, not whether more are allowed) - Update IgnorePaths field comment to accurately describe dockerignore glob syntax support (**/ patterns, *.ext wildcards) instead of just "exact matches and directory prefixes" Signed-off-by: arnas dundulis <arnas@vinted.com>
Signed-off-by: arnas dundulis <arnas@vinted.com>
6582bb3 to
e1d6198
Compare
|
so the main change to figuring out which files to sync:
after:
benchmarked this to save ~14% time and ~37% memory at 14k files |
The previous approach set hasChangesToSync=true on every reconcile when InitialSync was configured, causing unnecessary work entering and exiting maybeSync after all containers have already been synced. Now uses monitor.needsInitialSync() which returns true only when no containers are tracked yet (first reconcile) or when any tracked container has never been synced (lastFileTimeSynced is zero). Once all containers complete their initial sync, the normal hasChangesToSync gate applies. Signed-off-by: arnas dundulis <arnas@vinted.com>
The err2 rename was an artifact of the tar batching refactor. Since err is not used in the outer scope at that point, use err directly with var. Signed-off-by: arnas dundulis <arnas@vinted.com>
filepath.IsAbs("/absolute/path") returns false on Windows since it's
not a Windows absolute path (no drive letter). Use path.IsAbs (POSIX
semantics) instead so the validation rejects Unix-style absolute paths
on all platforms.
Fixes TestLiveUpdate_InitialSync_AbsolutePathError on Windows.
Signed-off-by: arnas dundulis <arnas@vinted.com>
|
I think I'm done making changes, sorry for the messy PR :{ |
|
i thought about it a while. i don't think i'm comfortable accepting the change with the ignore_paths/dockerignore arguments to initial_sync. i understand the problem you're trying to solve. But the feature only works if you hold it in a very specific way. And will lead to weird/broken behavior otherwise. I'd be comfortable accepting the initial_sync without the ignore bits. I'm also open to discussing other solutions to this problem. |
|
hey @nicks fair enough, thinking about this, the explicit I'll update the PR to omit the ignore param. |
|
looking at the code my claim
is not correct, that applies to the normal My proposal would be: |
Signed-off-by: arnas dundulis <arnas@vinted.com>
9bdeedc to
df2adb5
Compare
|
I've modified the implementation to leverage the ignore paths that |
Adds
initial_sync()as a newlive_updatestep that syncs all local files to the container on start or restart. This enables live_update with pre-built images that weren't built locally.Addresses #2582. Partially addresses #3961 and #4776. Improves on
file_sync_onlyextension (tilt-dev/tilt-extensions#77).What it does
initial_sync()— new step, must be first in the list. On container start/restart, walks allsyncpaths and transfers files to the containerignore— accepts dockerignore-style glob patterns to exclude filesdockerignore— optional path to a directory containing a.dockerignoreto reuse its patternsrunsteps respect their triggers during initial sync — steps with unmatched triggers are skipped, steps without triggers always runWhat it doesn't do
initial_syncis opt-in