Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,7 @@ admission policy installation; once an Installation exists it is the authority o
UseV3CRDs: v3CRDs,
APIDiscovery: apiDiscovery,
Extensions: extensionRegistry,
Controllers: enterprise.Controllers(variant),
}

err = controller.AddToManager(mgr, options)
Expand Down
1 change: 1 addition & 0 deletions docs/principles.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ API design principles and the Go/kubebuilder coding conventions for `api/v1` CRD
## Variants

- **Core code is variant-blind.** Controllers and render packages outside `pkg/enterprise` must not name a variant, in code or in comments. Behavior a single variant needs registers through `pkg/extensions`.
- **A controller only one variant runs lives in `pkg/enterprise/controller`, and its render code in `pkg/enterprise/render`.** Both mirror the core tree they came from. The controller is contributed through the controller list on `ControllerOptions` rather than named by `AddToManager`, so it carries no variant check of its own.

## Security

Expand Down
12 changes: 4 additions & 8 deletions internal/controller/controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,6 @@ func AddToManager(mgr ctrl.Manager, options options.ControllerOptions) error {
}).SetupWithManager(mgr, options); err != nil {
return fmt.Errorf("failed to create controller %s: %v", "ApplicationLayer", err)
}
if err := (&MonitorReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("Monitor"),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr, options); err != nil {
return fmt.Errorf("failed to create controller %s: %v", "Monitor", err)
}
if err := (&ManagerReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("Manager"),
Expand Down Expand Up @@ -202,5 +195,8 @@ func AddToManager(mgr ctrl.Manager, options options.ControllerOptions) error {
return fmt.Errorf("failed to create controller %s: %v", "OpenTelemetry", err)
}
// +kubebuilder:scaffold:builder
return nil

// The controllers only the running variant supplies, added last so that a variant
// can watch resources the core controllers own.
return options.AddControllers(mgr)
}
41 changes: 0 additions & 41 deletions internal/controller/monitor_controller.go

This file was deleted.

26 changes: 26 additions & 0 deletions pkg/controller/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ package options

import (
"context"
"fmt"

ctrl "sigs.k8s.io/controller-runtime"

v1 "github.com/tigera/operator/api/v1"
"github.com/tigera/operator/pkg/common"
Expand Down Expand Up @@ -77,4 +80,27 @@ type ControllerOptions struct {
// Extensions are the variant extensions the operator runs with, for the Variant
// above. The core operator leaves them unset and runs the base behavior.
Extensions extensions.Extensions

// Controllers are the reconcilers the running variant adds to the core set. The
// core operator leaves them unset and runs only the controllers every variant runs.
Controllers []Controller
}

// AddControllers adds the reconcilers the running variant contributes.
func (o ControllerOptions) AddControllers(mgr ctrl.Manager) error {
for _, c := range o.Controllers {
if err := c.Add(mgr, o); err != nil {
return fmt.Errorf("failed to create controller %s: %v", c.Name, err)
}
}
return nil
}

// Controller is a reconciler a variant contributes, so that the core controller
// manager can add it without naming the type.
type Controller struct {
// Name identifies the controller when its setup fails.
Name string

Add func(mgr ctrl.Manager, opts ControllerOptions) error
}
75 changes: 75 additions & 0 deletions pkg/controller/options/options_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright (c) 2026 Tigera, Inc. All rights reserved.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package options_test

import (
"errors"
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
ctrl "sigs.k8s.io/controller-runtime"

operatorv1 "github.com/tigera/operator/api/v1"
"github.com/tigera/operator/pkg/controller/options"
)

func TestOptions(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "pkg/controller/options Suite")
}

