Skip to content

Conversation

@amirejaz
Copy link
Contributor

@amirejaz amirejaz commented Nov 6, 2025

Summary

This PR implements a unified workload discovery interface for vmcp's backend discovery, enabling seamless discovery of MCP server workloads from both CLI (Docker/Podman) and Kubernetes environments. The implementation uses a minimal interface pattern to avoid coupling vmcp to platform-specific workload management implementations.

Motivation

The vmcp aggregator needs to discover backend MCP servers from workloads, but the existing workload managers (workloads.Manager for CLI and k8s.Manager for Kubernetes) expose many methods that vmcp doesn't need. This PR introduces a minimal WorkloadDiscoverer interface that:

  • Minimal Interface: Only exposes methods needed for backend discovery (ListWorkloadsInGroup, GetWorkload)

  • Platform Agnostic: Works with both CLI and Kubernetes workloads through a single interface

  • No Import Cycles: Interface and implementations are in pkg/vmcp/workloads, separate from aggregator

  • Direct Implementations: Uses statuses.StatusManager for CLI and direct Kubernetes client for K8s (no dependency on workload managers)

  • Clean Architecture: Follows interface segregation principle - vmcp only depends on what it needs

Implementation

WorkloadDiscoverer Interface

Location: pkg/vmcp/workloads/discoverer.go

A minimal interface that exposes only the methods needed for backend discovery:

type Discoverer interface {
    // ListWorkloadsInGroup returns all workload names that belong to the specified group
    ListWorkloadsInGroup(ctx context.Context, groupName string) ([]string, error)
    
    // GetWorkload retrieves workload details by name and converts it to a vmcp.Backend.
    // Returns nil if the workload exists but is not accessible (e.g., no URL).
    GetWorkload(ctx context.Context, workloadName string) (*vmcp.Backend, error)
}

Direct Implementations

CLI Implementation (pkg/vmcp/workloads/cli.go):

  • Uses statuses.StatusManager directly (not workloads.Manager)

  • Converts core.Workload to vmcp.Backend

  • Maps runtime.WorkloadStatus to vmcp.BackendHealthStatus

  • Filters workloads without URLs (not accessible)

Kubernetes Implementation (pkg/vmcp/workloads/k8s.go):

  • Uses Kubernetes controller-runtime client directly (not k8s.Manager)

  • Queries MCPServer CRDs directly

  • Converts MCPServer CRD to vmcp.Backend

  • Maps mcpv1alpha1.MCPServerPhase to vmcp.BackendHealthStatus

  • Filters workloads without URLs (not accessible)

Unified Backend Discoverer

Location: pkg/vmcp/aggregator/discoverer.go

A single backendDiscoverer that works with both CLI and Kubernetes workloads through the WorkloadDiscoverer interface:

  • Factory Function: NewBackendDiscoverer() automatically detects runtime and creates the appropriate discoverer

  • Manual Creation: NewUnifiedBackendDiscoverer() accepts any WorkloadDiscoverer implementation

  • Testing Support: NewBackendDiscovererWithManager() for injecting test implementations

Package Structure

pkg/vmcp/workloads/:

  • discoverer.go - Interface definition

  • cli.go - CLI implementation (NewCLIDiscoverer)

  • k8s.go - Kubernetes implementation (NewK8SDiscoverer)

  • mocks/mock_discoverer.go - Generated mock (no import cycles)

pkg/vmcp/aggregator/:

  • discoverer.go - Unified backend discoverer implementation

  • discoverer_test.go - Tests using generated mocks

Key Features

No Import Cycles

  • Interface and implementations are in pkg/vmcp/workloads (separate from aggregator)
  • Mock generation uses reflection mode, avoiding import cycles
  • Tests can import pkg/vmcp/workloads/mocks without issues

Direct Platform Access

  • CLI: Uses statuses.StatusManager directly, bypassing workloads.Manager
  • Kubernetes: Uses Kubernetes client directly, bypassing k8s.Manager
  • Reduces dependencies and coupling

Unified Discovery

  • Single BackendDiscoverer interface for both platforms
  • Automatic runtime detection in factory function
  • Consistent behavior across platforms

Comprehensive Testing

  • All tests use generated mocks (discoverermocks.NewMockDiscoverer)
  • Consolidated test file (discoverer_test.go) with unified test cases
  • Integration tests for CLI discoverer implementation
  • Full coverage of error cases and edge conditions

Files Added

Workload Discoverer Package:

  • pkg/vmcp/workloads/discoverer.go - Interface definition

  • pkg/vmcp/workloads/cli.go - CLI implementation

  • pkg/vmcp/workloads/k8s.go - Kubernetes implementation

  • pkg/vmcp/workloads/mocks/mock_discoverer.go - Generated mock

Aggregator:

  • pkg/vmcp/aggregator/discoverer.go - Unified backend discoverer

  • pkg/vmcp/aggregator/discoverer_test.go - Consolidated tests

Files Modified

  • pkg/vmcp/aggregator/discoverer.go - Updated to use workloads.Discoverer interface
  • cmd/vmcp/app/commands.go - Updated to use aggregator.NewBackendDiscoverer()

