Skip to content
Open
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
11 changes: 9 additions & 2 deletions util/app/path/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,15 @@ func GetAppRefreshPaths(app *v1alpha1.Application) []string {
if filepath.IsAbs(item) {
paths = append(paths, item[1:])
} else {
for _, source := range app.Spec.GetSources() {
paths = append(paths, filepath.Clean(filepath.Join(source.Path, item)))
// For sourceHydrator apps, resolve paths relative to DRY source
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is also called in the GetRepoObjs function and should return the right value depending if we are current acting on the dry or sync source.

if app.Spec.SourceHydrator != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should receive the current source in parameter since it influence the refresh path.

  • if source hydrator configured, if syncing sources, use sync source path
  • if source hydrator configured, if hydrating sources, use annotation path and Dry source (source in params) base
  • if source hydrator not configured, use annotation path and default source base (source in params)

drySource := app.Spec.SourceHydrator.GetDrySource()
paths = append(paths, filepath.Clean(filepath.Join(drySource.Path, item)))
} else {
// For non-hydrator apps, use the regular source(s)
for _, source := range app.Spec.GetSources() {
paths = append(paths, filepath.Clean(filepath.Join(source.Path, item)))
}
}
}
}
Expand Down
29 changes: 29 additions & 0 deletions util/app/path/path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,26 @@ func getMultiSourceApp(annotation string, paths ...string) *v1alpha1.Application
}
}

func getSourceHydratorApp(annotation string, drySourcePath string, syncSourcePath string) *v1alpha1.Application {
return &v1alpha1.Application{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
v1alpha1.AnnotationKeyManifestGeneratePaths: annotation,
},
},
Spec: v1alpha1.ApplicationSpec{
SourceHydrator: &v1alpha1.SourceHydrator{
DrySource: v1alpha1.DrySource{
Path: drySourcePath,
},
SyncSource: v1alpha1.SyncSource{
Path: syncSourcePath,
},
},
},
}
}

