Skip to content

Commit

Permalink
Merge pull request #1497 from Ronan-WeScale/fix-1401
Browse files Browse the repository at this point in the history
  • Loading branch information
weisdd authored May 16, 2024
2 parents d11f814 + c018681 commit ffaaf5b
Show file tree
Hide file tree
Showing 2 changed files with 275 additions and 2 deletions.
49 changes: 47 additions & 2 deletions controllers/controller_shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"context"
"encoding/json"
"slices"
"time"

"github.com/grafana/grafana-operator/v5/api/v1beta1"
Expand Down Expand Up @@ -33,9 +34,53 @@ 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 := labelsSatisfyMatchExpressions(instance.Labels, labelSelector.MatchExpressions)
if selected {
selectedList.Items = append(selectedList.Items, instance)
}
}

return selectedList, err
}

func labelsSatisfyMatchExpressions(labels map[string]string, matchExpressions []metav1.LabelSelectorRequirement) bool {
// To preserve support for scenario with instanceSelector: {}
if len(labels) == 0 {
return true
}

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
case metav1.LabelSelectorOpExists:
selected = true
case metav1.LabelSelectorOpIn:
selected = slices.Contains(matchExpression.Values, label)
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
}
}

return true
}

func ReconcilePlugins(ctx context.Context, k8sClient client.Client, scheme *runtime.Scheme, grafana *v1beta1.Grafana, plugins v1beta1.PluginList, resource string) error {
Expand Down
228 changes: 228 additions & 0 deletions controllers/controller_shared_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
/*
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/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestLabelsSatisfyMatchExpressions(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: true,
},
{
name: "No labels",
labels: map[string]string{},
matchExpressions: []metav1.LabelSelectorRequirement{
{
Operator: metav1.LabelSelectorOpExists,
Key: "dashboards",
},
},
want: true,
},
{
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",
},
},
want: false,
},
{
name: "Matches Exists",
labels: map[string]string{
"dashboards": "grafana",
},
matchExpressions: []metav1.LabelSelectorRequirement{
{
Operator: metav1.LabelSelectorOpExists,
Key: "dashboards",
},
},
want: true,
},
{
name: "Matches In",
labels: map[string]string{
"dashboards": "grafana",
},
matchExpressions: []metav1.LabelSelectorRequirement{
{
Operator: metav1.LabelSelectorOpIn,
Key: "dashboards",
Values: []string{
"grafana",
},
},
},
want: true,
},
{
name: "Matches NotIn",
labels: map[string]string{
"dashboards": "grafana",
},
matchExpressions: []metav1.LabelSelectorRequirement{
{
Operator: metav1.LabelSelectorOpNotIn,
Key: "dashboards",
Values: []string{
"grafana",
},
},
},
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",
},
},
},
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,
},
{
name: "Matches multiple expressions",
labels: map[string]string{
"dashboards": "grafana",
"environment": "production",
},
matchExpressions: []metav1.LabelSelectorRequirement{
{
Operator: metav1.LabelSelectorOpIn,
Key: "dashboards",
Values: []string{
"grafana",
},
},
{
Operator: metav1.LabelSelectorOpIn,
Key: "environment",
Values: []string{
"production",
},
},
},
want: true,
},
{
name: "Does not match one of expressions (matching labels, different value)",
labels: map[string]string{
"dashboards": "grafana",
"environment": "production",
},
matchExpressions: []metav1.LabelSelectorRequirement{
{
Operator: metav1.LabelSelectorOpIn,
Key: "dashboards",
Values: []string{
"grafana",
},
},
{
Operator: metav1.LabelSelectorOpIn,
Key: "environment",
Values: []string{
"development",
},
},
},
want: false,
},
{
name: "Does not match any of expressions (different labels)",
labels: map[string]string{
"random-label-1": "random-value-1",
"random-label-2": "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 {
t.Run(tt.name, func(t *testing.T) {
got := labelsSatisfyMatchExpressions(tt.labels, tt.matchExpressions)
assert.Equal(t, tt.want, got)
})
}
}

0 comments on commit ffaaf5b

Please sign in to comment.