178 lines
4.7 KiB
Go
178 lines
4.7 KiB
Go
package serviceadapter
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"time"
|
|
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
"k8s.io/client-go/dynamic"
|
|
"k8s.io/client-go/dynamic/dynamicinformer"
|
|
"k8s.io/client-go/tools/cache"
|
|
)
|
|
|
|
// InformerManager watches ServiceAdapter resources and populates the registry.
|
|
type InformerManager struct {
|
|
registry *Registry
|
|
informer cache.SharedIndexInformer
|
|
stopChan chan struct{}
|
|
factory dynamicinformer.DynamicSharedInformerFactory
|
|
}
|
|
|
|
// NewInformerManager creates a new informer that watches ServiceAdapters in the api namespace.
|
|
func NewInformerManager(dynamicClient dynamic.Interface, registry *Registry, namespace string) (*InformerManager, error) {
|
|
if registry == nil {
|
|
return nil, fmt.Errorf("registry cannot be nil")
|
|
}
|
|
|
|
if namespace == "" {
|
|
namespace = "api"
|
|
}
|
|
|
|
// ServiceAdapter GVR
|
|
gvr := schema.GroupVersionResource{
|
|
Group: "gateway.riotpiao.com",
|
|
Version: "v1",
|
|
Resource: "serviceadapters",
|
|
}
|
|
|
|
// Create informer factory scoped to namespace
|
|
factory := dynamicinformer.NewFilteredDynamicSharedInformerFactory(
|
|
dynamicClient,
|
|
30*time.Second,
|
|
namespace,
|
|
nil,
|
|
)
|
|
|
|
// Get the informer for ServiceAdapters
|
|
informer := factory.ForResource(gvr).Informer()
|
|
|
|
// Add event handlers
|
|
_, err := informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
|
AddFunc: func(obj interface{}) {
|
|
unstructObj := obj.(*unstructured.Unstructured)
|
|
if adapter, err := parseServiceAdapter(unstructObj); err == nil {
|
|
if registry.Add(adapter) == nil {
|
|
log.Printf("[INFO] ServiceAdapter added: %s/%s", adapter.Namespace, adapter.ServiceName)
|
|
}
|
|
}
|
|
},
|
|
UpdateFunc: func(oldObj, newObj interface{}) {
|
|
unstructObj := newObj.(*unstructured.Unstructured)
|
|
if adapter, err := parseServiceAdapter(unstructObj); err == nil {
|
|
if registry.Update(adapter) == nil {
|
|
log.Printf("[INFO] ServiceAdapter updated: %s/%s", adapter.Namespace, adapter.ServiceName)
|
|
}
|
|
}
|
|
},
|
|
DeleteFunc: func(obj interface{}) {
|
|
unstructObj, ok := obj.(*unstructured.Unstructured)
|
|
if !ok {
|
|
// Handle tombstone (for objects that were deleted)
|
|
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
|
|
if !ok {
|
|
return
|
|
}
|
|
unstructObj, ok = tombstone.Obj.(*unstructured.Unstructured)
|
|
if !ok {
|
|
return
|
|
}
|
|
}
|
|
|
|
if name, ok := unstructObj.Object["metadata"].(map[string]interface{})["name"]; ok {
|
|
registry.Delete(name.(string))
|
|
log.Printf("[INFO] ServiceAdapter deleted: %s", name)
|
|
}
|
|
},
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &InformerManager{
|
|
registry: registry,
|
|
informer: informer,
|
|
stopChan: make(chan struct{}),
|
|
factory: factory,
|
|
}, nil
|
|
}
|
|
|
|
// Start begins watching ServiceAdapter resources.
|
|
func (im *InformerManager) Start(ctx context.Context) error {
|
|
im.factory.Start(im.stopChan)
|
|
|
|
// Wait for informer cache to sync
|
|
if !cache.WaitForCacheSync(im.stopChan, im.informer.HasSynced) {
|
|
return fmt.Errorf("failed to sync ServiceAdapter informer cache")
|
|
}
|
|
|
|
log.Printf("[INFO] ServiceAdapter informer started, synced from cluster")
|
|
return nil
|
|
}
|
|
|
|
// Stop stops watching ServiceAdapter resources.
|
|
func (im *InformerManager) Stop() {
|
|
close(im.stopChan)
|
|
}
|
|
|
|
// parseServiceAdapter converts an unstructured object to ServiceAdapter.
|
|
func parseServiceAdapter(unstructured *unstructured.Unstructured) (*ServiceAdapter, error) {
|
|
metadata, ok := unstructured.Object["metadata"].(map[string]interface{})
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid metadata")
|
|
}
|
|
|
|
name, ok := metadata["name"].(string)
|
|
if !ok {
|
|
return nil, fmt.Errorf("missing metadata.name")
|
|
}
|
|
|
|
namespace, ok := metadata["namespace"].(string)
|
|
if !ok {
|
|
namespace = "default"
|
|
}
|
|
|
|
spec, ok := unstructured.Object["spec"].(map[string]interface{})
|
|
if !ok {
|
|
return nil, fmt.Errorf("missing spec")
|
|
}
|
|
|
|
// Parse spec fields (simplified - in real implementation would use JSON unmarshaling)
|
|
serviceName, ok := spec["serviceName"].(string)
|
|
if !ok {
|
|
return nil, fmt.Errorf("missing serviceName")
|
|
}
|
|
|
|
adapter := &ServiceAdapter{
|
|
Name: name,
|
|
Namespace: namespace,
|
|
ServiceName: serviceName,
|
|
CreatedAt: time.Now(),
|
|
}
|
|
|
|
// Parse upstream if present
|
|
if upstreamMap, ok := spec["upstream"].(map[string]interface{}); ok {
|
|
if url, ok := upstreamMap["url"].(string); ok {
|
|
adapter.Spec.Upstream.URL = url
|
|
}
|
|
if timeout, ok := upstreamMap["timeoutSeconds"].(float64); ok {
|
|
adapter.Spec.Upstream.TimeoutSeconds = int32(timeout)
|
|
}
|
|
}
|
|
|
|
// Parse auth if present
|
|
if authMap, ok := spec["auth"].(map[string]interface{}); ok {
|
|
if required, ok := authMap["required"].(bool); ok {
|
|
adapter.Spec.Auth.Required = required
|
|
}
|
|
if capability, ok := authMap["capability"].(string); ok {
|
|
adapter.Spec.Auth.Capability = capability
|
|
}
|
|
}
|
|
|
|
return adapter, nil
|
|
}
|