func Test_AppFilesHaveChanged(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -181,6 +201,11 @@ func Test_AppFilesHaveChanged(t *testing.T) {
{"file two relative paths - not matching", getApp(".README.md;../shared/my-deployment.yaml", "my-app"), []string{"kustomization.yaml"}, false},
{"file two relative paths, multi source - not matching", getMultiSourceApp(".README.md;../shared/my-deployment.yaml", "my-app", "other-path"), []string{"kustomization.yaml"}, false},
{"changed file absolute path - matching", getApp(".", "source/path"), []string{"/source/path/my-deployment.yaml"}, true},
// SourceHydrator tests - paths should be resolved relative to DRY source
{"sourceHydrator relative path - matching", getSourceHydratorApp(".", "source/envs/dev", "sync/path"), []string{"source/envs/dev/my-deployment.yaml"}, true},
{"sourceHydrator relative path - not matching sync path", getSourceHydratorApp(".", "source/envs/dev", "sync/path"), []string{"sync/path/my-deployment.yaml"}, false},
{"sourceHydrator ../base - matching dry base", getSourceHydratorApp(".;../base", "source/envs/dev", "sync/path"), []string{"source/envs/base/kustomization.yaml"}, true},
{"sourceHydrator ../base - not matching sync base", getSourceHydratorApp(".;../base", "source/envs/dev", "sync/path"), []string{"sync/base/kustomization.yaml"}, false},
}
for _, tt := range tests {
ttc := tt
Expand Down Expand Up @@ -209,6 +234,10 @@ func Test_GetAppRefreshPaths(t *testing.T) {
{"file two relative paths", getApp("./README.md;../shared/my-deployment.yaml", "my-app"), []string{"my-app/README.md", "shared/my-deployment.yaml"}},
{"glob path", getApp("/source/*/my-deployment.yaml", "source/path"), []string{"source/*/my-deployment.yaml"}},
{"empty path", getApp(".;", "source/path"), []string{"source/path"}},
// SourceHydrator tests - paths should be resolved relative to DRY source
{"sourceHydrator relative path", getSourceHydratorApp(".", "argocd-kustomize/envs/dev/usw2", "argocd/dev"), []string{"argocd-kustomize/envs/dev/usw2"}},
{"sourceHydrator ../base", getSourceHydratorApp(".;../base", "argocd-kustomize/envs/dev/usw2", "argocd/dev"), []string{"argocd-kustomize/envs/dev/usw2", "argocd-kustomize/envs/dev/base"}},
{"sourceHydrator absolute path", getSourceHydratorApp("/argocd-kustomize/base", "argocd-kustomize/envs/dev/usw2", "argocd/dev"), []string{"argocd-kustomize/base"}},
}
for _, tt := range tests {
ttc := tt
Expand Down
186 changes: 186 additions & 0 deletions util/webhook/testdata/github-commit-syncsource-event.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
{
"ref": "refs/heads/environments/dev",
"before": "d5c1ffa8e294bc18c639bfb4e0df499251034414",
"after": "63738bb582c8b540af7bcfc18f87c575c3ed66e0",
"created": false,
"deleted": false,
"forced": true,
"base_ref": null,
"compare": "https://github.com/jessesuen/test-repo/compare/d5c1ffa8e294...63738bb582c8",
"commits": [
{
"id": "63738bb582c8b540af7bcfc18f87c575c3ed66e0",
"tree_id": "64897da445207e409ad05af93b1f349ad0a4ee19",
"distinct": true,
"message": "Update hydrated manifests",
"timestamp": "2018-05-04T15:40:02-07:00",
"url": "https://github.com/jessesuen/test-repo/commit/63738bb582c8b540af7bcfc18f87c575c3ed66e0",
"author": {
"name": "Jesse Suen",
"email": "[email protected]",
"username": "jessesuen"
},
"committer": {
"name": "Jesse Suen",
"email": "[email protected]",
"username": "jessesuen"
},
"added": [
"ksapps/test-app/environments/staging-argocd-demo/main.jsonnet",
"ksapps/test-app/environments/staging-argocd-demo/params.libsonnet"
],
"removed": [

],
"modified": [
"ksapps/test-app/app.yaml"
]
}
],
"head_commit": {
"id": "63738bb582c8b540af7bcfc18f87c575c3ed66e0",
"tree_id": "64897da445207e409ad05af93b1f349ad0a4ee19",
"distinct": true,
"message": "Add staging-argocd-demo environment",
"timestamp": "2018-05-04T15:40:02-07:00",
"url": "https://github.com/jessesuen/test-repo/commit/63738bb582c8b540af7bcfc18f87c575c3ed66e0",
"author": {
"name": "Jesse Suen",
"email": "[email protected]",
"username": "jessesuen"
},
"committer": {
"name": "Jesse Suen",
"email": "[email protected]",
"username": "jessesuen"
},
"added": [
"ksapps/test-app/environments/staging-argocd-demo/main.jsonnet",
"ksapps/test-app/environments/staging-argocd-demo/params.libsonnet"
],
"removed": [

],
"modified": [
"ksapps/test-app/app.yaml"
]
},
"repository": {
"id": 123060978,
"name": "test-repo",
"full_name": "jessesuen/test-repo",
"owner": {
"name": "jessesuen",
"email": "[email protected]",
"login": "jessesuen",
"id": 12677113,
"avatar_url": "https://avatars0.githubusercontent.com/u/12677113?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/jessesuen",
"html_url": "https://github.com/jessesuen",
"followers_url": "https://api.github.com/users/jessesuen/followers",
"following_url": "https://api.github.com/users/jessesuen/following{/other_user}",
"gists_url": "https://api.github.com/users/jessesuen/gists{/gist_id}",
"starred_url": "https://api.github.com/users/jessesuen/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/jessesuen/subscriptions",
"organizations_url": "https://api.github.com/users/jessesuen/orgs",
"repos_url": "https://api.github.com/users/jessesuen/repos",
"events_url": "https://api.github.com/users/jessesuen/events{/privacy}",
"received_events_url": "https://api.github.com/users/jessesuen/received_events",
"type": "User",
"site_admin": false
},
"private": false,
"html_url": "https://github.com/jessesuen/test-repo",
"description": "Test Repository",
"fork": false,
"url": "https://github.com/jessesuen/test-repo",
"forks_url": "https://api.github.com/repos/jessesuen/test-repo/forks",
"keys_url": "https://api.github.com/repos/jessesuen/test-repo/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/jessesuen/test-repo/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/jessesuen/test-repo/teams",
"hooks_url": "https://api.github.com/repos/jessesuen/test-repo/hooks",
"issue_events_url": "https://api.github.com/repos/jessesuen/test-repo/issues/events{/number}",
"events_url": "https://api.github.com/repos/jessesuen/test-repo/events",
"assignees_url": "https://api.github.com/repos/jessesuen/test-repo/assignees{/user}",
"branches_url": "https://api.github.com/repos/jessesuen/test-repo/branches{/branch}",
"tags_url": "https://api.github.com/repos/jessesuen/test-repo/tags",
"blobs_url": "https://api.github.com/repos/jessesuen/test-repo/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/jessesuen/test-repo/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/jessesuen/test-repo/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/jessesuen/test-repo/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/jessesuen/test-repo/statuses/{sha}",
"languages_url": "https://api.github.com/repos/jessesuen/test-repo/languages",
"stargazers_url": "https://api.github.com/repos/jessesuen/test-repo/stargazers",
"contributors_url": "https://api.github.com/repos/jessesuen/test-repo/contributors",
"subscribers_url": "https://api.github.com/repos/jessesuen/test-repo/subscribers",
"subscription_url": "https://api.github.com/repos/jessesuen/test-repo/subscription",
"commits_url": "https://api.github.com/repos/jessesuen/test-repo/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/jessesuen/test-repo/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/jessesuen/test-repo/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/jessesuen/test-repo/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/jessesuen/test-repo/contents/{+path}",
"compare_url": "https://api.github.com/repos/jessesuen/test-repo/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/jessesuen/test-repo/merges",
"archive_url": "https://api.github.com/repos/jessesuen/test-repo/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/jessesuen/test-repo/downloads",
"issues_url": "https://api.github.com/repos/jessesuen/test-repo/issues{/number}",
"pulls_url": "https://api.github.com/repos/jessesuen/test-repo/pulls{/number}",
"milestones_url": "https://api.github.com/repos/jessesuen/test-repo/milestones{/number}",
"notifications_url": "https://api.github.com/repos/jessesuen/test-repo/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/jessesuen/test-repo/labels{/name}",
"releases_url": "https://api.github.com/repos/jessesuen/test-repo/releases{/id}",
"deployments_url": "https://api.github.com/repos/jessesuen/test-repo/deployments",
"created_at": 1519698615,
"updated_at": "2018-05-04T22:37:55Z",
"pushed_at": 1525473610,
"git_url": "git://github.com/jessesuen/test-repo.git",
"ssh_url": "[email protected]:jessesuen/test-repo.git",
"clone_url": "https://github.com/jessesuen/test-repo.git",
"svn_url": "https://github.com/jessesuen/test-repo",
"homepage": null,
"size": 538,
"stargazers_count": 0,
"watchers_count": 0,
"language": null,
"has_issues": true,
"has_projects": true,
"has_downloads": true,
"has_wiki": true,
"has_pages": false,
"forks_count": 1,
"mirror_url": null,
"archived": false,
"open_issues_count": 0,
"license": null,
"forks": 1,
"open_issues": 0,
"watchers": 0,
"default_branch": "master",
"stargazers": 0,
"master_branch": "master"
},
"pusher": {
"name": "jessesuen",
"email": "[email protected]"
},
"sender": {
"login": "jessesuen",
"id": 12677113,
"avatar_url": "https://avatars0.githubusercontent.com/u/12677113?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/jessesuen",
"html_url": "https://github.com/jessesuen",
"followers_url": "https://api.github.com/users/jessesuen/followers",
"following_url": "https://api.github.com/users/jessesuen/following{/other_user}",
"gists_url": "https://api.github.com/users/jessesuen/gists{/gist_id}",
"starred_url": "https://api.github.com/users/jessesuen/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/jessesuen/subscriptions",
"organizations_url": "https://api.github.com/users/jessesuen/orgs",
"repos_url": "https://api.github.com/users/jessesuen/repos",
"events_url": "https://api.github.com/users/jessesuen/events{/privacy}",
"received_events_url": "https://api.github.com/users/jessesuen/received_events",
"type": "User",
"site_admin": false
}
}
13 changes: 12 additions & 1 deletion util/webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,8 @@ func (a *ArgoCDWebhookHandler) HandleEvent(payload any) {
for _, app := range filteredApps {
if app.Spec.SourceHydrator != nil {
drySource := app.Spec.SourceHydrator.GetDrySource()
if sourceRevisionHasChanged(drySource, revision, touchedHead) && sourceUsesURL(drySource, webURL, repoRegexp) {
syncSource := app.Spec.SourceHydrator.GetSyncSource()
if sourceRevisionHasChanged(drySource, revision, touchedHead) && sourceUsesURL(drySource, webURL, repoRegexp) { // handle webhook for drySource
refreshPaths := path.GetAppRefreshPaths(&app)
if path.AppFilesHaveChanged(refreshPaths, changedFiles) {
namespacedAppInterface := a.appClientset.ArgoprojV1alpha1().Applications(app.Namespace)
Expand All @@ -379,6 +380,16 @@ func (a *ArgoCDWebhookHandler) HandleEvent(payload any) {
continue
}
}
} else if sourceRevisionHasChanged(syncSource, revision, touchedHead) && sourceUsesURL(syncSource, webURL, repoRegexp) { // handle webhook for syncSource
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should call storePreviouslyCachedManifests if no changes in the revision.

// syncSource webhook events only trigger sync, not hydration. Unlike drySource events,
// skip the manifest_generate_path file check since hydration is not required here.
namespacedAppInterface := a.appClientset.ArgoprojV1alpha1().Applications(app.Namespace)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also check if AppFilesHaveChanged.

log.Infof("webhook trigger refresh app from syncSource '%s'", app.Name)
_, err = argo.RefreshApp(namespacedAppInterface, app.Name, v1alpha1.RefreshTypeNormal, true)
if err != nil {
log.Warnf("Failed to refresh app '%s' after syncSource change: %v", app.Name, err)
continue
}
}
}

Expand Down
56 changes: 56 additions & 0 deletions util/webhook/webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,62 @@ func TestGitHubCommitEvent_Hydrate(t *testing.T) {
hook.Reset()
}

func TestGitHubCommitEvent_SyncSourceRefresh(t *testing.T) {
hook := test.NewGlobal()
var patched bool
reaction := func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
patchAction := action.(kubetesting.PatchAction)
assert.Equal(t, "app-to-refresh", patchAction.GetName())
patched = true
return true, nil, nil
}
h := NewMockHandler(&reactorDef{"patch", "applications", reaction}, []string{}, &v1alpha1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "app-to-refresh",
Namespace: "argocd",
Annotations: map[string]string{
"argocd.argoproj.io/manifest-generate-paths": ".",
},
},
Spec: v1alpha1.ApplicationSpec{
SourceHydrator: &v1alpha1.SourceHydrator{
DrySource: v1alpha1.DrySource{
RepoURL: "https://github.com/jessesuen/test-repo",
TargetRevision: "main",
Path: ".",
},
SyncSource: v1alpha1.SyncSource{
TargetBranch: "environments/dev",
Path: ".",
},
HydrateTo: nil,
},
},
},
)
req := httptest.NewRequest(http.MethodPost, "/api/webhook", http.NoBody)
req.Header.Set("X-GitHub-Event", "push")
eventJSON, err := os.ReadFile("testdata/github-commit-syncsource-event.json")
require.NoError(t, err)
req.Body = io.NopCloser(bytes.NewReader(eventJSON))
w := httptest.NewRecorder()
h.Handler(w, req)
close(h.queue)
h.Wait()
assert.Equal(t, http.StatusOK, w.Code)
assert.True(t, patched)

logMessages := make([]string, 0, len(hook.Entries))
for _, entry := range hook.Entries {
logMessages = append(logMessages, entry.Message)
}

assert.Contains(t, logMessages, "webhook trigger refresh app from syncSource 'app-to-refresh'")
// Should not hydrate the app
assert.NotContains(t, logMessages, "hydrate 'app-to-refresh'")
hook.Reset()
}

func TestGitHubTagEvent(t *testing.T) {
hook := test.NewGlobal()
h := NewMockHandler(nil, []string{})
Expand Down
Loading