Skip to content
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
22 changes: 13 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ DO=eval
export JOB_TYPE=prow
endif

sanity: generate gogenerate gogenerate-crd-creator generate-doc validate-no-offensive-lang goimport lint-metrics lint-monitoring
sanity: generate gogenerate prepare-tools-crd generate-doc validate-no-offensive-lang goimport lint-metrics lint-monitoring
go version
go fmt ./...
go mod tidy -v
Expand Down Expand Up @@ -80,7 +80,7 @@ build-manifest-splitter:
build-webhook: $(SOURCES) ## Build binary from source
go build -ldflags="${LDFLAGS}" -o _out/hyperconverged-cluster-webhook ./cmd/hyperconverged-cluster-webhook

build-manifests: gogenerate-crd-creator build-crd-creator build-csv-merger build-manifest-splitter build-manifest-templator
build-manifests: prepare-tools-crd build-csv-merger build-manifest-splitter build-manifest-templator
DUMP_NETWORK_POLICIES=$(DUMP_NETWORK_POLICIES) ./hack/build-manifests.sh

build-manifests-prev:
Expand Down Expand Up @@ -118,10 +118,10 @@ container-build: container-build-operator container-build-webhook container-buil

build-push-multi-arch-images: build-push-multi-arch-operator-image build-push-multi-arch-webhook-image build-push-multi-arch-functest-image build-push-multi-arch-artifacts-server

container-build-operator: gogenerate gogenerate-crd-creator
container-build-operator: gogenerate prepare-tools-crd
. "hack/cri-bin.sh" && $$CRI_BIN build --platform=linux/$(ARCH) -f build/Dockerfile -t $(IMAGE_REGISTRY)/$(OPERATOR_IMAGE):$(IMAGE_TAG) --build-arg git_sha=$(SHA) .

build-push-multi-arch-operator-image: gogenerate gogenerate-crd-creator
build-push-multi-arch-operator-image: gogenerate prepare-tools-crd
IMAGE_NAME=$(IMAGE_REGISTRY)/$(OPERATOR_IMAGE):$(IMAGE_TAG) SHA=SHA DOCKER_FILE=build/Dockerfile ./hack/build-push-multi-arch-images.sh

container-build-webhook:
Expand Down Expand Up @@ -245,9 +245,13 @@ bump-kubevirtci:
gogenerate: generate
go generate ./pkg/upgradepatch

gogenerate-crd-creator: generate
go generate ./tools/csv-merger
go generate ./tools/manifest-templator
generate-crd: generate build-crd-creator
./_out/crd-creator --output-file=config/crd/bases/hco.kubevirt.io_hyperconvergeds.yaml
@echo "the CRD file was generated in config/crd/bases/hco.kubevirt.io_hyperconvergeds.yaml"

prepare-tools-crd: generate-crd
cp config/crd/bases/hco.kubevirt.io_hyperconvergeds.yaml ./tools/csv-merger/generated-crd.yaml
cp config/crd/bases/hco.kubevirt.io_hyperconvergeds.yaml ./tools/manifest-templator/generated-crd.yaml

generate: generate-feature-gates
./hack/generate.sh
Expand All @@ -270,10 +274,10 @@ help: ## Show this help screen
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
@echo ''

test-unit: gogenerate gogenerate-crd-creator
test-unit: gogenerate prepare-tools-crd
./hack/unit-test.sh

test-unit-coverage: gogenerate gogenerate-crd-creator
test-unit-coverage: gogenerate prepare-tools-crd
./hack/unit-test-coverage.sh

