diff --git a/pkg/customresourcestate/generate/markers/helper_test.go b/pkg/customresourcestate/generate/markers/helper_test.go new file mode 100644 index 0000000000..09a52df5de --- /dev/null +++ b/pkg/customresourcestate/generate/markers/helper_test.go @@ -0,0 +1,123 @@ +/* +Copyright 2023 The Kubernetes Authors All rights reserved. + +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 markers + +import ( + "reflect" + "testing" + + "k8s.io/kube-state-metrics/v2/pkg/customresourcestate" +) + +func Test_jsonPath_Parse(t *testing.T) { + tests := []struct { + name string + j jsonPath + want []string + wantErr bool + }{ + { + name: "empty input", + j: "", + want: []string{}, + wantErr: false, + }, + { + name: "dot input", + j: ".", + want: []string{""}, + wantErr: false, + }, + { + name: "some path input", + j: ".foo.bar", + want: []string{"foo", "bar"}, + wantErr: false, + }, + { + name: "invalid character ,", + j: ".foo,.bar", + wantErr: true, + }, + { + name: "invalid closure", + j: "{.foo}", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.j.Parse() + if (err != nil) != tt.wantErr { + t.Errorf("jsonPath.Parse() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("jsonPath.Parse() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_newMetricMeta(t *testing.T) { + tests := []struct { + name string + basePath []string + j jsonPath + jsonLabelsFromPath map[string]jsonPath + want customresourcestate.MetricMeta + }{ + { + name: "with basePath and jsonpath, without jsonLabelsFromPath", + basePath: []string{"foo"}, + j: jsonPath(".bar"), + jsonLabelsFromPath: map[string]jsonPath{}, + want: customresourcestate.MetricMeta{ + Path: []string{"foo", "bar"}, + LabelsFromPath: map[string][]string{}, + }, + }, + { + name: "with basePath, jsonpath and jsonLabelsFromPath", + basePath: []string{"foo"}, + j: jsonPath(".bar"), + jsonLabelsFromPath: map[string]jsonPath{"some": ".label.from.path"}, + want: customresourcestate.MetricMeta{ + Path: []string{"foo", "bar"}, + LabelsFromPath: map[string][]string{ + "some": {"label", "from", "path"}, + }, + }, + }, + { + name: "no basePath, jsonpath and jsonLabelsFromPath", + basePath: []string{}, + j: jsonPath(""), + jsonLabelsFromPath: map[string]jsonPath{}, + want: customresourcestate.MetricMeta{ + Path: []string{}, + LabelsFromPath: map[string][]string{}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := newMetricMeta(tt.basePath, tt.j, tt.jsonLabelsFromPath); !reflect.DeepEqual(got, tt.want) { + t.Errorf("newMetricMeta() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/customresourcestate/generate/markers/labelfrompath.go b/pkg/customresourcestate/generate/markers/labelfrompath.go index 76c1eaeb87..215ebb0582 100644 --- a/pkg/customresourcestate/generate/markers/labelfrompath.go +++ b/pkg/customresourcestate/generate/markers/labelfrompath.go @@ -16,6 +16,7 @@ limitations under the License. package markers import ( + "errors" "fmt" "sigs.k8s.io/controller-tools/pkg/markers" @@ -59,14 +60,19 @@ func (labelFromPathMarker) Help() *markers.DefinitionHelp { } func (n labelFromPathMarker) ApplyToResource(resource *customresourcestate.Resource) error { - if resource.LabelsFromPath == nil { - resource.LabelsFromPath = map[string][]string{} + if resource == nil { + return errors.New("expected resource to not be nil") } + jsonPathElems, err := n.JSONPath.Parse() if err != nil { return err } + if resource.LabelsFromPath == nil { + resource.LabelsFromPath = map[string][]string{} + } + if jsonPath, labelExists := resource.LabelsFromPath[n.Name]; labelExists { if len(jsonPathElems) != len(jsonPath) { return fmt.Errorf("duplicate definition for label %q", n.Name) diff --git a/pkg/customresourcestate/generate/markers/labelfrompath_test.go b/pkg/customresourcestate/generate/markers/labelfrompath_test.go new file mode 100644 index 0000000000..4736b7eb05 --- /dev/null +++ b/pkg/customresourcestate/generate/markers/labelfrompath_test.go @@ -0,0 +1,132 @@ +/* +Copyright 2023 The Kubernetes Authors All rights reserved. + +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 markers + +import ( + "reflect" + "testing" + + "k8s.io/kube-state-metrics/v2/pkg/customresourcestate" +) + +func Test_labelFromPathMarker_ApplyToResource(t *testing.T) { + type fields struct { + Name string + JSONPath jsonPath + } + tests := []struct { + name string + fields fields + resource *customresourcestate.Resource + wantResource *customresourcestate.Resource + wantErr bool + }{ + { + name: "happy path", + fields: fields{ + Name: "foo", + JSONPath: ".bar", + }, + resource: &customresourcestate.Resource{}, + wantResource: &customresourcestate.Resource{ + Labels: customresourcestate.Labels{ + LabelsFromPath: map[string][]string{ + "foo": {"bar"}, + }, + }, + }, + wantErr: false, + }, + { + name: "label already exists with same path length", + fields: fields{ + Name: "foo", + JSONPath: ".bar", + }, + resource: &customresourcestate.Resource{ + Labels: customresourcestate.Labels{ + LabelsFromPath: map[string][]string{ + "foo": {"other"}, + }, + }, + }, + wantResource: &customresourcestate.Resource{ + Labels: customresourcestate.Labels{ + LabelsFromPath: map[string][]string{ + "foo": {"other"}, + }, + }, + }, + wantErr: true, + }, + { + name: "label already exists with different path length", + fields: fields{ + Name: "foo", + JSONPath: ".bar", + }, + resource: &customresourcestate.Resource{ + Labels: customresourcestate.Labels{ + LabelsFromPath: map[string][]string{ + "foo": {"other", "path"}, + }, + }, + }, + wantResource: &customresourcestate.Resource{ + Labels: customresourcestate.Labels{ + LabelsFromPath: map[string][]string{ + "foo": {"other", "path"}, + }, + }, + }, + wantErr: true, + }, + { + name: "invalid json path", + fields: fields{ + Name: "foo", + JSONPath: "{.bar}", + }, + resource: &customresourcestate.Resource{}, + wantResource: &customresourcestate.Resource{}, + wantErr: true, + }, + { + name: "nil resource", + fields: fields{ + Name: "foo", + JSONPath: "{.bar}", + }, + resource: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := labelFromPathMarker{ + Name: tt.fields.Name, + JSONPath: tt.fields.JSONPath, + } + if err := n.ApplyToResource(tt.resource); (err != nil) != tt.wantErr { + t.Errorf("labelFromPathMarker.ApplyToResource() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(tt.resource, tt.wantResource) { + t.Errorf("labelFromPathMarker.ApplyToResource() = %v, want %v", tt.resource, tt.wantResource) + } + + }) + } +}