Files Removed

  • pkg/vmcp/aggregator/workload_discoverer.go - Moved to pkg/vmcp/workloads/discoverer.go
  • pkg/vmcp/aggregator/cli_workload_discoverer.go - Moved to pkg/vmcp/workloads/cli.go
  • pkg/vmcp/aggregator/k8s_workload_discoverer.go - Moved to pkg/vmcp/workloads/k8s.go
  • pkg/vmcp/aggregator/cli_discoverer_test.go - Consolidated into discoverer_test.go
  • pkg/vmcp/aggregator/k8s_discoverer_test.go - Consolidated into discoverer_test.go
  • pkg/vmcp/aggregator/mocks/mock_workload_discoverer.go - Replaced by pkg/vmcp/workloads/mocks/mock_discoverer.go

Benefits

  1. Minimal Interface: Only exposes what vmcp needs, following interface segregation principle
  2. No Import Cycles: Clean package structure with interface and implementations in separate package
  3. Direct Access: Bypasses workload managers, reducing dependencies
  4. Unified Discovery: Single discoverer works with both platforms
  5. Better Testing: Uses generated mocks without import cycle issues
  6. Maintainability: Clear separation of concerns, easier to extend
  7. Reusability: WorkloadDiscoverer interface can be used by other components

Testing

  • All unit tests pass
  • Linting passes
  • No import cycles
  • Tests use generated mocks successfully
  • Verified CLI workload discovery
  • Verified Kubernetes workload discovery
  • Verified unified discoverer with both platforms
  • Tested error handling and edge cases

Design Decisions

Why a Separate Package?

  1. Avoid Import Cycles: Moving interface to pkg/vmcp/workloads prevents cycles with aggregator package
  2. Better Organization: Interface and implementations together in one package
  3. Reusability: Other components can use WorkloadDiscoverer without importing aggregator

Why Direct Implementations?

  1. Reduced Dependencies: Don't need full workloads.Manager or k8s.Manager interfaces
  2. Simpler: Direct access to what we need (statuses.StatusManager, Kubernetes client)
  3. Performance: Fewer layers of indirection

Why Minimal Interface?

  1. Interface Segregation: vmcp only depends on what it needs
  2. Flexibility: Different implementations can have different internal structures
  3. Testability: Easier to mock and test

Why Consolidated Tests?

  1. No Duplication: Both platforms test the same unified discoverer
  2. Easier Maintenance: Single test file to maintain
  3. Better Coverage: Tests cover the unified behavior, not platform-specific details

Related

  • Follows interface segregation principle
  • Enables unified backend discovery in vmcp aggregator
  • Prepares foundation for future workload discovery features
  • Integrates with ToolHive operator for Kubernetes workload discovery

Example Usage

Automatic Runtime Detection:

// Automatically detects CLI or Kubernetes and creates appropriate discoverer
discoverer, err := aggregator.NewBackendDiscoverer(ctx, groupsManager, authConfig)
if err != nil {
    return err
}

backends, err := discoverer.Discover(ctx, "engineering-team")

Manual Creation (Testing):

// Create discoverer with mock for testing
mockDiscoverer := discoverermocks.NewMockDiscoverer(ctrl)
discoverer := aggregator.NewUnifiedBackendDiscoverer(mockDiscoverer, groupsManager, authConfig)
backends, err := discoverer.Discover(ctx, "engineering-team")

Direct Implementation Usage:

// CLI mode
statusManager, _ := statuses.NewStatusManager(runtime)
cliDiscoverer := workloads.NewCLIDiscoverer(statusManager)

// Kubernetes mode
k8sDiscoverer, _ := workloads.NewK8SDiscoverer()

// Use with unified discoverer
discoverer := aggregator.NewUnifiedBackendDiscoverer(cliDiscoverer, groupsManager, authConfig)

implements #169

@amirejaz amirejaz marked this pull request as draft November 6, 2025 16:45
@codecov
Copy link

codecov bot commented Nov 6, 2025

Codecov Report

❌ Patch coverage is 31.02041% with 169 lines in your changes missing coverage. Please review.
✅ Project coverage is 55.58%. Comparing base (4ceed40) to head (07698d2).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
pkg/vmcp/workloads/k8s.go 0.00% 102 Missing ⚠️
pkg/workloads/manager.go 39.47% 46 Missing ⚠️
pkg/vmcp/aggregator/discoverer.go 75.40% 15 Missing ⚠️
cmd/vmcp/app/commands.go 0.00% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2487      +/-   ##
==========================================
- Coverage   55.84%   55.58%   -0.27%     
==========================================
  Files         312      312              
  Lines       29541    29658     +117     
==========================================
- Hits        16498    16485      -13     
- Misses      11601    11733     +132     
+ Partials     1442     1440       -2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Member

@dmjb dmjb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Marking this as "request changes" until we figure out how to split up this interface.

@amirejaz amirejaz marked this pull request as ready for review November 12, 2025 15:09
@amirejaz amirejaz requested review from dmjb and jhrozek November 12, 2025 15:09
@amirejaz amirejaz requested a review from JAORMX November 13, 2025 10:05
@amirejaz amirejaz changed the title Unified workload manager # Unified Workload Discovery for vmcp Nov 17, 2025
@amirejaz amirejaz changed the title # Unified Workload Discovery for vmcp Unified Workload Discovery for vmcp Nov 17, 2025
@amirejaz amirejaz requested review from dmjb and yrobla November 17, 2025 11:46
@amirejaz amirejaz merged commit a85cec7 into main Nov 17, 2025
31 checks passed
@amirejaz amirejaz deleted the unified-workload-manager branch November 17, 2025 19:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants