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
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,10 @@ gogenerate-crd-creator: generate
go generate ./tools/csv-merger
go generate ./tools/manifest-templator

generate:
generate-featuregate-comment:
go run ./tools/feature-gate-doc/

generate: generate-featuregate-comment
./hack/generate.sh

generate-doc: build-docgen
Expand Down Expand Up @@ -367,6 +370,7 @@ push-builder-image: retag-builder-image
deploy_fake_kubedescheduler \
build-docgen \
gogenerate \
generate-featuregate-comment \
generate \
generate-doc \
validate-no-offensive-lang \
Expand Down
3 changes: 2 additions & 1 deletion api/addtoscheme_hco_v1beta1.go → api/addtoscheme_hco.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package api

import (
hcov1 "github.com/kubevirt/hyperconverged-cluster-operator/api/v1"
hcov1beta1 "github.com/kubevirt/hyperconverged-cluster-operator/api/v1beta1"
)

func init() {
// Register the types with the Scheme so the components can map objects to GroupVersionKinds and back
AddToSchemes = append(AddToSchemes, hcov1beta1.SchemeBuilder.AddToScheme)
AddToSchemes = append(AddToSchemes, hcov1.AddToScheme, hcov1beta1.AddToScheme)
}
7 changes: 7 additions & 0 deletions api/v1/conversion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package v1

import "sigs.k8s.io/controller-runtime/pkg/conversion"

var _ conversion.Hub = &HyperConverged{}

func (*HyperConverged) Hub() { /* no implementation. Just to satisfy the Hub interface */ }
5 changes: 5 additions & 0 deletions api/v1/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Package v1 contains API Schema definitions for the hco v1 API group
// +k8s:deepcopy-gen=package,register
// +k8s:defaulter-gen=TypeMeta
// +groupName=hco.kubevirt.io
package v1
4 changes: 4 additions & 0 deletions api/v1/featuregates/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Package featuregates contains API Schema definitions for the feature gates in the hco v1 API group
// +k8s:deepcopy-gen=package,register
// + group Name=hco.kubevirt.io
package featuregates
125 changes: 125 additions & 0 deletions api/v1/featuregates/feature_gates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package featuregates

import (
"encoding/json"
"slices"
"strings"

"k8s.io/utils/ptr"
)

type FeatureGateState string

const (
Enabled FeatureGateState = "Enabled"
Disabled FeatureGateState = "Disabled"
)

// FeatureGate is an optional feature gate to enable or disable a new feature that is not generally available yet.
// +k8s:conversion-gen=false
// +k8s:openapi-gen=true
type FeatureGate struct {
// Name is the feature gate name
Name string `json:"name"`

// State determines if the feature gate is enabled ("Enabled"), or disabled ("False"). The default value is "Disabled".
// +kubebuilder:validation:Enum="Enabled";"Disabled"
State *FeatureGateState `json:"state,omitempty"`
}

func (fg FeatureGate) MarshalJSON() ([]byte, error) {
builder := &strings.Builder{}
builder.WriteString(`{"name":"`)
builder.WriteString(fg.Name)
builder.WriteByte('"')

if fg.State != nil && *fg.State == Disabled {
builder.WriteString(`,"state":"`)
builder.WriteString(string(Disabled))
builder.WriteByte('"')
}
builder.WriteByte('}')

return []byte(builder.String()), nil
}

func (fg *FeatureGate) UnmarshalJSON(bytes []byte) error {
type plain FeatureGate
err := json.Unmarshal(bytes, (*plain)(fg))
if err != nil {
return err
}

if fg.State == nil {
fg.State = ptr.To(Enabled)
}

return nil
}

// HyperConvergedFeatureGates is a set of optional feature gates to enable or disable new features that are not
// generally available yet.
// Add a new FeatureGate Object to this set, to enable a feature that is disabled by default, or to disable a feature
// that is enabled by default.
//
// +k8s:openapi-gen=true
// +k8s:conversion-gen=false
// +k8s:deepcopy-gen=false
type HyperConvergedFeatureGates []FeatureGate

func (fgs *HyperConvergedFeatureGates) Add(fg FeatureGate) {
idx := slices.IndexFunc(*fgs, func(item FeatureGate) bool {
return item.Name == fg.Name
})

if idx == -1 {
*fgs = append(*fgs, fg)
return
}

(*fgs)[idx].State = fg.State
}

func (fgs *HyperConvergedFeatureGates) Enable(name string) {
fgs.set(name, Enabled)
}

func (fgs *HyperConvergedFeatureGates) Disable(name string) {
fgs.set(name, Disabled)
}

func (fgs *HyperConvergedFeatureGates) set(name string, enabled FeatureGateState) {
idx := slices.IndexFunc(*fgs, func(item FeatureGate) bool {
return item.Name == name
})

if idx == -1 {
*fgs = append(*fgs, FeatureGate{Name: name, State: &enabled})
return
}

(*fgs)[idx].State = &enabled
}