test-fuzz-api-conversion: generate
Expand Down
20 changes: 12 additions & 8 deletions api/v1/featuregates/feature_gates.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const (
// +k8s:openapi-gen=true
type FeatureGate struct {
// Name is the feature gate name
// +kubebuilder:validation:MaxLength=256
Name string `json:"name"`

// State determines if the feature gate is Enabled, or Disabled. The default value is Enabled.
Expand All @@ -36,9 +37,9 @@ func (fg FeatureGate) MarshalJSON() ([]byte, error) {
builder.WriteString(fg.Name)
builder.WriteByte('"')

if fg.State != nil && *fg.State == Disabled {
if fg.State != nil {
builder.WriteString(`,"state":"`)
builder.WriteString(string(Disabled))
builder.WriteString(string(*fg.State))
builder.WriteByte('"')
}
builder.WriteByte('}')
Expand All @@ -54,7 +55,7 @@ func (fg *FeatureGate) UnmarshalJSON(bytes []byte) error {
}

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

return nil
Expand All @@ -68,6 +69,8 @@ func (fg *FeatureGate) UnmarshalJSON(bytes []byte) error {
// +k8s:openapi-gen=true
// +k8s:conversion-gen=false
// +k8s:deepcopy-gen=false
// +kubebuilder:validation:MaxItems=64
// +kubebuilder:validation:XValidation:rule="self.all(x, self.exists_one(y, x.name.lowerAscii() == y.name.lowerAscii()))",message="feature gate names must be unique (case-insensitive)"
type HyperConvergedFeatureGates []FeatureGate

// Enable enables a feature gate by its name
Expand All @@ -81,7 +84,7 @@ func (fgs *HyperConvergedFeatureGates) Disable(name string) {
}

func (fgs *HyperConvergedFeatureGates) set(name string, enabled State) {
idx := fgs.index(name)
idx := fgs.Index(name)

if idx == -1 {
*fgs = append(*fgs, FeatureGate{Name: name, State: &enabled})
Expand Down Expand Up @@ -118,7 +121,7 @@ func (fgs *HyperConvergedFeatureGates) IsEnabled(name string) bool {
return false
}

if idx := fgs.index(name); idx > -1 {
if idx := fgs.Index(name); idx > -1 {
state = ptr.Deref((*fgs)[idx].State, Enabled)
}

Expand All @@ -127,7 +130,7 @@ func (fgs *HyperConvergedFeatureGates) IsEnabled(name string) bool {

// IsExplicitlyEnabled checks if a feature gate is explicitly set in the feature gate list
func (fgs *HyperConvergedFeatureGates) IsExplicitlyEnabled(name string) (enabled bool, found bool) {
idx := fgs.index(name)
idx := fgs.Index(name)

if idx < 0 {
return false, false
Expand All @@ -136,8 +139,9 @@ func (fgs *HyperConvergedFeatureGates) IsExplicitlyEnabled(name string) (enabled
return ptr.Deref((*fgs)[idx].State, Enabled) == Enabled, true
}

func (fgs *HyperConvergedFeatureGates) index(name string) int {
func (fgs *HyperConvergedFeatureGates) Index(name string) int {
name = strings.ToLower(name)
return slices.IndexFunc(*fgs, func(fg FeatureGate) bool {
return fg.Name == name
return strings.ToLower(fg.Name) == name
})
}
19 changes: 13 additions & 6 deletions api/v1/featuregates/feature_gates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ var _ = Describe("FeatureGate", func() {

jsonBytes, err := json.Marshal(fg)
Expect(err).ToNot(HaveOccurred())
Expect(jsonBytes).To(MatchJSON(`{"name":"fgName"}`))
Expect(jsonBytes).To(MatchJSON(`{"name":"fgName", "state": "Enabled"}`))
})

It("should marshal a disabled feature gate", func() {
Expand All @@ -49,7 +49,7 @@ var _ = Describe("FeatureGate", func() {

jsonBytes, err := json.Marshal(fg)
Expect(err).ToNot(HaveOccurred())
Expect(jsonBytes).To(MatchJSON(`{"name":"fgName"}`))
Expect(jsonBytes).To(MatchJSON(`{"name":"fgName", "state": "Enabled"}`))
})

It("should marshal a disabled feature gate pointer", func() {
Expand Down Expand Up @@ -90,7 +90,7 @@ var _ = Describe("FeatureGate", func() {

jsonBytes, err := json.Marshal(fgs)
Expect(err).ToNot(HaveOccurred())
Expect(jsonBytes).To(MatchJSON(`[{"name":"noEnabledField"}, {"name": "enabledFG"}, {"name": "disabledFG", "state": "Disabled"}]`))
Expect(jsonBytes).To(MatchJSON(`[{"name":"noEnabledField"}, {"name": "enabledFG", "state": "Enabled"}, {"name": "disabledFG", "state": "Disabled"}]`))
})

It("should yaml marshal a FG array", func() {
Expand All @@ -112,6 +112,7 @@ var _ = Describe("FeatureGate", func() {
Expect(err).ToNot(HaveOccurred())
Expect(yamlBytes).To(MatchYAML(`- name: noEnabledField
- name: enabledFG
state: Enabled
- state: Disabled
name: disabledFG`,
))
Expand Down Expand Up @@ -185,15 +186,21 @@ var _ = Describe("Feature Gates", func() {

Entry("known alpha FG; in list; enabled", featuregates.HyperConvergedFeatureGates{{Name: "downwardMetrics", State: new(featuregates.Enabled)}}, "downwardMetrics", BeTrue()),
Entry("known alpha FG; in list; disabled", featuregates.HyperConvergedFeatureGates{{Name: "downwardMetrics", State: new(featuregates.Disabled)}}, "downwardMetrics", BeFalse()),
Entry("known alpha FG; not in list; disabled", featuregates.HyperConvergedFeatureGates{{Name: "deployKubeSecondaryDNS", State: new(featuregates.Enabled)}}, "downwardMetrics", BeFalse()),
Entry("known alpha FG; not in list;", featuregates.HyperConvergedFeatureGates{{Name: "deployKubeSecondaryDNS", State: new(featuregates.Enabled)}}, "downwardMetrics", BeFalse()),

Entry("known alpha FG with different casing; in list; enabled", featuregates.HyperConvergedFeatureGates{{Name: "DownwardMetricS", State: new(featuregates.Enabled)}}, "downwardMetrics", BeTrue()),
Entry("known alpha FG with different casing; in list; disabled", featuregates.HyperConvergedFeatureGates{{Name: "DownwardMetricS", State: new(featuregates.Disabled)}}, "downwardMetrics", BeFalse()),

Entry("known beta FG; in list; enabled", featuregates.HyperConvergedFeatureGates{{Name: "declarativeHotplugVolumes", State: new(featuregates.Enabled)}}, "declarativeHotplugVolumes", BeTrue()),
Entry("known beta FG; in list; disabled", featuregates.HyperConvergedFeatureGates{{Name: "declarativeHotplugVolumes", State: new(featuregates.Disabled)}}, "declarativeHotplugVolumes", BeFalse()),
Entry("known beta FG; not in list; disabled", featuregates.HyperConvergedFeatureGates{{Name: "deployKubeSecondaryDNS", State: new(featuregates.Enabled)}}, "declarativeHotplugVolumes", BeTrue()),
Entry("known beta FG; not in list;", featuregates.HyperConvergedFeatureGates{{Name: "deployKubeSecondaryDNS", State: new(featuregates.Enabled)}}, "declarativeHotplugVolumes", BeTrue()),

Entry("known beta FG with different casing; in list; enabled", featuregates.HyperConvergedFeatureGates{{Name: "DeclarativeHotplugVolumeS", State: new(featuregates.Enabled)}}, "declarativeHotplugVolumes", BeTrue()),
Entry("known beta FG with different casing; in list; disabled", featuregates.HyperConvergedFeatureGates{{Name: "DeclarativeHotplugVolumeS", State: new(featuregates.Disabled)}}, "declarativeHotplugVolumes", BeFalse()),

Entry("known deprecated FG; in list; enabled", featuregates.HyperConvergedFeatureGates{{Name: "withHostPassthroughCPU", State: new(featuregates.Enabled)}}, "withHostPassthroughCPU", BeFalse()),
Entry("known deprecated FG; in list; disabled", featuregates.HyperConvergedFeatureGates{{Name: "withHostPassthroughCPU", State: new(featuregates.Disabled)}}, "withHostPassthroughCPU", BeFalse()),
Entry("known deprecated FG; not in list; disabled", featuregates.HyperConvergedFeatureGates{{Name: "deployKubeSecondaryDNS", State: new(featuregates.Enabled)}}, "withHostPassthroughCPU", BeFalse()),
Entry("known deprecated FG; not in list;", featuregates.HyperConvergedFeatureGates{{Name: "deployKubeSecondaryDNS", State: new(featuregates.Enabled)}}, "withHostPassthroughCPU", BeFalse()),
)

Context("check Enable", func() {
Expand Down
32 changes: 20 additions & 12 deletions api/v1beta1/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,25 @@ import (
kubevirtv1 "kubevirt.io/api/core/v1"

hcov1 "github.com/kubevirt/hyperconverged-cluster-operator/api/v1"
hcov1fg "github.com/kubevirt/hyperconverged-cluster-operator/api/v1/featuregates"
)

const v1OnlyFieldAnnotation = APIVersionGroup + "/v1-only-fields"

const DisableMDevConfigurationFG = "disableMDevConfiguration"

type v1OnlyFields struct {
DeployNetworkResourcesInjector *bool `json:"deployNetworkResourcesInjector,omitempty"`
MDevConfigEnable *bool `json:"mdevConfigEnable,omitempty"`
DisableMDevConfigurationFG *bool `json:"disableMDevConfigurationFG,omitempty"`
DeployNetworkResourcesInjector *bool `json:"deployNetworkResourcesInjector,omitempty"`
MDevConfigEnable *bool `json:"mdevConfigEnable,omitempty"`
DisableMDevConfigurationFG *bool `json:"disableMDevConfigurationFG,omitempty"`
FeatureGates hcov1fg.HyperConvergedFeatureGates `json:"featureGates,omitempty"`
}

func (fields *v1OnlyFields) isEmpty() bool {
return fields.DeployNetworkResourcesInjector == nil &&
fields.MDevConfigEnable == nil &&
fields.DisableMDevConfigurationFG == nil &&
fields.FeatureGates == nil
}

// Implement the conversion.Convertible interface, to be used in the conversion webhook.
Expand Down Expand Up @@ -547,12 +556,8 @@ func restoreV1OnlyFields(src *HyperConverged, dst *hcov1.HyperConverged) error {
dst.Spec.Virtualization.MediatedDevicesConfiguration.Enabled = new(*v1Fields.MDevConfigEnable)
}

if v1Fields.DisableMDevConfigurationFG != nil {
if *v1Fields.DisableMDevConfigurationFG {
dst.Spec.FeatureGates.Enable(DisableMDevConfigurationFG)
} else {
dst.Spec.FeatureGates.Disable(DisableMDevConfigurationFG)
}
for _, fg := range v1Fields.FeatureGates {
dst.Spec.FeatureGates = append(dst.Spec.FeatureGates, *fg.DeepCopy())
}

return nil
Expand All @@ -567,11 +572,14 @@ func storeV1OnlyFields(src *hcov1.HyperConverged, dst *HyperConverged) error {
v1Fields.MDevConfigEnable = src.Spec.Virtualization.MediatedDevicesConfiguration.Enabled
}

if fgEnabled, fgFound := src.Spec.FeatureGates.IsExplicitlyEnabled(DisableMDevConfigurationFG); fgFound {
v1Fields.DisableMDevConfigurationFG = new(fgEnabled)
if len(src.Spec.FeatureGates) > 0 {
v1Fields.FeatureGates = make(hcov1fg.HyperConvergedFeatureGates, len(src.Spec.FeatureGates))
for i, fg := range src.Spec.FeatureGates {
v1Fields.FeatureGates[i] = *fg.DeepCopy()
}
}

if v1Fields == (v1OnlyFields{}) {
if v1Fields.isEmpty() {
return nil
}

Expand Down
Loading