Compare commits

...

1 Commits

Author SHA1 Message Date
Gonzalo Trigueros
6cfb7e5931 provisioning: add dynamic repository limit to validation 2026-01-12 17:18:13 +01:00
2 changed files with 37 additions and 6 deletions

View File

@@ -123,8 +123,9 @@ type APIBuilder struct {
extras []Extra
extraWorkers []jobs.Worker
restConfigGetter func(context.Context) (*clientrest.Config, error)
registry prometheus.Registerer
restConfigGetter func(context.Context) (*clientrest.Config, error)
registry prometheus.Registerer
namespaceLimitsProvider NamespaceLimitsProvider
}
// NewAPIBuilder creates an API builder.
@@ -151,6 +152,7 @@ func NewAPIBuilder(
registry prometheus.Registerer,
newStandaloneClientFactoryFunc func(loopbackConfigProvider apiserver.RestConfigProvider) resources.ClientFactory, // optional, only used for standalone apiserver
useExclusivelyAccessCheckerForAuthz bool,
namespaceLimitsProvider NamespaceLimitsProvider,
) *APIBuilder {
var clients resources.ClientFactory
if newStandaloneClientFactoryFunc != nil {
@@ -194,6 +196,7 @@ func NewAPIBuilder(
registry: registry,
validator: repository.NewValidator(minSyncInterval, allowedTargets, allowImageRendering),
useExclusivelyAccessCheckerForAuthz: useExclusivelyAccessCheckerForAuthz,
namespaceLimitsProvider: namespaceLimitsProvider,
}
for _, builder := range extraBuilders {
@@ -268,6 +271,7 @@ func RegisterAPIService(
allowedTargets = append(allowedTargets, provisioning.SyncTargetType(target))
}
limitsProvider := NewFixedNamespaceLimitsProvider(10)
builder := NewAPIBuilder(
cfg.DisableControllers,
repoFactory,
@@ -288,6 +292,7 @@ func RegisterAPIService(
reg,
nil,
false, // TODO: first, test this on the MT side before we enable it by default in ST as well
limitsProvider,
)
apiregistration.RegisterAPI(builder)
return builder, nil
@@ -801,7 +806,7 @@ func invalidRepositoryError(name string, list field.ErrorList) error {
}
func (b *APIBuilder) VerifyAgainstExistingRepositories(ctx context.Context, cfg *provisioning.Repository) *field.Error {
return VerifyAgainstExistingRepositories(ctx, b.store, cfg)
return VerifyAgainstExistingRepositories(ctx, b.store, cfg, b.namespaceLimitsProvider)
}
func (b *APIBuilder) GetPostStartHooks() (map[string]genericapiserver.PostStartHookFunc, error) {

View File

@@ -20,6 +20,11 @@ type RepositoryLister interface {
List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error)
}
// NamespaceLimitsProvider provides repository limits for a given namespace
type NamespaceLimitsProvider interface {
GetMaxRepositories(ctx context.Context, namespace string) (int, error)
}
// GetRepositoriesInNamespace retrieves all repositories in a given namespace
func GetRepositoriesInNamespace(ctx context.Context, store RepositoryLister) ([]provisioning.Repository, error) {
var allRepositories []provisioning.Repository
@@ -51,7 +56,7 @@ func GetRepositoriesInNamespace(ctx context.Context, store RepositoryLister) ([]
}
// VerifyAgainstExistingRepositories validates a repository configuration against existing repositories
func VerifyAgainstExistingRepositories(ctx context.Context, store RepositoryLister, cfg *provisioning.Repository) *field.Error {
func VerifyAgainstExistingRepositories(ctx context.Context, store RepositoryLister, cfg *provisioning.Repository, limitsProvider NamespaceLimitsProvider) *field.Error {
ctx, _, err := identity.WithProvisioningIdentity(ctx, cfg.Namespace)
if err != nil {
return &field.Error{Type: field.ErrorTypeInternal, Detail: err.Error()}
@@ -115,10 +120,31 @@ func VerifyAgainstExistingRepositories(ctx context.Context, store RepositoryList
count++
}
}
if count >= 10 {
maxRepositories, err := limitsProvider.GetMaxRepositories(ctx, cfg.Namespace)
if err != nil {
return &field.Error{Type: field.ErrorTypeInternal, Detail: fmt.Sprintf("Failed to get repository limit: %v", err)}
}
if count >= maxRepositories {
return field.Forbidden(field.NewPath("spec"),
"Maximum number of 10 repositories reached")
fmt.Sprintf("Maximum number of %d repositories reached", maxRepositories))
}
return nil
}
// FixedNamespaceLimitsProvider returns a fixed limit for all namespaces
type FixedNamespaceLimitsProvider struct {
limit int
}
// NewFixedNamespaceLimitsProvider creates a new FixedNamespaceLimitsProvider with the specified limit
func NewFixedNamespaceLimitsProvider(limit int) *FixedNamespaceLimitsProvider {
return &FixedNamespaceLimitsProvider{limit: limit}
}
// GetMaxRepositories returns the fixed limit for any namespace
func (f *FixedNamespaceLimitsProvider) GetMaxRepositories(ctx context.Context, namespace string) (int, error) {
return f.limit, nil
}