Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Watch Namespaces based on labels and label selectors #9976

Closed
wants to merge 123 commits into from
Closed
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
123 commits
Select commit Hold shift + click to select a range
e0dbe40
watch namespaces by labels
davidjumani Aug 19, 2024
f074c85
update settings
davidjumani Aug 23, 2024
935e05c
namespace cache
davidjumani Aug 23, 2024
8f1a43b
update
davidjumani Aug 27, 2024
7da343d
add check
davidjumani Aug 27, 2024
576c3a4
sync
davidjumani Aug 27, 2024
9441024
fix mutex
davidjumani Aug 27, 2024
ea82628
helm changes
davidjumani Aug 28, 2024
aac1301
refactor
davidjumani Aug 28, 2024
bbfb6ab
Merge branch 'main' of github.com:solo-io/gloo into label-wathc-names…
davidjumani Aug 28, 2024
972d09a
fix
davidjumani Aug 28, 2024
3eefaf5
refactor
davidjumani Aug 29, 2024
92151c2
tests
davidjumani Aug 29, 2024
ea20ee1
more tests
davidjumani Aug 29, 2024
2b6e9c4
cleanup
davidjumani Aug 29, 2024
f6add91
add stats
davidjumani Aug 30, 2024
8dd0c19
Merge branch 'main' into watch-namespace-selectors
davidjumani Aug 30, 2024
db9392c
tilt changes
davidjumani Aug 30, 2024
15f5555
changelog
davidjumani Aug 30, 2024
afb516a
update tests
davidjumani Aug 30, 2024
b920e44
revert helm test changes
davidjumani Aug 30, 2024
f1fd370
Adding changelog file to new location
Aug 30, 2024
62f416c
Deleting changelog file from old location
Aug 30, 2024
98a4de1
refactor tests
davidjumani Aug 30, 2024
f2a338e
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Aug 30, 2024
001898c
fix protos
davidjumani Aug 30, 2024
a1113a1
doc protos
davidjumani Aug 30, 2024
bc9e0ca
validation
davidjumani Sep 1, 2024
3cbcaf1
fix validation
davidjumani Sep 1, 2024
b581ea8
validation fix
davidjumani Sep 1, 2024
8f6f92c
move ns vwc
davidjumani Sep 1, 2024
34cd8a0
codegen
davidjumani Sep 1, 2024
4e35fef
tmp fix
davidjumani Sep 1, 2024
0bb7a75
Revert "tmp fix"
davidjumani Sep 1, 2024
8a98849
Revert "Revert "tmp fix""
davidjumani Sep 1, 2024
5f3812b
fix fix
davidjumani Sep 1, 2024
5a3b208
fix vw bug
davidjumani Sep 2, 2024
1ca45dc
refactor
davidjumani Sep 2, 2024
89223a3
fix
davidjumani Sep 2, 2024
e0ab8ce
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 3, 2024
418c6e2
use generated ns
davidjumani Sep 3, 2024
0aee9ac
don't add install ns
davidjumani Sep 3, 2024
24badfd
Adding changelog file to new location
Sep 3, 2024
e40bffb
Deleting changelog file from old location
Sep 3, 2024
4b01d35
refactor
davidjumani Sep 3, 2024
e9183c3
update tiltfile
davidjumani Sep 3, 2024
503c236
refactor
davidjumani Sep 3, 2024
91c713d
Revert "update tiltfile"
davidjumani Sep 3, 2024
191f8d2
more logging
davidjumani Sep 3, 2024
abbb707
fix test
davidjumani Sep 3, 2024
488a01c
add test
davidjumani Sep 3, 2024
9a46911
increase timeout
davidjumani Sep 3, 2024
a621496
remove nil possibility
davidjumani Sep 3, 2024
746e9b1
refactor
davidjumani Sep 3, 2024
90c1e83
increase timeout
davidjumani Sep 3, 2024
0982626
bump solo-apis
davidjumani Sep 3, 2024
e5b6b03
increase timeout even more
davidjumani Sep 4, 2024
a7b3032
bump solo-kit
davidjumani Sep 4, 2024
6946f20
cleanup
davidjumani Sep 4, 2024
58c9d97
dont watch if kubegateway enabled
davidjumani Sep 4, 2024
e971e06
increase timeout
davidjumani Sep 4, 2024
3ddee63
bump solo-kit
davidjumani Sep 4, 2024
49a8c94
bump solo-kit again
davidjumani Sep 4, 2024
f1fb1a8
refactor again
davidjumani Sep 4, 2024
dc3b6d9
add validation tests
davidjumani Sep 4, 2024
a01413b
increase test timeout
davidjumani Sep 5, 2024
5ba8ae9
update test manifests
davidjumani Sep 5, 2024
0d792f5
update tests
davidjumani Sep 5, 2024
ec708ce
undo timeout bump
davidjumani Sep 5, 2024
d12d90a
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 5, 2024
36531f6
Adding changelog file to new location
Sep 5, 2024
2f80b4f
Deleting changelog file from old location
Sep 5, 2024
052c88f
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 5, 2024
631a65a
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 6, 2024
42c4efd
backward compatibility
davidjumani Sep 7, 2024
4d85d1b
add tests
davidjumani Sep 8, 2024
d39e3d2
rename nsclient
davidjumani Sep 8, 2024
48aed3f
add more tests
davidjumani Sep 8, 2024
c9e08b1
fix test
davidjumani Sep 10, 2024
be79ded
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 10, 2024
36dfd53
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 10, 2024
12ec672
cleanup
davidjumani Sep 10, 2024
5cd164c
fix lint issue
davidjumani Sep 10, 2024
2241413
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 10, 2024
8c27700
be efficient
davidjumani Sep 10, 2024
cd4e2dc
fix tests
davidjumani Sep 10, 2024
de57608
prevent goroutine leaks
davidjumani Sep 11, 2024
5ca13b3
add more docs
davidjumani Sep 11, 2024
192f475
add more docs
davidjumani Sep 11, 2024
24a7cf1
simplify check
davidjumani Sep 11, 2024
c735f81
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 11, 2024
85f0de2
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 11, 2024
ebafe7d
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 11, 2024
38c4580
shortcircuit
davidjumani Sep 11, 2024
b6653ac
refactor
davidjumani Sep 11, 2024
6833a99
fix test failure
davidjumani Sep 11, 2024
6fa77ec
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 11, 2024
506c8a1
Adding changelog file to new location
Sep 11, 2024
54703c9
Deleting changelog file from old location
Sep 11, 2024
8c2b902
fix tests
davidjumani Sep 11, 2024
d9e2543
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 11, 2024
7c43113
revert shortcircuit
davidjumani Sep 12, 2024
d475b9d
revert return
davidjumani Sep 12, 2024
f5fda2f
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 12, 2024
58d1aaf
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 12, 2024
2e1018d
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 13, 2024
a7d6196
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 16, 2024
2af864c
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 16, 2024
4309efd
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 16, 2024
61c83dc
Adding changelog file to new location
Sep 17, 2024
81a9629
Deleting changelog file from old location
Sep 17, 2024
287a17e
address comments
davidjumani Sep 18, 2024
870c8a0
fix test
davidjumani Sep 18, 2024
1557cdf
Merge branch 'main' of github.com:solo-io/gloo into watch-namespace-s…
davidjumani Sep 18, 2024
78eae93
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 18, 2024
d607745
add comments
davidjumani Sep 20, 2024
2521a4b
address comments
davidjumani Sep 20, 2024
374c3ca
codegen
davidjumani Sep 20, 2024
6079884
remove context stuff
davidjumani Sep 20, 2024
a139cd2
Merge branch 'main' of github.com:solo-io/gloo into watch-namespace-s…
davidjumani Sep 20, 2024
bdab112
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 23, 2024
ca0afa7
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 24, 2024
19bcb53
Merge refs/heads/main into watch-namespace-selectors
soloio-bulldozer[bot] Sep 24, 2024
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
6 changes: 6 additions & 0 deletions changelog/v1.18.0-beta19/watch-namespace-selectors.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
changelog:
- type: NEW_FEATURE
issueLink: https://github.com/solo-io/gloo/issues/9274
resolvesIssue: false
description: Adds a new field `watchNamespaceSelectors` to the settings CR. This allows users to specify namespaces to watch based on label selectors. The `watchNamespaces` field will override this if specified.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions docs/content/reference/values.txt
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,10 @@
|settings.secretOptions.sources[].vault.aws.leaseIncrement|uint32||The time increment, in seconds, used in renewing the lease of the Vault token. See: https://developer.hashicorp.com/vault/docs/concepts/lease#lease-durations-and-renewal. Defaults to 0, which causes the default TTL to be used.|
|settings.secretOptions.sources[].directory.directory|string||Directory to read secrets from.|
|settings.kubeResourceOverride.NAME|interface||override fields in the generated resource by specifying the yaml structure to override under the top-level key.|
|settings.watchNamespaceSelectors[].matchLabels.NAME|string|||
|settings.watchNamespaceSelectors[].matchExpressions[].key|string|||
|settings.watchNamespaceSelectors[].matchExpressions[].operator|string|||
|settings.watchNamespaceSelectors[].matchExpressions[].values[]|string|||
|gloo.deployment.xdsPort|int|9977|port where gloo serves xDS API to Envoy.|
|gloo.deployment.restXdsPort|uint32|9976|port where gloo serves REST xDS API to Envoy.|
|gloo.deployment.validationPort|int|9988|port where gloo serves gRPC Proxy Validation to Gateway.|
Expand Down
6 changes: 6 additions & 0 deletions docs/data/ProtoMap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,12 @@ apis:
gloo.solo.io.KubernetesServiceDestination:
relativepath: reference/api/github.com/solo-io/gloo/projects/gloo/api/v1/proxy.proto.sk/#KubernetesServiceDestination
package: gloo.solo.io
gloo.solo.io.LabelSelector:
relativepath: reference/api/github.com/solo-io/gloo/projects/gloo/api/v1/settings.proto.sk/#LabelSelector
package: gloo.solo.io
gloo.solo.io.LabelSelectorRequirement:
relativepath: reference/api/github.com/solo-io/gloo/projects/gloo/api/v1/settings.proto.sk/#LabelSelectorRequirement
package: gloo.solo.io
gloo.solo.io.LbEndpoint:
relativepath: reference/api/github.com/solo-io/gloo/projects/gloo/api/v1/failover.proto.sk/#LbEndpoint
package: gloo.solo.io
Expand Down
22 changes: 22 additions & 0 deletions install/helm/gloo/crds/gloo.solo.io_v1_Settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,28 @@ spec:
token:
type: string
type: object
watchNamespaceSelectors:
items:
properties:
matchExpressions:
items:
properties:
key:
type: string
operator:
type: string
values:
items:
type: string
type: array
type: object
type: array
matchLabels:
additionalProperties:
type: string
type: object
type: object
type: array
watchNamespaces:
items:
type: string
Expand Down
2 changes: 2 additions & 0 deletions install/helm/gloo/generate/values.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package generate

