Documentation
¶
Overview ¶
Package sorter provides sorting functionality for Watchtower containers. It implements dependency-based topological sorting and creation time ordering.
Key components:
- SortByDependencies: Sorts containers in place by links, detecting circular references.
- SortByCreated: Sorts containers in place by creation time with fallback to current time.
- Sorter: Common interface for all sorting implementations.
Usage example:
// log is the process *zerolog.Logger.
err := sorter.SortByDependencies(log, containers, useComposeDependsOn)
if err != nil {
log.Error().Err(err).Msg("Dependency sort failed")
}
err = sorter.SortByCreated(log, containers)
if err != nil {
log.Error().Err(err).Msg("Time sort failed")
}
The package uses zerolog for logging sort operations and errors.
Index ¶
- Variables
- func ExtractServiceName(identifier string) string
- func FindMatchingIdentifiers(link string, identifiers []string) []string
- func IsPositiveInteger(s string) bool
- func SortByCreated(log *zerolog.Logger, containers []types.Container) error
- func SortByDependencies(log *zerolog.Logger, containers []types.Container, useComposeDependsOn bool) error
- type CircularReferenceError
- type DependencySorter
- type IdentifierCollisionError
- type Sorter
- type TimeSorter
Constants ¶
This section is empty.
Variables ¶
var ErrCircularReference = errors.New("circular reference detected")
ErrCircularReference indicates a circular dependency between containers.
var ErrIdentifierCollision = errors.New("identifier collision detected")
ErrIdentifierCollision indicates an identifier collision between containers.
Functions ¶
func ExtractServiceName ¶
ExtractServiceName extracts the service name from a container identifier.
Container identifiers from ResolveContainerIdentifier() follow the pattern:
- "project-service" when both project and service labels exist
- "project-service-N" for Docker Compose replicas (N is replica number)
- "servicename" when only service name is available (no project context)
This function extracts just the service name by:
- If there's no hyphen, return the whole string (it's already just a service name)
- If there are hyphens, the service name is the last segment (or last two segments if the last segment is a replica number)
Examples:
- "postgresql-postgres" -> "postgres"
- "postgresql-postgres-1" -> "postgres" (strips replica suffix)
- "myapp" -> "myapp"
- "my-app-service" -> "service" (last segment before any replica number)
- "my-app-service-2" -> "service" (strips replica suffix)
func FindMatchingIdentifiers ¶
FindMatchingIdentifiers returns the identifiers from the given list that match the provided link. It applies the same strategies used when building the dependency graph:
- Exact match.
- Replica prefix match: the identifier starts with "<link>-" and the suffix after the hyphen is a positive integer (Docker Compose replica numbering).
- Project-qualified suffix match (identifier ends with "-"+link), and for unhyphenated links only, ExtractServiceName equality on both sides. This strategy only succeeds when exactly one candidate matches. Multiple matches (e.g. the same service name in different projects) are treated as ambiguous and return no results.
Parameters:
- link: Dependency link to resolve (typically from Container.Links()).
- identifiers: List of known container identifiers to search within.
Returns:
- []string: Matching identifiers. Returns nil or an empty slice when there is no match or when the service-only strategy finds multiple candidates.
func IsPositiveInteger ¶
IsPositiveInteger checks if a string represents a positive integer (1 or greater).
This validation is critical for distinguishing Docker Compose-style replica suffixes (e.g., "db-1", "db-2") from other hyphenated container names (e.g., "db-backup", "db-temp"). Docker Compose uses sequential positive integers starting from 1 to identify replica instances. By requiring a positive integer suffix, we ensure that:
- "db" correctly matches "db-1" and "db-2" as replicas
- "db" does NOT match "dbase" (no hyphen) or "db-backup" (non-numeric suffix)
- "database" does NOT match "database2" (no separator)
This prevents false dependency relationships between unrelated containers with similar names, which could cause incorrect update ordering or circular dependencies.
func SortByCreated ¶
SortByCreated sorts containers in place by creation time.
Parameters:
- log: Process logger (may be unused by time sort).
- containers: Slice to sort in place.
Returns:
- error: propagated from TimeSorter.Sort.
func SortByDependencies ¶
func SortByDependencies(log *zerolog.Logger, containers []types.Container, useComposeDependsOn bool) error
SortByDependencies sorts containers in place by dependencies.
Parameters:
- log: Process logger.
- containers: Slice to sort in place.
- useComposeDependsOn: Whether to include Docker Compose depends_on label in dependency resolution.
Returns:
- error: Non-nil if circular reference detected, nil on success.
Types ¶
type CircularReferenceError ¶
CircularReferenceError represents a circular dependency error with the container name and cycle path.
func (CircularReferenceError) Error ¶
func (e CircularReferenceError) Error() string
Error implements the error interface.
func (CircularReferenceError) Unwrap ¶
func (e CircularReferenceError) Unwrap() error
Unwrap returns the underlying error for errors.Is compatibility.
type DependencySorter ¶
type DependencySorter struct{}
DependencySorter handles topological sorting by dependencies.
func (DependencySorter) Sort ¶
func (ds DependencySorter) Sort(log *zerolog.Logger, containers []types.Container, useComposeDependsOn bool) error
Sort sorts containers in place by dependencies, placing Watchtower containers last.
This function implements a two-phase sorting strategy to ensure proper update order:
- Separate Watchtower containers from regular containers, as Watchtower instances should always be updated last to avoid disrupting the update process itself.
- Perform topological sorting on non-Watchtower containers using Kahn's algorithm to respect dependency relationships (containers that depend on others must be updated after their dependencies).
The sorting ensures that:
- Dependent containers are updated after their dependencies
- Watchtower containers are processed last to maintain monitoring capability
- Circular dependencies are detected and reported as errors
Time Complexity: O(V + E) where V is containers and E is dependency links Space Complexity: O(V + E) for graph structures
Parameters:
- containers: Slice to sort in place. Modified directly.
- useComposeDependsOn: Whether to include Docker Compose depends_on label in dependency resolution.
Returns:
- error: Non-nil if circular reference detected, nil on success. Error includes the container name and cycle path for debugging.
type IdentifierCollisionError ¶
type IdentifierCollisionError struct {
DuplicateIdentifier string
AffectedContainers []types.Container
}
IdentifierCollisionError represents an error when multiple containers have the same normalized identifier.
func (IdentifierCollisionError) Error ¶
func (e IdentifierCollisionError) Error() string
Error implements the error interface.
func (IdentifierCollisionError) Unwrap ¶
func (e IdentifierCollisionError) Unwrap() error
Unwrap returns the underlying error for errors.Is compatibility.
type Sorter ¶
type Sorter interface {
Sort(log *zerolog.Logger, containers []types.Container, useComposeDependsOn bool) error
}
Sorter provides a common interface for sorting containers.
type TimeSorter ¶
type TimeSorter struct{}
TimeSorter sorts containers by creation time.
func (TimeSorter) Sort ¶
Sort sorts containers in place by creation time, using far future time as fallback for invalid dates.
Parameters:
- containers: Slice to sort in place.
- useComposeDependsOn: Whether to include Docker Compose depends_on label (unused for time sorting).
Returns:
- error: Always nil (no errors possible).