func (fgs *HyperConvergedFeatureGates) IsEnabled(name string) bool {
isEnabled, isFinal := featureGatesDetails.isEnabled(name)

if isFinal {
return isEnabled
}

enabled := Disabled
if isEnabled {
enabled = Enabled
}

idx := slices.IndexFunc(*fgs, func(fg FeatureGate) bool {
return fg.Name == name
})

if idx > -1 {
enabled = ptr.Deref((*fgs)[idx].State, Enabled)
}

return enabled == Enabled
}
131 changes: 131 additions & 0 deletions api/v1/featuregates/feature_gates_details.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package featuregates

type Phase int

const (
UnknownFeatureGate Phase = iota
PhaseAlpha
PhaseBeta
PhaseGA
PhaseDeprecated
)

type featureGateDetails struct {
phase Phase
description string
}

type featureGatesDetailsType map[string]featureGateDetails

var featureGatesDetails = featureGatesDetailsType{
"downwardMetrics": {
phase: PhaseAlpha,
description: "Allow to expose a limited set of host metrics to guests.",
},
"withHostPassthroughCPU": {
phase: PhaseDeprecated,
},
"enableCommonBootImageImport": {
phase: PhaseDeprecated,
description: "This feature gate is ignored. Use spec.enableCommonBootImageImport field instead",
},
"deployTektonTaskResources": {
phase: PhaseDeprecated,
},
"deployVmConsoleProxy": {
phase: PhaseDeprecated,
},
"deployKubeSecondaryDNS": {
phase: PhaseAlpha,
description: "Deploy KubeSecondaryDNS by CNAO",
},
"deployKubevirtIpamController": {
phase: PhaseDeprecated,
},
"nonRoot": {
phase: PhaseDeprecated,
},
"disableMDevConfiguration": {
phase: PhaseAlpha,
description: "Disable mediated devices handling on KubeVirt",
},
"persistentReservation": {
phase: PhaseAlpha,
description: "Enable persistent reservation of a LUN through the SCSI Persistent Reserve commands on Kubevirt." +
"In order to issue privileged SCSI ioctls, the VM requires activation of the persistent reservation flag. " +
"Once this feature gate is enabled, then the additional container with the qemu-pr-helper is deployed inside the virt-handler pod. " +
"Enabling (or removing) the feature gate causes the redeployment of the virt-handler pod.",
},
"enableManagedTenantQuota": {
phase: PhaseDeprecated,
},
"autoResourceLimits": {
phase: PhaseDeprecated,
},
"alignCPUs": {
phase: PhaseAlpha,
description: "Enable KubeVirt to request up to two additional dedicated CPUs in order to complete the total CPU count to an even parity when using emulator thread isolation. " +
"Note: this feature is in Developer Preview.",
},
"enableApplicationAwareQuota": {
phase: PhaseDeprecated,
description: "This feature gate is ignored. Use spec.enableApplicationAwareQuota field instead",
},
"primaryUserDefinedNetworkBinding": {
phase: PhaseDeprecated,
},
"enableMultiArchBootImageImport": {
phase: PhaseAlpha,
description: "allows the HCO to run on heterogeneous clusters with different CPU architectures. " +
"Enabling this feature gate will allow the HCO to create Golden Images for different CPU architectures.",
},
"decentralizedLiveMigration": {
phase: PhaseAlpha,
description: "enables the decentralized live migration (cross-cluster migration) feature." +
"This feature allows live migration of VirtualMachineInstances between different clusters. " +
"Note: This feature is in Developer Preview.",
},
"declarativeHotplugVolumes": {
phase: PhaseAlpha,
description: "enables the use of the declarative volume hotplug feature in KubeVirt. " +
`When enabled, the "DeclarativeHotplugVolumes" feature gate is enabled in the KubeVirt CR, instead of the "HotplugVolumes" feature gate. ` +
`When disabled, the "HotplugVolumes" feature gate is enabled (default behavior).`,
},
"videoConfig": {
phase: PhaseBeta,
description: "allows users to configure video device types for their virtual machines. " +
"This can be useful for workloads that require specific video capabilities or architectures.",
},
"objectGraph": {
phase: PhaseAlpha,
description: "enables the ObjectGraph VM and VMI subresource in KubeVirt. " +
"This subresource returns a structured list of k8s objects that are related to the specified VM or VMI, enabling better dependency tracking.",
},
}

func (fgs featureGatesDetailsType) isEnabled(name string) (isEnabled bool, isFinal bool) {
details, ok := fgs[name]
if !ok { // unsupported feature gate, even if it is in the featureGate list
return false, true
}

switch details.phase {
case PhaseGA:
isFinal = true
isEnabled = true
case PhaseDeprecated:
isFinal = true
isEnabled = false
case PhaseAlpha:
isFinal = false
isEnabled = false
case PhaseBeta:
isFinal = false
isEnabled = true
default:
isFinal = true
isEnabled = false
}

return isEnabled, isFinal
}
Loading