Fix Docker Swarm discovery network name mismatch

- Changed NetworkName from 'chorus_default' to 'chorus_net'
- This matches the actual network 'CHORUS_chorus_net' (service prefix added automatically)
- Fixes discovered_count:0 issue - now successfully discovering all 25 agents
- Updated IMPLEMENTATION-SUMMARY with deployment status

Result: All 25 CHORUS agents now discovered successfully via Docker Swarm API

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Code
2025-10-10 10:35:25 +11:00
parent 2826b28645
commit 9aeaa433fc
36 changed files with 4721 additions and 2213 deletions

View File

@@ -0,0 +1,136 @@
package licensing
import (
"sync"
"time"
"github.com/google/uuid"
)
// CacheEntry holds cached license validation data
type CacheEntry struct {
Response *ValidationResponse
ExpiresAt time.Time
}
// LicenseCache provides in-memory caching for license validations
type LicenseCache struct {
mu sync.RWMutex
entries map[string]*CacheEntry
ttl time.Duration
}
// NewLicenseCache creates a new license cache with specified TTL
func NewLicenseCache(ttl time.Duration) *LicenseCache {
cache := &LicenseCache{
entries: make(map[string]*CacheEntry),
ttl: ttl,
}
// Start cleanup goroutine
go cache.cleanup()
return cache
}
// Get retrieves cached validation response if available and not expired
func (c *LicenseCache) Get(deploymentID uuid.UUID, feature string) *ValidationResponse {
c.mu.RLock()
defer c.mu.RUnlock()
key := c.cacheKey(deploymentID, feature)
entry, exists := c.entries[key]
if !exists || time.Now().After(entry.ExpiresAt) {
return nil
}
return entry.Response
}
// Set stores validation response in cache with TTL
func (c *LicenseCache) Set(deploymentID uuid.UUID, feature string, response *ValidationResponse) {
c.mu.Lock()
defer c.mu.Unlock()
key := c.cacheKey(deploymentID, feature)
c.entries[key] = &CacheEntry{
Response: response,
ExpiresAt: time.Now().Add(c.ttl),
}
}
// Invalidate removes specific cache entry
func (c *LicenseCache) Invalidate(deploymentID uuid.UUID, feature string) {
c.mu.Lock()
defer c.mu.Unlock()
key := c.cacheKey(deploymentID, feature)
delete(c.entries, key)
}
// InvalidateAll removes all cached entries for a deployment
func (c *LicenseCache) InvalidateAll(deploymentID uuid.UUID) {
c.mu.Lock()
defer c.mu.Unlock()
prefix := deploymentID.String() + ":"
for key := range c.entries {
if len(key) > len(prefix) && key[:len(prefix)] == prefix {
delete(c.entries, key)
}
}
}
// Clear removes all cached entries
func (c *LicenseCache) Clear() {
c.mu.Lock()
defer c.mu.Unlock()
c.entries = make(map[string]*CacheEntry)
}
// Stats returns cache statistics
func (c *LicenseCache) Stats() map[string]interface{} {
c.mu.RLock()
defer c.mu.RUnlock()
totalEntries := len(c.entries)
expiredEntries := 0
now := time.Now()
for _, entry := range c.entries {
if now.After(entry.ExpiresAt) {
expiredEntries++
}
}
return map[string]interface{}{
"total_entries": totalEntries,
"expired_entries": expiredEntries,
"active_entries": totalEntries - expiredEntries,
"ttl_seconds": int(c.ttl.Seconds()),
}
}
// cacheKey generates cache key from deployment ID and feature
func (c *LicenseCache) cacheKey(deploymentID uuid.UUID, feature string) string {
return deploymentID.String() + ":" + feature
}
// cleanup removes expired entries periodically
func (c *LicenseCache) cleanup() {
ticker := time.NewTicker(c.ttl / 2) // Clean up twice as often as TTL
defer ticker.Stop()
for range ticker.C {
c.mu.Lock()
now := time.Now()
for key, entry := range c.entries {
if now.After(entry.ExpiresAt) {
delete(c.entries, key)
}
}
c.mu.Unlock()
}
}