Skip to content

Commit

Permalink
feat: this adds Config.Validate() to allow validation of configuratio…
Browse files Browse the repository at this point in the history
…n options before starting the manager
  • Loading branch information
pmalek committed Jan 19, 2023
1 parent 59d047f commit d475b26
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 81 deletions.
5 changes: 5 additions & 0 deletions internal/cmd/rootcmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,14 @@ func RunWithLogger(ctx context.Context, c *manager.Config, deprecatedLogger logr
return fmt.Errorf("failed to setup signal handler: %w", err)
}

if err := c.Validate(); err != nil {
return fmt.Errorf("failed to validate config: %w", err)
}

diag, err := StartDiagnosticsServer(ctx, manager.DiagnosticsPort, c, logger)
if err != nil {
return fmt.Errorf("failed to start diagnostics server: %w", err)
}

return manager.Run(ctx, c, diag.ConfigDumps, deprecatedLogger)
}
49 changes: 42 additions & 7 deletions internal/manager/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ package manager
import (
"context"
"fmt"
"regexp"
"strings"
"time"

"github.com/kong/go-kong/kong"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
cliflag "k8s.io/component-base/cli/flag"
Expand Down Expand Up @@ -57,7 +60,6 @@ type Config struct {
// Kubernetes configurations
KubeconfigPath string
IngressClassName string
EnableLeaderElection bool
LeaderElectionNamespace string
LeaderElectionID string
Concurrency int
Expand All @@ -66,9 +68,10 @@ type Config struct {
GatewayAPIControllerName string

// Ingress status
PublishService string
PublishStatusAddress []string
UpdateStatus bool
PublishService string
publishServiceNamespaceName types.NamespacedName
PublishStatusAddress []string
UpdateStatus bool

// Kubernetes API toggling
IngressExtV1beta1Enabled bool
Expand Down Expand Up @@ -101,12 +104,37 @@ type Config struct {
// helpful for advanced cases with load-balancers so that the ingress
// controller can be gracefully removed/drained from their rotation.
TermDelay time.Duration

flagSet *pflag.FlagSet
}

// -----------------------------------------------------------------------------
// Controller Manager - Config - Methods
// -----------------------------------------------------------------------------

// Validate validates the config. With time this logic may grow to invalidate
// incorrect configurations.
func (c *Config) Validate() error {
if !isControllerNameValid(c.GatewayAPIControllerName) {
return fmt.Errorf("--gateway-api-controller-name (%s) is invalid. The expected format is example.com/controller-name", c.GatewayAPIControllerName)
}

// if publish service has been provided, validate it and save the publish
// service namespaced name.
if c.PublishService != "" {
publishServiceSplit := strings.SplitN(c.PublishService, "/", 3)
if len(publishServiceSplit) != 2 {
return fmt.Errorf("--publish-service was expected to be in format <namespace>/<name> but got %s", c.PublishService)
}
c.publishServiceNamespaceName = types.NamespacedName{
Namespace: publishServiceSplit[0],
Name: publishServiceSplit[1],
}
}

return nil
}

// FlagSet binds the provided Config to commandline flags.
func (c *Config) FlagSet() *pflag.FlagSet {
flagSet := pflag.NewFlagSet("", pflag.ExitOnError)
Expand Down Expand Up @@ -218,7 +246,7 @@ func (c *Config) FlagSet() *pflag.FlagSet {

// Deprecated flags

flagSet.Float32Var(&c.ProxySyncSeconds, "sync-rate-limit", dataplane.DefaultSyncSeconds, "Use --proxy-sync-seconds instead")
_ = flagSet.Float32("sync-rate-limit", dataplane.DefaultSyncSeconds, "Use --proxy-sync-seconds instead")
_ = flagSet.MarkDeprecated("sync-rate-limit", "Use --proxy-sync-seconds instead")

_ = flagSet.Int("stderrthreshold", 0, "Has no effect and will be removed in future releases (see github issue #1297)")
Expand All @@ -230,9 +258,10 @@ func (c *Config) FlagSet() *pflag.FlagSet {
_ = flagSet.String("kong-custom-entities-secret", "", "Will be removed in next major release.")
_ = flagSet.MarkDeprecated("kong-custom-entities-secret", "Will be removed in next major release.")

flagSet.BoolVar(&c.EnableLeaderElection, "leader-elect", false, "DEPRECATED as of 2.1.0 leader election behavior is determined automatically and this flag has no effect")
_ = flagSet.MarkDeprecated("leader-elect", "DEPRECATED as of 2.1.0 leader election behavior is determined automatically and this flag has no effect")
_ = flagSet.Bool("leader-elect", false, "DEPRECATED as of 2.1.0: leader election behavior is determined automatically based on the Kong database setting and this flag has no effect")
_ = flagSet.MarkDeprecated("leader-elect", "DEPRECATED as of 2.1.0: leader election behavior is determined automatically based on the Kong database setting and this flag has no effect")

c.flagSet = flagSet
return flagSet
}

Expand Down Expand Up @@ -277,3 +306,9 @@ func (c *Config) GetKubeClient() (client.Client, error) {
}
return client.New(conf, client.Options{})
}

func isControllerNameValid(controllerName string) bool {
// https://github.com/kubernetes-sigs/gateway-api/blob/547122f7f55ac0464685552898c560658fb40073/apis/v1beta1/shared_types.go#L448-L463
re := regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$`)
return re.Match([]byte(controllerName))
}
4 changes: 2 additions & 2 deletions internal/manager/feature_gates.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ const (
)

// setupFeatureGates converts feature gates to controller enablement.
func setupFeatureGates(setupLog logr.Logger, c *Config) (map[string]bool, error) {
func setupFeatureGates(setupLog logr.Logger, featureGates map[string]bool) (map[string]bool, error) {
// generate a map of feature gates by string names to their controller enablement
ctrlMap := getFeatureGatesDefaults()

// override the default settings
for feature, enabled := range c.FeatureGates {
for feature, enabled := range featureGates {
setupLog.Info("found configuration option for gated feature", "feature", feature, "enabled", enabled)
_, ok := ctrlMap[feature]
if !ok {
Expand Down
11 changes: 5 additions & 6 deletions internal/manager/feature_gates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,23 @@ func TestFeatureGates(t *testing.T) {
baseLogger.SetOutput(out)
baseLogger.SetLevel(logrus.DebugLevel)
setupLog := logrusr.New(baseLogger)
config := new(Config)

t.Log("verifying feature gates setup defaults when no feature gates are configured")
fgs, err := setupFeatureGates(setupLog, config)
fgs, err := setupFeatureGates(setupLog, nil)
assert.NoError(t, err)
assert.Len(t, fgs, len(getFeatureGatesDefaults()))

t.Log("verifying feature gates setup results when valid feature gates options are present")
config.FeatureGates = map[string]bool{gatewayFeature: true}
fgs, err = setupFeatureGates(setupLog, config)
featureGates := map[string]bool{gatewayFeature: true}
fgs, err = setupFeatureGates(setupLog, featureGates)
assert.NoError(t, err)
assert.True(t, fgs[gatewayFeature])

t.Log("configuring several invalid feature gates options")
config.FeatureGates = map[string]bool{"invalidGateway": true}
featureGates = map[string]bool{"invalidGateway": true}

t.Log("verifying feature gates setup results when invalid feature gates options are present")
_, err = setupFeatureGates(setupLog, config)
_, err = setupFeatureGates(setupLog, featureGates)
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalidGateway is not a valid feature")
}
56 changes: 31 additions & 25 deletions internal/manager/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@ import (
"errors"
"fmt"
"net/http"
"regexp"
"time"

"github.com/blang/semver/v4"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
knativev1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1"
ctrl "sigs.k8s.io/controller-runtime"
Expand Down Expand Up @@ -42,23 +40,10 @@ import (
func Run(ctx context.Context, c *Config, diagnostic util.ConfigDumpDiagnostic, deprecatedLogger logrus.FieldLogger) error {
setupLog := ctrl.Log.WithName("setup")
setupLog.Info("starting controller manager", "release", metadata.Release, "repo", metadata.Repo, "commit", metadata.Commit)
setupLog.V(util.DebugLevel).Info("the ingress class name has been set", "value", c.IngressClassName)
setupLog.V(util.DebugLevel).Info("building the manager runtime scheme and loading apis into the scheme")
scheme := runtime.NewScheme()
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(konghqcomv1.AddToScheme(scheme))
utilruntime.Must(konghqcomv1alpha1.AddToScheme(scheme))
utilruntime.Must(configurationv1beta1.AddToScheme(scheme))
utilruntime.Must(knativev1alpha1.AddToScheme(scheme))
utilruntime.Must(gatewayv1alpha2.AddToScheme(scheme))
utilruntime.Must(gatewayv1beta1.AddToScheme(scheme))

if c.EnableLeaderElection {
setupLog.V(0).Info("the --leader-elect flag is deprecated and no longer has any effect: leader election is set based on the Kong database setting")
}
setupLog.Info("the ingress class name has been set", "value", c.IngressClassName)

setupLog.Info("getting enabled options and features")
featureGates, err := setupFeatureGates(setupLog, c)
featureGates, err := setupFeatureGates(setupLog, c.FeatureGates)
if err != nil {
return fmt.Errorf("failed to configure feature gates: %w", err)
}
Expand All @@ -68,11 +53,11 @@ func Run(ctx context.Context, c *Config, diagnostic util.ConfigDumpDiagnostic, d
if err != nil {
return fmt.Errorf("get kubeconfig from file %q: %w", c.KubeconfigPath, err)
}

setupLog.Info("getting the kong admin api client configuration")
if c.KongAdminToken != "" {
c.KongAdminAPIConfig.Headers = append(c.KongAdminAPIConfig.Headers, "kong-admin-token:"+c.KongAdminToken)
}

kongClients, err := getKongClients(ctx, c.KongAdminURL, c.KongWorkspace, c.KongAdminAPIConfig)
if err != nil {
return fmt.Errorf("unable to build kong api client(s): %w", err)
Expand All @@ -87,13 +72,12 @@ func Run(ctx context.Context, c *Config, diagnostic util.ConfigDumpDiagnostic, d
if err != nil {
return fmt.Errorf("could not validate Kong admin root(s) configuration: %w", err)
}

semV := semver.Version{Major: v.Major(), Minor: v.Minor(), Patch: v.Patch()}
versions.SetKongVersion(semV)
kongConfig := sendconfig.New(ctx, setupLog, kongClients, semV, dbMode, c.Concurrency, c.FilterTags)

setupLog.Info("configuring and building the controller manager")
controllerOpts, err := setupControllerOptions(setupLog, c, scheme, dbMode)
controllerOpts, err := setupControllerOptions(setupLog, c, dbMode)
if err != nil {
return fmt.Errorf("unable to setup controller options: %w", err)
}
Expand Down Expand Up @@ -126,7 +110,7 @@ func Run(ctx context.Context, c *Config, diagnostic util.ConfigDumpDiagnostic, d
}

setupLog.Info("Initializing Dataplane Synchronizer")
synchronizer, err := setupDataplaneSynchronizer(setupLog, deprecatedLogger, mgr, dataplaneClient, c)
synchronizer, err := setupDataplaneSynchronizer(setupLog, deprecatedLogger, mgr, dataplaneClient, c.ProxySyncSeconds)
if err != nil {
return fmt.Errorf("unable to initialize dataplane synchronizer: %w", err)
}
Expand Down Expand Up @@ -201,8 +185,30 @@ func Run(ctx context.Context, c *Config, diagnostic util.ConfigDumpDiagnostic, d
return mgr.Start(ctx)
}

func isControllerNameValid(controllerName string) bool {
// https://github.com/kubernetes-sigs/gateway-api/blob/547122f7f55ac0464685552898c560658fb40073/apis/v1beta1/shared_types.go#L448-L463
re := regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$`)
return re.Match([]byte(controllerName))
func getScheme() (*runtime.Scheme, error) {
scheme := runtime.NewScheme()

if err := clientgoscheme.AddToScheme(scheme); err != nil {
return nil, err
}
if err := konghqcomv1.AddToScheme(scheme); err != nil {
return nil, err
}
if err := konghqcomv1alpha1.AddToScheme(scheme); err != nil {
return nil, err
}
if err := configurationv1beta1.AddToScheme(scheme); err != nil {
return nil, err
}
if err := knativev1alpha1.AddToScheme(scheme); err != nil {
return nil, err
}
if err := gatewayv1alpha2.Install(scheme); err != nil {
return nil, err
}
if err := gatewayv1beta1.Install(scheme); err != nil {
return nil, err
}

return scheme, nil
}
Loading

0 comments on commit d475b26

Please sign in to comment.