Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions .golangci-kal.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
version: "2"
run:
allow-parallel-runners: true
linters:
default: none
enable:
- kubeapilinter # linter for Kube API conventions
settings:
custom:
kubeapilinter:
type: module
description: KAL is the Kube-API-Linter and lints Kube like APIs based on API conventions and best practices.
settings:
linters:
enable:
- "commentstart" # Ensure comments start with the serialized version of the field name.
- "conditions" # Ensure conditions have the correct json tags and markers.
- "conflictingmarkers"
- "duplicatemarkers" # Ensure there are no exact duplicate markers. for types and fields.
- "integers" # Ensure only int32 and int64 are used for integers.
- "jsontags" # Ensure every field has a json tag.
- "maxlength" # Ensure all strings and arrays have maximum lengths/maximum items.
- "nobools" # Bools do not evolve over time, should use enums instead.
- "nodurations" # Prevents usage of `Duration` types.
- "nofloats" # Ensure floats are not used.
- "nomaps" # Ensure maps are not used.
- "nonullable" # Ensure that types and fields do not have the nullable marker.
- "notimestamp" # Prevents usage of 'Timestamp' fields
- "optionalfields" # Ensure that all fields marked as optional adhere to being pointers and
# having the `omitempty` value in their `json` tag where appropriate.
- "optionalorrequired" # Every field should be marked as `+optional` or `+required`.
- "requiredfields" # Required fields should not be pointers, and should not have `omitempty`.
- "ssatags" # Ensure array fields have the appropriate listType markers
- "statusoptional" # Ensure all first children within status should be optional.
- "statussubresource" # All root objects that have a `status` field should have a status subresource.
- "uniquemarkers" # Ensure that types and fields do not contain more than a single definition of a marker that should only be present once.
- "nophase" # Phase fields are discouraged by the Kube API conventions, use conditions instead.

# Linters below this line are disabled, pending conversation on how and when to enable them.
disable:
- "*" # We will manually enable new linters after understanding the impact. Disable all by default.
lintersConfig:
conflictingmarkers:
conflicts:
- name: "default_vs_required"
sets:
- ["default", "kubebuilder:default"]
- ["required", "kubebuilder:validation:Required", "k8s:required"]
description: "A field with a default value cannot be required"
conditions:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed a couple of differences from upstream, is this expected?
https://github.com/kubernetes-sigs/kueue/blob/main/.golangci-kal.yml#L50-L61

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed up a change for conditions so it matches.

isFirstField: Warn # Require conditions to be the first field in the status struct.
usePatchStrategy: Ignore # Ignore patchStrategy markers on the Conditions field.
useProtobuf: Forbid # We don't use protobuf, so protobuf tags are not required.
optionalfields:
pointers:
preference: WhenRequired # Always | WhenRequired # Whether to always require pointers, or only when required. Defaults to `Always`.
policy: SuggestFix # SuggestFix | Warn # The policy for pointers in optional fields. Defaults to `SuggestFix`.
omitempty:
policy: SuggestFix # SuggestFix | Warn | Ignore # The policy for omitempty in optional fields. Defaults to `SuggestFix`.

exclusions:
generated: strict
rules:
## KAL should only run on API folders.
- path-except: "pkg/apis"
linters:
- kubeapilinter
## Breaking changes: These fields would need to be pointers but changing them is a breaking API change.
## KueueConfiguration optional fields should be pointers
- path: "pkg/apis/kueueoperator/v1/types.go"
text: "optionalfields: field KueueConfiguration.WorkloadManagement"
linters:
- kubeapilinter
- path: "pkg/apis/kueueoperator/v1/types.go"
text: "optionalfields: field KueueConfiguration.GangScheduling"
linters:
- kubeapilinter
- path: "pkg/apis/kueueoperator/v1/types.go"
text: "optionalfields: field KueueConfiguration.Preemption"
linters:
- kubeapilinter
## Required fields with valid zero values should be pointers
- path: "pkg/apis/kueueoperator/v1/types.go"
text: "requiredfields: field GangScheduling.Policy"
linters:
- kubeapilinter
- path: "pkg/apis/kueueoperator/v1/types.go"
text: "requiredfields: field ByWorkload.Admission"
linters:
- kubeapilinter
- path: "pkg/apis/kueueoperator/v1/types.go"
text: "requiredfields: field WorkloadManagement.LabelPolicy"
linters:
- kubeapilinter
- path: "pkg/apis/kueueoperator/v1/types.go"
text: "requiredfields: field Preemption.PreemptionPolicy"
linters:
- kubeapilinter

