* Implement hierarchical error handling for folder creation failures This commit implements hierarchical error handling to improve sync robustness when folder creation fails. Instead of failing the entire sync, the system now: 1. Tracks failed folder creations and automatically skips nested resources 2. Records skipped resources with FileActionIgnored (doesn't count toward error limits) 3. Allows other folder hierarchies to continue processing 4. Prevents folder deletion when child resource deletions fail Key Changes: - Add PathCreationError type to track which folder path failed - Modify progress recorder to automatically detect and track failures via Record() - Add IsNestedUnderFailedCreation() and HasFailedDeletionsUnder() checks - Update full and incremental sync to skip nested resources after folder failures - Deletions proceed even if parent folder creation failed (resource may exist from previous sync) - FileActionIgnored results don't count toward error limits Example behavior improvement: Before: /monitoring folder creation fails → all nested resources fail → other folders never processed After: /monitoring folder creation fails → nested resources ignored → /applications folder succeeds 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * provisioning: refactor hierarchical errors in folder management. * Move test to the corresponding package * Refactor timeout handling in applyChanges functions - Introduced wrapWithTimeout function to streamline timeout context management for applyChange calls. - Updated applyFoldersSerially and applyIncrementalChanges to utilize the new timeout wrapper. - Removed redundant logging and error handling code related to timeout in favor of centralized handling in wrapWithTimeout. - Adjusted test expectations to reflect changes in error reporting for context deadlines. --------- Co-authored-by: Roberto Jimenez Sanchez <roberto.jimenez@grafana.com> Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
69 lines
2.0 KiB
Go
69 lines
2.0 KiB
Go
package resources_test
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/grafana/grafana/pkg/registry/apis/provisioning/resources"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestPathCreationError(t *testing.T) {
|
|
t.Run("Error method returns formatted message", func(t *testing.T) {
|
|
underlyingErr := fmt.Errorf("underlying error")
|
|
pathErr := &resources.PathCreationError{
|
|
Path: "grafana/folder-1",
|
|
Err: underlyingErr,
|
|
}
|
|
|
|
expectedMsg := "failed to create path grafana/folder-1: underlying error"
|
|
require.Equal(t, expectedMsg, pathErr.Error())
|
|
})
|
|
|
|
t.Run("Unwrap returns underlying error", func(t *testing.T) {
|
|
underlyingErr := fmt.Errorf("underlying error")
|
|
pathErr := &resources.PathCreationError{
|
|
Path: "grafana/folder-1",
|
|
Err: underlyingErr,
|
|
}
|
|
|
|
unwrapped := pathErr.Unwrap()
|
|
require.Equal(t, underlyingErr, unwrapped)
|
|
require.EqualError(t, unwrapped, "underlying error")
|
|
})
|
|
|
|
t.Run("errors.Is finds underlying error", func(t *testing.T) {
|
|
underlyingErr := fmt.Errorf("underlying error")
|
|
pathErr := &resources.PathCreationError{
|
|
Path: "grafana/folder-1",
|
|
Err: underlyingErr,
|
|
}
|
|
|
|
require.True(t, errors.Is(pathErr, underlyingErr))
|
|
require.False(t, errors.Is(pathErr, fmt.Errorf("different error")))
|
|
})
|
|
|
|
t.Run("errors.As extracts PathCreationError", func(t *testing.T) {
|
|
underlyingErr := fmt.Errorf("underlying error")
|
|
pathErr := &resources.PathCreationError{
|
|
Path: "grafana/folder-1",
|
|
Err: underlyingErr,
|
|
}
|
|
|
|
var extractedErr *resources.PathCreationError
|
|
require.True(t, errors.As(pathErr, &extractedErr))
|
|
require.NotNil(t, extractedErr)
|
|
require.Equal(t, "grafana/folder-1", extractedErr.Path)
|
|
require.Equal(t, underlyingErr, extractedErr.Err)
|
|
})
|
|
|
|
t.Run("errors.As returns false for non-PathCreationError", func(t *testing.T) {
|
|
regularErr := fmt.Errorf("regular error")
|
|
|
|
var extractedErr *resources.PathCreationError
|
|
require.False(t, errors.As(regularErr, &extractedErr))
|
|
require.Nil(t, extractedErr)
|
|
})
|
|
}
|