Skip to content

Commit a8c6c0d

Browse files
(go/v4,deploy-image/alpha-v1) feat: integrate kube-api-linter and fix scaffolds
- Add support for kube-api-linter to validate Kubernetes API definitions More info: https://github.com/kubernetes-sigs/kube-api-linter - Fix all tutorials (getting-started, cronjob, multiversion) to comply with lint rules - Update all plugins scaffolds to according to rules - Adjust GitHub Actions to build and run the new custom linter in CI
1 parent 202a630 commit a8c6c0d

File tree

126 files changed

+1721
-675
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

126 files changed

+1721
-675
lines changed

.github/workflows/lint-sample.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ jobs:
1212
lint-samples:
1313
runs-on: ubuntu-latest
1414
strategy:
15+
fail-fast: false
1516
matrix:
1617
folder: [
1718
"testdata/project-v4",
@@ -32,6 +33,11 @@ jobs:
3233
- name: Prepare ${{ matrix.folder }}
3334
working-directory: ${{ matrix.folder }}
3435
run: go mod tidy
36+
- name: Build kubeapilinter
37+
working-directory: ${{ matrix.folder }}
38+
run: make kube-api-linter
39+
env:
40+
CGO_ENABLED: 1
3541
- name: Check linter configuration
3642
working-directory: ${{ matrix.folder }}
3743
run: make lint-config
@@ -40,7 +46,7 @@ jobs:
4046
with:
4147
version: v2.1.6
4248
working-directory: ${{ matrix.folder }}
43-
args: --config .golangci.yml ./...
49+
install-mode: goinstall
4450
- name: Run linter via makefile target
4551
working-directory: ${{ matrix.folder }}
4652
run: make lint

docs/book/src/cronjob-tutorial/testdata/project/.github/workflows/lint.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ jobs:
2121
uses: golangci/golangci-lint-action@v8
2222
with:
2323
version: v2.1.6
24+
install-mode: goinstall

docs/book/src/cronjob-tutorial/testdata/project/.golangci.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,20 @@ linters:
2121
- unconvert
2222
- unparam
2323
- unused
24+
- kubeapilinter
2425
settings:
2526
revive:
2627
rules:
2728
- name: comment-spacings
2829
- name: import-shadowing
30+
custom:
31+
kubeapilinter:
32+
path: "./bin/kube-api-linter.so"
33+
description: "Kube API Linter plugin"
34+
original-url: "sigs.k8s.io/kube-api-linter"
35+
settings:
36+
linters: { }
37+
lintersConfig: { }
2938
exclusions:
3039
generated: lax
3140
rules:
@@ -36,6 +45,9 @@ linters:
3645
- dupl
3746
- lll
3847
path: internal/*
48+
- path-except: "^api/"
49+
linters:
50+
- kubeapilinter
3951
paths:
4052
- third_party$
4153
- builtin$

docs/book/src/cronjob-tutorial/testdata/project/Makefile

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ cleanup-test-e2e: ## Tear down the Kind cluster used for e2e tests
8989
@$(KIND) delete cluster --name $(KIND_CLUSTER)
9090

9191
.PHONY: lint
92-
lint: golangci-lint ## Run golangci-lint linter
92+
lint: golangci-lint kube-api-linter ## Run golangci-lint linter
9393
$(GOLANGCI_LINT) run
9494

9595
.PHONY: lint-fix
@@ -219,6 +219,23 @@ golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
219219
$(GOLANGCI_LINT): $(LOCALBIN)
220220
$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))
221221

222+
# To lint Kubernetes API definitions.
223+
# More info: https://github.com/kubernetes-sigs/kube-api-linter
224+
KUBE_API_LINTER_PLUGIN := $(LOCALBIN)/kube-api-linter.so
225+
KUBE_API_LINTER_BUILD_DIR := ./hack/kube-api-linter
226+
227+
.PHONY: kube-api-linter
228+
kube-api-linter: $(KUBE_API_LINTER_PLUGIN) ## Build the kube-api-linter plugin
229+
$(KUBE_API_LINTER_PLUGIN): $(KUBE_API_LINTER_BUILD_DIR)/go.mod
230+
cd $(KUBE_API_LINTER_BUILD_DIR) && \
231+
go build -buildmode=plugin -o "$(abspath $(KUBE_API_LINTER_PLUGIN))" sigs.k8s.io/kube-api-linter/pkg/plugin
232+
$(KUBE_API_LINTER_BUILD_DIR)/go.mod:
233+
@echo "Setting up local module for kube-api-linter plugin..."
234+
mkdir -p $(KUBE_API_LINTER_BUILD_DIR)
235+
cd $(KUBE_API_LINTER_BUILD_DIR) && \
236+
go mod init local-kube-api-linter && \
237+
go get sigs.k8s.io/kube-api-linter@latest
238+
222239
# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
223240
# $1 - target path with name of binary
224241
# $2 - package url which can be installed

docs/book/src/cronjob-tutorial/testdata/project/api/v1/cronjob_types.go

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -64,46 +64,45 @@ import (
6464

6565
// CronJobSpec defines the desired state of CronJob.
6666
type CronJobSpec struct {
67+
// schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
6768
// +kubebuilder:validation:MinLength=0
68-
69-
// The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
69+
// +required
7070
Schedule string `json:"schedule"`
7171

72-
// +kubebuilder:validation:Minimum=0
73-
74-
// Optional deadline in seconds for starting the job if it misses scheduled
72+
// startingDeadlineSeconds defines in seconds for starting the job if it misses scheduled
7573
// time for any reason. Missed jobs executions will be counted as failed ones.
7674
// +optional
75+
// +kubebuilder:validation:Minimum=0
7776
StartingDeadlineSeconds *int64 `json:"startingDeadlineSeconds,omitempty"`
7877

79-
// Specifies how to treat concurrent executions of a Job.
78+
// concurrencyPolicy specifies how to treat concurrent executions of a Job.
8079
// Valid values are:
8180
// - "Allow" (default): allows CronJobs to run concurrently;
8281
// - "Forbid": forbids concurrent runs, skipping next run if previous run hasn't finished yet;
8382
// - "Replace": cancels currently running job and replaces it with a new one
8483
// +optional
85-
ConcurrencyPolicy ConcurrencyPolicy `json:"concurrencyPolicy,omitempty"`
84+
// +kubebuilder:default:=Allow
85+
ConcurrencyPolicy *ConcurrencyPolicy `json:"concurrencyPolicy,omitempty"`
8686

87-
// This flag tells the controller to suspend subsequent executions, it does
87+
// suspend tells the controller to suspend subsequent executions, it does
8888
// not apply to already started executions. Defaults to false.
8989
// +optional
9090
Suspend *bool `json:"suspend,omitempty"`
9191

92-
// Specifies the job that will be created when executing a CronJob.
92+
// jobTemplate defines the job that will be created when executing a CronJob.
93+
// +required
9394
JobTemplate batchv1.JobTemplateSpec `json:"jobTemplate"`
9495

95-
// +kubebuilder:validation:Minimum=0
96-
97-
// The number of successful finished jobs to retain.
96+
// successfulJobsHistoryLimit defines the number of successful finished jobs to retain.
9897
// This is a pointer to distinguish between explicit zero and not specified.
9998
// +optional
100-
SuccessfulJobsHistoryLimit *int32 `json:"successfulJobsHistoryLimit,omitempty"`
101-
10299
// +kubebuilder:validation:Minimum=0
100+
SuccessfulJobsHistoryLimit *int32 `json:"successfulJobsHistoryLimit,omitempty"`
103101

104-
// The number of failed finished jobs to retain.
102+
// failedJobsHistoryLimit defines the number of failed finished jobs to retain.
105103
// This is a pointer to distinguish between explicit zero and not specified.
106104
// +optional
105+
// +kubebuilder:validation:Minimum=0
107106
FailedJobsHistoryLimit *int32 `json:"failedJobsHistoryLimit,omitempty"`
108107
}
109108

@@ -147,11 +146,11 @@ type CronJobStatus struct {
147146
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
148147
// Important: Run "make" to regenerate code after modifying this file
149148

150-
// A list of pointers to currently running jobs.
149+
// active defines a list of pointers to currently running jobs.
151150
// +optional
152151
Active []corev1.ObjectReference `json:"active,omitempty"`
153152

154-
// Information when was the last time the job was successfully scheduled.
153+
// lastScheduleTime defines the when was the last time the job was successfully scheduled.
155154
// +optional
156155
LastScheduleTime *metav1.Time `json:"lastScheduleTime,omitempty"`
157156
}
@@ -169,11 +168,19 @@ type CronJobStatus struct {
169168
type CronJob struct {
170169
/*
171170
*/
172-
metav1.TypeMeta `json:",inline"`
171+
metav1.TypeMeta `json:",inline"`
172+
173+
// metadata is a standard object metadata.
174+
// +optional
173175
metav1.ObjectMeta `json:"metadata,omitempty"`
174176

175-
Spec CronJobSpec `json:"spec,omitempty"`
176-
Status CronJobStatus `json:"status,omitempty"`
177+
// spec defines the desired state of CronJob.
178+
// +required
179+
Spec CronJobSpec `json:"spec"`
180+
181+
// status defines the observed state of CronJob.
182+
// +optional
183+
Status *CronJobStatus `json:"status,omitempty"`
177184
}
178185

