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

use both app matchers, when appropriate #182

Merged
merged 3 commits into from
Apr 8, 2024
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
31 changes: 31 additions & 0 deletions pkg/affected_apps/matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,37 @@ type AffectedItems struct {
ApplicationSets []ApplicationSet
}

func (ai AffectedItems) Union(other AffectedItems) AffectedItems {
// merge apps
appNameSet := make(map[string]struct{})
for _, app := range ai.Applications {
appNameSet[app.Name] = struct{}{}
}
for _, app := range other.Applications {
if _, ok := appNameSet[app.Name]; ok {
continue
}

ai.Applications = append(ai.Applications, app)
}

// merge appsets
appSetNameSet := make(map[string]struct{})
for _, appSet := range ai.ApplicationSets {
appSetNameSet[appSet.Name] = struct{}{}
}
for _, appSet := range other.ApplicationSets {
if _, ok := appSetNameSet[appSet.Name]; ok {
continue
}

ai.ApplicationSets = append(ai.ApplicationSets, appSet)
}

// return the merge
return ai
}

type ApplicationSet struct {
Name string
}
Expand Down
29 changes: 29 additions & 0 deletions pkg/affected_apps/multi_matcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package affected_apps

import (
"context"

"github.com/pkg/errors"
)

func NewMultiMatcher(matchers ...Matcher) Matcher {
return MultiMatcher{matchers: matchers}
}

type MultiMatcher struct {
matchers []Matcher
}

func (m MultiMatcher) AffectedApps(ctx context.Context, changeList []string, targetBranch string) (AffectedItems, error) {
var total AffectedItems

for index, matcher := range m.matchers {
items, err := matcher.AffectedApps(ctx, changeList, targetBranch)
if err != nil {
return total, errors.Wrapf(err, "failed to find items in matcher #%d", index)
}
total = total.Union(items)
}

return total, nil
}
102 changes: 102 additions & 0 deletions pkg/affected_apps/multi_matcher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package affected_apps

