Skip to content

Commit

Permalink
feat: require Gateway CRD for controller (#156)
Browse files Browse the repository at this point in the history
Add flags to manually disable controllers.

Disable Gateway controller if CRD not present.

Return default config from function.
  • Loading branch information
rainest committed Aug 16, 2022
1 parent fe37a8c commit 8febaaf
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 55 deletions.
110 changes: 110 additions & 0 deletions internal/manager/controller_setup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package manager

import (
"reflect"

"github.com/kong/gateway-operator/controllers"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime/schema"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/manager"
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
)

// -----------------------------------------------------------------------------
// Controller Manager - Controller Definition Interfaces
// -----------------------------------------------------------------------------

// Controller is a Kubernetes controller that can be plugged into Manager.
type Controller interface {
SetupWithManager(ctrl.Manager) error
}

// AutoHandler decides whether the specific controller shall be enabled (true) or disabled (false).
type AutoHandler func(client.Client) bool

// ControllerDef is a specification of a Controller that can be conditionally registered with Manager.
type ControllerDef struct {
Enabled bool
AutoHandler AutoHandler
Controller Controller
}

// Name returns a human-readable name of the controller.
func (c *ControllerDef) Name() string {
return reflect.TypeOf(c.Controller).String()
}

// MaybeSetupWithManager runs SetupWithManager on the controller if it is enabled
// and its AutoHandler (if any) indicates that it can load.
func (c *ControllerDef) MaybeSetupWithManager(mgr ctrl.Manager) error {
if !c.Enabled {
return nil
}

if c.AutoHandler != nil {
if enable := c.AutoHandler(mgr.GetClient()); !enable {
return nil
}
}
return c.Controller.SetupWithManager(mgr)
}

func setupControllers(mgr manager.Manager, c *Config) []ControllerDef {
controllers := []ControllerDef{
// Gateway controller
{
Enabled: c.GatewayControllerEnabled,
AutoHandler: crdExistsChecker{
GVR: schema.GroupVersionResource{
Group: gatewayv1alpha2.SchemeGroupVersion.Group,
Version: gatewayv1alpha2.SchemeGroupVersion.Version,
Resource: "gateways",
},
}.CRDExists,
Controller: &controllers.GatewayReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
},
},
// ControlPlane controller
{
Enabled: c.ControlPlaneControllerEnabled,
Controller: &controllers.ControlPlaneReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
ClusterCASecretName: c.ClusterCASecretName,
ClusterCASecretNamespace: c.ClusterCASecretNamespace,
},
},
// DataPlane controller
{
Enabled: c.DataPlaneControllerEnabled,
Controller: &controllers.DataPlaneReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
ClusterCASecretName: c.ClusterCASecretName,
ClusterCASecretNamespace: c.ClusterCASecretNamespace,
},
},
}

return controllers
}

// crdExistsChecker verifies whether the resource type defined by GVR is supported by the k8s apiserver.
type crdExistsChecker struct {
GVR schema.GroupVersionResource
}

// CRDExists returns true iff the apiserver supports the specified group/version/resource.
func (c crdExistsChecker) CRDExists(r client.Client) bool {
return CRDExists(r, c.GVR)
}

