Skip to content

✨ Remove pointer, add omitzero & MinProperties for initialization fields/structs #12482

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 14, 2025
Merged
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
2 changes: 1 addition & 1 deletion .golangci-kal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ linters:
- kubeapilinter
# KAL does not handle omitzero correctly yet: https://github.com/kubernetes-sigs/kube-api-linter/pull/115
- path: "api/.*"
text: "optionalfields: field Status is optional and should (be a pointer|have the omitempty tag)"
text: "optionalfields: field (Status|Initialization) is optional and should (be a pointer|have the omitempty tag)"
linters:
- kubeapilinter
- path: "api/bootstrap/kubeadm/v1beta2"
Expand Down
4 changes: 4 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ linters:
# CAPI utils
- pkg: sigs.k8s.io/cluster-api/util/conditions/deprecated/v1beta1
alias: v1beta1conditions
- pkg: sigs.k8s.io/cluster-api/util/conditions
alias: ""
- pkg: sigs.k8s.io/cluster-api/util/patch
alias: ""
- pkg: sigs.k8s.io/cluster-api/internal/topology/names
alias: topologynames
# CAPD
Expand Down
11 changes: 3 additions & 8 deletions api/bootstrap/kubeadm/v1beta1/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,10 @@ func (src *KubeadmConfig) ConvertTo(dstRaw conversion.Hub) error {

// Recover intent for bool values converted to *bool.
initialization := bootstrapv1.KubeadmConfigInitializationStatus{}
var restoredBootstrapDataSecretCreated *bool
if restored.Status.Initialization != nil {
restoredBootstrapDataSecretCreated = restored.Status.Initialization.DataSecretCreated
}
restoredBootstrapDataSecretCreated := restored.Status.Initialization.DataSecretCreated
clusterv1.Convert_bool_To_Pointer_bool(src.Status.Ready, ok, restoredBootstrapDataSecretCreated, &initialization.DataSecretCreated)
if !reflect.DeepEqual(initialization, bootstrapv1.KubeadmConfigInitializationStatus{}) {
dst.Status.Initialization = &initialization
dst.Status.Initialization = initialization
}
if err := RestoreBoolIntentKubeadmConfigSpec(&src.Spec, &dst.Spec, ok, &restored.Spec); err != nil {
return err
Expand Down Expand Up @@ -363,9 +360,7 @@ func Convert_v1beta2_KubeadmConfigStatus_To_v1beta1_KubeadmConfigStatus(in *boot
}

// Move initialization to old fields
if in.Initialization != nil {
out.Ready = ptr.Deref(in.Initialization.DataSecretCreated, false)
}
out.Ready = ptr.Deref(in.Initialization.DataSecretCreated, false)

// Move new conditions (v1beta2) to the v1beta2 field.
if in.Conditions == nil {
Expand Down
7 changes: 0 additions & 7 deletions api/bootstrap/kubeadm/v1beta1/conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,6 @@ func hubKubeadmConfigStatus(in *bootstrapv1.KubeadmConfigStatus, c randfill.Cont
if in.Deprecated.V1Beta1 == nil {
in.Deprecated.V1Beta1 = &bootstrapv1.KubeadmConfigV1Beta1DeprecatedStatus{}
}

// Drop empty structs with only omit empty fields.
if in.Initialization != nil {
if reflect.DeepEqual(in.Initialization, &bootstrapv1.KubeadmConfigInitializationStatus{}) {
in.Initialization = nil
}
}
}

func hubKubeadmConfigSpec(in *bootstrapv1.KubeadmConfigSpec, c randfill.Continue) {
Expand Down
3 changes: 2 additions & 1 deletion api/bootstrap/kubeadm/v1beta2/kubeadmconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ type KubeadmConfigStatus struct {
// initialization provides observations of the KubeadmConfig initialization process.
// NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial Machine provisioning.
// +optional
Initialization *KubeadmConfigInitializationStatus `json:"initialization,omitempty"`
Initialization KubeadmConfigInitializationStatus `json:"initialization,omitempty,omitzero"`

// dataSecretName is the name of the secret that stores the bootstrap data script.
// +optional
Expand All @@ -489,6 +489,7 @@ type KubeadmConfigStatus struct {
}

// KubeadmConfigInitializationStatus provides observations of the KubeadmConfig initialization process.
// +kubebuilder:validation:MinProperties=1
type KubeadmConfigInitializationStatus struct {
// dataSecretCreated is true when the Machine's boostrap secret is created.
// NOTE: this field is part of the Cluster API contract, and it is used to orchestrate initial Machine provisioning.
Expand Down
6 changes: 1 addition & 5 deletions api/bootstrap/kubeadm/v1beta2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 3 additions & 8 deletions api/controlplane/kubeadm/v1beta1/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,10 @@ func (src *KubeadmControlPlane) ConvertTo(dstRaw conversion.Hub) error {

// Recover intent for bool values converted to *bool.
initialization := controlplanev1.KubeadmControlPlaneInitializationStatus{}
var restoredControlPlaneInitialized *bool
if restored.Status.Initialization != nil {
restoredControlPlaneInitialized = restored.Status.Initialization.ControlPlaneInitialized
}
restoredControlPlaneInitialized := restored.Status.Initialization.ControlPlaneInitialized
clusterv1.Convert_bool_To_Pointer_bool(src.Status.Initialized, ok, restoredControlPlaneInitialized, &initialization.ControlPlaneInitialized)
if !reflect.DeepEqual(initialization, controlplanev1.KubeadmControlPlaneInitializationStatus{}) {
dst.Status.Initialization = &initialization
dst.Status.Initialization = initialization
}

if err := bootstrapv1beta1.RestoreBoolIntentKubeadmConfigSpec(&src.Spec.KubeadmConfigSpec, &dst.Spec.KubeadmConfigSpec, ok, &restored.Spec.KubeadmConfigSpec); err != nil {
Expand Down Expand Up @@ -201,9 +198,7 @@ func Convert_v1beta2_KubeadmControlPlaneStatus_To_v1beta1_KubeadmControlPlaneSta
}

// Move initialized to ControlPlaneInitialized, rebuild ready
if in.Initialization != nil {
out.Initialized = ptr.Deref(in.Initialization.ControlPlaneInitialized, false)
}
out.Initialized = ptr.Deref(in.Initialization.ControlPlaneInitialized, false)
out.Ready = out.ReadyReplicas > 0

// Move new conditions (v1beta2) and replica counter to the v1beta2 field.
Expand Down
7 changes: 0 additions & 7 deletions api/controlplane/kubeadm/v1beta1/conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,6 @@ func hubKubeadmControlPlaneStatus(in *controlplanev1.KubeadmControlPlaneStatus,
in.Deprecated.V1Beta1 = &controlplanev1.KubeadmControlPlaneV1Beta1DeprecatedStatus{}
}

// Drop empty structs with only omit empty fields.
if in.Initialization != nil {
if reflect.DeepEqual(in.Initialization, &controlplanev1.KubeadmControlPlaneInitializationStatus{}) {
in.Initialization = nil
}
}

// nil becomes &0 after hub => spoke => hub conversion
// This is acceptable as usually Replicas is set and controllers using older apiVersions are not writing MachineSet status.
if in.Replicas == nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ type KubeadmControlPlaneStatus struct {
// initialization provides observations of the KubeadmControlPlane initialization process.
// NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial Machine provisioning.
// +optional
Initialization *KubeadmControlPlaneInitializationStatus `json:"initialization,omitempty"`
Initialization KubeadmControlPlaneInitializationStatus `json:"initialization,omitempty,omitzero"`

// selector is the label selector in string format to avoid introspection
// by clients, and is used to provide the CRD-based integration for the
Expand Down Expand Up @@ -688,6 +688,7 @@ type KubeadmControlPlaneStatus struct {
}

// KubeadmControlPlaneInitializationStatus provides observations of the KubeadmControlPlane initialization process.
// +kubebuilder:validation:MinProperties=1
type KubeadmControlPlaneInitializationStatus struct {
// controlPlaneInitialized is true when the KubeadmControlPlane provider reports that the Kubernetes control plane is initialized;
// A control plane is considered initialized when it can accept requests, no matter if this happens before
Expand Down
6 changes: 1 addition & 5 deletions api/controlplane/kubeadm/v1beta2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 15 additions & 30 deletions api/core/v1beta1/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,12 @@ func (src *Cluster) ConvertTo(dstRaw conversion.Hub) error {
clusterv1.Convert_bool_To_Pointer_bool(src.Spec.Paused, ok, restored.Spec.Paused, &dst.Spec.Paused)

initialization := clusterv1.ClusterInitializationStatus{}
var restoredControlPlaneInitialized, restoredInfrastructureProvisioned *bool
if restored.Status.Initialization != nil {
restoredControlPlaneInitialized = restored.Status.Initialization.ControlPlaneInitialized
restoredInfrastructureProvisioned = restored.Status.Initialization.InfrastructureProvisioned
}
restoredControlPlaneInitialized := restored.Status.Initialization.ControlPlaneInitialized
restoredInfrastructureProvisioned := restored.Status.Initialization.InfrastructureProvisioned
clusterv1.Convert_bool_To_Pointer_bool(src.Status.ControlPlaneReady, ok, restoredControlPlaneInitialized, &initialization.ControlPlaneInitialized)
clusterv1.Convert_bool_To_Pointer_bool(src.Status.InfrastructureReady, ok, restoredInfrastructureProvisioned, &initialization.InfrastructureProvisioned)
if !reflect.DeepEqual(initialization, clusterv1.ClusterInitializationStatus{}) {
dst.Status.Initialization = &initialization
dst.Status.Initialization = initialization
}
return nil
}
Expand Down Expand Up @@ -381,15 +378,12 @@ func (src *Machine) ConvertTo(dstRaw conversion.Hub) error {

// Recover intent for bool values converted to *bool.
initialization := clusterv1.MachineInitializationStatus{}
var restoredBootstrapDataSecretCreated, restoredInfrastructureProvisioned *bool
if restored.Status.Initialization != nil {
restoredBootstrapDataSecretCreated = restored.Status.Initialization.BootstrapDataSecretCreated
restoredInfrastructureProvisioned = restored.Status.Initialization.InfrastructureProvisioned
}
restoredBootstrapDataSecretCreated := restored.Status.Initialization.BootstrapDataSecretCreated
restoredInfrastructureProvisioned := restored.Status.Initialization.InfrastructureProvisioned
clusterv1.Convert_bool_To_Pointer_bool(src.Status.BootstrapReady, ok, restoredBootstrapDataSecretCreated, &initialization.BootstrapDataSecretCreated)
clusterv1.Convert_bool_To_Pointer_bool(src.Status.InfrastructureReady, ok, restoredInfrastructureProvisioned, &initialization.InfrastructureProvisioned)
if !reflect.DeepEqual(initialization, clusterv1.MachineInitializationStatus{}) {
dst.Status.Initialization = &initialization
dst.Status.Initialization = initialization
}

// Recover other values.
Expand Down Expand Up @@ -553,15 +547,12 @@ func (src *MachinePool) ConvertTo(dstRaw conversion.Hub) error {

// Recover intent for bool values converted to *bool.
initialization := clusterv1.MachinePoolInitializationStatus{}
var restoredBootstrapDataSecretCreated, restoredInfrastructureProvisioned *bool
if restored.Status.Initialization != nil {
restoredBootstrapDataSecretCreated = restored.Status.Initialization.BootstrapDataSecretCreated
restoredInfrastructureProvisioned = restored.Status.Initialization.InfrastructureProvisioned
}
restoredBootstrapDataSecretCreated := restored.Status.Initialization.BootstrapDataSecretCreated
restoredInfrastructureProvisioned := restored.Status.Initialization.InfrastructureProvisioned
clusterv1.Convert_bool_To_Pointer_bool(src.Status.BootstrapReady, ok, restoredBootstrapDataSecretCreated, &initialization.BootstrapDataSecretCreated)
clusterv1.Convert_bool_To_Pointer_bool(src.Status.InfrastructureReady, ok, restoredInfrastructureProvisioned, &initialization.InfrastructureProvisioned)
if !reflect.DeepEqual(initialization, clusterv1.MachinePoolInitializationStatus{}) {
dst.Status.Initialization = &initialization
dst.Status.Initialization = initialization
}

return nil
Expand Down Expand Up @@ -917,10 +908,8 @@ func Convert_v1beta2_ClusterStatus_To_v1beta1_ClusterStatus(in *clusterv1.Cluste
}

// Move initialization to old fields
if in.Initialization != nil {
out.ControlPlaneReady = ptr.Deref(in.Initialization.ControlPlaneInitialized, false)
out.InfrastructureReady = ptr.Deref(in.Initialization.InfrastructureProvisioned, false)
}
out.ControlPlaneReady = ptr.Deref(in.Initialization.ControlPlaneInitialized, false)
out.InfrastructureReady = ptr.Deref(in.Initialization.InfrastructureProvisioned, false)

// Move FailureDomains
if in.FailureDomains != nil {
Expand Down Expand Up @@ -1316,10 +1305,8 @@ func Convert_v1beta2_MachineStatus_To_v1beta1_MachineStatus(in *clusterv1.Machin
}

// Move initialization to old fields
if in.Initialization != nil {
out.BootstrapReady = ptr.Deref(in.Initialization.BootstrapDataSecretCreated, false)
out.InfrastructureReady = ptr.Deref(in.Initialization.InfrastructureProvisioned, false)
}
out.BootstrapReady = ptr.Deref(in.Initialization.BootstrapDataSecretCreated, false)
out.InfrastructureReady = ptr.Deref(in.Initialization.InfrastructureProvisioned, false)

// Move new conditions (v1beta2) to the v1beta2 field.
if in.Conditions == nil {
Expand Down Expand Up @@ -1436,10 +1423,8 @@ func Convert_v1beta2_MachinePoolStatus_To_v1beta1_MachinePoolStatus(in *clusterv
}

// Move initialization to old fields
if in.Initialization != nil {
out.BootstrapReady = ptr.Deref(in.Initialization.BootstrapDataSecretCreated, false)
out.InfrastructureReady = ptr.Deref(in.Initialization.InfrastructureProvisioned, false)
}
out.BootstrapReady = ptr.Deref(in.Initialization.BootstrapDataSecretCreated, false)
out.InfrastructureReady = ptr.Deref(in.Initialization.InfrastructureProvisioned, false)

// Move new conditions (v1beta2) and replica counters to the v1beta2 field.
if in.Conditions == nil && in.ReadyReplicas == nil && in.AvailableReplicas == nil && in.UpToDateReplicas == nil {
Expand Down
21 changes: 0 additions & 21 deletions api/core/v1beta1/conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,6 @@ func hubClusterStatus(in *clusterv1.ClusterStatus, c randfill.Continue) {
}
}

// Drop empty structs with only omit empty fields.
if in.Initialization != nil {
if reflect.DeepEqual(in.Initialization, &clusterv1.ClusterInitializationStatus{}) {
in.Initialization = nil
}
}

if len(in.FailureDomains) > 0 {
in.FailureDomains = nil // Remove all pre-existing potentially invalid FailureDomains
for i := range c.Int31n(20) {
Expand Down Expand Up @@ -456,13 +449,6 @@ func hubMachineStatus(in *clusterv1.MachineStatus, c randfill.Continue) {
in.Deprecated = nil
}
}

// Drop empty structs with only omit empty fields.
if in.Initialization != nil {
if reflect.DeepEqual(in.Initialization, &clusterv1.MachineInitializationStatus{}) {
in.Initialization = nil
}
}
}

func spokeMachine(in *Machine, c randfill.Continue) {
Expand Down Expand Up @@ -703,13 +689,6 @@ func hubMachinePoolStatus(in *clusterv1.MachinePoolStatus, c randfill.Continue)
in.Deprecated.V1Beta1 = &clusterv1.MachinePoolV1Beta1DeprecatedStatus{}
}

// Drop empty structs with only omit empty fields.
if in.Initialization != nil {
if reflect.DeepEqual(in.Initialization, &clusterv1.MachinePoolInitializationStatus{}) {
in.Initialization = nil
}
}

// nil becomes &0 after hub => spoke => hub conversion
// This is acceptable as usually Replicas is set and controllers using older apiVersions are not writing MachineSet status.
if in.Replicas == nil {
Expand Down
5 changes: 2 additions & 3 deletions api/core/v1beta2/cluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -976,7 +976,7 @@ type ClusterStatus struct {
// initialization provides observations of the Cluster initialization process.
// NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial Cluster provisioning.
// +optional
Initialization *ClusterInitializationStatus `json:"initialization,omitempty"`
Initialization ClusterInitializationStatus `json:"initialization,omitempty,omitzero"`

// controlPlane groups all the observations about Cluster's ControlPlane current state.
// +optional
Expand Down Expand Up @@ -1011,8 +1011,7 @@ type ClusterStatus struct {

// ClusterInitializationStatus provides observations of the Cluster initialization process.
// NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial Cluster provisioning.

// ClusterInitializationStatus provides observations of the Cluster initialization process.
// +kubebuilder:validation:MinProperties=1
type ClusterInitializationStatus struct {
// infrastructureProvisioned is true when the infrastructure provider reports that Cluster's infrastructure is fully provisioned.
// NOTE: this field is part of the Cluster API contract, and it is used to orchestrate provisioning.
Expand Down
3 changes: 2 additions & 1 deletion api/core/v1beta2/machine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ type MachineStatus struct {
// initialization provides observations of the Machine initialization process.
// NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial Machine provisioning.
// +optional
Initialization *MachineInitializationStatus `json:"initialization,omitempty"`
Initialization MachineInitializationStatus `json:"initialization,omitempty,omitzero"`

// nodeRef will point to the corresponding Node if it exists.
// +optional
Expand Down Expand Up @@ -571,6 +571,7 @@ type MachineNodeReference struct {

// MachineInitializationStatus provides observations of the Machine initialization process.
// NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial Machine provisioning.
// +kubebuilder:validation:MinProperties=1
type MachineInitializationStatus struct {
// infrastructureProvisioned is true when the infrastructure provider reports that Machine's infrastructure is fully provisioned.
// NOTE: this field is part of the Cluster API contract, and it is used to orchestrate provisioning.
Expand Down
3 changes: 2 additions & 1 deletion api/core/v1beta2/machinepool_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ type MachinePoolStatus struct {
// initialization provides observations of the MachinePool initialization process.
// NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial MachinePool provisioning.
// +optional
Initialization *MachinePoolInitializationStatus `json:"initialization,omitempty"`
Initialization MachinePoolInitializationStatus `json:"initialization,omitempty,omitzero"`

// nodeRefs will point to the corresponding Nodes if it they exist.
// +optional
Expand Down Expand Up @@ -161,6 +161,7 @@ type MachinePoolStatus struct {

// MachinePoolInitializationStatus provides observations of the MachinePool initialization process.
// NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial MachinePool provisioning.
// +kubebuilder:validation:MinProperties=1
type MachinePoolInitializationStatus struct {
// infrastructureProvisioned is true when the infrastructure provider reports that MachinePool's infrastructure is fully provisioned.
// NOTE: this field is part of the Cluster API contract, and it is used to orchestrate provisioning.
Expand Down
18 changes: 3 additions & 15 deletions api/core/v1beta2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading