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: Add possibility to specify write-back GIT repository as annotation #424

Merged
merged 4 commits into from
Aug 15, 2023
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
2 changes: 1 addition & 1 deletion .github/workflows/ci-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ jobs:
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v2
with:
version: v1.46.2
version: v1.52.2
args: --timeout 5m
test:
name: Ensure unit tests are passing
Expand Down
6 changes: 2 additions & 4 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@ run:
linters:
enable:
- vet
- deadcode
- goimports
- varcheck
- structcheck
- ineffassign
- unconvert
- unparam
- unused
linters-settings:
goimports:
local-prefixes: github.com/argoproj-labs/argocd-image-updater
service:
golangci-lint-version: 1.26.0
golangci-lint-version: 1.52.2
16 changes: 16 additions & 0 deletions docs/basics/update-methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,22 @@ kubectl -n argocd-image-updater create secret generic git-creds \
--from-file=sshPrivateKey=~/.ssh/id_rsa
```

### <a name="method-git-repository"></a>Specifying a repository when using a Helm repository in repoURL

By default, Argo CD Image Updater will use the value found in the Application
spec at `.spec.source.repoURL` as Git repository to checkout. But when using
a Helm repository as `.spec.source.repoURL` GIT will simply fail. To manually
specify the repository to push the changes, specify the
annotation `argocd-image-updater.argoproj.io/git-repository` on the Application
manifest.

The value of this annotation will define the Git repository to use, for example the
following would use a GitHub's repository:

```yaml
argocd-image-updater.argoproj.io/git-repository: git@github.com:example/example.git
```

### <a name="method-git-branch"></a>Specifying a branch to commit to

By default, Argo CD Image Updater will use the value found in the Application
Expand Down
12 changes: 6 additions & 6 deletions ext/git/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,12 @@ func NewClientExt(rawRepoURL string, root string, creds Creds, insecure bool, en

// Returns a HTTP client object suitable for go-git to use using the following
// pattern:
// - If insecure is true, always returns a client with certificate verification
// turned off.
// - If one or more custom certificates are stored for the repository, returns
// a client with those certificates in the list of root CAs used to verify
// the server's certificate.
// - Otherwise (and on non-fatal errors), a default HTTP client is returned.
// - If insecure is true, always returns a client with certificate verification
// turned off.
// - If one or more custom certificates are stored for the repository, returns
// a client with those certificates in the list of root CAs used to verify
// the server's certificate.
// - Otherwise (and on non-fatal errors), a default HTTP client is returned.
func GetRepoHTTPClient(repoURL string, insecure bool, creds Creds, proxyURL string) *http.Client {
// Default HTTP client
var customHTTPClient = &http.Client{
Expand Down
3 changes: 2 additions & 1 deletion ext/git/mocks/Client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pkg/argocd/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func commitChangesGit(app *v1alpha1.Application, wbc *WriteBackConfig, changeLis
logCtx := log.WithContext().AddField("application", app.GetName())
creds, err := wbc.GetCreds(app)
if err != nil {
return fmt.Errorf("could not get creds for repo '%s': %v", app.Spec.Source.RepoURL, err)
return fmt.Errorf("could not get creds for repo '%s': %v", wbc.GitRepo, err)
}
var gitC git.Client
if wbc.GitClient == nil {
Expand All @@ -145,7 +145,7 @@ func commitChangesGit(app *v1alpha1.Application, wbc *WriteBackConfig, changeLis
logCtx.Errorf("could not remove temp dir: %v", err)
}
}()
gitC, err = git.NewClientExt(app.Spec.Source.RepoURL, tempRoot, creds, false, false, "")
gitC, err = git.NewClientExt(wbc.GitRepo, tempRoot, creds, false, false, "")
if err != nil {
return err
}
Expand Down
18 changes: 9 additions & 9 deletions pkg/argocd/gitcreds.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,39 +14,39 @@ import (
)

// getGitCredsSource returns git credentials source that loads credentials from the secret or from Argo CD settings
func getGitCredsSource(creds string, kubeClient *kube.KubernetesClient) (GitCredsSource, error) {
func getGitCredsSource(creds string, kubeClient *kube.KubernetesClient, wbc *WriteBackConfig) (GitCredsSource, error) {
switch {
case creds == "repocreds":
return func(app *v1alpha1.Application) (git.Creds, error) {
return getCredsFromArgoCD(app, kubeClient)
return getCredsFromArgoCD(wbc, kubeClient)
}, nil
case strings.HasPrefix(creds, "secret:"):
return func(app *v1alpha1.Application) (git.Creds, error) {
return getCredsFromSecret(app, creds[len("secret:"):], kubeClient)
return getCredsFromSecret(wbc, creds[len("secret:"):], kubeClient)
}, nil
}
return nil, fmt.Errorf("unexpected credentials format. Expected 'repocreds' or 'secret:<namespace>/<secret>' but got '%s'", creds)
}

// getCredsFromArgoCD loads repository credentials from Argo CD settings
func getCredsFromArgoCD(app *v1alpha1.Application, kubeClient *kube.KubernetesClient) (git.Creds, error) {
func getCredsFromArgoCD(wbc *WriteBackConfig, kubeClient *kube.KubernetesClient) (git.Creds, error) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

settingsMgr := settings.NewSettingsManager(ctx, kubeClient.Clientset, kubeClient.Namespace)
argocdDB := db.NewDB(kubeClient.Namespace, settingsMgr, kubeClient.Clientset)
repo, err := argocdDB.GetRepository(ctx, app.Spec.Source.RepoURL)
repo, err := argocdDB.GetRepository(ctx, wbc.GitRepo)
if err != nil {
return nil, err
}
if !repo.HasCredentials() {
return nil, fmt.Errorf("credentials for '%s' are not configured in Argo CD settings", app.Spec.Source.RepoURL)
return nil, fmt.Errorf("credentials for '%s' are not configured in Argo CD settings", wbc.GitRepo)
}
return repo.GetGitCreds(), nil
}

// getCredsFromSecret loads repository credentials from secret
func getCredsFromSecret(app *v1alpha1.Application, credentialsSecret string, kubeClient *kube.KubernetesClient) (git.Creds, error) {
func getCredsFromSecret(wbc *WriteBackConfig, credentialsSecret string, kubeClient *kube.KubernetesClient) (git.Creds, error) {
var credentials map[string][]byte
var err error
s := strings.SplitN(credentialsSecret, "/", 2)
Expand All @@ -59,13 +59,13 @@ func getCredsFromSecret(app *v1alpha1.Application, credentialsSecret string, kub
return nil, fmt.Errorf("secret ref must be in format 'namespace/name', but is '%s'", credentialsSecret)
}

if ok, _ := git.IsSSHURL(app.Spec.Source.RepoURL); ok {
if ok, _ := git.IsSSHURL(wbc.GitRepo); ok {
var sshPrivateKey []byte
if sshPrivateKey, ok = credentials["sshPrivateKey"]; !ok {
return nil, fmt.Errorf("invalid secret %s: does not contain field sshPrivateKey", credentialsSecret)
}
return git.NewSSHCreds(string(sshPrivateKey), "", true), nil
} else if git.IsHTTPSURL(app.Spec.Source.RepoURL) {
} else if git.IsHTTPSURL(wbc.GitRepo) {
var username, password []byte
if username, ok = credentials["username"]; !ok {
return nil, fmt.Errorf("invalid secret %s: does not contain field username", credentialsSecret)
Expand Down
10 changes: 8 additions & 2 deletions pkg/argocd/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ type WriteBackConfig struct {
GitCommitMessage string
KustomizeBase string
Target string
GitRepo string
}

// The following are helper structs to only marshal the fields we require
Expand Down Expand Up @@ -476,7 +477,12 @@ func parseGitConfig(app *v1alpha1.Application, kubeClient *kube.KubernetesClient
wbc.GitWriteBranch = branches[1]
}
}
credsSource, err := getGitCredsSource(creds, kubeClient)
wbc.GitRepo = app.Spec.Source.RepoURL
repo, ok := app.Annotations[common.GitRepositoryAnnotation]
if ok {
wbc.GitRepo = repo
}
credsSource, err := getGitCredsSource(creds, kubeClient, wbc)
if err != nil {
return fmt.Errorf("invalid git credentials source: %v", err)
}
Expand All @@ -486,7 +492,7 @@ func parseGitConfig(app *v1alpha1.Application, kubeClient *kube.KubernetesClient

func commitChangesLocked(app *v1alpha1.Application, wbc *WriteBackConfig, state *SyncIterationState, changeList []ChangeEntry) error {
if wbc.RequiresLocking() {
lock := state.GetRepositoryLock(app.Spec.Source.RepoURL)
lock := state.GetRepositoryLock(wbc.GitRepo)
lock.Lock()
defer lock.Unlock()
}
Expand Down
42 changes: 42 additions & 0 deletions pkg/argocd/update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1691,6 +1691,48 @@ func Test_GetGitCreds(t *testing.T) {
require.Error(t, err)
require.Nil(t, creds)
})

t.Run("SSH creds from Argo CD settings with Helm Chart repoURL", func(t *testing.T) {
argoClient := argomock.ArgoCD{}
argoClient.On("UpdateSpec", mock.Anything, mock.Anything).Return(nil, nil)
secret := fixture.NewSecret("argocd-image-updater", "git-creds", map[string][]byte{
"sshPrivateKey": []byte("foo"),
})
kubeClient := kube.KubernetesClient{
Clientset: fake.NewFakeClientsetWithResources(secret),
}

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:secret:argocd-image-updater/git-creds",
"argocd-image-updater.argoproj.io/git-repository": "git@github.com:example/example.git",
},
},
Spec: v1alpha1.ApplicationSpec{
Source: v1alpha1.ApplicationSource{
RepoURL: "https://example-helm-repo.com/example",
TargetRevision: "main",
},
},
Status: v1alpha1.ApplicationStatus{
SourceType: v1alpha1.ApplicationSourceTypeKustomize,
},
}

wbc, err := getWriteBackConfig(&app, &kubeClient, &argoClient)
require.NoError(t, err)
require.Equal(t, wbc.GitRepo, "git@github.com:example/example.git")

creds, err := wbc.GetCreds(&app)
require.NoError(t, err)
require.NotNil(t, creds)
// Must have SSH creds
_, ok := creds.(git.SSHCreds)
require.True(t, ok)
})
}

func Test_CommitUpdates(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions pkg/common/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const (
const (
WriteBackMethodAnnotation = ImageUpdaterAnnotationPrefix + "/write-back-method"
GitBranchAnnotation = ImageUpdaterAnnotationPrefix + "/git-branch"
GitRepositoryAnnotation = ImageUpdaterAnnotationPrefix + "/git-repository"
WriteBackTargetAnnotation = ImageUpdaterAnnotationPrefix + "/write-back-target"
KustomizationPrefix = "kustomization"
)
Expand Down
Loading