179186
// +kubebuilder:object:root=true

docs/book/src/cronjob-tutorial/testdata/project/api/v1/zz_generated.deepcopy.go

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/book/src/cronjob-tutorial/testdata/project/config/crd/bases/batch.tutorial.kubebuilder.io_cronjobs.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ spec:
2727
spec:
2828
properties:
2929
concurrencyPolicy:
30+
default: Allow
3031
enum:
3132
- Allow
3233
- Forbid
@@ -3840,6 +3841,8 @@ spec:
38403841
format: date-time
38413842
type: string
38423843
type: object
3844+
required:
3845+
- spec
38433846
type: object
38443847
served: true
38453848
storage: true

docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/crd/batch.tutorial.kubebuilder.io_cronjobs.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ spec:
3333
spec:
3434
properties:
3535
concurrencyPolicy:
36+
default: Allow
3637
enum:
3738
- Allow
3839
- Forbid
@@ -3846,6 +3847,8 @@ spec:
38463847
format: date-time
38473848
type: string
38483849
type: object
3850+
required:
3851+
- spec
38493852
type: object
38503853
served: true
38513854
storage: true

docs/book/src/cronjob-tutorial/testdata/project/dist/install.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ spec:
3535
spec:
3636
properties:
3737
concurrencyPolicy:
38+
default: Allow
3839
enum:
3940
- Allow
4041
- Forbid
@@ -3848,6 +3849,8 @@ spec:
38483849
format: date-time
38493850
type: string
38503851
type: object
3852+
required:
3853+
- spec
38513854
type: object
38523855
served: true
38533856
storage: true

