Skip to content

Commit

Permalink
✨ Cluster Provider and cluster-aware controllers
Browse files Browse the repository at this point in the history
Signed-off-by: Vince Prignano <vincepri@redhat.com>
  • Loading branch information
vincepri committed Mar 6, 2023
1 parent e2d8821 commit 107c2dc
Show file tree
Hide file tree
Showing 28 changed files with 1,930 additions and 94 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ jobs:
- tools/setup-envtest
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3.5.0
with:
go-version: "1.19"
check-latest: true
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ $(CONTROLLER_GEN): $(TOOLS_DIR)/go.mod # Build controller-gen from tools folder.
$(GOLANGCI_LINT): .github/workflows/golangci-lint.yml # Download golanci-lint using hack script into tools folder.
hack/ensure-golangci-lint.sh \
-b $(TOOLS_BIN_DIR) \
$(shell cat .github/workflows/golangci-lint.yml | grep version | sed 's/.*version: //')
$(shell cat .github/workflows/golangci-lint.yml | grep "version: v" | sed 's/.*version: //')

## --------------------------------------
## Linting
Expand Down
75 changes: 75 additions & 0 deletions examples/fleet/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
module sigs.k8s.io/controller-runtime/examples/fleet

go 1.19

replace sigs.k8s.io/controller-runtime => ../..

require (
k8s.io/api v0.26.1
k8s.io/apimachinery v0.26.1
k8s.io/client-go v0.26.1
k8s.io/klog/v2 v2.90.0
sigs.k8s.io/controller-runtime v0.0.0-00010101000000-000000000000
sigs.k8s.io/kind v0.17.0
sigs.k8s.io/logical-cluster v0.0.1-alpha.0
)

require (
github.com/BurntSushi/toml v1.0.0 // indirect
github.com/alessio/shellescape v1.4.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 // indirect
github.com/google/uuid v1.1.2 // indirect
github.com/imdario/mergo v0.3.6 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/spf13/cobra v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/term v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/time v0.3.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.26.1 // indirect
k8s.io/component-base v0.26.1 // indirect
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
627 changes: 627 additions & 0 deletions examples/fleet/go.sum

Large diffs are not rendered by default.

194 changes: 194 additions & 0 deletions examples/fleet/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
package main

import (
"context"
"os"
"strings"
"sync"
"time"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/klog/v2"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/cluster"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
kind "sigs.k8s.io/kind/pkg/cluster"
"sigs.k8s.io/logical-cluster"
)

func init() {
ctrl.SetLogger(klog.Background())
}

func main() {
entryLog := log.Log.WithName("entrypoint")

testEnv := &envtest.Environment{}
cfg, err := testEnv.Start()
if err != nil {
entryLog.Error(err, "failed to start local environment")
os.Exit(1)
}
defer func() {
if testEnv == nil {
return
}
if err := testEnv.Stop(); err != nil {
entryLog.Error(err, "failed to stop local environment")
os.Exit(1)
}
}()

// Setup a Manager
entryLog.Info("Setting up manager")
mgr, err := manager.New(
cfg,
manager.Options{}.WithExperimentalClusterProvider(&KindClusterProvider{}),
)
if err != nil {
entryLog.Error(err, "unable to set up overall controller manager")
os.Exit(1)
}

builder.ControllerManagedBy(mgr).
For(&corev1.Pod{}).Complete(reconcile.Func(
func(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := log.FromContext(ctx)

cluster, err := mgr.GetCluster(ctx, req.Cluster)
if err != nil {
return reconcile.Result{}, err
}
client := cluster.GetClient()

// Retrieve the pod from the cluster.
pod := &corev1.Pod{}
if err := client.Get(ctx, req.NamespacedName, pod); err != nil {
return reconcile.Result{}, err
}
log.Info("Reconciling pod", "name", pod.Name, "uuid", pod.UID)

// Print any annotations that start with fleet.
for k, v := range pod.Labels {
if strings.HasPrefix(k, "fleet-") {
log.Info("Detected fleet annotation!", "key", k, "value", v)
}
}

return ctrl.Result{}, nil
},
))

entryLog.Info("Starting manager")
if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
entryLog.Error(err, "unable to run manager")
os.Exit(1)
}
}

// KindClusterProvider is a cluster provider that works with a local Kind instance.
type KindClusterProvider struct{}

func (k *KindClusterProvider) Get(ctx context.Context, name logical.Name, opts ...cluster.Option) (cluster.Cluster, error) {
provider := kind.NewProvider()
kubeconfig, err := provider.KubeConfig(string(name), false)
if err != nil {
return nil, err
}
// Parse the kubeconfig into a rest.Config.
cfg, err := clientcmd.RESTConfigFromKubeConfig([]byte(kubeconfig))
if err != nil {
return nil, err
}
return cluster.New(cfg, opts...)
}

func (k *KindClusterProvider) List() ([]logical.Name, error) {
provider := kind.NewProvider()
list, err := provider.List()
if err != nil {
return nil, err
}
res := make([]logical.Name, 0, len(list))
for _, cluster := range list {
if !strings.HasPrefix(cluster, "fleet-") {
continue
}
res = append(res, logical.Name(cluster))
}
return res, nil
}

func (k *KindClusterProvider) Watch() (cluster.Watcher, error) {
return &KindWatcher{ch: make(chan cluster.WatchEvent)}, nil
}

type KindWatcher struct {
init sync.Once
wg sync.WaitGroup
ch chan cluster.WatchEvent
cancel context.CancelFunc
}

func (k *KindWatcher) Stop() {
if k.cancel != nil {
k.cancel()
}
k.wg.Wait()
close(k.ch)
}
func (k *KindWatcher) ResultChan() <-chan cluster.WatchEvent {
k.init.Do(func() {
ctx, cancel := context.WithCancel(context.Background())
k.cancel = cancel
set := sets.New[string]()
k.wg.Add(1)
go func() {
defer k.wg.Done()
for {
select {
case <-time.After(2 * time.Second):
provider := kind.NewProvider()
list, err := provider.List()
if err != nil {
klog.Error(err)
continue
}
newSet := sets.New(list...)
// Check for new clusters.
for _, cl := range newSet.Difference(set).UnsortedList() {
if !strings.HasPrefix(cl, "fleet-") {
continue
}
k.ch <- cl.WatchEvent{
Type: watch.Added,
Name: logical.Name(cl),
}
}
// Check for deleted clusters.
for _, cl := range set.Difference(newSet).UnsortedList() {
if !strings.HasPrefix(cl, "fleet-") {
continue
}
k.ch <- cluster.WatchEvent{
Type: watch.Deleted,
Name: logical.Name(cl),
}
}
set = newSet
case <-ctx.Done():
return
}
}
}()
})
return k.ch
}
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ require (
k8s.io/component-base v0.26.1
k8s.io/klog/v2 v2.90.0
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448
sigs.k8s.io/logical-cluster v0.0.1-alpha.0
sigs.k8s.io/yaml v1.3.0
)

Expand Down Expand Up @@ -65,6 +66,7 @@ require (
golang.org/x/tools v0.6.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
5 changes: 4 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -570,8 +570,9 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
Expand Down Expand Up @@ -616,6 +617,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k=
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/logical-cluster v0.0.1-alpha.0 h1:vigMG0I1fgDVn0hsTOeZB55AmplXC7D4iLa60qeyX70=
sigs.k8s.io/logical-cluster v0.0.1-alpha.0/go.mod h1:7YymTkuUFI+tkwCRPMsk+TiyBQiPDKRArxVAAGpezZI=
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
Expand Down
Loading

0 comments on commit 107c2dc

Please sign in to comment.