Skip to content

Commit d6f5e8a

Browse files
authored
Merge pull request #75 from JoelSpeed/skip-items-structs
Skip reporting issues on Items style structs
2 parents 09cd5f8 + df3bb60 commit d6f5e8a

File tree

4 files changed

+302
-1
lines changed

4 files changed

+302
-1
lines changed

pkg/analysis/helpers/inspector/inspector.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,18 @@ func (i *inspector) InspectFields(inspectField func(field *ast.Field, stack []as
8080
return false
8181
}
8282

83-
_, ok = stack[len(stack)-3].(*ast.StructType)
83+
structType, ok := stack[len(stack)-3].(*ast.StructType)
8484
if !ok {
8585
// A field within a struct has a FieldList parent and then a StructType parent.
8686
// If we don't have a StructType parent, then we're not in a struct.
8787
return false
8888
}
8989

90+
if isItemsType(structType) {
91+
// The field belongs to an items type, we don't need to report lint errors for this.
92+
return false
93+
}
94+
9095
field, ok := n.(*ast.Field)
9196
if !ok {
9297
return true
@@ -119,3 +124,31 @@ func (i *inspector) InspectTypeSpec(inspectTypeSpec func(typeSpec *ast.TypeSpec,
119124
inspectTypeSpec(typeSpec, i.markers)
120125
})
121126
}
127+
128+
func isItemsType(structType *ast.StructType) bool {
129+
// An items type is a struct with TypeMeta, ListMeta and Items fields.
130+
if len(structType.Fields.List) != 3 {
131+
return false
132+
}
133+
134+
// Check if the first field is TypeMeta.
135+
// This should be a selector (e.g. metav1.TypeMeta)
136+
// Check the TypeMeta part as the package name may vary.
137+
if typeMeta, ok := structType.Fields.List[0].Type.(*ast.SelectorExpr); !ok || typeMeta.Sel.Name != "TypeMeta" {
138+
return false
139+
}
140+
141+
// Check if the second field is ListMeta.
142+
if listMeta, ok := structType.Fields.List[1].Type.(*ast.SelectorExpr); !ok || listMeta.Sel.Name != "ListMeta" {
143+
return false
144+
}
145+
146+
// Check if the third field is Items.
147+
// It should be an array, and be called Items.
148+
itemsField := structType.Fields.List[2]
149+
if _, ok := itemsField.Type.(*ast.ArrayType); !ok || len(itemsField.Names) == 0 || itemsField.Names[0].Name != "Items" {
150+
return false
151+
}
152+
153+
return true
154+
}

pkg/analysis/helpers/inspector/testdata/src/a/a.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package A
22

3+
import (
4+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
5+
)
6+
37
var (
48
String string
59
)
@@ -57,3 +61,34 @@ var Var = struct {
5761
}{
5862
Field: "field",
5963
}
64+
65+
// AItems is a list of A.
66+
// This represents the Items types in Kubernetes APIs.
67+
// We don't need to lint this type as it doesn't affect the API behaviour in general.
68+
type AItems struct {
69+
metav1.TypeMeta `json:",inline"`
70+
metav1.ListMeta `json:"metadata,omitempty"`
71+
72+
Items []A `json:"items"`
73+
}
74+
75+
type NotItems struct {
76+
metav1.TypeMeta `json:",inline"` // want "field: "
77+
metav1.ListMeta `json:"metadata,omitempty"` // want "field: " "json tag: metadata"
78+
79+
Items A `json:"items"` // want "field: Items" "json tag: items"
80+
}
81+
82+
type NotItemsWrongMetadata struct {
83+
metav1.TypeMeta `json:",inline"` // want "field: "
84+
metav1.ObjectMeta `json:"metadata,omitempty"` // want "field: " "json tag: metadata"
85+
86+
Items []A `json:"items"` // want "field: Items" "json tag: items"
87+
}
88+
89+
type NotItemsWrongTypeMeta struct {
90+
metav1.ObjectMeta `json:",inline"` // want "field: "
91+
metav1.ListMeta `json:"metadata,omitempty"` // want "field: " "json tag: metadata"
92+
93+
Items []A `json:"items"` // want "field: Items" "json tag: items"
94+
}
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
/*
2+
This is a copy of the minimum amount of the original file to be able to test the inspector linter.
3+
*/
4+
package v1
5+
6+
import (
7+
"time"
8+
9+
"k8s.io/apimachinery/pkg/types"
10+
)
11+
12+
// Time is a wrapper around time.Time which supports correct
13+
// marshaling to YAML and JSON. Wrappers are provided for many
14+
// of the factory methods that the time package offers.
15+
//
16+
// +protobuf.options.marshal=false
17+
// +protobuf.as=Timestamp
18+
// +protobuf.options.(gogoproto.goproto_stringer)=false
19+
type Time struct {
20+
time.Time `protobuf:"-"`
21+
}
22+
23+
// TypeMeta describes an individual object in an API response or request
24+
// with strings representing the type of the object and its API schema version.
25+
// Structures that are versioned or persisted should inline TypeMeta.
26+
//
27+
// +k8s:deepcopy-gen=false
28+
type TypeMeta struct {
29+
// Kind is a string value representing the REST resource this object represents.
30+
// Servers may infer this from the endpoint the client submits requests to.
31+
// Cannot be updated.
32+
// In CamelCase.
33+
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
34+
// +optional
35+
Kind string `json:"kind,omitempty" protobuf:"bytes,1,opt,name=kind"`
36+
37+
// APIVersion defines the versioned schema of this representation of an object.
38+
// Servers should convert recognized schemas to the latest internal value, and
39+
// may reject unrecognized values.
40+
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
41+
// +optional
42+
APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,2,opt,name=apiVersion"`
43+
}
44+
45+
// ListMeta describes metadata that synthetic resources must have, including lists and
46+
// various status objects. A resource may have only one of {ObjectMeta, ListMeta}.
47+
type ListMeta struct {
48+
// Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.
49+
// +optional
50+
SelfLink string `json:"selfLink,omitempty" protobuf:"bytes,1,opt,name=selfLink"`
51+
52+
// String that identifies the server's internal version of this object that
53+
// can be used by clients to determine when objects have changed.
54+
// Value must be treated as opaque by clients and passed unmodified back to the server.
55+
// Populated by the system.
56+
// Read-only.
57+
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
58+
// +optional
59+
ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,2,opt,name=resourceVersion"`
60+
61+
// continue may be set if the user set a limit on the number of items returned, and indicates that
62+
// the server has more data available. The value is opaque and may be used to issue another request
63+
// to the endpoint that served this list to retrieve the next set of available objects. Continuing a
64+
// consistent list may not be possible if the server configuration has changed or more than a few
65+
// minutes have passed. The resourceVersion field returned when using this continue value will be
66+
// identical to the value in the first response, unless you have received this token from an error
67+
// message.
68+
Continue string `json:"continue,omitempty" protobuf:"bytes,3,opt,name=continue"`
69+
70+
// remainingItemCount is the number of subsequent items in the list which are not included in this
71+
// list response. If the list request contained label or field selectors, then the number of
72+
// remaining items is unknown and the field will be left unset and omitted during serialization.
73+
// If the list is complete (either because it is not chunking or because this is the last chunk),
74+
// then there are no more remaining items and this field will be left unset and omitted during
75+
// serialization.
76+
// Servers older than v1.15 do not set this field.
77+
// The intended use of the remainingItemCount is *estimating* the size of a collection. Clients
78+
// should not rely on the remainingItemCount to be set or to be exact.
79+
// +optional
80+
RemainingItemCount *int64 `json:"remainingItemCount,omitempty" protobuf:"bytes,4,opt,name=remainingItemCount"`
81+
}
82+
83+
// ObjectMeta is metadata that all persisted resources must have, which includes all objects
84+
// users must create.
85+
// NB some fields have been removed here.
86+
type ObjectMeta struct {
87+
// Name must be unique within a namespace. Is required when creating resources, although
88+
// some resources may allow a client to request the generation of an appropriate name
89+
// automatically. Name is primarily intended for creation idempotence and configuration
90+
// definition.
91+
// Cannot be updated.
92+
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names
93+
// +optional
94+
Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"`
95+
96+
// GenerateName is an optional prefix, used by the server, to generate a unique
97+
// name ONLY IF the Name field has not been provided.
98+
// If this field is used, the name returned to the client will be different
99+
// than the name passed. This value will also be combined with a unique suffix.
100+
// The provided value has the same validation rules as the Name field,
101+
// and may be truncated by the length of the suffix required to make the value
102+
// unique on the server.
103+
//
104+
// If this field is specified and the generated name exists, the server will return a 409.
105+
//
106+
// Applied only if Name is not specified.
107+
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency
108+
// +optional
109+
GenerateName string `json:"generateName,omitempty" protobuf:"bytes,2,opt,name=generateName"`
110+
111+
// Namespace defines the space within which each name must be unique. An empty namespace is
112+
// equivalent to the "default" namespace, but "default" is the canonical representation.
113+
// Not all objects are required to be scoped to a namespace - the value of this field for
114+
// those objects will be empty.
115+
//
116+
// Must be a DNS_LABEL.
117+
// Cannot be updated.
118+
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces
119+
// +optional
120+
Namespace string `json:"namespace,omitempty" protobuf:"bytes,3,opt,name=namespace"`
121+
122+
// Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.
123+
// +optional
124+
SelfLink string `json:"selfLink,omitempty" protobuf:"bytes,4,opt,name=selfLink"`
125+
126+
// UID is the unique in time and space value for this object. It is typically generated by
127+
// the server on successful creation of a resource and is not allowed to change on PUT
128+
// operations.
129+
//
130+
// Populated by the system.
131+
// Read-only.
132+
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids
133+
// +optional
134+
UID types.UID `json:"uid,omitempty" protobuf:"bytes,5,opt,name=uid,casttype=k8s.io/kubernetes/pkg/types.UID"`
135+
136+
// An opaque value that represents the internal version of this object that can
137+
// be used by clients to determine when objects have changed. May be used for optimistic
138+
// concurrency, change detection, and the watch operation on a resource or set of resources.
139+
// Clients must treat these values as opaque and passed unmodified back to the server.
140+
// They may only be valid for a particular resource or set of resources.
141+
//
142+
// Populated by the system.
143+
// Read-only.
144+
// Value must be treated as opaque by clients and .
145+
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
146+
// +optional
147+
ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,6,opt,name=resourceVersion"`
148+
149+
// A sequence number representing a specific generation of the desired state.
150+
// Populated by the system. Read-only.
151+
// +optional
152+
Generation int64 `json:"generation,omitempty" protobuf:"varint,7,opt,name=generation"`
153+
154+
// CreationTimestamp is a timestamp representing the server time when this object was
155+
// created. It is not guaranteed to be set in happens-before order across separate operations.
156+
// Clients may not set this value. It is represented in RFC3339 form and is in UTC.
157+
//
158+
// Populated by the system.
159+
// Read-only.
160+
// Null for lists.
161+
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
162+
// +optional
163+
CreationTimestamp Time `json:"creationTimestamp,omitempty" protobuf:"bytes,8,opt,name=creationTimestamp"`
164+
165+
// DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This
166+
// field is set by the server when a graceful deletion is requested by the user, and is not
167+
// directly settable by a client. The resource is expected to be deleted (no longer visible
168+
// from resource lists, and not reachable by name) after the time in this field, once the
169+
// finalizers list is empty. As long as the finalizers list contains items, deletion is blocked.
170+
// Once the deletionTimestamp is set, this value may not be unset or be set further into the
171+
// future, although it may be shortened or the resource may be deleted prior to this time.
172+
// For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react
173+
// by sending a graceful termination signal to the containers in the pod. After that 30 seconds,
174+
// the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup,
175+
// remove the pod from the API. In the presence of network partitions, this object may still
176+
// exist after this timestamp, until an administrator or automated process can determine the
177+
// resource is fully terminated.
178+
// If not set, graceful deletion of the object has not been requested.
179+
//
180+
// Populated by the system when a graceful deletion is requested.
181+
// Read-only.
182+
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
183+
// +optional
184+
DeletionTimestamp *Time `json:"deletionTimestamp,omitempty" protobuf:"bytes,9,opt,name=deletionTimestamp"`
185+
186+
// Number of seconds allowed for this object to gracefully terminate before
187+
// it will be removed from the system. Only set when deletionTimestamp is also set.
188+
// May only be shortened.
189+
// Read-only.
190+
// +optional
191+
DeletionGracePeriodSeconds *int64 `json:"deletionGracePeriodSeconds,omitempty" protobuf:"varint,10,opt,name=deletionGracePeriodSeconds"`
192+
193+
// Map of string keys and values that can be used to organize and categorize
194+
// (scope and select) objects. May match selectors of replication controllers
195+
// and services.
196+
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels
197+
// +optional
198+
Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,11,rep,name=labels"`
199+
200+
// Annotations is an unstructured key value map stored with a resource that may be
201+
// set by external tools to store and retrieve arbitrary metadata. They are not
202+
// queryable and should be preserved when modifying objects.
203+
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations
204+
// +optional
205+
Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,12,rep,name=annotations"`
206+
207+
// Must be empty before the object is deleted from the registry. Each entry
208+
// is an identifier for the responsible component that will remove the entry
209+
// from the list. If the deletionTimestamp of the object is non-nil, entries
210+
// in this list can only be removed.
211+
// Finalizers may be processed and removed in any order. Order is NOT enforced
212+
// because it introduces significant risk of stuck finalizers.
213+
// finalizers is a shared field, any actor with permission can reorder it.
214+
// If the finalizer list is processed in order, then this can lead to a situation
215+
// in which the component responsible for the first finalizer in the list is
216+
// waiting for a signal (field value, external system, or other) produced by a
217+
// component responsible for a finalizer later in the list, resulting in a deadlock.
218+
// Without enforced ordering finalizers are free to order amongst themselves and
219+
// are not vulnerable to ordering changes in the list.
220+
// +optional
221+
// +patchStrategy=merge
222+
// +listType=set
223+
Finalizers []string `json:"finalizers,omitempty" patchStrategy:"merge" protobuf:"bytes,14,rep,name=finalizers"`
224+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/*
2+
This is a copy of the minimum amount of the original file to be able to test the inspector linter.
3+
*/
4+
package types
5+
6+
// UID is a type that holds unique ID values, including UUIDs. Because we
7+
// don't ONLY use UUIDs, this is an alias to string. Being a type captures
8+
// intent and helps make sure that UIDs and names do not get conflated.
9+
type UID string

0 commit comments

Comments
 (0)