var _ = Describe("AddControllers", func() {
It("adds every contributed controller, in order", func() {
var added []string
add := func(name string) options.Controller {
return options.Controller{Name: name, Add: func(ctrl.Manager, options.ControllerOptions) error {
added = append(added, name)
return nil
}}
}
opts := options.ControllerOptions{Controllers: []options.Controller{add("First"), add("Second")}}

Expect(opts.AddControllers(nil)).NotTo(HaveOccurred())
Expect(added).To(Equal([]string{"First", "Second"}))
})

It("passes the options through to the controller", func() {
var got options.ControllerOptions
opts := options.ControllerOptions{
Variant: operatorv1.CalicoEnterprise,
Controllers: []options.Controller{{Name: "Monitor", Add: func(_ ctrl.Manager, o options.ControllerOptions) error {
got = o
return nil
}}},
}

Expect(opts.AddControllers(nil)).NotTo(HaveOccurred())
Expect(got.Variant).To(Equal(operatorv1.CalicoEnterprise))
})

It("names the controller that failed", func() {
opts := options.ControllerOptions{Controllers: []options.Controller{{
Name: "Monitor",
Add: func(ctrl.Manager, options.ControllerOptions) error { return errors.New("no watch") },
}}}

Expect(opts.AddControllers(nil)).To(MatchError(ContainSubstring("controller Monitor: no watch")))
})

It("does nothing when the variant contributes none", func() {
Expect(options.ControllerOptions{}.AddControllers(nil)).NotTo(HaveOccurred())
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,6 @@ const ResourceName = "monitor"
var log = logf.Log.WithName("controller_monitor")

func Add(mgr manager.Manager, opts options.ControllerOptions) error {
if !opts.Variant.IsEnterprise() {
return nil
}

prometheusReady := &utils.ReadyFlag{}
tierWatchReady := &utils.ReadyFlag{}
licenseAPIReady := &utils.ReadyFlag{}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ func TestStatus(t *testing.T) {
logf.SetLogger(zap.New(zap.WriteTo(ginkgo.GinkgoWriter), zap.UseDevMode(true), zap.Level(uzap.NewAtomicLevelAt(uzap.DebugLevel))))
gomega.RegisterFailHandler(ginkgo.Fail)
suiteConfig, reporterConfig := ginkgo.GinkgoConfiguration()
reporterConfig.JUnitReport = "../../../report/ut/monitor_controller_suite.xml"
ginkgo.RunSpecs(t, "pkg/controller/monitor Suite", suiteConfig, reporterConfig)
reporterConfig.JUnitReport = "../../../../report/ut/monitor_controller_suite.xml"
ginkgo.RunSpecs(t, "pkg/enterprise/controller/monitor Suite", suiteConfig, reporterConfig)
}
33 changes: 33 additions & 0 deletions pkg/enterprise/controllers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) 2026 Tigera, Inc. All rights reserved.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package enterprise

import (
operatorv1 "github.com/tigera/operator/api/v1"
"github.com/tigera/operator/pkg/controller/options"
"github.com/tigera/operator/pkg/enterprise/controller/monitor"
)

// Controllers returns the reconcilers only Calico Enterprise runs, for the caller to
// pass to the controller manager. Registering here is what gates them, so the
// controllers themselves do not check the variant.
func Controllers(variant operatorv1.ProductVariant) []options.Controller {
if !variant.IsEnterprise() {
return nil
}
return []options.Controller{
{Name: "Monitor", Add: monitor.Add},
}
}
43 changes: 43 additions & 0 deletions pkg/enterprise/controllers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) 2026 Tigera, Inc. All rights reserved.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package enterprise_test

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

operatorv1 "github.com/tigera/operator/api/v1"
"github.com/tigera/operator/pkg/enterprise"
)

var _ = Describe("Controllers", func() {
DescribeTable("contributes the Enterprise-only controllers",
func(variant operatorv1.ProductVariant) {
names := []string{}
for _, c := range enterprise.Controllers(variant) {
Expect(c.Add).NotTo(BeNil())
names = append(names, c.Name)
}
Expect(names).To(ContainElement("Monitor"))
},
Entry("CalicoEnterprise", operatorv1.CalicoEnterprise),
//nolint:staticcheck // SA1019: the deprecated spelling is what this covers
Entry("TigeraSecureEnterprise", operatorv1.TigeraSecureEnterprise),
)

It("contributes nothing for Calico", func() {
Expect(enterprise.Controllers(operatorv1.Calico)).To(BeEmpty())
})
})
2 changes: 1 addition & 1 deletion pkg/enterprise/csr/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ import (

operatorv1 "github.com/tigera/operator/api/v1"
"github.com/tigera/operator/pkg/controller"
"github.com/tigera/operator/pkg/controller/monitor"
"github.com/tigera/operator/pkg/controller/utils"
"github.com/tigera/operator/pkg/ctrlruntime"
"github.com/tigera/operator/pkg/enterprise/controller/monitor"
eutils "github.com/tigera/operator/pkg/enterprise/utils"
"github.com/tigera/operator/pkg/extensions"
"github.com/tigera/operator/pkg/render"
Expand Down
2 changes: 1 addition & 1 deletion pkg/enterprise/csr/extension_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ import (
operatorv1 "github.com/tigera/operator/api/v1"
"github.com/tigera/operator/pkg/apis"
"github.com/tigera/operator/pkg/controller"
"github.com/tigera/operator/pkg/controller/monitor"
"github.com/tigera/operator/pkg/ctrlruntime"
ctrlrfake "github.com/tigera/operator/pkg/ctrlruntime/client/fake"
"github.com/tigera/operator/pkg/dns"
"github.com/tigera/operator/pkg/enterprise/controller/monitor"
"github.com/tigera/operator/pkg/render"
rmonitor "github.com/tigera/operator/pkg/render/monitor"
)
Expand Down
1 change: 1 addition & 0 deletions test/mainline_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ func setupManager(manageCRDs bool, multiTenant bool, variant operator.ProductVar
DetectedProvider: operator.ProviderNone,
Variant: variant,
Extensions: enterprise.New(variant, eoptions.Options{}),
Controllers: enterprise.Controllers(variant),
ManageCRDs: manageCRDs,
ShutdownContext: ctx,
K8sClientset: clientset,
Expand Down