issues:
max-same-issues: 0
13 changes: 13 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -140,17 +140,30 @@ lint: golangci-lint ## Run golangci-lint linter
lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes
$(GOLANGCI_LINT) run --fix --timeout 30m

.PHONY: lint-api
lint-api: golangci-lint-kal
export HOME=/tmp; export GOCACHE=/tmp/; export GOLANGCI_LINT_CACHE=/tmp/.cache; $(GOLANGCI_LINT_KAL) run -v --config .golangci-kal.yml

.PHONY: lint-api-fix
lint-api-fix: golangci-lint-kal
$(GOLANGCI_LINT_KAL) run -v --config .golangci-kal.yml --fix

## Tool Versions
CONTROLLER_TOOLS_VERSION ?= v0.17.1

GOLANGCI_LINT = $(shell pwd)/bin/golangci-lint
GOLANGCI_LINT_VERSION ?= v2.7.2
GOLANGCI_LINT_KAL = $(shell pwd)/bin/golangci-lint-kube-api-linter
golangci-lint:
@[ -f $(GOLANGCI_LINT) ] || { \
set -e ;\
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell dirname $(GOLANGCI_LINT)) $(GOLANGCI_LINT_VERSION) ;\
}

.PHONY: golangci-lint-kal
golangci-lint-kal: golangci-lint ## Build golangci-lint-kal from custom configuration.
export HOME=/tmp; export GOCACHE=/tmp/; export GOLANGCI_LINT_CACHE=/tmp/.cache; cd hack/golangci-kal; $(GOLANGCI_LINT) custom; mv bin/golangci-lint-kube-api-linter ${LOCALBIN}

.PHONY: operator-sdk
OPERATOR_SDK ?= $(LOCALBIN)/operator-sdk
operator-sdk: ## Download operator-sdk locally if necessary.
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ require (
sigs.k8s.io/controller-tools v0.4.1
sigs.k8s.io/jobset v0.10.1
sigs.k8s.io/kueue v0.15.0
sigs.k8s.io/lws v0.7.0
sigs.k8s.io/structured-merge-diff/v6 v6.3.1
sigs.k8s.io/yaml v1.6.0
)
Expand Down Expand Up @@ -144,6 +145,5 @@ require (
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.0 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/kube-storage-version-migrator v0.0.6-0.20230721195810-5c8923c5ff96 // indirect
sigs.k8s.io/lws v0.7.0 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
)
6 changes: 6 additions & 0 deletions hack/golangci-kal/.custom-gcl.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: v2.7.2
name: golangci-lint-kube-api-linter
destination: ./bin
plugins:
- module: 'sigs.k8s.io/kube-api-linter'
version: v0.0.0-20251208100930-d3015c953951
22 changes: 11 additions & 11 deletions pkg/apis/kueueoperator/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,19 @@ type Kueue struct {

// spec holds user settable values for configuration
// +required
Spec KueueOperandSpec `json:"spec"`
Spec KueueOperandSpec `json:"spec,omitzero"`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JoelSpeed @everettraven

Is there any concerns on breaking changes for omitzero or omitempty?

I don't have any CRD changes but I guess its worth asking if there could be any UX issues with adding these tags.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is going to be safe. Looking through, I can see that the empty marshalled spec object would have been rejected by the fact you have at least 1 required field that doesn't permit its empty value in the spec.

So previously a user who set nothing, would have marshalled the empty object, and got a rejection. Now, they wouldn't marshal anything, and would still get a rejection

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Joel!

// status holds observed values from the cluster.
// They may not be overridden.
// +optional
Status KueueStatus `json:"status,omitempty"`
Status KueueStatus `json:"status,omitzero,omitempty"`
}

