Skip to content

Commit

Permalink
Merge branch 'argoproj-labs:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
sribiere-jellysmack committed Sep 11, 2024
2 parents 316780d + 60b89f2 commit ac2ac95
Show file tree
Hide file tree
Showing 24 changed files with 496 additions and 45 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.22 AS builder
FROM golang:1.23 AS builder

RUN mkdir -p /src/argocd-image-updater
WORKDIR /src/argocd-image-updater
Expand Down
4 changes: 2 additions & 2 deletions docs/configuration/images.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ The correct image to execute will be chosen by Kubernetes.

There are generally two ways on how to specify pull secrets for Argo CD Image
Updater to use. Either you configure a secret reference globally for the
container registry (as described [here](../registries/)), or you can specify
container registry (as described [here](registries.md)), or you can specify
the pull secret to use for a given image using the annotation

```yaml
Expand Down Expand Up @@ -456,7 +456,7 @@ must be prefixed with `argocd-image-updater.argoproj.io/`.
|`<image_alias>.allow-tags`|*any*|A function to match tag names from the registry against to be considered for update|
|`<image_alias>.ignore-tags`|*none*|A comma-separated list of glob patterns that when matched, ignore a certain tag from the registry|
|`<image_alias>.pull-secret`|*none*|A reference to a secret to be used as registry credentials for this image|
|`<image_alias>.platform`|*none*|Only update to images for given platform(s). Comma-separated list, e.g. `linux/amd64,linux/arm64`|
|`<image_alias>.platforms`|*none*|Only update to images for given platform(s). Comma-separated list, e.g. `linux/amd64,linux/arm64`|
|`<image_alias>.helm.image-spec`|*none*|Name of the Helm parameter to specify the canonical name of the image, i.e. holds `image/name:1.0`. If this is set, other Helm parameter-related options will be ignored.|
|`<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`|
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/argoproj/argo-cd/v2 v2.11.7
github.com/argoproj/gitops-engine v0.7.1-0.20240715141605-18ba62e1f1fb
github.com/argoproj/pkg v0.13.7-0.20230627120311-a4dd357b057e
github.com/bmatcuk/doublestar/v4 v4.6.0
github.com/bmatcuk/doublestar/v4 v4.6.1
github.com/bradleyfalzon/ghinstallation/v2 v2.6.0
github.com/distribution/distribution/v3 v3.0.0-20230722181636-7b502560cad4
github.com/go-git/go-git/v5 v5.11.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/bmatcuk/doublestar/v4 v4.6.0 h1:HTuxyug8GyFbRkrffIpzNCSK4luc0TY3wzXvzIZhEXc=
github.com/bmatcuk/doublestar/v4 v4.6.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bombsimon/logrusr/v2 v2.0.1 h1:1VgxVNQMCvjirZIYaT9JYn6sAVGVEcNtRE0y4mvaOAM=
github.com/bombsimon/logrusr/v2 v2.0.1/go.mod h1:ByVAX+vHdLGAfdroiMg6q0zgq2FODY2lc5YJvzmOJio=
github.com/bradleyfalzon/ghinstallation/v2 v2.6.0 h1:IRY7Xy588KylkoycsUhFpW7cdGpy5Y5BPsz4IfuJtGk=
Expand Down
15 changes: 15 additions & 0 deletions manifests/base/rbac/argocd-image-updater-clusterrole.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: argocd-image-updater
app.kubernetes.io/part-of: argocd-image-updater
app.kubernetes.io/component: controller
name: argocd-image-updater
rules:
- apiGroups:
- ""
resources:
- events
verbs:
- create
15 changes: 15 additions & 0 deletions manifests/base/rbac/argocd-image-updater-clusterrolebinding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
app.kubernetes.io/name: argocd-image-updater
app.kubernetes.io/part-of: argocd-image-updater
app.kubernetes.io/component: controller
name: argocd-image-updater
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: argocd-image-updater
subjects:
- kind: ServiceAccount
name: argocd-image-updater
7 changes: 0 additions & 7 deletions manifests/base/rbac/argocd-image-updater-role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,3 @@ rules:
- list
- update
- patch
- apiGroups:
- ""
resources:
- events
verbs:
- create

2 changes: 2 additions & 0 deletions manifests/base/rbac/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- argocd-image-updater-clusterrole.yaml
- argocd-image-updater-clusterrolebinding.yaml
- argocd-image-updater-role.yaml
- argocd-image-updater-rolebinding.yaml
- argocd-image-updater-sa.yaml
26 changes: 26 additions & 0 deletions manifests/install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ rules:
- list
- update
- patch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/name: argocd-image-updater
app.kubernetes.io/part-of: argocd-image-updater
name: argocd-image-updater
rules:
- apiGroups:
- ""
resources:
Expand All @@ -57,6 +67,22 @@ subjects:
- kind: ServiceAccount
name: argocd-image-updater
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/name: argocd-image-updater
app.kubernetes.io/part-of: argocd-image-updater
name: argocd-image-updater
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: argocd-image-updater
subjects:
- kind: ServiceAccount
name: argocd-image-updater
---
apiVersion: v1
kind: ConfigMap
metadata:
Expand Down
2 changes: 1 addition & 1 deletion pkg/argocd/gitcreds.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func getCredsFromSecret(wbc *WriteBackConfig, credentialsSecret string, kubeClie
if err != nil {
return nil, fmt.Errorf("invalid value in field githubAppID: %w", err)
}
intGithubAppInstallationID, _ := strconv.ParseInt(string(githubAppInstallationID), 10, 64)
intGithubAppInstallationID, err := strconv.ParseInt(string(githubAppInstallationID), 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid value in field githubAppInstallationID: %w", err)
}
Expand Down
26 changes: 16 additions & 10 deletions pkg/argocd/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -451,28 +451,34 @@ func marshalParamsOverride(app *v1alpha1.Application, originalData []byte) ([]by
if helmAnnotationParamName == "" {
return nil, fmt.Errorf("could not find an image-name annotation for image %s", c.ImageName)
}
// for image-spec annotation, helmAnnotationParamName holds image-spec annotation value,
// and helmAnnotationParamVersion is empty
if helmAnnotationParamVersion == "" {
return nil, fmt.Errorf("could not find an image-tag annotation for image %s", c.ImageName)
if c.GetParameterHelmImageSpec(app.Annotations) == "" {
// not a full image-spec, so image-tag is required
return nil, fmt.Errorf("could not find an image-tag annotation for image %s", c.ImageName)
}
} else {
// image-tag annotation is present, so continue to process image-tag
helmParamVersion := getHelmParam(appSource.Helm.Parameters, helmAnnotationParamVersion)
if helmParamVersion == nil {
return nil, fmt.Errorf("%s parameter not found", helmAnnotationParamVersion)
}
err = setHelmValue(&helmNewValues, helmAnnotationParamVersion, helmParamVersion.Value)
if err != nil {
return nil, fmt.Errorf("failed to set image parameter version value: %v", err)
}
}

helmParamName := getHelmParam(appSource.Helm.Parameters, helmAnnotationParamName)
if helmParamName == nil {
return nil, fmt.Errorf("%s parameter not found", helmAnnotationParamName)
}

helmParamVersion := getHelmParam(appSource.Helm.Parameters, helmAnnotationParamVersion)
if helmParamVersion == nil {
return nil, fmt.Errorf("%s parameter not found", helmAnnotationParamVersion)
}

err = setHelmValue(&helmNewValues, helmAnnotationParamName, helmParamName.Value)
if err != nil {
return nil, fmt.Errorf("failed to set image parameter name value: %v", err)
}
err = setHelmValue(&helmNewValues, helmAnnotationParamVersion, helmParamVersion.Value)
if err != nil {
return nil, fmt.Errorf("failed to set image parameter version value: %v", err)
}
}

override, err = yaml.Marshal(helmNewValues)
Expand Down
152 changes: 151 additions & 1 deletion pkg/argocd/update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,75 @@ func Test_UpdateApplication(t *testing.T) {
assert.Equal(t, 2, res.NumImagesUpdated)
})

t.Run("Update app w/ GitHub App creds", func(t *testing.T) {
mockClientFn := func(endpoint *registry.RegistryEndpoint, username, password string) (registry.RegistryClient, error) {
regMock := regmock.RegistryClient{}
regMock.On("NewRepository", mock.Anything).Return(nil)
regMock.On("Tags", mock.Anything).Return([]string{"1.0.2", "1.0.3"}, nil)
return &regMock, nil
}

argoClient := argomock.ArgoCD{}
argoClient.On("UpdateSpec", mock.Anything, mock.Anything).Return(nil, nil)

secret := fixture.NewSecret("argocd-image-updater", "git-creds", map[string][]byte{
"githubAppID": []byte("12345678"),
"githubAppInstallationID": []byte("87654321"),
"githubAppPrivateKey": []byte("foo"),
})
kubeClient := kube.KubernetesClient{
Clientset: fake.NewFakeClientsetWithResources(secret),
}

annotations := map[string]string{
common.ImageUpdaterAnnotation: "foo=gcr.io/jannfis/foobar:>=1.0.1",
common.WriteBackMethodAnnotation: "git:secret:argocd-image-updater/git-creds",
}
appImages := &ApplicationImages{
Application: v1alpha1.Application{
ObjectMeta: v1.ObjectMeta{
Name: "guestbook",
Namespace: "guestbook",
Annotations: annotations,
},
Spec: v1alpha1.ApplicationSpec{
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://example.com/example",
TargetRevision: "main",
Kustomize: &v1alpha1.ApplicationSourceKustomize{
Images: v1alpha1.KustomizeImages{
"jannfis/foobar:1.0.1",
},
},
},
},
Status: v1alpha1.ApplicationStatus{
SourceType: v1alpha1.ApplicationSourceTypeKustomize,
Summary: v1alpha1.ApplicationSummary{
Images: []string{
"gcr.io/jannfis/foobar:1.0.1",
},
},
},
},
Images: *parseImageList(annotations),
}
res := UpdateApplication(&UpdateConfiguration{
NewRegFN: mockClientFn,
ArgoClient: &argoClient,
KubeClient: &kubeClient,
UpdateApp: appImages,
DryRun: false,
}, NewSyncIterationState())
assert.Equal(t, v1alpha1.KustomizeImage("gcr.io/jannfis/foobar:1.0.3"), appImages.Application.Spec.Source.Kustomize.Images[0])
assert.Equal(t, 0, res.NumSkipped)
assert.Equal(t, 1, res.NumApplicationsProcessed)
assert.Equal(t, 1, res.NumImagesConsidered)
// configured githubApp creds will take effect and git client will catch the invalid GithubAppPrivateKey "foo":
// "Could not update application spec: could not parse private key: invalid key: Key must be a PEM encoded PKCS1 or PKCS8 key"
assert.Equal(t, 1, res.NumErrors)
})

t.Run("Test successful update", func(t *testing.T) {
mockClientFn := func(endpoint *registry.RegistryEndpoint, username, password string) (registry.RegistryClient, error) {
regMock := regmock.RegistryClient{}
Expand Down Expand Up @@ -1383,6 +1452,56 @@ replicas: 1
assert.Equal(t, strings.TrimSpace(strings.ReplaceAll(expected, "\t", " ")), strings.TrimSpace(string(yaml)))
})

t.Run("Valid Helm source with Helm values file and image-spec", func(t *testing.T) {
expected := `
image.spec.foo: nginx:v1.0.0
replicas: 1
`
app := v1alpha1.Application{
ObjectMeta: v1.ObjectMeta{
Name: "testapp",
Annotations: map[string]string{
"argocd-image-updater.argoproj.io/image-list": "nginx",
"argocd-image-updater.argoproj.io/write-back-method": "git",
"argocd-image-updater.argoproj.io/write-back-target": "helmvalues:./test-values.yaml",
"argocd-image-updater.argoproj.io/nginx.helm.image-spec": "image.spec.foo",
},
},
Spec: v1alpha1.ApplicationSpec{
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://example.com/example",
TargetRevision: "main",
Helm: &v1alpha1.ApplicationSourceHelm{
Parameters: []v1alpha1.HelmParameter{
{
Name: "image.spec.foo",
Value: "nginx:v1.0.0",
ForceString: true,
},
},
},
},
},
Status: v1alpha1.ApplicationStatus{
SourceType: v1alpha1.ApplicationSourceTypeHelm,
Summary: v1alpha1.ApplicationSummary{
Images: []string{
"nginx:v0.0.0",
},
},
},
}

originalData := []byte(`
image.spec.foo: nginx:v0.0.0
replicas: 1
`)
yaml, err := marshalParamsOverride(&app, originalData)
require.NoError(t, err)
assert.NotEmpty(t, yaml)
assert.Equal(t, strings.TrimSpace(strings.ReplaceAll(expected, "\t", " ")), strings.TrimSpace(string(yaml)))
})

t.Run("Valid Helm source with Helm values file with multiple images", func(t *testing.T) {
expected := `
nginx.image.name: nginx
Expand Down Expand Up @@ -1834,7 +1953,6 @@ replicas: 1
originalData := []byte(`random: yaml`)
_, err := marshalParamsOverride(&app, originalData)
assert.Error(t, err)
assert.Equal(t, "wrongimage.name parameter not found", err.Error())
})

