@@ -19,7 +19,6 @@ import (
19
19
"fmt"
20
20
"go/ast"
21
21
"go/types"
22
- "sort"
23
22
"strings"
24
23
25
24
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -35,82 +34,65 @@ import (
35
34
type parser struct {
36
35
* crd.Parser
37
36
38
- CustomResourceStates map [schema.GroupKind ]customresourcestate.Resource
39
- FlattenedMetrics map [crd.TypeIdent ][]customresourcestate.Metric
37
+ CustomResourceStates map [crd.TypeIdent ]* customresourcestate.Resource
40
38
}
41
39
42
40
func newParser (p * crd.Parser ) * parser {
43
41
return & parser {
44
42
Parser : p ,
45
- CustomResourceStates : make (map [schema.GroupKind ]customresourcestate.Resource ),
46
- FlattenedMetrics : make (map [crd.TypeIdent ][]customresourcestate.Metric ),
43
+ CustomResourceStates : make (map [crd.TypeIdent ]* customresourcestate.Resource ),
47
44
}
48
45
}
49
46
50
- func (p * parser ) NeedResourceFor (groupKind schema.GroupKind ) {
51
- if _ , exists := p .CustomResourceStates [groupKind ]; exists {
47
+ // NeedResourceFor creates the customresourcestate.Resource object for the given
48
+ // GroupKind located at the package identified by packageID.
49
+ func (p * parser ) NeedResourceFor (pkg * loader.Package , groupKind schema.GroupKind ) {
50
+ typeIdent := crd.TypeIdent {Package : pkg , Name : groupKind .Kind }
51
+ // Skip if type was already processed.
52
+ if _ , exists := p .CustomResourceStates [typeIdent ]; exists {
52
53
return
53
54
}
54
55
55
- var packages []* loader.Package
56
- for pkg , gv := range p .GroupVersions {
57
- if gv .Group != groupKind .Group {
58
- continue
59
- }
60
- packages = append (packages , pkg )
56
+ // Already mark the cacheID so the next time it enters NeedResourceFor it skips early.
57
+ p .CustomResourceStates [typeIdent ] = nil
58
+
59
+ // Build the type identifier for the custom resource.
60
+ typeInfo := p .Types [typeIdent ]
61
+ // typeInfo is nil if this GroupKind is not part of this package. In that case
62
+ // we have nothing to process.
63
+ if typeInfo == nil {
64
+ return
65
+ }
66
+
67
+ // Skip if gvk marker is not set. This marker is the opt-in for creating metrics
68
+ // for a custom resource.
69
+ if m := typeInfo .Markers .Get (markers .GVKMarkerName ); m == nil {
70
+ return
61
71
}
62
72
73
+ // Initialize the Resource object.
63
74
resource := customresourcestate.Resource {
64
75
GroupVersionKind : customresourcestate.GroupVersionKind {
65
- Group : groupKind .Group ,
66
- Kind : groupKind .Kind ,
76
+ Group : groupKind .Group ,
77
+ Kind : groupKind .Kind ,
78
+ Version : p .GroupVersions [pkg ].Version ,
67
79
},
80
+ // Create the metrics generators for the custom resource.
81
+ Metrics : p .NeedMetricsGeneratorFor (typeIdent ),
68
82
}
69
83
70
- for _ , pkg := range packages {
71
- typeIdent := crd.TypeIdent {Package : pkg , Name : groupKind .Kind }
72
- typeInfo := p .Types [typeIdent ]
73
- if typeInfo == nil {
74
- continue
75
- }
76
-
77
- // Skip if gvk marker is not set to not create configuration for CRs used in other CRs.
78
- // E.g. to not create configuration for KubeadmControlPlaneTemplate.
79
- if m := typeInfo .Markers .Get (markers .GVKMarkerName ); m == nil {
80
- continue
81
- }
82
-
83
- resource .Metrics = p .NeedMetricsGeneratorFor (typeIdent )
84
-
85
- sort .Slice (resource .Metrics , func (i , j int ) bool {
86
- return resource .Metrics [i ].Name < resource .Metrics [j ].Name
87
- })
88
-
89
- if resource .GroupVersionKind .Version != "" {
90
- klog .Fatal ("GroupVersionKind.Version is already set" , "resource" , resource )
91
- }
92
- resource .GroupVersionKind .Version = p .GroupVersions [pkg ].Version
93
- }
94
-
95
- for _ , pkg := range packages {
96
- typeIdent := crd.TypeIdent {Package : pkg , Name : groupKind .Kind }
97
- typeInfo := p .Types [typeIdent ]
98
- if typeInfo == nil {
99
- continue
100
- }
101
-
102
- for _ , markerVals := range typeInfo .Markers {
103
- for _ , val := range markerVals {
104
- if resourceMarker , isResourceMarker := val .(markers.ResourceMarker ); isResourceMarker {
105
- if err := resourceMarker .ApplyToResource (& resource ); err != nil {
106
- pkg .AddError (loader .ErrFromNode (err /* an okay guess */ , typeInfo .RawSpec ))
107
- }
84
+ // Iterate through all markers and run the ApplyToResource function of the ResourceMarkers.
85
+ for _ , markerVals := range typeInfo .Markers {
86
+ for _ , val := range markerVals {
87
+ if resourceMarker , isResourceMarker := val .(markers.ResourceMarker ); isResourceMarker {
88
+ if err := resourceMarker .ApplyToResource (& resource ); err != nil {
89
+ pkg .AddError (loader .ErrFromNode (err /* an okay guess */ , typeInfo .RawSpec ))
108
90
}
109
91
}
110
92
}
111
93
}
112
94
113
- p .CustomResourceStates [groupKind ] = resource
95
+ p .CustomResourceStates [typeIdent ] = & resource
114
96
}
115
97
116
98
type generatorRequester interface {
@@ -134,19 +116,6 @@ func newGeneratorContext(pkg *loader.Package, req generatorRequester) *generator
134
116
}
135
117
}
136
118
137
- // requestGenerator asks for the generator for a type in the package with the
138
- // given import path.
139
- func (c * generatorContext ) requestGenerator (pkgPath , typeName string ) []customresourcestate.Generator {
140
- pkg := c .pkg
141
- if pkgPath != "" {
142
- pkg = c .pkg .Imports ()[pkgPath ]
143
- }
144
- return c .generatorRequester .NeedMetricsGeneratorFor (crd.TypeIdent {
145
- Package : pkg ,
146
- Name : typeName ,
147
- })
148
- }
149
-
150
119
func generatorsFromMarkers (m ctrlmarkers.MarkerValues , basePath ... string ) []customresourcestate.Generator {
151
120
generators := []customresourcestate.Generator {}
152
121
@@ -163,18 +132,22 @@ func generatorsFromMarkers(m ctrlmarkers.MarkerValues, basePath ...string) []cus
163
132
return generators
164
133
}
165
134
135
+ // NeedMetricsGeneratorFor creates the customresourcestate.Generator object for a
136
+ // Custom Resource.
166
137
func (p * parser ) NeedMetricsGeneratorFor (typ crd.TypeIdent ) []customresourcestate.Generator {
167
- if _ , knownMetrics := p .FlattenedMetrics [typ ]; knownMetrics {
168
- return nil
169
- }
170
-
171
138
info , gotInfo := p .Types [typ ]
172
139
if ! gotInfo {
173
140
klog .Fatal ("expected to get info for %v but does not exist" , typ )
174
141
}
175
142
143
+ // Add metric generators defined by markers at the type.
176
144
generators := generatorsFromMarkers (info .Markers )
145
+
146
+ // Traverse fields of the object and process markers.
147
+ // Note: Partially inspired by controller-tools.
148
+ // xref: https://github.com/kubernetes-sigs/controller-tools/blob/d89d6ae3df218a85f7cd9e477157cace704b37d1/pkg/crd/schema.go#L350
177
149
for _ , f := range info .Fields {
150
+ // Only fields with the `json:"..."` tag are relevant. Others are not part of the Custom Resource.
178
151
jsonTag , hasTag := f .Tag .Lookup ("json" )
179
152
if ! hasTag {
180
153
// if the field doesn't have a JSON tag, it doesn't belong in output (and shouldn't exist in a serialized type)
@@ -186,30 +159,22 @@ func (p *parser) NeedMetricsGeneratorFor(typ crd.TypeIdent) []customresourcestat
186
159
continue
187
160
}
188
161
162
+ // Add metric generators defined by markers at the field.
189
163
generators = append (generators , generatorsFromMarkers (f .Markers , jsonOpts [0 ])... )
190
164
165
+ // Create new generator context and recursively process the fields.
191
166
generatorCtx := newGeneratorContext (typ .Package , p )
192
167
for _ , generator := range generatorsFor (generatorCtx , f .RawField .Type ) {
193
- generators = append (generators , prependPathOnGenerator (generator , jsonOpts [0 ]))
168
+ generators = append (generators , addPathPrefixOnGenerator (generator , jsonOpts [0 ]))
194
169
}
195
170
}
196
171
197
172
return generators
198
173
}
199
174
200
- func prependPathOnGenerator (generator customresourcestate.Generator , pathPrefix string ) customresourcestate.Generator {
201
- switch generator .Each .Type {
202
- case customresourcestate .MetricTypeGauge :
203
- generator .Each .Gauge .MetricMeta .Path = append ([]string {pathPrefix }, generator .Each .Gauge .MetricMeta .Path ... )
204
- case customresourcestate .MetricTypeStateSet :
205
- generator .Each .StateSet .MetricMeta .Path = append ([]string {pathPrefix }, generator .Each .StateSet .MetricMeta .Path ... )
206
- case customresourcestate .MetricTypeInfo :
207
- generator .Each .Info .MetricMeta .Path = append ([]string {pathPrefix }, generator .Each .Info .MetricMeta .Path ... )
208
- }
209
-
210
- return generator
211
- }
212
-
175
+ // generatorsFor creates generators for the given AST type.
176
+ // Note: Partially inspired by controller-tools.
177
+ // xref: https://github.com/kubernetes-sigs/controller-tools/blob/d89d6ae3df218a85f7cd9e477157cace704b37d1/pkg/crd/schema.go#L167-L193
213
178
func generatorsFor (ctx * generatorContext , rawType ast.Expr ) []customresourcestate.Generator {
214
179
switch expr := rawType .(type ) {
215
180
case * ast.Ident :
@@ -236,6 +201,8 @@ func generatorsFor(ctx *generatorContext, rawType ast.Expr) []customresourcestat
236
201
return nil
237
202
}
238
203
204
+ // localNamedToGenerators recurses back to NeedMetricsGeneratorFor for the type to
205
+ // get generators defined at the objects in a custom resource.
239
206
func localNamedToGenerators (ctx * generatorContext , ident * ast.Ident ) []customresourcestate.Generator {
240
207
typeInfo := ctx .pkg .TypesInfo .TypeOf (ident )
241
208
if typeInfo == types .Typ [types .Invalid ] {
@@ -260,3 +227,30 @@ func localNamedToGenerators(ctx *generatorContext, ident *ast.Ident) []customres
260
227
}
261
228
return ctx .requestGenerator (pkgPath , typeNameInfo .Name ())
262
229
}
230
+
231
+ // requestGenerator asks for the generator for a type in the package with the
232
+ // given import path.
233
+ func (c * generatorContext ) requestGenerator (pkgPath , typeName string ) []customresourcestate.Generator {
234
+ pkg := c .pkg
235
+ if pkgPath != "" {
236
+ pkg = c .pkg .Imports ()[pkgPath ]
237
+ }
238
+ return c .generatorRequester .NeedMetricsGeneratorFor (crd.TypeIdent {
239
+ Package : pkg ,
240
+ Name : typeName ,
241
+ })
242
+ }
243
+
244
+ // addPathPrefixOnGenerator prefixes the path set at the generators MetricMeta object.
245
+ func addPathPrefixOnGenerator (generator customresourcestate.Generator , pathPrefix string ) customresourcestate.Generator {
246
+ switch generator .Each .Type {
247
+ case customresourcestate .MetricTypeGauge :
248
+ generator .Each .Gauge .MetricMeta .Path = append ([]string {pathPrefix }, generator .Each .Gauge .MetricMeta .Path ... )
249
+ case customresourcestate .MetricTypeStateSet :
250
+ generator .Each .StateSet .MetricMeta .Path = append ([]string {pathPrefix }, generator .Each .StateSet .MetricMeta .Path ... )
251
+ case customresourcestate .MetricTypeInfo :
252
+ generator .Each .Info .MetricMeta .Path = append ([]string {pathPrefix }, generator .Each .Info .MetricMeta .Path ... )
253
+ }
254
+
255
+ return generator
256
+ }
0 commit comments