type KueueOperandSpec struct {
operatorv1.OperatorSpec `json:",inline"`
// config is the desired configuration
// for the Kueue operator.
// +required
Config KueueConfiguration `json:"config"`
Config KueueConfiguration `json:"config,omitzero"`
}

type KueueConfiguration struct {
Expand All @@ -45,7 +45,7 @@ type KueueConfiguration struct {
// known as external frameworks.
// Kueue will only manage workloads that correspond to the specified integrations.
// +required
Integrations Integrations `json:"integrations"`
Integrations Integrations `json:"integrations,omitzero"`
// workloadManagement controls how Kueue manages workloads.
// By default Kueue will manage workloads that have a queue-name label.
// Workloads that are missing the queue-name will be ignored by Kueue.
Expand Down Expand Up @@ -133,7 +133,7 @@ type ExternalFramework struct {
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:XValidation:rule="self.matches(r'^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$')"
// +required
Group string `json:"group"`
Group string `json:"group,omitempty"`
// resource is the Resource type of the external framework.
// Resource types are lowercase and plural (e.g. pods, deployments).
// Must be a valid DNS 1123 label consisting of a lower-case alphanumeric string
Expand All @@ -144,7 +144,7 @@ type ExternalFramework struct {
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:XValidation:rule="self.matches(r'^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')"
// +required
Resource string `json:"resource"`
Resource string `json:"resource,omitempty"`
// version is the version of the api (e.g. v1alpha1, v1beta1, v1).
// Must be a valid DNS 1035 label consisting of a lower-case alphanumeric string
// and hyphens of at most 63 characters in length.
Expand All @@ -154,7 +154,7 @@ type ExternalFramework struct {
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:XValidation:rule="self.matches(r'^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')"
// +required
Version string `json:"version"`
Version string `json:"version,omitempty"`
}

// This is the integrations for Kueue.
Expand All @@ -171,7 +171,7 @@ type Integrations struct {
// +kubebuilder:validation:XValidation:rule="self.all(x, self.exists_one(y, x == y))",message="each item in frameworks must be unique"
// +listType=set
// +required
Frameworks []KueueIntegration `json:"frameworks"`
Frameworks []KueueIntegration `json:"frameworks,omitempty"`
// externalFrameworks are a list of GroupVersionResources
// that are managed for Kueue by external controllers.
// externalFrameworks are optional and should only be used if you have an external controller
Expand All @@ -181,7 +181,7 @@ type Integrations struct {
// +listMapKey=group
// +kubebuilder:validation:MaxItems=32
// +optional
ExternalFrameworks []ExternalFramework `json:"externalFrameworks"`
ExternalFrameworks []ExternalFramework `json:"externalFrameworks,omitempty"`
// labelKeysToCopy are a list of label keys that are copied once a workload is created.
// These keys are persisted to the internal Kueue workload object.
// If not specified, only the Kueue labels will be copied.
Expand All @@ -190,7 +190,7 @@ type Integrations struct {
// +listType=map
// +listMapKey=key
// +optional
LabelKeysToCopy []LabelKeys `json:"labelKeysToCopy"`
LabelKeysToCopy []LabelKeys `json:"labelKeysToCopy,omitempty"`
}

type LabelKeys struct {
Expand All @@ -208,7 +208,7 @@ type LabelKeys struct {
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:XValidation:rule="self.matches(r'^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?([a-z0-9]([-a-z0-9]*[a-z0-9])?)$')"
// +required
Key string `json:"key"`
Key string `json:"key,omitempty"`
}

// +kubebuilder:validation:Enum=ByWorkload;None;""
Expand Down