layer1: add namespace tombstone helper step 34
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
# 2026-04-26 — NamespaceManager rewrite, step 34
|
||||||
|
|
||||||
|
## Цель шага
|
||||||
|
|
||||||
|
Подготовить безопасный helper для delete/tombstone событий Namespace informer-а.
|
||||||
|
|
||||||
|
## Что меняем
|
||||||
|
|
||||||
|
1. Добавляем `NamespaceFromObject()`.
|
||||||
|
2. Helper поддерживает:
|
||||||
|
- `*corev1.Namespace`
|
||||||
|
- `cache.DeletedFinalStateUnknown`
|
||||||
|
3. Добавляем `NamespaceEventFromObject()`.
|
||||||
|
4. Добавляем unit tests.
|
||||||
|
|
||||||
|
## Что НЕ меняем
|
||||||
|
|
||||||
|
- не подключаем delete handling в watcher-ы на этом шаге;
|
||||||
|
- не меняем runtime behavior.
|
||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
k8sCache "k8s.io/client-go/tools/cache"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -88,6 +89,32 @@ func NamespaceEventFromNamespace(eventType NamespaceEventType, namespace *corev1
|
|||||||
return NewNamespaceEvent(eventType, namespace.Name, namespace.Labels, source, observedAt)
|
return NewNamespaceEvent(eventType, namespace.Name, namespace.Labels, source, observedAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NamespaceFromObject(obj interface{}) (*corev1.Namespace, bool) {
|
||||||
|
switch typed := obj.(type) {
|
||||||
|
case *corev1.Namespace:
|
||||||
|
return typed, true
|
||||||
|
case k8sCache.DeletedFinalStateUnknown:
|
||||||
|
namespace, ok := typed.Obj.(*corev1.Namespace)
|
||||||
|
return namespace, ok
|
||||||
|
case *k8sCache.DeletedFinalStateUnknown:
|
||||||
|
if typed == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
namespace, ok := typed.Obj.(*corev1.Namespace)
|
||||||
|
return namespace, ok
|
||||||
|
default:
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NamespaceEventFromObject(eventType NamespaceEventType, obj interface{}, source NamespaceSource, observedAt time.Time) NamespaceEvent {
|
||||||
|
namespace, ok := NamespaceFromObject(obj)
|
||||||
|
if !ok {
|
||||||
|
return NewNamespaceEvent(eventType, "", nil, source, observedAt)
|
||||||
|
}
|
||||||
|
return NamespaceEventFromNamespace(eventType, namespace, source, observedAt)
|
||||||
|
}
|
||||||
|
|
||||||
func (nr NamespaceRecord) Clone() NamespaceRecord {
|
func (nr NamespaceRecord) Clone() NamespaceRecord {
|
||||||
clone := nr
|
clone := nr
|
||||||
clone.Labels = cloneStringMap(nr.Labels)
|
clone.Labels = cloneStringMap(nr.Labels)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
k8sCache "k8s.io/client-go/tools/cache"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNamespaceRecordClone(t *testing.T) {
|
func TestNamespaceRecordClone(t *testing.T) {
|
||||||
@@ -107,3 +108,40 @@ func TestNamespaceEventFromNamespace(t *testing.T) {
|
|||||||
t.Fatalf("expected event labels to stay detached from original namespace")
|
t.Fatalf("expected event labels to stay detached from original namespace")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNamespaceFromObject(t *testing.T) {
|
||||||
|
namespace := &corev1.Namespace{}
|
||||||
|
namespace.Name = "tenant-a"
|
||||||
|
|
||||||
|
extracted, ok := NamespaceFromObject(namespace)
|
||||||
|
if !ok || extracted.Name != "tenant-a" {
|
||||||
|
t.Fatalf("expected namespace object to be extracted directly")
|
||||||
|
}
|
||||||
|
|
||||||
|
extracted, ok = NamespaceFromObject(k8sCache.DeletedFinalStateUnknown{Obj: namespace})
|
||||||
|
if !ok || extracted.Name != "tenant-a" {
|
||||||
|
t.Fatalf("expected namespace tombstone to be extracted")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNamespaceEventFromObject(t *testing.T) {
|
||||||
|
namespace := &corev1.Namespace{}
|
||||||
|
namespace.Name = "tenant-a"
|
||||||
|
namespace.Labels = map[string]string{ManagedNamespaceLabelKey: ManagedNamespaceLabelValue}
|
||||||
|
|
||||||
|
event := NamespaceEventFromObject(NamespaceEventRemove, k8sCache.DeletedFinalStateUnknown{Obj: namespace}, NamespaceSourceWatcher, time.Now().UTC())
|
||||||
|
if event.Type != NamespaceEventRemove {
|
||||||
|
t.Fatalf("expected remove event type")
|
||||||
|
}
|
||||||
|
if event.Name != "tenant-a" {
|
||||||
|
t.Fatalf("expected tenant-a event name")
|
||||||
|
}
|
||||||
|
if event.Labels[ManagedNamespaceLabelKey] != ManagedNamespaceLabelValue {
|
||||||
|
t.Fatalf("expected labels copied from tombstone namespace")
|
||||||
|
}
|
||||||
|
|
||||||
|
emptyEvent := NamespaceEventFromObject(NamespaceEventRemove, "not-a-namespace", NamespaceSourceWatcher, time.Now().UTC())
|
||||||
|
if emptyEvent.Name != "" {
|
||||||
|
t.Fatalf("expected empty name for unsupported object")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user