9.9 KiB
Watchtower — Detailed Update Flow & Data Shapes
This file provides a precise, developer-oriented mapping of the update call chain and full data-shape details with file references to help maintenance and debugging.
Note: file paths are relative to the repository root.
Entry points
-
main()—main.go- Sets default log level and calls
cmd.Execute().
- Sets default log level and calls
-
cmd.Execute()/ Cobra root command —cmd/root.goPreRunconfigures flags, createscontainer.Client, sets registry flags (registry.InsecureSkipVerify,registry.RegistryCABundle) and may validate CA bundle.runUpdatesWithNotificationsconstructstypes.UpdateParamsand callsinternal/actions.Update.
Primary orchestration
internal/actions.Update(client container.Client, params types.UpdateParams) (types.Report, error)—internal/actions/update.go- High level steps:
- Optional pre-checks:
pkg/lifecycle.ExecutePreChecks(client, params)ifparams.LifecycleHooks. - Container discovery:
client.ListContainers(params.Filter)(wrapper inpkg/container/client.go). - For each container:
client.IsContainerStale(container, params)— defined inpkg/container/client.go.- Pull logic:
client.PullImage(ctx, container)(may skip viacontainer.IsNoPull(params)). - Digest optimization:
pkg/registry/digest.CompareDigest(container, registryAuth).- Token flow:
pkg/registry/auth.GetToken→GetBearerHeader→GetAuthURL. - Token cache: see
pkg/registry/auth/auth.go(getCachedToken,storeToken). - HEAD request:
pkg/registry/digest.GetDigestconstructshttp.Clientwithdigest.newTransport().
- Token flow:
- Pull logic:
client.HasNewImage(ctx, container)compares local and remote image IDs.container.VerifyConfiguration()to ensure image/container metadata is sufficient to recreate the container.- Mark progress via
session.Progress(AddScanned,AddSkipped), callcontainers[i].SetStale(stale).
- Sort by dependencies:
sorter.SortByDependencies(containers). UpdateImplicitRestart(containers)setsLinkedToRestartingflags for dependent containers.- Build
containersToUpdate(non-monitor-only) and mark for update inProgress. - Update execution:
- Rolling restart (
params.RollingRestart):performRollingRestartstops and restarts each marked container in reverse order. - Normal:
stopContainersInReversedOrderthenrestartContainersInSortedOrder.- Stop:
stopStaleContaineroptionally runslifecycle.ExecutePreUpdateCommandandclient.StopContainer. - Restart:
restartStaleContainermayclient.RenameContainer(if self),client.StartContainer, thenlifecycle.ExecutePostUpdateCommand.
- Stop:
- Rolling restart (
- Optional
cleanupImages(client, imageIDs)whenparams.Cleanup. - Optional post-checks:
pkg/lifecycle.ExecutePostChecks(client, params). - Return
progress.Report().
- Optional pre-checks:
- High level steps:
File-level locations (key functions)
-
internal/actions/update.goUpdate,performRollingRestart,stopContainersInReversedOrder,stopStaleContainer,restartContainersInSortedOrder,restartStaleContainer,UpdateImplicitRestart.
-
pkg/container/client.godockerClient.IsContainerStale,PullImage,HasNewImage,ListContainers,GetContainer,StopContainer,StartContainer,RenameContainer,RemoveImageByID,ExecuteCommand.
-
pkg/container/container.go- Concrete
Containerstruct and implementation oftypes.Container.
- Concrete
-
pkg/registry/auth/auth.goGetToken,GetBearerHeader, token cache functionsgetCachedTokenandstoreToken.
-
pkg/registry/digest/digest.goCompareDigest,GetDigest,newTransport(transport respectsregistry.InsecureSkipVerifyandregistry.GetRegistryCertPool()),NewTransportForTest.
-
pkg/registry/registry.goInsecureSkipVerify(bool),RegistryCABundle(string), andGetRegistryCertPool().
-
pkg/lifecycle/lifecycle.goExecutePreChecks,ExecutePostChecks,ExecutePreUpdateCommand,ExecutePostUpdateCommand.
-
pkg/session/progress.goandpkg/session/container_status.goProgress(map) andContainerStatuswith fields and state enum.
Data shapes — full details
Below are the main data shapes used in the update flow with fields and brief descriptions.
types.UpdateParams (file: pkg/types/update_params.go)
type UpdateParams struct {
Filter Filter // Filter applied to container selection
Cleanup bool // Whether to remove old images after update
NoRestart bool // Skip restarting containers
Timeout time.Duration// Timeout used when stopping containers / exec
MonitorOnly bool // Global monitor-only flag
NoPull bool // Global no-pull flag
LifecycleHooks bool // Enable lifecycle hook commands
RollingRestart bool // Use rolling restart strategy
LabelPrecedence bool // Prefers container labels over CLI flags
}
container.Client interface (file: pkg/container/client.go)
Methods (signatures):
ListContainers(Filter) ([]types.Container, error)— discover containersGetContainer(containerID types.ContainerID) (types.Container, error)— inspect containerStopContainer(types.Container, time.Duration) errorStartContainer(types.Container) (types.ContainerID, error)RenameContainer(types.Container, string) errorIsContainerStale(types.Container, types.UpdateParams) (bool, types.ImageID, error)ExecuteCommand(containerID types.ContainerID, command string, timeout int) (SkipUpdate bool, err error)RemoveImageByID(types.ImageID) errorWarnOnHeadPullFailed(types.Container) bool
types.Container interface (file: pkg/types/container.go)
Key methods used during update: (method signatures only)
ContainerInfo() *types.ContainerJSONID() ContainerIDIsRunning() boolName() stringImageID() ImageIDSafeImageID() ImageIDImageName() stringEnabled() (bool, bool)IsMonitorOnly(UpdateParams) boolScope() (string, bool)Links() []stringToRestart() boolIsWatchtower() boolStopSignal() stringHasImageInfo() boolImageInfo() *types.ImageInspectGetLifecyclePreCheckCommand() stringGetLifecyclePostCheckCommand() stringGetLifecyclePreUpdateCommand() stringGetLifecyclePostUpdateCommand() stringVerifyConfiguration() errorSetStale(bool)/IsStale() boolIsNoPull(UpdateParams) boolSetLinkedToRestarting(bool)/IsLinkedToRestarting() boolPreUpdateTimeout() int/PostUpdateTimeout() intIsRestarting() boolGetCreateConfig() *dockercontainer.Config/GetCreateHostConfig() *dockercontainer.HostConfig
Concrete Container fields (file: pkg/container/container.go):
LinkedToRestarting boolStale boolcontainerInfo *types.ContainerJSONimageInfo *types.ImageInspect
session.ContainerStatus (file: pkg/session/container_status.go)
Fields:
containerID types.ContainerIDoldImage types.ImageIDnewImage types.ImageIDcontainerName stringimageName stringerror(embedded error)state session.State(enum: Skipped/Scanned/Updated/Failed/Fresh/Stale)
session.Progress is map[types.ContainerID]*ContainerStatus and exposes helper methods: AddScanned, AddSkipped, MarkForUpdate, UpdateFailed, and Report() which returns a types.Report.
types.TokenResponse (used by pkg/registry/auth) — inferred fields
Token stringExpiresIn int(seconds)
Registry TLS configuration (file: pkg/registry/registry.go)
var InsecureSkipVerify bool— when true,digest.newTransport()setstls.Config{InsecureSkipVerify: true}var RegistryCABundle string— path to PEM bundle;GetRegistryCertPool()reads/merges it into system roots
Token cache (file: pkg/registry/auth/auth.go)
Implementation details:
type cachedToken struct { token string; expiresAt time.Time }var tokenCache = map[string]cachedToken{}protected bytokenCacheMu *sync.Mutexvar now = time.Now(overridable in tests)getCachedToken(key string) stringreturns token if present and not expired (deletes expired entries)storeToken(key, token string, ttl int)stores token with TTL (seconds), ttl<=0 => no expiry- Cache key: full auth URL string (realm+service+scope)
Transport behavior for digest HEAD & token requests
pkg/registry/digest.newTransport()builds a*http.Transportthat:- Uses
http.ProxyFromEnvironmentand sane defaults for timeouts and connection pooling. - If
registry.InsecureSkipVerifyis true, setsTLSClientConfig = &tls.Config{InsecureSkipVerify: true}. - Else, if
registry.GetRegistryCertPool()returns a non-nil pool, setsTLSClientConfig = &tls.Config{RootCAs: pool}(merges system roots + bundle).
- Uses
Edge cases and behavior notes
- If
container.VerifyConfiguration()fails, container is marked skipped with the error logged and the update continues for other containers. - If
lifecycle.ExecutePreUpdateCommandreturnsskipUpdate(exit code 75), the container update is skipped. - Watchtower self-update: the current watchtower container is renamed before starting the new container so the new container can reclaim the original name.
- Digest HEAD failures fall back to full
docker pulland may log atWarndepending onWarnOnHeadPullFailed. - Tokens are scoped per
repository:<path>:pull— this prevents accidental reuse across repositories.
How to use this doc
- Use the file references above to jump to implementations when changing behavior (e.g., token caching or TLS transport changes).
- For any change that affects pull/token behavior, update
pkg/registry/authtests andpkg/registry/digesttests, and run race-enabled tests.
If you want, I can also open a PR body (title + description + checklist) for you to paste into GitHub, or generate a patch file containing these new docs for you to push from your machine.