Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support app-wide update-strategy annotations #338

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/configuration/images.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,3 +402,21 @@ must be prefixed with `argocd-image-updater.argoproj.io`.
|`<image_alias>.helm.image-name`|`image.name`|Name of the Helm parameter used for specifying the image name, i.e. holds `image/name`|
|`<image_alias>.helm.image-tag`|`image.tag`|Name of the Helm parameter used for specifying the image tag, i.e. holds `1.0`|
|`<image_alias>.kustomize.image-name`|*original name of image*|Name of Kustomize image parameter to set during updates|

### Application-wide defaults

If you want to update multiple images in an Application, that all share common
settings (such as, update strategy, allowed tags, etc), you can define common
options. These options are valid for all images, unless an image overrides it
with specific configuration.

The following annotations are available. Please note, all annotations must be
prefixed with `argocd-image-updater.argoproj.io/`.

|Annotation name|Description|
|---------------|-----------|
|`update-strategy`|The update strategy to be used for all images|
|`force-update`|If set to "true" (with quotes), even images that are not currently deployed will be updated|
|`allow-tags`|A function to match tag names from registry against to be considered for update|
|`ignore-tags`|A comma-separated list of glob patterns that when match ignore a certain tag from the registry|
|`pull-secret`|A reference to a secret to be used as registry credentials for this image|
2 changes: 1 addition & 1 deletion pkg/argocd/update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ func Test_UpdateApplication(t *testing.T) {
Name: "guestbook",
Namespace: "guestbook",
Annotations: map[string]string{
fmt.Sprintf(common.SecretListAnnotation, "dummy"): "secret:foo/bar#creds",
fmt.Sprintf(common.PullSecretAnnotation, "dummy"): "secret:foo/bar#creds",
},
},
Spec: v1alpha1.ApplicationSpec{
Expand Down
11 changes: 8 additions & 3 deletions pkg/common/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,24 @@ const (
KustomizeApplicationNameAnnotation = ImageUpdaterAnnotationPrefix + "/%s.kustomize.image-name"
)

// Upgrade strategy related annotations
// Image specific configuration annotations
const (
OldMatchOptionAnnotation = ImageUpdaterAnnotationPrefix + "/%s.tag-match" // Deprecated and will be removed
AllowTagsOptionAnnotation = ImageUpdaterAnnotationPrefix + "/%s.allow-tags"
IgnoreTagsOptionAnnotation = ImageUpdaterAnnotationPrefix + "/%s.ignore-tags"
ForceUpdateOptionAnnotation = ImageUpdaterAnnotationPrefix + "/%s.force-update"
UpdateStrategyAnnotation = ImageUpdaterAnnotationPrefix + "/%s.update-strategy"
PullSecretAnnotation = ImageUpdaterAnnotationPrefix + "/%s.pull-secret"
PlatformsAnnotation = ImageUpdaterAnnotationPrefix + "/%s.platforms"
)

// Image pull secret related annotations
// Application-wide update strategy related annotations
const (
SecretListAnnotation = ImageUpdaterAnnotationPrefix + "/%s.pull-secret"
ApplicationWideAllowTagsOptionAnnotation = ImageUpdaterAnnotationPrefix + "/allow-tags"
ApplicationWideIgnoreTagsOptionAnnotation = ImageUpdaterAnnotationPrefix + "/ignore-tags"
ApplicationWideForceUpdateOptionAnnotation = ImageUpdaterAnnotationPrefix + "/force-update"
ApplicationWideUpdateStrategyAnnotation = ImageUpdaterAnnotationPrefix + "/update-strategy"
ApplicationWidePullSecretAnnotation = ImageUpdaterAnnotationPrefix + "/pull-secret"
)

// Application update configuration related annotations
Expand Down
109 changes: 77 additions & 32 deletions pkg/image/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,24 +57,42 @@ func (img *ContainerImage) GetParameterKustomizeImageName(annotations map[string
// HasForceUpdateOptionAnnotation gets the value for force-update option for the
// image from a set of annotations
func (img *ContainerImage) HasForceUpdateOptionAnnotation(annotations map[string]string) bool {
key := fmt.Sprintf(common.ForceUpdateOptionAnnotation, img.normalizedSymbolicName())
val, ok := annotations[key]
return ok && val == "true"
forceUpdateAnnotations := []string{
fmt.Sprintf(common.ForceUpdateOptionAnnotation, img.normalizedSymbolicName()),
common.ApplicationWideForceUpdateOptionAnnotation,
}
var forceUpdateVal = ""
for _, key := range forceUpdateAnnotations {
if val, ok := annotations[key]; ok {
forceUpdateVal = val
break
}
}
return forceUpdateVal == "true"
}

// GetParameterSort gets and validates the value for the sort option for the
// image from a set of annotations
func (img *ContainerImage) GetParameterUpdateStrategy(annotations map[string]string) UpdateStrategy {
updateStrategyAnnotations := []string{
fmt.Sprintf(common.UpdateStrategyAnnotation, img.normalizedSymbolicName()),
common.ApplicationWideUpdateStrategyAnnotation,
}
var updateStrategyVal = ""
for _, key := range updateStrategyAnnotations {
if val, ok := annotations[key]; ok {
updateStrategyVal = val
break
}
}
logCtx := img.LogContext()
key := fmt.Sprintf(common.UpdateStrategyAnnotation, img.normalizedSymbolicName())
val, ok := annotations[key]
if !ok {
if updateStrategyVal == "" {
logCtx.Tracef("No sort option found")
// Default is sort by version
logCtx.Tracef("No sort option %s found", key)
return StrategySemVer
}
logCtx.Tracef("found update strategy %s in %s", val, key)
return img.ParseUpdateStrategy(val)
logCtx.Tracef("Found update strategy %s", updateStrategyVal)
return img.ParseUpdateStrategy(updateStrategyVal)
}

func (img *ContainerImage) ParseUpdateStrategy(val string) UpdateStrategy {
Expand All @@ -92,30 +110,39 @@ func (img *ContainerImage) ParseUpdateStrategy(val string) UpdateStrategy {
logCtx.Warnf("Unknown sort option %s -- using semver", val)
return StrategySemVer
}

}

// GetParameterMatch returns the match function and pattern to use for matching
// tag names. If an invalid option is found, it returns MatchFuncNone as the
// default, to prevent accidental matches.
func (img *ContainerImage) GetParameterMatch(annotations map[string]string) (MatchFuncFn, interface{}) {
allowTagsAnnotations := []string{
fmt.Sprintf(common.AllowTagsOptionAnnotation, img.normalizedSymbolicName()),
common.ApplicationWideAllowTagsOptionAnnotation,
}
var allowTagsVal = ""
for _, key := range allowTagsAnnotations {
if val, ok := annotations[key]; ok {
allowTagsVal = val
break
}
}
logCtx := img.LogContext()
key := fmt.Sprintf(common.AllowTagsOptionAnnotation, img.normalizedSymbolicName())
val, ok := annotations[key]
if !ok {
if allowTagsVal == "" {
// The old match-tag annotation is deprecated and will be subject to removal
// in a future version.
key = fmt.Sprintf(common.OldMatchOptionAnnotation, img.normalizedSymbolicName())
val, ok = annotations[key]
if !ok {
logCtx.Tracef("No match annotation %s found", key)
return MatchFuncAny, ""
} else {
key := fmt.Sprintf(common.OldMatchOptionAnnotation, img.normalizedSymbolicName())
val, ok := annotations[key]
if ok {
logCtx.Warnf("The 'tag-match' annotation is deprecated and subject to removal. Please use 'allow-tags' annotation instead.")
allowTagsVal = val
}
}

return img.ParseMatchfunc(val)
if allowTagsVal == "" {
logCtx.Tracef("No match annotation found")
return MatchFuncAny, ""
}
return img.ParseMatchfunc(allowTagsVal)
}

// ParseMatchfunc returns a matcher function and its argument from given value
Expand Down Expand Up @@ -148,32 +175,50 @@ func (img *ContainerImage) ParseMatchfunc(val string) (MatchFuncFn, interface{})

// GetParameterPullSecret retrieves an image's pull secret credentials
func (img *ContainerImage) GetParameterPullSecret(annotations map[string]string) *CredentialSource {
pullSecretAnnotations := []string{
fmt.Sprintf(common.PullSecretAnnotation, img.normalizedSymbolicName()),
common.ApplicationWidePullSecretAnnotation,
}
var pullSecretVal = ""
for _, key := range pullSecretAnnotations {
if val, ok := annotations[key]; ok {
pullSecretVal = val
break
}
}
logCtx := img.LogContext()
key := fmt.Sprintf(common.SecretListAnnotation, img.normalizedSymbolicName())
val, ok := annotations[key]
if !ok {
logCtx.Tracef("No secret annotation %s found", key)
if pullSecretVal == "" {
logCtx.Tracef("No pull-secret annotation found")
return nil
}
credSrc, err := ParseCredentialSource(val, false)
credSrc, err := ParseCredentialSource(pullSecretVal, false)
if err != nil {
logCtx.Warnf("Invalid credential reference specified: %s", val)
logCtx.Warnf("Invalid credential reference specified: %s", pullSecretVal)
return nil
}
return credSrc
}

// GetParameterIgnoreTags retrieves a list of tags to ignore from a comma-separated string
func (img *ContainerImage) GetParameterIgnoreTags(annotations map[string]string) []string {
ignoreTagsAnnotations := []string{
fmt.Sprintf(common.IgnoreTagsOptionAnnotation, img.normalizedSymbolicName()),
common.ApplicationWideIgnoreTagsOptionAnnotation,
}
var ignoreTagsVal = ""
for _, key := range ignoreTagsAnnotations {
if val, ok := annotations[key]; ok {
ignoreTagsVal = val
break
}
}
logCtx := img.LogContext()
key := fmt.Sprintf(common.IgnoreTagsOptionAnnotation, img.normalizedSymbolicName())
val, ok := annotations[key]
if !ok {
logCtx.Tracef("No ignore-tags annotation %s found", key)
if ignoreTagsVal == "" {
logCtx.Tracef("No ignore-tags annotation found")
return nil
}
ignoreList := make([]string, 0)
tags := strings.Split(strings.TrimSpace(val), ",")
tags := strings.Split(strings.TrimSpace(ignoreTagsVal), ",")
for _, tag := range tags {
// We ignore empty tags
trimmed := strings.TrimSpace(tag)
Expand Down
Loading