docs/book/src/cronjob-tutorial/testdata/project/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
k8s.io/api v0.33.0
1010
k8s.io/apimachinery v0.33.0
1111
k8s.io/client-go v0.33.0
12+
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
1213
sigs.k8s.io/controller-runtime v0.21.0
1314
)
1415

@@ -89,7 +90,6 @@ require (
8990
k8s.io/component-base v0.33.0 // indirect
9091
k8s.io/klog/v2 v2.130.1 // indirect
9192
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
92-
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
9393
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect
9494
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
9595
sigs.k8s.io/randfill v1.0.0 // indirect

docs/book/src/cronjob-tutorial/testdata/project/internal/controller/cronjob_controller.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -441,13 +441,17 @@ func (r *CronJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
441441
*/
442442
// figure out how to run this job -- concurrency policy might forbid us from running
443443
// multiple at the same time...
444-
if cronJob.Spec.ConcurrencyPolicy == batchv1.ForbidConcurrent && len(activeJobs) > 0 {
444+
policy := batchv1.AllowConcurrent
445+
if cronJob.Spec.ConcurrencyPolicy != nil {
446+
policy = *cronJob.Spec.ConcurrencyPolicy
447+
}
448+
if policy == batchv1.ForbidConcurrent && len(activeJobs) > 0 {
445449
log.V(1).Info("concurrency policy blocks concurrent runs, skipping", "num active", len(activeJobs))
446450
return scheduledResult, nil
447451
}
448452

449453
// ...or instruct us to replace existing ones...
450-
if cronJob.Spec.ConcurrencyPolicy == batchv1.ReplaceConcurrent {
454+
if cronJob.Spec.ConcurrencyPolicy != nil && *cronJob.Spec.ConcurrencyPolicy == batchv1.ReplaceConcurrent {
451455
for _, activeJob := range activeJobs {
452456
// we don't care if the job was already deleted
453457
if err := r.Delete(ctx, activeJob, client.PropagationPolicy(metav1.DeletePropagationBackground)); client.IgnoreNotFound(err) != nil {

docs/book/src/cronjob-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"k8s.io/apimachinery/pkg/runtime/schema"
2727
validationutils "k8s.io/apimachinery/pkg/util/validation"
2828
"k8s.io/apimachinery/pkg/util/validation/field"
29+
"k8s.io/utils/ptr"
2930

3031
"k8s.io/apimachinery/pkg/runtime"
3132
ctrl "sigs.k8s.io/controller-runtime"
@@ -113,8 +114,8 @@ func (d *CronJobCustomDefaulter) Default(_ context.Context, obj runtime.Object)
113114

114115
// applyDefaults applies default values to CronJob fields.
115116
func (d *CronJobCustomDefaulter) applyDefaults(cronJob *batchv1.CronJob) {
116-
if cronJob.Spec.ConcurrencyPolicy == "" {
117-
cronJob.Spec.ConcurrencyPolicy = d.DefaultConcurrencyPolicy
117+
if cronJob.Spec.ConcurrencyPolicy == nil {
118+
cronJob.Spec.ConcurrencyPolicy = ptr.To(d.DefaultConcurrencyPolicy)
118119
}
119120
if cronJob.Spec.Suspend == nil {
120121
cronJob.Spec.Suspend = new(bool)

docs/book/src/cronjob-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook_test.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222

2323
batchv1 "tutorial.kubebuilder.io/project/api/v1"
2424
// TODO (user): Add any additional imports if needed
25+
"k8s.io/utils/ptr"
2526
)
2627

2728
var _ = Describe("CronJob Webhook", func() {
@@ -39,9 +40,9 @@ var _ = Describe("CronJob Webhook", func() {
3940
obj = &batchv1.CronJob{
4041
Spec: batchv1.CronJobSpec{
4142
Schedule: schedule,
42-
ConcurrencyPolicy: batchv1.AllowConcurrent,
43-
SuccessfulJobsHistoryLimit: new(int32),
44-
FailedJobsHistoryLimit: new(int32),
43+
ConcurrencyPolicy: ptr.To(batchv1.AllowConcurrent),
44+
SuccessfulJobsHistoryLimit: ptr.To(int32(3)),
45+
FailedJobsHistoryLimit: ptr.To(int32(1)),
4546
},
4647
}
4748
*obj.Spec.SuccessfulJobsHistoryLimit = 3
@@ -50,9 +51,9 @@ var _ = Describe("CronJob Webhook", func() {
5051
oldObj = &batchv1.CronJob{
5152
Spec: batchv1.CronJobSpec{
5253
Schedule: schedule,
53-
ConcurrencyPolicy: batchv1.AllowConcurrent,
54-
SuccessfulJobsHistoryLimit: new(int32),
55-
FailedJobsHistoryLimit: new(int32),
54+
ConcurrencyPolicy: ptr.To(batchv1.AllowConcurrent),
55+
SuccessfulJobsHistoryLimit: ptr.To(int32(3)),
56+
FailedJobsHistoryLimit: ptr.To(int32(1)),
5657
},
5758
}
5859
*oldObj.Spec.SuccessfulJobsHistoryLimit = 3
@@ -77,7 +78,7 @@ var _ = Describe("CronJob Webhook", func() {
7778
Context("When creating CronJob under Defaulting Webhook", func() {
7879
It("Should apply defaults when a required field is empty", func() {
7980
By("simulating a scenario where defaults should be applied")
80-
obj.Spec.ConcurrencyPolicy = "" // This should default to AllowConcurrent
81+
obj.Spec.ConcurrencyPolicy = nil // This should default to AllowConcurrent
8182
obj.Spec.Suspend = nil // This should default to false
8283
obj.Spec.SuccessfulJobsHistoryLimit = nil // This should default to 3
8384
obj.Spec.FailedJobsHistoryLimit = nil // This should default to 1
@@ -86,15 +87,15 @@ var _ = Describe("CronJob Webhook", func() {
8687
_ = defaulter.Default(ctx, obj)
8788

8889
By("checking that the default values are set")
89-
Expect(obj.Spec.ConcurrencyPolicy).To(Equal(batchv1.AllowConcurrent), "Expected ConcurrencyPolicy to default to AllowConcurrent")
90+
Expect(obj.Spec.ConcurrencyPolicy).To(HaveValue(Equal(batchv1.AllowConcurrent)), "Expected ConcurrencyPolicy to default to AllowConcurrent")
9091
Expect(*obj.Spec.Suspend).To(BeFalse(), "Expected Suspend to default to false")
9192
Expect(*obj.Spec.SuccessfulJobsHistoryLimit).To(Equal(int32(3)), "Expected SuccessfulJobsHistoryLimit to default to 3")
9293
Expect(*obj.Spec.FailedJobsHistoryLimit).To(Equal(int32(1)), "Expected FailedJobsHistoryLimit to default to 1")
9394
})
9495

9596
It("Should not overwrite fields that are already set", func() {
9697
By("setting fields that would normally get a default")
97-
obj.Spec.ConcurrencyPolicy = batchv1.ForbidConcurrent
98+
obj.Spec.ConcurrencyPolicy = ptr.To(batchv1.ForbidConcurrent)
9899
obj.Spec.Suspend = new(bool)
99100
*obj.Spec.Suspend = true
100101
obj.Spec.SuccessfulJobsHistoryLimit = new(int32)
@@ -106,7 +107,7 @@ var _ = Describe("CronJob Webhook", func() {
106107
_ = defaulter.Default(ctx, obj)
107108

108109
By("checking that the fields were not overwritten")
109-
Expect(obj.Spec.ConcurrencyPolicy).To(Equal(batchv1.ForbidConcurrent), "Expected ConcurrencyPolicy to retain its set value")
110+
Expect(obj.Spec.ConcurrencyPolicy).To(HaveValue(Equal(batchv1.ForbidConcurrent)), "Expected ConcurrencyPolicy to retain its set value")
110111
Expect(*obj.Spec.Suspend).To(BeTrue(), "Expected Suspend to retain its set value")
111112
Expect(*obj.Spec.SuccessfulJobsHistoryLimit).To(Equal(int32(5)), "Expected SuccessfulJobsHistoryLimit to retain its set value")
112113
Expect(*obj.Spec.FailedJobsHistoryLimit).To(Equal(int32(2)), "Expected FailedJobsHistoryLimit to retain its set value")

0 commit comments

Comments
 (0)