diff --git a/cmd/thv-operator/api/v1alpha1/embeddingserver_types.go b/cmd/thv-operator/api/v1alpha1/embeddingserver_types.go index 3f82b15e07..ea9e2244ba 100644 --- a/cmd/thv-operator/api/v1alpha1/embeddingserver_types.go +++ b/cmd/thv-operator/api/v1alpha1/embeddingserver_types.go @@ -4,6 +4,7 @@ package v1alpha1 import ( + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -77,7 +78,7 @@ type EmbeddingServerSpec struct { // +listType=map // +listMapKey=name // +optional - Env []EnvVar `json:"env,omitempty"` + Env []corev1.EnvVar `json:"env,omitempty"` // Resources defines compute resources for the embedding server // +optional diff --git a/cmd/thv-operator/api/v1alpha1/mcpserver_types.go b/cmd/thv-operator/api/v1alpha1/mcpserver_types.go index 277046e078..3dc7d0d48e 100644 --- a/cmd/thv-operator/api/v1alpha1/mcpserver_types.go +++ b/cmd/thv-operator/api/v1alpha1/mcpserver_types.go @@ -236,7 +236,7 @@ type MCPServerSpec struct { // +listType=map // +listMapKey=name // +optional - Env []EnvVar `json:"env,omitempty"` + Env []corev1.EnvVar `json:"env,omitempty"` // Volumes are volumes to mount in the MCP server container // +listType=map @@ -408,7 +408,7 @@ type ProxyDeploymentOverrides struct { // +listType=map // +listMapKey=name // +optional - Env []EnvVar `json:"env,omitempty"` + Env []corev1.EnvVar `json:"env,omitempty"` // ImagePullSecrets allows specifying image pull secrets for the proxy runner // These are applied to both the Deployment and the ServiceAccount @@ -429,15 +429,15 @@ type ResourceMetadataOverrides struct { } // EnvVar represents an environment variable in a container -type EnvVar struct { - // Name of the environment variable - // +kubebuilder:validation:Required - Name string `json:"name"` - - // Value of the environment variable - // +kubebuilder:validation:Required - Value string `json:"value"` -} +// type EnvVar struct { +// // Name of the environment variable +// // +kubebuilder:validation:Required +// Name string `json:"name"` + +// // Value of the environment variable +// // +kubebuilder:validation:Required +// Value string `json:"value"` +// } // Volume represents a volume to mount in a container type Volume struct { diff --git a/cmd/thv-operator/api/v1alpha1/zz_generated.deepcopy.go b/cmd/thv-operator/api/v1alpha1/zz_generated.deepcopy.go index e278931442..125d6d5060 100644 --- a/cmd/thv-operator/api/v1alpha1/zz_generated.deepcopy.go +++ b/cmd/thv-operator/api/v1alpha1/zz_generated.deepcopy.go @@ -21,9 +21,9 @@ limitations under the License. package v1alpha1 import ( - corev1 "k8s.io/api/core/v1" + "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -174,7 +174,7 @@ func (in *CABundleSource) DeepCopyInto(out *CABundleSource) { *out = *in if in.ConfigMapRef != nil { in, out := &in.ConfigMapRef, &out.ConfigMapRef - *out = new(corev1.ConfigMapKeySelector) + *out = new(v1.ConfigMapKeySelector) (*in).DeepCopyInto(*out) } } @@ -385,8 +385,10 @@ func (in *EmbeddingServerSpec) DeepCopyInto(out *EmbeddingServerSpec) { } if in.Env != nil { in, out := &in.Env, &out.Env - *out = make([]EnvVar, len(*in)) - copy(*out, *in) + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } out.Resources = in.Resources if in.ModelCache != nil { @@ -426,7 +428,7 @@ func (in *EmbeddingServerStatus) DeepCopyInto(out *EmbeddingServerStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -464,21 +466,6 @@ func (in *EmbeddingStatefulSetOverrides) DeepCopy() *EmbeddingStatefulSetOverrid return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvVar) DeepCopyInto(out *EnvVar) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvVar. -func (in *EnvVar) DeepCopy() *EnvVar { - if in == nil { - return nil - } - out := new(EnvVar) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExternalAuthConfigRef) DeepCopyInto(out *ExternalAuthConfigRef) { *out = *in @@ -817,7 +804,7 @@ func (in *MCPExternalAuthConfigStatus) DeepCopyInto(out *MCPExternalAuthConfigSt *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -933,7 +920,7 @@ func (in *MCPGroupStatus) DeepCopyInto(out *MCPGroupStatus) { } if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1059,7 +1046,7 @@ func (in *MCPOIDCConfigStatus) DeepCopyInto(out *MCPOIDCConfigStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1159,7 +1146,7 @@ func (in *MCPRegistrySpec) DeepCopyInto(out *MCPRegistrySpec) { } if in.PGPassSecretRef != nil { in, out := &in.PGPassSecretRef, &out.PGPassSecretRef - *out = new(corev1.SecretKeySelector) + *out = new(v1.SecretKeySelector) (*in).DeepCopyInto(*out) } if in.PodTemplateSpec != nil { @@ -1184,7 +1171,7 @@ func (in *MCPRegistryStatus) DeepCopyInto(out *MCPRegistryStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1336,7 +1323,7 @@ func (in *MCPRemoteProxyStatus) DeepCopyInto(out *MCPRemoteProxyStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1474,7 +1461,7 @@ func (in *MCPServerEntryStatus) DeepCopyInto(out *MCPServerEntryStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1533,8 +1520,10 @@ func (in *MCPServerSpec) DeepCopyInto(out *MCPServerSpec) { } if in.Env != nil { in, out := &in.Env, &out.Env - *out = make([]EnvVar, len(*in)) - copy(*out, *in) + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } if in.Volumes != nil { in, out := &in.Volumes, &out.Volumes @@ -1649,7 +1638,7 @@ func (in *MCPServerStatus) DeepCopyInto(out *MCPServerStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1770,7 +1759,7 @@ func (in *MCPTelemetryConfigStatus) DeepCopyInto(out *MCPTelemetryConfigStatus) *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1932,7 +1921,7 @@ func (in *MCPToolConfigStatus) DeepCopyInto(out *MCPToolConfigStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -2272,12 +2261,14 @@ func (in *ProxyDeploymentOverrides) DeepCopyInto(out *ProxyDeploymentOverrides) } if in.Env != nil { in, out := &in.Env, &out.Env - *out = make([]EnvVar, len(*in)) - copy(*out, *in) + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } if in.ImagePullSecrets != nil { in, out := &in.ImagePullSecrets, &out.ImagePullSecrets - *out = make([]corev1.LocalObjectReference, len(*in)) + *out = make([]v1.LocalObjectReference, len(*in)) copy(*out, *in) } } @@ -2999,7 +2990,7 @@ func (in *VirtualMCPCompositeToolDefinitionStatus) DeepCopyInto(out *VirtualMCPC } if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -3136,7 +3127,7 @@ func (in *VirtualMCPServerStatus) DeepCopyInto(out *VirtualMCPServerStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } diff --git a/cmd/thv-operator/controllers/mcpremoteproxy_deployment_test.go b/cmd/thv-operator/controllers/mcpremoteproxy_deployment_test.go index f4105195c6..482b19cc40 100644 --- a/cmd/thv-operator/controllers/mcpremoteproxy_deployment_test.go +++ b/cmd/thv-operator/controllers/mcpremoteproxy_deployment_test.go @@ -136,7 +136,7 @@ func TestDeploymentForMCPRemoteProxy(t *testing.T) { "custom-annotation": "custom-annotation-value", }, }, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "CUSTOM_ENV", Value: "custom-value"}, {Name: "TOOLHIVE_DEBUG", Value: "true"}, }, diff --git a/cmd/thv-operator/controllers/mcpserver_controller.go b/cmd/thv-operator/controllers/mcpserver_controller.go index 68c6f96d47..f7a4a957f1 100644 --- a/cmd/thv-operator/controllers/mcpserver_controller.go +++ b/cmd/thv-operator/controllers/mcpserver_controller.go @@ -1149,12 +1149,7 @@ func (r *MCPServerReconciler) deploymentForMCPServer( // Add user-specified proxy environment variables from ResourceOverrides if m.Spec.ResourceOverrides != nil && m.Spec.ResourceOverrides.ProxyDeployment != nil { - for _, envVar := range m.Spec.ResourceOverrides.ProxyDeployment.Env { - env = append(env, corev1.EnvVar{ - Name: envVar.Name, - Value: envVar.Value, - }) - } + env = append(env, m.Spec.ResourceOverrides.ProxyDeployment.Env...) } // Add volume mounts for user-defined volumes @@ -1762,12 +1757,7 @@ func (r *MCPServerReconciler) deploymentNeedsUpdate( // Add user-specified environment variables if mcpServer.Spec.ResourceOverrides != nil && mcpServer.Spec.ResourceOverrides.ProxyDeployment != nil { - for _, envVar := range mcpServer.Spec.ResourceOverrides.ProxyDeployment.Env { - expectedProxyEnv = append(expectedProxyEnv, corev1.EnvVar{ - Name: envVar.Name, - Value: envVar.Value, - }) - } + expectedProxyEnv = append(expectedProxyEnv, mcpServer.Spec.ResourceOverrides.ProxyDeployment.Env...) } // Add default environment variables that are always injected expectedProxyEnv = ctrlutil.EnsureRequiredEnvVars(ctx, expectedProxyEnv) diff --git a/cmd/thv-operator/controllers/mcpserver_resource_overrides_test.go b/cmd/thv-operator/controllers/mcpserver_resource_overrides_test.go index ba6e516752..9f8d1fb61b 100644 --- a/cmd/thv-operator/controllers/mcpserver_resource_overrides_test.go +++ b/cmd/thv-operator/controllers/mcpserver_resource_overrides_test.go @@ -155,7 +155,7 @@ func TestResourceOverrides(t *testing.T) { "environment": "test", }, }, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "HTTP_PROXY", Value: "http://proxy.example.com:8080", @@ -203,7 +203,7 @@ func TestResourceOverrides(t *testing.T) { ProxyPort: 8080, ResourceOverrides: &mcpv1alpha1.ResourceOverrides{ ProxyDeployment: &mcpv1alpha1.ProxyDeploymentOverrides{ - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TOOLHIVE_DEBUG", Value: "true"}, }, }, @@ -249,7 +249,7 @@ func TestResourceOverrides(t *testing.T) { "version": "v1.2.3", }, }, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "LOG_LEVEL", Value: "debug", @@ -483,7 +483,7 @@ func TestDeploymentNeedsUpdateProxyEnv(t *testing.T) { ProxyPort: 8080, ResourceOverrides: &mcpv1alpha1.ResourceOverrides{ ProxyDeployment: &mcpv1alpha1.ProxyDeploymentOverrides{ - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "HTTP_PROXY", Value: "http://proxy.example.com:8080"}, {Name: "NO_PROXY", Value: "localhost,127.0.0.1"}, }, @@ -509,7 +509,7 @@ func TestDeploymentNeedsUpdateProxyEnv(t *testing.T) { ProxyPort: 8080, ResourceOverrides: &mcpv1alpha1.ResourceOverrides{ ProxyDeployment: &mcpv1alpha1.ProxyDeploymentOverrides{ - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "HTTP_PROXY", Value: "http://new-proxy.example.com:8080"}, {Name: "NO_PROXY", Value: "localhost,127.0.0.1"}, }, @@ -535,7 +535,7 @@ func TestDeploymentNeedsUpdateProxyEnv(t *testing.T) { ProxyPort: 8080, ResourceOverrides: &mcpv1alpha1.ResourceOverrides{ ProxyDeployment: &mcpv1alpha1.ProxyDeploymentOverrides{ - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "HTTP_PROXY", Value: "http://proxy.example.com:8080"}, {Name: "NO_PROXY", Value: "localhost,127.0.0.1"}, {Name: "CUSTOM_ENV", Value: "custom-value"}, @@ -562,7 +562,7 @@ func TestDeploymentNeedsUpdateProxyEnv(t *testing.T) { ProxyPort: 8080, ResourceOverrides: &mcpv1alpha1.ResourceOverrides{ ProxyDeployment: &mcpv1alpha1.ProxyDeploymentOverrides{ - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "HTTP_PROXY", Value: "http://proxy.example.com:8080"}, }, }, diff --git a/cmd/thv-operator/controllers/mcpserver_runconfig.go b/cmd/thv-operator/controllers/mcpserver_runconfig.go index 92c1f98d1b..45f4f6dba4 100644 --- a/cmd/thv-operator/controllers/mcpserver_runconfig.go +++ b/cmd/thv-operator/controllers/mcpserver_runconfig.go @@ -566,7 +566,7 @@ func (*MCPServerReconciler) validateToolsFilter(config *runner.RunConfig) error } // convertEnvVarsFromMCPServer converts MCPServer environment variables to builder format -func convertEnvVarsFromMCPServer(envs []mcpv1alpha1.EnvVar) map[string]string { +func convertEnvVarsFromMCPServer(envs []corev1.EnvVar) map[string]string { if len(envs) == 0 { return nil } diff --git a/cmd/thv-operator/controllers/mcpserver_runconfig_test.go b/cmd/thv-operator/controllers/mcpserver_runconfig_test.go index 0750c29474..9539d7f706 100644 --- a/cmd/thv-operator/controllers/mcpserver_runconfig_test.go +++ b/cmd/thv-operator/controllers/mcpserver_runconfig_test.go @@ -41,7 +41,7 @@ func createRunConfigTestScheme() *runtime.Scheme { return testScheme } -func createTestMCPServerWithConfig(name, namespace, image string, envVars []mcpv1alpha1.EnvVar) *mcpv1alpha1.MCPServer { +func createTestMCPServerWithConfig(name, namespace, image string, envVars []corev1.EnvVar) *mcpv1alpha1.MCPServer { return &mcpv1alpha1.MCPServer{ ObjectMeta: metav1.ObjectMeta{ Name: name, @@ -96,7 +96,7 @@ func TestCreateRunConfigFromMCPServer(t *testing.T) { Image: "env-image:latest", Transport: "sse", ProxyPort: 9090, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "VAR1", Value: "value1"}, {Name: "VAR2", Value: "value2"}, }, @@ -322,7 +322,7 @@ func TestCreateRunConfigFromMCPServer(t *testing.T) { MCPPort: 8080, ProxyMode: "streamable-http", Args: []string{"--comprehensive", "--test"}, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "ENV1", Value: "value1"}, {Name: "ENV2", Value: "value2"}, {Name: "EMPTY_VALUE", Value: ""}, @@ -611,7 +611,7 @@ func TestDeterministicConfigMapGeneration(t *testing.T) { ProxyPort: 9090, MCPPort: 8080, Args: []string{"--arg1", "--arg2", "--complex-flag=value"}, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "VAR_C", Value: "value_c"}, {Name: "VAR_A", Value: "value_a"}, {Name: "VAR_B", Value: "value_b"}, @@ -1324,7 +1324,7 @@ func TestEnsureRunConfigConfigMapCompleteFlow(t *testing.T) { } // Step 1: Create initial MCPServer and ConfigMap - mcpServer := createTestMCPServerWithConfig("flow-server", "flow-ns", "test:v1", []mcpv1alpha1.EnvVar{ + mcpServer := createTestMCPServerWithConfig("flow-server", "flow-ns", "test:v1", []corev1.EnvVar{ {Name: "ENV1", Value: "value1"}, }) @@ -1355,7 +1355,7 @@ func TestEnsureRunConfigConfigMapCompleteFlow(t *testing.T) { // The checksum will automatically change when content changes mcpServer.Spec.Image = "test:v2" - mcpServer.Spec.Env = []mcpv1alpha1.EnvVar{ + mcpServer.Spec.Env = []corev1.EnvVar{ {Name: "ENV1", Value: "value1"}, {Name: "ENV2", Value: "value2"}, } diff --git a/cmd/thv-operator/test-integration/embedding-server/embeddingserver_creation_test.go b/cmd/thv-operator/test-integration/embedding-server/embeddingserver_creation_test.go index efb3841a54..f177ffcc78 100644 --- a/cmd/thv-operator/test-integration/embedding-server/embeddingserver_creation_test.go +++ b/cmd/thv-operator/test-integration/embedding-server/embeddingserver_creation_test.go @@ -502,7 +502,7 @@ var _ = Describe("EmbeddingServer Controller Integration Tests", func() { Model: "sentence-transformers/all-MiniLM-L6-v2", Image: "ghcr.io/huggingface/text-embeddings-inference:latest", Port: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "CUSTOM_VAR_1", Value: "value1"}, {Name: "CUSTOM_VAR_2", Value: "value2"}, }, diff --git a/cmd/thv-operator/test-integration/embedding-server/embeddingserver_update_test.go b/cmd/thv-operator/test-integration/embedding-server/embeddingserver_update_test.go index 12aecdffa3..887d9698b7 100644 --- a/cmd/thv-operator/test-integration/embedding-server/embeddingserver_update_test.go +++ b/cmd/thv-operator/test-integration/embedding-server/embeddingserver_update_test.go @@ -177,7 +177,7 @@ var _ = Describe("EmbeddingServer Controller Update Tests", func() { Model: "sentence-transformers/all-MiniLM-L6-v2", Image: "ghcr.io/huggingface/text-embeddings-inference:latest", Port: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "LOG_LEVEL", Value: "info"}, }, }, @@ -186,7 +186,7 @@ var _ = Describe("EmbeddingServer Controller Update Tests", func() { { Name: "Should update StatefulSet when env var value changes", ApplyUpdate: func(es *mcpv1alpha1.EmbeddingServer) { - es.Spec.Env = []mcpv1alpha1.EnvVar{ + es.Spec.Env = []corev1.EnvVar{ {Name: "LOG_LEVEL", Value: "debug"}, } }, @@ -205,7 +205,7 @@ var _ = Describe("EmbeddingServer Controller Update Tests", func() { { Name: "Should update StatefulSet when new env var is added", ApplyUpdate: func(es *mcpv1alpha1.EmbeddingServer) { - es.Spec.Env = []mcpv1alpha1.EnvVar{ + es.Spec.Env = []corev1.EnvVar{ {Name: "LOG_LEVEL", Value: "debug"}, {Name: "NEW_VAR", Value: "new_value"}, } diff --git a/cmd/thv-operator/test-integration/mcp-server/mcpserver_controller_integration_test.go b/cmd/thv-operator/test-integration/mcp-server/mcpserver_controller_integration_test.go index 846cb3944b..3e8ed79c27 100644 --- a/cmd/thv-operator/test-integration/mcp-server/mcpserver_controller_integration_test.go +++ b/cmd/thv-operator/test-integration/mcp-server/mcpserver_controller_integration_test.go @@ -69,7 +69,7 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { ProxyPort: 8080, MCPPort: 8080, Args: []string{"--verbose"}, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "DEBUG", Value: "true", @@ -113,7 +113,6 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { }) It("Should create a Deployment with proper configuration", func() { - // Wait for Deployment to be created deployment := &appsv1.Deployment{} Eventually(func() error { @@ -229,11 +228,9 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { Expect(container.ReadinessProbe.ProbeHandler.HTTPGet.Port).To(Equal(intstr.FromString("http"))) Expect(container.ReadinessProbe.InitialDelaySeconds).To(Equal(int32(5))) Expect(container.ReadinessProbe.PeriodSeconds).To(Equal(int32(5))) - }) It("Should create the RunConfig ConfigMap", func() { - // Wait for Service to be created (using the correct naming pattern) configMap := &corev1.ConfigMap{} configMapName := mcpServerName + "-runconfig" @@ -253,7 +250,6 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { }) It("Should create a Service for the MCPServer Proxy", func() { - // Wait for Service to be created (using the correct naming pattern) service := &corev1.Service{} serviceName := "mcp-" + mcpServerName + "-proxy" @@ -271,11 +267,9 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { Expect(service.Spec.Type).To(Equal(corev1.ServiceTypeClusterIP)) Expect(service.Spec.Ports).To(HaveLen(1)) Expect(service.Spec.Ports[0].Port).To(Equal(int32(8080))) - }) It("Should create RBAC resources when ServiceAccount is not specified", func() { - // Wait for ServiceAccount to be created serviceAccountName := mcpServerName + "-proxy-runner" serviceAccount := &corev1.ServiceAccount{} @@ -320,7 +314,6 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { Expect(roleBinding.Subjects).To(HaveLen(1)) Expect(roleBinding.Subjects[0].Name).To(Equal(serviceAccountName)) Expect(roleBinding.RoleRef.Name).To(Equal(serviceAccountName)) - }) It("Should set ObservedGeneration in status after reconciliation", func() { @@ -359,7 +352,6 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { }) It("Should update Deployment when MCPServer spec changes", func() { - // Wait for Deployment to be created deployment := &appsv1.Deployment{} Eventually(func() error { diff --git a/cmd/thv-operator/test-integration/mcp-server/mcpserver_runconfig_integration_test.go b/cmd/thv-operator/test-integration/mcp-server/mcpserver_runconfig_integration_test.go index edcb62a849..f57a33caea 100644 --- a/cmd/thv-operator/test-integration/mcp-server/mcpserver_runconfig_integration_test.go +++ b/cmd/thv-operator/test-integration/mcp-server/mcpserver_runconfig_integration_test.go @@ -65,7 +65,7 @@ var _ = Describe("RunConfig ConfigMap Integration Tests", func() { ProxyPort: 8080, MCPPort: 8081, Args: []string{"--verbose", "--debug"}, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "DEBUG", Value: "true", @@ -300,7 +300,7 @@ var _ = Describe("RunConfig ConfigMap Integration Tests", func() { // Update multiple fields mcpServer.Spec.Image = "example/mcp-server:v2.0.0" mcpServer.Spec.ProxyPort = 9090 - mcpServer.Spec.Env = append(mcpServer.Spec.Env, mcpv1alpha1.EnvVar{ + mcpServer.Spec.Env = append(mcpServer.Spec.Env, corev1.EnvVar{ Name: "NEW_VAR", Value: "new_value", }) @@ -777,7 +777,7 @@ var _ = Describe("RunConfig ConfigMap Integration Tests", func() { ProxyPort: 9090, MCPPort: 8080, Args: []string{"--arg1", "--arg2", "--arg3"}, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "VAR_C", Value: "value_c"}, {Name: "VAR_A", Value: "value_a"}, {Name: "VAR_B", Value: "value_b"}, diff --git a/docs/operator/crd-api.md b/docs/operator/crd-api.md index 4789885ba7..f7cef87805 100644 --- a/docs/operator/crd-api.md +++ b/docs/operator/crd-api.md @@ -1108,7 +1108,7 @@ _Appears in:_ | `imagePullPolicy` _string_ | ImagePullPolicy defines the pull policy for the container image | IfNotPresent | Enum: [Always Never IfNotPresent]
Optional: \{\}
| | `port` _integer_ | Port is the port to expose the embedding service on | 8080 | Maximum: 65535
Minimum: 1
| | `args` _string array_ | Args are additional arguments to pass to the embedding inference server | | Optional: \{\}
| -| `env` _[api.v1alpha1.EnvVar](#apiv1alpha1envvar) array_ | Env are environment variables to set in the container | | Optional: \{\}
| +| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#envvar-v1-core) array_ | Env are environment variables to set in the container | | Optional: \{\}
| | `resources` _[api.v1alpha1.ResourceRequirements](#apiv1alpha1resourcerequirements)_ | Resources defines compute resources for the embedding server | | Optional: \{\}
| | `modelCache` _[api.v1alpha1.ModelCacheConfig](#apiv1alpha1modelcacheconfig)_ | ModelCache configures persistent storage for downloaded models
When enabled, models are cached in a PVC and reused across pod restarts | | Optional: \{\}
| | `podTemplateSpec` _[RawExtension](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#rawextension-runtime-pkg)_ | PodTemplateSpec allows customizing the pod (node selection, tolerations, etc.)
This field accepts a PodTemplateSpec object as JSON/YAML.
Note that to modify the specific container the embedding server runs in, you must specify
the 'embedding' container name in the PodTemplateSpec. | | Type: object
Optional: \{\}
| @@ -1155,25 +1155,6 @@ _Appears in:_ | `podTemplateMetadataOverrides` _[api.v1alpha1.ResourceMetadataOverrides](#apiv1alpha1resourcemetadataoverrides)_ | PodTemplateMetadataOverrides defines metadata overrides for the pod template | | Optional: \{\}
| -#### api.v1alpha1.EnvVar - - - -EnvVar represents an environment variable in a container - - - -_Appears in:_ -- [api.v1alpha1.EmbeddingServerSpec](#apiv1alpha1embeddingserverspec) -- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) -- [api.v1alpha1.ProxyDeploymentOverrides](#apiv1alpha1proxydeploymentoverrides) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `name` _string_ | Name of the environment variable | | Required: \{\}
| -| `value` _string_ | Value of the environment variable | | Required: \{\}
| - - #### api.v1alpha1.ExternalAuthConfigRef @@ -2128,7 +2109,7 @@ _Appears in:_ | `proxyPort` _integer_ | ProxyPort is the port to expose the proxy runner on | 8080 | Maximum: 65535
Minimum: 1
| | `mcpPort` _integer_ | MCPPort is the port that MCP server listens to | | Maximum: 65535
Minimum: 1
Optional: \{\}
| | `args` _string array_ | Args are additional arguments to pass to the MCP server | | Optional: \{\}
| -| `env` _[api.v1alpha1.EnvVar](#apiv1alpha1envvar) array_ | Env are environment variables to set in the MCP server container | | Optional: \{\}
| +| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#envvar-v1-core) array_ | Env are environment variables to set in the MCP server container | | Optional: \{\}
| | `volumes` _[api.v1alpha1.Volume](#apiv1alpha1volume) array_ | Volumes are volumes to mount in the MCP server container | | Optional: \{\}
| | `resources` _[api.v1alpha1.ResourceRequirements](#apiv1alpha1resourcerequirements)_ | Resources defines the resource requirements for the MCP server container | | Optional: \{\}
| | `secrets` _[api.v1alpha1.SecretRef](#apiv1alpha1secretref) array_ | Secrets are references to secrets to mount in the MCP server container | | Optional: \{\}
| @@ -2647,7 +2628,7 @@ _Appears in:_ | `annotations` _object (keys:string, values:string)_ | Annotations to add or override on the resource | | Optional: \{\}
| | `labels` _object (keys:string, values:string)_ | Labels to add or override on the resource | | Optional: \{\}
| | `podTemplateMetadataOverrides` _[api.v1alpha1.ResourceMetadataOverrides](#apiv1alpha1resourcemetadataoverrides)_ | | | | -| `env` _[api.v1alpha1.EnvVar](#apiv1alpha1envvar) array_ | Env are environment variables to set in the proxy container (thv run process)
These affect the toolhive proxy itself, not the MCP server it manages
Use TOOLHIVE_DEBUG=true to enable debug logging in the proxy | | Optional: \{\}
| +| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#envvar-v1-core) array_ | Env are environment variables to set in the proxy container (thv run process)
These affect the toolhive proxy itself, not the MCP server it manages
Use TOOLHIVE_DEBUG=true to enable debug logging in the proxy | | Optional: \{\}
| | `imagePullSecrets` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#localobjectreference-v1-core) array_ | ImagePullSecrets allows specifying image pull secrets for the proxy runner
These are applied to both the Deployment and the ServiceAccount | | Optional: \{\}
| diff --git a/pkg/export/k8s.go b/pkg/export/k8s.go index e57cfda9aa..e6f58fe9bd 100644 --- a/pkg/export/k8s.go +++ b/pkg/export/k8s.go @@ -10,6 +10,7 @@ import ( "io" "strings" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/yaml" @@ -94,9 +95,9 @@ func runConfigToMCPServer(config *runner.RunConfig) (*v1alpha1.MCPServer, error) // Convert environment variables if len(config.EnvVars) > 0 { - mcpServer.Spec.Env = make([]v1alpha1.EnvVar, 0, len(config.EnvVars)) + mcpServer.Spec.Env = make([]corev1.EnvVar, 0, len(config.EnvVars)) for key, value := range config.EnvVars { - mcpServer.Spec.Env = append(mcpServer.Spec.Env, v1alpha1.EnvVar{ + mcpServer.Spec.Env = append(mcpServer.Spec.Env, corev1.EnvVar{ Name: key, Value: value, }) diff --git a/test/e2e/thv-operator/acceptance_tests/ratelimit_test.go b/test/e2e/thv-operator/acceptance_tests/ratelimit_test.go index 09a4284c67..4140d5feac 100644 --- a/test/e2e/thv-operator/acceptance_tests/ratelimit_test.go +++ b/test/e2e/thv-operator/acceptance_tests/ratelimit_test.go @@ -57,7 +57,7 @@ var _ = Describe("MCPServer Rate Limiting", Ordered, func() { Transport: "streamable-http", ProxyPort: 8080, MCPPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, SessionStorage: &mcpv1alpha1.SessionStorageConfig{ @@ -149,7 +149,7 @@ var _ = Describe("MCPServer Rate Limiting", Ordered, func() { Transport: "streamable-http", ProxyPort: 8080, MCPPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, SessionStorage: &mcpv1alpha1.SessionStorageConfig{ @@ -210,7 +210,7 @@ var _ = Describe("MCPServer Rate Limiting", Ordered, func() { Transport: "streamable-http", ProxyPort: 8080, MCPPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, SessionStorage: &mcpv1alpha1.SessionStorageConfig{ diff --git a/test/e2e/thv-operator/virtualmcp/helpers.go b/test/e2e/thv-operator/virtualmcp/helpers.go index d68614afda..614c001790 100644 --- a/test/e2e/thv-operator/virtualmcp/helpers.go +++ b/test/e2e/thv-operator/virtualmcp/helpers.go @@ -673,7 +673,7 @@ func CreateMCPServerAndWait( ProxyPort: 8080, MCPPort: 8080, Resources: defaultMCPServerResources(), - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, @@ -707,7 +707,7 @@ type BackendConfig struct { Transport string // defaults to "streamable-http" if empty ExternalAuthConfigRef *mcpv1alpha1.ExternalAuthConfigRef Secrets []mcpv1alpha1.SecretRef - Env []mcpv1alpha1.EnvVar // additional env vars beyond TRANSPORT + Env []corev1.EnvVar // additional env vars beyond TRANSPORT // Resources overrides the default resource requests/limits. When nil, // defaultMCPServerResources() is used to ensure containers are scheduled // with reasonable resource guarantees and do not compete excessively. @@ -765,7 +765,7 @@ func CreateMultipleMCPServersInParallel( ExternalAuthConfigRef: backends[idx].ExternalAuthConfigRef, Secrets: backends[idx].Secrets, Resources: resources, - Env: append([]mcpv1alpha1.EnvVar{ + Env: append([]corev1.EnvVar{ {Name: "TRANSPORT", Value: backendTransport}, }, backends[idx].Env...), }, diff --git a/test/e2e/thv-operator/virtualmcp/virtualmcp_auth_discovery_test.go b/test/e2e/thv-operator/virtualmcp/virtualmcp_auth_discovery_test.go index e933c8ee68..138e4988bd 100644 --- a/test/e2e/thv-operator/virtualmcp/virtualmcp_auth_discovery_test.go +++ b/test/e2e/thv-operator/virtualmcp/virtualmcp_auth_discovery_test.go @@ -1454,7 +1454,7 @@ var _ = Describe("Auth Config Error Handling", Ordered, func() { ExternalAuthConfigRef: &mcpv1alpha1.ExternalAuthConfigRef{ Name: workingAuthConfigName, }, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, @@ -1477,7 +1477,7 @@ var _ = Describe("Auth Config Error Handling", Ordered, func() { ExternalAuthConfigRef: &mcpv1alpha1.ExternalAuthConfigRef{ Name: missingAuthConfigName, }, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, diff --git a/test/e2e/thv-operator/virtualmcp/virtualmcp_circuit_breaker_test.go b/test/e2e/thv-operator/virtualmcp/virtualmcp_circuit_breaker_test.go index 5f10ab327f..2f8032ecc8 100644 --- a/test/e2e/thv-operator/virtualmcp/virtualmcp_circuit_breaker_test.go +++ b/test/e2e/thv-operator/virtualmcp/virtualmcp_circuit_breaker_test.go @@ -57,7 +57,7 @@ var _ = Describe("VirtualMCPServer Circuit Breaker Lifecycle", Ordered, func() { Transport: "streamable-http", ProxyPort: 8080, MCPPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, @@ -76,7 +76,7 @@ var _ = Describe("VirtualMCPServer Circuit Breaker Lifecycle", Ordered, func() { Transport: "streamable-http", ProxyPort: 8080, MCPPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, diff --git a/test/e2e/thv-operator/virtualmcp/virtualmcp_external_auth_test.go b/test/e2e/thv-operator/virtualmcp/virtualmcp_external_auth_test.go index fd1d73e47c..f71dabe4a0 100644 --- a/test/e2e/thv-operator/virtualmcp/virtualmcp_external_auth_test.go +++ b/test/e2e/thv-operator/virtualmcp/virtualmcp_external_auth_test.go @@ -835,7 +835,7 @@ var _ = Describe("VirtualMCPServer Health Check with HeaderInjection Auth", Orde Transport: "streamable-http", ProxyPort: 8080, MCPPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, ExternalAuthConfigRef: &mcpv1alpha1.ExternalAuthConfigRef{ @@ -1051,7 +1051,7 @@ var _ = Describe("VirtualMCPServer Health Check with TokenExchange Auth", Ordere Transport: "streamable-http", ProxyPort: 8080, MCPPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, ExternalAuthConfigRef: &mcpv1alpha1.ExternalAuthConfigRef{ diff --git a/test/e2e/thv-operator/virtualmcp/virtualmcp_optimizer_multibackend_test.go b/test/e2e/thv-operator/virtualmcp/virtualmcp_optimizer_multibackend_test.go index 5b2f72a09f..b5bd9d3d08 100644 --- a/test/e2e/thv-operator/virtualmcp/virtualmcp_optimizer_multibackend_test.go +++ b/test/e2e/thv-operator/virtualmcp/virtualmcp_optimizer_multibackend_test.go @@ -91,7 +91,7 @@ var _ = Describe("VirtualMCPServer Optimizer Multi-Backend", Ordered, func() { Name: backend5Name, Namespace: testNamespace, GroupRef: mcpGroupName, Image: images.TerraformMCPServerImage, // 9 tools Transport: "streamable-http", - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT_MODE", Value: "streamable-http"}, {Name: "TRANSPORT_HOST", Value: "0.0.0.0"}, }, diff --git a/test/e2e/thv-operator/virtualmcp/virtualmcp_telemetry_test.go b/test/e2e/thv-operator/virtualmcp/virtualmcp_telemetry_test.go index 61f01c6904..74e937e405 100644 --- a/test/e2e/thv-operator/virtualmcp/virtualmcp_telemetry_test.go +++ b/test/e2e/thv-operator/virtualmcp/virtualmcp_telemetry_test.go @@ -47,7 +47,7 @@ var _ = Describe("VirtualMCPServer Telemetry Config", Ordered, func() { Transport: "streamable-http", ProxyPort: 8080, MCPPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, diff --git a/test/e2e/thv-operator/virtualmcp/virtualmcp_yardstick_base_test.go b/test/e2e/thv-operator/virtualmcp/virtualmcp_yardstick_base_test.go index 4273a67cac..a0f849481b 100644 --- a/test/e2e/thv-operator/virtualmcp/virtualmcp_yardstick_base_test.go +++ b/test/e2e/thv-operator/virtualmcp/virtualmcp_yardstick_base_test.go @@ -13,6 +13,7 @@ import ( "github.com/mark3labs/mcp-go/mcp" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -51,7 +52,7 @@ var _ = Describe("VirtualMCPServer Yardstick Base", Ordered, func() { Transport: "streamable-http", ProxyPort: 8080, MCPPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, @@ -69,7 +70,7 @@ var _ = Describe("VirtualMCPServer Yardstick Base", Ordered, func() { Transport: "streamable-http", ProxyPort: 8080, MCPPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, },