Skip to content
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

POC: using go 1.23 iterators #3257

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
9 changes: 6 additions & 3 deletions controllers/operands/cdi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"k8s.io/client-go/tools/reference"
"k8s.io/utils/ptr"

"github.com/kubevirt/hyperconverged-cluster-operator/pkg/stream"
cdiv1beta1 "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1"

"sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -103,9 +104,11 @@ var _ = Describe("CDI Operand", func() {
outdatedResource, err := NewCDI(hco)
Expect(err).ToNot(HaveOccurred())
expectedLabels := maps.Clone(outdatedResource.Labels)
for k, v := range expectedLabels {
outdatedResource.Labels[k] = "wrong_" + v
}

outdatedResource.Labels = maps.Collect(stream.Transform22(maps.All(outdatedResource.Labels), func(k, v string) (string, string) {
return k, "wrong_" + v
}))

outdatedResource.Labels[userLabelKey] = userLabelValue

cl := commontestutils.InitClient([]client.Object{hco, outdatedResource})
Expand Down
14 changes: 7 additions & 7 deletions controllers/operands/dashboard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"maps"
"os"
"path"
"slices"
"strings"
"time"

Expand All @@ -17,6 +18,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log/zap"

"github.com/kubevirt/hyperconverged-cluster-operator/controllers/commontestutils"
"github.com/kubevirt/hyperconverged-cluster-operator/pkg/stream"
hcoutil "github.com/kubevirt/hyperconverged-cluster-operator/pkg/util"
)

Expand Down Expand Up @@ -192,13 +194,11 @@ var _ = Describe("Dashboard tests", func() {
Expect(cmList.Items[0].Name).To(Equal("grafana-dashboard-kubevirt-top-consumers"))
})

expectedLabels := make(map[string]map[string]string)

By("getting opinionated labels", func() {
for _, cm := range cmList.Items {
expectedLabels[cm.Name] = maps.Clone(cm.Labels)
}
})
By("getting opinionated labels")
mapNameLabels := func(cm corev1.ConfigMap) (string, map[string]string) {
return cm.Name, maps.Clone(cm.Labels)
}
expectedLabels := maps.Collect(stream.Transform2(slices.Values(cmList.Items), mapNameLabels))

