mirror of
https://github.com/containrrr/watchtower.git
synced 2025-12-16 15:10:12 +01:00
Adjusting implementation of delay-days to those suggested in PR review comments, to perform necessary checks in update.go. Added some comments where useful for understanding existing functionality.
This commit is contained in:
parent
6bd125d137
commit
8bcf748eb0
2 changed files with 46 additions and 38 deletions
|
|
@ -2,6 +2,8 @@ package actions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/containrrr/watchtower/internal/util"
|
"github.com/containrrr/watchtower/internal/util"
|
||||||
"github.com/containrrr/watchtower/pkg/container"
|
"github.com/containrrr/watchtower/pkg/container"
|
||||||
|
|
@ -33,13 +35,23 @@ func Update(client container.Client, params types.UpdateParams) (types.Report, e
|
||||||
staleCheckFailed := 0
|
staleCheckFailed := 0
|
||||||
|
|
||||||
for i, targetContainer := range containers {
|
for i, targetContainer := range containers {
|
||||||
|
// stale will be true if there is a more recent image than the current container is using
|
||||||
stale, newestImage, err := client.IsContainerStale(targetContainer, params)
|
stale, newestImage, err := client.IsContainerStale(targetContainer, params)
|
||||||
shouldUpdate := stale && !params.NoRestart && !targetContainer.IsMonitorOnly(params)
|
shouldUpdate := stale && !params.NoRestart && !targetContainer.IsMonitorOnly(params)
|
||||||
|
imageUpdateDelayResolved := true
|
||||||
|
imageAgeDays := 0
|
||||||
if err == nil && shouldUpdate {
|
if err == nil && shouldUpdate {
|
||||||
// Check to make sure we have all the necessary information for recreating the container
|
// Check to make sure we have all the necessary information for recreating the container, including ImageInfo
|
||||||
err = targetContainer.VerifyConfiguration()
|
err = targetContainer.VerifyConfiguration()
|
||||||
// If the image information is incomplete and trace logging is enabled, log it for further diagnosis
|
if err == nil {
|
||||||
if err != nil && log.IsLevelEnabled(log.TraceLevel) {
|
if params.DelayDays > 0 {
|
||||||
|
imageAgeDays, err := getImageAgeDays(targetContainer.ImageInfo().Created)
|
||||||
|
if err == nil {
|
||||||
|
imageUpdateDelayResolved = imageAgeDays >= params.DelayDays
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if log.IsLevelEnabled(log.TraceLevel) {
|
||||||
|
// If the image information is incomplete and trace logging is enabled, log it for further diagnosis
|
||||||
imageInfo := targetContainer.ImageInfo()
|
imageInfo := targetContainer.ImageInfo()
|
||||||
log.Tracef("Image info: %#v", imageInfo)
|
log.Tracef("Image info: %#v", imageInfo)
|
||||||
log.Tracef("Container info: %#v", targetContainer.ContainerInfo())
|
log.Tracef("Container info: %#v", targetContainer.ContainerInfo())
|
||||||
|
|
@ -54,6 +66,11 @@ func Update(client container.Client, params types.UpdateParams) (types.Report, e
|
||||||
stale = false
|
stale = false
|
||||||
staleCheckFailed++
|
staleCheckFailed++
|
||||||
progress.AddSkipped(targetContainer, err)
|
progress.AddSkipped(targetContainer, err)
|
||||||
|
} else if !imageUpdateDelayResolved {
|
||||||
|
log.Infof("New image found for %s that was created %d day(s) ago but update delayed until %d day(s) after creation", targetContainer.Name(), imageAgeDays, params.DelayDays)
|
||||||
|
// technically the container is stale but we set it to false here because it is this stale flag that tells downstream methods whether to perform the update
|
||||||
|
stale = false
|
||||||
|
progress.AddScanned(targetContainer, newestImage)
|
||||||
} else {
|
} else {
|
||||||
progress.AddScanned(targetContainer, newestImage)
|
progress.AddScanned(targetContainer, newestImage)
|
||||||
}
|
}
|
||||||
|
|
@ -71,6 +88,8 @@ func Update(client container.Client, params types.UpdateParams) (types.Report, e
|
||||||
|
|
||||||
UpdateImplicitRestart(containers)
|
UpdateImplicitRestart(containers)
|
||||||
|
|
||||||
|
// containersToUpdate will contain all containers, not just those that need to be updated. The "stale" flag is checked via container.ToRestart()
|
||||||
|
// within stopContainersInReversedOrder and restartContainersInSortedOrder to skip over containers with stale set to false (unless LinkedToRestarting set)
|
||||||
var containersToUpdate []types.Container
|
var containersToUpdate []types.Container
|
||||||
for _, c := range containers {
|
for _, c := range containers {
|
||||||
if !c.IsMonitorOnly(params) {
|
if !c.IsMonitorOnly(params) {
|
||||||
|
|
@ -265,3 +284,27 @@ func linkedContainerMarkedForRestart(links []string, containers []types.Containe
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Finds the difference between now and a given date, in full days. Input date is expected to originate
|
||||||
|
// from an image's Created attribute, but since these are not always in ISO 8601 with the same number of
|
||||||
|
// digits for milliseconds, the function also accounts for variations.
|
||||||
|
func getImageAgeDays(imageCreatedDateTime string) (int, error) {
|
||||||
|
|
||||||
|
// Date strings sometimes vary in how many digits after the decimal point are present. If present, drop millisecond portion to standardize.
|
||||||
|
dotIndex := strings.Index(imageCreatedDateTime, ".")
|
||||||
|
if dotIndex != -1 {
|
||||||
|
imageCreatedDateTime = imageCreatedDateTime[:dotIndex] + "Z"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define the layout string for the date format without milliseconds
|
||||||
|
layout := "2006-01-02T15:04:05Z"
|
||||||
|
imageCreatedDate, error := time.Parse(layout, imageCreatedDateTime)
|
||||||
|
|
||||||
|
if error != nil {
|
||||||
|
log.Errorf("Error parsing imageCreatedDateTime date (%s). Error: %s", imageCreatedDateTime, error)
|
||||||
|
return -1, error
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(time.Since(imageCreatedDate).Hours() / 24), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -328,21 +328,6 @@ func (client dockerClient) IsContainerStale(container t.Container, params t.Upda
|
||||||
return client.HasNewImage(ctx, container, params)
|
return client.HasNewImage(ctx, container, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Date strings sometimes vary in how many digits after the decimal point are present. This function
|
|
||||||
// standardizes them by removing the milliseconds.
|
|
||||||
func truncateMilliseconds(dateString string) string {
|
|
||||||
// Find the position of the dot (.) in the date string
|
|
||||||
dotIndex := strings.Index(dateString, ".")
|
|
||||||
|
|
||||||
// If the dot is found, truncate the string before the dot
|
|
||||||
if dotIndex != -1 {
|
|
||||||
return dateString[:dotIndex] + "Z"
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the dot is not found, return the original string
|
|
||||||
return dateString
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client dockerClient) HasNewImage(ctx context.Context, container t.Container, params t.UpdateParams) (hasNew bool, latestImage t.ImageID, err error) {
|
func (client dockerClient) HasNewImage(ctx context.Context, container t.Container, params t.UpdateParams) (hasNew bool, latestImage t.ImageID, err error) {
|
||||||
currentImageID := t.ImageID(container.ContainerInfo().ContainerJSONBase.Image)
|
currentImageID := t.ImageID(container.ContainerInfo().ContainerJSONBase.Image)
|
||||||
imageName := container.ImageName()
|
imageName := container.ImageName()
|
||||||
|
|
@ -358,26 +343,6 @@ func (client dockerClient) HasNewImage(ctx context.Context, container t.Containe
|
||||||
return false, currentImageID, nil
|
return false, currentImageID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disabled by default
|
|
||||||
if params.DelayDays > 0 {
|
|
||||||
// Define the layout string for the date format without milliseconds
|
|
||||||
layout := "2006-01-02T15:04:05Z"
|
|
||||||
newImageDate, error := time.Parse(layout, truncateMilliseconds(newImageInfo.Created))
|
|
||||||
|
|
||||||
if error != nil {
|
|
||||||
log.Errorf("Error parsing Created date (%s) for container %s latest label. Error: %s", newImageInfo.Created, container.Name(), error)
|
|
||||||
return false, currentImageID, nil
|
|
||||||
} else {
|
|
||||||
requiredDays := params.DelayDays
|
|
||||||
diffDays := int(time.Since(newImageDate).Hours() / 24)
|
|
||||||
|
|
||||||
if diffDays < requiredDays {
|
|
||||||
log.Infof("New image found for %s that was created %d day(s) ago but update delayed until %d day(s) after creation", container.Name(), diffDays, requiredDays)
|
|
||||||
return false, currentImageID, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("Found new %s image (%s)", imageName, newImageID.ShortID())
|
log.Infof("Found new %s image (%s)", imageName, newImageID.ShortID())
|
||||||
return true, newImageID, nil
|
return true, newImageID, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue