Skip to content

Commit

Permalink
Concurrent argocd diff
Browse files Browse the repository at this point in the history
  • Loading branch information
ashvarts committed Dec 4, 2024
1 parent d76b09c commit e83c23b
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 13 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/telefonistka
vendor/
internal/pkg/mocks/argocd_settings.go
internal/pkg/mocks/argocd_project.go
16 changes: 11 additions & 5 deletions internal/pkg/argocd/argocd.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ func generateDiffOfAComponent(ctx context.Context, commentDiff bool, componentPa
componentDiffResult.AppWasTemporarilyCreated = true
}
} else {
componentDiffResult.DiffError = fmt.Errorf("No ArgoCD application found for component path %s(repo %s)", componentPath, repo)
componentDiffResult.DiffError = fmt.Errorf("no ArgoCD application found for component path %s(repo %s)", componentPath, repo)
return
}
} else {
Expand All @@ -483,7 +483,7 @@ func generateDiffOfAComponent(ctx context.Context, commentDiff bool, componentPa
Name: &app.Name, // we expect only one app with this label and repo selectors
Refresh: &refreshType,
}
app, err := ac.app.Get(ctx, &appNameQuery)
app, err = ac.app.Get(ctx, &appNameQuery)
if err != nil {
componentDiffResult.DiffError = err
log.Errorf("Error getting app(HardRefresh) %v: %v", appNameQuery.Name, err)
Expand Down Expand Up @@ -565,10 +565,17 @@ func GenerateDiffOfChangedComponents(ctx context.Context, componentsToDiff map[s
return false, true, nil, err
}

diffResult := make(chan DiffResult, len(componentsToDiff))
for componentPath, shouldIDiff := range componentsToDiff {
currentDiffResult := generateDiffOfAComponent(ctx, shouldIDiff, componentPath, prBranch, repo, argoClients, argoSettings, useSHALabelForArgoDicovery, createTempAppObjectFromNewApps)
go func(componentPath string, shouldDiff bool) {
diffResult <- generateDiffOfAComponent(ctx, shouldIDiff, componentPath, prBranch, repo, argoClients, argoSettings, useSHALabelForArgoDicovery, createTempAppObjectFromNewApps)
}(componentPath, shouldIDiff)
}

for range componentsToDiff {
currentDiffResult := <-diffResult
if currentDiffResult.DiffError != nil {
log.Errorf("Error generating diff for component %s: %v", componentPath, currentDiffResult.DiffError)
log.Errorf("Error generating diff for component %s: %v", currentDiffResult.ComponentPath, currentDiffResult.DiffError)
hasComponentDiffErrors = true
err = currentDiffResult.DiffError
}
Expand All @@ -577,6 +584,5 @@ func GenerateDiffOfChangedComponents(ctx context.Context, componentsToDiff map[s
}
diffResults = append(diffResults, currentDiffResult)
}

return hasComponentDiff, hasComponentDiffErrors, diffResults, err
}
86 changes: 78 additions & 8 deletions internal/pkg/argocd/argocd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ import (
"text/template"
"time"

"github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
"github.com/argoproj/argo-cd/v2/pkg/apiclient/project"
"github.com/argoproj/argo-cd/v2/pkg/apiclient/settings"
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
reposerverApiClient "github.com/argoproj/argo-cd/v2/reposerver/apiclient"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/wayfair-incubator/telefonistka/internal/pkg/mocks"
"github.com/wayfair-incubator/telefonistka/internal/pkg/testutils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -315,6 +320,7 @@ func TestFindArgocdAppByPathAnnotationNotFound(t *testing.T) {
}

func TestFetchArgoDiffConcurrently(t *testing.T) {
t.Parallel()
// MockApplicationServiceClient
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
Expand All @@ -326,17 +332,20 @@ func TestFetchArgoDiffConcurrently(t *testing.T) {
// mock the argoClients
mockAppServiceClient := mocks.NewMockApplicationServiceClient(mockCtrl)
mockSettingsServiceClient := mocks.NewMockSettingsServiceClient(mockCtrl)
mockProjectServiceClient := mocks.NewMockProjectServiceClient(mockCtrl)

argoClients = argoCdClients{
app: mockAppServiceClient,
setting: mockSettingsServiceClient,
project: mockProjectServiceClient,
}

// slowReply simulates a slow reply from the server
slowReply := func(ctx context.Context, in any, opts ...any) {
time.Sleep(1 * time.Second)
time.Sleep(time.Second)
}

// makeComponents
// makeComponents for test
makeComponents := func(num int) map[string]bool {
components := make(map[string]bool, num)
for i := 0; i < num; i++ {
Expand All @@ -345,26 +354,87 @@ func TestFetchArgoDiffConcurrently(t *testing.T) {
return components
}

mockSettingsServiceClient.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil, nil)
mockSettingsServiceClient.EXPECT().
Get(gomock.Any(), gomock.Any()).
Return(&settings.Settings{
URL: "https://test-argocd.test.test",
}, nil)
// mock the List method
mockAppServiceClient.EXPECT().
List(gomock.Any(), gomock.Any(), gomock.Any()).
Return(&argoappv1.ApplicationList{}, nil).
Return(&argoappv1.ApplicationList{
Items: []argoappv1.Application{
{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{},
Spec: argoappv1.ApplicationSpec{},
Status: argoappv1.ApplicationStatus{},
Operation: &argoappv1.Operation{},
},
},
}, nil).
AnyTimes().
Do(slowReply)
// type argoCdClients struct {
Do(slowReply) // simulate slow reply

// mock the Get method
mockAppServiceClient.EXPECT().
Get(gomock.Any(), gomock.Any()).
Return(&argoappv1.Application{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{
Name: "test-app",
},
Spec: argoappv1.ApplicationSpec{
Source: &argoappv1.ApplicationSource{
TargetRevision: "test-revision",
},
SyncPolicy: &argoappv1.SyncPolicy{
Automated: &argoappv1.SyncPolicyAutomated{},
},
},
Status: argoappv1.ApplicationStatus{},
Operation: &argoappv1.Operation{},
}, nil).
AnyTimes()

// mock managedResource
mockAppServiceClient.EXPECT().
ManagedResources(gomock.Any(), gomock.Any()).
Return(&application.ManagedResourcesResponse{}, nil).
AnyTimes()

// mock the GetManifests method
mockAppServiceClient.EXPECT().
GetManifests(gomock.Any(), gomock.Any()).
Return(&reposerverApiClient.ManifestResponse{}, nil).
AnyTimes()

// mock the GetDetailedProject method
mockProjectServiceClient.EXPECT().
GetDetailedProject(gomock.Any(), gomock.Any()).
Return(&project.DetailedProjectsResponse{}, nil).
AnyTimes() // type argoCdClients struct {
// app application.ApplicationServiceClient
// project projectpkg.ProjectServiceClient
// setting settings.SettingsServiceClient
// appSet applicationsetpkg.ApplicationSetServiceClient
// }

GenerateDiffOfChangedComponents(
const numComponents = 5
// start timer
start := time.Now()
_, _, diffResults, _ := GenerateDiffOfChangedComponents( //nolint:dogsled
context.TODO(),
makeComponents(5),
makeComponents(numComponents),
"test-pr-branch",
"test-repo",
true,
false,
)

// stop timer
elapsed := time.Since(start)
assert.Equal(t, numComponents, len(diffResults))
// assert that the entire run takes less than numComponents * 1 second
assert.Less(t, elapsed, time.Duration(numComponents)*time.Second)
}
3 changes: 3 additions & 0 deletions internal/pkg/mocks/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ package mocks
// This package contains generated mocks

//go:generate go run github.com/golang/mock/[email protected] -destination=argocd_application.go -package=mocks github.com/argoproj/argo-cd/v2/pkg/apiclient/application ApplicationServiceClient

//go:generate go run github.com/golang/mock/[email protected] -destination=argocd_settings.go -package=mocks github.com/argoproj/argo-cd/v2/pkg/apiclient/settings SettingsServiceClient

//go:generate go run github.com/golang/mock/[email protected] -destination=argocd_project.go -package=mocks github.com/argoproj/argo-cd/v2/pkg/apiclient/project ProjectServiceClient

0 comments on commit e83c23b

Please sign in to comment.