summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornoah <noah@hackedu.io>2021-05-11 20:17:38 +1200
committerGitHub <noreply@github.com>2021-05-11 10:17:38 +0200
commitadd387ecfa5c59b34119eabd8b81c1daddcdc06e (patch)
treebaa5d5c63641505ac16aa849316b85e1bf741190
parentb47f7e961649acf4c9e7c98d575284a143707ce8 (diff)
feat: allow write-back to actual kustomization files (#200)
* feat: allow write-back to actual kustomization files #199 * fix: was not handling default path correctly default is the source path * fix: sort imports
-rw-r--r--.github/actions/spelling/allow.txt2
-rw-r--r--Dockerfile3
-rw-r--r--docs/configuration/applications.md29
-rw-r--r--go.mod12
-rw-r--r--go.sum28
-rw-r--r--pkg/argocd/git.go171
-rw-r--r--pkg/argocd/git_test.go49
-rw-r--r--pkg/argocd/update.go45
-rw-r--r--pkg/argocd/update_test.go167
-rw-r--r--pkg/common/constants.go2
-rw-r--r--pkg/image/image_test.go9
11 files changed, 435 insertions, 82 deletions
diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt
index 72fa87a..787ae03 100644
--- a/.github/actions/spelling/allow.txt
+++ b/.github/actions/spelling/allow.txt
@@ -107,6 +107,7 @@ kubectl
kubefake
kubernetes
Kustomization
+kustomization
kustomize
ldflags
LDFLAGS
@@ -232,6 +233,7 @@ workflow
workflows
www
yaml
+yml
yourimage
yourorg
yourtoken
diff --git a/Dockerfile b/Dockerfile
index 3a01127..e3a51fe 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,6 +2,9 @@ FROM golang:1.14.13 AS builder
RUN mkdir -p /src/argocd-image-updater
WORKDIR /src/argocd-image-updater
+# cache dependencies as a layer for faster rebuilds
+COPY go.mod go.sum ./
+RUN go mod download
COPY . .
RUN mkdir -p dist && \
diff --git a/docs/configuration/applications.md b/docs/configuration/applications.md
index a53cfb8..00cd442 100644
--- a/docs/configuration/applications.md
+++ b/docs/configuration/applications.md
@@ -257,3 +257,32 @@ In order to test a template before configuring it for use in Image Updater,
you can store the template you want to use in a temporary file, and then use
the `argocd-image-updater template /path/to/file` command to render the
template using pre-defined data and see its outcome on the terminal.
+
+#### Git Write-Back Target
+
+By default, git write-back will create or update `.argocd-source-<appName>.yaml`.
+
+If you are using Kustomize and want the image updates available for normal use with `kustomize`,
+you may set the `write-back-target` to `kustomization`. This method commits changes to the Kustomization
+file back to git as though you ran `kustomize edit set image`.
+
+```yaml
+argocd-image-updater.argoproj.io/write-back-method: git # all git options are supported
+argocd-image-updater.argoproj.io/write-back-target: kustomization
+```
+
+You may also specify which kustomization to update with either a path relative to the project source path...
+
+```yaml
+argocd-image-updater.argoproj.io/write-back-target: "kustomization:../../base"
+# if the Application spec.source.path = config/overlays/foo, this would update the kustomization in config/base
+```
+
+...or absolute with respect to the repository:
+
+```yaml
+# absolute paths start with /
+argocd-image-updater.argoproj.io/write-back-target: "kustomization:/config/overlays/bar"
+```
+
+Note that the Kustomization directory needs to be specified, not a file, like when using Kustomize.
diff --git a/go.mod b/go.mod
index a1760c7..36bb962 100644
--- a/go.mod
+++ b/go.mod
@@ -9,6 +9,8 @@ require (
github.com/argoproj/pkg v0.0.0-20200624215116-23e74cb168fe
github.com/docker/distribution v2.7.1+incompatible
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
+ github.com/go-openapi/spec v0.19.5 // indirect
+ github.com/google/go-cmp v0.4.0 // indirect
github.com/gorilla/mux v1.7.4 // indirect
github.com/nokia/docker-registry-client v0.0.0-20201015093031-af1a6d3b4fb1
github.com/patrickmn/go-cache v2.1.0+incompatible
@@ -17,14 +19,18 @@ require (
github.com/spf13/cobra v1.0.0
github.com/stretchr/testify v1.6.1
go.uber.org/ratelimit v0.1.1-0.20201110185707-e86515f0dda9
- golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975
- golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208
+ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
+ golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect
+ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
+ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 // indirect
+ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/src-d/go-git.v4 v4.13.1
- gopkg.in/yaml.v2 v2.3.0
+ gopkg.in/yaml.v2 v2.4.0
k8s.io/api v1.18.8
k8s.io/apimachinery v1.18.8
k8s.io/client-go v11.0.1-0.20190816222228-6d55c1b1f1ca+incompatible
k8s.io/kubectl v1.18.8 // indirect
+ sigs.k8s.io/kustomize v2.0.3+incompatible
)
replace (
diff --git a/go.sum b/go.sum
index eaeabcd..bc7d46f 100644
--- a/go.sum
+++ b/go.sum
@@ -219,8 +219,9 @@ github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nA
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
-github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc=
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw=
+github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
@@ -303,8 +304,9 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
github.com/google/cadvisor v0.35.0/go.mod h1:1nql6U13uTHaLYB8rLS5x9IJc2qT6Xd/Tr1sTX6NE48=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-jsonnet v0.16.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
@@ -658,8 +660,9 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -698,8 +701,9 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -712,8 +716,9 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -740,14 +745,17 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.0.0-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
@@ -782,6 +790,9 @@ golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDq
golang.org/x/tools v0.0.0-20190909030654-5b82db07426d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
gonum.org/v1/gonum v0.6.2/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU=
@@ -837,8 +848,9 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
diff --git a/pkg/argocd/git.go b/pkg/argocd/git.go
index dd80b00..1a70978 100644
--- a/pkg/argocd/git.go
+++ b/pkg/argocd/git.go
@@ -6,8 +6,15 @@ import (
"io/ioutil"
"os"
"path"
+ "path/filepath"
"text/template"
+ "sigs.k8s.io/kustomize/pkg/commands/kustfile"
+ "sigs.k8s.io/kustomize/pkg/fs"
+ image2 "sigs.k8s.io/kustomize/pkg/image"
+
+ "github.com/argoproj-labs/argocd-image-updater/pkg/image"
+
"github.com/argoproj-labs/argocd-image-updater/ext/git"
"github.com/argoproj-labs/argocd-image-updater/pkg/log"
@@ -51,25 +58,27 @@ func TemplateCommitMessage(tpl *template.Template, appName string, changeList []
return cmBuf.String()
}
+type changeWriter func(app *v1alpha1.Application, wbc *WriteBackConfig, gitC git.Client) (err error, skip bool)
+
// commitChanges commits any changes required for updating one or more images
// after the UpdateApplication cycle has finished.
-func commitChangesGit(app *v1alpha1.Application, wbc *WriteBackConfig) error {
+func commitChangesGit(app *v1alpha1.Application, wbc *WriteBackConfig, write changeWriter) error {
creds, err := wbc.GetCreds(app)
if err != nil {
return fmt.Errorf("could not get creds for repo '%s': %v", app.Spec.Source.RepoURL, err)
}
- tempRoot, err := ioutil.TempDir(os.TempDir(), fmt.Sprintf("git-%s", app.Name))
- if err != nil {
- return err
- }
- defer func() {
- err := os.RemoveAll(tempRoot)
- if err != nil {
- log.Errorf("could not remove temp dir: %v", err)
- }
- }()
var gitC git.Client
if wbc.GitClient == nil {
+ tempRoot, err := ioutil.TempDir(os.TempDir(), fmt.Sprintf("git-%s", app.Name))
+ if err != nil {
+ return err
+ }
+ defer func() {
+ err := os.RemoveAll(tempRoot)
+ if err != nil {
+ log.Errorf("could not remove temp dir: %v", err)
+ }
+ }()
gitC, err = git.NewClientExt(app.Spec.Source.RepoURL, tempRoot, creds, false, false)
if err != nil {
return err
@@ -115,12 +124,49 @@ func commitChangesGit(app *v1alpha1.Application, wbc *WriteBackConfig) error {
if err != nil {
return err
}
+
+ if err, skip := write(app, wbc, gitC); err != nil {
+ return err
+ } else if skip {
+ return nil
+ }
+
+ commitOpts := &git.CommitOptions{}
+ if wbc.GitCommitMessage != "" {
+ cm, err := ioutil.TempFile("", "image-updater-commit-msg")
+ if err != nil {
+ return fmt.Errorf("cold not create temp file: %v", err)
+ }
+ log.Debugf("Writing commit message to %s", cm.Name())
+ err = ioutil.WriteFile(cm.Name(), []byte(wbc.GitCommitMessage), 0600)
+ if err != nil {
+ _ = cm.Close()
+ return fmt.Errorf("could not write commit message to %s: %v", cm.Name(), err)
+ }
+ commitOpts.CommitMessagePath = cm.Name()
+ _ = cm.Close()
+ defer os.Remove(cm.Name())
+ }
+
+ err = gitC.Commit("", commitOpts)
+ if err != nil {
+ return err
+ }
+ err = gitC.Push("origin", checkOutBranch, false)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func writeOverrides(app *v1alpha1.Application, _ *WriteBackConfig, gitC git.Client) (err error, skip bool) {
targetExists := true
- targetFile := path.Join(tempRoot, app.Spec.Source.Path, fmt.Sprintf(".argocd-source-%s.yaml", app.Name))
+ targetFile := path.Join(gitC.Root(), app.Spec.Source.Path, fmt.Sprintf(".argocd-source-%s.yaml", app.Name))
_, err = os.Stat(targetFile)
if err != nil {
if !os.IsNotExist(err) {
- return err
+ return
} else {
targetExists = false
}
@@ -128,7 +174,7 @@ func commitChangesGit(app *v1alpha1.Application, wbc *WriteBackConfig) error {
override, err := marshalParamsOverride(app)
if err != nil {
- return fmt.Errorf("could not marshal parameters: %v", err)
+ return
}
// If the target file already exist in the repository, we will check whether
@@ -137,51 +183,96 @@ func commitChangesGit(app *v1alpha1.Application, wbc *WriteBackConfig) error {
if targetExists {
data, err := ioutil.ReadFile(targetFile)
if err != nil {
- return err
+ return err, false
}
if string(data) == string(override) {
log.Debugf("target parameter file and marshaled data are the same, skipping commit.")
- return nil
+ return nil, true
}
}
err = ioutil.WriteFile(targetFile, override, 0600)
if err != nil {
- return err
+ return
}
if !targetExists {
err = gitC.Add(targetFile)
- if err != nil {
- return err
- }
}
+ return
+}
- commitOpts := &git.CommitOptions{}
- if wbc.GitCommitMessage != "" {
- cm, err := ioutil.TempFile("", "image-updater-commit-msg")
- if err != nil {
- return fmt.Errorf("cold not create temp file: %v", err)
- }
- log.Debugf("Writing commit message to %s", cm.Name())
- err = ioutil.WriteFile(cm.Name(), []byte(wbc.GitCommitMessage), 0600)
- if err != nil {
- _ = cm.Close()
- return fmt.Errorf("could not write commit message to %s: %v", cm.Name(), err)
- }
- commitOpts.CommitMessagePath = cm.Name()
- _ = cm.Close()
- defer os.Remove(cm.Name())
+var _ changeWriter = writeOverrides
+
+// writeKustomization writes any changes required for updating one or more images to a kustomization.yml
+func writeKustomization(app *v1alpha1.Application, wbc *WriteBackConfig, gitC git.Client) (err error, skip bool) {
+ if oldDir, err := os.Getwd(); err != nil {
+ return err, false
+ } else {
+ defer func() {
+ _ = os.Chdir(oldDir)
+ }()
}
- err = gitC.Commit("", commitOpts)
+ base := filepath.Join(gitC.Root(), wbc.KustomizeBase)
+ if err := os.Chdir(base); err != nil {
+ return err, false
+ }
+
+ log.Infof("updating base %s", base)
+
+ kf, err := kustfile.NewKustomizationFile(fs.MakeRealFS())
if err != nil {
- return err
+ return
}
- err = gitC.Push("origin", checkOutBranch, false)
+ kustomization, err := kf.Read()
if err != nil {
- return err
+ return
}
- return nil
+Images:
+ for _, img := range app.Spec.Source.Kustomize.Images {
+ override := parseImageOverride(img)
+ for i, imgSet := range kustomization.Images {
+ if imgSet.Name == override.Name {
+ kustomization.Images[i] = override
+ continue Images
+ }
+ }
+ // wasn't an existing override, add one
+ kustomization.Images = append(kustomization.Images, override)
+ }
+
+ if err := kf.Write(kustomization); err != nil {
+ return err, false
+ }
+
+ return
}
+
+func parseImageOverride(str v1alpha1.KustomizeImage) image2.Image {
+ // TODO is this a valid use? format could diverge
+ img := image.NewFromIdentifier(string(str))
+ tagName := ""
+ tagDigest := ""
+ if img.ImageTag != nil {
+ tagName = img.ImageTag.TagName
+ tagDigest = img.ImageTag.TagDigest
+ }
+ if img.RegistryURL != "" {
+ // NewFromIdentifier strips off the registry
+ img.ImageName = img.RegistryURL + "/" + img.ImageName
+ }
+ if img.ImageAlias == "" {
+ img.ImageAlias = img.ImageName
+ img.ImageName = "" // inside baseball (see return): name isn't changing, just tag, so don't write newName
+ }
+ return image2.Image{
+ Name: img.ImageAlias,
+ NewName: img.ImageName,
+ NewTag: tagName,
+ Digest: tagDigest,
+ }
+}
+
+var _ changeWriter = writeKustomization
diff --git a/pkg/argocd/git_test.go b/pkg/argocd/git_test.go
index 94cabe0..503c8e5 100644
--- a/pkg/argocd/git_test.go
+++ b/pkg/argocd/git_test.go
@@ -9,7 +9,9 @@ import (
"github.com/argoproj-labs/argocd-image-updater/pkg/image"
"github.com/argoproj-labs/argocd-image-updater/pkg/tag"
+ "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/stretchr/testify/assert"
+ image2 "sigs.k8s.io/kustomize/pkg/image"
)
func Test_TemplateCommitMessage(t *testing.T) {
@@ -37,3 +39,50 @@ updates image bar/baz tag '2.0' to '2.1'
assert.Equal(t, exp, r)
})
}
+
+func Test_parseImageOverride(t *testing.T) {
+ cases := []struct {
+ name string
+ override v1alpha1.KustomizeImage
+ expected image2.Image
+ }{
+ {"tag update", "ghcr.io:1234/foo/foo:123", image2.Image{
+ Name: "ghcr.io:1234/foo/foo",
+ NewTag: "123",
+ }},
+ {"image update", "ghcr.io:1234/foo/foo=ghcr.io:1234/bar", image2.Image{
+ Name: "ghcr.io:1234/foo/foo",
+ NewName: "ghcr.io:1234/bar",
+ }},
+ {"update everything", "ghcr.io:1234/foo/foo=1234.foo.com:9876/bar:123", image2.Image{
+ Name: "ghcr.io:1234/foo/foo",
+ NewName: "1234.foo.com:9876/bar",
+ NewTag: "123",
+ }},
+ {"change registry and tag", "ghcr.io:1234/foo/foo=1234.dkr.ecr.us-east-1.amazonaws.com/bar:123", image2.Image{
+ Name: "ghcr.io:1234/foo/foo",
+ NewName: "1234.dkr.ecr.us-east-1.amazonaws.com/bar",
+ NewTag: "123",
+ }},
+ {"change only registry", "0001.dkr.ecr.us-east-1.amazonaws.com/bar=1234.dkr.ecr.us-east-1.amazonaws.com/bar", image2.Image{
+ Name: "0001.dkr.ecr.us-east-1.amazonaws.com/bar",
+ NewName: "1234.dkr.ecr.us-east-1.amazonaws.com/bar",
+ }},
+ {"change image and set digest", "foo=acme/app@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", image2.Image{
+ Name: "foo",
+ NewName: "acme/app",
+ Digest: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ }},
+ {"set digest", "acme/app@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", image2.Image{
+ Name: "acme/app",
+ Digest: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ }},
+ }
+
+ for _, tt := range cases {
+ t.Run(tt.name, func(t *testing.T) {
+ assert.Equal(t, tt.expected, parseImageOverride(tt.override))
+ })
+ }
+
+}
diff --git a/pkg/argocd/update.go b/pkg/argocd/update.go
index 333d31a..e7d99c5 100644
--- a/pkg/argocd/update.go
+++ b/pkg/argocd/update.go
@@ -3,6 +3,7 @@ package argocd
import (
"context"
"fmt"
+ "path/filepath"
"strings"
"sync"
"text/template"
@@ -16,10 +17,9 @@ import (
"github.com/argoproj-labs/argocd-image-updater/pkg/registry"
"github.com/argoproj-labs/argocd-image-updater/pkg/tag"
- "gopkg.in/yaml.v2"
-
"github.com/argoproj/argo-cd/pkg/apiclient/application"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
+ "gopkg.in/yaml.v2"
)
// Stores some statistics about the results of a run
@@ -64,6 +64,7 @@ type WriteBackConfig struct {
GitCommitUser string
GitCommitEmail string
GitCommitMessage string
+ KustomizeBase string
}
// The following are helper structs to only marshal the fields we require
@@ -420,15 +421,12 @@ func getWriteBackConfig(app *v1alpha1.Application, kubeClient *kube.KubernetesCl
switch strings.TrimSpace(method) {
case "git":
wbc.Method = WriteBackGit
- branch, ok := app.Annotations[common.GitBranchAnnotation]
- if ok {
- wbc.GitBranch = strings.TrimSpace(branch)
+ if target, ok := app.Annotations[common.WriteBackTargetAnnotation]; ok && strings.HasPrefix(target, common.KustomizationPrefix) {
+ wbc.KustomizeBase = parseTarget(target, app.Spec.Source.Path)
}
- credsSource, err := getGitCredsSource(creds, kubeClient)
- if err != nil {
- return nil, fmt.Errorf("invalid git credentials source: %v", err)
+ if err := parseGitConfig(app, kubeClient, wbc, creds); err != nil {
+ return nil, err
}
- wbc.GetCreds = credsSource
default:
return nil, fmt.Errorf("invalid update mechanism: %s", method)
}
@@ -436,6 +434,29 @@ func getWriteBackConfig(app *v1alpha1.Application, kubeClient *kube.KubernetesCl
return wbc, nil
}
+func parseTarget(target string, sourcePath string) (kustomizeBase string) {
+ if target == common.KustomizationPrefix {
+ return filepath.Join(sourcePath, ".")
+ } else if base := target[len(common.KustomizationPrefix)+1:]; strings.HasPrefix(base, "/") {
+ return base[1:]
+ } else {
+ return filepath.Join(sourcePath, base)
+ }
+}
+
+func parseGitConfig(app *v1alpha1.Application, kubeClient *kube.KubernetesClient, wbc *WriteBackConfig, creds string) error {
+ branch, ok := app.Annotations[common.GitBranchAnnotation]
+ if ok {
+ wbc.GitBranch = strings.TrimSpace(branch)
+ }
+ credsSource, err := getGitCredsSource(creds, kubeClient)
+ if err != nil {
+ return fmt.Errorf("invalid git credentials source: %v", err)
+ }
+ wbc.GetCreds = credsSource
+ return nil
+}
+
func commitChangesLocked(app *v1alpha1.Application, wbc *WriteBackConfig, state *SyncIterationState) error {
if wbc.RequiresLocking() {
lock := state.GetRepositoryLock(app.Spec.Source.RepoURL)
@@ -459,7 +480,11 @@ func commitChanges(app *v1alpha1.Application, wbc *WriteBackConfig) error {
return err
}
case WriteBackGit:
- return commitChangesGit(app, wbc)
+ // if the kustomize base is set, the target is a kustomization
+ if wbc.KustomizeBase != "" {
+ return commitChangesGit(app, wbc, writeKustomization)
+ }
+ return commitChangesGit(app, wbc, writeOverrides)
default:
return fmt.Errorf("unknown write back method set: %d", wbc.Method)
}
diff --git a/pkg/argocd/update_test.go b/pkg/argocd/update_test.go
index b53387a..9f4aec9 100644
--- a/pkg/argocd/update_test.go
+++ b/pkg/argocd/update_test.go
@@ -3,6 +3,9 @@ package argocd
import (
"errors"
"fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
"strings"
"testing"
@@ -1032,6 +1035,42 @@ func Test_GetWriteBackConfig(t *testing.T) {
assert.Equal(t, wbc.Method, WriteBackApplication)
})
+ t.Run("kustomization write-back config", func(t *testing.T) {
+ 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": "kustomization:../bar",
+ },
+ },
+ Spec: v1alpha1.ApplicationSpec{
+ Source: v1alpha1.ApplicationSource{
+ RepoURL: "https://example.com/example",
+ TargetRevision: "main",
+ Path: "config/foo",
+ },
+ },
+ Status: v1alpha1.ApplicationStatus{
+ SourceType: v1alpha1.ApplicationSourceTypeKustomize,
+ },
+ }
+
+ argoClient := argomock.ArgoCD{}
+ argoClient.On("UpdateSpec", mock.Anything, mock.Anything).Return(nil, nil)
+
+ kubeClient := kube.KubernetesClient{
+ Clientset: fake.NewFakeKubeClient(),
+ }
+
+ wbc, err := getWriteBackConfig(&app, &kubeClient, &argoClient)
+ require.NoError(t, err)
+ require.NotNil(t, wbc)
+ assert.Equal(t, wbc.Method, WriteBackGit)
+ assert.Equal(t, wbc.KustomizeBase, "config/bar")
+ })
+
t.Run("Default write-back config - argocd", func(t *testing.T) {
app := v1alpha1.Application{
ObjectMeta: v1.ObjectMeta{
@@ -1366,9 +1405,8 @@ func Test_CommitUpdates(t *testing.T) {
}
t.Run("Good commit to target revision", func(t *testing.T) {
- gitMock := &gitmock.Client{}
- gitMock.On("Init").Return(nil)
- gitMock.On("Fetch").Return(nil)
+ gitMock, _, cleanup := mockGit(t)
+ defer cleanup()
gitMock.On("Checkout", mock.Anything).Run(func(args mock.Arguments) {
args.Assert(t, "main")
}).Return(nil)
@@ -1385,9 +1423,8 @@ func Test_CommitUpdates(t *testing.T) {
})
t.Run("Good commit to configured branch", func(t *testing.T) {
- gitMock := &gitmock.Client{}
- gitMock.On("Init").Return(nil)
- gitMock.On("Fetch").Return(nil)
+ gitMock, _, cleanup := mockGit(t)
+ defer cleanup()
gitMock.On("Checkout", mock.Anything).Run(func(args mock.Arguments) {
args.Assert(t, "mybranch")
}).Return(nil)
@@ -1407,9 +1444,8 @@ func Test_CommitUpdates(t *testing.T) {
t.Run("Good commit to default branch", func(t *testing.T) {
app := app.DeepCopy()
- gitMock := &gitmock.Client{}
- gitMock.On("Init").Return(nil)
- gitMock.On("Fetch").Return(nil)
+ gitMock, _, cleanup := mockGit(t)
+ defer cleanup()
gitMock.On("Checkout", mock.Anything).Run(func(args mock.Arguments) {
args.Assert(t, "mydefaultbranch")
}).Return(nil)
@@ -1427,11 +1463,67 @@ func Test_CommitUpdates(t *testing.T) {
assert.NoError(t, err)
})
+ t.Run("Good commit to kustomization", func(t *testing.T) {
+ app := app.DeepCopy()
+ app.Annotations[common.WriteBackTargetAnnotation] = "kustomization"
+ app.Spec.Source.Kustomize = &v1alpha1.ApplicationSourceKustomize{Images: v1alpha1.KustomizeImages{"foo=bar", "bar=baz:123"}}
+ gitMock, dir, cleanup := mockGit(t)
+ defer cleanup()
+ kf := filepath.Join(dir, "kustomization.yml")
+ assert.NoError(t, ioutil.WriteFile(kf, []byte(`
+kind: Kustomization
+apiVersion: kustomize.config.k8s.io/v1beta1
+`), os.ModePerm))
+
+ gitMock.On("Checkout", mock.Anything).Run(func(args mock.Arguments) {
+ args.Assert(t, "mydefaultbranch")
+ }).Return(nil)
+ gitMock.On("Add", mock.Anything).Return(nil)
+ gitMock.On("Commit", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
+ gitMock.On("Push", mock.Anything, mock.Anything, mock.Anything).Return(nil)
+ gitMock.On("SymRefToBranch", mock.Anything).Return("mydefaultbranch", nil)
+ wbc, err := getWriteBackConfig(app, &kubeClient, &argoClient)
+ require.NoError(t, err)
+ wbc.GitClient = gitMock
+ app.Spec.Source.TargetRevision = "HEAD"
+ wbc.GitBranch = ""
+
+ err = commitChanges(app, wbc)
+ assert.NoError(t, err)
+ kust, err := ioutil.ReadFile(kf)
+ assert.NoError(t, err)
+ assert.YAMLEq(t, `
+kind: Kustomization
+apiVersion: kustomize.config.k8s.io/v1beta1
+images:
+ - name: foo
+ newName: bar
+ - name: bar
+ newName: baz
+ newTag: "123"
+`, string(kust))
+
+ // test the merge case too
+ app.Spec.Source.Kustomize.Images = v1alpha1.KustomizeImages{"foo:123", "bar=qux"}
+ err = commitChanges(app, wbc)
+ assert.NoError(t, err)
+ kust, err = ioutil.ReadFile(kf)
+ assert.NoError(t, err)
+ assert.YAMLEq(t, `
+kind: Kustomization
+apiVersion: kustomize.config.k8s.io/v1beta1
+images:
+ - name: foo
+ newTag: "123"
+ - name: bar
+ newName: qux
+`, string(kust))
+ })
+
t.Run("Good commit with author information", func(t *testing.T) {
app := app.DeepCopy()
- gitMock := &gitmock.Client{}
- gitMock.On("Init").Return(nil)
- gitMock.On("Fetch").Return(nil)
+ gitMock, _, cleanup := mockGit(t)
+ defer cleanup()
gitMock.On("Checkout", mock.Anything).Run(func(args mock.Arguments) {
args.Assert(t, "mydefaultbranch")
}).Return(nil)
@@ -1526,9 +1618,8 @@ func Test_CommitUpdates(t *testing.T) {
})
t.Run("Cannot commit", func(t *testing.T) {
- gitMock := &gitmock.Client{}
- gitMock.On("Init").Return(nil)
- gitMock.On("Fetch").Return(nil)
+ gitMock, _, cleanup := mockGit(t)
+ defer cleanup()
gitMock.On("Checkout", mock.Anything).Return(nil)
gitMock.On("Add", mock.Anything).Return(nil)
gitMock.On("Commit", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("cannot commit"))
@@ -1542,9 +1633,8 @@ func Test_CommitUpdates(t *testing.T) {
})
t.Run("Cannot push", func(t *testing.T) {
- gitMock := &gitmock.Client{}
- gitMock.On("Init").Return(nil)
- gitMock.On("Fetch").Return(nil)
+ gitMock, _, cleanup := mockGit(t)
+ defer cleanup()
gitMock.On("Checkout", mock.Anything).Return(nil)
gitMock.On("Add", mock.Anything).Return(nil)
gitMock.On("Commit", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
@@ -1559,9 +1649,8 @@ func Test_CommitUpdates(t *testing.T) {
t.Run("Cannot resolve default branch", func(t *testing.T) {
app := app.DeepCopy()
- gitMock := &gitmock.Client{}
- gitMock.On("Init").Return(nil)
- gitMock.On("Fetch").Return(nil)
+ gitMock, _, cleanup := mockGit(t)
+ defer cleanup()
gitMock.On("Checkout", mock.Anything).Return(nil)
gitMock.On("Add", mock.Anything).Return(nil)
gitMock.On("Commit", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
@@ -1577,3 +1666,39 @@ func Test_CommitUpdates(t *testing.T) {
assert.Errorf(t, err, "failed to resolve ref")
})
}
+
+func Test_parseTarget(t *testing.T) {
+ cases := []struct {
+ name string
+ expected string
+ target string
+ path string
+ }{
+ {"default", ".", "kustomization", ""},
+ {"explicit default", ".", "kustomization:.", "."},
+ {"default path, explicit target", ".", "kustomization:.", ""},
+ {"default target with path", "foo/bar", "kustomization", "foo/bar"},
+ {"default both", ".", "kustomization", ""},
+ {"absolute path", "foo", "kustomization:/foo", "bar"},
+ {"relative path", "bar/foo", "kustomization:foo", "bar"},
+ {"sibling path", "bar/baz", "kustomization:../baz", "bar/foo"},
+ }
+
+ for _, tt := range cases {
+ t.Run(tt.name, func(t *testing.T) {
+ assert.Equal(t, tt.expected, parseTarget(tt.target, tt.path))
+ })
+ }
+}
+
+func mockGit(t *testing.T) (gitMock *gitmock.Client, dir string, cleanup func()) {
+ dir, err := ioutil.TempDir("", "wb-kust")
+ assert.NoError(t, err)
+ gitMock = &gitmock.Client{}
+ gitMock.On("Root").Return(dir)
+ gitMock.On("Init").Return(nil)
+ gitMock.On("Fetch").Return(nil)
+ return gitMock, dir, func() {
+ _ = os.RemoveAll(dir)
+ }
+}
diff --git a/pkg/common/constants.go b/pkg/common/constants.go
index b42f1a9..6c7fec3 100644
--- a/pkg/common/constants.go
+++ b/pkg/common/constants.go
@@ -44,6 +44,8 @@ const (
const (
WriteBackMethodAnnotation = ImageUpdaterAnnotationPrefix + "/write-back-method"
GitBranchAnnotation = ImageUpdaterAnnotationPrefix + "/git-branch"
+ WriteBackTargetAnnotation = ImageUpdaterAnnotationPrefix + "/write-back-target"
+ KustomizationPrefix = "kustomization"
)
// The default Git commit message's template
diff --git a/pkg/image/image_test.go b/pkg/image/image_test.go
index 3e74aaa..9cde998 100644
--- a/pkg/image/image_test.go
+++ b/pkg/image/image_test.go
@@ -54,6 +54,15 @@ func Test_ParseImageTags(t *testing.T) {
assert.Equal(t, "0.1", image.ImageTag.TagName)
})
+ t.Run("Parse valid image name with source name and registry info with port", func(t *testing.T) {
+ image := NewFromIdentifier("ghcr.io:4567/jannfis/orig-image=gcr.io:1234/jannfis/test-image:0.1")
+ assert.Equal(t, "gcr.io:1234", image.RegistryURL)
+ assert.Equal(t, "ghcr.io:4567/jannfis/orig-image", image.ImageAlias)
+ assert.Equal(t, "jannfis/test-image", image.ImageName)
+ require.NotNil(t, image.ImageTag)
+ assert.Equal(t, "0.1", image.ImageTag.TagName)
+ })
+
t.Run("Parse image without version source name and registry info", func(t *testing.T) {
image := NewFromIdentifier("jannfis/orig-image=gcr.io/jannfis/test-image")
assert.Equal(t, "gcr.io", image.RegistryURL)