Files
grafana/apps/provisioning/pkg/repository/tester.go
T
Roberto Jiménez Sánchez a0751b6e71 Provisioning: Default to folder sync only and block new instance sync repositories (#115569)
* Default to folder sync only and block new instance sync repositories

- Change default allowed_targets to folder-only in backend configuration
- Modify validation to only enforce allowedTargets on CREATE operations
- Add deprecation warning for existing instance sync repositories
- Update frontend defaults and tests to reflect new behavior

Fixes #619

* Update warning message: change 'deprecated' to 'not fully supported'

* Fix health check: don't validate allowedTargets for existing repositories

Health checks for existing repositories should treat them as UPDATE operations,
not CREATE operations, so they don't fail validation for instance sync target.

* Fix tests and update i18n translations

- Update BootstrapStep tests to reflect folder-only default behavior
- Run i18n-extract to update translation file structure

* Fix integration tests

* Fix tests

* Fix provisioning test wizard

* Fix fronted test
2025-12-19 11:44:15 +00:00

91 lines
2.9 KiB
Go

package repository
import (
"context"
"net/http"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/validation/field"
provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1"
)
// SimpleRepositoryTester will validate the repository configuration, and then proceed to test the connection to the repository
type SimpleRepositoryTester struct {
validator RepositoryValidator
}
func NewSimpleRepositoryTester(validator RepositoryValidator) SimpleRepositoryTester {
return SimpleRepositoryTester{
validator: validator,
}
}
// TestRepository validates the repository and then runs a health check
func (t *SimpleRepositoryTester) TestRepository(ctx context.Context, repo Repository) (*provisioning.TestResults, error) {
// Determine if this is a CREATE or UPDATE operation
// If the repository has been observed by the controller (ObservedGeneration > 0),
// it's an existing repository and we should treat it as UPDATE
cfg := repo.Config()
isCreate := cfg.Status.ObservedGeneration == 0
errors := t.validator.ValidateRepository(repo, isCreate)
if len(errors) > 0 {
rsp := &provisioning.TestResults{
Code: http.StatusUnprocessableEntity, // Invalid
Success: false,
Errors: make([]provisioning.ErrorDetails, len(errors)),
}
for i, err := range errors {
rsp.Errors[i] = provisioning.ErrorDetails{
Type: metav1.CauseType(err.Type),
Field: err.Field,
Detail: err.Detail,
}
}
return rsp, nil
}
return repo.Test(ctx)
}
type VerifyAgainstExistingRepositories func(ctx context.Context, cfg *provisioning.Repository) *field.Error // defined this way to prevent an import cycle
// RepositoryTesterWithExistingChecker will validate the repository configuration, run a health check, and then compare it against existing repositories
type RepositoryTesterWithExistingChecker struct {
tester SimpleRepositoryTester
verify VerifyAgainstExistingRepositories
}
func NewRepositoryTesterWithExistingChecker(tester SimpleRepositoryTester, verify VerifyAgainstExistingRepositories) RepositoryTesterWithExistingChecker {
return RepositoryTesterWithExistingChecker{
tester: tester,
verify: verify,
}
}
// TestRepositoryAndCheckExisting validates the repository, runs a health check, and then compares it against existing repositories
func (c *RepositoryTesterWithExistingChecker) TestRepositoryAndCheckExisting(ctx context.Context, repo Repository) (*provisioning.TestResults, error) {
rsp, err := c.tester.TestRepository(ctx, repo)
if err != nil {
return nil, err
}
if rsp.Success {
cfg := repo.Config()
if validationErr := c.verify(ctx, cfg); validationErr != nil {
rsp = &provisioning.TestResults{
Success: false,
Code: http.StatusUnprocessableEntity,
Errors: []provisioning.ErrorDetails{{
Type: metav1.CauseType(validationErr.Type),
Field: validationErr.Field,
Detail: validationErr.Detail,
}},
}
}
}
return rsp, nil
}