Skip to content

Commit 64af2b6

Browse files
authored
Split Runtime interface between Deployer and Runtime (#1060)
As we split apart the k8s proxy runner from the CLI/API code, we need to decouple the code in runner and transport from concerns which are not relevant to k8s. As a first step, split the runtime interface in two: a Deployer interface which contains the subset of Runtime needed for the code in runner and transport, and leave the other methods in Runtime. Runtime is altered to extend from Deployer in order to minimize changes in the code, but in future we could decoupe them further to allow for implementations of Runtime if it makes sense to do so.
1 parent a749c54 commit 64af2b6

File tree

14 files changed

+167
-74
lines changed

14 files changed

+167
-74
lines changed

pkg/container/docker/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const (
4141
LabelValueTrue = "true"
4242
)
4343

44-
// Client implements the Runtime interface for Docker (and compatible runtimes)
44+
// Client implements the Deployer interface for Docker (and compatible runtimes)
4545
type Client struct {
4646
runtimeType runtime.Type
4747
socketPath string

pkg/container/kubernetes/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const (
4242
mcpContainerName = "mcp"
4343
)
4444

45-
// Client implements the Runtime interface for container operations
45+
// Client implements the Deployer interface for container operations
4646
type Client struct {
4747
runtimeType runtime.Type
4848
client kubernetes.Interface

pkg/container/runtime/mocks/mock_runtime.go

Lines changed: 85 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/container/runtime/types.go

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,10 @@ type PortMapping struct {
4141
Protocol string
4242
}
4343

44-
// Runtime defines the interface for container runtimes that manage workloads.
45-
//
46-
// A workload in ToolHive represents a complete deployment unit that may consist of:
47-
// - Primary MCP server container
48-
// - Sidecar containers (for logging, monitoring, proxying, etc.)
49-
// - Network configurations and port mappings
50-
// - Volume mounts and storage
51-
// - Service discovery and load balancing components
52-
// - Security policies and permission profiles
53-
//
54-
// This is a departure from simple container management, as modern deployments
55-
// often require orchestrating multiple interconnected components that work
56-
// together to provide a complete service.
57-
//
58-
//go:generate mockgen -destination=mocks/mock_runtime.go -package=mocks -source=types.go Runtime
59-
type Runtime interface {
44+
// Deployer contains the methods to start and stop a workload.
45+
// This is intended as a subset of the Runtime interface for
46+
// the runner code.
47+
type Deployer interface {
6048
// DeployWorkload creates and starts a complete workload deployment.
6149
// This includes the primary container, any required sidecars, networking setup,
6250
// volume mounts, and service configuration. The workload is started as part
@@ -86,16 +74,45 @@ type Runtime interface {
8674
isolateNetwork bool,
8775
) (string, int, error)
8876

89-
// ListWorkloads lists all deployed workloads managed by this runtime.
90-
// Returns information about each workload including its components,
91-
// status, and resource usage.
92-
ListWorkloads(ctx context.Context) ([]ContainerInfo, error)
93-
9477
// StopWorkload gracefully stops a running workload and all its components.
9578
// This includes stopping the primary container, sidecars, and cleaning up
9679
// any associated network resources. The workload remains available for restart.
9780
StopWorkload(ctx context.Context, workloadID string) error
9881

82+
// AttachToWorkload establishes a direct connection to the primary container
83+
// of the workload for interactive communication. This is typically used
84+
// for stdio transport where direct input/output streaming is required.
85+
AttachToWorkload(ctx context.Context, workloadID string) (io.WriteCloser, io.ReadCloser, error)
86+
87+
// IsWorkloadRunning checks if a workload is currently running and healthy.
88+
// This verifies that the primary container is running and that any
89+
// required sidecars are also operational.
90+
IsWorkloadRunning(ctx context.Context, workloadID string) (bool, error)
91+
}
92+
93+
// Runtime defines the interface for container runtimes that manage workloads.
94+
//
95+
// A workload in ToolHive represents a complete deployment unit that may consist of:
96+
// - Primary MCP server container
97+
// - Sidecar containers (for logging, monitoring, proxying, etc.)
98+
// - Network configurations and port mappings
99+
// - Volume mounts and storage
100+
// - Service discovery and load balancing components
101+
// - Security policies and permission profiles
102+
//
103+
// This is a departure from simple container management, as modern deployments
104+
// often require orchestrating multiple interconnected components that work
105+
// together to provide a complete service.
106+
//
107+
//go:generate mockgen -destination=mocks/mock_runtime.go -package=mocks -source=types.go Runtime
108+
type Runtime interface {
109+
Deployer
110+
111+
// ListWorkloads lists all deployed workloads managed by this runtime.
112+
// Returns information about each workload including its components,
113+
// status, and resource usage.
114+
ListWorkloads(ctx context.Context) ([]ContainerInfo, error)
115+
99116
// RemoveWorkload completely removes a workload and all its components.
100117
// This includes removing containers, cleaning up networks, volumes,
101118
// and any other resources associated with the workload. This operation
@@ -108,21 +125,11 @@ type Runtime interface {
108125
// main MCP server container.
109126
GetWorkloadLogs(ctx context.Context, workloadID string, follow bool) (string, error)
110127

111-
// IsWorkloadRunning checks if a workload is currently running and healthy.
112-
// This verifies that the primary container is running and that any
113-
// required sidecars are also operational.
114-
IsWorkloadRunning(ctx context.Context, workloadID string) (bool, error)
115-
116128
// GetWorkloadInfo retrieves detailed information about a workload.
117129
// This includes status, resource usage, network configuration,
118130
// and metadata about all components in the workload.
119131
GetWorkloadInfo(ctx context.Context, workloadID string) (ContainerInfo, error)
120132

121-
// AttachToWorkload establishes a direct connection to the primary container
122-
// of the workload for interactive communication. This is typically used
123-
// for stdio transport where direct input/output streaming is required.
124-
AttachToWorkload(ctx context.Context, workloadID string) (io.WriteCloser, io.ReadCloser, error)
125-
126133
// IsRunning checks the health of the container runtime.
127134
// This is used to verify that the runtime is operational and can manage workloads.
128135
IsRunning(ctx context.Context) error

pkg/runner/config.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ type RunConfig struct {
101101
// Only applicable when using Kubernetes runtime
102102
K8sPodTemplatePatch string `json:"k8s_pod_template_patch,omitempty" yaml:"k8s_pod_template_patch,omitempty"`
103103

104-
// Runtime is the container runtime to use (not serialized)
105-
Runtime rt.Runtime `json:"-" yaml:"-"`
104+
// Deployer is the container runtime to use (not serialized)
105+
Deployer rt.Deployer `json:"-" yaml:"-"`
106106

107107
// IsolateNetwork indicates whether to isolate the network for the container
108108
IsolateNetwork bool `json:"isolate_network,omitempty" yaml:"isolate_network,omitempty"`
@@ -148,7 +148,7 @@ func NewRunConfig() *RunConfig {
148148
// NewRunConfigFromFlags creates a new RunConfig with values from command-line flags
149149
func NewRunConfigFromFlags(
150150
ctx context.Context,
151-
runtime rt.Runtime,
151+
runtime rt.Deployer,
152152
cmdArgs []string,
153153
name string,
154154
imageURL string,

pkg/runner/config_builder.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ func NewRunConfigBuilder() *RunConfigBuilder {
3939
}
4040

4141
// WithRuntime sets the container runtime
42-
func (b *RunConfigBuilder) WithRuntime(runtime rt.Runtime) *RunConfigBuilder {
43-
b.config.Runtime = runtime
42+
func (b *RunConfigBuilder) WithRuntime(deployer rt.Deployer) *RunConfigBuilder {
43+
b.config.Deployer = deployer
4444
return b
4545
}
4646

pkg/runner/config_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,7 @@ func TestNewRunConfigFromFlags(t *testing.T) {
631631
require.NoError(t, err, "NewRunConfigFromFlags should not return an error")
632632

633633
assert.NotNil(t, config, "NewRunConfigFromFlags should return a non-nil config")
634-
assert.Equal(t, runtime, config.Runtime, "Runtime should match")
634+
assert.Equal(t, runtime, config.Deployer, "Deployer should match")
635635
assert.Equal(t, targetHost, config.TargetHost, "TargetHost should match")
636636
// The metadata args are appended to the command-line args
637637
expectedCmdArgs := append([]string{}, cmdArgs...)

pkg/runner/runner.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func (r *Runner) Run(ctx context.Context) error {
4949
TargetPort: r.Config.TargetPort,
5050
Host: r.Config.Host,
5151
TargetHost: r.Config.TargetHost,
52-
Runtime: r.Config.Runtime,
52+
Deployer: r.Config.Deployer,
5353
Debug: r.Config.Debug,
5454
}
5555

@@ -163,7 +163,7 @@ func (r *Runner) Run(ctx context.Context) error {
163163
// Set up the transport
164164
logger.Infof("Setting up %s transport...", r.Config.Transport)
165165
if err := transportHandler.Setup(
166-
ctx, r.Config.Runtime, r.Config.ContainerName, r.Config.Image, r.Config.CmdArgs,
166+
ctx, r.Config.Deployer, r.Config.ContainerName, r.Config.Image, r.Config.CmdArgs,
167167
r.Config.EnvVars, r.Config.ContainerLabels, r.Config.PermissionProfile, r.Config.K8sPodTemplatePatch,
168168
r.Config.IsolateNetwork,
169169
); err != nil {

pkg/transport/errors.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ var (
1313
ErrTransportNotStarted = errors.New("transport not started")
1414
ErrTransportClosed = errors.New("transport closed")
1515
ErrInvalidMessage = errors.New("invalid message")
16-
ErrRuntimeNotSet = errors.New("container runtime not set")
16+
ErrRuntimeNotSet = errors.New("container deployer not set")
1717
ErrContainerIDNotSet = errors.New("container ID not set")
1818
ErrContainerNameNotSet = errors.New("container name not set")
1919
)

pkg/transport/factory.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ func (*Factory) Create(config types.Config) (types.Transport, error) {
2020
switch config.Type {
2121
case types.TransportTypeStdio:
2222
tr := NewStdioTransport(
23-
config.Host, config.ProxyPort, config.Runtime, config.Debug, config.PrometheusHandler, config.Middlewares...)
23+
config.Host, config.ProxyPort, config.Deployer, config.Debug, config.PrometheusHandler, config.Middlewares...,
24+
)
2425
tr.SetProxyMode(config.ProxyMode)
2526
return tr, nil
2627
case types.TransportTypeSSE:
@@ -29,7 +30,7 @@ func (*Factory) Create(config types.Config) (types.Transport, error) {
2930
config.Host,
3031
config.ProxyPort,
3132
config.TargetPort,
32-
config.Runtime,
33+
config.Deployer,
3334
config.Debug,
3435
config.TargetHost,
3536
config.PrometheusHandler,
@@ -41,7 +42,7 @@ func (*Factory) Create(config types.Config) (types.Transport, error) {
4142
config.Host,
4243
config.ProxyPort,
4344
config.TargetPort,
44-
config.Runtime,
45+
config.Deployer,
4546
config.Debug,
4647
config.TargetHost,
4748
config.PrometheusHandler,

0 commit comments

Comments
 (0)