Skip to content

Commit 582458c

Browse files
committed
Implement unit and integration tests
1 parent 8b40296 commit 582458c

File tree

9 files changed

+689
-2
lines changed

9 files changed

+689
-2
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package generator
17+
18+
import (
19+
"bytes"
20+
"io"
21+
"os"
22+
"path"
23+
"testing"
24+
25+
"sigs.k8s.io/controller-tools/pkg/genall"
26+
"sigs.k8s.io/controller-tools/pkg/loader"
27+
"sigs.k8s.io/controller-tools/pkg/markers"
28+
)
29+
30+
func Test_Generate(t *testing.T) {
31+
cwd, err := os.Getwd()
32+
if err != nil {
33+
t.Error(err)
34+
}
35+
36+
optionsRegistry := &markers.Registry{}
37+
38+
metricGenerator := CustomResourceConfigGenerator{}
39+
if err := metricGenerator.RegisterMarkers(optionsRegistry); err != nil {
40+
t.Error(err)
41+
}
42+
43+
out := &outputRule{
44+
buf: &bytes.Buffer{},
45+
}
46+
47+
// Load the passed packages as roots.
48+
roots, err := loader.LoadRoots(path.Join(cwd, "testdata", "..."))
49+
if err != nil {
50+
t.Errorf("loading packages %v", err)
51+
}
52+
53+
gen := CustomResourceConfigGenerator{}
54+
55+
generationContext := &genall.GenerationContext{
56+
Collector: &markers.Collector{Registry: optionsRegistry},
57+
Roots: roots,
58+
Checker: &loader.TypeChecker{},
59+
OutputRule: out,
60+
}
61+
62+
t.Log("Trying to generate a custom resource configuration from the loaded packages")
63+
64+
if err := gen.Generate(generationContext); err != nil {
65+
t.Error(err)
66+
}
67+
output := out.buf.String()
68+
69+
t.Log("Comparing output to testdata to check for regressions")
70+
71+
expectedFile, err := os.ReadFile(path.Clean(path.Join(cwd, "testdata", "foo-config.yaml")))
72+
if err != nil {
73+
t.Error(err)
74+
}
75+
76+
if string(expectedFile) != output {
77+
t.Log("output:")
78+
t.Log(output)
79+
t.Error("Expected output to match file testdata/foo-config.yaml")
80+
}
81+
}
82+
83+
type outputRule struct {
84+
buf *bytes.Buffer
85+
}
86+
87+
func (o *outputRule) Open(_ *loader.Package, _ string) (io.WriteCloser, error) {
88+
return nopCloser{o.buf}, nil
89+
}
90+
91+
type nopCloser struct {
92+
io.Writer
93+
}
94+
95+
func (n nopCloser) Close() error {
96+
return nil
97+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
---
2+
kind: CustomResourceStateMetrics
3+
spec:
4+
resources:
5+
- errorLogV: 0
6+
groupVersionKind:
7+
group: bar.example.com
8+
kind: Foo
9+
version: foo
10+
labelsFromPath:
11+
cluster_name:
12+
- metadata
13+
- labels
14+
- cluster.x-k8s.io/cluster-name
15+
name:
16+
- metadata
17+
- name
18+
metricNamePrefix: foo
19+
metrics:
20+
- each:
21+
gauge:
22+
labelFromKey: ""
23+
nilIsZero: false
24+
path:
25+
- metadata
26+
- creationTimestamp
27+
valueFrom: null
28+
type: Gauge
29+
help: Unix creation timestamp.
30+
name: created
31+
- each:
32+
info:
33+
labelFromKey: ""
34+
labelsFromPath:
35+
owner_is_controller:
36+
- controller
37+
owner_kind:
38+
- kind
39+
owner_name:
40+
- name
41+
owner_uid:
42+
- uid
43+
path:
44+
- metadata
45+
- ownerReferences
46+
type: Info
47+
help: Owner references.
48+
name: owner
49+
- each:
50+
stateSet:
51+
labelName: status
52+
labelsFromPath:
53+
type:
54+
- type
55+
list:
56+
- "True"
57+
- "False"
58+
- Unknown
59+
path:
60+
- status
61+
- conditions
62+
valueFrom:
63+
- status
64+
type: StateSet
65+
help: The condition of a foo.
66+
name: status_condition
67+
- each:
68+
gauge:
69+
labelFromKey: ""
70+
labelsFromPath:
71+
status:
72+
- status
73+
type:
74+
- type
75+
nilIsZero: false
76+
path:
77+
- status
78+
- conditions
79+
valueFrom:
80+
- lastTransitionTime
81+
type: Gauge
82+
help: The condition last transition time of a foo.
83+
name: status_condition_last_transition_time
84+
resourcePlural: ""
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
//go:generate sh -c "go run ../../../../../ generate ./... > foo-config.yaml"
18+
19+
// +groupName=bar.example.com
20+
package foo
21+
22+
import (
23+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+
)
25+
26+
// FooSpec is the spec of Foo.
27+
type FooSpec struct {
28+
// This tests that defaulted fields are stripped for v1beta1,
29+
// but not for v1
30+
DefaultedString string `json:"defaultedString"`
31+
}
32+
33+
// FooStatus is the status of Foo.
34+
type FooStatus struct {
35+
// +Metrics:stateset:name="status_condition",help="The condition of a foo.",labelName="status",JSONPath=".status",list={"True","False","Unknown"},labelsFromPath={"type":".type"}
36+
// +Metrics:gauge:name="status_condition_last_transition_time",help="The condition last transition time of a foo.",valueFrom=.lastTransitionTime,labelsFromPath={"type":".type","status":".status"}
37+
Conditions Condition `json:"conditions,omitempty"`
38+
}
39+
40+
// Foo is a test object.
41+
// +Metrics:gvk:namePrefix="foo"
42+
// +Metrics:labelFromPath:name="name",JSONPath=".metadata.name"
43+
// +Metrics:gauge:name="created",JSONPath=".metadata.creationTimestamp",help="Unix creation timestamp."
44+
// +Metrics:info:name="owner",JSONPath=".metadata.ownerReferences",help="Owner references.",labelsFromPath={owner_is_controller:".controller",owner_kind:".kind",owner_name:".name",owner_uid:".uid"}
45+
// +Metrics:labelFromPath:name="cluster_name",JSONPath=.metadata.labels.cluster\.x-k8s\.io/cluster-name
46+
type Foo struct {
47+
// TypeMeta comments should NOT appear in the CRD spec
48+
metav1.TypeMeta `json:",inline"`
49+
// ObjectMeta comments should NOT appear in the CRD spec
50+
metav1.ObjectMeta `json:"metadata,omitempty"`
51+
52+
// Spec comments SHOULD appear in the CRD spec
53+
Spec FooSpec `json:"spec,omitempty"`
54+
// Status comments SHOULD appear in the CRD spec
55+
Status FooStatus `json:"status,omitempty"`
56+
}
57+
58+
// Condition is a test condition.
59+
type Condition struct {
60+
// Type of condition.
61+
Type string `json:"type"`
62+
// Status of condition.
63+
Status string `json:"status"`
64+
// LastTransitionTime of condition.
65+
LastTransitionTime metav1.Time `json:"lastTransitionTime"`
66+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package markers
17+
18+
import (
19+
"reflect"
20+
"testing"
21+
22+
"k8s.io/kube-state-metrics/v2/pkg/customresourcestate"
23+
)
24+
25+
func Test_jsonPath_Parse(t *testing.T) {
26+
tests := []struct {
27+
name string
28+
j jsonPath
29+
want []string
30+
wantErr bool
31+
}{
32+
{
33+
name: "empty input",
34+
j: "",
35+
want: []string{},
36+
wantErr: false,
37+
},
38+
{
39+
name: "dot input",
40+
j: ".",
41+
want: []string{""},
42+
wantErr: false,
43+
},
44+
{
45+
name: "some path input",
46+
j: ".foo.bar",
47+
want: []string{"foo", "bar"},
48+
wantErr: false,
49+
},
50+
{
51+
name: "invalid character ,",
52+
j: ".foo,.bar",
53+
wantErr: true,
54+
},
55+
{
56+
name: "invalid closure",
57+
j: "{.foo}",
58+
wantErr: true,
59+
},
60+
}
61+
for _, tt := range tests {
62+
t.Run(tt.name, func(t *testing.T) {
63+
got, err := tt.j.Parse()
64+
if (err != nil) != tt.wantErr {
65+
t.Errorf("jsonPath.Parse() error = %v, wantErr %v", err, tt.wantErr)
66+
return
67+
}
68+
if !reflect.DeepEqual(got, tt.want) {
69+
t.Errorf("jsonPath.Parse() = %v, want %v", got, tt.want)
70+
}
71+
})
72+
}
73+
}
74+
75+
func Test_newMetricMeta(t *testing.T) {
76+
tests := []struct {
77+
name string
78+
basePath []string
79+
j jsonPath
80+
jsonLabelsFromPath map[string]jsonPath
81+
want customresourcestate.MetricMeta
82+
}{
83+
{
84+
name: "with basePath and jsonpath, without jsonLabelsFromPath",
85+
basePath: []string{"foo"},
86+
j: jsonPath(".bar"),
87+
jsonLabelsFromPath: map[string]jsonPath{},
88+
want: customresourcestate.MetricMeta{
89+
Path: []string{"foo", "bar"},
90+
LabelsFromPath: map[string][]string{},
91+
},
92+
},
93+
{
94+
name: "with basePath, jsonpath and jsonLabelsFromPath",
95+
basePath: []string{"foo"},
96+
j: jsonPath(".bar"),
97+
jsonLabelsFromPath: map[string]jsonPath{"some": ".label.from.path"},
98+
want: customresourcestate.MetricMeta{
99+
Path: []string{"foo", "bar"},
100+
LabelsFromPath: map[string][]string{
101+
"some": {"label", "from", "path"},
102+
},
103+
},
104+
},
105+
{
106+
name: "no basePath, jsonpath and jsonLabelsFromPath",
107+
basePath: []string{},
108+
j: jsonPath(""),
109+
jsonLabelsFromPath: map[string]jsonPath{},
110+
want: customresourcestate.MetricMeta{
111+
Path: []string{},
112+
LabelsFromPath: map[string][]string{},
113+
},
114+
},
115+
}
116+
for _, tt := range tests {
117+
t.Run(tt.name, func(t *testing.T) {
118+
if got := newMetricMeta(tt.basePath, tt.j, tt.jsonLabelsFromPath); !reflect.DeepEqual(got, tt.want) {
119+
t.Errorf("newMetricMeta() = %v, want %v", got, tt.want)
120+
}
121+
})
122+
}
123+
}

0 commit comments

Comments
 (0)