import (
"context"
"testing"

"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/stretchr/testify/require"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type fakeMatcher struct {
items AffectedItems
}

func (f fakeMatcher) AffectedApps(ctx context.Context, changeList []string, targetBranch string) (AffectedItems, error) {
return f.items, nil
}

func TestMultiMatcher(t *testing.T) {
t.Run("exists in one but not two", func(t *testing.T) {
app1 := v1alpha1.Application{ObjectMeta: v1.ObjectMeta{Name: "app-1"}}
matcher1 := fakeMatcher{
items: AffectedItems{
Applications: []v1alpha1.Application{app1},
},
}
matcher2 := fakeMatcher{}

ctx := context.Background()
matcher := NewMultiMatcher(matcher1, matcher2)
total, err := matcher.AffectedApps(ctx, nil, "")

require.NoError(t, err)
require.Len(t, total.Applications, 1)
require.Equal(t, app1, total.Applications[0])
})

t.Run("exists in two but not one", func(t *testing.T) {
app1 := v1alpha1.Application{ObjectMeta: v1.ObjectMeta{Name: "app-1"}}
matcher1 := fakeMatcher{}
matcher2 := fakeMatcher{
items: AffectedItems{
Applications: []v1alpha1.Application{app1},
},
}

ctx := context.Background()
matcher := NewMultiMatcher(matcher1, matcher2)
total, err := matcher.AffectedApps(ctx, nil, "")

require.NoError(t, err)
require.Len(t, total.Applications, 1)
require.Equal(t, app1, total.Applications[0])
})

t.Run("exists in both", func(t *testing.T) {
app1 := v1alpha1.Application{ObjectMeta: v1.ObjectMeta{Name: "app-1"}}
matcher1 := fakeMatcher{
items: AffectedItems{
Applications: []v1alpha1.Application{app1},
},
}
matcher2 := fakeMatcher{
items: AffectedItems{
Applications: []v1alpha1.Application{app1},
},
}

ctx := context.Background()
matcher := NewMultiMatcher(matcher1, matcher2)
total, err := matcher.AffectedApps(ctx, nil, "")

require.NoError(t, err)
require.Len(t, total.Applications, 1)
require.Equal(t, app1, total.Applications[0])
})

t.Run("each contains unique app", func(t *testing.T) {
app1 := v1alpha1.Application{ObjectMeta: v1.ObjectMeta{Name: "app-1"}}
app2 := v1alpha1.Application{ObjectMeta: v1.ObjectMeta{Name: "app-2"}}
matcher1 := fakeMatcher{
items: AffectedItems{
Applications: []v1alpha1.Application{app1},
},
}
matcher2 := fakeMatcher{
items: AffectedItems{
Applications: []v1alpha1.Application{app2},
},
}

ctx := context.Background()
matcher := NewMultiMatcher(matcher1, matcher2)
total, err := matcher.AffectedApps(ctx, nil, "")

require.NoError(t, err)
require.Len(t, total.Applications, 2)
require.Equal(t, app1, total.Applications[0])
require.Equal(t, app2, total.Applications[1])
})
}
23 changes: 14 additions & 9 deletions pkg/events/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,22 @@ func (ce *CheckEvent) GenerateListOfAffectedApps(ctx context.Context, repo *git.
var err error

var matcher affected_apps.Matcher
cfg, _ := repo_config.LoadRepoConfig(repo.Directory)
if cfg != nil {

log.Debug().Msg("using an argocd matcher")
matcher, err = affected_apps.NewArgocdMatcher(ce.ctr.VcsToArgoMap, repo)
if err != nil {
return errors.Wrap(err, "failed to create argocd matcher")
}

cfg, err := repo_config.LoadRepoConfig(repo.Directory)
if err != nil {
return errors.Wrap(err, "failed to load repo config")
} else if cfg != nil {
log.Debug().Msg("using the config matcher")
matcher = affected_apps.NewConfigMatcher(cfg, ce.ctr)
} else {
log.Debug().Msg("using an argocd matcher")
matcher, err = affected_apps.NewArgocdMatcher(ce.ctr.VcsToArgoMap, repo)
if err != nil {
return errors.Wrap(err, "failed to create argocd matcher")
}
configMatcher := affected_apps.NewConfigMatcher(cfg, ce.ctr)
matcher = affected_apps.NewMultiMatcher(matcher, configMatcher)
}

ce.affectedItems, err = matcher.AffectedApps(ctx, ce.fileList, targetBranch)
if err != nil {
telemetry.SetError(span, err, "Get Affected Apps")
Expand Down
21 changes: 13 additions & 8 deletions pkg/repo_config/loader.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package repo_config

import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"gopkg.in/dealancer/validate.v2"
"gopkg.in/yaml.v3"
Expand All @@ -23,17 +22,23 @@ var ErrConfigFileNotFound = errors.New("project config file not found")
func LoadRepoConfig(repoDir string) (*Config, error) {
file, err := searchConfigFile(repoDir)
if err != nil {
return nil, err
if errors.Is(err, ErrConfigFileNotFound) {
return nil, nil
}

return nil, errors.Wrap(err, "failed to find config file")
}
cfg, err := LoadRepoConfigFile(file)

cfg, err := loadRepoConfigFile(file)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "failed to load repo config file")
}

return cfg, nil
}

func RepoConfigFilenameVariations() []string {
filenames := []string{}
var filenames []string
for _, ext := range RepoConfigFileExtensions {
filenames = append(filenames, RepoConfigFilenamePrefix+ext)
}
Expand All @@ -44,7 +49,7 @@ func searchConfigFile(repoDir string) (string, error) {
for _, ext := range RepoConfigFileExtensions {
fn := filepath.Join(repoDir, RepoConfigFilenamePrefix+ext)
fi, err := os.Stat(fn)
if err != nil && err != os.ErrNotExist && !strings.Contains(err.Error(), "no such file or directory") {
if err != nil && !os.IsNotExist(err) {
log.Warn().Err(err).Str("filename", fn).Msg("error while attempting to read project config file")
continue
}
Expand All @@ -56,7 +61,7 @@ func searchConfigFile(repoDir string) (string, error) {
return "", ErrConfigFileNotFound
}

func LoadRepoConfigFile(file string) (*Config, error) {
func loadRepoConfigFile(file string) (*Config, error) {
b, err := os.ReadFile(file)
if err != nil {
log.Error().Err(err).Str("filename", file).Msg("could not read project config file")
Expand Down
6 changes: 3 additions & 3 deletions pkg/repo_config/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ func Test_loadProjectConfigFile(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := LoadRepoConfigFile(tt.args.file)
got, err := loadRepoConfigFile(tt.args.file)
if (err != nil) != tt.wantErr {
t.Errorf("LoadRepoConfigFile() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("loadRepoConfigFile() error = %v, wantErr %v", err, tt.wantErr)
return
}

Expand Down Expand Up @@ -241,7 +241,7 @@ func TestLoadRepoConfig(t *testing.T) {
repoDir: cwd + "/testdata/not-found/",
},
want: nil,
wantErr: true,
wantErr: false,
},
}
for _, tt := range tests {
Expand Down
Loading