Skip to content

Commit

Permalink
use both app matchers, when appropriate (#182)
Browse files Browse the repository at this point in the history
  • Loading branch information
djeebus authored Apr 8, 2024
1 parent 14e5941 commit cbd0da7
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 20 deletions.
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

0 comments on commit cbd0da7

Please sign in to comment.