apply feedback review

This commit is contained in:
Ezequiel Victorero
2026-01-14 15:43:27 -03:00
parent ea5b39cd9f
commit 7b91acbaeb
3 changed files with 52 additions and 33 deletions
@@ -59,10 +59,7 @@ func convertSnapshotToK8sResource(v *dashboardsnapshots.DashboardSnapshot, names
Namespace: namespacer(v.OrgID),
},
Spec: dashV0.SnapshotSpec{
Title: &v.Name,
Expires: &expires,
External: &v.External,
ExternalUrl: &v.ExternalURL,
Title: &v.Name,
},
}
+2 -2
View File
@@ -286,8 +286,8 @@ func (srv *CleanUpService) deleteKubernetesExpiredSnapshots(ctx context.Context)
namespaceMapper := request.GetNamespaceMapper(srv.Cfg)
snapshots, err := client.Resource(gvr).Namespace(namespaceMapper(o.ID)).List(ctx, v1.ListOptions{})
if err != nil {
logger.Error("Failed to list snapshots", "error", err.Error())
return
logger.Error("Failed to list snapshots for org", "orgID", o.ID, "error", err.Error())
continue
}
// Check each snapshot for expiration
for _, item := range snapshots.Items {
+49 -27
View File
@@ -13,8 +13,6 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
@@ -175,6 +173,27 @@ func TestDeleteExpiredSnapshots_KubernetesMode(t *testing.T) {
service.deleteExpiredSnapshots(context.Background())
mockResource.AssertExpectations(t)
})
t.Run("continues processing other orgs when one org fails to list snapshots", func(t *testing.T) {
// Create an expired snapshot for the second org
expiredTime := time.Now().Add(-time.Hour).UnixMilli()
expiredSnapshot := createUnstructuredSnapshot("expired-snap-org2", "org-2", expiredTime)
// Use namespace-aware mock that returns error for first namespace, success for second
mockResource := &namespaceAwareMockResource{
expiredSnapshot: expiredSnapshot,
deletedNames: make(map[string]bool),
}
mockDynClient := new(mockDynamicClient)
mockDynClient.On("Resource", mock.Anything).Return(mockResource)
service := createK8sCleanupService(t, mockDynClient)
service.deleteExpiredSnapshots(context.Background())
// Verify that the snapshot from the second org was still deleted despite the first org failing
require.True(t, mockResource.deletedNames["expired-snap-org2"], "expected snapshot from second org to be deleted")
})
}
// Helper function to create unstructured snapshots for testing
@@ -224,9 +243,11 @@ func (m *mockDynamicClient) Resource(resource schema.GroupVersionResource) dynam
return args.Get(0).(dynamic.NamespaceableResourceInterface)
}
// mockResourceInterface is a minimal mock for dynamic.ResourceInterface
// mockResourceInterface is a minimal mock for dynamic.ResourceInterface.
// Embeds dynamic.ResourceInterface to avoid implementing unused methods.
type mockResourceInterface struct {
mock.Mock
dynamic.ResourceInterface
}
func (m *mockResourceInterface) Namespace(ns string) dynamic.ResourceInterface {
@@ -247,31 +268,32 @@ func (m *mockResourceInterface) Delete(ctx context.Context, name string, opts me
return args.Error(0)
}
// Unused methods - panic if called unexpectedly
func (m *mockResourceInterface) Create(ctx context.Context, obj *unstructured.Unstructured, opts metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) {
panic("not implemented")
// namespaceAwareMockResource is a mock that returns different results based on namespace.
// Used to test that processing continues when one org fails.
// Embeds dynamic.ResourceInterface to avoid implementing unused methods.
type namespaceAwareMockResource struct {
dynamic.ResourceInterface
currentNamespace string
expiredSnapshot *unstructured.Unstructured
deletedNames map[string]bool
}
func (m *mockResourceInterface) Update(ctx context.Context, obj *unstructured.Unstructured, opts metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error) {
panic("not implemented")
func (m *namespaceAwareMockResource) Namespace(ns string) dynamic.ResourceInterface {
m.currentNamespace = ns
return m
}
func (m *mockResourceInterface) UpdateStatus(ctx context.Context, obj *unstructured.Unstructured, opts metav1.UpdateOptions) (*unstructured.Unstructured, error) {
panic("not implemented")
func (m *namespaceAwareMockResource) List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error) {
// Fail for the first org namespace (org ID 1 maps to "default"), succeed for the second (org ID 2 maps to "org-2")
if m.currentNamespace == "default" {
return nil, errors.New("simulated list error for org-1")
}
return &unstructured.UnstructuredList{
Items: []unstructured.Unstructured{*m.expiredSnapshot},
}, nil
}
func (m *mockResourceInterface) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
panic("not implemented")
}
func (m *mockResourceInterface) Get(ctx context.Context, name string, opts metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) {
panic("not implemented")
}
func (m *mockResourceInterface) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
panic("not implemented")
}
func (m *mockResourceInterface) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (*unstructured.Unstructured, error) {
panic("not implemented")
}
func (m *mockResourceInterface) Apply(ctx context.Context, name string, obj *unstructured.Unstructured, opts metav1.ApplyOptions, subresources ...string) (*unstructured.Unstructured, error) {
panic("not implemented")
}
func (m *mockResourceInterface) ApplyStatus(ctx context.Context, name string, obj *unstructured.Unstructured, opts metav1.ApplyOptions) (*unstructured.Unstructured, error) {
panic("not implemented")
func (m *namespaceAwareMockResource) Delete(ctx context.Context, name string, opts metav1.DeleteOptions, subresources ...string) error {
m.deletedNames[name] = true
return nil
}