By("altering the cm objects", func() {
for _, foundResource := range cmList.Items {
Expand Down
10 changes: 6 additions & 4 deletions controllers/operands/imageStream.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package operands
import (
"errors"
"fmt"
"maps"
"os"
"path/filepath"
"reflect"
"slices"
"strings"

log "github.com/go-logr/logr"
Expand All @@ -18,6 +20,7 @@ import (

hcov1beta1 "github.com/kubevirt/hyperconverged-cluster-operator/api/v1beta1"
"github.com/kubevirt/hyperconverged-cluster-operator/controllers/common"
"github.com/kubevirt/hyperconverged-cluster-operator/pkg/stream"
"github.com/kubevirt/hyperconverged-cluster-operator/pkg/util"
)

Expand Down Expand Up @@ -138,10 +141,9 @@ type isHooks struct {
}

func newIsHook(required *imagev1.ImageStream, origNS string) *isHooks {
tags := make(map[string]imagev1.TagReference)
for _, tag := range required.Spec.Tags {
tags[tag.Name] = tag
}
tags := maps.Collect(stream.Transform22(slices.All(required.Spec.Tags), func(_ int, tag imagev1.TagReference) (string, imagev1.TagReference) {
return tag.Name, tag
}))
return &isHooks{required: required, tags: tags, originalNS: origNS}
}

Expand Down
59 changes: 32 additions & 27 deletions controllers/operands/kubevirt.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os"
"path"
"reflect"
"slices"
"strconv"
"strings"

Expand All @@ -26,6 +27,7 @@ import (

hcov1beta1 "github.com/kubevirt/hyperconverged-cluster-operator/api/v1beta1"
"github.com/kubevirt/hyperconverged-cluster-operator/controllers/common"
"github.com/kubevirt/hyperconverged-cluster-operator/pkg/stream"
hcoutil "github.com/kubevirt/hyperconverged-cluster-operator/pkg/util"
kubevirtcorev1 "kubevirt.io/api/core/v1"
)
Expand Down Expand Up @@ -397,16 +399,17 @@ func hcWorkloadUpdateStrategyToKv(hcObject *hcov1beta1.HyperConvergedWorkloadUpd
}

if hcObject.BatchEvictionSize != nil {
kvObject.BatchEvictionSize = new(int)
*kvObject.BatchEvictionSize = *hcObject.BatchEvictionSize
kvObject.BatchEvictionSize = ptr.To(*hcObject.BatchEvictionSize)
}

if size := len(hcObject.WorkloadUpdateMethods); size > 0 {
kvObject.WorkloadUpdateMethods = make([]kubevirtcorev1.WorkloadUpdateMethod, size)
for i, updateMethod := range hcObject.WorkloadUpdateMethods {
kvObject.WorkloadUpdateMethods[i] = kubevirtcorev1.WorkloadUpdateMethod(updateMethod)
}
}
kvObject.WorkloadUpdateMethods = slices.Collect(
stream.Transform(
slices.Values(hcObject.WorkloadUpdateMethods),
func(method string) kubevirtcorev1.WorkloadUpdateMethod {
return kubevirtcorev1.WorkloadUpdateMethod(method)
},
),
)
}

return kvObject
Expand Down Expand Up @@ -537,16 +540,19 @@ func getNetworkBindings(hcoNetworkBindings map[string]kubevirtcorev1.InterfaceBi
}

func getObsoleteCPUConfig(hcObsoleteCPUConf *hcov1beta1.HyperConvergedObsoleteCPUs) (map[string]bool, string) {
obsoleteCPUModels := make(map[string]bool)
for _, cpu := range hardcodedObsoleteCPUModels {
obsoleteCPUModels[cpu] = true
transformFunc := func(cpu string) (string, bool) {
return cpu, true
}

obsoleteCPUModels := maps.Collect(stream.Transform2(slices.Values(hardcodedObsoleteCPUModels), transformFunc))

minCPUModel := ""

if hcObsoleteCPUConf != nil {
for _, cpu := range hcObsoleteCPUConf.CPUModels {
obsoleteCPUModels[cpu] = true
}
maps.Insert(
obsoleteCPUModels,
stream.Transform2(slices.Values(hcObsoleteCPUConf.CPUModels), transformFunc),
)

minCPUModel = hcObsoleteCPUConf.MinCPUModel
}
Expand Down Expand Up @@ -651,21 +657,20 @@ func toKvUSBHostDevices(hcoUSBHostdevices []hcov1beta1.USBHostDevice) []kubevirt
}

func toKvMediatedDevices(hcoMediatedDevices []hcov1beta1.MediatedHostDevice) []kubevirtcorev1.MediatedHostDevice {
if len(hcoMediatedDevices) > 0 {
mediatedDevices := make([]kubevirtcorev1.MediatedHostDevice, 0, len(hcoMediatedDevices))
for _, hcoMediatedHostDevice := range hcoMediatedDevices {
if !hcoMediatedHostDevice.Disabled {
mediatedDevices = append(mediatedDevices, kubevirtcorev1.MediatedHostDevice{
MDEVNameSelector: hcoMediatedHostDevice.MDEVNameSelector,
ResourceName: hcoMediatedHostDevice.ResourceName,
ExternalResourceProvider: hcoMediatedHostDevice.ExternalResourceProvider,
})
}
}

return mediatedDevices
filter := func(dev hcov1beta1.MediatedHostDevice) bool {
return !dev.Disabled
}
return nil

mapHCO2KV := func(hcoDev hcov1beta1.MediatedHostDevice) kubevirtcorev1.MediatedHostDevice {
return kubevirtcorev1.MediatedHostDevice{
MDEVNameSelector: hcoDev.MDEVNameSelector,
ResourceName: hcoDev.ResourceName,
ExternalResourceProvider: hcoDev.ExternalResourceProvider,
}
}

return slices.Collect(stream.Transform(stream.Filter(slices.Values(hcoMediatedDevices), filter), mapHCO2KV))
}

func hcLiveMigrationToKv(lm hcov1beta1.LiveMigrationConfigurations) (*kubevirtcorev1.MigrationConfiguration, error) {
Expand Down
13 changes: 3 additions & 10 deletions controllers/operands/operand.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"os"
"slices"
"strings"

jsonpatch "github.com/evanphx/json-patch/v5"
Expand All @@ -18,6 +19,7 @@ import (

hcov1beta1 "github.com/kubevirt/hyperconverged-cluster-operator/api/v1beta1"
"github.com/kubevirt/hyperconverged-cluster-operator/controllers/common"
"github.com/kubevirt/hyperconverged-cluster-operator/pkg/stream"
hcoutil "github.com/kubevirt/hyperconverged-cluster-operator/pkg/util"
)

Expand Down Expand Up @@ -443,14 +445,5 @@ func osConditionToK8s(condition conditionsv1.Condition) metav1.Condition {
}

func osConditionsToK8s(conditions []conditionsv1.Condition) []metav1.Condition {
if len(conditions) == 0 {
return nil
}

newCond := make([]metav1.Condition, len(conditions))
for i, c := range conditions {
newCond[i] = osConditionToK8s(c)
}

return newCond
return slices.Collect(stream.Transform(slices.Values(conditions), osConditionToK8s))
}
68 changes: 68 additions & 0 deletions pkg/stream/stream.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package stream

import "iter"

// Filter creates an iterator that filters the elements of a sequence (iterator), by a given function filterFn
// that for each element of type V1, returns true to include the element in the result sequence, or false, to
// skip the element.
// The Filter function returns an iter.Seq sequence.
func Filter[T any](seq iter.Seq[T], filterFn func(T) bool) iter.Seq[T] {
return func(yield func(T) bool) {
for v := range seq {
if filterFn(v) {
if !yield(v) {
return
}
}
}
}
}

// Transform creates an iterator that modifies the elements of a sequence (iterator), by a given function transFn
// that for each element of type V1, returns a value V2.
// The Transform function returns an iter.Seq sequence.
func Transform[V1, V2 any](seq iter.Seq[V1], transFn func(V1) V2) iter.Seq[V2] {
return func(yield func(V2) bool) {
for v1 := range seq {
if !yield(transFn(v1)) {
return
}
}
}
}

// Transform2 creates an iterator that modifies the elements of a sequence (iterator), by a given function transFn
// that for each element of type V1, returns a key K and a value V2.
// The Transform2 function returns an iter.Seq2 sequence.
func Transform2[K, V1, V2 any](seq iter.Seq[V1], transFn func(V1) (K, V2)) iter.Seq2[K, V2] {
return func(yield func(K, V2) bool) {
for v1 := range seq {
if !yield(transFn(v1)) {
return
}
}
}
}

// Transform22 creates an iterator that modifies the elements of a sequence (iterator), by a given function transFn
// that for each pair of K1, V1, returns a pair of K2, V2.
// The Transform22 function returns an iter.Seq2 sequence.
func Transform22[K1, V1, K2, V2 any](seq iter.Seq2[K1, V1], transFn func(K1, V1) (K2, V2)) iter.Seq2[K2, V2] {
return func(yield func(K2, V2) bool) {
for k1, v1 := range seq {
if !yield(transFn(k1, v1)) {
return
}
}
}
}

func Iter2Values[K, V any](seq2 iter.Seq2[K, V]) iter.Seq[V] {
return func(yield func(V) bool) {
for _, v := range seq2 {
if !yield(v) {
return
}
}
}
}
75 changes: 75 additions & 0 deletions pkg/stream/stream_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package stream

import (
"maps"
"slices"
"strconv"
"testing"

"github.com/onsi/gomega"
)

func TestTransform(t *testing.T) {
g := gomega.NewGomegaWithT(t)
var s []int
g.Expect(slices.Collect(Transform(slices.Values(s), strconv.Itoa))).To(gomega.BeNil())

s = []int{1, 2, 3}

res := slices.Collect(Transform(slices.Values(s), strconv.Itoa))
g.Expect(res).To(gomega.HaveLen(3))
g.Expect(res).To(gomega.ContainElements("1", "2", "3"))
}

func TestTransform2(t *testing.T) {
g := gomega.NewGomegaWithT(t)
var s []int
trans2Func := func(i int) (int, string) {
return i, strconv.Itoa(i)
}

g.Expect(maps.Collect(Transform2(slices.Values(s), trans2Func))).To(gomega.BeEmpty())

s = []int{1, 2, 3}

res := maps.Collect(Transform2(slices.Values(s), trans2Func))
g.Expect(res).To(gomega.HaveLen(3))
g.Expect(res).To(gomega.HaveKeyWithValue(1, "1"))
g.Expect(res).To(gomega.HaveKeyWithValue(2, "2"))
g.Expect(res).To(gomega.HaveKeyWithValue(3, "3"))
}

func TestTransform22(t *testing.T) {
g := gomega.NewGomegaWithT(t)
var s []int
trans22Func := func(i, v int) (int, string) {
return i * i, strconv.Itoa(v)
}

g.Expect(maps.Collect(Transform22(slices.All(s), trans22Func))).To(gomega.BeEmpty())

s = []int{11, 22, 33, 44}

res := maps.Collect(Transform22(slices.All(s), trans22Func))
g.Expect(res).To(gomega.HaveLen(4))
g.Expect(res).To(gomega.HaveKeyWithValue(0, "11"))
g.Expect(res).To(gomega.HaveKeyWithValue(1, "22"))
g.Expect(res).To(gomega.HaveKeyWithValue(4, "33"))
g.Expect(res).To(gomega.HaveKeyWithValue(9, "44"))

res2 := slices.Collect(Iter2Values(Transform22(slices.All(s), trans22Func)))
g.Expect(res2).To(gomega.HaveLen(4))
g.Expect(res2).To(gomega.ContainElements("11", "22", "33", "44"))
}

func TestFilter(t *testing.T) {
g := gomega.NewGomegaWithT(t)
var s []int

isEven := func(i int) bool { return i&1 == 0 }
g.Expect(slices.Collect(Filter(slices.Values(s), isEven))).To(gomega.BeNil())
s = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
res := slices.Collect(Filter(slices.Values(s), isEven))
g.Expect(res).To(gomega.HaveLen(4))
g.Expect(res).To(gomega.ContainElements(2, 4, 6, 8))
}
Loading
Loading