diff --git a/pkg/models/resources/v1beta1/interface.go b/pkg/models/resources/v1beta1/interface.go index e4ba8283b..a7ea1e223 100644 --- a/pkg/models/resources/v1beta1/interface.go +++ b/pkg/models/resources/v1beta1/interface.go @@ -7,19 +7,20 @@ package v1beta1 import ( + "bytes" "context" "encoding/json" "fmt" "sort" "strings" - "github.com/oliveagle/jsonpath" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/selection" + "k8s.io/client-go/util/jsonpath" "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/client" @@ -244,7 +245,7 @@ func contains(object runtime.Object, queryValue query.Value) bool { key := requirement.Field value := requirement.Value - var input map[string]interface{} + var input interface{} data, err := json.Marshal(object) if err != nil { klog.V(4).Infof("failed marshal to JSON string: %s", err) @@ -254,11 +255,20 @@ func contains(object runtime.Object, queryValue query.Value) bool { klog.V(4).Infof("failed unmarshal to map object: %s", err) return false } - rawValue, err := jsonpath.JsonPathLookup(input, "$."+key) - if err != nil { - klog.V(4).Infof("failed to lookup jsonpath: %s", err) + + buf := new(bytes.Buffer) + lookup := jsonpath.New("") + lookup.AllowMissingKeys(true) + if err = lookup.Parse(fmt.Sprintf("{.%s}", key)); err != nil { + klog.V(4).Infof("failed parse jsonpath: %s", err) return false } + if err = lookup.Execute(buf, input); err != nil { + klog.V(4).Infof("failed execute jsonpath: %s", err) + return false + } + + rawValue := buf.String() // Values prefixed with ~ support case insensitivity. (e.g., a=~b, can hit b, B) if strings.HasPrefix(value, "~") { diff --git a/pkg/models/resources/v1beta1/resourcemanager_test.go b/pkg/models/resources/v1beta1/resourcemanager_test.go new file mode 100644 index 000000000..d8466c343 --- /dev/null +++ b/pkg/models/resources/v1beta1/resourcemanager_test.go @@ -0,0 +1,75 @@ +package v1beta1 + +import ( + "testing" + + "k8s.io/apimachinery/pkg/runtime" + + "kubesphere.io/kubesphere/pkg/apiserver/query" +) + +func TestContains_MatchesFieldSelector(t *testing.T) { + object := &runtime.Unknown{ + Raw: []byte(`{"metadata":{"name":"test-object","namespace":"default"}}`), + } + queryValue := query.Value("metadata.name=test-object") + + if !contains(object, queryValue) { + t.Errorf("Expected object to match field selector, but it did not") + } +} + +func TestContains_DoesNotMatchFieldSelector(t *testing.T) { + object := &runtime.Unknown{ + Raw: []byte(`{"metadata":{"name":"test-object","namespace":"default"}}`), + } + queryValue := query.Value("metadata.name=nonexistent-object") + + if contains(object, queryValue) { + t.Errorf("Expected object not to match field selector, but it did") + } +} + +func TestContains_InvalidFieldSelector(t *testing.T) { + object := &runtime.Unknown{ + Raw: []byte(`{"metadata":{"name":"test-object","namespace":"default"}}`), + } + queryValue := query.Value("invalid-selector") + + if contains(object, queryValue) { + t.Errorf("Expected object not to match invalid field selector, but it did") + } +} + +func TestContains_CaseInsensitiveMatch(t *testing.T) { + object := &runtime.Unknown{ + Raw: []byte(`{"metadata":{"name":"Test-Object","namespace":"default"}}`), + } + queryValue := query.Value("metadata.name=~test-object") + + if !contains(object, queryValue) { + t.Errorf("Expected object to match field selector case-insensitively, but it did not") + } +} + +func TestContains_EmptyObject(t *testing.T) { + object := &runtime.Unknown{ + Raw: []byte(`{}`), + } + queryValue := query.Value("metadata.name=test-object") + + if contains(object, queryValue) { + t.Errorf("Expected empty object not to match field selector, but it did") + } +} + +func TestContains_NestedDataWithSpecialCharacters(t *testing.T) { + object := &runtime.Unknown{ + Raw: []byte(`{"metadata":{"name":"test-object","annotations":{"example.com/special-key":"special-value"}}}`), + } + queryValue := query.Value(`metadata.annotations.example\.com/special-key=special-value`) + + if !contains(object, queryValue) { + t.Errorf("Expected object to match field selector with nested data and special characters, but it did not") + } +}