// CRDExists returns false if CRD does not exist.
func CRDExists(client client.Client, gvr schema.GroupVersionResource) bool {
_, err := client.RESTMapper().KindFor(gvr)
return !meta.IsNoMatchError(err)
}
58 changes: 26 additions & 32 deletions internal/manager/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ import (
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"

operatorv1alpha1 "github.com/kong/gateway-operator/apis/v1alpha1"
"github.com/kong/gateway-operator/controllers"
"github.com/kong/gateway-operator/internal/admission"
"github.com/kong/gateway-operator/internal/manager/metadata"
"github.com/kong/gateway-operator/internal/telemetry"
Expand Down Expand Up @@ -90,18 +89,27 @@ type Config struct {
KubeconfigPath string
ClusterCASecretName string
ClusterCASecretNamespace string

GatewayControllerEnabled bool
ControlPlaneControllerEnabled bool
DataPlaneControllerEnabled bool
}

var DefaultConfig = Config{
MetricsAddr: ":8080",
ProbeAddr: ":8081",
WebhookPort: 9443,
DevelopmentMode: false,
LeaderElection: true,
ClusterCASecretName: "kong-operator-ca",
// TODO: Extract this into a named const and use it in all the placed where
// "kong-system" is used verbatim: https://github.com/Kong/gateway-operator/pull/149.
ClusterCASecretNamespace: "kong-system",
func DefaultConfig() Config {
return Config{
MetricsAddr: ":8080",
ProbeAddr: ":8081",
WebhookPort: 9443,
DevelopmentMode: false,
LeaderElection: true,
ClusterCASecretName: "kong-operator-ca",
// TODO: Extract this into a named const and use it in all the placed where
// "kong-system" is used verbatim: https://github.com/Kong/gateway-operator/pull/149.
ClusterCASecretNamespace: "kong-system",
GatewayControllerEnabled: true,
ControlPlaneControllerEnabled: true,
DataPlaneControllerEnabled: true,
}
}

func Run(cfg Config) error {
Expand Down Expand Up @@ -143,28 +151,14 @@ func Run(cfg Config) error {
return fmt.Errorf("unable to start manager: %w", err)
}

if err = (&controllers.DataPlaneReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
ClusterCASecretName: cfg.ClusterCASecretName,
ClusterCASecretNamespace: cfg.ClusterCASecretNamespace,
}).SetupWithManager(mgr); err != nil {
return fmt.Errorf("unable to create controller DataPlane: %w", err)
}
if err = (&controllers.ControlPlaneReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
ClusterCASecretName: cfg.ClusterCASecretName,
ClusterCASecretNamespace: cfg.ClusterCASecretNamespace,
}).SetupWithManager(mgr); err != nil {
return fmt.Errorf("unable to create controller ControlPlane: %w", err)
}
if err = (&controllers.GatewayReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
return fmt.Errorf("unable to create controller Gateway: %w", err)
// load controllers
controllers := setupControllers(mgr, &cfg)
for _, c := range controllers {
if err := c.MaybeSetupWithManager(mgr); err != nil {
return fmt.Errorf("unable to create controller %q: %w", c.Name(), err)
}
}

//+kubebuilder:scaffold:builder

if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
Expand Down
53 changes: 32 additions & 21 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,18 @@ import (

func main() {
var (
metricsAddr string
probeAddr string
disableLeaderElection bool
controllerName string
anonymousReports bool
apiServerHost string
kubeconfigPath string
clusterCASecret string
clusterCASecretNamespace string
metricsAddr string
probeAddr string
disableLeaderElection bool
controllerName string
anonymousReports bool
apiServerHost string
kubeconfigPath string
clusterCASecret string
clusterCASecretNamespace string
enableControllerGateway bool
enableControllerControlPlane bool
enableControllerDataPlane bool
)

flagSet := flag.NewFlagSet("", flag.ExitOnError)
Expand All @@ -52,17 +55,22 @@ func main() {
flagSet.StringVar(&controllerName, "controller-name", "", "a controller name to use if other than the default, only needed for multi-tenancy")
flagSet.StringVar(&clusterCASecret, "cluster-ca-secret", "kong-operator-ca", "name of the Secret containing the cluster CA certificate")
flagSet.StringVar(&clusterCASecretNamespace, "cluster-ca-secret-namespace", "", "name of the namespace for Secret containing the cluster CA certificate")

flagSet.BoolVar(&enableControllerGateway, "enable-controller-gateway", true, "Enable the Gateway controller.")
flagSet.BoolVar(&enableControllerControlPlane, "enable-controller-controlplane", true, "Enable the ControlPlane controller.")
flagSet.BoolVar(&enableControllerDataPlane, "enable-controller-dataplane", true, "Enable the DataPlane controller.")

if err := flagSet.Parse(os.Args[1:]); err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
developmentModeEnabled := manager.DefaultConfig.DevelopmentMode
developmentModeEnabled := manager.DefaultConfig().DevelopmentMode
if v := os.Getenv("CONTROLLER_DEVELOPMENT_MODE"); v == "true" { // TODO: clean env handling https://github.com/Kong/gateway-operator/issues/19
fmt.Println("INFO: development mode has been enabled")
developmentModeEnabled = true
}

leaderElection := manager.DefaultConfig.LeaderElection
leaderElection := manager.DefaultConfig().LeaderElection
if disableLeaderElection {
fmt.Println("INFO: leader election has been disabled")
leaderElection = false
Expand Down Expand Up @@ -91,16 +99,19 @@ func main() {
}

cfg := manager.Config{
DevelopmentMode: developmentModeEnabled,
MetricsAddr: metricsAddr,
ProbeAddr: probeAddr,
LeaderElection: leaderElection,
ControllerName: controllerName,
AnonymousReports: anonymousReports,
APIServerPath: apiServerHost,
KubeconfigPath: kubeconfigPath,
ClusterCASecretName: clusterCASecret,
ClusterCASecretNamespace: clusterCASecretNamespace,
DevelopmentMode: developmentModeEnabled,
MetricsAddr: metricsAddr,
ProbeAddr: probeAddr,
LeaderElection: leaderElection,
ControllerName: controllerName,
AnonymousReports: anonymousReports,
APIServerPath: apiServerHost,
KubeconfigPath: kubeconfigPath,
ClusterCASecretName: clusterCASecret,
ClusterCASecretNamespace: clusterCASecretNamespace,
GatewayControllerEnabled: enableControllerGateway,
ControlPlaneControllerEnabled: enableControllerControlPlane,
DataPlaneControllerEnabled: enableControllerDataPlane,
}

if err := manager.Run(cfg); err != nil {
Expand Down
7 changes: 5 additions & 2 deletions test/integration/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func TestMain(m *testing.M) {
timeout := time.Now().Add(time.Minute)
for timeout.After(time.Now()) {
err = func() error {
ca, err := k8sClient.CoreV1().Secrets("kong-system").Get(ctx, manager.DefaultConfig.ClusterCASecretName, metav1.GetOptions{})
ca, err := k8sClient.CoreV1().Secrets("kong-system").Get(ctx, manager.DefaultConfig().ClusterCASecretName, metav1.GetOptions{})
if err != nil {
return err
}
Expand Down Expand Up @@ -242,10 +242,13 @@ func setupControllerLogger() (closeLogFile func() error) {
}

func startControllerManager() {
cfg := manager.DefaultConfig
cfg := manager.DefaultConfig()
cfg.LeaderElection = false
cfg.DevelopmentMode = true
cfg.ControllerName = "konghq.com/gateway-operator-integration-tests"
cfg.GatewayControllerEnabled = true
cfg.ControlPlaneControllerEnabled = true
cfg.DataPlaneControllerEnabled = true

if runWebhookTests {
cfg.WebhookCertDir = webhookCertDir
Expand Down

0 comments on commit 8febaaf

Please sign in to comment.