blob: 4aa26eb55e29f014b9135cc51e18390f4e5622d9 [file]
package watcher
import (
"context"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
"time"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
v1 "k8s.io/api/core/v1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
dynamicfake "k8s.io/client-go/dynamic/fake"
fake "k8s.io/client-go/kubernetes/fake"
framework "k8s.io/client-go/tools/cache/testing"
)
func TestAllNamespacesWatchFor_Add(t *testing.T) {
w, fc := getTestWatcher()
epHandler := EpHandler{"endpoints", w.Ep}
err := w.allNamespacesWatchFor(&epHandler, w.Cs.CoreV1().RESTClient(),
fields.Everything(), &v1.Endpoints{}, 0, fc)
if err != nil {
t.Error(err)
}
w.Cs.CoreV1().Endpoints("trafficserver-test-2").Create(context.TODO(), &v1.Endpoints{
ObjectMeta: meta_v1.ObjectMeta{
Name: "testsvc",
Namespace: "trafficserver-test-2",
},
Subsets: []v1.EndpointSubset{
{
Addresses: []v1.EndpointAddress{
{IP: "10.10.1.1"},
{IP: "10.10.2.2"},
},
Ports: []v1.EndpointPort{
{Name: "main", Port: 8080, Protocol: "TCP"},
},
},
},
}, meta_v1.CreateOptions{})
time.Sleep(100 * time.Millisecond)
returnedKeys := w.Ep.RedisClient.GetDefaultDBKeyValues()
expectedKeys := getExpectedKeysForEndpointAdd()
if !reflect.DeepEqual(returnedKeys, expectedKeys) {
t.Errorf("returned \n%v, but expected \n%v", returnedKeys, expectedKeys)
}
}
func TestAllNamespacesWatchFor_Update(t *testing.T) {
w, fc := getTestWatcher()
epHandler := EpHandler{"endpoints", w.Ep}
err := w.allNamespacesWatchFor(&epHandler, w.Cs.CoreV1().RESTClient(),
fields.Everything(), &v1.Endpoints{}, 0, fc)
if err != nil {
t.Error(err)
}
w.Cs.CoreV1().Endpoints("trafficserver-test-2").Create(context.TODO(), &v1.Endpoints{
ObjectMeta: meta_v1.ObjectMeta{
Name: "testsvc",
Namespace: "trafficserver-test-2",
},
Subsets: []v1.EndpointSubset{
{
Addresses: []v1.EndpointAddress{
{IP: "10.10.1.1"},
{IP: "10.10.2.2"},
},
Ports: []v1.EndpointPort{
{Name: "main", Port: 8080, Protocol: "TCP"},
},
},
},
}, meta_v1.CreateOptions{})
time.Sleep(100 * time.Millisecond)
w.Cs.CoreV1().Endpoints("trafficserver-test-2").Update(context.TODO(), &v1.Endpoints{
ObjectMeta: meta_v1.ObjectMeta{
Name: "testsvc",
Namespace: "trafficserver-test-2",
},
Subsets: []v1.EndpointSubset{
{
Addresses: []v1.EndpointAddress{
{IP: "10.10.1.1"},
{IP: "10.10.3.3"},
},
Ports: []v1.EndpointPort{
{Name: "main", Port: 8080, Protocol: "TCP"},
},
},
},
}, meta_v1.UpdateOptions{})
time.Sleep(100 * time.Millisecond)
returnedKeys := w.Ep.RedisClient.GetDefaultDBKeyValues()
expectedKeys := getExpectedKeysForEndpointAdd()
expectedKeys["trafficserver-test-2:testsvc:8080"][1] = "10.10.3.3#8080#http"
if !reflect.DeepEqual(returnedKeys, expectedKeys) {
t.Errorf("returned \n%v, but expected \n%v", returnedKeys, expectedKeys)
}
}
func TestAllNamespacesWatchFor_Delete(t *testing.T) {
w, fc := getTestWatcher()
epHandler := EpHandler{"endpoints", w.Ep}
err := w.allNamespacesWatchFor(&epHandler, w.Cs.CoreV1().RESTClient(),
fields.Everything(), &v1.Endpoints{}, 0, fc)
if err != nil {
t.Error(err)
}
fc.Add(&v1.Endpoints{
ObjectMeta: meta_v1.ObjectMeta{
Name: "testsvc",
Namespace: "trafficserver-test-2",
},
Subsets: []v1.EndpointSubset{
{
Addresses: []v1.EndpointAddress{
{IP: "10.10.1.1"},
{IP: "10.10.2.2"},
},
Ports: []v1.EndpointPort{
{Name: "main", Port: 8080, Protocol: "TCP"},
},
},
},
})
time.Sleep(100 * time.Millisecond)
fc.Delete(&v1.Endpoints{
ObjectMeta: meta_v1.ObjectMeta{
Name: "testsvc",
Namespace: "trafficserver-test-2",
},
Subsets: []v1.EndpointSubset{
{
Addresses: []v1.EndpointAddress{
{IP: "10.10.1.1"},
{IP: "10.10.3.3"},
},
Ports: []v1.EndpointPort{
{Name: "main", Port: 8080, Protocol: "TCP"},
},
},
},
})
time.Sleep(100 * time.Millisecond)
returnedKeys := w.Ep.RedisClient.GetDefaultDBKeyValues()
expectedKeys := make(map[string][]string)
if !reflect.DeepEqual(returnedKeys, expectedKeys) {
t.Errorf("returned \n%v, but expected \n%v", returnedKeys, expectedKeys)
}
}
func TestInNamespacesWatchFor_Add(t *testing.T) {
w, _ := getTestWatcher()
cmHandler := CMHandler{"configmaps", w.Ep}
targetNs := []string{"trafficserver"}
err := w.inNamespacesWatchFor(&cmHandler, w.Cs.CoreV1().RESTClient(),
targetNs, fields.Everything(), &v1.ConfigMap{}, 0)
if err != nil {
t.Error(err)
}
w.Cs.CoreV1().ConfigMaps("trafficserver").Create(context.TODO(), &v1.ConfigMap{
ObjectMeta: meta_v1.ObjectMeta{
Name: "testsvc",
Namespace: "trafficserver",
Annotations: map[string]string{
"ats-configmap": "true",
},
},
Data: map[string]string{
"proxy.config.output.logfile.rolling_enabled": "1",
"proxy.config.output.logfile.rolling_interval_sec": "4000",
"proxy.config.restart.active_client_threshold": "2",
},
}, meta_v1.CreateOptions{})
time.Sleep(100 * time.Millisecond)
rEnabled, err := cmHandler.Ep.ATSManager.ConfigGet("proxy.config.output.logfile.rolling_enabled")
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(rEnabled, "1") {
t.Errorf("returned \n%s, but expected \n%s", rEnabled, "1")
}
rInterval, err := cmHandler.Ep.ATSManager.ConfigGet("proxy.config.output.logfile.rolling_interval_sec")
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(rInterval, "4000") {
t.Errorf("returned \n%s, but expected \n%s", rInterval, "4000")
}
threshold, err := cmHandler.Ep.ATSManager.ConfigGet("proxy.config.restart.active_client_threshold")
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(threshold, "2") {
t.Errorf("returned \n%s, but expected \n%s", threshold, "2")
}
}
func TestInNamespacesWatchFor_Update(t *testing.T) {
w, _ := getTestWatcher()
cmHandler := CMHandler{"configmaps", w.Ep}
targetNs := []string{"trafficserver"}
err := w.inNamespacesWatchFor(&cmHandler, w.Cs.CoreV1().RESTClient(),
targetNs, fields.Everything(), &v1.ConfigMap{}, 0)
if err != nil {
t.Error(err)
}
w.Cs.CoreV1().ConfigMaps("trafficserver").Create(context.TODO(), &v1.ConfigMap{
ObjectMeta: meta_v1.ObjectMeta{
Name: "testsvc",
Namespace: "trafficserver",
Annotations: map[string]string{
"ats-configmap": "true",
},
},
Data: map[string]string{
"proxy.config.output.logfile.rolling_enabled": "1",
"proxy.config.output.logfile.rolling_interval_sec": "4000",
"proxy.config.restart.active_client_threshold": "2",
},
}, meta_v1.CreateOptions{})
time.Sleep(100 * time.Millisecond)
w.Cs.CoreV1().ConfigMaps("trafficserver").Update(context.TODO(), &v1.ConfigMap{
ObjectMeta: meta_v1.ObjectMeta{
Name: "testsvc",
Namespace: "trafficserver",
Annotations: map[string]string{
"ats-configmap": "true",
},
},
Data: map[string]string{
"proxy.config.output.logfile.rolling_enabled": "1",
"proxy.config.output.logfile.rolling_interval_sec": "3000",
"proxy.config.restart.active_client_threshold": "0",
},
}, meta_v1.UpdateOptions{})
time.Sleep(100 * time.Millisecond)
rEnabled, err := cmHandler.Ep.ATSManager.ConfigGet("proxy.config.output.logfile.rolling_enabled")
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(rEnabled, "1") {
t.Errorf("returned \n%s, but expected \n%s", rEnabled, "1")
}
rInterval, err := cmHandler.Ep.ATSManager.ConfigGet("proxy.config.output.logfile.rolling_interval_sec")
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(rInterval, "3000") {
t.Errorf("returned \n%s, but expected \n%s", rInterval, "3000")
}
threshold, err := cmHandler.Ep.ATSManager.ConfigGet("proxy.config.restart.active_client_threshold")
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(threshold, "0") {
t.Errorf("returned \n%s, but expected \n%s", threshold, "0")
}
}
func TestInNamespacesWatchFor_ShouldNotAdd(t *testing.T) {
w, _ := getTestWatcher()
cmHandler := CMHandler{"configmaps", w.Ep}
targetNs := []string{"trafficserver"}
err := w.inNamespacesWatchFor(&cmHandler, w.Cs.CoreV1().RESTClient(),
targetNs, fields.Everything(), &v1.ConfigMap{}, 0)
if err != nil {
t.Error(err)
}
w.Cs.CoreV1().ConfigMaps("trafficserver").Create(context.TODO(), &v1.ConfigMap{
ObjectMeta: meta_v1.ObjectMeta{
Name: "testsvc",
Namespace: "trafficserver",
Annotations: map[string]string{
"ats-configmap": "true",
},
},
Data: map[string]string{
"proxy.config.output.logfile.rolling_enabled": "1",
"proxy.config.output.logfile.rolling_interval_sec": "4000",
"proxy.config.restart.active_client_threshold": "2",
},
}, meta_v1.CreateOptions{})
time.Sleep(100 * time.Millisecond)
w.Cs.CoreV1().ConfigMaps("trafficserver-2").Create(context.TODO(), &v1.ConfigMap{
ObjectMeta: meta_v1.ObjectMeta{
Name: "testsvc-2",
Namespace: "trafficserver-2",
},
Data: map[string]string{
"proxy.config.output.logfile.rolling_enabled": "1",
"proxy.config.output.logfile.rolling_interval_sec": "3000",
"proxy.config.restart.active_client_threshold": "4",
},
}, meta_v1.CreateOptions{})
time.Sleep(100 * time.Millisecond)
rEnabled, err := cmHandler.Ep.ATSManager.ConfigGet("proxy.config.output.logfile.rolling_enabled")
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(rEnabled, "1") {
t.Errorf("returned \n%s, but expected \n%s", rEnabled, "1")
}
rInterval, err := cmHandler.Ep.ATSManager.ConfigGet("proxy.config.output.logfile.rolling_interval_sec")
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(rInterval, "4000") {
t.Errorf("returned \n%s, but expected \n%s", rInterval, "4000")
}
threshold, err := cmHandler.Ep.ATSManager.ConfigGet("proxy.config.restart.active_client_threshold")
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(threshold, "2") {
t.Errorf("returned \n%s, but expected \n%s", threshold, "2")
}
}
// getTestWatcher returns a Watcher configured with a typed fake clientset.
// It uses createExampleEndpointWithFakeATS (assumed to exist in other test code)
// and a FakeControllerSource for the informer tests.
func getTestWatcher() (Watcher, *framework.FakeControllerSource) {
clientset := fake.NewSimpleClientset()
fc := framework.NewFakeControllerSource()
exampleEndpoint := createExampleEndpointWithFakeATS()
stopChan := make(chan struct{})
ingressWatcher := Watcher{
Cs: clientset,
ATSNamespace: "trafficserver-test-2",
Ep: &exampleEndpoint,
StopChan: stopChan,
}
return ingressWatcher, fc
}
// getTestWatcherForCache returns a Watcher configured with a fake dynamic client
// that knows the List kind for the ATSCachingPolicy resource.
func getTestWatcherForCache() (Watcher, *framework.FakeControllerSource) {
scheme := runtime.NewScheme()
gvr := schema.GroupVersionResource{
Group: "k8s.trafficserver.apache.com",
Version: "v1alpha1",
Resource: "atscachingpolicies",
}
// Map the GVR to its List kind name used by the informer reflection/listing.
gvrToListKind := map[schema.GroupVersionResource]string{
gvr: "ATSCachingPolicyList",
}
// dynamic fake client
dynClient := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(scheme, gvrToListKind)
clientset := fake.NewSimpleClientset()
fc := framework.NewFakeControllerSource()
exampleEndpoint := createExampleEndpointWithFakeATSCache()
stopChan := make(chan struct{})
ingressWatcher := Watcher{
Cs: clientset,
DynamicClient: dynClient,
ATSNamespace: "trafficserver-test-2",
Ep: &exampleEndpoint,
StopChan: stopChan,
ResyncPeriod: 0,
}
return ingressWatcher, fc
}
func filePath(t *testing.T) string {
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "cache.config")
f, err := os.Create(tmpFile)
if err != nil {
t.Fatal(err)
}
defer f.Close()
return tmpFile
}
// --- Tests that exercise WatchAtsCachingPolicy (Add/Update/Delete) ---
// Each test starts the caching-policy watcher (which attaches AtsCacheHandler),
// then creates/updates/deletes an unstructured ATSCachingPolicy CR and finally
// calls the fake ATS manager's CacheSet() to mimic the handler's reload action.
// Test Add event triggers CacheSet
func TestWatchAtsCachingPolicy_Add(t *testing.T) {
w, _ := getTestWatcherForCache()
path := filePath(t)
err := w.WatchAtsCachingPolicy(path)
if err != nil {
t.Fatalf("failed to start watcher: %v", err)
}
gvr := schema.GroupVersionResource{
Group: "k8s.trafficserver.apache.com",
Version: "v1alpha1",
Resource: "atscachingpolicies",
}
dynClient := w.DynamicClient.Resource(gvr).Namespace("default")
// Create a new caching policy
policy := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "k8s.trafficserver.apache.com/v1alpha1",
"kind": "ATSCachingPolicy",
"metadata": map[string]interface{}{
"name": "policy-add",
"namespace": "default",
},
"spec": map[string]interface{}{
"rules": []interface{}{
map[string]interface{}{
"pattern": "/images/*",
"action": "cache",
"ttl": "3600s",
},
},
},
},
}
_, err = dynClient.Create(context.TODO(), policy, meta_v1.CreateOptions{})
if err != nil {
t.Fatalf("failed to create caching policy: %v", err)
}
time.Sleep(200 * time.Millisecond)
// Verify CacheSet call worked
msg, err := w.Ep.ATSManager.CacheSet()
if err != nil {
t.Fatalf("CacheSet failed after add: %v", err)
}
if msg == "" {
t.Errorf("expected non-empty CacheSet message after add")
}
}
// Test Update event triggers CacheSet
func TestWatchAtsCachingPolicy_Update(t *testing.T) {
w, _ := getTestWatcherForCache()
path := filePath(t)
err := w.WatchAtsCachingPolicy(path)
if err != nil {
t.Fatalf("failed to start watcher: %v", err)
}
gvr := schema.GroupVersionResource{
Group: "k8s.trafficserver.apache.com",
Version: "v1alpha1",
Resource: "atscachingpolicies",
}
dynClient := w.DynamicClient.Resource(gvr).Namespace("default")
// Create a policy first
policy := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "k8s.trafficserver.apache.com/v1alpha1",
"kind": "ATSCachingPolicy",
"metadata": map[string]interface{}{
"name": "policy-update",
"namespace": "default",
},
"spec": map[string]interface{}{
"rules": []interface{}{
map[string]interface{}{
"pattern": "/images/*",
"action": "cache",
"ttl": "3600s",
},
},
},
},
}
_, err = dynClient.Create(context.TODO(), policy, meta_v1.CreateOptions{})
if err != nil {
t.Fatalf("failed to create caching policy before update: %v", err)
}
// Update the policy
policy.Object["spec"] = map[string]interface{}{
"rules": []interface{}{
map[string]interface{}{
"pattern": "/videos/*",
"action": "cache",
"ttl": "7200s",
},
},
}
_, err = dynClient.Update(context.TODO(), policy, meta_v1.UpdateOptions{})
if err != nil {
t.Fatalf("failed to update caching policy: %v", err)
}
time.Sleep(200 * time.Millisecond)
// Verify CacheSet call worked
msg, err := w.Ep.ATSManager.CacheSet()
if err != nil {
t.Fatalf("CacheSet failed after update: %v", err)
}
if msg == "" {
t.Errorf("expected non-empty CacheSet message after update")
}
}
// Test Delete event triggers CacheSet
func TestWatchAtsCachingPolicy_Delete(t *testing.T) {
w, _ := getTestWatcherForCache()
path := filePath(t)
err := w.WatchAtsCachingPolicy(path)
if err != nil {
t.Fatalf("failed to start watcher: %v", err)
}
gvr := schema.GroupVersionResource{
Group: "k8s.trafficserver.apache.com",
Version: "v1alpha1",
Resource: "atscachingpolicies",
}
dynClient := w.DynamicClient.Resource(gvr).Namespace("default")
// Create a policy first
policy := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "k8s.trafficserver.apache.com/v1alpha1",
"kind": "ATSCachingPolicy",
"metadata": map[string]interface{}{
"name": "policy-delete",
"namespace": "default",
},
"spec": map[string]interface{}{
"rules": []interface{}{
map[string]interface{}{
"pattern": "/docs/*",
"action": "cache",
"ttl": "1800s",
},
},
},
},
}
_, err = dynClient.Create(context.TODO(), policy, meta_v1.CreateOptions{})
if err != nil {
t.Fatalf("failed to create caching policy before delete: %v", err)
}
// Delete the policy
err = dynClient.Delete(context.TODO(), "policy-delete", meta_v1.DeleteOptions{})
if err != nil {
t.Fatalf("failed to delete caching policy: %v", err)
}
time.Sleep(200 * time.Millisecond)
// Verify CacheSet call worked
msg, err := w.Ep.ATSManager.CacheSet()
if err != nil {
t.Fatalf("CacheSet failed after delete: %v", err)
}
if msg == "" {
t.Errorf("expected non-empty CacheSet message after delete")
}
}
func getTestWatcherForSni() Watcher {
scheme := runtime.NewScheme()
gvr := schema.GroupVersionResource{
Group: "trafficserver.apache.org",
Version: "v1alpha1",
Resource: "atssnipolicies",
}
gvrToListKind := map[schema.GroupVersionResource]string{
gvr: "ATSSniPolicyList",
}
dynClient := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(scheme, gvrToListKind)
clientset := fake.NewSimpleClientset()
exampleEndpoint := createExampleEndpointWithFakeATSSni()
stopChan := make(chan struct{})
sniWatcher := Watcher{
Cs: clientset,
DynamicClient: dynClient,
ATSNamespace: "trafficserver-test-2",
Ep: &exampleEndpoint,
StopChan: stopChan,
ResyncPeriod: 0,
}
return sniWatcher
}
func tempSniFile(t *testing.T) string {
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "sni.yaml")
if err := os.WriteFile(tmpFile, []byte{}, 0644); err != nil {
t.Fatal(err)
}
return tmpFile
}
func newSniCR(name, fqdn string) *unstructured.Unstructured {
return &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "trafficserver.apache.org/v1alpha1",
"kind": "ATSSniPolicy",
"metadata": map[string]interface{}{
"name": name,
"namespace": "default",
},
"spec": map[string]interface{}{
"sni": []interface{}{
map[string]interface{}{
"fqdn": fqdn,
"verify_client": "STRICT",
"host_sni_policy": "PERMISSIVE",
"valid_tls_versions_in": []interface{}{
"TLSv1_2",
},
},
},
},
},
}
}
// --- TESTS ---
func TestWatchAtsSniPolicy_Add(t *testing.T) {
w := getTestWatcherForSni()
path := tempSniFile(t)
err := w.WatchAtsSniPolicy(path)
if err != nil {
t.Fatalf("failed to start watcher: %v", err)
}
gvr := schema.GroupVersionResource{
Group: "trafficserver.apache.org",
Version: "v1alpha1",
Resource: "atssnipolicies",
}
dynClient := w.DynamicClient.Resource(gvr).Namespace("default")
// Create CR
cr := newSniCR("policy-add", "ats.test.com")
_, err = dynClient.Create(context.TODO(), cr, meta_v1.CreateOptions{})
if err != nil {
t.Fatalf("failed to create SNI CR: %v", err)
}
time.Sleep(200 * time.Millisecond)
// File must contain fqdn
data, _ := os.ReadFile(path)
if !strings.Contains(string(data), "ats.test.com") {
t.Errorf("expected fqdn ats.test.com in sni.yaml, got:\n%s", string(data))
}
}
func TestWatchAtsSniPolicy_Update(t *testing.T) {
w := getTestWatcherForSni()
path := tempSniFile(t)
err := w.WatchAtsSniPolicy(path)
if err != nil {
t.Fatalf("failed to start watcher: %v", err)
}
gvr := schema.GroupVersionResource{
Group: "trafficserver.apache.org",
Version: "v1alpha1",
Resource: "atssnipolicies",
}
dynClient := w.DynamicClient.Resource(gvr).Namespace("default")
// Create CR with fqdn
cr := newSniCR("policy-update", "ats.test.com")
_, err = dynClient.Create(context.TODO(), cr, meta_v1.CreateOptions{})
if err != nil {
t.Fatalf("failed to create SNI CR: %v", err)
}
// Update CR: keep ats.test.com, add new-site.com
cr.Object["spec"] = map[string]interface{}{
"sni": []interface{}{
map[string]interface{}{
"fqdn": "ats.test.com",
"verify_client": "NONE",
"host_sni_policy": "ENFORCE",
},
map[string]interface{}{
"fqdn": "new-site.com",
"verify_client": "NONE",
"host_sni_policy": "ENFORCE",
},
},
}
_, err = dynClient.Update(context.TODO(), cr, meta_v1.UpdateOptions{})
if err != nil {
t.Fatalf("failed to update SNI CR: %v", err)
}
time.Sleep(200 * time.Millisecond)
// File should contain ats.test.com and new-site.com, but not host-test.com
data, _ := os.ReadFile(path)
content := string(data)
if !strings.Contains(content, "ats.test.com") {
t.Errorf("expected fqdn ats.test.com in sni.yaml after update, got:\n%s", content)
}
if !strings.Contains(content, "new-site.com") {
t.Errorf("expected fqdn new-site.com in sni.yaml after update, got:\n%s", content)
}
}
func TestWatchAtsSniPolicy_Delete(t *testing.T) {
w := getTestWatcherForSni()
path := tempSniFile(t)
err := w.WatchAtsSniPolicy(path)
if err != nil {
t.Fatalf("failed to start watcher: %v", err)
}
gvr := schema.GroupVersionResource{
Group: "trafficserver.apache.org",
Version: "v1alpha1",
Resource: "atssnipolicies",
}
dynClient := w.DynamicClient.Resource(gvr).Namespace("default")
// Create CR with fqdn
cr := newSniCR("policy-delete", "ats.test.com")
_, err = dynClient.Create(context.TODO(), cr, meta_v1.CreateOptions{})
if err != nil {
t.Fatalf("failed to create SNI CR: %v", err)
}
// Delete CR
err = dynClient.Delete(context.TODO(), "policy-delete", meta_v1.DeleteOptions{})
if err != nil {
t.Fatalf("failed to delete SNI CR: %v", err)
}
time.Sleep(200 * time.Millisecond)
// File should be empty, because both fqdn entries came from the deleted CR
data, _ := os.ReadFile(path)
if len(strings.TrimSpace(string(data))) != 0 {
t.Errorf("expected sni.yaml to be empty after delete, got:\n%s", string(data))
}
}