import (
v1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1"
corev1 "k8s.io/api/core/v1"
)

Expand Down Expand Up @@ -234,6 +235,7 @@ type Settings struct {
DevMode *bool `json:"devMode,omitempty" desc:"Whether or not to enable dev mode. Defaults to false. Setting to true at install time will expose the gloo dev admin endpoint on port 10010. Not recommended for production. Warning: this value is deprecated as of 1.17 and will be removed in a future release."`
SecretOptions SecretOptions `json:"secretOptions,omitempty" desc:"Options for how Gloo Edge should handle secrets."`
*KubeResourceOverride
WatchNamespaceSelectors []*v1.LabelSelector `json:"watchNamespaceSelectors,omitempty" desc:"A list of Kubernetes selectors that specify the set of namespaces to restrict the namespaces that Gloo controllers take into consideration when watching for resources. Elements in the list are disjunctive (OR semantics), i.e. a namespace will be included if it matches any selector."`
}

type AwsSettings struct {
Expand Down
9 changes: 7 additions & 2 deletions install/helm/gloo/templates/18-settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,13 @@ spec:
{{- range . }}
- {{ . }}
{{- end }}
{{- end }}
{{- end }}
{{- end }} {{/* with .Values.settings.watchNamespaces */}}
{{- end }} {{/* if .Values.settings.singleNamespace */}}

{{- if .Values.settings.watchNamespaceSelectors }}
watchNamespaceSelectors:
{{ toYaml .Values.settings.watchNamespaceSelectors | nindent 4 }}
{{- end }} {{/* if .Values.settings.watchNamespaceSelectors */}}

{{- end }} {{/* if .Values.settings.create */}}
{{- end }} {{/* define "settings.settingsSpec" */}}
Expand Down
177 changes: 173 additions & 4 deletions pkg/utils/settingsutil/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,37 @@ package settingsutil

import (
"context"
"fmt"
"slices"
"sync"

"github.com/solo-io/gloo/pkg/utils"
"github.com/solo-io/gloo/projects/gloo/cli/pkg/helpers"
v1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1"
"github.com/solo-io/gloo/projects/gloo/pkg/defaults"
"github.com/solo-io/solo-kit/pkg/api/external/kubernetes/namespace"
"github.com/solo-io/solo-kit/pkg/api/v1/clients"
"github.com/solo-io/solo-kit/pkg/api/v1/clients/kube/cache"
"github.com/solo-io/solo-kit/pkg/api/v1/resources/common/kubernetes"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/utils/lru"
)

type settingsKeyStruct struct{}

var settingsKey = settingsKeyStruct{}
var (
mu sync.Mutex

// Setting a cache size of 2 should suffice for :
// - The current translation loop
// - The new loop about to run when the settings have changed
namespacesToWatchCache = lru.New(2)

settingsKey = settingsKeyStruct{}
)

func WithSettings(ctx context.Context, settings *v1.Settings) context.Context {
return context.WithValue(ctx, settingsKey, settings)
Expand Down Expand Up @@ -37,11 +61,11 @@ func MaybeFromContext(ctx context.Context) *v1.Settings {
return nil
}

func IsAllNamespacesFromSettings(s *v1.Settings) bool {
if s == nil {
func IsAllNamespacesFromSettings(settings *v1.Settings) bool {
if settings == nil {
return false
}
return IsAllNamespaces(s.GetWatchNamespaces())
return IsAllNamespaces(GetNamespacesToWatch(settings))
}

func IsAllNamespaces(watchNs []string) bool {
Expand All @@ -54,3 +78,148 @@ func IsAllNamespaces(watchNs []string) bool {
return false
}
}

// GenerateNamespacesToWatch generates the list of namespaces to watch based on :
// - If `watchNamespaces` is defined, return it and do not consider `watchNamespaceSelectors`
// - If `watchNamespaces` and `watchNamespaceSelectors` are not defined, return all namespaces
// - If `watchNamespaces` is not defined and `watchNamespaceSelectors` is defined, return all namespaces that match the `watchNamespaceSelectors`
// In every case, the `discoveryNamespace` (defaults to `gloo-system`) is appended to the list of namespaces
func GenerateNamespacesToWatch(settings *v1.Settings, namespaces kubernetes.KubeNamespaceList) ([]string, error) {
writeNamespace := settings.GetDiscoveryNamespace()
if writeNamespace == "" {
writeNamespace = defaults.GlooSystem
}

if len(settings.GetWatchNamespaces()) != 0 {
// Prevent an error where the controller can not read resources written by discovery if the
// install or discovery namespace is not watched
return utils.ProcessWatchNamespaces(settings.GetWatchNamespaces(), writeNamespace), nil
}

// Watch all namespaces if `watchNamespaces` or `watchNamespaceSelectors` is not specified
if len(settings.GetWatchNamespaceSelectors()) == 0 {
return []string{""}, nil
}

var selectors []labels.Selector
selectedNamespaces := sets.NewString()

for _, selector := range settings.GetWatchNamespaceSelectors() {
ls, err := labelSelectorAsSelector(selector)
if err != nil {
return nil, err
}
selectors = append(selectors, ls)
}

for _, ns := range namespaces {
for _, selector := range selectors {
if selector.Matches(labels.Set(ns.Labels)) {
selectedNamespaces.Insert(ns.Name)
break
}
}
}

// Prevent an error where the controller can not read resources written by discovery if the
// install or discovery namespace is not watched
selectedNamespaces.Insert(writeNamespace)

return selectedNamespaces.List(), nil
}

func setNamespacesToWatch(settings *v1.Settings, namespaces []string) {
namespacesToWatchCache.Add(settings.MustHash(), namespaces)
}

// UpdateNamespacesToWatch generates and updated the list of namespaces to watch and returns true if updated
func UpdateNamespacesToWatch(settings *v1.Settings, namespaces kubernetes.KubeNamespaceList) (bool, error) {
// Run this method synchronously to prevent any issues with caching the namespaces to watch
mu.Lock()
defer mu.Unlock()

newNamespacesToWatch, err := GenerateNamespacesToWatch(settings, namespaces)
if err != nil {
return false, err
}

ns, ok := namespacesToWatchCache.Get(settings.MustHash())
if ok {
currentNamespacesToWatch, ok := ns.([]string)
if ok && slices.Equal(newNamespacesToWatch, currentNamespacesToWatch) {
return false, nil
}
}

setNamespacesToWatch(settings, newNamespacesToWatch)
return true, nil
}

func getAllNamespaces() (kubernetes.KubeNamespaceList, error) {
kubeClient := helpers.MustKubeClient()
kubeCache, _ := cache.NewKubeCoreCache(context.TODO(), kubeClient)
nsClient := namespace.NewNamespaceClient(kubeClient, kubeCache)

return nsClient.List(clients.ListOpts{})
}

// GetNamespacesToWatch returns the list of namespaces to watch based on the last run of `GenerateNamespacesToWatch`
func GetNamespacesToWatch(settings *v1.Settings) []string {
ns, ok := namespacesToWatchCache.Get(settings.MustHash())
if ok {
currentNamespacesToWatch, ok := ns.([]string)
if ok {
return currentNamespacesToWatch
}
}

// Fallback to fetching all namespaces and updating the cache if not found
allNamespaces, err := getAllNamespaces()
if err != nil {
panic("Unable to fetch namespaces")
}
UpdateNamespacesToWatch(settings, allNamespaces)
ns, _ = namespacesToWatchCache.Get(settings.MustHash())
return ns.([]string)
}

// TODO: remove this and use k8s.io/apimachinery to do this once the settings proto uses the predefined fields
func labelSelectorAsSelector(ps *v1.LabelSelector) (labels.Selector, error) {
if ps == nil {
return labels.Nothing(), nil
}
if len(ps.GetMatchLabels())+len(ps.GetMatchExpressions()) == 0 {
return labels.Everything(), nil
}
requirements := make([]labels.Requirement, 0, len(ps.GetMatchLabels())+len(ps.GetMatchExpressions()))
for k, v := range ps.GetMatchLabels() {
r, err := labels.NewRequirement(k, selection.Equals, []string{v})
if err != nil {
return nil, err
}
requirements = append(requirements, *r)
}
for _, expr := range ps.GetMatchExpressions() {
var op selection.Operator
switch metav1.LabelSelectorOperator(expr.GetOperator()) {
case metav1.LabelSelectorOpIn:
op = selection.In
case metav1.LabelSelectorOpNotIn:
op = selection.NotIn
case metav1.LabelSelectorOpExists:
op = selection.Exists
case metav1.LabelSelectorOpDoesNotExist:
op = selection.DoesNotExist
default:
return nil, fmt.Errorf("%q is not a valid label selector operator", expr.GetOperator())
}
r, err := labels.NewRequirement(expr.GetKey(), op, append([]string(nil), expr.GetValues()...))
if err != nil {
return nil, err
}
requirements = append(requirements, *r)
}
selector := labels.NewSelector()
selector = selector.Add(requirements...)
return selector, nil
}
Loading
Loading