From 506fb707c2477d084b1613c7c5f019de3ecbc994 Mon Sep 17 00:00:00 2001 From: Ronan-WeScale Date: Tue, 16 Apr 2024 17:42:58 +0200 Subject: [PATCH 01/17] Add - matchExpression --- controllers/controller_shared.go | 43 ++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/controllers/controller_shared.go b/controllers/controller_shared.go index bd32f9033..60574266a 100644 --- a/controllers/controller_shared.go +++ b/controllers/controller_shared.go @@ -33,9 +33,48 @@ func GetMatchingInstances(ctx context.Context, k8sClient client.Client, labelSel opts := []client.ListOption{ client.MatchingLabels(labelSelector.MatchLabels), } - err := k8sClient.List(ctx, &list, opts...) - return list, err + + var selectedList v1beta1.GrafanaList + + for _, instance := range list.Items { + selected := true + for _, matchExpression := range labelSelector.MatchExpressions { + if val, ok := instance.Labels[matchExpression.Key]; ok { + if matchExpression.Operator == "DoesNotExist" { + selected = false + break + } else if matchExpression.Operator == "Exists" { + break + } + valueMatched := labelMatchedExpression(val, matchExpression) + if !valueMatched { + selected = false + break + } + } else if matchExpression.Operator == "In" || matchExpression.Operator == "Exists" { + selected = false + break + } + } + + if selected { + selectedList.Items = append(selectedList.Items, instance) + } + } + + return selectedList, err +} + +func labelMatchedExpression(val string, matchExpression v1.LabelSelectorRequirement) bool { + valueMatched := matchExpression.Operator == "NotIn" + + for _, value := range matchExpression.Values { + if val == value { + return matchExpression.Operator == "In" + } + } + return valueMatched } func ReconcilePlugins(ctx context.Context, k8sClient client.Client, scheme *runtime.Scheme, grafana *v1beta1.Grafana, plugins v1beta1.PluginList, resource string) error { From 976c6ea1fc01c34180848f3fc4fddffa84203efc Mon Sep 17 00:00:00 2001 From: Ronan-WeScale Date: Tue, 16 Apr 2024 17:43:31 +0200 Subject: [PATCH 02/17] Add - MatchExpression kind resources --- hack/kind/resources/default/grafana-2.yaml | 32 ++++++++++++++ .../default/grafana-dashboard-exist.yaml | 42 ++++++++++++++++++ .../default/grafana-dashboard-in.yaml | 44 +++++++++++++++++++ .../default/grafana-dashboard-notexist.yaml | 42 ++++++++++++++++++ .../default/grafana-dashboard-notin.yaml | 44 +++++++++++++++++++ .../resources/default/grafana-dashboard.yaml | 4 +- .../kind/resources/default/kustomization.yaml | 5 +++ 7 files changed, 211 insertions(+), 2 deletions(-) create mode 100644 hack/kind/resources/default/grafana-2.yaml create mode 100644 hack/kind/resources/default/grafana-dashboard-exist.yaml create mode 100644 hack/kind/resources/default/grafana-dashboard-in.yaml create mode 100644 hack/kind/resources/default/grafana-dashboard-notexist.yaml create mode 100644 hack/kind/resources/default/grafana-dashboard-notin.yaml diff --git a/hack/kind/resources/default/grafana-2.yaml b/hack/kind/resources/default/grafana-2.yaml new file mode 100644 index 000000000..4c210880c --- /dev/null +++ b/hack/kind/resources/default/grafana-2.yaml @@ -0,0 +1,32 @@ +apiVersion: grafana.integreatly.org/v1beta1 +kind: Grafana +metadata: + name: grafana-2 + labels: + dashboards: "grafana-2" + exists: "" +spec: + client: + preferIngress: true + config: + log: + mode: "console" + auth: + disable_login_form: "false" + security: + admin_user: root + admin_password: secret + ingress: + spec: + ingressClassName: nginx + rules: + - host: grafana2.127.0.0.1.nip.io + http: + paths: + - backend: + service: + name: grafana-2-service + port: + number: 3000 + path: / + pathType: Prefix diff --git a/hack/kind/resources/default/grafana-dashboard-exist.yaml b/hack/kind/resources/default/grafana-dashboard-exist.yaml new file mode 100644 index 000000000..93a90460c --- /dev/null +++ b/hack/kind/resources/default/grafana-dashboard-exist.yaml @@ -0,0 +1,42 @@ +apiVersion: grafana.integreatly.org/v1beta1 +kind: GrafanaDashboard +metadata: + name: grafana-exist + labels: + dashboards: "grafana" +spec: + folder: my-folder-name + instanceSelector: + matchExpressions: + - key: exists + operator: Exists + json: | + { + "id": null, + "title": "Simple Dashboard Exist", + "tags": [], + "style": "dark", + "timezone": "browser", + "editable": true, + "hideControls": false, + "graphTooltip": 1, + "panels": [], + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "time_options": [], + "refresh_intervals": [] + }, + "templating": { + "list": [] + }, + "annotations": { + "list": [] + }, + "refresh": "5s", + "schemaVersion": 17, + "version": 0, + "links": [] + } diff --git a/hack/kind/resources/default/grafana-dashboard-in.yaml b/hack/kind/resources/default/grafana-dashboard-in.yaml new file mode 100644 index 000000000..8bacc4427 --- /dev/null +++ b/hack/kind/resources/default/grafana-dashboard-in.yaml @@ -0,0 +1,44 @@ +apiVersion: grafana.integreatly.org/v1beta1 +kind: GrafanaDashboard +metadata: + name: grafana-in + labels: + dashboards: "grafana" +spec: + folder: my-folder-name + instanceSelector: + matchExpressions: + - key: dashboards + operator: In + values: + - grafana-2 + json: | + { + "id": null, + "title": "Simple Dashboard In Grafana-2", + "tags": [], + "style": "dark", + "timezone": "browser", + "editable": true, + "hideControls": false, + "graphTooltip": 1, + "panels": [], + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "time_options": [], + "refresh_intervals": [] + }, + "templating": { + "list": [] + }, + "annotations": { + "list": [] + }, + "refresh": "5s", + "schemaVersion": 17, + "version": 0, + "links": [] + } diff --git a/hack/kind/resources/default/grafana-dashboard-notexist.yaml b/hack/kind/resources/default/grafana-dashboard-notexist.yaml new file mode 100644 index 000000000..b5b7fb044 --- /dev/null +++ b/hack/kind/resources/default/grafana-dashboard-notexist.yaml @@ -0,0 +1,42 @@ +apiVersion: grafana.integreatly.org/v1beta1 +kind: GrafanaDashboard +metadata: + name: grafana-notexist + labels: + dashboards: "grafana" +spec: + folder: my-folder-name + instanceSelector: + matchExpressions: + - key: exists + operator: DoesNotExist + json: | + { + "id": null, + "title": "Simple Dashboard NotExist", + "tags": [], + "style": "dark", + "timezone": "browser", + "editable": true, + "hideControls": false, + "graphTooltip": 1, + "panels": [], + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "time_options": [], + "refresh_intervals": [] + }, + "templating": { + "list": [] + }, + "annotations": { + "list": [] + }, + "refresh": "5s", + "schemaVersion": 17, + "version": 0, + "links": [] + } diff --git a/hack/kind/resources/default/grafana-dashboard-notin.yaml b/hack/kind/resources/default/grafana-dashboard-notin.yaml new file mode 100644 index 000000000..4318e8933 --- /dev/null +++ b/hack/kind/resources/default/grafana-dashboard-notin.yaml @@ -0,0 +1,44 @@ +apiVersion: grafana.integreatly.org/v1beta1 +kind: GrafanaDashboard +metadata: + name: grafana-notin-grafana-2 + labels: + dashboards: "grafana" +spec: + folder: my-folder-name + instanceSelector: + matchExpressions: + - key: dashboards + operator: NotIn + values: + - grafana-2 + json: | + { + "id": null, + "title": "Simple Dashboard notIn grafana-2", + "tags": [], + "style": "dark", + "timezone": "browser", + "editable": true, + "hideControls": false, + "graphTooltip": 1, + "panels": [], + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "time_options": [], + "refresh_intervals": [] + }, + "templating": { + "list": [] + }, + "annotations": { + "list": [] + }, + "refresh": "5s", + "schemaVersion": 17, + "version": 0, + "links": [] + } diff --git a/hack/kind/resources/default/grafana-dashboard.yaml b/hack/kind/resources/default/grafana-dashboard.yaml index dad634db2..72b532452 100644 --- a/hack/kind/resources/default/grafana-dashboard.yaml +++ b/hack/kind/resources/default/grafana-dashboard.yaml @@ -8,11 +8,11 @@ spec: folder: my-folder-name instanceSelector: matchLabels: - dashboards: "grafana" + dashboards: grafana json: | { "id": null, - "title": "Simple Dashboard", + "title": "Simple Dashboard MatchLabels", "tags": [], "style": "dark", "timezone": "browser", diff --git a/hack/kind/resources/default/kustomization.yaml b/hack/kind/resources/default/kustomization.yaml index ddecf6c73..d353287d4 100644 --- a/hack/kind/resources/default/kustomization.yaml +++ b/hack/kind/resources/default/kustomization.yaml @@ -1,5 +1,10 @@ resources: - grafana.yaml + - grafana-2.yaml - grafana-dashboard.yaml + - grafana-dashboard-in.yaml + - grafana-dashboard-notin.yaml + - grafana-dashboard-exist.yaml + - grafana-dashboard-notexist.yaml - grafana-datasource.yaml - grafana-contactpoint.yaml From abc474abc534e6b24eeb159b45867a9c62a93e86 Mon Sep 17 00:00:00 2001 From: Ronan-WeScale Date: Tue, 16 Apr 2024 18:08:13 +0200 Subject: [PATCH 03/17] Fix - trailing whitespace --- hack/kind/resources/default/kustomization.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hack/kind/resources/default/kustomization.yaml b/hack/kind/resources/default/kustomization.yaml index d353287d4..09cecbd24 100644 --- a/hack/kind/resources/default/kustomization.yaml +++ b/hack/kind/resources/default/kustomization.yaml @@ -1,6 +1,6 @@ resources: - grafana.yaml - - grafana-2.yaml + - grafana-2.yaml - grafana-dashboard.yaml - grafana-dashboard-in.yaml - grafana-dashboard-notin.yaml From 1884ab47e1ea6c18156653e0888e7d98e5e0a88e Mon Sep 17 00:00:00 2001 From: Ronan-WeScale Date: Mon, 29 Apr 2024 15:26:49 +0200 Subject: [PATCH 04/17] Remove - unused kind resources --- hack/kind/resources/default/grafana-2.yaml | 32 -------------- .../default/grafana-dashboard-exist.yaml | 42 ------------------ .../default/grafana-dashboard-in.yaml | 44 ------------------- .../default/grafana-dashboard-notexist.yaml | 42 ------------------ .../default/grafana-dashboard-notin.yaml | 44 ------------------- .../kind/resources/default/kustomization.yaml | 5 --- 6 files changed, 209 deletions(-) delete mode 100644 hack/kind/resources/default/grafana-2.yaml delete mode 100644 hack/kind/resources/default/grafana-dashboard-exist.yaml delete mode 100644 hack/kind/resources/default/grafana-dashboard-in.yaml delete mode 100644 hack/kind/resources/default/grafana-dashboard-notexist.yaml delete mode 100644 hack/kind/resources/default/grafana-dashboard-notin.yaml diff --git a/hack/kind/resources/default/grafana-2.yaml b/hack/kind/resources/default/grafana-2.yaml deleted file mode 100644 index 4c210880c..000000000 --- a/hack/kind/resources/default/grafana-2.yaml +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: grafana.integreatly.org/v1beta1 -kind: Grafana -metadata: - name: grafana-2 - labels: - dashboards: "grafana-2" - exists: "" -spec: - client: - preferIngress: true - config: - log: - mode: "console" - auth: - disable_login_form: "false" - security: - admin_user: root - admin_password: secret - ingress: - spec: - ingressClassName: nginx - rules: - - host: grafana2.127.0.0.1.nip.io - http: - paths: - - backend: - service: - name: grafana-2-service - port: - number: 3000 - path: / - pathType: Prefix diff --git a/hack/kind/resources/default/grafana-dashboard-exist.yaml b/hack/kind/resources/default/grafana-dashboard-exist.yaml deleted file mode 100644 index 93a90460c..000000000 --- a/hack/kind/resources/default/grafana-dashboard-exist.yaml +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: grafana.integreatly.org/v1beta1 -kind: GrafanaDashboard -metadata: - name: grafana-exist - labels: - dashboards: "grafana" -spec: - folder: my-folder-name - instanceSelector: - matchExpressions: - - key: exists - operator: Exists - json: | - { - "id": null, - "title": "Simple Dashboard Exist", - "tags": [], - "style": "dark", - "timezone": "browser", - "editable": true, - "hideControls": false, - "graphTooltip": 1, - "panels": [], - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": { - "time_options": [], - "refresh_intervals": [] - }, - "templating": { - "list": [] - }, - "annotations": { - "list": [] - }, - "refresh": "5s", - "schemaVersion": 17, - "version": 0, - "links": [] - } diff --git a/hack/kind/resources/default/grafana-dashboard-in.yaml b/hack/kind/resources/default/grafana-dashboard-in.yaml deleted file mode 100644 index 8bacc4427..000000000 --- a/hack/kind/resources/default/grafana-dashboard-in.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: grafana.integreatly.org/v1beta1 -kind: GrafanaDashboard -metadata: - name: grafana-in - labels: - dashboards: "grafana" -spec: - folder: my-folder-name - instanceSelector: - matchExpressions: - - key: dashboards - operator: In - values: - - grafana-2 - json: | - { - "id": null, - "title": "Simple Dashboard In Grafana-2", - "tags": [], - "style": "dark", - "timezone": "browser", - "editable": true, - "hideControls": false, - "graphTooltip": 1, - "panels": [], - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": { - "time_options": [], - "refresh_intervals": [] - }, - "templating": { - "list": [] - }, - "annotations": { - "list": [] - }, - "refresh": "5s", - "schemaVersion": 17, - "version": 0, - "links": [] - } diff --git a/hack/kind/resources/default/grafana-dashboard-notexist.yaml b/hack/kind/resources/default/grafana-dashboard-notexist.yaml deleted file mode 100644 index b5b7fb044..000000000 --- a/hack/kind/resources/default/grafana-dashboard-notexist.yaml +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: grafana.integreatly.org/v1beta1 -kind: GrafanaDashboard -metadata: - name: grafana-notexist - labels: - dashboards: "grafana" -spec: - folder: my-folder-name - instanceSelector: - matchExpressions: - - key: exists - operator: DoesNotExist - json: | - { - "id": null, - "title": "Simple Dashboard NotExist", - "tags": [], - "style": "dark", - "timezone": "browser", - "editable": true, - "hideControls": false, - "graphTooltip": 1, - "panels": [], - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": { - "time_options": [], - "refresh_intervals": [] - }, - "templating": { - "list": [] - }, - "annotations": { - "list": [] - }, - "refresh": "5s", - "schemaVersion": 17, - "version": 0, - "links": [] - } diff --git a/hack/kind/resources/default/grafana-dashboard-notin.yaml b/hack/kind/resources/default/grafana-dashboard-notin.yaml deleted file mode 100644 index 4318e8933..000000000 --- a/hack/kind/resources/default/grafana-dashboard-notin.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: grafana.integreatly.org/v1beta1 -kind: GrafanaDashboard -metadata: - name: grafana-notin-grafana-2 - labels: - dashboards: "grafana" -spec: - folder: my-folder-name - instanceSelector: - matchExpressions: - - key: dashboards - operator: NotIn - values: - - grafana-2 - json: | - { - "id": null, - "title": "Simple Dashboard notIn grafana-2", - "tags": [], - "style": "dark", - "timezone": "browser", - "editable": true, - "hideControls": false, - "graphTooltip": 1, - "panels": [], - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": { - "time_options": [], - "refresh_intervals": [] - }, - "templating": { - "list": [] - }, - "annotations": { - "list": [] - }, - "refresh": "5s", - "schemaVersion": 17, - "version": 0, - "links": [] - } diff --git a/hack/kind/resources/default/kustomization.yaml b/hack/kind/resources/default/kustomization.yaml index 09cecbd24..ddecf6c73 100644 --- a/hack/kind/resources/default/kustomization.yaml +++ b/hack/kind/resources/default/kustomization.yaml @@ -1,10 +1,5 @@ resources: - grafana.yaml - - grafana-2.yaml - grafana-dashboard.yaml - - grafana-dashboard-in.yaml - - grafana-dashboard-notin.yaml - - grafana-dashboard-exist.yaml - - grafana-dashboard-notexist.yaml - grafana-datasource.yaml - grafana-contactpoint.yaml From 337b660442bba9b2c106a88d38e408bbfd6180e3 Mon Sep 17 00:00:00 2001 From: Ronan-WeScale Date: Mon, 29 Apr 2024 15:27:10 +0200 Subject: [PATCH 05/17] Update - controller matchInstance func --- controllers/controller_shared.go | 47 ++++++++++++++------------------ 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/controllers/controller_shared.go b/controllers/controller_shared.go index 60574266a..e7968c5f4 100644 --- a/controllers/controller_shared.go +++ b/controllers/controller_shared.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "slices" "time" "github.com/grafana/grafana-operator/v5/api/v1beta1" @@ -38,26 +39,7 @@ func GetMatchingInstances(ctx context.Context, k8sClient client.Client, labelSel var selectedList v1beta1.GrafanaList for _, instance := range list.Items { - selected := true - for _, matchExpression := range labelSelector.MatchExpressions { - if val, ok := instance.Labels[matchExpression.Key]; ok { - if matchExpression.Operator == "DoesNotExist" { - selected = false - break - } else if matchExpression.Operator == "Exists" { - break - } - valueMatched := labelMatchedExpression(val, matchExpression) - if !valueMatched { - selected = false - break - } - } else if matchExpression.Operator == "In" || matchExpression.Operator == "Exists" { - selected = false - break - } - } - + selected := labelMatchedExpression(instance, labelSelector) if selected { selectedList.Items = append(selectedList.Items, instance) } @@ -66,15 +48,28 @@ func GetMatchingInstances(ctx context.Context, k8sClient client.Client, labelSel return selectedList, err } -func labelMatchedExpression(val string, matchExpression v1.LabelSelectorRequirement) bool { - valueMatched := matchExpression.Operator == "NotIn" +func labelMatchedExpression(instance v1beta1.Grafana, labelSelector *v1.LabelSelector) bool { + selected := true + for _, matchExpression := range labelSelector.MatchExpressions { + if label, ok := instance.Labels[matchExpression.Key]; ok { + switch matchExpression.Operator { + case metav1.LabelSelectorOpDoesNotExist: + selected = false + case metav1.LabelSelectorOpExists: + selected = true + case metav1.LabelSelectorOpIn: + selected = slices.Contains(matchExpression.Values, label) + case metav1.LabelSelectorOpNotIn: + selected = !slices.Contains(matchExpression.Values, label) + } - for _, value := range matchExpression.Values { - if val == value { - return matchExpression.Operator == "In" + // All matchExpressions must evaluate to true in order to satisfy the conditions + if !selected { + break + } } } - return valueMatched + return selected } func ReconcilePlugins(ctx context.Context, k8sClient client.Client, scheme *runtime.Scheme, grafana *v1beta1.Grafana, plugins v1beta1.PluginList, resource string) error { From ac4450b3125d41c52ca718033238f2e2dd319b14 Mon Sep 17 00:00:00 2001 From: Ronan-WeScale Date: Mon, 29 Apr 2024 15:27:54 +0200 Subject: [PATCH 06/17] Add - instanceSelector e2e --- .../examples/instanceSelector/00-assert.yaml | 16 ++ .../instanceSelector/00-resources.yaml | 65 +++++++ .../examples/instanceSelector/01-assert.yaml | 6 + .../instanceSelector/01-resources.yaml | 160 ++++++++++++++++++ .../examples/instanceSelector/02-assert.yaml | 47 +++++ .../instanceSelector/02-resources.yaml | 69 ++++++++ .../instanceSelector/chainsaw-test.yaml | 26 +++ 7 files changed, 389 insertions(+) create mode 100644 tests/e2e/examples/instanceSelector/00-assert.yaml create mode 100644 tests/e2e/examples/instanceSelector/00-resources.yaml create mode 100644 tests/e2e/examples/instanceSelector/01-assert.yaml create mode 100644 tests/e2e/examples/instanceSelector/01-resources.yaml create mode 100644 tests/e2e/examples/instanceSelector/02-assert.yaml create mode 100644 tests/e2e/examples/instanceSelector/02-resources.yaml create mode 100755 tests/e2e/examples/instanceSelector/chainsaw-test.yaml diff --git a/tests/e2e/examples/instanceSelector/00-assert.yaml b/tests/e2e/examples/instanceSelector/00-assert.yaml new file mode 100644 index 000000000..da6f55c3e --- /dev/null +++ b/tests/e2e/examples/instanceSelector/00-assert.yaml @@ -0,0 +1,16 @@ +apiVersion: grafana.integreatly.org/v1beta1 +kind: Grafana +metadata: + name: grafana +status: + stage: complete + stageStatus: success +--- +apiVersion: grafana.integreatly.org/v1beta1 +kind: Grafana +metadata: + name: grafana-1 +status: + stage: complete + stageStatus: success +--- diff --git a/tests/e2e/examples/instanceSelector/00-resources.yaml b/tests/e2e/examples/instanceSelector/00-resources.yaml new file mode 100644 index 000000000..c98465cbe --- /dev/null +++ b/tests/e2e/examples/instanceSelector/00-resources.yaml @@ -0,0 +1,65 @@ +--- +apiVersion: grafana.integreatly.org/v1beta1 +kind: Grafana +metadata: + name: grafana + labels: + dashboards: "grafana" +spec: + client: + preferIngress: true + config: + log: + mode: "console" + auth: + disable_login_form: "false" + security: + admin_user: root + admin_password: secret + ingress: + spec: + ingressClassName: nginx + rules: + - host: grafana.127.0.0.1.nip.io + http: + paths: + - backend: + service: + name: grafana-service + port: + number: 3000 + path: / + pathType: Prefix +--- +apiVersion: grafana.integreatly.org/v1beta1 +kind: Grafana +metadata: + name: grafana-1 + labels: + dashboards: "grafana-1" +spec: + client: + preferIngress: true + config: + log: + mode: "console" + auth: + disable_login_form: "false" + security: + admin_user: root + admin_password: secret + ingress: + spec: + ingressClassName: nginx + rules: + - host: grafana-1.127.0.0.1.nip.io + http: + paths: + - backend: + service: + name: grafana-1-service + port: + number: 3000 + path: / + pathType: Prefix +--- diff --git a/tests/e2e/examples/instanceSelector/01-assert.yaml b/tests/e2e/examples/instanceSelector/01-assert.yaml new file mode 100644 index 000000000..389607321 --- /dev/null +++ b/tests/e2e/examples/instanceSelector/01-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: dashboard-definition +--- + diff --git a/tests/e2e/examples/instanceSelector/01-resources.yaml b/tests/e2e/examples/instanceSelector/01-resources.yaml new file mode 100644 index 000000000..3d7ae679b --- /dev/null +++ b/tests/e2e/examples/instanceSelector/01-resources.yaml @@ -0,0 +1,160 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: dashboard-definition +data: + matchLabels: > + { + "id": null, + "title": "MatchLabels", + "tags": [], + "style": "dark", + "timezone": "browser", + "editable": true, + "hideControls": false, + "graphTooltip": 1, + "panels": [], + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "time_options": [], + "refresh_intervals": [] + }, + "templating": { + "list": [] + }, + "annotations": { + "list": [] + }, + "refresh": "5s", + "schemaVersion": 17, + "version": 0, + "links": [], + "uid": "matchLabels" + } + Exists: > + { + "id": null, + "title": "Exists", + "tags": [], + "style": "dark", + "timezone": "browser", + "editable": true, + "hideControls": false, + "graphTooltip": 1, + "panels": [], + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "time_options": [], + "refresh_intervals": [] + }, + "templating": { + "list": [] + }, + "annotations": { + "list": [] + }, + "refresh": "5s", + "schemaVersion": 17, + "version": 0, + "links": [], + "uid": "exists" + } + NotExists: > + { + "id": null, + "title": "NotExists", + "tags": [], + "style": "dark", + "timezone": "browser", + "editable": true, + "hideControls": false, + "graphTooltip": 1, + "panels": [], + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "time_options": [], + "refresh_intervals": [] + }, + "templating": { + "list": [] + }, + "annotations": { + "list": [] + }, + "refresh": "5s", + "schemaVersion": 17, + "version": 0, + "links": [], + "uid": "notexists" + } + NotIn: > + { + "id": null, + "title": "NotIn", + "tags": [], + "style": "dark", + "timezone": "browser", + "editable": true, + "hideControls": false, + "graphTooltip": 1, + "panels": [], + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "time_options": [], + "refresh_intervals": [] + }, + "templating": { + "list": [] + }, + "annotations": { + "list": [] + }, + "refresh": "5s", + "schemaVersion": 17, + "version": 0, + "links": [], + "uid": "notin" + } + In: > + { + "id": null, + "title": "In", + "tags": [], + "style": "dark", + "timezone": "browser", + "editable": true, + "hideControls": false, + "graphTooltip": 1, + "panels": [], + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "time_options": [], + "refresh_intervals": [] + }, + "templating": { + "list": [] + }, + "annotations": { + "list": [] + }, + "refresh": "5s", + "schemaVersion": 17, + "version": 0, + "links": [], + "uid": "in" + } \ No newline at end of file diff --git a/tests/e2e/examples/instanceSelector/02-assert.yaml b/tests/e2e/examples/instanceSelector/02-assert.yaml new file mode 100644 index 000000000..c6b1d2227 --- /dev/null +++ b/tests/e2e/examples/instanceSelector/02-assert.yaml @@ -0,0 +1,47 @@ +apiVersion: grafana.integreatly.org/v1beta1 +kind: GrafanaDashboard +metadata: + name: grafana-match-labels +--- +apiVersion: grafana.integreatly.org/v1beta1 +kind: GrafanaDashboard +metadata: + name: grafana-exists +--- +apiVersion: grafana.integreatly.org/v1beta1 +kind: GrafanaDashboard +metadata: + name: grafana-not-exists +--- +apiVersion: grafana.integreatly.org/v1beta1 +kind: GrafanaDashboard +metadata: + name: grafana-not-in +--- +apiVersion: grafana.integreatly.org/v1beta1 +kind: GrafanaDashboard +metadata: + name: grafana-in +--- +apiVersion: grafana.integreatly.org/v1beta1 +kind: Grafana +metadata: + name: grafana +status: + dashboards: + - (join('/', [$namespace, 'grafana-not-in/notin'])) + - (join('/', [$namespace, 'grafana-exists/exists'])) + - (join('/', [$namespace, 'grafana-match-labels/matchLabels'])) + stage: complete + stageStatus: success +--- +apiVersion: grafana.integreatly.org/v1beta1 +kind: Grafana +metadata: + name: grafana-1 +status: + dashboards: + - (join('/', [$namespace, 'grafana-in/in'])) + - (join('/', [$namespace, 'grafana-exists/exists'])) + stage: complete + stageStatus: success \ No newline at end of file diff --git a/tests/e2e/examples/instanceSelector/02-resources.yaml b/tests/e2e/examples/instanceSelector/02-resources.yaml new file mode 100644 index 000000000..474e12b81 --- /dev/null +++ b/tests/e2e/examples/instanceSelector/02-resources.yaml @@ -0,0 +1,69 @@ +--- +apiVersion: grafana.integreatly.org/v1beta1 +kind: GrafanaDashboard +metadata: + name: grafana-match-labels +spec: + instanceSelector: + matchLabels: + dashboards: "grafana" + configMapRef: + name: dashboard-definition + key: matchLabels +--- +apiVersion: grafana.integreatly.org/v1beta1 +kind: GrafanaDashboard +metadata: + name: grafana-exists +spec: + instanceSelector: + matchExpressions: + - key: dashboards + operator: Exists + configMapRef: + name: dashboard-definition + key: Exists +--- +apiVersion: grafana.integreatly.org/v1beta1 +kind: GrafanaDashboard +metadata: + name: grafana-not-exists +spec: + instanceSelector: + matchExpressions: + - key: dashboards + operator: DoesNotExist + configMapRef: + name: dashboard-definition + key: NotExists +--- +apiVersion: grafana.integreatly.org/v1beta1 +kind: GrafanaDashboard +metadata: + name: grafana-not-in +spec: + instanceSelector: + matchExpressions: + - key: dashboards + operator: NotIn + values: + - grafana-1 + configMapRef: + name: dashboard-definition + key: NotIn +--- +apiVersion: grafana.integreatly.org/v1beta1 +kind: GrafanaDashboard +metadata: + name: grafana-in +spec: + instanceSelector: + matchExpressions: + - key: dashboards + operator: In + values: + - grafana-1 + configMapRef: + name: dashboard-definition + key: In +--- \ No newline at end of file diff --git a/tests/e2e/examples/instanceSelector/chainsaw-test.yaml b/tests/e2e/examples/instanceSelector/chainsaw-test.yaml new file mode 100755 index 000000000..5f80d6d5d --- /dev/null +++ b/tests/e2e/examples/instanceSelector/chainsaw-test.yaml @@ -0,0 +1,26 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/kyverno/chainsaw/main/.schemas/json/test-chainsaw-v1alpha1.json +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: instance-selector +spec: + steps: + - name: step-00 + try: + - apply: + file: 00-resources.yaml + - assert: + file: 00-assert.yaml + - name: step-01 + try: + - apply: + file: 01-resources.yaml + - assert: + file: 01-assert.yaml + - name: step-02 + try: + - apply: + file: 02-resources.yaml + - assert: + template: true + file: 02-assert.yaml From d6106d5c44e8f94e29617590f213562cab36c50e Mon Sep 17 00:00:00 2001 From: Ronan-WeScale Date: Mon, 29 Apr 2024 18:39:16 +0200 Subject: [PATCH 07/17] Fix - file-checks newline --- tests/e2e/examples/instanceSelector/01-resources.yaml | 2 +- tests/e2e/examples/instanceSelector/02-assert.yaml | 2 +- tests/e2e/examples/instanceSelector/02-resources.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/e2e/examples/instanceSelector/01-resources.yaml b/tests/e2e/examples/instanceSelector/01-resources.yaml index 3d7ae679b..6132a7b96 100644 --- a/tests/e2e/examples/instanceSelector/01-resources.yaml +++ b/tests/e2e/examples/instanceSelector/01-resources.yaml @@ -157,4 +157,4 @@ data: "version": 0, "links": [], "uid": "in" - } \ No newline at end of file + } diff --git a/tests/e2e/examples/instanceSelector/02-assert.yaml b/tests/e2e/examples/instanceSelector/02-assert.yaml index c6b1d2227..ae4e8c00a 100644 --- a/tests/e2e/examples/instanceSelector/02-assert.yaml +++ b/tests/e2e/examples/instanceSelector/02-assert.yaml @@ -44,4 +44,4 @@ status: - (join('/', [$namespace, 'grafana-in/in'])) - (join('/', [$namespace, 'grafana-exists/exists'])) stage: complete - stageStatus: success \ No newline at end of file + stageStatus: success diff --git a/tests/e2e/examples/instanceSelector/02-resources.yaml b/tests/e2e/examples/instanceSelector/02-resources.yaml index 474e12b81..2be540940 100644 --- a/tests/e2e/examples/instanceSelector/02-resources.yaml +++ b/tests/e2e/examples/instanceSelector/02-resources.yaml @@ -66,4 +66,4 @@ spec: configMapRef: name: dashboard-definition key: In ---- \ No newline at end of file +--- From e6120895122983ce7012e7562de8382b63eec689 Mon Sep 17 00:00:00 2001 From: Ronan-WeScale Date: Tue, 30 Apr 2024 11:44:53 +0200 Subject: [PATCH 08/17] Fix - e2e test by deleting ingress --- .../instanceSelector/00-resources.yaml | 32 ++----------------- .../examples/instanceSelector/02-assert.yaml | 2 +- 2 files changed, 3 insertions(+), 31 deletions(-) diff --git a/tests/e2e/examples/instanceSelector/00-resources.yaml b/tests/e2e/examples/instanceSelector/00-resources.yaml index c98465cbe..b68e7b913 100644 --- a/tests/e2e/examples/instanceSelector/00-resources.yaml +++ b/tests/e2e/examples/instanceSelector/00-resources.yaml @@ -7,7 +7,7 @@ metadata: dashboards: "grafana" spec: client: - preferIngress: true + preferIngress: false config: log: mode: "console" @@ -16,20 +16,6 @@ spec: security: admin_user: root admin_password: secret - ingress: - spec: - ingressClassName: nginx - rules: - - host: grafana.127.0.0.1.nip.io - http: - paths: - - backend: - service: - name: grafana-service - port: - number: 3000 - path: / - pathType: Prefix --- apiVersion: grafana.integreatly.org/v1beta1 kind: Grafana @@ -39,7 +25,7 @@ metadata: dashboards: "grafana-1" spec: client: - preferIngress: true + preferIngress: false config: log: mode: "console" @@ -48,18 +34,4 @@ spec: security: admin_user: root admin_password: secret - ingress: - spec: - ingressClassName: nginx - rules: - - host: grafana-1.127.0.0.1.nip.io - http: - paths: - - backend: - service: - name: grafana-1-service - port: - number: 3000 - path: / - pathType: Prefix --- diff --git a/tests/e2e/examples/instanceSelector/02-assert.yaml b/tests/e2e/examples/instanceSelector/02-assert.yaml index ae4e8c00a..1617ff16a 100644 --- a/tests/e2e/examples/instanceSelector/02-assert.yaml +++ b/tests/e2e/examples/instanceSelector/02-assert.yaml @@ -29,9 +29,9 @@ metadata: name: grafana status: dashboards: + - (join('/', [$namespace, 'grafana-match-labels/matchLabels'])) - (join('/', [$namespace, 'grafana-not-in/notin'])) - (join('/', [$namespace, 'grafana-exists/exists'])) - - (join('/', [$namespace, 'grafana-match-labels/matchLabels'])) stage: complete stageStatus: success --- From 3a5500571cb755a6d264721f6a629ce983f4e007 Mon Sep 17 00:00:00 2001 From: Ronan-WeScale Date: Tue, 30 Apr 2024 17:07:20 +0200 Subject: [PATCH 09/17] Add - unit test for labelMatchedExpression func --- controllers/dashboard_controller_test.go | 150 +++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/controllers/dashboard_controller_test.go b/controllers/dashboard_controller_test.go index f14f919d3..39e6afaef 100644 --- a/controllers/dashboard_controller_test.go +++ b/controllers/dashboard_controller_test.go @@ -155,3 +155,153 @@ func TestGetDashboardEnvs(t *testing.T) { assert.NotNil(t, envs) assert.True(t, len(envs) == 1, "Expected 1 env, got %d", len(envs)) } + +func TestLabelMatchedExpression(t *testing.T) { + dashboardList := v1beta1.GrafanaDashboardList{ + TypeMeta: metav1.TypeMeta{}, + ListMeta: metav1.ListMeta{}, + Items: []v1beta1.GrafanaDashboard{ + { + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "grafana-match-labels", + Namespace: "grafana-operator-system", + }, + Spec: v1beta1.GrafanaDashboardSpec{ + InstanceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "dashboard": "grafana", + }, + }, + }, + }, + { + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "grafana-exists", + Namespace: "grafana-operator-system", + }, + Spec: v1beta1.GrafanaDashboardSpec{ + InstanceSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Operator: metav1.LabelSelectorOpExists, + Key: "dashboard", + }, + }, + }, + }, + }, + { + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "grafana-not-exists", + Namespace: "grafana-operator-system", + }, + Spec: v1beta1.GrafanaDashboardSpec{ + InstanceSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Operator: metav1.LabelSelectorOpDoesNotExist, + Key: "dashboard", + }, + }, + }, + }, + }, + { + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "grafana-not-in", + Namespace: "grafana-operator-system", + }, + Spec: v1beta1.GrafanaDashboardSpec{ + InstanceSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Operator: metav1.LabelSelectorOpNotIn, + Key: "dashboard", + Values: []string{ + "grafana-1", + }, + }, + }, + }, + }, + }, + { + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "grafana-in", + Namespace: "grafana-operator-system", + }, + Spec: v1beta1.GrafanaDashboardSpec{ + InstanceSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Operator: metav1.LabelSelectorOpIn, + Key: "dashboard", + Values: []string{ + "grafana-1", + }, + }, + }, + }, + }, + }, + }, + } + grafanaList := []v1beta1.Grafana{ + { + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "grafana", + Namespace: "grafana-operator-system", + Labels: map[string]string{ + "dashboard": "grafana", + }, + }, + Status: v1beta1.GrafanaStatus{ + Dashboards: v1beta1.NamespacedResourceList{ + "grafana-match-labels/matchLabels", + "grafana-not-in/notin", + "grafana-exists/exists", + }, + }, + }, + { + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "grafana-1", + Namespace: "grafana-operator-system", + Labels: map[string]string{ + "dashboard": "grafana-1", + }, + }, + Status: v1beta1.GrafanaStatus{ + Dashboards: v1beta1.NamespacedResourceList{ + "grafana-in/in", + "grafana-exists/exists", + }, + }, + }, + } + + for _, dashboard := range dashboardList.Items { + var selectedList v1beta1.GrafanaList + for _, instance := range grafanaList { + selected := labelMatchedExpression(instance, dashboard.Spec.InstanceSelector) + if selected { + selectedList.Items = append(selectedList.Items, instance) + } + } + for _, instance := range selectedList.Items { + if instance.Name == "grafana" { + assert.Equal(t, v1beta1.NamespacedResourceList{"grafana-match-labels/matchLabels", "grafana-not-in/notin", "grafana-exists/exists"}, instance.Status.Dashboards) + } + if instance.Name == "grafana-1" { + assert.Equal(t, v1beta1.NamespacedResourceList{"grafana-in/in", "grafana-exists/exists"}, instance.Status.Dashboards) + } + } + } +} From 20c80ce57a943d45bc5d9715da98b30452002e79 Mon Sep 17 00:00:00 2001 From: Ronan-WeScale Date: Tue, 30 Apr 2024 17:07:35 +0200 Subject: [PATCH 10/17] Fix - e2e test --- tests/e2e/examples/instanceSelector/02-assert.yaml | 9 ++------- tests/e2e/examples/instanceSelector/chainsaw-test.yaml | 2 ++ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/e2e/examples/instanceSelector/02-assert.yaml b/tests/e2e/examples/instanceSelector/02-assert.yaml index 1617ff16a..f170a91a4 100644 --- a/tests/e2e/examples/instanceSelector/02-assert.yaml +++ b/tests/e2e/examples/instanceSelector/02-assert.yaml @@ -28,10 +28,7 @@ kind: Grafana metadata: name: grafana status: - dashboards: - - (join('/', [$namespace, 'grafana-match-labels/matchLabels'])) - - (join('/', [$namespace, 'grafana-not-in/notin'])) - - (join('/', [$namespace, 'grafana-exists/exists'])) + (length(dashboards)): 3 stage: complete stageStatus: success --- @@ -40,8 +37,6 @@ kind: Grafana metadata: name: grafana-1 status: - dashboards: - - (join('/', [$namespace, 'grafana-in/in'])) - - (join('/', [$namespace, 'grafana-exists/exists'])) + (length(dashboards)): 2 stage: complete stageStatus: success diff --git a/tests/e2e/examples/instanceSelector/chainsaw-test.yaml b/tests/e2e/examples/instanceSelector/chainsaw-test.yaml index 5f80d6d5d..0f75ede61 100755 --- a/tests/e2e/examples/instanceSelector/chainsaw-test.yaml +++ b/tests/e2e/examples/instanceSelector/chainsaw-test.yaml @@ -21,6 +21,8 @@ spec: try: - apply: file: 02-resources.yaml + - sleep: + duration: 10s - assert: template: true file: 02-assert.yaml From 92e407045dfefcc7351d0c6d5af274da98eb8de3 Mon Sep 17 00:00:00 2001 From: Igor Beliakov Date: Sun, 5 May 2024 13:35:41 +0200 Subject: [PATCH 11/17] chore(controllers): move TestLabelMatchedExpression to another file --- controllers/controller_shared_test.go | 175 +++++++++++++++++++++++ controllers/dashboard_controller_test.go | 150 ------------------- 2 files changed, 175 insertions(+), 150 deletions(-) create mode 100644 controllers/controller_shared_test.go diff --git a/controllers/controller_shared_test.go b/controllers/controller_shared_test.go new file mode 100644 index 000000000..9c5d5b83f --- /dev/null +++ b/controllers/controller_shared_test.go @@ -0,0 +1,175 @@ +/* +Copyright 2022. + +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 controllers + +import ( + "testing" + + "github.com/grafana/grafana-operator/v5/api/v1beta1" + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestLabelMatchedExpression(t *testing.T) { + dashboardList := v1beta1.GrafanaDashboardList{ + TypeMeta: metav1.TypeMeta{}, + ListMeta: metav1.ListMeta{}, + Items: []v1beta1.GrafanaDashboard{ + { + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "grafana-match-labels", + Namespace: "grafana-operator-system", + }, + Spec: v1beta1.GrafanaDashboardSpec{ + InstanceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "dashboard": "grafana", + }, + }, + }, + }, + { + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "grafana-exists", + Namespace: "grafana-operator-system", + }, + Spec: v1beta1.GrafanaDashboardSpec{ + InstanceSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Operator: metav1.LabelSelectorOpExists, + Key: "dashboard", + }, + }, + }, + }, + }, + { + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "grafana-not-exists", + Namespace: "grafana-operator-system", + }, + Spec: v1beta1.GrafanaDashboardSpec{ + InstanceSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Operator: metav1.LabelSelectorOpDoesNotExist, + Key: "dashboard", + }, + }, + }, + }, + }, + { + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "grafana-not-in", + Namespace: "grafana-operator-system", + }, + Spec: v1beta1.GrafanaDashboardSpec{ + InstanceSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Operator: metav1.LabelSelectorOpNotIn, + Key: "dashboard", + Values: []string{ + "grafana-1", + }, + }, + }, + }, + }, + }, + { + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "grafana-in", + Namespace: "grafana-operator-system", + }, + Spec: v1beta1.GrafanaDashboardSpec{ + InstanceSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Operator: metav1.LabelSelectorOpIn, + Key: "dashboard", + Values: []string{ + "grafana-1", + }, + }, + }, + }, + }, + }, + }, + } + grafanaList := []v1beta1.Grafana{ + { + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "grafana", + Namespace: "grafana-operator-system", + Labels: map[string]string{ + "dashboard": "grafana", + }, + }, + Status: v1beta1.GrafanaStatus{ + Dashboards: v1beta1.NamespacedResourceList{ + "grafana-match-labels/matchLabels", + "grafana-not-in/notin", + "grafana-exists/exists", + }, + }, + }, + { + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "grafana-1", + Namespace: "grafana-operator-system", + Labels: map[string]string{ + "dashboard": "grafana-1", + }, + }, + Status: v1beta1.GrafanaStatus{ + Dashboards: v1beta1.NamespacedResourceList{ + "grafana-in/in", + "grafana-exists/exists", + }, + }, + }, + } + + for _, dashboard := range dashboardList.Items { + var selectedList v1beta1.GrafanaList + for _, instance := range grafanaList { + selected := labelMatchedExpression(instance, dashboard.Spec.InstanceSelector) + if selected { + selectedList.Items = append(selectedList.Items, instance) + } + } + for _, instance := range selectedList.Items { + if instance.Name == "grafana" { + assert.Equal(t, v1beta1.NamespacedResourceList{"grafana-match-labels/matchLabels", "grafana-not-in/notin", "grafana-exists/exists"}, instance.Status.Dashboards) + } + if instance.Name == "grafana-1" { + assert.Equal(t, v1beta1.NamespacedResourceList{"grafana-in/in", "grafana-exists/exists"}, instance.Status.Dashboards) + } + } + } +} diff --git a/controllers/dashboard_controller_test.go b/controllers/dashboard_controller_test.go index 39e6afaef..f14f919d3 100644 --- a/controllers/dashboard_controller_test.go +++ b/controllers/dashboard_controller_test.go @@ -155,153 +155,3 @@ func TestGetDashboardEnvs(t *testing.T) { assert.NotNil(t, envs) assert.True(t, len(envs) == 1, "Expected 1 env, got %d", len(envs)) } - -func TestLabelMatchedExpression(t *testing.T) { - dashboardList := v1beta1.GrafanaDashboardList{ - TypeMeta: metav1.TypeMeta{}, - ListMeta: metav1.ListMeta{}, - Items: []v1beta1.GrafanaDashboard{ - { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "grafana-match-labels", - Namespace: "grafana-operator-system", - }, - Spec: v1beta1.GrafanaDashboardSpec{ - InstanceSelector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "dashboard": "grafana", - }, - }, - }, - }, - { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "grafana-exists", - Namespace: "grafana-operator-system", - }, - Spec: v1beta1.GrafanaDashboardSpec{ - InstanceSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Operator: metav1.LabelSelectorOpExists, - Key: "dashboard", - }, - }, - }, - }, - }, - { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "grafana-not-exists", - Namespace: "grafana-operator-system", - }, - Spec: v1beta1.GrafanaDashboardSpec{ - InstanceSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Operator: metav1.LabelSelectorOpDoesNotExist, - Key: "dashboard", - }, - }, - }, - }, - }, - { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "grafana-not-in", - Namespace: "grafana-operator-system", - }, - Spec: v1beta1.GrafanaDashboardSpec{ - InstanceSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Operator: metav1.LabelSelectorOpNotIn, - Key: "dashboard", - Values: []string{ - "grafana-1", - }, - }, - }, - }, - }, - }, - { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "grafana-in", - Namespace: "grafana-operator-system", - }, - Spec: v1beta1.GrafanaDashboardSpec{ - InstanceSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Operator: metav1.LabelSelectorOpIn, - Key: "dashboard", - Values: []string{ - "grafana-1", - }, - }, - }, - }, - }, - }, - }, - } - grafanaList := []v1beta1.Grafana{ - { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "grafana", - Namespace: "grafana-operator-system", - Labels: map[string]string{ - "dashboard": "grafana", - }, - }, - Status: v1beta1.GrafanaStatus{ - Dashboards: v1beta1.NamespacedResourceList{ - "grafana-match-labels/matchLabels", - "grafana-not-in/notin", - "grafana-exists/exists", - }, - }, - }, - { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "grafana-1", - Namespace: "grafana-operator-system", - Labels: map[string]string{ - "dashboard": "grafana-1", - }, - }, - Status: v1beta1.GrafanaStatus{ - Dashboards: v1beta1.NamespacedResourceList{ - "grafana-in/in", - "grafana-exists/exists", - }, - }, - }, - } - - for _, dashboard := range dashboardList.Items { - var selectedList v1beta1.GrafanaList - for _, instance := range grafanaList { - selected := labelMatchedExpression(instance, dashboard.Spec.InstanceSelector) - if selected { - selectedList.Items = append(selectedList.Items, instance) - } - } - for _, instance := range selectedList.Items { - if instance.Name == "grafana" { - assert.Equal(t, v1beta1.NamespacedResourceList{"grafana-match-labels/matchLabels", "grafana-not-in/notin", "grafana-exists/exists"}, instance.Status.Dashboards) - } - if instance.Name == "grafana-1" { - assert.Equal(t, v1beta1.NamespacedResourceList{"grafana-in/in", "grafana-exists/exists"}, instance.Status.Dashboards) - } - } - } -} From 57c8cf4fcc94a5fb753a8fc3c18c03470a538b78 Mon Sep 17 00:00:00 2001 From: Igor Beliakov Date: Sun, 5 May 2024 14:37:05 +0200 Subject: [PATCH 12/17] chore: rewrite labelsMatchExpressions and TestLabelsMatchExpressions --- controllers/controller_shared.go | 24 ++- controllers/controller_shared_test.go | 267 ++++++++++++++------------ 2 files changed, 165 insertions(+), 126 deletions(-) diff --git a/controllers/controller_shared.go b/controllers/controller_shared.go index e7968c5f4..efe5e9da2 100644 --- a/controllers/controller_shared.go +++ b/controllers/controller_shared.go @@ -39,7 +39,7 @@ func GetMatchingInstances(ctx context.Context, k8sClient client.Client, labelSel var selectedList v1beta1.GrafanaList for _, instance := range list.Items { - selected := labelMatchedExpression(instance, labelSelector) + selected := labelsMatchExpressions(instance.Labels, labelSelector.MatchExpressions) if selected { selectedList.Items = append(selectedList.Items, instance) } @@ -48,10 +48,19 @@ func GetMatchingInstances(ctx context.Context, k8sClient client.Client, labelSel return selectedList, err } -func labelMatchedExpression(instance v1beta1.Grafana, labelSelector *v1.LabelSelector) bool { - selected := true - for _, matchExpression := range labelSelector.MatchExpressions { - if label, ok := instance.Labels[matchExpression.Key]; ok { +func labelsMatchExpressions(labels map[string]string, matchExpressions []metav1.LabelSelectorRequirement) bool { + if len(labels) == 0 { + return false + } + + if len(matchExpressions) == 0 { + return true + } + + for _, matchExpression := range matchExpressions { + selected := false + + if label, ok := labels[matchExpression.Key]; ok { switch matchExpression.Operator { case metav1.LabelSelectorOpDoesNotExist: selected = false @@ -65,11 +74,12 @@ func labelMatchedExpression(instance v1beta1.Grafana, labelSelector *v1.LabelSel // All matchExpressions must evaluate to true in order to satisfy the conditions if !selected { - break + return false } } } - return selected + + return true } func ReconcilePlugins(ctx context.Context, k8sClient client.Client, scheme *runtime.Scheme, grafana *v1beta1.Grafana, plugins v1beta1.PluginList, resource string) error { diff --git a/controllers/controller_shared_test.go b/controllers/controller_shared_test.go index 9c5d5b83f..bc617f603 100644 --- a/controllers/controller_shared_test.go +++ b/controllers/controller_shared_test.go @@ -19,157 +19,186 @@ package controllers import ( "testing" - "github.com/grafana/grafana-operator/v5/api/v1beta1" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func TestLabelMatchedExpression(t *testing.T) { - dashboardList := v1beta1.GrafanaDashboardList{ - TypeMeta: metav1.TypeMeta{}, - ListMeta: metav1.ListMeta{}, - Items: []v1beta1.GrafanaDashboard{ - { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "grafana-match-labels", - Namespace: "grafana-operator-system", +func TestLabelsMatchExpressions(t *testing.T) { + tests := []struct { + name string + labels map[string]string + matchExpressions []metav1.LabelSelectorRequirement + want bool + }{ + { + name: "No labels and no expressions", + labels: map[string]string{}, + matchExpressions: []metav1.LabelSelectorRequirement{}, + want: false, + }, + { + name: "No labels", + labels: map[string]string{}, + matchExpressions: []metav1.LabelSelectorRequirement{ + { + Operator: metav1.LabelSelectorOpExists, + Key: "dashboards", }, - Spec: v1beta1.GrafanaDashboardSpec{ - InstanceSelector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "dashboard": "grafana", - }, - }, + }, + want: false, + }, + { + name: "No matchExpressions", + labels: map[string]string{ + "dashboards": "grafana", + }, + matchExpressions: []metav1.LabelSelectorRequirement{}, + want: true, + }, + { + name: "Matches DoesNotExist", + labels: map[string]string{ + "dashboards": "grafana", + }, + matchExpressions: []metav1.LabelSelectorRequirement{ + { + Operator: metav1.LabelSelectorOpDoesNotExist, + Key: "dashboards", }, }, - { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "grafana-exists", - Namespace: "grafana-operator-system", + want: false, + }, + { + name: "Matches Exists", + labels: map[string]string{ + "dashboards": "grafana", + }, + matchExpressions: []metav1.LabelSelectorRequirement{ + { + Operator: metav1.LabelSelectorOpExists, + Key: "dashboards", }, - Spec: v1beta1.GrafanaDashboardSpec{ - InstanceSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Operator: metav1.LabelSelectorOpExists, - Key: "dashboard", - }, - }, + }, + want: true, + }, + { + name: "Matches In", + labels: map[string]string{ + "dashboards": "grafana", + }, + matchExpressions: []metav1.LabelSelectorRequirement{ + { + Operator: metav1.LabelSelectorOpIn, + Key: "dashboards", + Values: []string{ + "grafana", }, }, }, - { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "grafana-not-exists", - Namespace: "grafana-operator-system", - }, - Spec: v1beta1.GrafanaDashboardSpec{ - InstanceSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Operator: metav1.LabelSelectorOpDoesNotExist, - Key: "dashboard", - }, - }, + want: true, + }, + { + name: "Matches NotIn", + labels: map[string]string{ + "dashboards": "grafana", + }, + matchExpressions: []metav1.LabelSelectorRequirement{ + { + Operator: metav1.LabelSelectorOpNotIn, + Key: "dashboards", + Values: []string{ + "grafana", }, }, }, - { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "grafana-not-in", - Namespace: "grafana-operator-system", - }, - Spec: v1beta1.GrafanaDashboardSpec{ - InstanceSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Operator: metav1.LabelSelectorOpNotIn, - Key: "dashboard", - Values: []string{ - "grafana-1", - }, - }, - }, + want: false, + }, + { + name: "Does not match In", + labels: map[string]string{ + "dashboards": "grafana", + }, + matchExpressions: []metav1.LabelSelectorRequirement{ + { + Operator: metav1.LabelSelectorOpIn, + Key: "dashboards", + Values: []string{ + "grafana-external", }, }, }, - { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "grafana-in", - Namespace: "grafana-operator-system", - }, - Spec: v1beta1.GrafanaDashboardSpec{ - InstanceSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Operator: metav1.LabelSelectorOpIn, - Key: "dashboard", - Values: []string{ - "grafana-1", - }, - }, - }, + want: false, + }, + { + name: "Does not match NotIn", + labels: map[string]string{ + "dashboards": "grafana", + }, + matchExpressions: []metav1.LabelSelectorRequirement{ + { + Operator: metav1.LabelSelectorOpNotIn, + Key: "dashboards", + Values: []string{ + "grafana-external", }, }, }, + want: true, }, - } - grafanaList := []v1beta1.Grafana{ { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "grafana", - Namespace: "grafana-operator-system", - Labels: map[string]string{ - "dashboard": "grafana", - }, + name: "Matches multiple expressions", + labels: map[string]string{ + "dashboards": "grafana", + "environment": "production", }, - Status: v1beta1.GrafanaStatus{ - Dashboards: v1beta1.NamespacedResourceList{ - "grafana-match-labels/matchLabels", - "grafana-not-in/notin", - "grafana-exists/exists", + matchExpressions: []metav1.LabelSelectorRequirement{ + { + Operator: metav1.LabelSelectorOpIn, + Key: "dashboards", + Values: []string{ + "grafana", + }, + }, + { + Operator: metav1.LabelSelectorOpIn, + Key: "environment", + Values: []string{ + "production", + }, }, }, + want: true, }, { - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "grafana-1", - Namespace: "grafana-operator-system", - Labels: map[string]string{ - "dashboard": "grafana-1", - }, + name: "Doesn't match one of expressions", + labels: map[string]string{ + "dashboards": "grafana", + "environment": "production", }, - Status: v1beta1.GrafanaStatus{ - Dashboards: v1beta1.NamespacedResourceList{ - "grafana-in/in", - "grafana-exists/exists", + matchExpressions: []metav1.LabelSelectorRequirement{ + { + Operator: metav1.LabelSelectorOpIn, + Key: "dashboards", + Values: []string{ + "grafana", + }, + }, + { + Operator: metav1.LabelSelectorOpIn, + Key: "environment", + Values: []string{ + "development", + }, }, }, + want: false, }, } - for _, dashboard := range dashboardList.Items { - var selectedList v1beta1.GrafanaList - for _, instance := range grafanaList { - selected := labelMatchedExpression(instance, dashboard.Spec.InstanceSelector) - if selected { - selectedList.Items = append(selectedList.Items, instance) - } - } - for _, instance := range selectedList.Items { - if instance.Name == "grafana" { - assert.Equal(t, v1beta1.NamespacedResourceList{"grafana-match-labels/matchLabels", "grafana-not-in/notin", "grafana-exists/exists"}, instance.Status.Dashboards) - } - if instance.Name == "grafana-1" { - assert.Equal(t, v1beta1.NamespacedResourceList{"grafana-in/in", "grafana-exists/exists"}, instance.Status.Dashboards) - } - } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := labelsMatchExpressions(tt.labels, tt.matchExpressions) + assert.Equal(t, tt.want, got) + }) } } From 59970124efcacfd44114964d0bdc11eb7568d1b2 Mon Sep 17 00:00:00 2001 From: Igor Beliakov Date: Sun, 5 May 2024 14:38:59 +0200 Subject: [PATCH 13/17] chore: remove e2e for matchExpressions --- .../resources/default/grafana-dashboard.yaml | 4 +- .../examples/instanceSelector/00-assert.yaml | 16 -- .../instanceSelector/00-resources.yaml | 37 ---- .../examples/instanceSelector/01-assert.yaml | 6 - .../instanceSelector/01-resources.yaml | 160 ------------------ .../examples/instanceSelector/02-assert.yaml | 42 ----- .../instanceSelector/02-resources.yaml | 69 -------- .../instanceSelector/chainsaw-test.yaml | 28 --- 8 files changed, 2 insertions(+), 360 deletions(-) delete mode 100644 tests/e2e/examples/instanceSelector/00-assert.yaml delete mode 100644 tests/e2e/examples/instanceSelector/00-resources.yaml delete mode 100644 tests/e2e/examples/instanceSelector/01-assert.yaml delete mode 100644 tests/e2e/examples/instanceSelector/01-resources.yaml delete mode 100644 tests/e2e/examples/instanceSelector/02-assert.yaml delete mode 100644 tests/e2e/examples/instanceSelector/02-resources.yaml delete mode 100755 tests/e2e/examples/instanceSelector/chainsaw-test.yaml diff --git a/hack/kind/resources/default/grafana-dashboard.yaml b/hack/kind/resources/default/grafana-dashboard.yaml index 72b532452..dad634db2 100644 --- a/hack/kind/resources/default/grafana-dashboard.yaml +++ b/hack/kind/resources/default/grafana-dashboard.yaml @@ -8,11 +8,11 @@ spec: folder: my-folder-name instanceSelector: matchLabels: - dashboards: grafana + dashboards: "grafana" json: | { "id": null, - "title": "Simple Dashboard MatchLabels", + "title": "Simple Dashboard", "tags": [], "style": "dark", "timezone": "browser", diff --git a/tests/e2e/examples/instanceSelector/00-assert.yaml b/tests/e2e/examples/instanceSelector/00-assert.yaml deleted file mode 100644 index da6f55c3e..000000000 --- a/tests/e2e/examples/instanceSelector/00-assert.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: grafana.integreatly.org/v1beta1 -kind: Grafana -metadata: - name: grafana -status: - stage: complete - stageStatus: success ---- -apiVersion: grafana.integreatly.org/v1beta1 -kind: Grafana -metadata: - name: grafana-1 -status: - stage: complete - stageStatus: success ---- diff --git a/tests/e2e/examples/instanceSelector/00-resources.yaml b/tests/e2e/examples/instanceSelector/00-resources.yaml deleted file mode 100644 index b68e7b913..000000000 --- a/tests/e2e/examples/instanceSelector/00-resources.yaml +++ /dev/null @@ -1,37 +0,0 @@ ---- -apiVersion: grafana.integreatly.org/v1beta1 -kind: Grafana -metadata: - name: grafana - labels: - dashboards: "grafana" -spec: - client: - preferIngress: false - config: - log: - mode: "console" - auth: - disable_login_form: "false" - security: - admin_user: root - admin_password: secret ---- -apiVersion: grafana.integreatly.org/v1beta1 -kind: Grafana -metadata: - name: grafana-1 - labels: - dashboards: "grafana-1" -spec: - client: - preferIngress: false - config: - log: - mode: "console" - auth: - disable_login_form: "false" - security: - admin_user: root - admin_password: secret ---- diff --git a/tests/e2e/examples/instanceSelector/01-assert.yaml b/tests/e2e/examples/instanceSelector/01-assert.yaml deleted file mode 100644 index 389607321..000000000 --- a/tests/e2e/examples/instanceSelector/01-assert.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: dashboard-definition ---- - diff --git a/tests/e2e/examples/instanceSelector/01-resources.yaml b/tests/e2e/examples/instanceSelector/01-resources.yaml deleted file mode 100644 index 6132a7b96..000000000 --- a/tests/e2e/examples/instanceSelector/01-resources.yaml +++ /dev/null @@ -1,160 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: dashboard-definition -data: - matchLabels: > - { - "id": null, - "title": "MatchLabels", - "tags": [], - "style": "dark", - "timezone": "browser", - "editable": true, - "hideControls": false, - "graphTooltip": 1, - "panels": [], - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": { - "time_options": [], - "refresh_intervals": [] - }, - "templating": { - "list": [] - }, - "annotations": { - "list": [] - }, - "refresh": "5s", - "schemaVersion": 17, - "version": 0, - "links": [], - "uid": "matchLabels" - } - Exists: > - { - "id": null, - "title": "Exists", - "tags": [], - "style": "dark", - "timezone": "browser", - "editable": true, - "hideControls": false, - "graphTooltip": 1, - "panels": [], - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": { - "time_options": [], - "refresh_intervals": [] - }, - "templating": { - "list": [] - }, - "annotations": { - "list": [] - }, - "refresh": "5s", - "schemaVersion": 17, - "version": 0, - "links": [], - "uid": "exists" - } - NotExists: > - { - "id": null, - "title": "NotExists", - "tags": [], - "style": "dark", - "timezone": "browser", - "editable": true, - "hideControls": false, - "graphTooltip": 1, - "panels": [], - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": { - "time_options": [], - "refresh_intervals": [] - }, - "templating": { - "list": [] - }, - "annotations": { - "list": [] - }, - "refresh": "5s", - "schemaVersion": 17, - "version": 0, - "links": [], - "uid": "notexists" - } - NotIn: > - { - "id": null, - "title": "NotIn", - "tags": [], - "style": "dark", - "timezone": "browser", - "editable": true, - "hideControls": false, - "graphTooltip": 1, - "panels": [], - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": { - "time_options": [], - "refresh_intervals": [] - }, - "templating": { - "list": [] - }, - "annotations": { - "list": [] - }, - "refresh": "5s", - "schemaVersion": 17, - "version": 0, - "links": [], - "uid": "notin" - } - In: > - { - "id": null, - "title": "In", - "tags": [], - "style": "dark", - "timezone": "browser", - "editable": true, - "hideControls": false, - "graphTooltip": 1, - "panels": [], - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": { - "time_options": [], - "refresh_intervals": [] - }, - "templating": { - "list": [] - }, - "annotations": { - "list": [] - }, - "refresh": "5s", - "schemaVersion": 17, - "version": 0, - "links": [], - "uid": "in" - } diff --git a/tests/e2e/examples/instanceSelector/02-assert.yaml b/tests/e2e/examples/instanceSelector/02-assert.yaml deleted file mode 100644 index f170a91a4..000000000 --- a/tests/e2e/examples/instanceSelector/02-assert.yaml +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: grafana.integreatly.org/v1beta1 -kind: GrafanaDashboard -metadata: - name: grafana-match-labels ---- -apiVersion: grafana.integreatly.org/v1beta1 -kind: GrafanaDashboard -metadata: - name: grafana-exists ---- -apiVersion: grafana.integreatly.org/v1beta1 -kind: GrafanaDashboard -metadata: - name: grafana-not-exists ---- -apiVersion: grafana.integreatly.org/v1beta1 -kind: GrafanaDashboard -metadata: - name: grafana-not-in ---- -apiVersion: grafana.integreatly.org/v1beta1 -kind: GrafanaDashboard -metadata: - name: grafana-in ---- -apiVersion: grafana.integreatly.org/v1beta1 -kind: Grafana -metadata: - name: grafana -status: - (length(dashboards)): 3 - stage: complete - stageStatus: success ---- -apiVersion: grafana.integreatly.org/v1beta1 -kind: Grafana -metadata: - name: grafana-1 -status: - (length(dashboards)): 2 - stage: complete - stageStatus: success diff --git a/tests/e2e/examples/instanceSelector/02-resources.yaml b/tests/e2e/examples/instanceSelector/02-resources.yaml deleted file mode 100644 index 2be540940..000000000 --- a/tests/e2e/examples/instanceSelector/02-resources.yaml +++ /dev/null @@ -1,69 +0,0 @@ ---- -apiVersion: grafana.integreatly.org/v1beta1 -kind: GrafanaDashboard -metadata: - name: grafana-match-labels -spec: - instanceSelector: - matchLabels: - dashboards: "grafana" - configMapRef: - name: dashboard-definition - key: matchLabels ---- -apiVersion: grafana.integreatly.org/v1beta1 -kind: GrafanaDashboard -metadata: - name: grafana-exists -spec: - instanceSelector: - matchExpressions: - - key: dashboards - operator: Exists - configMapRef: - name: dashboard-definition - key: Exists ---- -apiVersion: grafana.integreatly.org/v1beta1 -kind: GrafanaDashboard -metadata: - name: grafana-not-exists -spec: - instanceSelector: - matchExpressions: - - key: dashboards - operator: DoesNotExist - configMapRef: - name: dashboard-definition - key: NotExists ---- -apiVersion: grafana.integreatly.org/v1beta1 -kind: GrafanaDashboard -metadata: - name: grafana-not-in -spec: - instanceSelector: - matchExpressions: - - key: dashboards - operator: NotIn - values: - - grafana-1 - configMapRef: - name: dashboard-definition - key: NotIn ---- -apiVersion: grafana.integreatly.org/v1beta1 -kind: GrafanaDashboard -metadata: - name: grafana-in -spec: - instanceSelector: - matchExpressions: - - key: dashboards - operator: In - values: - - grafana-1 - configMapRef: - name: dashboard-definition - key: In ---- diff --git a/tests/e2e/examples/instanceSelector/chainsaw-test.yaml b/tests/e2e/examples/instanceSelector/chainsaw-test.yaml deleted file mode 100755 index 0f75ede61..000000000 --- a/tests/e2e/examples/instanceSelector/chainsaw-test.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/kyverno/chainsaw/main/.schemas/json/test-chainsaw-v1alpha1.json -apiVersion: chainsaw.kyverno.io/v1alpha1 -kind: Test -metadata: - name: instance-selector -spec: - steps: - - name: step-00 - try: - - apply: - file: 00-resources.yaml - - assert: - file: 00-assert.yaml - - name: step-01 - try: - - apply: - file: 01-resources.yaml - - assert: - file: 01-assert.yaml - - name: step-02 - try: - - apply: - file: 02-resources.yaml - - sleep: - duration: 10s - - assert: - template: true - file: 02-assert.yaml From 4fa7dc54f497295d51238690db1ac01d05246366 Mon Sep 17 00:00:00 2001 From: Igor Beliakov Date: Sun, 5 May 2024 14:52:10 +0200 Subject: [PATCH 14/17] chore: rename labelsMatchExpressions to labelsSatisfyMatchExpressions --- controllers/controller_shared.go | 4 ++-- controllers/controller_shared_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/controllers/controller_shared.go b/controllers/controller_shared.go index efe5e9da2..6d030a341 100644 --- a/controllers/controller_shared.go +++ b/controllers/controller_shared.go @@ -39,7 +39,7 @@ func GetMatchingInstances(ctx context.Context, k8sClient client.Client, labelSel var selectedList v1beta1.GrafanaList for _, instance := range list.Items { - selected := labelsMatchExpressions(instance.Labels, labelSelector.MatchExpressions) + selected := labelsSatisfyMatchExpressions(instance.Labels, labelSelector.MatchExpressions) if selected { selectedList.Items = append(selectedList.Items, instance) } @@ -48,7 +48,7 @@ func GetMatchingInstances(ctx context.Context, k8sClient client.Client, labelSel return selectedList, err } -func labelsMatchExpressions(labels map[string]string, matchExpressions []metav1.LabelSelectorRequirement) bool { +func labelsSatisfyMatchExpressions(labels map[string]string, matchExpressions []metav1.LabelSelectorRequirement) bool { if len(labels) == 0 { return false } diff --git a/controllers/controller_shared_test.go b/controllers/controller_shared_test.go index bc617f603..da367823c 100644 --- a/controllers/controller_shared_test.go +++ b/controllers/controller_shared_test.go @@ -23,7 +23,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func TestLabelsMatchExpressions(t *testing.T) { +func TestLabelsSatisfyMatchExpressions(t *testing.T) { tests := []struct { name string labels map[string]string @@ -197,7 +197,7 @@ func TestLabelsMatchExpressions(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := labelsMatchExpressions(tt.labels, tt.matchExpressions) + got := labelsSatisfyMatchExpressions(tt.labels, tt.matchExpressions) assert.Equal(t, tt.want, got) }) } From cafe456db92e3eed5118def38ed0a31988edee4a Mon Sep 17 00:00:00 2001 From: Igor Beliakov Date: Mon, 13 May 2024 11:54:16 +0200 Subject: [PATCH 15/17] fix(labelsMatchExpressions): cover case with different labels --- controllers/controller_shared.go | 8 ++++---- controllers/controller_shared_test.go | 26 +++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/controllers/controller_shared.go b/controllers/controller_shared.go index 6d030a341..8d4cc7ca8 100644 --- a/controllers/controller_shared.go +++ b/controllers/controller_shared.go @@ -71,11 +71,11 @@ func labelsSatisfyMatchExpressions(labels map[string]string, matchExpressions [] case metav1.LabelSelectorOpNotIn: selected = !slices.Contains(matchExpression.Values, label) } + } - // All matchExpressions must evaluate to true in order to satisfy the conditions - if !selected { - return false - } + // All matchExpressions must evaluate to true in order to satisfy the conditions + if !selected { + return false } } diff --git a/controllers/controller_shared_test.go b/controllers/controller_shared_test.go index da367823c..8aa3cfd33 100644 --- a/controllers/controller_shared_test.go +++ b/controllers/controller_shared_test.go @@ -170,7 +170,7 @@ func TestLabelsSatisfyMatchExpressions(t *testing.T) { want: true, }, { - name: "Doesn't match one of expressions", + name: "Does not match one of expressions (matching labels, different value)", labels: map[string]string{ "dashboards": "grafana", "environment": "production", @@ -193,6 +193,30 @@ func TestLabelsSatisfyMatchExpressions(t *testing.T) { }, want: false, }, + { + name: "Does not match any of expressions (different labels)", + labels: map[string]string{ + "random-label": "random-value-1", + "random-label2": "random-value-2", + }, + matchExpressions: []metav1.LabelSelectorRequirement{ + { + Operator: metav1.LabelSelectorOpIn, + Key: "dashboards", + Values: []string{ + "grafana", + }, + }, + { + Operator: metav1.LabelSelectorOpIn, + Key: "environment", + Values: []string{ + "development", + }, + }, + }, + want: false, + }, } for _, tt := range tests { From cbabed9e30d41be46dfe3e503fa02c19a4714300 Mon Sep 17 00:00:00 2001 From: Igor Beliakov Date: Mon, 13 May 2024 12:03:07 +0200 Subject: [PATCH 16/17] chore(TestLabelsSatisfyMatchExpressions): improve naming for labels --- controllers/controller_shared_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/controllers/controller_shared_test.go b/controllers/controller_shared_test.go index 8aa3cfd33..2529555e5 100644 --- a/controllers/controller_shared_test.go +++ b/controllers/controller_shared_test.go @@ -196,8 +196,8 @@ func TestLabelsSatisfyMatchExpressions(t *testing.T) { { name: "Does not match any of expressions (different labels)", labels: map[string]string{ - "random-label": "random-value-1", - "random-label2": "random-value-2", + "random-label-1": "random-value-1", + "random-label-2": "random-value-2", }, matchExpressions: []metav1.LabelSelectorRequirement{ { From ea373eadcac79512148ec439e0435fb79a283185 Mon Sep 17 00:00:00 2001 From: Igor Beliakov Date: Thu, 16 May 2024 10:54:43 +0200 Subject: [PATCH 17/17] fix(labelsMatchExpressions): reintroduce support for instanceSelector: {} --- controllers/controller_shared.go | 3 ++- controllers/controller_shared_test.go | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/controllers/controller_shared.go b/controllers/controller_shared.go index 8d4cc7ca8..05081653b 100644 --- a/controllers/controller_shared.go +++ b/controllers/controller_shared.go @@ -49,8 +49,9 @@ func GetMatchingInstances(ctx context.Context, k8sClient client.Client, labelSel } func labelsSatisfyMatchExpressions(labels map[string]string, matchExpressions []metav1.LabelSelectorRequirement) bool { + // To preserve support for scenario with instanceSelector: {} if len(labels) == 0 { - return false + return true } if len(matchExpressions) == 0 { diff --git a/controllers/controller_shared_test.go b/controllers/controller_shared_test.go index 2529555e5..607972671 100644 --- a/controllers/controller_shared_test.go +++ b/controllers/controller_shared_test.go @@ -34,7 +34,7 @@ func TestLabelsSatisfyMatchExpressions(t *testing.T) { name: "No labels and no expressions", labels: map[string]string{}, matchExpressions: []metav1.LabelSelectorRequirement{}, - want: false, + want: true, }, { name: "No labels", @@ -45,7 +45,7 @@ func TestLabelsSatisfyMatchExpressions(t *testing.T) { Key: "dashboards", }, }, - want: false, + want: true, }, { name: "No matchExpressions",