refactor: use single ExportFn interface for both export functions

Simplified the worker by using the same ExportFn interface for both ExportAll
and ExportSpecificResources. Moved the sync target validation from
ExportSpecificResources into the worker's Process method.

Changes:
- Remove ExportSpecificResourcesFn type (reuse ExportFn)
- Rename exportFn to exportAllFn for clarity
- Update ExportSpecificResources to match ExportFn signature
- Move folder sync target validation to worker Process method
- Update all tests to remove repoConfig parameter
- Remove obsolete unit test for instance sync rejection (now tested in worker)
This commit is contained in:
Roberto Jimenez Sanchez
2025-12-03 10:44:37 +01:00
parent 14bf1a46c8
commit 99a4f2362e
5 changed files with 53 additions and 102 deletions
@@ -105,17 +105,12 @@ func ExportResources(ctx context.Context, options provisioning.ExportJobOptions,
// ExportSpecificResources exports a list of specific resources identified by ResourceRef entries.
// It validates that resources are not folders, are supported, and are unmanaged.
// Specific resource export only works with folder sync targets, not instance sync targets.
func ExportSpecificResources(ctx context.Context, repoConfig *provisioning.Repository, options provisioning.ExportJobOptions, clients resources.ResourceClients, repositoryResources resources.RepositoryResources, progress jobs.JobProgressRecorder) error {
// Note: The caller must validate that the repository has a folder sync target before calling this function.
func ExportSpecificResources(ctx context.Context, repoName string, options provisioning.ExportJobOptions, clients resources.ResourceClients, repositoryResources resources.RepositoryResources, progress jobs.JobProgressRecorder) error {
if len(options.Resources) == 0 {
return errors.New("no resources specified for export")
}
// Validate that specific resource export is only used with folder sync targets
if repoConfig.Spec.Sync.Target != provisioning.SyncTargetTypeFolder {
return fmt.Errorf("specific resource export is only supported for folder sync targets, but repository has target type '%s'", repoConfig.Spec.Sync.Target)
}
progress.SetMessage(ctx, "exporting specific resources")
tree, err := loadUnmanagedFolderTree(ctx, clients, progress)
@@ -17,27 +17,7 @@ import (
"github.com/grafana/grafana/pkg/registry/apis/provisioning/resources"
)
// Helper function to create a repository config with folder sync target
func createFolderSyncRepository() *provisioningV0.Repository {
return &provisioningV0.Repository{
Spec: provisioningV0.RepositorySpec{
Sync: provisioningV0.SyncOptions{
Target: provisioningV0.SyncTargetTypeFolder,
},
},
}
}
// Helper function to create a repository config with instance sync target
func createInstanceSyncRepository() *provisioningV0.Repository {
return &provisioningV0.Repository{
Spec: provisioningV0.RepositorySpec{
Sync: provisioningV0.SyncOptions{
Target: provisioningV0.SyncTargetTypeInstance,
},
},
}
}
// Helper function to create folder objects
func createFolderObject(name, uid, parentFolderUID string) unstructured.Unstructured {
@@ -72,7 +52,7 @@ func createDashboardObjectWithFolder(name, folderID string) unstructured.Unstruc
}
// Helper function to run ExportSpecificResources test
func runExportSpecificResourcesTest(t *testing.T, repoConfig *provisioningV0.Repository, resourceRefs []provisioningV0.ResourceRef, folderItems []unstructured.Unstructured, setupProgress func(*jobs.MockJobProgressRecorder), setupResources func(*resources.MockRepositoryResources, *resources.MockResourceClients)) error {
func runExportSpecificResourcesTest(t *testing.T, resourceRefs []provisioningV0.ResourceRef, folderItems []unstructured.Unstructured, setupProgress func(*jobs.MockJobProgressRecorder), setupResources func(*resources.MockRepositoryResources, *resources.MockResourceClients)) error {
resourceClients := resources.NewMockResourceClients(t)
mockProgress := jobs.NewMockJobProgressRecorder(t)
setupProgress(mockProgress)
@@ -86,7 +66,7 @@ func runExportSpecificResourcesTest(t *testing.T, repoConfig *provisioningV0.Rep
Resources: resourceRefs,
}
err := ExportSpecificResources(context.Background(), repoConfig, options, resourceClients, repoResources, mockProgress)
err := ExportSpecificResources(context.Background(), "test-repo", options, resourceClients, repoResources, mockProgress)
mockProgress.AssertExpectations(t)
repoResources.AssertExpectations(t)
@@ -96,7 +76,6 @@ func runExportSpecificResourcesTest(t *testing.T, repoConfig *provisioningV0.Rep
}
func TestExportSpecificResources_Success(t *testing.T) {
repoConfig := createFolderSyncRepository()
resourceRefs := []provisioningV0.ResourceRef{
{
@@ -172,12 +151,11 @@ func TestExportSpecificResources_Success(t *testing.T) {
})).Return("grafana/dashboard-2.json", nil)
}
err := runExportSpecificResourcesTest(t, repoConfig, resourceRefs, folderItems, setupProgress, setupResources)
err := runExportSpecificResourcesTest(t, resourceRefs, folderItems, setupProgress, setupResources)
require.NoError(t, err)
}
func TestExportSpecificResources_EmptyResources(t *testing.T) {
repoConfig := createFolderSyncRepository()
options := provisioningV0.ExportJobOptions{
Path: "grafana",
@@ -189,35 +167,12 @@ func TestExportSpecificResources_EmptyResources(t *testing.T) {
repoResources := resources.NewMockRepositoryResources(t)
mockProgress := jobs.NewMockJobProgressRecorder(t)
err := ExportSpecificResources(context.Background(), repoConfig, options, resourceClients, repoResources, mockProgress)
err := ExportSpecificResources(context.Background(), "test-repo", options, resourceClients, repoResources, mockProgress)
require.EqualError(t, err, "no resources specified for export")
}
func TestExportSpecificResources_RejectsInstanceSyncTarget(t *testing.T) {
repoConfig := createInstanceSyncRepository()
options := provisioningV0.ExportJobOptions{
Path: "grafana",
Branch: "feature/branch",
Resources: []provisioningV0.ResourceRef{
{
Name: "dashboard-1",
Kind: "Dashboard",
Group: resources.DashboardResource.Group,
},
},
}
resourceClients := resources.NewMockResourceClients(t)
repoResources := resources.NewMockRepositoryResources(t)
mockProgress := jobs.NewMockJobProgressRecorder(t)
err := ExportSpecificResources(context.Background(), repoConfig, options, resourceClients, repoResources, mockProgress)
require.EqualError(t, err, "specific resource export is only supported for folder sync targets, but repository has target type 'instance'")
}
func TestExportSpecificResources_RejectsFolders(t *testing.T) {
repoConfig := createFolderSyncRepository()
resourceRefs := []provisioningV0.ResourceRef{
{
@@ -247,12 +202,11 @@ func TestExportSpecificResources_RejectsFolders(t *testing.T) {
// No ForKind or WriteResourceFileFromObject calls expected for folders
}
err := runExportSpecificResourcesTest(t, repoConfig, resourceRefs, folderItems, setupProgress, setupResources)
err := runExportSpecificResourcesTest(t, resourceRefs, folderItems, setupProgress, setupResources)
require.NoError(t, err)
}
func TestExportSpecificResources_RejectsManagedResources(t *testing.T) {
repoConfig := createFolderSyncRepository()
resourceRefs := []provisioningV0.ResourceRef{
{
@@ -299,12 +253,11 @@ func TestExportSpecificResources_RejectsManagedResources(t *testing.T) {
// No WriteResourceFileFromObject call expected for managed resources
}
err := runExportSpecificResourcesTest(t, repoConfig, resourceRefs, folderItems, setupProgress, setupResources)
err := runExportSpecificResourcesTest(t, resourceRefs, folderItems, setupProgress, setupResources)
require.NoError(t, err)
}
func TestExportSpecificResources_RejectsUnsupportedResources(t *testing.T) {
repoConfig := createFolderSyncRepository()
resourceRefs := []provisioningV0.ResourceRef{
{
@@ -347,12 +300,11 @@ func TestExportSpecificResources_RejectsUnsupportedResources(t *testing.T) {
// No WriteResourceFileFromObject call expected for unsupported resources
}
err := runExportSpecificResourcesTest(t, repoConfig, resourceRefs, folderItems, setupProgress, setupResources)
err := runExportSpecificResourcesTest(t, resourceRefs, folderItems, setupProgress, setupResources)
require.NoError(t, err)
}
func TestExportSpecificResources_FolderPathResolution(t *testing.T) {
repoConfig := createFolderSyncRepository()
resourceRefs := []provisioningV0.ResourceRef{
{
@@ -403,12 +355,11 @@ func TestExportSpecificResources_FolderPathResolution(t *testing.T) {
})).Return("grafana/team-a/subteam/dashboard-in-nested-folder.json", nil)
}
err := runExportSpecificResourcesTest(t, repoConfig, resourceRefs, folderItems, setupProgress, setupResources)
err := runExportSpecificResourcesTest(t, resourceRefs, folderItems, setupProgress, setupResources)
require.NoError(t, err)
}
func TestExportSpecificResources_FolderClientError(t *testing.T) {
repoConfig := createFolderSyncRepository()
resourceRefs := []provisioningV0.ResourceRef{
{
@@ -427,12 +378,11 @@ func TestExportSpecificResources_FolderClientError(t *testing.T) {
resourceClients.On("Folder", mock.Anything).Return(nil, fmt.Errorf("folder client error"))
}
err := runExportSpecificResourcesTest(t, repoConfig, resourceRefs, nil, setupProgress, setupResources)
err := runExportSpecificResourcesTest(t, resourceRefs, nil, setupProgress, setupResources)
require.EqualError(t, err, "get folder client: folder client error")
}
func TestExportSpecificResources_ResourceNotFound(t *testing.T) {
repoConfig := createFolderSyncRepository()
resourceRefs := []provisioningV0.ResourceRef{
{
@@ -471,12 +421,11 @@ func TestExportSpecificResources_ResourceNotFound(t *testing.T) {
resourceClients.On("ForKind", mock.Anything, gvk).Return(dashboardClient, resources.DashboardResource, nil)
}
err := runExportSpecificResourcesTest(t, repoConfig, resourceRefs, folderItems, setupProgress, setupResources)
err := runExportSpecificResourcesTest(t, resourceRefs, folderItems, setupProgress, setupResources)
require.NoError(t, err)
}
func TestExportSpecificResources_DashboardVersionConversion(t *testing.T) {
repoConfig := createFolderSyncRepository()
resourceRefs := []provisioningV0.ResourceRef{
{
@@ -553,12 +502,11 @@ func TestExportSpecificResources_DashboardVersionConversion(t *testing.T) {
})).Return("grafana/v2-dashboard.json", nil)
}
err := runExportSpecificResourcesTest(t, repoConfig, resourceRefs, folderItems, setupProgress, setupResources)
err := runExportSpecificResourcesTest(t, resourceRefs, folderItems, setupProgress, setupResources)
require.NoError(t, err)
}
func TestExportSpecificResources_TooManyErrors(t *testing.T) {
repoConfig := createFolderSyncRepository()
resourceRefs := []provisioningV0.ResourceRef{
{
@@ -602,6 +550,6 @@ func TestExportSpecificResources_TooManyErrors(t *testing.T) {
mock.Anything).Return("", fmt.Errorf("write error"))
}
err := runExportSpecificResourcesTest(t, repoConfig, resourceRefs, folderItems, setupProgress, setupResources)
err := runExportSpecificResourcesTest(t, resourceRefs, folderItems, setupProgress, setupResources)
require.EqualError(t, err, "too many errors")
}
@@ -21,26 +21,29 @@ type ExportFn func(ctx context.Context, repoName string, options provisioning.Ex
type WrapWithStageFn func(ctx context.Context, repo repository.Repository, stageOptions repository.StageOptions, fn func(repo repository.Repository, staged bool) error) error
type ExportWorker struct {
clientFactory resources.ClientFactory
repositoryResources resources.RepositoryResourcesFactory
exportFn ExportFn
wrapWithStageFn WrapWithStageFn
metrics jobs.JobMetrics
clientFactory resources.ClientFactory
repositoryResources resources.RepositoryResourcesFactory
exportAllFn ExportFn
exportSpecificResourcesFn ExportFn
wrapWithStageFn WrapWithStageFn
metrics jobs.JobMetrics
}
func NewExportWorker(
clientFactory resources.ClientFactory,
repositoryResources resources.RepositoryResourcesFactory,
exportFn ExportFn,
exportAllFn ExportFn,
exportSpecificResourcesFn ExportFn,
wrapWithStageFn WrapWithStageFn,
metrics jobs.JobMetrics,
) *ExportWorker {
return &ExportWorker{
clientFactory: clientFactory,
repositoryResources: repositoryResources,
exportFn: exportFn,
wrapWithStageFn: wrapWithStageFn,
metrics: metrics,
clientFactory: clientFactory,
repositoryResources: repositoryResources,
exportAllFn: exportAllFn,
exportSpecificResourcesFn: exportSpecificResourcesFn,
wrapWithStageFn: wrapWithStageFn,
metrics: metrics,
}
}
@@ -100,15 +103,19 @@ func (r *ExportWorker) Process(ctx context.Context, repo repository.Repository,
return fmt.Errorf("create repository resource client: %w", err)
}
// Check if Resources list is provided (bulk export mode)
// Check if Resources list is provided (specific resources export mode)
if len(options.Resources) > 0 {
progress.SetTotal(ctx, len(options.Resources))
progress.StrictMaxErrors(1) // Fail fast on any error during export
return ExportSpecificResources(ctx, cfg, *options, clients, repositoryResources, progress)
// Validate that specific resource export is only used with folder sync targets
if cfg.Spec.Sync.Target != provisioning.SyncTargetTypeFolder {
return fmt.Errorf("specific resource export is only supported for folder sync targets, but repository has target type '%s'", cfg.Spec.Sync.Target)
}
return r.exportSpecificResourcesFn(ctx, cfg.Name, *options, clients, repositoryResources, progress)
}
// Fall back to existing ExportAll behavior for backward compatibility
return r.exportFn(ctx, cfg.Name, *options, clients, repositoryResources, progress)
return r.exportAllFn(ctx, cfg.Name, *options, clients, repositoryResources, progress)
}
err := r.wrapWithStageFn(ctx, repo, cloneOptions, fn)
@@ -56,7 +56,7 @@ func TestExportWorker_IsSupported(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := NewExportWorker(nil, nil, nil, nil, metrics)
r := NewExportWorker(nil, nil, nil, nil, nil, metrics)
got := r.IsSupported(context.Background(), tt.job)
require.Equal(t, tt.want, got)
})
@@ -70,7 +70,7 @@ func TestExportWorker_ProcessNoExportSettings(t *testing.T) {
},
}
r := NewExportWorker(nil, nil, nil, nil, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
r := NewExportWorker(nil, nil, nil, nil, nil, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
err := r.Process(context.Background(), nil, job, nil)
require.EqualError(t, err, "missing export settings")
}
@@ -93,7 +93,7 @@ func TestExportWorker_ProcessWriteNotAllowed(t *testing.T) {
},
})
r := NewExportWorker(nil, nil, nil, nil, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
r := NewExportWorker(nil, nil, nil, nil, nil, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
err := r.Process(context.Background(), mockRepo, job, nil)
require.EqualError(t, err, "this repository is read only")
}
@@ -117,7 +117,7 @@ func TestExportWorker_ProcessBranchNotAllowedForLocal(t *testing.T) {
},
})
r := NewExportWorker(nil, nil, nil, nil, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
r := NewExportWorker(nil, nil, nil, nil, nil, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
err := r.Process(context.Background(), mockRepo, job, nil)
require.EqualError(t, err, "this repository does not support the branch workflow")
}
@@ -149,7 +149,7 @@ func TestExportWorker_ProcessFailedToCreateClients(t *testing.T) {
return fn(repo, true)
})
r := NewExportWorker(mockClients, nil, nil, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
r := NewExportWorker(mockClients, nil, nil, nil, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
mockProgress := jobs.NewMockJobProgressRecorder(t)
err := r.Process(context.Background(), mockRepo, job, mockProgress)
@@ -185,7 +185,7 @@ func TestExportWorker_ProcessNotReaderWriter(t *testing.T) {
return fn(repo, true)
})
r := NewExportWorker(mockClients, nil, nil, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
r := NewExportWorker(mockClients, nil, nil, nil, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
err := r.Process(context.Background(), mockRepo, job, mockProgress)
require.EqualError(t, err, "export job submitted targeting repository that is not a ReaderWriter")
}
@@ -221,7 +221,7 @@ func TestExportWorker_ProcessRepositoryResourcesError(t *testing.T) {
mockStageFn.On("Execute", context.Background(), mockRepo, mock.Anything, mock.Anything).Return(func(ctx context.Context, repo repository.Repository, stageOpts repository.StageOptions, fn func(repository.Repository, bool) error) error {
return fn(repo, true)
})
r := NewExportWorker(mockClients, mockRepoResources, nil, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
r := NewExportWorker(mockClients, mockRepoResources, nil, nil, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
err := r.Process(context.Background(), mockRepo, job, mockProgress)
require.EqualError(t, err, "create repository resource client: failed to create repository resources client")
}
@@ -273,7 +273,7 @@ func TestExportWorker_ProcessStageOptions(t *testing.T) {
return fn(repo, true)
})
r := NewExportWorker(mockClients, mockRepoResources, mockExportFn.Execute, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
r := NewExportWorker(mockClients, mockRepoResources, mockExportFn.Execute, nil, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
err := r.Process(context.Background(), mockRepo, job, mockProgress)
require.NoError(t, err)
}
@@ -355,7 +355,7 @@ func TestExportWorker_ProcessStageOptionsWithBranch(t *testing.T) {
return fn(repo, true)
})
r := NewExportWorker(mockClients, mockRepoResources, mockExportFn.Execute, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
r := NewExportWorker(mockClients, mockRepoResources, mockExportFn.Execute, nil, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
err := r.Process(context.Background(), mockRepo, job, mockProgress)
require.NoError(t, err)
})
@@ -398,7 +398,7 @@ func TestExportWorker_ProcessExportFnError(t *testing.T) {
return fn(repo, true)
})
r := NewExportWorker(mockClients, mockRepoResources, mockExportFn.Execute, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
r := NewExportWorker(mockClients, mockRepoResources, mockExportFn.Execute, nil, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
err := r.Process(context.Background(), mockRepo, job, mockProgress)
require.EqualError(t, err, "export failed")
}
@@ -426,7 +426,7 @@ func TestExportWorker_ProcessWrapWithStageFnError(t *testing.T) {
mockStageFn := NewMockWrapWithStageFn(t)
mockStageFn.On("Execute", mock.Anything, mockRepo, mock.Anything, mock.Anything).Return(errors.New("stage failed"))
r := NewExportWorker(nil, nil, nil, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
r := NewExportWorker(nil, nil, nil, nil, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
err := r.Process(context.Background(), mockRepo, job, mockProgress)
require.EqualError(t, err, "stage failed")
}
@@ -452,7 +452,7 @@ func TestExportWorker_ProcessBranchNotAllowedForStageableRepositories(t *testing
mockProgress := jobs.NewMockJobProgressRecorder(t)
// No progress messages expected in current implementation
r := NewExportWorker(nil, nil, nil, nil, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
r := NewExportWorker(nil, nil, nil, nil, nil, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
err := r.Process(context.Background(), mockRepo, job, mockProgress)
require.EqualError(t, err, "this repository does not support the branch workflow")
}
@@ -504,7 +504,7 @@ func TestExportWorker_ProcessGitRepository(t *testing.T) {
return fn(repo, true)
})
r := NewExportWorker(mockClients, mockRepoResources, mockExportFn.Execute, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
r := NewExportWorker(mockClients, mockRepoResources, mockExportFn.Execute, nil, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
err := r.Process(context.Background(), mockRepo, job, mockProgress)
require.NoError(t, err)
}
@@ -550,7 +550,7 @@ func TestExportWorker_ProcessGitRepositoryExportFnError(t *testing.T) {
return fn(repo, true)
})
r := NewExportWorker(mockClients, mockRepoResources, mockExportFn.Execute, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
r := NewExportWorker(mockClients, mockRepoResources, mockExportFn.Execute, nil, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
err := r.Process(context.Background(), mockRepo, job, mockProgress)
require.EqualError(t, err, "export failed")
}
@@ -613,7 +613,7 @@ func TestExportWorker_RefURLsSetWithBranch(t *testing.T) {
return fn(mockReaderWriter, true)
})
r := NewExportWorker(mockClients, mockRepoResources, mockExportFn.Execute, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
r := NewExportWorker(mockClients, mockRepoResources, mockExportFn.Execute, nil, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
err := r.Process(context.Background(), mockRepoWithURLs, job, mockProgress)
require.NoError(t, err)
@@ -670,7 +670,7 @@ func TestExportWorker_RefURLsNotSetWithoutBranch(t *testing.T) {
return fn(mockReaderWriter, true)
})
r := NewExportWorker(mockClients, mockRepoResources, mockExportFn.Execute, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
r := NewExportWorker(mockClients, mockRepoResources, mockExportFn.Execute, nil, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
err := r.Process(context.Background(), mockRepoWithURLs, job, mockProgress)
require.NoError(t, err)
@@ -727,7 +727,7 @@ func TestExportWorker_RefURLsNotSetForNonURLRepository(t *testing.T) {
return fn(mockReaderWriter, true)
})
r := NewExportWorker(mockClients, mockRepoResources, mockExportFn.Execute, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
r := NewExportWorker(mockClients, mockRepoResources, mockExportFn.Execute, nil, mockStageFn.Execute, jobs.RegisterJobMetrics(prometheus.NewPedanticRegistry()))
err := r.Process(context.Background(), mockRepo, job, mockProgress)
require.NoError(t, err)
@@ -705,6 +705,7 @@ func (b *APIBuilder) GetPostStartHooks() (map[string]genericapiserver.PostStartH
b.clients,
b.repositoryResources,
export.ExportAll,
export.ExportSpecificResources,
stageIfPossible,
metrics,
)