t.Run("Image-tag annotation value not found in Helm source parameters list", func(t *testing.T) {
Expand Down Expand Up @@ -2574,6 +2692,38 @@ func Test_GetGitCreds(t *testing.T) {
// Must have HTTPS GitHub App creds
_, ok := creds.(git.GitHubAppCreds)
require.True(t, ok)

// invalid secrete data in GitHub App creds
invalidSecretEntries := []map[string][]byte{
{ // missing githubAppPrivateKey
"githubAppID": []byte("12345678"),
"githubAppInstallationID": []byte("87654321"),
}, { // missing githubAppInstallationID
"githubAppID": []byte("12345678"),
"githubAppPrivateKey": []byte("foo"),
}, { // missing githubAppID
"githubAppInstallationID": []byte("87654321"),
"githubAppPrivateKey": []byte("foo"),
}, { // ID should be a number
"githubAppID": []byte("NaN"),
"githubAppInstallationID": []byte("87654321"),
"githubAppPrivateKey": []byte("foo"),
}, {
"githubAppID": []byte("12345678"),
"githubAppInstallationID": []byte("NaN"),
"githubAppPrivateKey": []byte("foo"),
},
}
for _, secretEntry := range invalidSecretEntries {
secret = fixture.NewSecret("argocd-image-updater", "git-creds", secretEntry)
kubeClient = kube.KubernetesClient{
Clientset: fake.NewFakeClientsetWithResources(secret),
}
wbc, err = getWriteBackConfig(&app, &kubeClient, &argoClient)
require.NoError(t, err)
_, err = wbc.GetCreds(&app)
require.Error(t, err)
}
})

t.Run("SSH creds from a secret", func(t *testing.T) {
Expand Down
Loading

0 comments on commit ac2ac95

Please sign in to comment.