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: add namespace selector using env variable #1434

Merged
1 change: 1 addition & 0 deletions deploy/helm/grafana-operator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,5 @@ It's easier to just manage this configuration outside of the operator.
| serviceMonitor.targetLabels | list | `[]` | Set of labels to transfer from the Kubernetes Service onto the target |
| serviceMonitor.telemetryPath | string | `"/metrics"` | Set path to metrics path |
| tolerations | list | `[]` | pod tolerations |
| watchNamespaceSelector | string | `""` | Sets the WATCH_NAMESPACE_SELECTOR environment variable, it defines which namespaces the operator should be listening for based on label and key value pair added on namespace kind. By default it's all namespaces. |
| watchNamespaces | string | `""` | Sets the WATCH_NAMESPACE environment variable, it defines which namespaces the operator should be listening for. By default it's all namespaces, if you only want to listen for the same namespace as the operator is deployed to look at namespaceScope. |
6 changes: 6 additions & 0 deletions deploy/helm/grafana-operator/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ spec:
{{ else }}
value: {{ .Values.watchNamespaces }}
{{- end }}
- name: WATCH_NAMESPACE_SELECTOR
{{- if and .Values.namespaceScope (eq .Values.watchNamespaceSelector "") }}
value: ""
{{ else }}
value: {{quote .Values.watchNamespaceSelector }}
{{- end }}
{{- with .Values.env }}
{{- toYaml . | nindent 12 }}
{{- end }}
Expand Down
5 changes: 5 additions & 0 deletions deploy/helm/grafana-operator/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ leaderElect: false
# By default it's all namespaces, if you only want to listen for the same namespace as the operator is deployed to look at namespaceScope.
watchNamespaces: ""

# -- Sets the WATCH_NAMESPACE_SELECTOR environment variable,
# it defines which namespaces the operator should be listening for based on label and key value pair added on namespace kind.
# By default it's all namespaces.
watchNamespaceSelector: ""

# -- Determines if the target cluster is OpenShift. Additional rbac permissions for routes will be added on OpenShift
isOpenShift: false

Expand Down
4 changes: 4 additions & 0 deletions docs/docs/grafana.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ To support that, we offer 3 operational modes (you can switch between those thro
- Cluster-wide permissions are still required;
- single namespace (`WATCH_NAMESPACE: "grafana"`):
- With this mode, it's possible to use `Role` + `RoleBinding` to grant the operator the required access.
- mutliple namespaces using label selector (`WATCH_NAMESPACE_SELECTOR: "environment: dev"`):
- With this mode, it is possible detect and load all namespaces that match the label selector automatically.
New namespaces won't be automatically included until the Grafana operator is restarted.
- Cluster-wide permissions are still required;

## External Grafana instances

Expand Down
42 changes: 41 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ import (
"strings"
"syscall"

"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/config"
"sigs.k8s.io/controller-runtime/pkg/webhook"

"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"

corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/cache"

routev1 "github.com/openshift/api/route/v1"
Expand All @@ -55,6 +58,10 @@ const (
// watchNamespaceEnvVar is the constant for env variable WATCH_NAMESPACE which specifies the Namespace to watch.
// If empty or undefined, the operator will run in cluster scope.
watchNamespaceEnvVar = "WATCH_NAMESPACE"
// watchNamespaceEnvSelector is the constant for env variable WATCH_NAMESPACE_SELECTOR which specifies the Namespace label and key to watch.
// eg: "environment: dev"
// If empty or undefined, the operator will run in cluster scope.
watchNamespaceEnvSelector = "WATCH_NAMESPACE_SELECTOR"
)

var (
Expand Down Expand Up @@ -89,6 +96,7 @@ func main() {
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))

watchNamespace, _ := os.LookupEnv(watchNamespaceEnvVar)
watchNamespaceSelector, _ := os.LookupEnv(watchNamespaceEnvSelector)

controllerOptions := ctrl.Options{
Scheme: scheme,
Expand Down Expand Up @@ -119,6 +127,34 @@ func main() {
}
return defaultNamespaces
}
getNamespaceConfigSelector := func(selector string) map[string]cache.Config {
cl, err := client.New(config.GetConfigOrDie(), client.Options{})
if err != nil {
setupLog.Error(err, "Failed to get watch namespaces")
}
nsList := &corev1.NamespaceList{}
listOpts := []client.ListOption{
client.MatchingLabels(map[string]string{strings.Split(selector, ":")[0]: strings.Split(selector, ":")[1]}),
}
err = cl.List(context.Background(), nsList, listOpts...)
if err != nil {
setupLog.Error(err, "Failed to get watch namespaces")
}
defaultNamespaces := map[string]cache.Config{}
for _, v := range nsList.Items {
// Generate a mapping of namespaces to label/field selectors, set to Everything() to enable matching all
// instances in all namespaces from watchNamespace to be controlled by the operator
// this is the default behavior of the operator on v5, if you require finer grained control over this
// please file an issue in the grafana-operator/grafana-operator GH project
defaultNamespaces[v.Name] = cache.Config{
LabelSelector: labels.Everything(), // Match any labels
FieldSelector: fields.Everything(), // Match any fields
Transform: nil,
UnsafeDisableDeepCopy: nil,
}
}
return defaultNamespaces
}
switch {
case strings.Contains(watchNamespace, ","):
// multi namespace scoped
Expand All @@ -128,8 +164,12 @@ func main() {
// namespace scoped
controllerOptions.Cache.DefaultNamespaces = getNamespaceConfig(watchNamespace)
setupLog.Info("operator running in namespace scoped mode", "namespace", watchNamespace)
case strings.Contains(watchNamespaceSelector, ":"):
// namespace scoped
controllerOptions.Cache.DefaultNamespaces = getNamespaceConfigSelector(watchNamespaceSelector)
setupLog.Info("operator running in namespace scoped mode using namespace selector", "namespace", watchNamespace)

case watchNamespace == "":
case watchNamespace == "" && watchNamespaceSelector == "":
// cluster scoped
setupLog.Info("operator running in cluster scoped mode")
}
Expand Down
Loading