diff --git a/.github/workflows/operator-ci.yml b/.github/workflows/operator-ci.yml index f72eea2c3..a01dbfd72 100644 --- a/.github/workflows/operator-ci.yml +++ b/.github/workflows/operator-ci.yml @@ -95,7 +95,7 @@ jobs: - name: Check for changes id: git-check run: | - git diff --exit-code deploy/charts/operator-crds/crds || echo "crd-changes=true" >> $GITHUB_OUTPUT + git diff --exit-code deploy/charts/operator-crds/crd-files || echo "crd-changes=true" >> $GITHUB_OUTPUT git diff --exit-code deploy/charts/operator/templates || echo "operator-changes=true" >> $GITHUB_OUTPUT - name: Fail if CRDs are not up to date diff --git a/cmd/thv-operator/Taskfile.yml b/cmd/thv-operator/Taskfile.yml index 52cf8e1af..ef55a7229 100644 --- a/cmd/thv-operator/Taskfile.yml +++ b/cmd/thv-operator/Taskfile.yml @@ -184,8 +184,8 @@ tasks: platforms: [windows] ignore_error: true # Windows has no mkdir -p, so just ignore error if it exists - go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.17.3 - - $(go env GOPATH)/bin/controller-gen crd webhook paths="./cmd/thv-operator/..." output:crd:artifacts:config=deploy/charts/operator-crds/crds - - $(go env GOPATH)/bin/controller-gen rbac:roleName=toolhive-operator-manager-role paths="./cmd/thv-operator/..." output:rbac:artifacts:config=deploy/charts/operator/templates/clusterrole + - $(go env GOPATH)/bin/controller-gen crd webhook paths="./cmd/thv-operator/..." output:crd:artifacts:config=deploy/charts/operator-crds/crd-files + - $(go env GOPATH)/bin/controller-gen rbac:roleName=toolhive-operator-manager-role paths="./cmd/thv-operator/..." output:rbac:artifacts:config=deploy/charts/operator/generated-rbac operator-test: desc: Run tests for the operator diff --git a/cmd/thv-operator/main.go b/cmd/thv-operator/main.go index 831d64436..df51bbe29 100644 --- a/cmd/thv-operator/main.go +++ b/cmd/thv-operator/main.go @@ -7,13 +7,14 @@ import ( "flag" "fmt" "os" + "strconv" "strings" + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) + // to ensure that exec-entrypoint and run can make use of them. "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" - // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) - // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" @@ -36,6 +37,18 @@ var ( setupLog = log.Log.WithName("setup") ) +// Feature flags for controller groups +const ( + featureServer = "ENABLE_SERVER" + featureRegistry = "ENABLE_REGISTRY" + featureVMCP = "ENABLE_VMCP" +) + +// controllerDependencies maps each controller group to its required dependencies +var controllerDependencies = map[string][]string{ + featureVMCP: {featureServer}, // Virtual MCP requires server controllers +} + func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(mcpv1alpha1.AddToScheme(scheme)) @@ -111,6 +124,69 @@ func main() { // setupControllersAndWebhooks sets up all controllers and webhooks with the manager func setupControllersAndWebhooks(mgr ctrl.Manager) error { + // Check feature flags + enableServer := isFeatureEnabled(featureServer, true) + enableRegistry := isFeatureEnabled(featureRegistry, true) + enableVMCP := isFeatureEnabled(featureVMCP, true) + + // Track enabled features for dependency checking + enabledFeatures := map[string]bool{ + featureServer: enableServer, + featureRegistry: enableRegistry, + featureVMCP: enableVMCP, + } + + // Check dependencies and log warnings for missing dependencies + for feature, deps := range controllerDependencies { + if !enabledFeatures[feature] { + continue // Skip if feature itself is disabled + } + for _, dep := range deps { + if !enabledFeatures[dep] { + setupLog.Info( + fmt.Sprintf("%s requires %s to be enabled, skipping %s controllers", feature, dep, feature), + "feature", feature, + "required_dependency", dep, + ) + enabledFeatures[feature] = false // Mark as effectively disabled + break + } + } + } + + // Set up server-related controllers + if enabledFeatures[featureServer] { + if err := setupServerControllers(mgr, enableRegistry); err != nil { + return err + } + } else { + setupLog.Info("ENABLE_SERVER is disabled, skipping server-related controllers") + } + + // Set up registry controller + if enabledFeatures[featureRegistry] { + if err := setupRegistryController(mgr); err != nil { + return err + } + } else { + setupLog.Info("ENABLE_REGISTRY is disabled, skipping MCPRegistry controller") + } + + // Set up Virtual MCP controllers and webhooks + if enabledFeatures[featureVMCP] { + if err := setupAggregationControllers(mgr); err != nil { + return err + } + } else { + setupLog.Info("ENABLE_VMCP is disabled, skipping Virtual MCP controllers and webhooks") + } + + //+kubebuilder:scaffold:builder + return nil +} + +// setupServerControllers sets up server-related controllers (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, ToolConfig) +func setupServerControllers(mgr ctrl.Manager, enableRegistry bool) error { // Set up field indexing for MCPServer.Spec.GroupRef if err := mgr.GetFieldIndexer().IndexField( context.Background(), @@ -127,21 +203,27 @@ func setupControllersAndWebhooks(mgr ctrl.Manager) error { return fmt.Errorf("unable to create field index for spec.groupRef: %w", err) } - // Create a shared platform detector for all controllers - sharedPlatformDetector := ctrlutil.NewSharedPlatformDetector() + // Set image validation mode based on whether registry is enabled + // If ENABLE_REGISTRY is enabled, enforce registry-based image validation + // Otherwise, allow all images + imageValidation := validation.ImageValidationAlwaysAllow + if enableRegistry { + imageValidation = validation.ImageValidationRegistryEnforcing + } + + // Set up MCPServer controller rec := &controllers.MCPServerReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorderFor("mcpserver-controller"), - PlatformDetector: sharedPlatformDetector, - ImageValidation: validation.ImageValidationAlwaysAllow, + PlatformDetector: ctrlutil.NewSharedPlatformDetector(), + ImageValidation: imageValidation, } - if err := rec.SetupWithManager(mgr); err != nil { return fmt.Errorf("unable to create controller MCPServer: %w", err) } - // Register MCPToolConfig controller + // Set up MCPToolConfig controller if err := (&controllers.ToolConfigReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), @@ -149,7 +231,7 @@ func setupControllersAndWebhooks(mgr ctrl.Manager) error { return fmt.Errorf("unable to create controller MCPToolConfig: %w", err) } - // Register MCPExternalAuthConfig controller + // Set up MCPExternalAuthConfig controller if err := (&controllers.MCPExternalAuthConfigReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), @@ -157,7 +239,7 @@ func setupControllersAndWebhooks(mgr ctrl.Manager) error { return fmt.Errorf("unable to create controller MCPExternalAuthConfig: %w", err) } - // Register MCPRemoteProxy controller + // Set up MCPRemoteProxy controller if err := (&controllers.MCPRemoteProxyReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), @@ -166,13 +248,22 @@ func setupControllersAndWebhooks(mgr ctrl.Manager) error { return fmt.Errorf("unable to create controller MCPRemoteProxy: %w", err) } - // Only register MCPRegistry controller if feature flag is enabled - rec.ImageValidation = validation.ImageValidationRegistryEnforcing + return nil +} +// setupRegistryController sets up the MCPRegistry controller +func setupRegistryController(mgr ctrl.Manager) error { if err := (controllers.NewMCPRegistryReconciler(mgr.GetClient(), mgr.GetScheme())).SetupWithManager(mgr); err != nil { return fmt.Errorf("unable to create controller MCPRegistry: %w", err) } + return nil +} +// setupAggregationControllers sets up Virtual MCP-related controllers and webhooks +// (MCPGroup, VirtualMCPServer, and their webhooks) +// Note: This function assumes server controllers are enabled (enforced by dependency check) +// The field index for MCPServer.Spec.GroupRef is created in setupServerControllers +func setupAggregationControllers(mgr ctrl.Manager) error { // Set up MCPGroup controller if err := (&controllers.MCPGroupReconciler{ Client: mgr.GetClient(), @@ -199,11 +290,25 @@ func setupControllersAndWebhooks(mgr ctrl.Manager) error { if err := (&mcpv1alpha1.VirtualMCPCompositeToolDefinition{}).SetupWebhookWithManager(mgr); err != nil { return fmt.Errorf("unable to create webhook VirtualMCPCompositeToolDefinition: %w", err) } - //+kubebuilder:scaffold:builder return nil } +// isFeatureEnabled checks if a feature flag environment variable is enabled. +// If the environment variable is not set, it returns the default value. +// The environment variable is considered enabled if it's set to "true" (case-insensitive). +func isFeatureEnabled(envVar string, defaultValue bool) bool { + value, found := os.LookupEnv(envVar) + if !found { + return defaultValue + } + enabled, err := strconv.ParseBool(value) + if err != nil { + return defaultValue + } + return enabled +} + // getDefaultNamespaces returns a map of namespaces to cache.Config for the operator to watch. // if WATCH_NAMESPACE is not set, returns nil which is defaulted to a cluster scope. func getDefaultNamespaces() map[string]cache.Config { diff --git a/cmd/thv-operator/test-integration/mcp-external-auth/suite_test.go b/cmd/thv-operator/test-integration/mcp-external-auth/suite_test.go index 8a3d9f989..3c1fe570d 100644 --- a/cmd/thv-operator/test-integration/mcp-external-auth/suite_test.go +++ b/cmd/thv-operator/test-integration/mcp-external-auth/suite_test.go @@ -57,7 +57,9 @@ var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds")}, + CRDDirectoryPaths: []string{ + filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crd-files"), + }, ErrorIfCRDPathMissing: true, } diff --git a/cmd/thv-operator/test-integration/mcp-group/suite_test.go b/cmd/thv-operator/test-integration/mcp-group/suite_test.go index cbfb64b27..f377e84a1 100644 --- a/cmd/thv-operator/test-integration/mcp-group/suite_test.go +++ b/cmd/thv-operator/test-integration/mcp-group/suite_test.go @@ -58,7 +58,9 @@ var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds")}, + CRDDirectoryPaths: []string{ + filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crd-files"), + }, ErrorIfCRDPathMissing: true, } diff --git a/cmd/thv-operator/test-integration/mcp-registry/suite_test.go b/cmd/thv-operator/test-integration/mcp-registry/suite_test.go index 516284300..7ea1f17f7 100644 --- a/cmd/thv-operator/test-integration/mcp-registry/suite_test.go +++ b/cmd/thv-operator/test-integration/mcp-registry/suite_test.go @@ -68,7 +68,7 @@ var _ = BeforeSuite(func() { testEnv = &envtest.Environment{ UseExistingCluster: &useExistingCluster, CRDDirectoryPaths: []string{ - filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds"), + filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crd-files"), }, ErrorIfCRDPathMissing: true, BinaryAssetsDirectory: kubebuilderAssets, diff --git a/cmd/thv-operator/test-integration/mcp-server/suite_test.go b/cmd/thv-operator/test-integration/mcp-server/suite_test.go index 17acaab3b..b11c009f1 100644 --- a/cmd/thv-operator/test-integration/mcp-server/suite_test.go +++ b/cmd/thv-operator/test-integration/mcp-server/suite_test.go @@ -61,7 +61,9 @@ var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds")}, + CRDDirectoryPaths: []string{ + filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crd-files"), + }, ErrorIfCRDPathMissing: true, } diff --git a/cmd/thv-operator/test-integration/virtualmcp/suite_test.go b/cmd/thv-operator/test-integration/virtualmcp/suite_test.go index 54f18616b..39cc4344a 100644 --- a/cmd/thv-operator/test-integration/virtualmcp/suite_test.go +++ b/cmd/thv-operator/test-integration/virtualmcp/suite_test.go @@ -61,7 +61,9 @@ var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds")}, + CRDDirectoryPaths: []string{ + filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crd-files"), + }, ErrorIfCRDPathMissing: true, } @@ -125,6 +127,14 @@ var _ = BeforeSuite(func() { }).SetupWithManager(k8sManager) Expect(err).ToNot(HaveOccurred()) + // Set up VirtualMCPServer webhook + err = (&mcpv1alpha1.VirtualMCPServer{}).SetupWebhookWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred()) + + // Set up VirtualMCPCompositeToolDefinition webhook + err = (&mcpv1alpha1.VirtualMCPCompositeToolDefinition{}).SetupWebhookWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred()) + // Start the manager in a goroutine go func() { defer GinkgoRecover() diff --git a/ct-install.yaml b/ct-install.yaml index 550eeb676..871dd443f 100644 --- a/ct-install.yaml +++ b/ct-install.yaml @@ -8,3 +8,7 @@ validate-maintainers: false remote: origin target-branch: main +skip-clean-up: true + +helm-extra-set-args: > + --set operator.testMode=true \ No newline at end of file diff --git a/deploy/charts/operator-crds/Chart.yaml b/deploy/charts/operator-crds/Chart.yaml index 0401ec855..a23859333 100644 --- a/deploy/charts/operator-crds/Chart.yaml +++ b/deploy/charts/operator-crds/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: toolhive-operator-crds description: A Helm chart for installing the ToolHive Operator CRDs into Kubernetes. type: application -version: 0.0.69 +version: 0.0.70 appVersion: "0.0.1" diff --git a/deploy/charts/operator-crds/README.md b/deploy/charts/operator-crds/README.md index 070fa77f9..6f0bf7db6 100644 --- a/deploy/charts/operator-crds/README.md +++ b/deploy/charts/operator-crds/README.md @@ -1,6 +1,6 @@ # ToolHive Operator CRDs Helm Chart -![Version: 0.0.69](https://img.shields.io/badge/Version-0.0.69-informational?style=flat-square) +![Version: 0.0.70](https://img.shields.io/badge/Version-0.0.70-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for installing the ToolHive Operator CRDs into Kubernetes. @@ -40,3 +40,71 @@ To uninstall/delete the `toolhive-operator-crds` deployment: helm uninstall ``` +### Skipping CRDs + +By default, all CRDs are installed. You can selectively disable CRD groups based on your needs: + +#### Skipping Server CRDs + +To skip server-related CRDs (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig): + +```shell +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ + --set crds.install.server=false +``` + +**Important:** When server CRDs are not installed, you should also disable the server controllers in the operator: + +```shell +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ + -n toolhive-system --create-namespace \ + --set operator.features.server=false +``` + +#### Skipping Registry CRD + +To skip the registry CRD (MCPRegistry): + +```shell +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ + --set crds.install.registry=false +``` + +**Important:** When registry CRD is not installed, you should also disable the registry controller in the operator: + +```shell +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ + -n toolhive-system --create-namespace \ + --set operator.features.registry=false +``` + +#### Skipping Virtual MCP CRDs + +To skip Virtual MCP CRDs (VirtualMCPServer, VirtualMCPCompositeToolDefinition, and MCPGroup): + +```shell +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ + --set crds.install.virtualMCP=false +``` + +You can also combine this with disabling the registry CRD (see [Skipping Registry CRD](#skipping-registry-crd) section above) if you don't need registry features. + +**Important:** When Virtual MCP CRDs are not installed, you should also disable the Virtual MCP controllers in the operator: + +```shell +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ + -n toolhive-system --create-namespace \ + --set operator.features.virtualMCP=false +``` + +If you also disabled the registry CRD, disable the registry controller as well (see the [Skipping Registry CRD](#skipping-registry-crd) section above). + +This is useful for deployments that don't require Virtual MCP aggregation features. When `operator.features.virtualMCP=false`, the operator will skip setting up the VirtualMCPServer controller, MCPGroup controller, and their associated webhooks. + +## Values + +| Key | Type | Default | Description | +|-----|-------------|------|---------| +| crds.install.registry | bool | `true` | Install registry CRD (MCPRegistry). Users who only need server management without registry features can set this to false to skip installing the registry CRD. | +| crds.install.server | bool | `true` | Install server-related CRDs (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig). Users who only need registry or aggregation features can set this to false to skip installing server management CRDs. | +| crds.install.virtualMCP | bool | `true` | Install Virtual MCP CRDs (VirtualMCPServer, VirtualMCPCompositeToolDefinition and MCPGroup). Users who only need core MCP server management can set this to false to skip installing Virtual MCP aggregation features. | diff --git a/deploy/charts/operator-crds/README.md.gotmpl b/deploy/charts/operator-crds/README.md.gotmpl index d6d24d0b5..cdf55d079 100644 --- a/deploy/charts/operator-crds/README.md.gotmpl +++ b/deploy/charts/operator-crds/README.md.gotmpl @@ -48,7 +48,67 @@ To uninstall/delete the `toolhive-operator-crds` deployment: helm uninstall ``` -{{ template "chart.requirementsSection" . }} +### Skipping CRDs + +By default, all CRDs are installed. You can selectively disable CRD groups based on your needs: + +#### Skipping Server CRDs + +To skip server-related CRDs (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig): + +```shell +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ + --set crds.install.server=false +``` + +**Important:** When server CRDs are not installed, you should also disable the server controllers in the operator: + +```shell +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ + -n toolhive-system --create-namespace \ + --set operator.features.server=false +``` + +#### Skipping Registry CRD + +To skip the registry CRD (MCPRegistry): + +```shell +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ + --set crds.install.registry=false +``` + +**Important:** When registry CRD is not installed, you should also disable the registry controller in the operator: -{{ template "chart.valuesSection" . }} +```shell +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ + -n toolhive-system --create-namespace \ + --set operator.features.registry=false +``` + +#### Skipping Virtual MCP CRDs + +To skip Virtual MCP CRDs (VirtualMCPServer, VirtualMCPCompositeToolDefinition, and MCPGroup): + +```shell +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ + --set crds.install.virtualMCP=false +``` + +You can also combine this with disabling the registry CRD (see [Skipping Registry CRD](#skipping-registry-crd) section above) if you don't need registry features. + +**Important:** When Virtual MCP CRDs are not installed, you should also disable the Virtual MCP controllers in the operator: + +```shell +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ + -n toolhive-system --create-namespace \ + --set operator.features.virtualMCP=false +``` + +If you also disabled the registry CRD, disable the registry controller as well (see the [Skipping Registry CRD](#skipping-registry-crd) section above). + +This is useful for deployments that don't require Virtual MCP aggregation features. When `operator.features.virtualMCP=false`, the operator will skip setting up the VirtualMCPServer controller, MCPGroup controller, and their associated webhooks. + +{{ template "chart.requirementsSection" . }} +{{ template "chart.valuesSection" . }} \ No newline at end of file diff --git a/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml b/deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml similarity index 100% rename from deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml rename to deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml diff --git a/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpgroups.yaml b/deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpgroups.yaml similarity index 100% rename from deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpgroups.yaml rename to deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpgroups.yaml diff --git a/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpregistries.yaml b/deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpregistries.yaml similarity index 100% rename from deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpregistries.yaml rename to deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpregistries.yaml diff --git a/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpremoteproxies.yaml b/deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpremoteproxies.yaml similarity index 100% rename from deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpremoteproxies.yaml rename to deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpremoteproxies.yaml diff --git a/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpservers.yaml b/deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpservers.yaml similarity index 100% rename from deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpservers.yaml rename to deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpservers.yaml diff --git a/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcptoolconfigs.yaml b/deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcptoolconfigs.yaml similarity index 100% rename from deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcptoolconfigs.yaml rename to deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcptoolconfigs.yaml diff --git a/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml b/deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml similarity index 100% rename from deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml rename to deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml diff --git a/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpservers.yaml b/deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_virtualmcpservers.yaml similarity index 100% rename from deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpservers.yaml rename to deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_virtualmcpservers.yaml diff --git a/deploy/charts/operator-crds/templates/mcpexternalauthconfigs-crd.yaml b/deploy/charts/operator-crds/templates/mcpexternalauthconfigs-crd.yaml new file mode 100644 index 000000000..7f3c3d40e --- /dev/null +++ b/deploy/charts/operator-crds/templates/mcpexternalauthconfigs-crd.yaml @@ -0,0 +1,3 @@ +{{- if or .Values.crds.install.server .Values.crds.install.virtualMCP }} +{{ $.Files.Get "crd-files/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml" }} +{{- end }} diff --git a/deploy/charts/operator-crds/templates/mcpgroups-crd.yaml b/deploy/charts/operator-crds/templates/mcpgroups-crd.yaml new file mode 100644 index 000000000..f43a70e95 --- /dev/null +++ b/deploy/charts/operator-crds/templates/mcpgroups-crd.yaml @@ -0,0 +1,3 @@ +{{- if .Values.crds.install.virtualMCP }} +{{ $.Files.Get "crd-files/toolhive.stacklok.dev_mcpgroups.yaml" }} +{{- end }} diff --git a/deploy/charts/operator-crds/templates/mcpregistries-crd.yaml b/deploy/charts/operator-crds/templates/mcpregistries-crd.yaml new file mode 100644 index 000000000..db1181a71 --- /dev/null +++ b/deploy/charts/operator-crds/templates/mcpregistries-crd.yaml @@ -0,0 +1,3 @@ +{{- if .Values.crds.install.registry }} +{{ $.Files.Get "crd-files/toolhive.stacklok.dev_mcpregistries.yaml" }} +{{- end }} diff --git a/deploy/charts/operator-crds/templates/mcpremoteproxies-crd.yaml b/deploy/charts/operator-crds/templates/mcpremoteproxies-crd.yaml new file mode 100644 index 000000000..a2cbd396a --- /dev/null +++ b/deploy/charts/operator-crds/templates/mcpremoteproxies-crd.yaml @@ -0,0 +1,3 @@ +{{- if .Values.crds.install.server }} +{{ $.Files.Get "crd-files/toolhive.stacklok.dev_mcpremoteproxies.yaml" }} +{{- end }} diff --git a/deploy/charts/operator-crds/templates/mcpservers-crd.yaml b/deploy/charts/operator-crds/templates/mcpservers-crd.yaml new file mode 100644 index 000000000..0dd845615 --- /dev/null +++ b/deploy/charts/operator-crds/templates/mcpservers-crd.yaml @@ -0,0 +1,3 @@ +{{- if .Values.crds.install.server }} +{{ $.Files.Get "crd-files/toolhive.stacklok.dev_mcpservers.yaml" }} +{{- end }} diff --git a/deploy/charts/operator-crds/templates/mcptoolconfigs-crd.yaml b/deploy/charts/operator-crds/templates/mcptoolconfigs-crd.yaml new file mode 100644 index 000000000..bea19fa70 --- /dev/null +++ b/deploy/charts/operator-crds/templates/mcptoolconfigs-crd.yaml @@ -0,0 +1,3 @@ +{{- if .Values.crds.install.server }} +{{ $.Files.Get "crd-files/toolhive.stacklok.dev_mcptoolconfigs.yaml" }} +{{- end }} diff --git a/deploy/charts/operator-crds/templates/virtualmcpcompositetooldefinitions-crd.yaml b/deploy/charts/operator-crds/templates/virtualmcpcompositetooldefinitions-crd.yaml new file mode 100644 index 000000000..265d64169 --- /dev/null +++ b/deploy/charts/operator-crds/templates/virtualmcpcompositetooldefinitions-crd.yaml @@ -0,0 +1,3 @@ +{{- if .Values.crds.install.virtualMCP }} +{{ $.Files.Get "crd-files/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml" }} +{{- end }} diff --git a/deploy/charts/operator-crds/templates/virtualmcpservers-crd.yaml b/deploy/charts/operator-crds/templates/virtualmcpservers-crd.yaml new file mode 100644 index 000000000..40de72d7d --- /dev/null +++ b/deploy/charts/operator-crds/templates/virtualmcpservers-crd.yaml @@ -0,0 +1,3 @@ +{{- if .Values.crds.install.virtualMCP }} +{{ $.Files.Get "crd-files/toolhive.stacklok.dev_virtualmcpservers.yaml" }} +{{- end }} diff --git a/deploy/charts/operator-crds/values.yaml b/deploy/charts/operator-crds/values.yaml index 804d4fc18..60dbdae47 100644 --- a/deploy/charts/operator-crds/values.yaml +++ b/deploy/charts/operator-crds/values.yaml @@ -1 +1,15 @@ -# empty values file as we do not need any values for the CRDs at the moment +# Configuration for CRD installation +crds: + install: + # -- Install server-related CRDs (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig). + # Users who only need registry or aggregation features can set this to false to skip + # installing server management CRDs. + server: true + # -- Install registry CRD (MCPRegistry). + # Users who only need server management without registry features can set this to false + # to skip installing the registry CRD. + registry: true + # -- Install Virtual MCP CRDs (VirtualMCPServer, VirtualMCPCompositeToolDefinition and MCPGroup). + # Users who only need core MCP server management can set this to false to skip + # installing Virtual MCP aggregation features. + virtualMCP: true diff --git a/deploy/charts/operator/Chart.yaml b/deploy/charts/operator/Chart.yaml index 602cfceb4..7be652a2d 100644 --- a/deploy/charts/operator/Chart.yaml +++ b/deploy/charts/operator/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: toolhive-operator description: A Helm chart for deploying the ToolHive Operator into Kubernetes. type: application -version: 0.5.3 +version: 0.5.4 appVersion: "v0.6.6" diff --git a/deploy/charts/operator/README.md b/deploy/charts/operator/README.md index 46f4cea10..965ead0f7 100644 --- a/deploy/charts/operator/README.md +++ b/deploy/charts/operator/README.md @@ -1,6 +1,6 @@ # ToolHive Operator Helm Chart -![Version: 0.5.3](https://img.shields.io/badge/Version-0.5.3-informational?style=flat-square) +![Version: 0.5.4](https://img.shields.io/badge/Version-0.5.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying the ToolHive Operator into Kubernetes. @@ -52,7 +52,7 @@ The command removes all the Kubernetes components associated with the chart and |-----|-------------|------|---------| | fullnameOverride | string | `"toolhive-operator"` | Provide a fully-qualified name override for resources | | nameOverride | string | `""` | Override the name of the chart | -| operator | object | `{"affinity":{},"autoscaling":{"enabled":false,"maxReplicas":100,"minReplicas":1,"targetCPUUtilizationPercentage":80},"containerSecurityContext":{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}},"env":{},"features":{"experimental":false},"gc":{"gogc":75,"gomeglimit":"150MiB"},"image":"ghcr.io/stacklok/toolhive/operator:v0.6.6","imagePullPolicy":"IfNotPresent","imagePullSecrets":[],"leaderElectionRole":{"binding":{"name":"toolhive-operator-leader-election-rolebinding"},"name":"toolhive-operator-leader-election-role","rules":[{"apiGroups":[""],"resources":["configmaps"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":["coordination.k8s.io"],"resources":["leases"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":[""],"resources":["events"],"verbs":["create","patch"]}]},"livenessProbe":{"httpGet":{"path":"/healthz","port":"health"},"initialDelaySeconds":15,"periodSeconds":20},"nodeSelector":{},"podAnnotations":{},"podLabels":{},"podSecurityContext":{"runAsNonRoot":true},"ports":[{"containerPort":8080,"name":"metrics","protocol":"TCP"},{"containerPort":8081,"name":"health","protocol":"TCP"}],"proxyHost":"0.0.0.0","rbac":{"allowedNamespaces":[],"scope":"cluster"},"readinessProbe":{"httpGet":{"path":"/readyz","port":"health"},"initialDelaySeconds":5,"periodSeconds":10},"replicaCount":1,"resources":{"limits":{"cpu":"500m","memory":"128Mi"},"requests":{"cpu":"10m","memory":"64Mi"}},"serviceAccount":{"annotations":{},"automountServiceAccountToken":true,"create":true,"labels":{},"name":"toolhive-operator"},"tolerations":[],"toolhiveRunnerImage":"ghcr.io/stacklok/toolhive/proxyrunner:v0.6.6","volumeMounts":[],"volumes":[]}` | All values for the operator deployment and associated resources | +| operator | object | `{"affinity":{},"autoscaling":{"enabled":false,"maxReplicas":100,"minReplicas":1,"targetCPUUtilizationPercentage":80},"containerSecurityContext":{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}},"env":{},"features":{"experimental":false,"registry":true,"server":true,"virtualMCP":true},"gc":{"gogc":75,"gomeglimit":"150MiB"},"image":"ghcr.io/stacklok/toolhive/operator:v0.6.6","imagePullPolicy":"IfNotPresent","imagePullSecrets":[],"leaderElectionRole":{"binding":{"name":"toolhive-operator-leader-election-rolebinding"},"name":"toolhive-operator-leader-election-role","rules":[{"apiGroups":[""],"resources":["configmaps"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":["coordination.k8s.io"],"resources":["leases"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":[""],"resources":["events"],"verbs":["create","patch"]}]},"livenessProbe":{"httpGet":{"path":"/healthz","port":"health"},"initialDelaySeconds":15,"periodSeconds":20},"nodeSelector":{},"podAnnotations":{},"podLabels":{},"podSecurityContext":{"runAsNonRoot":true},"ports":[{"containerPort":8080,"name":"metrics","protocol":"TCP"},{"containerPort":8081,"name":"health","protocol":"TCP"}],"proxyHost":"0.0.0.0","rbac":{"allowedNamespaces":[],"scope":"cluster"},"readinessProbe":{"httpGet":{"path":"/readyz","port":"health"},"initialDelaySeconds":5,"periodSeconds":10},"replicaCount":1,"resources":{"limits":{"cpu":"500m","memory":"128Mi"},"requests":{"cpu":"10m","memory":"64Mi"}},"serviceAccount":{"annotations":{},"automountServiceAccountToken":true,"create":true,"labels":{},"name":"toolhive-operator"},"tolerations":[],"toolhiveRunnerImage":"ghcr.io/stacklok/toolhive/proxyrunner:v0.6.6","volumeMounts":[],"volumes":[]}` | All values for the operator deployment and associated resources | | operator.affinity | object | `{}` | Affinity settings for the operator pod | | operator.autoscaling | object | `{"enabled":false,"maxReplicas":100,"minReplicas":1,"targetCPUUtilizationPercentage":80}` | Configuration for horizontal pod autoscaling | | operator.autoscaling.enabled | bool | `false` | Enable autoscaling for the operator | @@ -60,7 +60,11 @@ The command removes all the Kubernetes components associated with the chart and | operator.autoscaling.minReplicas | int | `1` | Minimum number of replicas | | operator.autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization percentage for autoscaling | | operator.containerSecurityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}}` | Container security context settings for the operator | -| operator.env | object | `{}` | Environment variables to set in the operator container | +| operator.env | object | `{}` | Additional environment variables to set in the operator container. | +| operator.features.experimental | bool | `false` | Enable experimental features | +| operator.features.registry | bool | `true` | Enable registry controller (MCPRegistry). This automatically sets ENABLE_REGISTRY environment variable. | +| operator.features.server | bool | `true` | Enable server-related controllers (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig). This automatically sets ENABLE_SERVER environment variable. | +| operator.features.virtualMCP | bool | `true` | Enable Virtual MCP aggregation features (VirtualMCPServer, MCPGroup controllers and webhooks). Set to false to disable Virtual MCP controllers when Virtual MCP CRDs are not installed. This automatically sets ENABLE_VMCP environment variable. Requires server to be enabled (server: true). | | operator.gc | object | `{"gogc":75,"gomeglimit":"150MiB"}` | Go memory limits and garbage collection percentage for the operator container | | operator.gc.gogc | int | `75` | Go garbage collection percentage for the operator container | | operator.gc.gomeglimit | string | `"150MiB"` | Go memory limits for the operator container | diff --git a/deploy/charts/operator/generated-rbac/role.yaml b/deploy/charts/operator/generated-rbac/role.yaml new file mode 100644 index 000000000..74a25d945 --- /dev/null +++ b/deploy/charts/operator/generated-rbac/role.yaml @@ -0,0 +1,149 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: toolhive-operator-manager-role +rules: +- apiGroups: + - "" + resources: + - configmaps + - serviceaccounts + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - pods + - secrets + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - pods/attach + verbs: + - create + - get +- apiGroups: + - "" + resources: + - pods/log + verbs: + - get +- apiGroups: + - "" + resources: + - services + verbs: + - apply + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - statefulsets + verbs: + - apply + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - rolebindings + - roles + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - toolhive.stacklok.dev + resources: + - mcpexternalauthconfigs + - mcpgroups + - mcpregistries + - mcpremoteproxies + - mcpservers + - mcptoolconfigs + - virtualmcpservers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - toolhive.stacklok.dev + resources: + - mcpexternalauthconfigs/finalizers + - mcpgroups/finalizers + - mcpregistries/finalizers + - mcpservers/finalizers + - mcptoolconfigs/finalizers + verbs: + - update +- apiGroups: + - toolhive.stacklok.dev + resources: + - mcpexternalauthconfigs/status + - mcpgroups/status + - mcpregistries/status + - mcpremoteproxies/status + - mcpservers/status + - mcptoolconfigs/status + - virtualmcpservers/status + verbs: + - get + - patch + - update +- apiGroups: + - toolhive.stacklok.dev + resources: + - virtualmcpcompositetooldefinitions + verbs: + - get + - list + - watch diff --git a/deploy/charts/operator/templates/clusterrole/role.yaml b/deploy/charts/operator/templates/clusterrole/role.yaml index 74a25d945..7272b75d3 100644 --- a/deploy/charts/operator/templates/clusterrole/role.yaml +++ b/deploy/charts/operator/templates/clusterrole/role.yaml @@ -1,149 +1,3 @@ ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: toolhive-operator-manager-role -rules: -- apiGroups: - - "" - resources: - - configmaps - - serviceaccounts - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - "" - resources: - - pods - - secrets - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - pods/attach - verbs: - - create - - get -- apiGroups: - - "" - resources: - - pods/log - verbs: - - get -- apiGroups: - - "" - resources: - - services - verbs: - - apply - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - deployments - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - statefulsets - verbs: - - apply - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - rbac.authorization.k8s.io - resources: - - rolebindings - - roles - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - toolhive.stacklok.dev - resources: - - mcpexternalauthconfigs - - mcpgroups - - mcpregistries - - mcpremoteproxies - - mcpservers - - mcptoolconfigs - - virtualmcpservers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - toolhive.stacklok.dev - resources: - - mcpexternalauthconfigs/finalizers - - mcpgroups/finalizers - - mcpregistries/finalizers - - mcpservers/finalizers - - mcptoolconfigs/finalizers - verbs: - - update -- apiGroups: - - toolhive.stacklok.dev - resources: - - mcpexternalauthconfigs/status - - mcpgroups/status - - mcpregistries/status - - mcpremoteproxies/status - - mcpservers/status - - mcptoolconfigs/status - - virtualmcpservers/status - verbs: - - get - - patch - - update -- apiGroups: - - toolhive.stacklok.dev - resources: - - virtualmcpcompositetooldefinitions - verbs: - - get - - list - - watch +{{- if not (default false .Values.operator.testMode) }} +{{ $.Files.Get "generated-rbac/role.yaml" }} +{{- end }} diff --git a/deploy/charts/operator/templates/clusterrole/rolebinding.yaml b/deploy/charts/operator/templates/clusterrole/rolebinding.yaml index 57324ff00..a3392c8dd 100644 --- a/deploy/charts/operator/templates/clusterrole/rolebinding.yaml +++ b/deploy/charts/operator/templates/clusterrole/rolebinding.yaml @@ -1,3 +1,5 @@ +{{- if not (default false .Values.operator.testMode) }} + {{- if eq .Values.operator.rbac.scope "cluster" }} --- apiVersion: rbac.authorization.k8s.io/v1 @@ -35,4 +37,6 @@ roleRef: kind: ClusterRole name: toolhive-operator-manager-role {{- end }} -{{- end }} \ No newline at end of file +{{- end }} + +{{- end }} diff --git a/deploy/charts/operator/templates/deployment.yaml b/deploy/charts/operator/templates/deployment.yaml index d42e00f19..67774023d 100644 --- a/deploy/charts/operator/templates/deployment.yaml +++ b/deploy/charts/operator/templates/deployment.yaml @@ -1,3 +1,4 @@ +{{- if not (default false .Values.operator.testMode) }} apiVersion: apps/v1 kind: Deployment metadata: @@ -47,7 +48,6 @@ spec: value: {{ .Values.operator.gc.gomeglimit | quote }} - name: GOGC value: {{ .Values.operator.gc.gogc | quote }} - # Always use structured JSON logs in Kubernetes (not configurable) - name: UNSTRUCTURED_LOGS value: "false" - name: POD_NAMESPACE @@ -58,6 +58,12 @@ spec: value: "true" - name: ENABLE_EXPERIMENTAL_FEATURES value: {{ .Values.operator.features.experimental | quote }} + - name: ENABLE_SERVER + value: {{ .Values.operator.features.server | quote }} + - name: ENABLE_REGISTRY + value: {{ .Values.operator.features.registry | quote }} + - name: ENABLE_VMCP + value: {{ .Values.operator.features.virtualMCP | quote }} {{- if eq .Values.operator.rbac.scope "namespace" }} - name: WATCH_NAMESPACE value: "{{ .Values.operator.rbac.allowedNamespaces | join "," }}" @@ -97,3 +103,4 @@ spec: tolerations: {{- toYaml . | nindent 8 }} {{- end }} +{{- end }} diff --git a/deploy/charts/operator/templates/hpa.yaml b/deploy/charts/operator/templates/hpa.yaml index 4b81539fd..734b7375e 100644 --- a/deploy/charts/operator/templates/hpa.yaml +++ b/deploy/charts/operator/templates/hpa.yaml @@ -1,4 +1,4 @@ -{{- if .Values.operator.autoscaling.enabled }} +{{- if and (not (default false .Values.operator.testMode)) .Values.operator.autoscaling.enabled }} apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: diff --git a/deploy/charts/operator/templates/leader-election-role.yaml b/deploy/charts/operator/templates/leader-election-role.yaml index 315156a78..5da93a8d3 100644 --- a/deploy/charts/operator/templates/leader-election-role.yaml +++ b/deploy/charts/operator/templates/leader-election-role.yaml @@ -1,3 +1,4 @@ +{{- if not (default false .Values.operator.testMode) }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role @@ -23,4 +24,5 @@ roleRef: subjects: - kind: ServiceAccount name: {{ .Values.operator.serviceAccount.name }} - namespace: {{ .Release.Namespace }} \ No newline at end of file + namespace: {{ .Release.Namespace }} + {{- end }} \ No newline at end of file diff --git a/deploy/charts/operator/templates/registry-api-clusterrole.yaml b/deploy/charts/operator/templates/registry-api-clusterrole.yaml index 34cebb100..eca86cd78 100644 --- a/deploy/charts/operator/templates/registry-api-clusterrole.yaml +++ b/deploy/charts/operator/templates/registry-api-clusterrole.yaml @@ -1,3 +1,4 @@ +{{- if not (default false .Values.operator.testMode) }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: @@ -15,3 +16,4 @@ rules: - get - list - watch +{{- end }} diff --git a/deploy/charts/operator/templates/registry-api-clusterrolebinding.yaml b/deploy/charts/operator/templates/registry-api-clusterrolebinding.yaml index 039c3daae..8f01c0a94 100644 --- a/deploy/charts/operator/templates/registry-api-clusterrolebinding.yaml +++ b/deploy/charts/operator/templates/registry-api-clusterrolebinding.yaml @@ -1,3 +1,4 @@ +{{- if not (default false .Values.operator.testMode) }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: @@ -14,3 +15,4 @@ subjects: - kind: ServiceAccount name: {{ .Values.registryAPI.serviceAccount.name }} namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/deploy/charts/operator/templates/registry-api-serviceaccount.yaml b/deploy/charts/operator/templates/registry-api-serviceaccount.yaml index 0a4db7a71..0779fba3c 100644 --- a/deploy/charts/operator/templates/registry-api-serviceaccount.yaml +++ b/deploy/charts/operator/templates/registry-api-serviceaccount.yaml @@ -1,3 +1,4 @@ +{{- if not (default false .Values.operator.testMode) }} apiVersion: v1 kind: ServiceAccount metadata: @@ -15,3 +16,4 @@ metadata: {{- toYaml .Values.registryAPI.serviceAccount.annotations | nindent 4 }} {{- end }} automountServiceAccountToken: {{ .Values.registryAPI.serviceAccount.automountServiceAccountToken }} +{{- end }} diff --git a/deploy/charts/operator/templates/serviceaccount.yaml b/deploy/charts/operator/templates/serviceaccount.yaml index 699b6379e..776a47050 100644 --- a/deploy/charts/operator/templates/serviceaccount.yaml +++ b/deploy/charts/operator/templates/serviceaccount.yaml @@ -1,4 +1,4 @@ -{{- if .Values.operator.serviceAccount.create }} +{{- if and (not (default false .Values.operator.testMode)) .Values.operator.serviceAccount.create }} apiVersion: v1 kind: ServiceAccount metadata: diff --git a/deploy/charts/operator/values-openshift.yaml b/deploy/charts/operator/values-openshift.yaml index 20fd79915..e7ad3e314 100644 --- a/deploy/charts/operator/values-openshift.yaml +++ b/deploy/charts/operator/values-openshift.yaml @@ -5,9 +5,21 @@ fullnameOverride: "toolhive-operator" # -- All values for the operator deployment and associated resources operator: - # Whether to enable experimental features + # Feature flags to enable/disable controller groups features: + # -- Enable experimental features experimental: false + # -- Enable server-related controllers (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig). + # This automatically sets ENABLE_SERVER environment variable. + server: true + # -- Enable registry controller (MCPRegistry). + # This automatically sets ENABLE_REGISTRY environment variable. + registry: true + # -- Enable Virtual MCP aggregation features (VirtualMCPServer, MCPGroup controllers and webhooks). + # Set to false to disable Virtual MCP controllers when Virtual MCP CRDs are not installed. + # This automatically sets ENABLE_VMCP environment variable. + # Requires server to be enabled (server: true). + virtualMCP: true # -- Number of replicas for the operator deployment replicaCount: 1 @@ -24,7 +36,10 @@ operator: # -- Host for the proxy deployed by the operator proxyHost: 0.0.0.0 - # -- Environment variables to set in the operator container + # -- Additional environment variables to set in the operator container. + # Note: Feature flags (server, registry, virtualMCP) should be set via operator.features.* helm values + # instead of using env variables directly. This allows for proper documentation and validation. + # Use this only for custom environment variables not covered by feature flags. env: {} # -- List of ports to expose from the operator container diff --git a/deploy/charts/operator/values.yaml b/deploy/charts/operator/values.yaml index d8264f6b7..4a42b81b1 100644 --- a/deploy/charts/operator/values.yaml +++ b/deploy/charts/operator/values.yaml @@ -5,9 +5,21 @@ fullnameOverride: "toolhive-operator" # -- All values for the operator deployment and associated resources operator: - # Whether to enable experimental features + # Feature flags to enable/disable controller groups features: + # -- Enable experimental features experimental: false + # -- Enable server-related controllers (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig). + # This automatically sets ENABLE_SERVER environment variable. + server: true + # -- Enable registry controller (MCPRegistry). + # This automatically sets ENABLE_REGISTRY environment variable. + registry: true + # -- Enable Virtual MCP aggregation features (VirtualMCPServer, MCPGroup controllers and webhooks). + # Set to false to disable Virtual MCP controllers when Virtual MCP CRDs are not installed. + # This automatically sets ENABLE_VMCP environment variable. + # Requires server to be enabled (server: true). + virtualMCP: true # -- Number of replicas for the operator deployment replicaCount: 1 @@ -24,7 +36,7 @@ operator: # -- Host for the proxy deployed by the operator proxyHost: 0.0.0.0 - # -- Environment variables to set in the operator container + # -- Additional environment variables to set in the operator container. env: {} # -- List of ports to expose from the operator container