Skip to content

Commit

Permalink
Merge pull request #293 from negz/coopt
Browse files Browse the repository at this point in the history
Add a `controller.Options` type
  • Loading branch information
negz authored Sep 22, 2021
2 parents 9f3f799 + f2b0ca3 commit 658dfc7
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 8 deletions.
68 changes: 68 additions & 0 deletions pkg/controller/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
Copyright 2021 The Crossplane Authors.
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 controller

import (
"time"

"k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/controller"

"github.com/crossplane/crossplane-runtime/pkg/feature"
"github.com/crossplane/crossplane-runtime/pkg/logging"
"github.com/crossplane/crossplane-runtime/pkg/ratelimiter"
)

// DefaultOptions returns a functional set of options with conservative
// defaults.
func DefaultOptions() Options {
return Options{
Logger: logging.NewNopLogger(),
GlobalRateLimiter: ratelimiter.NewGlobal(ratelimiter.DefaultGlobalRPS),
PollInterval: 1 * time.Minute,
MaxConcurrentReconciles: 1,
Features: &feature.Flags{},
}
}

// Options frequently used by most Crossplane controllers.
type Options struct {
// The Logger controllers should use.
Logger logging.Logger

// The GlobalRateLimiter used by this controller manager. The rate of
// reconciles across all controllers will be subject to this limit.
GlobalRateLimiter workqueue.RateLimiter

// PollInterval at which each controller should speculatively poll to
// determine whether it has work to do.
PollInterval time.Duration

// MaxConcurrentReconciles for each controller.
MaxConcurrentReconciles int

// Features that should be enabled.
Features *feature.Flags
}

// ForControllerRuntime extracts options for controller-runtime.
func (o Options) ForControllerRuntime() controller.Options {
return controller.Options{
MaxConcurrentReconciles: o.MaxConcurrentReconciles,
RateLimiter: ratelimiter.NewController(o.GlobalRateLimiter),
}
}
51 changes: 51 additions & 0 deletions pkg/feature/feature.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
Copyright 2021 The Crossplane Authors.
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 feature contains utilities for managing Crossplane features.
package feature

import (
"sync"
)

// A Flag enables a particular feature.
type Flag string

// Flags that are enabled. The zero value - i.e. &feature.Flags{} - is usable.
type Flags struct {
m sync.RWMutex
enabled map[Flag]bool
}

// Enable a feature flag.
func (fs *Flags) Enable(f Flag) {
fs.m.Lock()
if fs.enabled == nil {
fs.enabled = make(map[Flag]bool)
}
fs.enabled[f] = true
fs.m.Unlock()
}

// Enabled returns true if the supplied feature flag is enabled.
func (fs *Flags) Enabled(f Flag) bool {
if fs == nil {
return false
}
fs.m.RLock()
defer fs.m.RUnlock()
return fs.enabled[f]
}
61 changes: 61 additions & 0 deletions pkg/feature/feature_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
Copyright 2021 The Crossplane Authors.
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 feature

import (
"testing"

"github.com/google/go-cmp/cmp"
)

func TestEnable(t *testing.T) {
var cool Flag = "cool"

t.Run("EnableMutatesZeroValue", func(t *testing.T) {
f := &Flags{}
f.Enable(cool)

want := true
got := f.Enabled(cool)

if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("f.Enabled(...): -want, +got:\n%s", diff)
}
})

t.Run("EnabledOnEmptyFlagsReturnsFalse", func(t *testing.T) {
f := &Flags{}

want := false
got := f.Enabled(cool)

if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("f.Enabled(...): -want, +got:\n%s", diff)
}
})

t.Run("EnabledOnNilReturnsFalse", func(t *testing.T) {
var f *Flags

want := false
got := f.Enabled(cool)

if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("f.Enabled(...): -want, +got:\n%s", diff)
}
})
}
44 changes: 36 additions & 8 deletions pkg/ratelimiter/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

// Package ratelimiter contains suggested default ratelimiters for Crossplane.
package ratelimiter

import (
Expand All @@ -24,24 +25,51 @@ import (
"sigs.k8s.io/controller-runtime/pkg/ratelimiter"
)

// DefaultProviderRPS is the recommended default average requeues per second
// tolerated by a provider's controller manager.
const DefaultProviderRPS = 1
const (
// DefaultGlobalRPS is the recommended default average requeues per
// second tolerated by Crossplane controller managers.
DefaultGlobalRPS = 1

// DefaultProviderRPS is the recommended default average requeues per
// second tolerated by a Crossplane provider.
//
// Deprecated: Use DefaultGlobalRPS
DefaultProviderRPS = DefaultGlobalRPS
)

// NewGlobal returns a token bucket rate limiter meant for limiting the number
// of average total requeues per second for all controllers registered with a
// controller manager. The bucket size is a linear function of the requeues per
// second.
func NewGlobal(rps int) *workqueue.BucketRateLimiter {
return &workqueue.BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(rps), rps*10)}
}

// NewController returns a rate limiter that takes the maximum delay between the
// passed rate limiter and a per-item exponential backoff limiter. The
// exponential backoff limiter has a base delay of 1s and a maximum of 60s.
func NewController(global ratelimiter.RateLimiter) ratelimiter.RateLimiter {
return workqueue.NewMaxOfRateLimiter(
workqueue.NewItemExponentialFailureRateLimiter(1*time.Second, 60*time.Second),
global,
)
}

// NewDefaultProviderRateLimiter returns a token bucket rate limiter meant for
// limiting the number of average total requeues per second for all controllers
// registered with a controller manager. The bucket size is a linear function of
// the requeues per second.
//
// Deprecated: Use NewGlobal.
func NewDefaultProviderRateLimiter(rps int) *workqueue.BucketRateLimiter {
return &workqueue.BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(rps), rps*10)}
return NewGlobal(rps)
}

// NewDefaultManagedRateLimiter returns a rate limiter that takes the maximum
// delay between the passed provider and a per-item exponential backoff limiter.
// The exponential backoff limiter has a base delay of 1s and a maximum of 60s.
//
// Deprecated: Use NewController.
func NewDefaultManagedRateLimiter(provider ratelimiter.RateLimiter) ratelimiter.RateLimiter {
return workqueue.NewMaxOfRateLimiter(
workqueue.NewItemExponentialFailureRateLimiter(1*time.Second, 60*time.Second),
provider,
)
return NewController(provider)
}

0 comments on commit 658dfc7

Please sign in to comment.