Provisioning: Configurable Repository Types in monolith and operators (#110822)
* Configurable repository types in monolith and operator * Default to Github in operators * Regenerate wire * Fix and implement unit tests * Same types for enterprise tests * Remove unnecessary conversion * Remove the issue with import cycles
This commit is contained in:
@@ -3,8 +3,6 @@ package repository
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"sort"
|
||||
|
||||
provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1"
|
||||
@@ -28,12 +26,14 @@ type Factory interface {
|
||||
}
|
||||
|
||||
type factory struct {
|
||||
extras map[provisioning.RepositoryType]Extra
|
||||
extras map[provisioning.RepositoryType]Extra
|
||||
enabled map[provisioning.RepositoryType]struct{}
|
||||
}
|
||||
|
||||
func ProvideFactory(extras []Extra) (Factory, error) {
|
||||
func ProvideFactory(enabled map[provisioning.RepositoryType]struct{}, extras []Extra) (Factory, error) {
|
||||
f := &factory{
|
||||
extras: make(map[provisioning.RepositoryType]Extra, len(extras)),
|
||||
enabled: enabled,
|
||||
extras: make(map[provisioning.RepositoryType]Extra, len(extras)),
|
||||
}
|
||||
|
||||
for _, e := range extras {
|
||||
@@ -47,16 +47,27 @@ func ProvideFactory(extras []Extra) (Factory, error) {
|
||||
}
|
||||
|
||||
func (f *factory) Types() []provisioning.RepositoryType {
|
||||
types := slices.Collect(maps.Keys(f.extras))
|
||||
var types []provisioning.RepositoryType
|
||||
for t := range f.enabled {
|
||||
if _, exists := f.extras[t]; exists {
|
||||
types = append(types, t)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(types, func(i, j int) bool {
|
||||
return string(types[i]) < string(types[j])
|
||||
})
|
||||
|
||||
return types
|
||||
}
|
||||
|
||||
func (f *factory) Build(ctx context.Context, r *provisioning.Repository) (Repository, error) {
|
||||
for _, e := range f.extras {
|
||||
if e.Type() == r.Spec.Type {
|
||||
if _, enabled := f.enabled[e.Type()]; !enabled {
|
||||
return nil, fmt.Errorf("repository type %q is not enabled", e.Type())
|
||||
}
|
||||
|
||||
return e.Build(ctx, r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ import (
|
||||
|
||||
func TestNewFactory(t *testing.T) {
|
||||
t.Run("creates factory with empty extras", func(t *testing.T) {
|
||||
factory, err := ProvideFactory([]Extra{})
|
||||
enabled := map[provisioning.RepositoryType]struct{}{}
|
||||
factory, err := ProvideFactory(enabled, []Extra{})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, factory)
|
||||
@@ -32,8 +33,13 @@ func TestNewFactory(t *testing.T) {
|
||||
githubExtra := &MockExtra{}
|
||||
githubExtra.On("Type").Return(provisioning.GitHubRepositoryType)
|
||||
|
||||
enabled := map[provisioning.RepositoryType]struct{}{
|
||||
provisioning.LocalRepositoryType: {},
|
||||
provisioning.GitRepositoryType: {},
|
||||
provisioning.GitHubRepositoryType: {},
|
||||
}
|
||||
extras := []Extra{localExtra, gitExtra, githubExtra}
|
||||
factory, err := ProvideFactory(extras)
|
||||
factory, err := ProvideFactory(enabled, extras)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, factory)
|
||||
@@ -60,8 +66,11 @@ func TestNewFactory(t *testing.T) {
|
||||
secondExtra := &MockExtra{}
|
||||
secondExtra.On("Type").Return(provisioning.LocalRepositoryType)
|
||||
|
||||
enabled := map[provisioning.RepositoryType]struct{}{
|
||||
provisioning.LocalRepositoryType: {},
|
||||
}
|
||||
extras := []Extra{firstExtra, secondExtra}
|
||||
factory, err := ProvideFactory(extras)
|
||||
factory, err := ProvideFactory(enabled, extras)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, factory)
|
||||
@@ -81,8 +90,12 @@ func TestNewFactory(t *testing.T) {
|
||||
duplicateGitExtra := &MockExtra{}
|
||||
duplicateGitExtra.On("Type").Return(provisioning.GitRepositoryType)
|
||||
|
||||
enabled := map[provisioning.RepositoryType]struct{}{
|
||||
provisioning.LocalRepositoryType: {},
|
||||
provisioning.GitRepositoryType: {},
|
||||
}
|
||||
extras := []Extra{localExtra, gitExtra, duplicateGitExtra}
|
||||
factory, err := ProvideFactory(extras)
|
||||
factory, err := ProvideFactory(enabled, extras)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, factory)
|
||||
@@ -94,7 +107,8 @@ func TestNewFactory(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("handles nil extras slice", func(t *testing.T) {
|
||||
factory, err := ProvideFactory(nil)
|
||||
enabled := map[provisioning.RepositoryType]struct{}{}
|
||||
factory, err := ProvideFactory(enabled, nil)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, factory)
|
||||
@@ -105,7 +119,8 @@ func TestNewFactory(t *testing.T) {
|
||||
|
||||
func TestFactory_Types(t *testing.T) {
|
||||
t.Run("returns empty slice for factory with no extras", func(t *testing.T) {
|
||||
factory, err := ProvideFactory([]Extra{})
|
||||
enabled := map[provisioning.RepositoryType]struct{}{}
|
||||
factory, err := ProvideFactory(enabled, []Extra{})
|
||||
|
||||
require.NoError(t, err)
|
||||
types := factory.Types()
|
||||
@@ -128,8 +143,15 @@ func TestFactory_Types(t *testing.T) {
|
||||
gitlabExtra := &MockExtra{}
|
||||
gitlabExtra.On("Type").Return(provisioning.GitLabRepositoryType)
|
||||
|
||||
enabled := map[provisioning.RepositoryType]struct{}{
|
||||
provisioning.LocalRepositoryType: {},
|
||||
provisioning.GitRepositoryType: {},
|
||||
provisioning.GitHubRepositoryType: {},
|
||||
provisioning.BitbucketRepositoryType: {},
|
||||
provisioning.GitLabRepositoryType: {},
|
||||
}
|
||||
extras := []Extra{localExtra, gitExtra, githubExtra, bitbucketExtra, gitlabExtra}
|
||||
factory, err := ProvideFactory(extras)
|
||||
factory, err := ProvideFactory(enabled, extras)
|
||||
|
||||
require.NoError(t, err)
|
||||
types := factory.Types()
|
||||
@@ -163,8 +185,13 @@ func TestFactory_Types(t *testing.T) {
|
||||
githubExtra := &MockExtra{}
|
||||
githubExtra.On("Type").Return(provisioning.GitHubRepositoryType)
|
||||
|
||||
enabled := map[provisioning.RepositoryType]struct{}{
|
||||
provisioning.LocalRepositoryType: {},
|
||||
provisioning.GitRepositoryType: {},
|
||||
provisioning.GitHubRepositoryType: {},
|
||||
}
|
||||
extras := []Extra{githubExtra, localExtra, gitExtra} // Intentionally unordered
|
||||
factory, err := ProvideFactory(extras)
|
||||
factory, err := ProvideFactory(enabled, extras)
|
||||
|
||||
require.NoError(t, err)
|
||||
types1 := factory.Types()
|
||||
@@ -196,7 +223,10 @@ func TestFactory_Build(t *testing.T) {
|
||||
localExtra.On("Type").Return(provisioning.LocalRepositoryType)
|
||||
localExtra.On("Build", mock.Anything, mock.Anything).Return(expectedRepo, nil)
|
||||
|
||||
factory, err := ProvideFactory([]Extra{localExtra})
|
||||
enabled := map[provisioning.RepositoryType]struct{}{
|
||||
provisioning.LocalRepositoryType: {},
|
||||
}
|
||||
factory, err := ProvideFactory(enabled, []Extra{localExtra})
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
@@ -217,7 +247,10 @@ func TestFactory_Build(t *testing.T) {
|
||||
gitExtra := &MockExtra{}
|
||||
gitExtra.On("Type").Return(provisioning.GitRepositoryType)
|
||||
|
||||
factory, err := ProvideFactory([]Extra{gitExtra})
|
||||
enabled := map[provisioning.RepositoryType]struct{}{
|
||||
provisioning.GitRepositoryType: {},
|
||||
}
|
||||
factory, err := ProvideFactory(enabled, []Extra{gitExtra})
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
@@ -242,7 +275,10 @@ func TestFactory_Build(t *testing.T) {
|
||||
localExtra.On("Type").Return(provisioning.LocalRepositoryType)
|
||||
localExtra.On("Build", mock.Anything, mock.Anything).Return(nil, expectedError)
|
||||
|
||||
factory, err := ProvideFactory([]Extra{localExtra})
|
||||
enabled := map[provisioning.RepositoryType]struct{}{
|
||||
provisioning.LocalRepositoryType: {},
|
||||
}
|
||||
factory, err := ProvideFactory(enabled, []Extra{localExtra})
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
@@ -270,7 +306,11 @@ func TestFactory_Build(t *testing.T) {
|
||||
gitExtra.On("Type").Return(provisioning.GitRepositoryType)
|
||||
gitExtra.On("Build", mock.Anything, mock.Anything).Return(gitRepo, nil)
|
||||
|
||||
factory, err := ProvideFactory([]Extra{localExtra, gitExtra})
|
||||
enabled := map[provisioning.RepositoryType]struct{}{
|
||||
provisioning.LocalRepositoryType: {},
|
||||
provisioning.GitRepositoryType: {},
|
||||
}
|
||||
factory, err := ProvideFactory(enabled, []Extra{localExtra, gitExtra})
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
@@ -293,7 +333,10 @@ func TestFactory_Build(t *testing.T) {
|
||||
localExtra := &MockExtra{}
|
||||
localExtra.On("Type").Return(provisioning.LocalRepositoryType)
|
||||
|
||||
factory, err := ProvideFactory([]Extra{localExtra})
|
||||
enabled := map[provisioning.RepositoryType]struct{}{
|
||||
provisioning.LocalRepositoryType: {},
|
||||
}
|
||||
factory, err := ProvideFactory(enabled, []Extra{localExtra})
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
@@ -326,7 +369,10 @@ func TestFactory_Build(t *testing.T) {
|
||||
return c.Value(testKey("test")) == "value"
|
||||
}), mock.Anything).Return(localRepo, nil)
|
||||
|
||||
factory, err := ProvideFactory([]Extra{localExtra})
|
||||
enabled := map[provisioning.RepositoryType]struct{}{
|
||||
provisioning.LocalRepositoryType: {},
|
||||
}
|
||||
factory, err := ProvideFactory(enabled, []Extra{localExtra})
|
||||
require.NoError(t, err)
|
||||
|
||||
repoConfig := &provisioning.Repository{
|
||||
@@ -341,3 +387,241 @@ func TestFactory_Build(t *testing.T) {
|
||||
localExtra.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFactory_Types_EnabledFiltering(t *testing.T) {
|
||||
t.Run("returns only enabled types", func(t *testing.T) {
|
||||
localExtra := &MockExtra{}
|
||||
localExtra.On("Type").Return(provisioning.LocalRepositoryType)
|
||||
|
||||
gitExtra := &MockExtra{}
|
||||
gitExtra.On("Type").Return(provisioning.GitRepositoryType)
|
||||
|
||||
githubExtra := &MockExtra{}
|
||||
githubExtra.On("Type").Return(provisioning.GitHubRepositoryType)
|
||||
|
||||
// Only enable local and github, not git
|
||||
enabled := map[provisioning.RepositoryType]struct{}{
|
||||
provisioning.LocalRepositoryType: {},
|
||||
provisioning.GitHubRepositoryType: {},
|
||||
}
|
||||
extras := []Extra{localExtra, gitExtra, githubExtra}
|
||||
factory, err := ProvideFactory(enabled, extras)
|
||||
|
||||
require.NoError(t, err)
|
||||
types := factory.Types()
|
||||
|
||||
assert.Len(t, types, 2)
|
||||
expectedTypes := []provisioning.RepositoryType{
|
||||
provisioning.GitHubRepositoryType,
|
||||
provisioning.LocalRepositoryType,
|
||||
}
|
||||
assert.Equal(t, expectedTypes, types)
|
||||
|
||||
localExtra.AssertExpectations(t)
|
||||
gitExtra.AssertExpectations(t)
|
||||
githubExtra.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("returns empty when no types enabled", func(t *testing.T) {
|
||||
localExtra := &MockExtra{}
|
||||
localExtra.On("Type").Return(provisioning.LocalRepositoryType)
|
||||
|
||||
gitExtra := &MockExtra{}
|
||||
gitExtra.On("Type").Return(provisioning.GitRepositoryType)
|
||||
|
||||
// Nothing enabled
|
||||
enabled := map[provisioning.RepositoryType]struct{}{}
|
||||
extras := []Extra{localExtra, gitExtra}
|
||||
factory, err := ProvideFactory(enabled, extras)
|
||||
|
||||
require.NoError(t, err)
|
||||
types := factory.Types()
|
||||
|
||||
assert.Empty(t, types)
|
||||
|
||||
localExtra.AssertExpectations(t)
|
||||
gitExtra.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("ignores enabled types without corresponding extras", func(t *testing.T) {
|
||||
localExtra := &MockExtra{}
|
||||
localExtra.On("Type").Return(provisioning.LocalRepositoryType)
|
||||
|
||||
// Enable local and git, but only have local extra
|
||||
enabled := map[provisioning.RepositoryType]struct{}{
|
||||
provisioning.LocalRepositoryType: {},
|
||||
provisioning.GitRepositoryType: {},
|
||||
}
|
||||
extras := []Extra{localExtra}
|
||||
factory, err := ProvideFactory(enabled, extras)
|
||||
|
||||
require.NoError(t, err)
|
||||
types := factory.Types()
|
||||
|
||||
// Should only return local since git extra doesn't exist
|
||||
assert.Len(t, types, 1)
|
||||
assert.Equal(t, []provisioning.RepositoryType{provisioning.LocalRepositoryType}, types)
|
||||
|
||||
localExtra.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestProvideFactory_EnabledParameter(t *testing.T) {
|
||||
t.Run("creates factory with specific enabled types", func(t *testing.T) {
|
||||
localExtra := &MockExtra{}
|
||||
localExtra.On("Type").Return(provisioning.LocalRepositoryType)
|
||||
|
||||
gitExtra := &MockExtra{}
|
||||
gitExtra.On("Type").Return(provisioning.GitRepositoryType)
|
||||
|
||||
githubExtra := &MockExtra{}
|
||||
githubExtra.On("Type").Return(provisioning.GitHubRepositoryType)
|
||||
|
||||
// Only enable local and git, not github
|
||||
enabled := map[provisioning.RepositoryType]struct{}{
|
||||
provisioning.LocalRepositoryType: {},
|
||||
provisioning.GitRepositoryType: {},
|
||||
}
|
||||
extras := []Extra{localExtra, gitExtra, githubExtra}
|
||||
|
||||
factory, err := ProvideFactory(enabled, extras)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, factory)
|
||||
types := factory.Types()
|
||||
|
||||
// Should only return enabled types
|
||||
assert.Len(t, types, 2)
|
||||
expectedTypes := []provisioning.RepositoryType{
|
||||
provisioning.GitRepositoryType,
|
||||
provisioning.LocalRepositoryType,
|
||||
}
|
||||
assert.Equal(t, expectedTypes, types)
|
||||
|
||||
localExtra.AssertExpectations(t)
|
||||
gitExtra.AssertExpectations(t)
|
||||
githubExtra.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("creates factory with no enabled types", func(t *testing.T) {
|
||||
localExtra := &MockExtra{}
|
||||
localExtra.On("Type").Return(provisioning.LocalRepositoryType)
|
||||
|
||||
gitExtra := &MockExtra{}
|
||||
gitExtra.On("Type").Return(provisioning.GitRepositoryType)
|
||||
|
||||
// Enable nothing
|
||||
enabled := map[provisioning.RepositoryType]struct{}{}
|
||||
extras := []Extra{localExtra, gitExtra}
|
||||
|
||||
factory, err := ProvideFactory(enabled, extras)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, factory)
|
||||
types := factory.Types()
|
||||
|
||||
assert.Empty(t, types)
|
||||
|
||||
localExtra.AssertExpectations(t)
|
||||
gitExtra.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("enabled types without extras are ignored", func(t *testing.T) {
|
||||
localExtra := &MockExtra{}
|
||||
localExtra.On("Type").Return(provisioning.LocalRepositoryType)
|
||||
|
||||
// Enable both local and git, but only provide local extra
|
||||
enabled := map[provisioning.RepositoryType]struct{}{
|
||||
provisioning.LocalRepositoryType: {},
|
||||
provisioning.GitRepositoryType: {},
|
||||
}
|
||||
extras := []Extra{localExtra}
|
||||
|
||||
factory, err := ProvideFactory(enabled, extras)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, factory)
|
||||
types := factory.Types()
|
||||
|
||||
// Should only return local since git extra doesn't exist
|
||||
assert.Len(t, types, 1)
|
||||
assert.Equal(t, []provisioning.RepositoryType{provisioning.LocalRepositoryType}, types)
|
||||
|
||||
localExtra.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("still errors for duplicate repository types", func(t *testing.T) {
|
||||
// Create duplicate extras to trigger error
|
||||
firstExtra := &MockExtra{}
|
||||
firstExtra.On("Type").Return(provisioning.LocalRepositoryType)
|
||||
|
||||
secondExtra := &MockExtra{}
|
||||
secondExtra.On("Type").Return(provisioning.LocalRepositoryType)
|
||||
|
||||
enabled := map[provisioning.RepositoryType]struct{}{
|
||||
provisioning.LocalRepositoryType: {},
|
||||
}
|
||||
extras := []Extra{firstExtra, secondExtra}
|
||||
|
||||
factory, err := ProvideFactory(enabled, extras)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, factory)
|
||||
assert.Contains(t, err.Error(), "repository type \"local\" is already registered")
|
||||
|
||||
firstExtra.AssertExpectations(t)
|
||||
secondExtra.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("build fails for disabled repository type", func(t *testing.T) {
|
||||
localExtra := &MockExtra{}
|
||||
localExtra.On("Type").Return(provisioning.LocalRepositoryType)
|
||||
|
||||
// Create factory with local extra but don't enable it
|
||||
enabled := map[provisioning.RepositoryType]struct{}{}
|
||||
factory, err := ProvideFactory(enabled, []Extra{localExtra})
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
repoConfig := &provisioning.Repository{
|
||||
Spec: provisioning.RepositorySpec{
|
||||
Type: provisioning.LocalRepositoryType,
|
||||
},
|
||||
}
|
||||
|
||||
result, err := factory.Build(ctx, repoConfig)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, result)
|
||||
assert.Contains(t, err.Error(), "repository type \"local\" is not enabled")
|
||||
localExtra.AssertNotCalled(t, "Build")
|
||||
localExtra.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("build succeeds for enabled repository type", func(t *testing.T) {
|
||||
expectedRepo := &MockConfigRepository{}
|
||||
localExtra := &MockExtra{}
|
||||
localExtra.On("Type").Return(provisioning.LocalRepositoryType)
|
||||
localExtra.On("Build", mock.Anything, mock.Anything).Return(expectedRepo, nil)
|
||||
|
||||
// Create factory with local extra and enable it
|
||||
enabled := map[provisioning.RepositoryType]struct{}{
|
||||
provisioning.LocalRepositoryType: {},
|
||||
}
|
||||
factory, err := ProvideFactory(enabled, []Extra{localExtra})
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
repoConfig := &provisioning.Repository{
|
||||
Spec: provisioning.RepositorySpec{
|
||||
Type: provisioning.LocalRepositoryType,
|
||||
},
|
||||
}
|
||||
|
||||
result, err := factory.Build(ctx, repoConfig)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expectedRepo, result)
|
||||
localExtra.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user