diff --git a/api/client/generated/clientset/versioned/clientset.go b/api/client/generated/clientset/versioned/clientset.go index d2a4e3bbec..e3027ebb46 100644 --- a/api/client/generated/clientset/versioned/clientset.go +++ b/api/client/generated/clientset/versioned/clientset.go @@ -23,6 +23,7 @@ import ( http "net/http" virtualizationv1alpha2 "github.com/deckhouse/virtualization/api/client/generated/clientset/versioned/typed/core/v1alpha2" + virtualizationv1alpha3 "github.com/deckhouse/virtualization/api/client/generated/clientset/versioned/typed/core/v1alpha3" discovery "k8s.io/client-go/discovery" rest "k8s.io/client-go/rest" flowcontrol "k8s.io/client-go/util/flowcontrol" @@ -31,12 +32,14 @@ import ( type Interface interface { Discovery() discovery.DiscoveryInterface VirtualizationV1alpha2() virtualizationv1alpha2.VirtualizationV1alpha2Interface + VirtualizationV1alpha3() virtualizationv1alpha3.VirtualizationV1alpha3Interface } // Clientset contains the clients for groups. type Clientset struct { *discovery.DiscoveryClient virtualizationV1alpha2 *virtualizationv1alpha2.VirtualizationV1alpha2Client + virtualizationV1alpha3 *virtualizationv1alpha3.VirtualizationV1alpha3Client } // VirtualizationV1alpha2 retrieves the VirtualizationV1alpha2Client @@ -44,6 +47,11 @@ func (c *Clientset) VirtualizationV1alpha2() virtualizationv1alpha2.Virtualizati return c.virtualizationV1alpha2 } +// VirtualizationV1alpha3 retrieves the VirtualizationV1alpha3Client +func (c *Clientset) VirtualizationV1alpha3() virtualizationv1alpha3.VirtualizationV1alpha3Interface { + return c.virtualizationV1alpha3 +} + // Discovery retrieves the DiscoveryClient func (c *Clientset) Discovery() discovery.DiscoveryInterface { if c == nil { @@ -92,6 +100,10 @@ func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, if err != nil { return nil, err } + cs.virtualizationV1alpha3, err = virtualizationv1alpha3.NewForConfigAndClient(&configShallowCopy, httpClient) + if err != nil { + return nil, err + } cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfigAndClient(&configShallowCopy, httpClient) if err != nil { @@ -114,6 +126,7 @@ func NewForConfigOrDie(c *rest.Config) *Clientset { func New(c rest.Interface) *Clientset { var cs Clientset cs.virtualizationV1alpha2 = virtualizationv1alpha2.New(c) + cs.virtualizationV1alpha3 = virtualizationv1alpha3.New(c) cs.DiscoveryClient = discovery.NewDiscoveryClient(c) return &cs diff --git a/api/client/generated/clientset/versioned/fake/clientset_generated.go b/api/client/generated/clientset/versioned/fake/clientset_generated.go index 732401580d..0976b8490f 100644 --- a/api/client/generated/clientset/versioned/fake/clientset_generated.go +++ b/api/client/generated/clientset/versioned/fake/clientset_generated.go @@ -22,6 +22,8 @@ import ( clientset "github.com/deckhouse/virtualization/api/client/generated/clientset/versioned" virtualizationv1alpha2 "github.com/deckhouse/virtualization/api/client/generated/clientset/versioned/typed/core/v1alpha2" fakevirtualizationv1alpha2 "github.com/deckhouse/virtualization/api/client/generated/clientset/versioned/typed/core/v1alpha2/fake" + virtualizationv1alpha3 "github.com/deckhouse/virtualization/api/client/generated/clientset/versioned/typed/core/v1alpha3" + fakevirtualizationv1alpha3 "github.com/deckhouse/virtualization/api/client/generated/clientset/versioned/typed/core/v1alpha3/fake" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" @@ -92,3 +94,8 @@ var ( func (c *Clientset) VirtualizationV1alpha2() virtualizationv1alpha2.VirtualizationV1alpha2Interface { return &fakevirtualizationv1alpha2.FakeVirtualizationV1alpha2{Fake: &c.Fake} } + +// VirtualizationV1alpha3 retrieves the VirtualizationV1alpha3Client +func (c *Clientset) VirtualizationV1alpha3() virtualizationv1alpha3.VirtualizationV1alpha3Interface { + return &fakevirtualizationv1alpha3.FakeVirtualizationV1alpha3{Fake: &c.Fake} +} diff --git a/api/client/generated/clientset/versioned/fake/register.go b/api/client/generated/clientset/versioned/fake/register.go index 1edca93ed7..599b8a83a9 100644 --- a/api/client/generated/clientset/versioned/fake/register.go +++ b/api/client/generated/clientset/versioned/fake/register.go @@ -20,6 +20,7 @@ package fake import ( virtualizationv1alpha2 "github.com/deckhouse/virtualization/api/core/v1alpha2" + virtualizationv1alpha3 "github.com/deckhouse/virtualization/api/core/v1alpha3" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" schema "k8s.io/apimachinery/pkg/runtime/schema" @@ -32,6 +33,7 @@ var codecs = serializer.NewCodecFactory(scheme) var localSchemeBuilder = runtime.SchemeBuilder{ virtualizationv1alpha2.AddToScheme, + virtualizationv1alpha3.AddToScheme, } // AddToScheme adds all types of this clientset into the given scheme. This allows composition diff --git a/api/client/generated/clientset/versioned/scheme/register.go b/api/client/generated/clientset/versioned/scheme/register.go index e4dd8c01b4..6e5bae592a 100644 --- a/api/client/generated/clientset/versioned/scheme/register.go +++ b/api/client/generated/clientset/versioned/scheme/register.go @@ -20,6 +20,7 @@ package scheme import ( virtualizationv1alpha2 "github.com/deckhouse/virtualization/api/core/v1alpha2" + virtualizationv1alpha3 "github.com/deckhouse/virtualization/api/core/v1alpha3" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" schema "k8s.io/apimachinery/pkg/runtime/schema" @@ -32,6 +33,7 @@ var Codecs = serializer.NewCodecFactory(Scheme) var ParameterCodec = runtime.NewParameterCodec(Scheme) var localSchemeBuilder = runtime.SchemeBuilder{ virtualizationv1alpha2.AddToScheme, + virtualizationv1alpha3.AddToScheme, } // AddToScheme adds all types of this clientset into the given scheme. This allows composition diff --git a/api/client/generated/clientset/versioned/typed/core/v1alpha3/core_client.go b/api/client/generated/clientset/versioned/typed/core/v1alpha3/core_client.go new file mode 100644 index 0000000000..667a073b12 --- /dev/null +++ b/api/client/generated/clientset/versioned/typed/core/v1alpha3/core_client.go @@ -0,0 +1,101 @@ +/* +Copyright Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha3 + +import ( + http "net/http" + + scheme "github.com/deckhouse/virtualization/api/client/generated/clientset/versioned/scheme" + corev1alpha3 "github.com/deckhouse/virtualization/api/core/v1alpha3" + rest "k8s.io/client-go/rest" +) + +type VirtualizationV1alpha3Interface interface { + RESTClient() rest.Interface + VirtualMachineClassesGetter +} + +// VirtualizationV1alpha3Client is used to interact with features provided by the virtualization.deckhouse.io group. +type VirtualizationV1alpha3Client struct { + restClient rest.Interface +} + +func (c *VirtualizationV1alpha3Client) VirtualMachineClasses() VirtualMachineClassInterface { + return newVirtualMachineClasses(c) +} + +// NewForConfig creates a new VirtualizationV1alpha3Client for the given config. +// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), +// where httpClient was generated with rest.HTTPClientFor(c). +func NewForConfig(c *rest.Config) (*VirtualizationV1alpha3Client, error) { + config := *c + setConfigDefaults(&config) + httpClient, err := rest.HTTPClientFor(&config) + if err != nil { + return nil, err + } + return NewForConfigAndClient(&config, httpClient) +} + +// NewForConfigAndClient creates a new VirtualizationV1alpha3Client for the given config and http client. +// Note the http client provided takes precedence over the configured transport values. +func NewForConfigAndClient(c *rest.Config, h *http.Client) (*VirtualizationV1alpha3Client, error) { + config := *c + setConfigDefaults(&config) + client, err := rest.RESTClientForConfigAndClient(&config, h) + if err != nil { + return nil, err + } + return &VirtualizationV1alpha3Client{client}, nil +} + +// NewForConfigOrDie creates a new VirtualizationV1alpha3Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *VirtualizationV1alpha3Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new VirtualizationV1alpha3Client for the given RESTClient. +func New(c rest.Interface) *VirtualizationV1alpha3Client { + return &VirtualizationV1alpha3Client{c} +} + +func setConfigDefaults(config *rest.Config) { + gv := corev1alpha3.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = rest.CodecFactoryForGeneratedClient(scheme.Scheme, scheme.Codecs).WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *VirtualizationV1alpha3Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/api/client/generated/clientset/versioned/typed/core/v1alpha3/doc.go b/api/client/generated/clientset/versioned/typed/core/v1alpha3/doc.go new file mode 100644 index 0000000000..cc161d1e74 --- /dev/null +++ b/api/client/generated/clientset/versioned/typed/core/v1alpha3/doc.go @@ -0,0 +1,20 @@ +/* +Copyright Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1alpha3 diff --git a/api/client/generated/clientset/versioned/typed/core/v1alpha3/fake/doc.go b/api/client/generated/clientset/versioned/typed/core/v1alpha3/fake/doc.go new file mode 100644 index 0000000000..302a814007 --- /dev/null +++ b/api/client/generated/clientset/versioned/typed/core/v1alpha3/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/api/client/generated/clientset/versioned/typed/core/v1alpha3/fake/fake_core_client.go b/api/client/generated/clientset/versioned/typed/core/v1alpha3/fake/fake_core_client.go new file mode 100644 index 0000000000..e86d5e9b67 --- /dev/null +++ b/api/client/generated/clientset/versioned/typed/core/v1alpha3/fake/fake_core_client.go @@ -0,0 +1,40 @@ +/* +Copyright Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha3 "github.com/deckhouse/virtualization/api/client/generated/clientset/versioned/typed/core/v1alpha3" + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" +) + +type FakeVirtualizationV1alpha3 struct { + *testing.Fake +} + +func (c *FakeVirtualizationV1alpha3) VirtualMachineClasses() v1alpha3.VirtualMachineClassInterface { + return newFakeVirtualMachineClasses(c) +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeVirtualizationV1alpha3) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/api/client/generated/clientset/versioned/typed/core/v1alpha3/fake/fake_virtualmachineclass.go b/api/client/generated/clientset/versioned/typed/core/v1alpha3/fake/fake_virtualmachineclass.go new file mode 100644 index 0000000000..b00b1bd86e --- /dev/null +++ b/api/client/generated/clientset/versioned/typed/core/v1alpha3/fake/fake_virtualmachineclass.go @@ -0,0 +1,52 @@ +/* +Copyright Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + corev1alpha3 "github.com/deckhouse/virtualization/api/client/generated/clientset/versioned/typed/core/v1alpha3" + v1alpha3 "github.com/deckhouse/virtualization/api/core/v1alpha3" + gentype "k8s.io/client-go/gentype" +) + +// fakeVirtualMachineClasses implements VirtualMachineClassInterface +type fakeVirtualMachineClasses struct { + *gentype.FakeClientWithList[*v1alpha3.VirtualMachineClass, *v1alpha3.VirtualMachineClassList] + Fake *FakeVirtualizationV1alpha3 +} + +func newFakeVirtualMachineClasses(fake *FakeVirtualizationV1alpha3) corev1alpha3.VirtualMachineClassInterface { + return &fakeVirtualMachineClasses{ + gentype.NewFakeClientWithList[*v1alpha3.VirtualMachineClass, *v1alpha3.VirtualMachineClassList]( + fake.Fake, + "", + v1alpha3.SchemeGroupVersion.WithResource("virtualmachineclasses"), + v1alpha3.SchemeGroupVersion.WithKind("VirtualMachineClass"), + func() *v1alpha3.VirtualMachineClass { return &v1alpha3.VirtualMachineClass{} }, + func() *v1alpha3.VirtualMachineClassList { return &v1alpha3.VirtualMachineClassList{} }, + func(dst, src *v1alpha3.VirtualMachineClassList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha3.VirtualMachineClassList) []*v1alpha3.VirtualMachineClass { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v1alpha3.VirtualMachineClassList, items []*v1alpha3.VirtualMachineClass) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, + } +} diff --git a/api/client/generated/clientset/versioned/typed/core/v1alpha3/generated_expansion.go b/api/client/generated/clientset/versioned/typed/core/v1alpha3/generated_expansion.go new file mode 100644 index 0000000000..4d90e21047 --- /dev/null +++ b/api/client/generated/clientset/versioned/typed/core/v1alpha3/generated_expansion.go @@ -0,0 +1,21 @@ +/* +Copyright Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha3 + +type VirtualMachineClassExpansion interface{} diff --git a/api/client/generated/clientset/versioned/typed/core/v1alpha3/virtualmachineclass.go b/api/client/generated/clientset/versioned/typed/core/v1alpha3/virtualmachineclass.go new file mode 100644 index 0000000000..86c69a1c87 --- /dev/null +++ b/api/client/generated/clientset/versioned/typed/core/v1alpha3/virtualmachineclass.go @@ -0,0 +1,70 @@ +/* +Copyright Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha3 + +import ( + context "context" + + scheme "github.com/deckhouse/virtualization/api/client/generated/clientset/versioned/scheme" + corev1alpha3 "github.com/deckhouse/virtualization/api/core/v1alpha3" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + gentype "k8s.io/client-go/gentype" +) + +// VirtualMachineClassesGetter has a method to return a VirtualMachineClassInterface. +// A group's client should implement this interface. +type VirtualMachineClassesGetter interface { + VirtualMachineClasses() VirtualMachineClassInterface +} + +// VirtualMachineClassInterface has methods to work with VirtualMachineClass resources. +type VirtualMachineClassInterface interface { + Create(ctx context.Context, virtualMachineClass *corev1alpha3.VirtualMachineClass, opts v1.CreateOptions) (*corev1alpha3.VirtualMachineClass, error) + Update(ctx context.Context, virtualMachineClass *corev1alpha3.VirtualMachineClass, opts v1.UpdateOptions) (*corev1alpha3.VirtualMachineClass, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, virtualMachineClass *corev1alpha3.VirtualMachineClass, opts v1.UpdateOptions) (*corev1alpha3.VirtualMachineClass, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*corev1alpha3.VirtualMachineClass, error) + List(ctx context.Context, opts v1.ListOptions) (*corev1alpha3.VirtualMachineClassList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *corev1alpha3.VirtualMachineClass, err error) + VirtualMachineClassExpansion +} + +// virtualMachineClasses implements VirtualMachineClassInterface +type virtualMachineClasses struct { + *gentype.ClientWithList[*corev1alpha3.VirtualMachineClass, *corev1alpha3.VirtualMachineClassList] +} + +// newVirtualMachineClasses returns a VirtualMachineClasses +func newVirtualMachineClasses(c *VirtualizationV1alpha3Client) *virtualMachineClasses { + return &virtualMachineClasses{ + gentype.NewClientWithList[*corev1alpha3.VirtualMachineClass, *corev1alpha3.VirtualMachineClassList]( + "virtualmachineclasses", + c.RESTClient(), + scheme.ParameterCodec, + "", + func() *corev1alpha3.VirtualMachineClass { return &corev1alpha3.VirtualMachineClass{} }, + func() *corev1alpha3.VirtualMachineClassList { return &corev1alpha3.VirtualMachineClassList{} }, + ), + } +} diff --git a/api/client/generated/informers/externalversions/core/interface.go b/api/client/generated/informers/externalversions/core/interface.go index 1364a78eb7..f1da9e577c 100644 --- a/api/client/generated/informers/externalversions/core/interface.go +++ b/api/client/generated/informers/externalversions/core/interface.go @@ -20,6 +20,7 @@ package core import ( v1alpha2 "github.com/deckhouse/virtualization/api/client/generated/informers/externalversions/core/v1alpha2" + v1alpha3 "github.com/deckhouse/virtualization/api/client/generated/informers/externalversions/core/v1alpha3" internalinterfaces "github.com/deckhouse/virtualization/api/client/generated/informers/externalversions/internalinterfaces" ) @@ -27,6 +28,8 @@ import ( type Interface interface { // V1alpha2 provides access to shared informers for resources in V1alpha2. V1alpha2() v1alpha2.Interface + // V1alpha3 provides access to shared informers for resources in V1alpha3. + V1alpha3() v1alpha3.Interface } type group struct { @@ -44,3 +47,8 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList func (g *group) V1alpha2() v1alpha2.Interface { return v1alpha2.New(g.factory, g.namespace, g.tweakListOptions) } + +// V1alpha3 returns a new v1alpha3.Interface. +func (g *group) V1alpha3() v1alpha3.Interface { + return v1alpha3.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/api/client/generated/informers/externalversions/core/v1alpha3/interface.go b/api/client/generated/informers/externalversions/core/v1alpha3/interface.go new file mode 100644 index 0000000000..e201f89fa0 --- /dev/null +++ b/api/client/generated/informers/externalversions/core/v1alpha3/interface.go @@ -0,0 +1,45 @@ +/* +Copyright Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha3 + +import ( + internalinterfaces "github.com/deckhouse/virtualization/api/client/generated/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // VirtualMachineClasses returns a VirtualMachineClassInformer. + VirtualMachineClasses() VirtualMachineClassInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// VirtualMachineClasses returns a VirtualMachineClassInformer. +func (v *version) VirtualMachineClasses() VirtualMachineClassInformer { + return &virtualMachineClassInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} diff --git a/api/client/generated/informers/externalversions/core/v1alpha3/virtualmachineclass.go b/api/client/generated/informers/externalversions/core/v1alpha3/virtualmachineclass.go new file mode 100644 index 0000000000..f94694cc8e --- /dev/null +++ b/api/client/generated/informers/externalversions/core/v1alpha3/virtualmachineclass.go @@ -0,0 +1,101 @@ +/* +Copyright Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha3 + +import ( + context "context" + time "time" + + versioned "github.com/deckhouse/virtualization/api/client/generated/clientset/versioned" + internalinterfaces "github.com/deckhouse/virtualization/api/client/generated/informers/externalversions/internalinterfaces" + corev1alpha3 "github.com/deckhouse/virtualization/api/client/generated/listers/core/v1alpha3" + apicorev1alpha3 "github.com/deckhouse/virtualization/api/core/v1alpha3" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// VirtualMachineClassInformer provides access to a shared informer and lister for +// VirtualMachineClasses. +type VirtualMachineClassInformer interface { + Informer() cache.SharedIndexInformer + Lister() corev1alpha3.VirtualMachineClassLister +} + +type virtualMachineClassInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewVirtualMachineClassInformer constructs a new informer for VirtualMachineClass type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewVirtualMachineClassInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredVirtualMachineClassInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredVirtualMachineClassInformer constructs a new informer for VirtualMachineClass type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredVirtualMachineClassInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.VirtualizationV1alpha3().VirtualMachineClasses().List(context.Background(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.VirtualizationV1alpha3().VirtualMachineClasses().Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.VirtualizationV1alpha3().VirtualMachineClasses().List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.VirtualizationV1alpha3().VirtualMachineClasses().Watch(ctx, options) + }, + }, + &apicorev1alpha3.VirtualMachineClass{}, + resyncPeriod, + indexers, + ) +} + +func (f *virtualMachineClassInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredVirtualMachineClassInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *virtualMachineClassInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&apicorev1alpha3.VirtualMachineClass{}, f.defaultInformer) +} + +func (f *virtualMachineClassInformer) Lister() corev1alpha3.VirtualMachineClassLister { + return corev1alpha3.NewVirtualMachineClassLister(f.Informer().GetIndexer()) +} diff --git a/api/client/generated/informers/externalversions/generic.go b/api/client/generated/informers/externalversions/generic.go index 1d37a9b7ae..938b674b16 100644 --- a/api/client/generated/informers/externalversions/generic.go +++ b/api/client/generated/informers/externalversions/generic.go @@ -22,6 +22,7 @@ import ( fmt "fmt" v1alpha2 "github.com/deckhouse/virtualization/api/core/v1alpha2" + v1alpha3 "github.com/deckhouse/virtualization/api/core/v1alpha3" schema "k8s.io/apimachinery/pkg/runtime/schema" cache "k8s.io/client-go/tools/cache" ) @@ -82,6 +83,10 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource case v1alpha2.SchemeGroupVersion.WithResource("virtualmachinesnapshots"): return &genericInformer{resource: resource.GroupResource(), informer: f.Virtualization().V1alpha2().VirtualMachineSnapshots().Informer()}, nil + // Group=virtualization.deckhouse.io, Version=v1alpha3 + case v1alpha3.SchemeGroupVersion.WithResource("virtualmachineclasses"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Virtualization().V1alpha3().VirtualMachineClasses().Informer()}, nil + } return nil, fmt.Errorf("no informer found for %v", resource) diff --git a/api/client/generated/listers/core/v1alpha3/expansion_generated.go b/api/client/generated/listers/core/v1alpha3/expansion_generated.go new file mode 100644 index 0000000000..4aed995351 --- /dev/null +++ b/api/client/generated/listers/core/v1alpha3/expansion_generated.go @@ -0,0 +1,23 @@ +/* +Copyright Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha3 + +// VirtualMachineClassListerExpansion allows custom methods to be added to +// VirtualMachineClassLister. +type VirtualMachineClassListerExpansion interface{} diff --git a/api/client/generated/listers/core/v1alpha3/virtualmachineclass.go b/api/client/generated/listers/core/v1alpha3/virtualmachineclass.go new file mode 100644 index 0000000000..f3616caec8 --- /dev/null +++ b/api/client/generated/listers/core/v1alpha3/virtualmachineclass.go @@ -0,0 +1,48 @@ +/* +Copyright Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha3 + +import ( + corev1alpha3 "github.com/deckhouse/virtualization/api/core/v1alpha3" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" +) + +// VirtualMachineClassLister helps list VirtualMachineClasses. +// All objects returned here must be treated as read-only. +type VirtualMachineClassLister interface { + // List lists all VirtualMachineClasses in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*corev1alpha3.VirtualMachineClass, err error) + // Get retrieves the VirtualMachineClass from the index for a given name. + // Objects returned here must be treated as read-only. + Get(name string) (*corev1alpha3.VirtualMachineClass, error) + VirtualMachineClassListerExpansion +} + +// virtualMachineClassLister implements the VirtualMachineClassLister interface. +type virtualMachineClassLister struct { + listers.ResourceIndexer[*corev1alpha3.VirtualMachineClass] +} + +// NewVirtualMachineClassLister returns a new VirtualMachineClassLister. +func NewVirtualMachineClassLister(indexer cache.Indexer) VirtualMachineClassLister { + return &virtualMachineClassLister{listers.New[*corev1alpha3.VirtualMachineClass](indexer, corev1alpha3.Resource("virtualmachineclass"))} +} diff --git a/api/core/v1alpha2/conversion_suite_test.go b/api/core/v1alpha2/conversion_suite_test.go new file mode 100644 index 0000000000..3771e6d24b --- /dev/null +++ b/api/core/v1alpha2/conversion_suite_test.go @@ -0,0 +1,29 @@ +/* +Copyright 2025 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha2 + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestConversion(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "VirtualMachineClass Conversion Suite") +} diff --git a/api/core/v1alpha2/virtual_machine_class.go b/api/core/v1alpha2/virtual_machine_class.go index 1df0a83122..0275d2380f 100644 --- a/api/core/v1alpha2/virtual_machine_class.go +++ b/api/core/v1alpha2/virtual_machine_class.go @@ -39,6 +39,7 @@ const ( // +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="VirtualMachineClass phase." // +kubebuilder:printcolumn:name="IsDefault",type="string",JSONPath=".metadata.annotations.virtualmachineclass\\.virtualization\\.deckhouse\\.io\\/is-default-class",description="Default class for virtual machines without specified class." // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time of resource creation." +// +kubebuilder:deprecatedversion:warning="v1alpha2.VirtualMachineClass is deprecated; use v1alpha3.VirtualMachineClass" // +genclient // +genclient:nonNamespaced // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/api/core/v1alpha2/virtual_machine_class_conversion.go b/api/core/v1alpha2/virtual_machine_class_conversion.go new file mode 100644 index 0000000000..785bb29973 --- /dev/null +++ b/api/core/v1alpha2/virtual_machine_class_conversion.go @@ -0,0 +1,220 @@ +/* +Copyright 2025 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha2 + +import ( + "fmt" + "strconv" + + "sigs.k8s.io/controller-runtime/pkg/conversion" + + "github.com/deckhouse/virtualization/api/core/v1alpha3" +) + +var _ conversion.Convertible = &VirtualMachineClass{} + +func (src *VirtualMachineClass) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*v1alpha3.VirtualMachineClass) + + dst.ObjectMeta = src.ObjectMeta + dst.Spec = convertSpecV2ToV3(src.Spec) + dst.Status = convertStatusV2ToV3(src.Status) + + return nil +} + +func (dst *VirtualMachineClass) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*v1alpha3.VirtualMachineClass) + + dst.ObjectMeta = src.ObjectMeta + convertedSpec, err := convertSpecV3ToV2(src.Spec) + if err != nil { + return err + } + dst.Spec = convertedSpec + dst.Status = convertStatusV3ToV2(src.Status) + + return nil +} +func convertSpecV3ToV2(v3Spec v1alpha3.VirtualMachineClassSpec) (VirtualMachineClassSpec, error) { + v2Spec := VirtualMachineClassSpec{ + NodeSelector: NodeSelector{ + MatchLabels: v3Spec.NodeSelector.MatchLabels, + MatchExpressions: v3Spec.NodeSelector.MatchExpressions, + }, + Tolerations: v3Spec.Tolerations, + CPU: CPU{ + Type: CPUType(v3Spec.CPU.Type), + Model: v3Spec.CPU.Model, + Features: v3Spec.CPU.Features, + }, + } + + if v3Spec.CPU.Discovery != nil { + v2Spec.CPU.Discovery = &CpuDiscovery{ + NodeSelector: v3Spec.CPU.Discovery.NodeSelector, + } + } + + if len(v3Spec.SizingPolicies) > 0 { + v2Spec.SizingPolicies = make([]SizingPolicy, len(v3Spec.SizingPolicies)) + for i, v3Policy := range v3Spec.SizingPolicies { + v2Policy := SizingPolicy{ + DedicatedCores: v3Policy.DedicatedCores, + } + + if v3Policy.Memory != nil { + v2Policy.Memory = &SizingPolicyMemory{ + MemoryMinMax: MemoryMinMax{ + Min: v3Policy.Memory.Min, + Max: v3Policy.Memory.Max, + }, + Step: v3Policy.Memory.Step, + PerCore: SizingPolicyMemoryPerCore{ + MemoryMinMax: MemoryMinMax{ + Min: v3Policy.Memory.PerCore.Min, + Max: v3Policy.Memory.PerCore.Max, + }, + }, + } + } + + if v3Policy.Cores != nil { + v2Policy.Cores = &SizingPolicyCores{ + Min: v3Policy.Cores.Min, + Max: v3Policy.Cores.Max, + Step: v3Policy.Cores.Step, + } + } + + if len(v3Policy.CoreFractions) > 0 { + v2Policy.CoreFractions = make([]CoreFractionValue, len(v3Policy.CoreFractions)) + for j, v3Fraction := range v3Policy.CoreFractions { + fractionStr := string(v3Fraction) + if len(fractionStr) > 0 && fractionStr[len(fractionStr)-1] == '%' { + fractionStr = fractionStr[:len(fractionStr)-1] + } + fractionInt, err := strconv.Atoi(fractionStr) + if err != nil { + return VirtualMachineClassSpec{}, fmt.Errorf("failed to parse core fraction: %w", err) + } + if fractionInt < 1 || fractionInt > 100 { + return VirtualMachineClassSpec{}, fmt.Errorf("core fraction value must be between 1 and 100, got %d", fractionInt) + } + v2Policy.CoreFractions[j] = CoreFractionValue(fractionInt) + } + } + + v2Spec.SizingPolicies[i] = v2Policy + } + } + + return v2Spec, nil +} + +func convertSpecV2ToV3(v2Spec VirtualMachineClassSpec) v1alpha3.VirtualMachineClassSpec { + v3Spec := v1alpha3.VirtualMachineClassSpec{ + NodeSelector: v1alpha3.NodeSelector{ + MatchLabels: v2Spec.NodeSelector.MatchLabels, + MatchExpressions: v2Spec.NodeSelector.MatchExpressions, + }, + Tolerations: v2Spec.Tolerations, + CPU: v1alpha3.CPU{ + Type: v1alpha3.CPUType(v2Spec.CPU.Type), + Model: v2Spec.CPU.Model, + Features: v2Spec.CPU.Features, + }, + } + + if v2Spec.CPU.Discovery != nil { + v3Spec.CPU.Discovery = &v1alpha3.CpuDiscovery{ + NodeSelector: v2Spec.CPU.Discovery.NodeSelector, + } + } + + if len(v2Spec.SizingPolicies) > 0 { + v3Spec.SizingPolicies = make([]v1alpha3.SizingPolicy, len(v2Spec.SizingPolicies)) + for i, v2Policy := range v2Spec.SizingPolicies { + v3Policy := v1alpha3.SizingPolicy{ + DedicatedCores: v2Policy.DedicatedCores, + } + + if v2Policy.Memory != nil { + v3Policy.Memory = &v1alpha3.SizingPolicyMemory{ + MemoryMinMax: v1alpha3.MemoryMinMax{ + Min: v2Policy.Memory.Min, + Max: v2Policy.Memory.Max, + }, + Step: v2Policy.Memory.Step, + PerCore: v1alpha3.SizingPolicyMemoryPerCore{ + MemoryMinMax: v1alpha3.MemoryMinMax{ + Min: v2Policy.Memory.PerCore.Min, + Max: v2Policy.Memory.PerCore.Max, + }, + }, + } + } + + if v2Policy.Cores != nil { + v3Policy.Cores = &v1alpha3.SizingPolicyCores{ + Min: v2Policy.Cores.Min, + Max: v2Policy.Cores.Max, + Step: v2Policy.Cores.Step, + } + } + + if len(v2Policy.CoreFractions) > 0 { + v3Policy.CoreFractions = make([]v1alpha3.CoreFractionValue, len(v2Policy.CoreFractions)) + for j, v2Fraction := range v2Policy.CoreFractions { + v3Policy.CoreFractions[j] = v1alpha3.CoreFractionValue(fmt.Sprintf("%d%%", v2Fraction)) + } + } + + v3Spec.SizingPolicies[i] = v3Policy + } + } + + return v3Spec +} + +func convertStatusV3ToV2(v3Status v1alpha3.VirtualMachineClassStatus) VirtualMachineClassStatus { + return VirtualMachineClassStatus{ + Phase: VirtualMachineClassPhase(v3Status.Phase), + CpuFeatures: CpuFeatures{ + Enabled: v3Status.CpuFeatures.Enabled, + NotEnabledCommon: v3Status.CpuFeatures.NotEnabledCommon, + }, + AvailableNodes: v3Status.AvailableNodes, + MaxAllocatableResources: v3Status.MaxAllocatableResources, + Conditions: v3Status.Conditions, + ObservedGeneration: v3Status.ObservedGeneration, + } +} + +func convertStatusV2ToV3(v2Status VirtualMachineClassStatus) v1alpha3.VirtualMachineClassStatus { + return v1alpha3.VirtualMachineClassStatus{ + Phase: v1alpha3.VirtualMachineClassPhase(v2Status.Phase), + CpuFeatures: v1alpha3.CpuFeatures{ + Enabled: v2Status.CpuFeatures.Enabled, + NotEnabledCommon: v2Status.CpuFeatures.NotEnabledCommon, + }, + AvailableNodes: v2Status.AvailableNodes, + MaxAllocatableResources: v2Status.MaxAllocatableResources, + Conditions: v2Status.Conditions, + ObservedGeneration: v2Status.ObservedGeneration, + } +} diff --git a/api/core/v1alpha2/virtual_machine_class_conversion_test.go b/api/core/v1alpha2/virtual_machine_class_conversion_test.go new file mode 100644 index 0000000000..f182f199b2 --- /dev/null +++ b/api/core/v1alpha2/virtual_machine_class_conversion_test.go @@ -0,0 +1,399 @@ +/* +Copyright 2025 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha2 + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/deckhouse/virtualization/api/core/v1alpha3" +) + +var _ = Describe("VirtualMachineClass Conversion", func() { + Context("ConvertTo v1alpha2", func() { + DescribeTable("should convert valid CoreFractionValue strings", + func(coreFractions []v1alpha3.CoreFractionValue) { + v3Class := &v1alpha3.VirtualMachineClass{ + ObjectMeta: metav1.ObjectMeta{Name: "test-class"}, + Spec: v1alpha3.VirtualMachineClassSpec{ + SizingPolicies: []v1alpha3.SizingPolicy{ + { + CoreFractions: coreFractions, + Cores: &v1alpha3.SizingPolicyCores{ + Min: 1, + Max: 8, + Step: 1, + }, + }, + }, + }, + } + + v2Class := &VirtualMachineClass{} + err := v2Class.ConvertFrom(v3Class) + + Expect(err).NotTo(HaveOccurred()) + Expect(v2Class.Name).To(Equal(v3Class.Name)) + Expect(v2Class.Spec.SizingPolicies).To(HaveLen(1)) + Expect(v2Class.Spec.SizingPolicies[0].CoreFractions).To(HaveLen(len(coreFractions))) + }, + Entry("single value", []v1alpha3.CoreFractionValue{"5%"}), + Entry("multiple values", []v1alpha3.CoreFractionValue{"5%", "10%", "25%", "50%", "100%"}), + Entry("minimum value 1%", []v1alpha3.CoreFractionValue{"1%"}), + Entry("maximum value 100%", []v1alpha3.CoreFractionValue{"100%"}), + Entry("mixed valid values", []v1alpha3.CoreFractionValue{"1%", "50%", "100%"}), + Entry("value without percent sign", []v1alpha3.CoreFractionValue{"50"}), + ) + + DescribeTable("should fail on invalid CoreFractionValue strings", + func(coreFractions []v1alpha3.CoreFractionValue, expectedErrorSubstring string) { + v3Class := &v1alpha3.VirtualMachineClass{ + ObjectMeta: metav1.ObjectMeta{Name: "test-class"}, + Spec: v1alpha3.VirtualMachineClassSpec{ + SizingPolicies: []v1alpha3.SizingPolicy{ + { + CoreFractions: coreFractions, + Cores: &v1alpha3.SizingPolicyCores{ + Min: 1, + Max: 4, + Step: 1, + }, + }, + }, + }, + } + + v2Class := &VirtualMachineClass{} + err := v2Class.ConvertFrom(v3Class) + + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring(expectedErrorSubstring)) + }, + Entry("value below minimum (0%)", []v1alpha3.CoreFractionValue{"0%"}, "must be between 1 and 100, got 0"), + Entry("value above maximum (101%)", []v1alpha3.CoreFractionValue{"101%"}, "must be between 1 and 100, got 101"), + Entry("negative value", []v1alpha3.CoreFractionValue{"-5%"}, "must be between 1 and 100, got -5"), + Entry("non-numeric value", []v1alpha3.CoreFractionValue{"abc%"}, "failed to parse core fraction"), + Entry("empty string", []v1alpha3.CoreFractionValue{""}, "failed to parse core fraction"), + Entry("percent sign in wrong position", []v1alpha3.CoreFractionValue{"%50"}, "failed to parse core fraction"), + Entry("one invalid in multiple", []v1alpha3.CoreFractionValue{"5%", "150%", "100%"}, "must be between 1 and 100, got 150"), + ) + + It("should preserve ObjectMeta", func() { + v3Class := &v1alpha3.VirtualMachineClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-class", + Namespace: "test-ns", + Labels: map[string]string{ + "test-label": "test-value", + }, + }, + Spec: v1alpha3.VirtualMachineClassSpec{}, + } + + v2Class := &VirtualMachineClass{} + err := v2Class.ConvertFrom(v3Class) + + Expect(err).NotTo(HaveOccurred()) + Expect(v2Class.Name).To(Equal("test-class")) + Expect(v2Class.Namespace).To(Equal("test-ns")) + Expect(v2Class.Labels).To(HaveKeyWithValue("test-label", "test-value")) + }) + }) + + Context("ConvertFrom v1alpha2", func() { + DescribeTable("should convert v1alpha2 CoreFractionValue integers to percentage strings", + func(v2CoreFractions []CoreFractionValue, expectedV3Values []v1alpha3.CoreFractionValue) { + v2Class := &VirtualMachineClass{ + ObjectMeta: metav1.ObjectMeta{Name: "test-class"}, + Spec: VirtualMachineClassSpec{ + SizingPolicies: []SizingPolicy{ + { + CoreFractions: v2CoreFractions, + Cores: &SizingPolicyCores{ + Min: 1, + Max: 8, + Step: 1, + }, + }, + }, + }, + } + + v3Class := &v1alpha3.VirtualMachineClass{} + err := v2Class.ConvertTo(v3Class) + + Expect(err).NotTo(HaveOccurred()) + Expect(v3Class.Spec.SizingPolicies).To(HaveLen(1)) + Expect(v3Class.Spec.SizingPolicies[0].CoreFractions).To(Equal(expectedV3Values)) + }, + Entry("single value", []CoreFractionValue{5}, []v1alpha3.CoreFractionValue{"5%"}), + Entry("multiple values", []CoreFractionValue{5, 10, 25, 50, 100}, []v1alpha3.CoreFractionValue{"5%", "10%", "25%", "50%", "100%"}), + Entry("minimum value", []CoreFractionValue{1}, []v1alpha3.CoreFractionValue{"1%"}), + Entry("maximum value", []CoreFractionValue{100}, []v1alpha3.CoreFractionValue{"100%"}), + ) + }) + + Context("Round-trip conversion", func() { + DescribeTable("should preserve values through v2 -> v3 -> v2 conversion", + func(v2CoreFractions []CoreFractionValue) { + originalV2 := &VirtualMachineClass{ + ObjectMeta: metav1.ObjectMeta{Name: "test-class"}, + Spec: VirtualMachineClassSpec{ + SizingPolicies: []SizingPolicy{ + { + CoreFractions: v2CoreFractions, + Cores: &SizingPolicyCores{ + Min: 1, + Max: 8, + Step: 1, + }, + }, + }, + }, + } + + v3Class := &v1alpha3.VirtualMachineClass{} + err := originalV2.ConvertTo(v3Class) + Expect(err).NotTo(HaveOccurred()) + + roundTripV2 := &VirtualMachineClass{} + err = roundTripV2.ConvertFrom(v3Class) + Expect(err).NotTo(HaveOccurred()) + + Expect(roundTripV2.Spec.SizingPolicies).To(HaveLen(1)) + Expect(roundTripV2.Spec.SizingPolicies[0].CoreFractions).To(Equal(v2CoreFractions)) + }, + Entry("single value", []CoreFractionValue{5}), + Entry("multiple values", []CoreFractionValue{5, 10, 25, 50, 100}), + Entry("boundary values", []CoreFractionValue{1, 100}), + ) + }) + + Context("Full spec conversion", func() { + var ( + minMem, maxMem, stepMem resource.Quantity + minPerCoreMem, maxPerCoreMem resource.Quantity + ) + + BeforeEach(func() { + minMem = resource.MustParse("1Gi") + maxMem = resource.MustParse("8Gi") + stepMem = resource.MustParse("1Gi") + minPerCoreMem = resource.MustParse("512Mi") + maxPerCoreMem = resource.MustParse("2Gi") + }) + + It("should preserve all fields in ConvertTo", func() { + v3Class := &v1alpha3.VirtualMachineClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "full-test-class", + Namespace: "test-namespace", + Labels: map[string]string{ + "test-label": "test-value", + }, + Annotations: map[string]string{ + "test-annotation": "test-value", + }, + }, + Spec: v1alpha3.VirtualMachineClassSpec{ + NodeSelector: v1alpha3.NodeSelector{ + MatchLabels: map[string]string{ + "node-role": "worker", + "zone": "us-east-1a", + }, + MatchExpressions: []corev1.NodeSelectorRequirement{ + { + Key: "cpu-type", + Operator: corev1.NodeSelectorOpIn, + Values: []string{"intel", "amd"}, + }, + }, + }, + Tolerations: []corev1.Toleration{ + { + Key: "dedicated", + Operator: corev1.TolerationOpEqual, + Value: "virtualization", + Effect: corev1.TaintEffectNoSchedule, + }, + }, + CPU: v1alpha3.CPU{ + Type: v1alpha3.CPUTypeModel, + Model: "IvyBridge", + Features: nil, + Discovery: nil, + }, + SizingPolicies: []v1alpha3.SizingPolicy{ + { + Memory: &v1alpha3.SizingPolicyMemory{ + MemoryMinMax: v1alpha3.MemoryMinMax{ + Min: minMem, + Max: maxMem, + }, + Step: stepMem, + PerCore: v1alpha3.SizingPolicyMemoryPerCore{ + MemoryMinMax: v1alpha3.MemoryMinMax{ + Min: minPerCoreMem, + Max: maxPerCoreMem, + }, + }, + }, + CoreFractions: []v1alpha3.CoreFractionValue{"5%", "10%", "50%", "100%"}, + DedicatedCores: []bool{false, true}, + Cores: &v1alpha3.SizingPolicyCores{ + Min: 1, + Max: 16, + Step: 2, + }, + }, + }, + }, + } + + v2Class := &VirtualMachineClass{} + err := v2Class.ConvertFrom(v3Class) + Expect(err).NotTo(HaveOccurred()) + + Expect(v2Class.Name).To(Equal("full-test-class")) + Expect(v2Class.Namespace).To(Equal("test-namespace")) + Expect(v2Class.Labels).To(HaveKeyWithValue("test-label", "test-value")) + Expect(v2Class.Annotations).To(HaveKeyWithValue("test-annotation", "test-value")) + + Expect(v2Class.Spec.NodeSelector.MatchLabels).To(Equal(map[string]string{ + "node-role": "worker", + "zone": "us-east-1a", + })) + Expect(v2Class.Spec.NodeSelector.MatchExpressions).To(HaveLen(1)) + Expect(v2Class.Spec.NodeSelector.MatchExpressions[0].Key).To(Equal("cpu-type")) + + Expect(v2Class.Spec.Tolerations).To(HaveLen(1)) + Expect(v2Class.Spec.Tolerations[0].Key).To(Equal("dedicated")) + Expect(v2Class.Spec.Tolerations[0].Value).To(Equal("virtualization")) + + Expect(string(v2Class.Spec.CPU.Type)).To(Equal("Model")) + Expect(v2Class.Spec.CPU.Model).To(Equal("IvyBridge")) + + Expect(v2Class.Spec.SizingPolicies).To(HaveLen(1)) + policy := v2Class.Spec.SizingPolicies[0] + + Expect(policy.Memory).NotTo(BeNil()) + Expect(policy.Memory.Min.Equal(minMem)).To(BeTrue()) + Expect(policy.Memory.Max.Equal(maxMem)).To(BeTrue()) + Expect(policy.Memory.Step.Equal(stepMem)).To(BeTrue()) + Expect(policy.Memory.PerCore.Min.Equal(minPerCoreMem)).To(BeTrue()) + Expect(policy.Memory.PerCore.Max.Equal(maxPerCoreMem)).To(BeTrue()) + + Expect(policy.CoreFractions).To(Equal([]CoreFractionValue{5, 10, 50, 100})) + Expect(policy.DedicatedCores).To(Equal([]bool{false, true})) + + Expect(policy.Cores).NotTo(BeNil()) + Expect(policy.Cores.Min).To(Equal(1)) + Expect(policy.Cores.Max).To(Equal(16)) + Expect(policy.Cores.Step).To(Equal(2)) + }) + + It("should preserve all fields in ConvertFrom", func() { + v2Class := &VirtualMachineClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "full-test-class", + Namespace: "test-namespace", + Labels: map[string]string{ + "test-label": "test-value", + }, + }, + Spec: VirtualMachineClassSpec{ + NodeSelector: NodeSelector{ + MatchLabels: map[string]string{ + "node-role": "worker", + }, + }, + Tolerations: []corev1.Toleration{ + { + Key: "dedicated", + Operator: corev1.TolerationOpEqual, + Value: "virtualization", + Effect: corev1.TaintEffectNoSchedule, + }, + }, + CPU: CPU{ + Type: CPUTypeFeatures, + Features: []string{"mmx", "sse2", "vmx"}, + }, + SizingPolicies: []SizingPolicy{ + { + Memory: &SizingPolicyMemory{ + MemoryMinMax: MemoryMinMax{ + Min: minMem, + Max: maxMem, + }, + Step: stepMem, + PerCore: SizingPolicyMemoryPerCore{ + MemoryMinMax: MemoryMinMax{ + Min: minPerCoreMem, + Max: maxPerCoreMem, + }, + }, + }, + CoreFractions: []CoreFractionValue{10, 50, 100}, + DedicatedCores: []bool{true}, + Cores: &SizingPolicyCores{ + Min: 2, + Max: 8, + Step: 2, + }, + }, + }, + }, + } + + v3Class := &v1alpha3.VirtualMachineClass{} + err := v2Class.ConvertTo(v3Class) + Expect(err).NotTo(HaveOccurred()) + + Expect(v3Class.Name).To(Equal("full-test-class")) + Expect(v3Class.Namespace).To(Equal("test-namespace")) + Expect(v3Class.Labels).To(HaveKeyWithValue("test-label", "test-value")) + + Expect(v3Class.Spec.NodeSelector.MatchLabels).To(HaveKeyWithValue("node-role", "worker")) + + Expect(v3Class.Spec.Tolerations).To(HaveLen(1)) + Expect(v3Class.Spec.Tolerations[0].Key).To(Equal("dedicated")) + + Expect(string(v3Class.Spec.CPU.Type)).To(Equal("Features")) + Expect(v3Class.Spec.CPU.Features).To(Equal([]string{"mmx", "sse2", "vmx"})) + + Expect(v3Class.Spec.SizingPolicies).To(HaveLen(1)) + policy := v3Class.Spec.SizingPolicies[0] + + Expect(policy.Memory).NotTo(BeNil()) + Expect(policy.Memory.Min.Equal(minMem)).To(BeTrue()) + Expect(policy.Memory.Max.Equal(maxMem)).To(BeTrue()) + Expect(policy.Memory.Step.Equal(stepMem)).To(BeTrue()) + + Expect(policy.CoreFractions).To(Equal([]v1alpha3.CoreFractionValue{"10%", "50%", "100%"})) + Expect(policy.DedicatedCores).To(Equal([]bool{true})) + + Expect(policy.Cores).NotTo(BeNil()) + Expect(policy.Cores.Min).To(Equal(2)) + Expect(policy.Cores.Max).To(Equal(8)) + Expect(policy.Cores.Step).To(Equal(2)) + }) + }) +}) diff --git a/api/core/v1alpha3/doc.go b/api/core/v1alpha3/doc.go new file mode 100644 index 0000000000..0d53bde091 --- /dev/null +++ b/api/core/v1alpha3/doc.go @@ -0,0 +1,22 @@ +/* +Copyright 2025 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +k8s:deepcopy-gen=package +// +k8s:openapi-gen=true + +// Package v1alpha3 is the v1alpha3 version of the API. +// +groupName=virtualization.deckhouse.io +package v1alpha3 diff --git a/api/core/v1alpha3/register.go b/api/core/v1alpha3/register.go new file mode 100644 index 0000000000..0028d52785 --- /dev/null +++ b/api/core/v1alpha3/register.go @@ -0,0 +1,64 @@ +/* +Copyright 2025 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha3 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + + "github.com/deckhouse/virtualization/api/core" +) + +const Version = "v1alpha3" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: core.GroupName, Version: Version} + +// VirtualMachineClassGVK is group version kind for VirtualMachineClass +var VirtualMachineClassGVK = schema.GroupVersionKind{Group: SchemeGroupVersion.Group, Version: SchemeGroupVersion.Version, Kind: VirtualMachineClassKind} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +func GroupVersionResource(resource string) schema.GroupVersionResource { + return SchemeGroupVersion.WithResource(resource) +} + +var ( + // SchemeBuilder tbd + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + // AddToScheme tbd + AddToScheme = SchemeBuilder.AddToScheme +) + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &VirtualMachineClass{}, + &VirtualMachineClassList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/api/core/v1alpha3/virtual_machine_class.go b/api/core/v1alpha3/virtual_machine_class.go new file mode 100644 index 0000000000..7d900de45e --- /dev/null +++ b/api/core/v1alpha3/virtual_machine_class.go @@ -0,0 +1,243 @@ +/* +Copyright 2025 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +kubebuilder:object:generate=true +// +groupName=virtualization.deckhouse.io +package v1alpha3 + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + VirtualMachineClassKind = "VirtualMachineClass" + VirtualMachineClassResource = "virtualmachineclasses" +) + +// VirtualMachineClass resource describes CPU requirements, node placement, and sizing policy for VM resources. +// A resource cannot be deleted as long as it is used in one of the VMs. +// +// +kubebuilder:object:root=true +// +kubebuilder:metadata:labels={heritage=deckhouse,module=virtualization,backup.deckhouse.io/cluster-config=true} +// +kubebuilder:subresource:status +// +kubebuilder:resource:categories={virtualization-cluster},scope=Cluster,shortName={vmc,vmclass},singular=virtualmachineclass +// +kubebuilder:storageversion +// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="VirtualMachineClass phase." +// +kubebuilder:printcolumn:name="IsDefault",type="string",JSONPath=".metadata.annotations.virtualmachineclass\\.virtualization\\.deckhouse\\.io\\/is-default-class",description="Default class for virtual machines without specified class." +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time of resource creation." +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type VirtualMachineClass struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec VirtualMachineClassSpec `json:"spec"` + Status VirtualMachineClassStatus `json:"status,omitempty"` +} + +// VirtualMachineClassList contains a list of VirtualMachineClasses. +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type VirtualMachineClassList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + // Items provides a list of VirtualMachineClasses. + Items []VirtualMachineClass `json:"items"` +} + +type VirtualMachineClassSpec struct { + NodeSelector NodeSelector `json:"nodeSelector,omitempty"` + // Tolerations are the same as `spec.tolerations` for [pods](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/). + // These tolerations will be merged with the tolerations specified in the VirtualMachine resource. VirtualMachine tolerations have a higher priority. + Tolerations []corev1.Toleration `json:"tolerations,omitempty"` + // +kubebuilder:validation:Required + CPU CPU `json:"cpu"` + SizingPolicies []SizingPolicy `json:"sizingPolicies,omitempty"` +} + +// NodeSelector defines the nodes targeted for VM scheduling. +type NodeSelector struct { + // A map of {key,value} pairs. + // A single {key,value} pair in the matchLabels map is equivalent to an element of matchExpressions whose key field is "key", operator is "In", and the value array contains only "value". + // The requirements are ANDed. + MatchLabels map[string]string `json:"matchLabels,omitempty"` + // A list of node selector requirements by node's labels. + MatchExpressions []corev1.NodeSelectorRequirement `json:"matchExpressions,omitempty"` +} + +// CPU defines the requirements for the virtual CPU model. +// +kubebuilder:validation:XValidation:rule="self == oldSelf",message=".spec.cpu is immutable" +// +kubebuilder:validation:XValidation:rule="self.type == 'HostPassthrough' || self.type == 'Host' ? !has(self.model) && !has(self.features) && !has(self.discovery) : true",message="HostPassthrough and Host cannot have model, features or discovery" +// +kubebuilder:validation:XValidation:rule="self.type == 'Discovery' ? !has(self.model) && !has(self.features) : true",message="Discovery cannot have model or features" +// +kubebuilder:validation:XValidation:rule="self.type == 'Model' ? has(self.model) && !has(self.features) && !has(self.discovery) : true",message="Model requires model and cannot have features or discovery" +// +kubebuilder:validation:XValidation:rule="self.type == 'Features' ? has(self.features) && !has(self.model) && !has(self.discovery): true",message="Features requires features and cannot have model or discovery" +type CPU struct { + // +kubebuilder:validation:Required + Type CPUType `json:"type"` + // CPU model name. For more information about CPU models and topology, refer to the [libvirt docs](https://libvirt.org/formatdomain.html#cpu-model-and-topology). + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:example=IvyBridge + Model string `json:"model,omitempty"` + // List of CPU instructions (features) required when type=Features. + // For more information about CPU features, refer to the [libvirt docs](https://libvirt.org/formatdomain.html#cpu-model-and-topology). + // + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:example={mmx, vmx, sse2} + Features []string `json:"features,omitempty"` + // Create a CPU model based on intersecting CPU features for selected nodes. + Discovery *CpuDiscovery `json:"discovery,omitempty"` +} + +type CpuDiscovery struct { + // A selection of nodes to be used as the basis for creating a universal CPU model. + NodeSelector metav1.LabelSelector `json:"nodeSelector,omitempty"` +} + +// SizingPolicy defines a policy for allocating computational resources to VMs. +// It is represented as a list. +// The cores.min - cores.max ranges for different elements of the list must not overlap. +type SizingPolicy struct { + // Memory sizing policy. + Memory *SizingPolicyMemory `json:"memory,omitempty"` + // Allowed values of the `coreFraction` parameter in percentages (e.g., "5%", "10%", "25%", "50%", "100%"). + CoreFractions []CoreFractionValue `json:"coreFractions,omitempty"` + // Allowed values of the `dedicatedCores` parameter. + DedicatedCores []bool `json:"dedicatedCores,omitempty"` + // The policy applies for a specified range of the number of CPU cores. + Cores *SizingPolicyCores `json:"cores,omitempty"` +} + +// CoreFractionValue represents CPU core fraction as a percentage string (e.g., "5%", "10%", "25%", "50%", "100%"). +type CoreFractionValue string + +type SizingPolicyMemory struct { + MemoryMinMax `json:",inline"` + // Memory size discretization step. For example, the combination of `min=2Gi, `max=4Gi` and `step=1Gi` allows to set the virtual machine memory size to 2Gi, 3Gi, or 4Gi. + // + // +kubebuilder:example="512Mi" + Step resource.Quantity `json:"step,omitempty"` + + // Amount of memory per CPU core. + PerCore SizingPolicyMemoryPerCore `json:"perCore,omitempty"` +} + +type SizingPolicyMemoryPerCore struct { + MemoryMinMax `json:",inline"` +} + +type MemoryMinMax struct { + // Minimum amount of memory. + // + // +kubebuilder:example="1Gi" + Min resource.Quantity `json:"min,omitempty"` + // Maximum amount of memory. + // + // +kubebuilder:example="8Gi" + Max resource.Quantity `json:"max,omitempty"` +} + +// +kubebuilder:validation:XValidation:rule="self.max > self.min",message="The maximum must be greater than the minimum" +// +kubebuilder:validation:XValidation:rule="has(self.step) ? self.max > self.step : true",message="The maximum must be greater than the step" +type SizingPolicyCores struct { + // Minimum number of CPU cores. + // + // +kubebuilder:validation:Required + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:example=1 + Min int `json:"min"` + // Maximum number of CPU cores. + // + // +kubebuilder:validation:Required + // +kubebuilder:validation:Maximum=1024 + // +kubebuilder:example=10 + Max int `json:"max"` + // Discretization step for the CPU core number. For example, the combination of `min=2`, `max=10`, and `step=4` allows to set the number of virtual machine CPU cores to 2, 6, or 10. + // + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:example=1 + Step int `json:"step,omitempty"` +} + +// CPUType defines the CPU type, the following options are supported: +// * `Host`: Uses a virtual CPU with an instruction set closely matching the platform node's CPU. +// This provides high performance and functionality, as well as compatibility with "live" migration for nodes with similar processor types. +// For example, VM migration between nodes with Intel and AMD processors will not work. +// This is also true for different CPU generations, as their instruction set is different. +// * `HostPassthrough`: Uses the platform node's physical CPU directly, without any modifications. +// When using this class, the guest VM can only be transferred to a target node with a CPU exactly matching the source node's CPU. +// * `Discovery`: Create a virtual CPU based on instruction sets of physical CPUs for a selected set of nodes. +// * `Model`: CPU model. A CPU model is a named and previously defined set of supported CPU instructions. +// * `Features`: A required set of supported instructions for the CPU. +// +// +kubebuilder:validation:Enum={Host,HostPassthrough,Discovery,Model,Features} +type CPUType string + +const ( + CPUTypeHost CPUType = "Host" + CPUTypeHostPassthrough CPUType = "HostPassthrough" + CPUTypeDiscovery CPUType = "Discovery" + CPUTypeModel CPUType = "Model" + CPUTypeFeatures CPUType = "Features" +) + +type VirtualMachineClassStatus struct { + Phase VirtualMachineClassPhase `json:"phase"` + CpuFeatures CpuFeatures `json:"cpuFeatures,omitempty"` + // List of nodes that support this CPU model. + // It is not displayed for the following types: `Host`, `HostPassthrough`. + // + // +kubebuilder:example={node-1, node-2} + AvailableNodes []string `json:"availableNodes,omitempty"` + // Maximum amount of free CPU and memory resources observed among all available nodes. + // +kubebuilder:example={"maxAllocatableResources: {\"cpu\": 1, \"memory\": \"10Gi\"}"} + MaxAllocatableResources corev1.ResourceList `json:"maxAllocatableResources,omitempty"` + // The latest detailed observations of the VirtualMachineClass resource. + Conditions []metav1.Condition `json:"conditions,omitempty"` + // Resource generation last processed by the controller. + ObservedGeneration int64 `json:"observedGeneration,omitempty"` +} + +// CpuFeatures +// Information on CPU features supported by this model. +// Shown only for `Features` or `Discovery` types. +type CpuFeatures struct { + // List of CPU features for this model. + // + // +kubebuilder:example={mmx, vmx, sse2} + Enabled []string `json:"enabled,omitempty"` + // List of unused processor features additionally available for a given group of nodes. + // + // +kubebuilder:example={ssse3, vme} + NotEnabledCommon []string `json:"notEnabledCommon,omitempty"` +} + +// VirtualMachineClassPhase defines the current resource status: +// * `Pending`: The resource is not ready and waits until the suitable nodes supporting the required CPU model are available. +// * `Ready`: The resource is ready and available for use. +// * `Terminating`: The resource is terminating. +// +// +kubebuilder:validation:Enum={Pending,Ready,Terminating} +type VirtualMachineClassPhase string + +const ( + ClassPhasePending VirtualMachineClassPhase = "Pending" + ClassPhaseReady VirtualMachineClassPhase = "Ready" + ClassPhaseTerminating VirtualMachineClassPhase = "Terminating" +) diff --git a/api/core/v1alpha3/virtual_machine_class_conversion.go b/api/core/v1alpha3/virtual_machine_class_conversion.go new file mode 100644 index 0000000000..233b260e4c --- /dev/null +++ b/api/core/v1alpha3/virtual_machine_class_conversion.go @@ -0,0 +1,23 @@ +/* +Copyright 2025 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha3 + +import "sigs.k8s.io/controller-runtime/pkg/conversion" + +var _ conversion.Hub = &VirtualMachineClass{} + +func (*VirtualMachineClass) Hub() {} diff --git a/api/core/v1alpha3/vmclasscondition/condition.go b/api/core/v1alpha3/vmclasscondition/condition.go new file mode 100644 index 0000000000..8790297ff9 --- /dev/null +++ b/api/core/v1alpha3/vmclasscondition/condition.go @@ -0,0 +1,51 @@ +/* +Copyright 2025 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vmclasscondition + +type Type string + +func (t Type) String() string { + return string(t) +} + +const ( + TypeReady Type = "Ready" + TypeDiscovered Type = "Discovered" + TypeInUse Type = "InUse" +) + +type Reason string + +func (r Reason) String() string { + return string(r) +} + +const ( + // ReasonNoCpuFeaturesEnabled determines that processor functions are not available. + ReasonNoCpuFeaturesEnabled Reason = "NoCpuFeaturesEnabled" + // ReasonNoSuitableNodesFound determines that no suitable node has been found. + ReasonNoSuitableNodesFound Reason = "NoSuitableNodesFound" + // ReasonSuitableNodesFound determines that suitable node has been found. + ReasonSuitableNodesFound Reason = "SuitableNodesFound" + + ReasonDiscoverySucceeded Reason = "DiscoverySucceeded" + ReasonDiscoverySkip Reason = "DiscoverySkip" + ReasonDiscoveryFailed Reason = "DiscoveryFailed" + + // ReasonVMClassInUse is the event reason indicating that the VMClass is being used by a virtual machine. + ReasonVMClassInUse Reason = "VirtualMachineClassInUse" +) diff --git a/api/core/v1alpha3/zz_generated.deepcopy.go b/api/core/v1alpha3/zz_generated.deepcopy.go new file mode 100644 index 0000000000..7034c5d025 --- /dev/null +++ b/api/core/v1alpha3/zz_generated.deepcopy.go @@ -0,0 +1,362 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha3 + +import ( + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CPU) DeepCopyInto(out *CPU) { + *out = *in + if in.Features != nil { + in, out := &in.Features, &out.Features + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Discovery != nil { + in, out := &in.Discovery, &out.Discovery + *out = new(CpuDiscovery) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CPU. +func (in *CPU) DeepCopy() *CPU { + if in == nil { + return nil + } + out := new(CPU) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CpuDiscovery) DeepCopyInto(out *CpuDiscovery) { + *out = *in + in.NodeSelector.DeepCopyInto(&out.NodeSelector) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CpuDiscovery. +func (in *CpuDiscovery) DeepCopy() *CpuDiscovery { + if in == nil { + return nil + } + out := new(CpuDiscovery) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CpuFeatures) DeepCopyInto(out *CpuFeatures) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.NotEnabledCommon != nil { + in, out := &in.NotEnabledCommon, &out.NotEnabledCommon + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CpuFeatures. +func (in *CpuFeatures) DeepCopy() *CpuFeatures { + if in == nil { + return nil + } + out := new(CpuFeatures) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MemoryMinMax) DeepCopyInto(out *MemoryMinMax) { + *out = *in + out.Min = in.Min.DeepCopy() + out.Max = in.Max.DeepCopy() + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MemoryMinMax. +func (in *MemoryMinMax) DeepCopy() *MemoryMinMax { + if in == nil { + return nil + } + out := new(MemoryMinMax) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NodeSelector) DeepCopyInto(out *NodeSelector) { + *out = *in + if in.MatchLabels != nil { + in, out := &in.MatchLabels, &out.MatchLabels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.MatchExpressions != nil { + in, out := &in.MatchExpressions, &out.MatchExpressions + *out = make([]v1.NodeSelectorRequirement, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeSelector. +func (in *NodeSelector) DeepCopy() *NodeSelector { + if in == nil { + return nil + } + out := new(NodeSelector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SizingPolicy) DeepCopyInto(out *SizingPolicy) { + *out = *in + if in.Memory != nil { + in, out := &in.Memory, &out.Memory + *out = new(SizingPolicyMemory) + (*in).DeepCopyInto(*out) + } + if in.CoreFractions != nil { + in, out := &in.CoreFractions, &out.CoreFractions + *out = make([]CoreFractionValue, len(*in)) + copy(*out, *in) + } + if in.DedicatedCores != nil { + in, out := &in.DedicatedCores, &out.DedicatedCores + *out = make([]bool, len(*in)) + copy(*out, *in) + } + if in.Cores != nil { + in, out := &in.Cores, &out.Cores + *out = new(SizingPolicyCores) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SizingPolicy. +func (in *SizingPolicy) DeepCopy() *SizingPolicy { + if in == nil { + return nil + } + out := new(SizingPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SizingPolicyCores) DeepCopyInto(out *SizingPolicyCores) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SizingPolicyCores. +func (in *SizingPolicyCores) DeepCopy() *SizingPolicyCores { + if in == nil { + return nil + } + out := new(SizingPolicyCores) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SizingPolicyMemory) DeepCopyInto(out *SizingPolicyMemory) { + *out = *in + in.MemoryMinMax.DeepCopyInto(&out.MemoryMinMax) + out.Step = in.Step.DeepCopy() + in.PerCore.DeepCopyInto(&out.PerCore) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SizingPolicyMemory. +func (in *SizingPolicyMemory) DeepCopy() *SizingPolicyMemory { + if in == nil { + return nil + } + out := new(SizingPolicyMemory) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SizingPolicyMemoryPerCore) DeepCopyInto(out *SizingPolicyMemoryPerCore) { + *out = *in + in.MemoryMinMax.DeepCopyInto(&out.MemoryMinMax) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SizingPolicyMemoryPerCore. +func (in *SizingPolicyMemoryPerCore) DeepCopy() *SizingPolicyMemoryPerCore { + if in == nil { + return nil + } + out := new(SizingPolicyMemoryPerCore) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VirtualMachineClass) DeepCopyInto(out *VirtualMachineClass) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualMachineClass. +func (in *VirtualMachineClass) DeepCopy() *VirtualMachineClass { + if in == nil { + return nil + } + out := new(VirtualMachineClass) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VirtualMachineClass) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VirtualMachineClassList) DeepCopyInto(out *VirtualMachineClassList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]VirtualMachineClass, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualMachineClassList. +func (in *VirtualMachineClassList) DeepCopy() *VirtualMachineClassList { + if in == nil { + return nil + } + out := new(VirtualMachineClassList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VirtualMachineClassList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VirtualMachineClassSpec) DeepCopyInto(out *VirtualMachineClassSpec) { + *out = *in + in.NodeSelector.DeepCopyInto(&out.NodeSelector) + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = make([]v1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.CPU.DeepCopyInto(&out.CPU) + if in.SizingPolicies != nil { + in, out := &in.SizingPolicies, &out.SizingPolicies + *out = make([]SizingPolicy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualMachineClassSpec. +func (in *VirtualMachineClassSpec) DeepCopy() *VirtualMachineClassSpec { + if in == nil { + return nil + } + out := new(VirtualMachineClassSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VirtualMachineClassStatus) DeepCopyInto(out *VirtualMachineClassStatus) { + *out = *in + in.CpuFeatures.DeepCopyInto(&out.CpuFeatures) + if in.AvailableNodes != nil { + in, out := &in.AvailableNodes, &out.AvailableNodes + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.MaxAllocatableResources != nil { + in, out := &in.MaxAllocatableResources, &out.MaxAllocatableResources + *out = make(v1.ResourceList, len(*in)) + for key, val := range *in { + (*out)[key] = val.DeepCopy() + } + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualMachineClassStatus. +func (in *VirtualMachineClassStatus) DeepCopy() *VirtualMachineClassStatus { + if in == nil { + return nil + } + out := new(VirtualMachineClassStatus) + in.DeepCopyInto(out) + return out +} diff --git a/api/go.mod b/api/go.mod index 0aece635b3..f64524d560 100644 --- a/api/go.mod +++ b/api/go.mod @@ -10,13 +10,15 @@ tool ( require ( github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 + github.com/onsi/ginkgo/v2 v2.22.0 + github.com/onsi/gomega v1.36.1 github.com/spf13/pflag v1.0.7 k8s.io/api v0.33.3 k8s.io/apiextensions-apiserver v0.33.3 k8s.io/apimachinery v0.33.3 k8s.io/client-go v0.33.3 - k8s.io/kube-openapi v0.0.0-20250701173324-9bd5c66d9911 kubevirt.io/api v1.3.1 + sigs.k8s.io/controller-runtime v0.21.0 ) require ( @@ -28,10 +30,12 @@ require ( github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gobuffalo/flect v1.0.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect + github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -66,10 +70,11 @@ require ( k8s.io/code-generator v0.33.3 // indirect k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f // indirect k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20250701173324-9bd5c66d9911 // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect kubevirt.io/containerized-data-importer-api v1.57.0-alpha1 // indirect kubevirt.io/controller-lifecycle-operator-sdk/api v0.0.0-20220329064328-f3cc58c6ed90 // indirect - sigs.k8s.io/controller-tools v0.18.0 // indirect + sigs.k8s.io/controller-tools v0.16.5 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect diff --git a/api/go.sum b/api/go.sum index 8bb4a2fd8b..ec3dce0a5a 100644 --- a/api/go.sum +++ b/api/go.sum @@ -62,7 +62,6 @@ github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= @@ -104,8 +103,8 @@ github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -184,8 +183,8 @@ github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xl github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= -github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= @@ -207,8 +206,8 @@ github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3ev github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= -github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= -github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/openshift/api v0.0.0-20230503133300-8bbcb7ca7183 h1:t/CahSnpqY46sQR01SoS+Jt0jtjgmhgE6lFmRnO4q70= github.com/openshift/api v0.0.0-20230503133300-8bbcb7ca7183/go.mod h1:4VWG+W22wrB4HfBL88P40DxLEpSOaiBVxUnfalfJo9k= github.com/openshift/custom-resource-status v1.1.2 h1:C3DL44LEbvlbItfd8mT5jWrqPfHnSOQoQf/sypqA6A4= @@ -575,8 +574,10 @@ kubevirt.io/containerized-data-importer-api v1.57.0-alpha1 h1:IWo12+ei3jltSN5jQN kubevirt.io/containerized-data-importer-api v1.57.0-alpha1/go.mod h1:Y/8ETgHS1GjO89bl682DPtQOYEU/1ctPFBz6Sjxm4DM= kubevirt.io/controller-lifecycle-operator-sdk/api v0.0.0-20220329064328-f3cc58c6ed90 h1:QMrd0nKP0BGbnxTqakhDZAUhGKxPiPiN5gSDqKUmGGc= kubevirt.io/controller-lifecycle-operator-sdk/api v0.0.0-20220329064328-f3cc58c6ed90/go.mod h1:018lASpFYBsYN6XwmA2TIrPCx6e0gviTd/ZNtSitKgc= -sigs.k8s.io/controller-tools v0.18.0 h1:rGxGZCZTV2wJreeRgqVoWab/mfcumTMmSwKzoM9xrsE= -sigs.k8s.io/controller-tools v0.18.0/go.mod h1:gLKoiGBriyNh+x1rWtUQnakUYEujErjXs9pf+x/8n1U= +sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= +sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= +sigs.k8s.io/controller-tools v0.16.5 h1:5k9FNRqziBPwqr17AMEPPV/En39ZBplLAdOwwQHruP4= +sigs.k8s.io/controller-tools v0.16.5/go.mod h1:8vztuRVzs8IuuJqKqbXCSlXcw+lkAv/M2sTpg55qjMY= sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= diff --git a/api/scripts/update-codegen.sh b/api/scripts/update-codegen.sh index ce1e2d98d9..7cdbb054b4 100755 --- a/api/scripts/update-codegen.sh +++ b/api/scripts/update-codegen.sh @@ -55,7 +55,7 @@ function generate::subresources { --output-file "zz_generated.openapi.go" \ --go-header-file "${SCRIPT_DIR}/boilerplate.go.txt" \ -r /dev/null \ - "${THIS_PKG}/subresources/v1alpha2" "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/version" + "${THIS_PKG}/subresources/v1alpha2" "${THIS_PKG}/core/v1alpha3" "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/version" } function generate::core { @@ -75,7 +75,7 @@ function generate::crds { OUTPUT_BASE=$(mktemp -d) trap 'rm -rf "${OUTPUT_BASE}"' ERR EXIT - go tool controller-gen crd paths="${API_ROOT}/core/v1alpha2/..." output:crd:dir="${OUTPUT_BASE}" + go tool controller-gen crd paths="${API_ROOT}/core/v1alpha2/...;${API_ROOT}/core/v1alpha3/..." output:crd:dir="${OUTPUT_BASE}" # shellcheck disable=SC2044 for file in $(find "${OUTPUT_BASE}"/* -type f -iname "*.yaml"); do diff --git a/crds/clustervirtualimages.yaml b/crds/clustervirtualimages.yaml index 93bc215d84..233ee5ca29 100644 --- a/crds/clustervirtualimages.yaml +++ b/crds/clustervirtualimages.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.16.5 labels: backup.deckhouse.io/cluster-config: "true" heritage: deckhouse diff --git a/crds/doc-ru-virtualmachineclasses.yaml b/crds/doc-ru-virtualmachineclasses.yaml index fd11d51e4f..c0abe39ad8 100644 --- a/crds/doc-ru-virtualmachineclasses.yaml +++ b/crds/doc-ru-virtualmachineclasses.yaml @@ -133,7 +133,7 @@ spec: properties: coreFractions: description: | - Допустимые значения параметра `coreFraction`. + Допустимые значения параметра `coreFraction` в процентах (например, "5%", "10%", "25%", "50%", "100%"). cores: description: | Политика применяется для заданного диапазона числа ядер CPU. diff --git a/crds/virtualdisks.yaml b/crds/virtualdisks.yaml index c00c80d46b..10a8e480c2 100644 --- a/crds/virtualdisks.yaml +++ b/crds/virtualdisks.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.16.5 labels: heritage: deckhouse module: virtualization diff --git a/crds/virtualimages.yaml b/crds/virtualimages.yaml index 700a9418b2..4503916cfd 100644 --- a/crds/virtualimages.yaml +++ b/crds/virtualimages.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.16.5 labels: heritage: deckhouse module: virtualization diff --git a/crds/virtualmachineblockdeviceattachments.yaml b/crds/virtualmachineblockdeviceattachments.yaml index 939fc91607..45e8b70eb6 100644 --- a/crds/virtualmachineblockdeviceattachments.yaml +++ b/crds/virtualmachineblockdeviceattachments.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.16.5 labels: heritage: deckhouse module: virtualization diff --git a/crds/virtualmachineclasses.yaml b/crds/virtualmachineclasses.yaml index c3fdfa794a..b98325cc2b 100644 --- a/crds/virtualmachineclasses.yaml +++ b/crds/virtualmachineclasses.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.16.5 labels: backup.deckhouse.io/cluster-config: "true" heritage: deckhouse @@ -36,6 +36,8 @@ spec: jsonPath: .metadata.creationTimestamp name: Age type: date + deprecated: true + deprecationWarning: v1alpha2.VirtualMachineClass is deprecated; use v1alpha3.VirtualMachineClass name: v1alpha2 schema: openAPIV3Schema: @@ -510,6 +512,500 @@ spec: - spec type: object served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - description: VirtualMachineClass phase. + jsonPath: .status.phase + name: Phase + type: string + - description: Default class for virtual machines without specified class. + jsonPath: .metadata.annotations.virtualmachineclass\.virtualization\.deckhouse\.io\/is-default-class + name: IsDefault + type: string + - description: Time of resource creation. + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha3 + schema: + openAPIV3Schema: + description: |- + VirtualMachineClass resource describes CPU requirements, node placement, and sizing policy for VM resources. + A resource cannot be deleted as long as it is used in one of the VMs. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + properties: + cpu: + description: CPU defines the requirements for the virtual CPU model. + properties: + discovery: + description: + Create a CPU model based on intersecting CPU features + for selected nodes. + properties: + nodeSelector: + description: + A selection of nodes to be used as the basis + for creating a universal CPU model. + properties: + matchExpressions: + description: + matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: + key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object + features: + description: |- + List of CPU instructions (features) required when type=Features. + For more information about CPU features, refer to the [libvirt docs](https://libvirt.org/formatdomain.html#cpu-model-and-topology). + example: + - mmx + - vmx + - sse2 + items: + type: string + minItems: 1 + type: array + model: + description: + CPU model name. For more information about CPU models + and topology, refer to the [libvirt docs](https://libvirt.org/formatdomain.html#cpu-model-and-topology). + example: IvyBridge + minLength: 1 + type: string + type: + description: |- + CPUType defines the CPU type, the following options are supported: + * `Host`: Uses a virtual CPU with an instruction set closely matching the platform node's CPU. + This provides high performance and functionality, as well as compatibility with "live" migration for nodes with similar processor types. + For example, VM migration between nodes with Intel and AMD processors will not work. + This is also true for different CPU generations, as their instruction set is different. + * `HostPassthrough`: Uses the platform node's physical CPU directly, without any modifications. + When using this class, the guest VM can only be transferred to a target node with a CPU exactly matching the source node's CPU. + * `Discovery`: Create a virtual CPU based on instruction sets of physical CPUs for a selected set of nodes. + * `Model`: CPU model. A CPU model is a named and previously defined set of supported CPU instructions. + * `Features`: A required set of supported instructions for the CPU. + enum: + - Host + - HostPassthrough + - Discovery + - Model + - Features + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: .spec.cpu is immutable + rule: self == oldSelf + - message: + HostPassthrough and Host cannot have model, features or + discovery + rule: + "self.type == 'HostPassthrough' || self.type == 'Host' + ? !has(self.model) && !has(self.features) && !has(self.discovery) + : true" + - message: Discovery cannot have model or features + rule: + "self.type == 'Discovery' ? !has(self.model) && !has(self.features) + : true" + - message: Model requires model and cannot have features or discovery + rule: + "self.type == 'Model' ? has(self.model) && !has(self.features) + && !has(self.discovery) : true" + - message: Features requires features and cannot have model or discovery + rule: + "self.type == 'Features' ? has(self.features) && !has(self.model) + && !has(self.discovery): true" + nodeSelector: + description: NodeSelector defines the nodes targeted for VM scheduling. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + A map of {key,value} pairs. + A single {key,value} pair in the matchLabels map is equivalent to an element of matchExpressions whose key field is "key", operator is "In", and the value array contains only "value". + The requirements are ANDed. + type: object + type: object + sizingPolicies: + items: + description: |- + SizingPolicy defines a policy for allocating computational resources to VMs. + It is represented as a list. + The cores.min - cores.max ranges for different elements of the list must not overlap. + properties: + coreFractions: + description: + Allowed values of the `coreFraction` parameter + in percentages (e.g., "5%", "10%", "25%", "50%", "100%"). + items: + description: + CoreFractionValue represents CPU core fraction + as a percentage string (e.g., "5%", "10%", "25%", "50%", + "100%"). + type: string + type: array + cores: + description: + The policy applies for a specified range of the + number of CPU cores. + properties: + max: + description: Maximum number of CPU cores. + example: 10 + maximum: 1024 + type: integer + min: + description: Minimum number of CPU cores. + example: 1 + minimum: 1 + type: integer + step: + description: + Discretization step for the CPU core number. + For example, the combination of `min=2`, `max=10`, and + `step=4` allows to set the number of virtual machine CPU + cores to 2, 6, or 10. + example: 1 + minimum: 1 + type: integer + required: + - max + - min + type: object + x-kubernetes-validations: + - message: The maximum must be greater than the minimum + rule: self.max > self.min + - message: The maximum must be greater than the step + rule: "has(self.step) ? self.max > self.step : true" + dedicatedCores: + description: Allowed values of the `dedicatedCores` parameter. + items: + type: boolean + type: array + memory: + description: Memory sizing policy. + properties: + max: + anyOf: + - type: integer + - type: string + description: Maximum amount of memory. + example: 8Gi + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + min: + anyOf: + - type: integer + - type: string + description: Minimum amount of memory. + example: 1Gi + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + perCore: + description: Amount of memory per CPU core. + properties: + max: + anyOf: + - type: integer + - type: string + description: Maximum amount of memory. + example: 8Gi + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + min: + anyOf: + - type: integer + - type: string + description: Minimum amount of memory. + example: 1Gi + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + step: + anyOf: + - type: integer + - type: string + description: + Memory size discretization step. For example, + the combination of `min=2Gi, `max=4Gi` and `step=1Gi` + allows to set the virtual machine memory size to 2Gi, + 3Gi, or 4Gi. + example: 512Mi + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + type: array + tolerations: + description: |- + Tolerations are the same as `spec.tolerations` for [pods](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/). + These tolerations will be merged with the tolerations specified in the VirtualMachine resource. VirtualMachine tolerations have a higher priority. + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + required: + - cpu + type: object + status: + properties: + availableNodes: + description: |- + List of nodes that support this CPU model. + It is not displayed for the following types: `Host`, `HostPassthrough`. + example: + - node-1 + - node-2 + items: + type: string + type: array + conditions: + description: + The latest detailed observations of the VirtualMachineClass + resource. + items: + description: + Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + cpuFeatures: + description: |- + CpuFeatures + Information on CPU features supported by this model. + Shown only for `Features` or `Discovery` types. + properties: + enabled: + description: " List of CPU features for this model." + example: + - mmx + - vmx + - sse2 + items: + type: string + type: array + notEnabledCommon: + description: + List of unused processor features additionally available + for a given group of nodes. + example: + - ssse3 + - vme + items: + type: string + type: array + type: object + maxAllocatableResources: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: + Maximum amount of free CPU and memory resources observed + among all available nodes. + example: + - 'maxAllocatableResources: {"cpu": 1, "memory": "10Gi"}' + type: object + observedGeneration: + description: Resource generation last processed by the controller. + format: int64 + type: integer + phase: + description: |- + VirtualMachineClassPhase defines the current resource status: + * `Pending`: The resource is not ready and waits until the suitable nodes supporting the required CPU model are available. + * `Ready`: The resource is ready and available for use. + * `Terminating`: The resource is terminating. + enum: + - Pending + - Ready + - Terminating + type: string + required: + - phase + type: object + required: + - spec + type: object + served: true storage: true subresources: status: {} diff --git a/crds/virtualmachineoperations.yaml b/crds/virtualmachineoperations.yaml index c96023b308..a67b8e2f84 100644 --- a/crds/virtualmachineoperations.yaml +++ b/crds/virtualmachineoperations.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.16.5 labels: heritage: deckhouse module: virtualization diff --git a/crds/virtualmachinerestores.yaml b/crds/virtualmachinerestores.yaml index eb848d5250..1d37016201 100644 --- a/crds/virtualmachinerestores.yaml +++ b/crds/virtualmachinerestores.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.16.5 labels: heritage: deckhouse module: virtualization diff --git a/crds/virtualmachinesnapshots.yaml b/crds/virtualmachinesnapshots.yaml index c1b681a99e..dcac3a221e 100644 --- a/crds/virtualmachinesnapshots.yaml +++ b/crds/virtualmachinesnapshots.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.16.5 labels: heritage: deckhouse module: virtualization diff --git a/images/hooks/go.mod b/images/hooks/go.mod index 027e9646b4..0f65b8ba81 100644 --- a/images/hooks/go.mod +++ b/images/hooks/go.mod @@ -47,7 +47,7 @@ require ( github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-containerregistry v0.17.0 // indirect - github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect + github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmoiron/sqlx v1.3.5 // indirect diff --git a/images/hooks/go.sum b/images/hooks/go.sum index d39e4d9de0..0fa95aed83 100644 --- a/images/hooks/go.sum +++ b/images/hooks/go.sum @@ -147,8 +147,9 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= diff --git a/images/virtualization-artifact/cmd/virtualization-controller/main.go b/images/virtualization-artifact/cmd/virtualization-controller/main.go index f4b0f9d26a..eb12610303 100644 --- a/images/virtualization-artifact/cmd/virtualization-controller/main.go +++ b/images/virtualization-artifact/cmd/virtualization-controller/main.go @@ -61,12 +61,14 @@ import ( "github.com/deckhouse/virtualization-controller/pkg/controller/vmsnapshot" "github.com/deckhouse/virtualization-controller/pkg/controller/volumemigration" workloadupdater "github.com/deckhouse/virtualization-controller/pkg/controller/workload-updater" + "github.com/deckhouse/virtualization-controller/pkg/crd" "github.com/deckhouse/virtualization-controller/pkg/featuregates" "github.com/deckhouse/virtualization-controller/pkg/logger" "github.com/deckhouse/virtualization-controller/pkg/migration" "github.com/deckhouse/virtualization-controller/pkg/version" "github.com/deckhouse/virtualization/api/client/kubeclient" "github.com/deckhouse/virtualization/api/core/v1alpha2" + "github.com/deckhouse/virtualization/api/core/v1alpha3" ) const ( @@ -222,6 +224,7 @@ func main() { clientgoscheme.AddToScheme, extv1.AddToScheme, v1alpha2.AddToScheme, + v1alpha3.AddToScheme, cdiv1beta1.AddToScheme, virtv1.AddToScheme, vsv1.AddToScheme, @@ -296,18 +299,23 @@ func main() { // Setup context to gracefully handle termination. ctx := signals.SetupSignalHandler() - onlyMigrationClient, err := client.New(cfg, client.Options{Scheme: scheme}) + preManagerClient, err := client.New(cfg, client.Options{Scheme: scheme}) if err != nil { log.Error(err.Error()) os.Exit(1) } - mCtrl, err := migration.NewController(onlyMigrationClient, log) + mCtrl, err := migration.NewController(preManagerClient, log) if err != nil { log.Error(err.Error()) os.Exit(1) } mCtrl.Run(ctx) + if err = crd.EnsureVMClassConversionWebhook(ctx, preManagerClient, controllerNamespace); err != nil { + log.Error("Failed to ensure VirtualMachineClass CRD conversion webhook", logger.SlogErr(err)) + os.Exit(1) + } + if err = indexer.IndexALL(ctx, mgr); err != nil { log.Error(err.Error()) os.Exit(1) diff --git a/images/virtualization-artifact/go.mod b/images/virtualization-artifact/go.mod index 7acf9e46f5..45fbb3f22a 100644 --- a/images/virtualization-artifact/go.mod +++ b/images/virtualization-artifact/go.mod @@ -83,7 +83,7 @@ require ( github.com/google/cel-go v0.23.2 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect + github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect diff --git a/images/virtualization-artifact/go.sum b/images/virtualization-artifact/go.sum index e1a15aaec0..f1157c9053 100644 --- a/images/virtualization-artifact/go.sum +++ b/images/virtualization-artifact/go.sum @@ -158,8 +158,9 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= diff --git a/images/virtualization-artifact/pkg/apiserver/api/generated/openapi/zz_generated.openapi.go b/images/virtualization-artifact/pkg/apiserver/api/generated/openapi/zz_generated.openapi.go index 02340f68ab..e21b8679f5 100644 --- a/images/virtualization-artifact/pkg/apiserver/api/generated/openapi/zz_generated.openapi.go +++ b/images/virtualization-artifact/pkg/apiserver/api/generated/openapi/zz_generated.openapi.go @@ -29,6 +29,19 @@ import ( func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { return map[string]common.OpenAPIDefinition{ + "github.com/deckhouse/virtualization/api/core/v1alpha3.CPU": schema_virtualization_api_core_v1alpha3_CPU(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha3.CpuDiscovery": schema_virtualization_api_core_v1alpha3_CpuDiscovery(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha3.CpuFeatures": schema_virtualization_api_core_v1alpha3_CpuFeatures(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha3.MemoryMinMax": schema_virtualization_api_core_v1alpha3_MemoryMinMax(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha3.NodeSelector": schema_virtualization_api_core_v1alpha3_NodeSelector(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha3.SizingPolicy": schema_virtualization_api_core_v1alpha3_SizingPolicy(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha3.SizingPolicyCores": schema_virtualization_api_core_v1alpha3_SizingPolicyCores(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha3.SizingPolicyMemory": schema_virtualization_api_core_v1alpha3_SizingPolicyMemory(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha3.SizingPolicyMemoryPerCore": schema_virtualization_api_core_v1alpha3_SizingPolicyMemoryPerCore(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha3.VirtualMachineClass": schema_virtualization_api_core_v1alpha3_VirtualMachineClass(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha3.VirtualMachineClassList": schema_virtualization_api_core_v1alpha3_VirtualMachineClassList(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha3.VirtualMachineClassSpec": schema_virtualization_api_core_v1alpha3_VirtualMachineClassSpec(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha3.VirtualMachineClassStatus": schema_virtualization_api_core_v1alpha3_VirtualMachineClassStatus(ref), "github.com/deckhouse/virtualization/api/subresources/v1alpha2.VirtualMachine": schema_virtualization_api_subresources_v1alpha2_VirtualMachine(ref), "github.com/deckhouse/virtualization/api/subresources/v1alpha2.VirtualMachineAddVolume": schema_virtualization_api_subresources_v1alpha2_VirtualMachineAddVolume(ref), "github.com/deckhouse/virtualization/api/subresources/v1alpha2.VirtualMachineCancelEvacuation": schema_virtualization_api_subresources_v1alpha2_VirtualMachineCancelEvacuation(ref), @@ -91,6 +104,580 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA } } +func schema_virtualization_api_core_v1alpha3_CPU(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "CPU defines the requirements for the virtual CPU model.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "model": { + SchemaProps: spec.SchemaProps{ + Description: "CPU model name. For more information about CPU models and topology, refer to the [libvirt docs](https://libvirt.org/formatdomain.html#cpu-model-and-topology).", + Type: []string{"string"}, + Format: "", + }, + }, + "features": { + SchemaProps: spec.SchemaProps{ + Description: "List of CPU instructions (features) required when type=Features. For more information about CPU features, refer to the [libvirt docs](https://libvirt.org/formatdomain.html#cpu-model-and-topology).", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "discovery": { + SchemaProps: spec.SchemaProps{ + Description: "Create a CPU model based on intersecting CPU features for selected nodes.", + Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha3.CpuDiscovery"), + }, + }, + }, + Required: []string{"type"}, + }, + }, + Dependencies: []string{ + "github.com/deckhouse/virtualization/api/core/v1alpha3.CpuDiscovery"}, + } +} + +func schema_virtualization_api_core_v1alpha3_CpuDiscovery(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "nodeSelector": { + SchemaProps: spec.SchemaProps{ + Description: "A selection of nodes to be used as the basis for creating a universal CPU model.", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, + } +} + +func schema_virtualization_api_core_v1alpha3_CpuFeatures(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "CpuFeatures Information on CPU features supported by this model. Shown only for `Features` or `Discovery` types.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "enabled": { + SchemaProps: spec.SchemaProps{ + Description: "List of CPU features for this model.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "notEnabledCommon": { + SchemaProps: spec.SchemaProps{ + Description: "List of unused processor features additionally available for a given group of nodes.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_virtualization_api_core_v1alpha3_MemoryMinMax(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "min": { + SchemaProps: spec.SchemaProps{ + Description: "Minimum amount of memory.", + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + "max": { + SchemaProps: spec.SchemaProps{ + Description: "Maximum amount of memory.", + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_virtualization_api_core_v1alpha3_NodeSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeSelector defines the nodes targeted for VM scheduling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "matchLabels": { + SchemaProps: spec.SchemaProps{ + Description: "A map of {key,value} pairs. A single {key,value} pair in the matchLabels map is equivalent to an element of matchExpressions whose key field is \"key\", operator is \"In\", and the value array contains only \"value\". The requirements are ANDed.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "matchExpressions": { + SchemaProps: spec.SchemaProps{ + Description: "A list of node selector requirements by node's labels.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.NodeSelectorRequirement"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeSelectorRequirement"}, + } +} + +func schema_virtualization_api_core_v1alpha3_SizingPolicy(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SizingPolicy defines a policy for allocating computational resources to VMs. It is represented as a list. The cores.min - cores.max ranges for different elements of the list must not overlap.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "memory": { + SchemaProps: spec.SchemaProps{ + Description: "Memory sizing policy.", + Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha3.SizingPolicyMemory"), + }, + }, + "coreFractions": { + SchemaProps: spec.SchemaProps{ + Description: "Allowed values of the `coreFraction` parameter in percentages (e.g., \"5%\", \"10%\", \"25%\", \"50%\", \"100%\").", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "dedicatedCores": { + SchemaProps: spec.SchemaProps{ + Description: "Allowed values of the `dedicatedCores` parameter.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + "cores": { + SchemaProps: spec.SchemaProps{ + Description: "The policy applies for a specified range of the number of CPU cores.", + Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha3.SizingPolicyCores"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/deckhouse/virtualization/api/core/v1alpha3.SizingPolicyCores", "github.com/deckhouse/virtualization/api/core/v1alpha3.SizingPolicyMemory"}, + } +} + +func schema_virtualization_api_core_v1alpha3_SizingPolicyCores(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "min": { + SchemaProps: spec.SchemaProps{ + Description: "Minimum number of CPU cores.", + Default: 0, + Type: []string{"integer"}, + Format: "int32", + }, + }, + "max": { + SchemaProps: spec.SchemaProps{ + Description: "Maximum number of CPU cores.", + Default: 0, + Type: []string{"integer"}, + Format: "int32", + }, + }, + "step": { + SchemaProps: spec.SchemaProps{ + Description: "Discretization step for the CPU core number. For example, the combination of `min=2`, `max=10`, and `step=4` allows to set the number of virtual machine CPU cores to 2, 6, or 10.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"min", "max"}, + }, + }, + } +} + +func schema_virtualization_api_core_v1alpha3_SizingPolicyMemory(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "min": { + SchemaProps: spec.SchemaProps{ + Description: "Minimum amount of memory.", + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + "max": { + SchemaProps: spec.SchemaProps{ + Description: "Maximum amount of memory.", + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + "step": { + SchemaProps: spec.SchemaProps{ + Description: "Memory size discretization step. For example, the combination of `min=2Gi, `max=4Gi` and `step=1Gi` allows to set the virtual machine memory size to 2Gi, 3Gi, or 4Gi.", + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + "perCore": { + SchemaProps: spec.SchemaProps{ + Description: "Amount of memory per CPU core.", + Default: map[string]interface{}{}, + Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha3.SizingPolicyMemoryPerCore"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/deckhouse/virtualization/api/core/v1alpha3.SizingPolicyMemoryPerCore", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_virtualization_api_core_v1alpha3_SizingPolicyMemoryPerCore(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "min": { + SchemaProps: spec.SchemaProps{ + Description: "Minimum amount of memory.", + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + "max": { + SchemaProps: spec.SchemaProps{ + Description: "Maximum amount of memory.", + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_virtualization_api_core_v1alpha3_VirtualMachineClass(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "VirtualMachineClass resource describes CPU requirements, node placement, and sizing policy for VM resources. A resource cannot be deleted as long as it is used in one of the VMs.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha3.VirtualMachineClassSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha3.VirtualMachineClassStatus"), + }, + }, + }, + Required: []string{"spec"}, + }, + }, + Dependencies: []string{ + "github.com/deckhouse/virtualization/api/core/v1alpha3.VirtualMachineClassSpec", "github.com/deckhouse/virtualization/api/core/v1alpha3.VirtualMachineClassStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_virtualization_api_core_v1alpha3_VirtualMachineClassList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "VirtualMachineClassList contains a list of VirtualMachineClasses.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "Items provides a list of VirtualMachineClasses.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha3.VirtualMachineClass"), + }, + }, + }, + }, + }, + }, + Required: []string{"metadata", "items"}, + }, + }, + Dependencies: []string{ + "github.com/deckhouse/virtualization/api/core/v1alpha3.VirtualMachineClass", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_virtualization_api_core_v1alpha3_VirtualMachineClassSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "nodeSelector": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha3.NodeSelector"), + }, + }, + "tolerations": { + SchemaProps: spec.SchemaProps{ + Description: "Tolerations are the same as `spec.tolerations` for [pods](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/). These tolerations will be merged with the tolerations specified in the VirtualMachine resource. VirtualMachine tolerations have a higher priority.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.Toleration"), + }, + }, + }, + }, + }, + "cpu": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha3.CPU"), + }, + }, + "sizingPolicies": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha3.SizingPolicy"), + }, + }, + }, + }, + }, + }, + Required: []string{"cpu"}, + }, + }, + Dependencies: []string{ + "github.com/deckhouse/virtualization/api/core/v1alpha3.CPU", "github.com/deckhouse/virtualization/api/core/v1alpha3.NodeSelector", "github.com/deckhouse/virtualization/api/core/v1alpha3.SizingPolicy", "k8s.io/api/core/v1.Toleration"}, + } +} + +func schema_virtualization_api_core_v1alpha3_VirtualMachineClassStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "phase": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "cpuFeatures": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha3.CpuFeatures"), + }, + }, + "availableNodes": { + SchemaProps: spec.SchemaProps{ + Description: "List of nodes that support this CPU model. It is not displayed for the following types: `Host`, `HostPassthrough`.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "maxAllocatableResources": { + SchemaProps: spec.SchemaProps{ + Description: "Maximum amount of free CPU and memory resources observed among all available nodes.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "conditions": { + SchemaProps: spec.SchemaProps{ + Description: "The latest detailed observations of the VirtualMachineClass resource.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"), + }, + }, + }, + }, + }, + "observedGeneration": { + SchemaProps: spec.SchemaProps{ + Description: "Resource generation last processed by the controller.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + Required: []string{"phase"}, + }, + }, + Dependencies: []string{ + "github.com/deckhouse/virtualization/api/core/v1alpha3.CpuFeatures", "k8s.io/apimachinery/pkg/api/resource.Quantity", "k8s.io/apimachinery/pkg/apis/meta/v1.Condition"}, + } +} + func schema_virtualization_api_subresources_v1alpha2_VirtualMachine(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/images/virtualization-artifact/pkg/common/testutil/testutil.go b/images/virtualization-artifact/pkg/common/testutil/testutil.go index 05fa961d64..c4e139a0a7 100644 --- a/images/virtualization-artifact/pkg/common/testutil/testutil.go +++ b/images/virtualization-artifact/pkg/common/testutil/testutil.go @@ -33,12 +33,14 @@ import ( "github.com/deckhouse/deckhouse/pkg/log" "github.com/deckhouse/virtualization-controller/pkg/controller/indexer" "github.com/deckhouse/virtualization/api/core/v1alpha2" + "github.com/deckhouse/virtualization/api/core/v1alpha3" ) func NewFakeClientWithObjects(objs ...client.Object) (client.WithWatch, error) { scheme := apiruntime.NewScheme() for _, f := range []func(*apiruntime.Scheme) error{ v1alpha2.AddToScheme, + v1alpha3.AddToScheme, virtv1.AddToScheme, cdiv1.AddToScheme, clientgoscheme.AddToScheme, @@ -67,6 +69,7 @@ func NewFakeClientWithInterceptorWithObjects(interceptor interceptor.Funcs, objs scheme := apiruntime.NewScheme() for _, f := range []func(*apiruntime.Scheme) error{ v1alpha2.AddToScheme, + v1alpha3.AddToScheme, virtv1.AddToScheme, cdiv1.AddToScheme, clientgoscheme.AddToScheme, diff --git a/images/virtualization-artifact/pkg/controller/service/virtual_machine_class_service.go b/images/virtualization-artifact/pkg/controller/service/virtual_machine_class_service.go index 420ffe66d6..4f3a755a03 100644 --- a/images/virtualization-artifact/pkg/controller/service/virtual_machine_class_service.go +++ b/images/virtualization-artifact/pkg/controller/service/virtual_machine_class_service.go @@ -22,7 +22,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "github.com/deckhouse/virtualization-controller/pkg/common/annotations" - "github.com/deckhouse/virtualization/api/core/v1alpha2" + "github.com/deckhouse/virtualization/api/core/v1alpha3" ) type VirtualMachineClassService struct { @@ -35,14 +35,14 @@ func NewVirtualMachineClassService(client client.Client) *VirtualMachineClassSer } } -func (v *VirtualMachineClassService) IsDefault(vmClass *v1alpha2.VirtualMachineClass) bool { +func (v *VirtualMachineClassService) IsDefault(vmClass *v1alpha3.VirtualMachineClass) bool { if vmClass == nil { return false } return vmClass.Annotations[annotations.AnnVirtualMachineClassDefault] == "true" } -func (v *VirtualMachineClassService) ValidateDefaultAnnotation(vmClass *v1alpha2.VirtualMachineClass) error { +func (v *VirtualMachineClassService) ValidateDefaultAnnotation(vmClass *v1alpha3.VirtualMachineClass) error { if vmClass == nil { return nil } @@ -56,12 +56,12 @@ func (v *VirtualMachineClassService) ValidateDefaultAnnotation(vmClass *v1alpha2 return nil } -func (v *VirtualMachineClassService) GetDefault(classes *v1alpha2.VirtualMachineClassList) (*v1alpha2.VirtualMachineClass, error) { +func (v *VirtualMachineClassService) GetDefault(classes *v1alpha3.VirtualMachineClassList) (*v1alpha3.VirtualMachineClass, error) { if classes == nil { return nil, nil } - var defaultClass *v1alpha2.VirtualMachineClass + var defaultClass *v1alpha3.VirtualMachineClass for i := range classes.Items { if !v.IsDefault(&classes.Items[i]) { continue diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/defaulter/defaulters_suite_test.go b/images/virtualization-artifact/pkg/controller/vm/internal/defaulter/defaulters_suite_test.go index 35c9398e98..242747ce35 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/defaulter/defaulters_suite_test.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/defaulter/defaulters_suite_test.go @@ -29,6 +29,7 @@ import ( "github.com/deckhouse/virtualization-controller/pkg/controller/service" "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/defaulter" "github.com/deckhouse/virtualization/api/core/v1alpha2" + "github.com/deckhouse/virtualization/api/core/v1alpha3" ) func TestDefaulters(t *testing.T) { @@ -50,21 +51,21 @@ var _ = Describe("Set default class in virtualMachineClasName", func() { classDefaulter = defaulter.NewVirtualMachineClassNameDefaulter(fakeClient, vmClassService) } - newVMClass := func(name string) *v1alpha2.VirtualMachineClass { - return &v1alpha2.VirtualMachineClass{ + newVMClass := func(name string) *v1alpha3.VirtualMachineClass { + return &v1alpha3.VirtualMachineClass{ TypeMeta: metav1.TypeMeta{ - Kind: v1alpha2.VirtualMachineClassKind, - APIVersion: v1alpha2.SchemeGroupVersion.String(), + Kind: v1alpha3.VirtualMachineClassKind, + APIVersion: v1alpha3.SchemeGroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ Name: name, }, - Spec: v1alpha2.VirtualMachineClassSpec{}, - Status: v1alpha2.VirtualMachineClassStatus{}, + Spec: v1alpha3.VirtualMachineClassSpec{}, + Status: v1alpha3.VirtualMachineClassStatus{}, } } - newDefaultVMClass := func(name string) *v1alpha2.VirtualMachineClass { + newDefaultVMClass := func(name string) *v1alpha3.VirtualMachineClass { vmClass := newVMClass(name) vmClass.Annotations = map[string]string{ annotations.AnnVirtualMachineClassDefault: "true", diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/defaulter/virtual_machine_class_name.go b/images/virtualization-artifact/pkg/controller/vm/internal/defaulter/virtual_machine_class_name.go index 834afb6f5f..22298d574c 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/defaulter/virtual_machine_class_name.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/defaulter/virtual_machine_class_name.go @@ -24,6 +24,7 @@ import ( "github.com/deckhouse/virtualization-controller/pkg/controller/service" "github.com/deckhouse/virtualization/api/core/v1alpha2" + "github.com/deckhouse/virtualization/api/core/v1alpha3" ) type VirtualMachineClassNameDefaulter struct { @@ -45,7 +46,7 @@ func (v *VirtualMachineClassNameDefaulter) Default(ctx context.Context, vm *v1al } // Detect and assign default class name. - classes := &v1alpha2.VirtualMachineClassList{} + classes := &v1alpha3.VirtualMachineClassList{} err := v.client.List(ctx, classes) if err != nil { return fmt.Errorf("failed to list virtual machine classes: %w", err) diff --git a/images/virtualization-artifact/pkg/controller/vmclass/internal/validators/policy_changes_validator.go b/images/virtualization-artifact/pkg/controller/vmclass/internal/validators/policy_changes_validator.go index 104f340060..177494455c 100644 --- a/images/virtualization-artifact/pkg/controller/vmclass/internal/validators/policy_changes_validator.go +++ b/images/virtualization-artifact/pkg/controller/vmclass/internal/validators/policy_changes_validator.go @@ -25,6 +25,7 @@ import ( "github.com/deckhouse/virtualization-controller/pkg/eventrecord" "github.com/deckhouse/virtualization/api/core/v1alpha2" + "github.com/deckhouse/virtualization/api/core/v1alpha3" ) type PolicyChangesValidator struct { @@ -35,11 +36,11 @@ func NewPolicyChangesValidator(recorder eventrecord.EventRecorderLogger) *Policy return &PolicyChangesValidator{recorder: recorder} } -func (v *PolicyChangesValidator) ValidateCreate(_ context.Context, _ *v1alpha2.VirtualMachineClass) (admission.Warnings, error) { +func (v *PolicyChangesValidator) ValidateCreate(_ context.Context, _ *v1alpha3.VirtualMachineClass) (admission.Warnings, error) { return nil, nil } -func (v *PolicyChangesValidator) ValidateUpdate(_ context.Context, oldVMClass, newVMClass *v1alpha2.VirtualMachineClass) (admission.Warnings, error) { +func (v *PolicyChangesValidator) ValidateUpdate(_ context.Context, oldVMClass, newVMClass *v1alpha3.VirtualMachineClass) (admission.Warnings, error) { if !reflect.DeepEqual(oldVMClass.Spec.SizingPolicies, newVMClass.Spec.SizingPolicies) { v.recorder.Event(newVMClass, corev1.EventTypeNormal, v1alpha2.ReasonVMClassSizingPoliciesWereChanged, "Sizing policies were changed") } diff --git a/images/virtualization-artifact/pkg/controller/vmclass/internal/validators/single_default_class_validator.go b/images/virtualization-artifact/pkg/controller/vmclass/internal/validators/single_default_class_validator.go index 1fa89031c9..a73d294517 100644 --- a/images/virtualization-artifact/pkg/controller/vmclass/internal/validators/single_default_class_validator.go +++ b/images/virtualization-artifact/pkg/controller/vmclass/internal/validators/single_default_class_validator.go @@ -25,7 +25,7 @@ import ( "github.com/deckhouse/virtualization-controller/pkg/common/annotations" "github.com/deckhouse/virtualization-controller/pkg/controller/service" - "github.com/deckhouse/virtualization/api/core/v1alpha2" + "github.com/deckhouse/virtualization/api/core/v1alpha3" ) type SingleDefaultClassValidator struct { @@ -40,7 +40,7 @@ func NewSingleDefaultClassValidator(client client.Client, vmClassService *servic } } -func (v *SingleDefaultClassValidator) ValidateCreate(ctx context.Context, vmClass *v1alpha2.VirtualMachineClass) (admission.Warnings, error) { +func (v *SingleDefaultClassValidator) ValidateCreate(ctx context.Context, vmClass *v1alpha3.VirtualMachineClass) (admission.Warnings, error) { err := v.vmClassService.ValidateDefaultAnnotation(vmClass) if err != nil { return nil, err @@ -54,7 +54,7 @@ func (v *SingleDefaultClassValidator) ValidateCreate(ctx context.Context, vmClas return nil, nil } -func (v *SingleDefaultClassValidator) ValidateUpdate(ctx context.Context, _, newVMClass *v1alpha2.VirtualMachineClass) (admission.Warnings, error) { +func (v *SingleDefaultClassValidator) ValidateUpdate(ctx context.Context, _, newVMClass *v1alpha3.VirtualMachineClass) (admission.Warnings, error) { err := v.vmClassService.ValidateDefaultAnnotation(newVMClass) if err != nil { return nil, err @@ -68,8 +68,8 @@ func (v *SingleDefaultClassValidator) ValidateUpdate(ctx context.Context, _, new return nil, nil } -func (v *SingleDefaultClassValidator) checkDefaultIsSingle(ctx context.Context, vmClass *v1alpha2.VirtualMachineClass) error { - classes := &v1alpha2.VirtualMachineClassList{} +func (v *SingleDefaultClassValidator) checkDefaultIsSingle(ctx context.Context, vmClass *v1alpha3.VirtualMachineClass) error { + classes := &v1alpha3.VirtualMachineClassList{} err := v.client.List(ctx, classes) if err != nil { return fmt.Errorf("failed to list virtual machine classes: %w", err) diff --git a/images/virtualization-artifact/pkg/controller/vmclass/internal/validators/sizing_policies_validator.go b/images/virtualization-artifact/pkg/controller/vmclass/internal/validators/sizing_policies_validator.go index d31d65029d..0d6ea89a53 100644 --- a/images/virtualization-artifact/pkg/controller/vmclass/internal/validators/sizing_policies_validator.go +++ b/images/virtualization-artifact/pkg/controller/vmclass/internal/validators/sizing_policies_validator.go @@ -19,11 +19,12 @@ package validators import ( "context" "fmt" + "regexp" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - "github.com/deckhouse/virtualization/api/core/v1alpha2" + "github.com/deckhouse/virtualization/api/core/v1alpha3" ) type SizingPoliciesValidator struct { @@ -34,7 +35,11 @@ func NewSizingPoliciesValidator(client client.Client) *SizingPoliciesValidator { return &SizingPoliciesValidator{client: client} } -func (v *SizingPoliciesValidator) ValidateCreate(_ context.Context, vmclass *v1alpha2.VirtualMachineClass) (admission.Warnings, error) { +func (v *SizingPoliciesValidator) ValidateCreate(_ context.Context, vmclass *v1alpha3.VirtualMachineClass) (admission.Warnings, error) { + if err := validateCoreFractions(vmclass); err != nil { + return nil, err + } + if !HasValidCores(&vmclass.Spec) { return nil, fmt.Errorf("vmclass %s has sizing policies but none of them specify cores", vmclass.Name) } @@ -46,7 +51,11 @@ func (v *SizingPoliciesValidator) ValidateCreate(_ context.Context, vmclass *v1a return nil, nil } -func (v *SizingPoliciesValidator) ValidateUpdate(_ context.Context, _, newVMClass *v1alpha2.VirtualMachineClass) (admission.Warnings, error) { +func (v *SizingPoliciesValidator) ValidateUpdate(_ context.Context, _, newVMClass *v1alpha3.VirtualMachineClass) (admission.Warnings, error) { + if err := validateCoreFractions(newVMClass); err != nil { + return nil, err + } + if HasCPUSizePoliciesCrosses(&newVMClass.Spec) { return nil, fmt.Errorf("vmclass %s has size policy cpu crosses", newVMClass.Name) } @@ -54,7 +63,7 @@ func (v *SizingPoliciesValidator) ValidateUpdate(_ context.Context, _, newVMClas return nil, nil } -func HasCPUSizePoliciesCrosses(vmclass *v1alpha2.VirtualMachineClassSpec) bool { +func HasCPUSizePoliciesCrosses(vmclass *v1alpha3.VirtualMachineClassSpec) bool { usedPairs := make(map[[2]int]struct{}) for i, policy1 := range vmclass.SizingPolicies { @@ -87,7 +96,7 @@ func HasCPUSizePoliciesCrosses(vmclass *v1alpha2.VirtualMachineClassSpec) bool { return false } -func HasValidCores(vmclass *v1alpha2.VirtualMachineClassSpec) bool { +func HasValidCores(vmclass *v1alpha3.VirtualMachineClassSpec) bool { if len(vmclass.SizingPolicies) == 0 { return true } @@ -99,3 +108,14 @@ func HasValidCores(vmclass *v1alpha2.VirtualMachineClassSpec) bool { } return true } + +func validateCoreFractions(vmclass *v1alpha3.VirtualMachineClass) error { + for i, policy := range vmclass.Spec.SizingPolicies { + for j, coreFraction := range policy.CoreFractions { + if !regexp.MustCompile(`^([1-9]|[1-9][0-9]|100)%$`).MatchString(string(coreFraction)) { + return fmt.Errorf("spec.sizingPolicies[%d].coreFractions[%d]: coreFraction must be a percentage between 1%% and 100%% (e.g., 5%%, 10%%, 50%%), got %q", i, j, coreFraction) + } + } + } + return nil +} diff --git a/images/virtualization-artifact/pkg/controller/vmclass/internal/validators/validators_suite_test.go b/images/virtualization-artifact/pkg/controller/vmclass/internal/validators/validators_suite_test.go index 0bd0d5d38b..0441f0f74d 100644 --- a/images/virtualization-artifact/pkg/controller/vmclass/internal/validators/validators_suite_test.go +++ b/images/virtualization-artifact/pkg/controller/vmclass/internal/validators/validators_suite_test.go @@ -28,7 +28,7 @@ import ( "github.com/deckhouse/virtualization-controller/pkg/common/testutil" "github.com/deckhouse/virtualization-controller/pkg/controller/service" "github.com/deckhouse/virtualization-controller/pkg/controller/vmclass/internal/validators" - "github.com/deckhouse/virtualization/api/core/v1alpha2" + "github.com/deckhouse/virtualization/api/core/v1alpha3" ) func TestValidators(t *testing.T) { @@ -37,11 +37,11 @@ func TestValidators(t *testing.T) { } var _ = Describe("Spec policies validator", func() { - var vmclass v1alpha2.VirtualMachineClass + var vmclass v1alpha3.VirtualMachineClass Context("empty vmclass", func() { BeforeEach(func() { - vmclass = v1alpha2.VirtualMachineClass{} + vmclass = v1alpha3.VirtualMachineClass{} }) It("Should return no problem when empty value", func() { @@ -51,23 +51,23 @@ var _ = Describe("Spec policies validator", func() { Context("vmclass with no cpu size policies crosses", func() { BeforeEach(func() { - vmclass = v1alpha2.VirtualMachineClass{} - vmclass.Spec.SizingPolicies = append(vmclass.Spec.SizingPolicies, v1alpha2.SizingPolicy{ - Cores: &v1alpha2.SizingPolicyCores{ + vmclass = v1alpha3.VirtualMachineClass{} + vmclass.Spec.SizingPolicies = append(vmclass.Spec.SizingPolicies, v1alpha3.SizingPolicy{ + Cores: &v1alpha3.SizingPolicyCores{ Min: 1, Max: 4, Step: 1, }, }) - vmclass.Spec.SizingPolicies = append(vmclass.Spec.SizingPolicies, v1alpha2.SizingPolicy{ - Cores: &v1alpha2.SizingPolicyCores{ + vmclass.Spec.SizingPolicies = append(vmclass.Spec.SizingPolicies, v1alpha3.SizingPolicy{ + Cores: &v1alpha3.SizingPolicyCores{ Min: 5, Max: 9, Step: 1, }, }) - vmclass.Spec.SizingPolicies = append(vmclass.Spec.SizingPolicies, v1alpha2.SizingPolicy{ - Cores: &v1alpha2.SizingPolicyCores{ + vmclass.Spec.SizingPolicies = append(vmclass.Spec.SizingPolicies, v1alpha3.SizingPolicy{ + Cores: &v1alpha3.SizingPolicyCores{ Min: 10, Max: 15, Step: 1, @@ -82,23 +82,23 @@ var _ = Describe("Spec policies validator", func() { Context("vmclass with cpu size policies crosses", func() { BeforeEach(func() { - vmclass = v1alpha2.VirtualMachineClass{} - vmclass.Spec.SizingPolicies = append(vmclass.Spec.SizingPolicies, v1alpha2.SizingPolicy{ - Cores: &v1alpha2.SizingPolicyCores{ + vmclass = v1alpha3.VirtualMachineClass{} + vmclass.Spec.SizingPolicies = append(vmclass.Spec.SizingPolicies, v1alpha3.SizingPolicy{ + Cores: &v1alpha3.SizingPolicyCores{ Min: 1, Max: 4, Step: 1, }, }) - vmclass.Spec.SizingPolicies = append(vmclass.Spec.SizingPolicies, v1alpha2.SizingPolicy{ - Cores: &v1alpha2.SizingPolicyCores{ + vmclass.Spec.SizingPolicies = append(vmclass.Spec.SizingPolicies, v1alpha3.SizingPolicy{ + Cores: &v1alpha3.SizingPolicyCores{ Min: 4, Max: 9, Step: 1, }, }) - vmclass.Spec.SizingPolicies = append(vmclass.Spec.SizingPolicies, v1alpha2.SizingPolicy{ - Cores: &v1alpha2.SizingPolicyCores{ + vmclass.Spec.SizingPolicies = append(vmclass.Spec.SizingPolicies, v1alpha3.SizingPolicy{ + Cores: &v1alpha3.SizingPolicyCores{ Min: 10, Max: 15, Step: 1, @@ -126,21 +126,21 @@ var _ = Describe("Single default class validator", func() { validator = validators.NewSingleDefaultClassValidator(fakeClient, vmClassService) } - newVMClass := func(name string) *v1alpha2.VirtualMachineClass { - return &v1alpha2.VirtualMachineClass{ + newVMClass := func(name string) *v1alpha3.VirtualMachineClass { + return &v1alpha3.VirtualMachineClass{ TypeMeta: metav1.TypeMeta{ - Kind: v1alpha2.VirtualMachineClassKind, - APIVersion: v1alpha2.SchemeGroupVersion.String(), + Kind: v1alpha3.VirtualMachineClassKind, + APIVersion: v1alpha3.SchemeGroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ Name: name, }, - Spec: v1alpha2.VirtualMachineClassSpec{}, - Status: v1alpha2.VirtualMachineClassStatus{}, + Spec: v1alpha3.VirtualMachineClassSpec{}, + Status: v1alpha3.VirtualMachineClassStatus{}, } } - newDefaultVMClass := func(name string) *v1alpha2.VirtualMachineClass { + newDefaultVMClass := func(name string) *v1alpha3.VirtualMachineClass { vmClass := newVMClass(name) vmClass.Annotations = map[string]string{ annotations.AnnVirtualMachineClassDefault: "true", diff --git a/images/virtualization-artifact/pkg/controller/vmclass/vmclass_controller.go b/images/virtualization-artifact/pkg/controller/vmclass/vmclass_controller.go index 2653fdce7a..96033a289c 100644 --- a/images/virtualization-artifact/pkg/controller/vmclass/vmclass_controller.go +++ b/images/virtualization-artifact/pkg/controller/vmclass/vmclass_controller.go @@ -31,6 +31,7 @@ import ( "github.com/deckhouse/virtualization-controller/pkg/eventrecord" "github.com/deckhouse/virtualization-controller/pkg/logger" "github.com/deckhouse/virtualization/api/core/v1alpha2" + "github.com/deckhouse/virtualization/api/core/v1alpha3" ) const ( @@ -58,7 +59,6 @@ func NewController( RecoverPanic: ptr.To(true), LogConstructor: logger.NewConstructor(log), CacheSyncTimeout: 10 * time.Minute, - UsePriorityQueue: ptr.To(true), }) if err != nil { return nil, err @@ -68,9 +68,18 @@ func NewController( return nil, err } + validator := NewValidator(mgr.GetClient(), log, recorder, vmClassService) + if err = builder.WebhookManagedBy(mgr). For(&v1alpha2.VirtualMachineClass{}). - WithValidator(NewValidator(mgr.GetClient(), log, recorder, vmClassService)). + WithValidator(validator). + Complete(); err != nil { + return nil, err + } + + if err = builder.WebhookManagedBy(mgr). + For(&v1alpha3.VirtualMachineClass{}). + WithValidator(validator). Complete(); err != nil { return nil, err } diff --git a/images/virtualization-artifact/pkg/controller/vmclass/vmclass_webhook.go b/images/virtualization-artifact/pkg/controller/vmclass/vmclass_webhook.go index 6fa9008c4e..3c41129e3f 100644 --- a/images/virtualization-artifact/pkg/controller/vmclass/vmclass_webhook.go +++ b/images/virtualization-artifact/pkg/controller/vmclass/vmclass_webhook.go @@ -29,11 +29,12 @@ import ( "github.com/deckhouse/virtualization-controller/pkg/controller/vmclass/internal/validators" "github.com/deckhouse/virtualization-controller/pkg/eventrecord" "github.com/deckhouse/virtualization/api/core/v1alpha2" + "github.com/deckhouse/virtualization/api/core/v1alpha3" ) type VirtualMachineClassValidator interface { - ValidateCreate(ctx context.Context, vm *v1alpha2.VirtualMachineClass) (admission.Warnings, error) - ValidateUpdate(ctx context.Context, oldVM, newVM *v1alpha2.VirtualMachineClass) (admission.Warnings, error) + ValidateCreate(ctx context.Context, vm *v1alpha3.VirtualMachineClass) (admission.Warnings, error) + ValidateUpdate(ctx context.Context, oldVM, newVM *v1alpha3.VirtualMachineClass) (admission.Warnings, error) } type Validator struct { @@ -53,9 +54,18 @@ func NewValidator(client client.Client, log *log.Logger, recorder eventrecord.Ev } func (v *Validator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - vmclass, ok := obj.(*v1alpha2.VirtualMachineClass) - if !ok { - return nil, fmt.Errorf("expected a new VirtualMachine but got a %T", obj) + var vmclass *v1alpha3.VirtualMachineClass + + switch o := obj.(type) { + case *v1alpha3.VirtualMachineClass: + vmclass = o + case *v1alpha2.VirtualMachineClass: + vmclass = &v1alpha3.VirtualMachineClass{} + if err := o.ConvertTo(vmclass); err != nil { + return nil, fmt.Errorf("failed to convert v1alpha2 to v1alpha3: %w", err) + } + default: + return nil, fmt.Errorf("expected a VirtualMachineClass but got a %T", obj) } var warnings admission.Warnings @@ -72,13 +82,29 @@ func (v *Validator) ValidateCreate(ctx context.Context, obj runtime.Object) (adm } func (v *Validator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { - oldVMClass, ok := oldObj.(*v1alpha2.VirtualMachineClass) - if !ok { + var oldVMClass, newVMClass *v1alpha3.VirtualMachineClass + + switch o := oldObj.(type) { + case *v1alpha3.VirtualMachineClass: + oldVMClass = o + case *v1alpha2.VirtualMachineClass: + oldVMClass = &v1alpha3.VirtualMachineClass{} + if err := o.ConvertTo(oldVMClass); err != nil { + return nil, fmt.Errorf("failed to convert old v1alpha2 to v1alpha3: %w", err) + } + default: return nil, fmt.Errorf("expected an old VirtualMachineClass but got a %T", oldObj) } - newVMClass, ok := newObj.(*v1alpha2.VirtualMachineClass) - if !ok { + switch n := newObj.(type) { + case *v1alpha3.VirtualMachineClass: + newVMClass = n + case *v1alpha2.VirtualMachineClass: + newVMClass = &v1alpha3.VirtualMachineClass{} + if err := n.ConvertTo(newVMClass); err != nil { + return nil, fmt.Errorf("failed to convert new v1alpha2 to v1alpha3: %w", err) + } + default: return nil, fmt.Errorf("expected a new VirtualMachineClass but got a %T", newObj) } diff --git a/images/virtualization-artifact/pkg/crd/ensure_conversion.go b/images/virtualization-artifact/pkg/crd/ensure_conversion.go new file mode 100644 index 0000000000..cc8f1c90f7 --- /dev/null +++ b/images/virtualization-artifact/pkg/crd/ensure_conversion.go @@ -0,0 +1,74 @@ +/* +Copyright 2025 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package crd + +import ( + "context" + "fmt" + "os" + + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +const ( + vmClassCRDName = "virtualmachineclasses.virtualization.deckhouse.io" + tlsCertPath = "/tmp/k8s-webhook-server/serving-certs/ca.crt" +) + +// EnsureVMClassConversionWebhook configures the VirtualMachineClass CRD to use webhook-based conversion. +// This allows Kubernetes to convert between v1alpha2 (storage version) and v1alpha3 (served version). +// Returns nil if conversion is already configured, otherwise returns an error. +func EnsureVMClassConversionWebhook(ctx context.Context, c client.Client, controllerNamespace string) error { + logger := log.FromContext(ctx).WithName("crd-conversion") + + crd := &apiextensionsv1.CustomResourceDefinition{} + if err := c.Get(ctx, client.ObjectKey{Name: vmClassCRDName}, crd); err != nil { + return fmt.Errorf("get VirtualMachineClass CRD: %w", err) + } + + caBundle, err := os.ReadFile(tlsCertPath) + if err != nil { + return fmt.Errorf("read TLS CA certificate from %s: %w", tlsCertPath, err) + } + + crd.Spec.Conversion = &apiextensionsv1.CustomResourceConversion{ + Strategy: apiextensionsv1.WebhookConverter, + Webhook: &apiextensionsv1.WebhookConversion{ + ClientConfig: &apiextensionsv1.WebhookClientConfig{ + Service: &apiextensionsv1.ServiceReference{ + Name: "virtualization-controller", + Namespace: controllerNamespace, + Path: ptr.To("/convert"), + Port: ptr.To[int32](443), + }, + CABundle: caBundle, + }, + ConversionReviewVersions: []string{"v1"}, + }, + } + + if err := c.Update(ctx, crd); err != nil { + logger.Error(err, "Failed to update VirtualMachineClass CRD with conversion webhook configuration") + return fmt.Errorf("update CRD with conversion webhook: %w", err) + } + + logger.Info("Successfully configured VirtualMachineClass CRD with webhook conversion") + return nil +} diff --git a/src/cli/go.mod b/src/cli/go.mod index 4d728824a2..bd767e6226 100644 --- a/src/cli/go.mod +++ b/src/cli/go.mod @@ -69,6 +69,7 @@ require ( kubevirt.io/api v1.3.1 // indirect kubevirt.io/containerized-data-importer-api v1.57.0-alpha1 // indirect kubevirt.io/controller-lifecycle-operator-sdk/api v0.0.0-20220329064328-f3cc58c6ed90 // indirect + sigs.k8s.io/controller-runtime v0.21.0 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect diff --git a/src/cli/go.sum b/src/cli/go.sum index a6acb020c5..7c203f2035 100644 --- a/src/cli/go.sum +++ b/src/cli/go.sum @@ -191,8 +191,8 @@ github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xl github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= -github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= @@ -214,8 +214,8 @@ github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3ev github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= -github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/openshift/api v0.0.0-20230503133300-8bbcb7ca7183 h1:t/CahSnpqY46sQR01SoS+Jt0jtjgmhgE6lFmRnO4q70= github.com/openshift/api v0.0.0-20230503133300-8bbcb7ca7183/go.mod h1:4VWG+W22wrB4HfBL88P40DxLEpSOaiBVxUnfalfJo9k= github.com/openshift/custom-resource-status v1.1.2 h1:C3DL44LEbvlbItfd8mT5jWrqPfHnSOQoQf/sypqA6A4= @@ -593,6 +593,8 @@ kubevirt.io/containerized-data-importer-api v1.57.0-alpha1 h1:IWo12+ei3jltSN5jQN kubevirt.io/containerized-data-importer-api v1.57.0-alpha1/go.mod h1:Y/8ETgHS1GjO89bl682DPtQOYEU/1ctPFBz6Sjxm4DM= kubevirt.io/controller-lifecycle-operator-sdk/api v0.0.0-20220329064328-f3cc58c6ed90 h1:QMrd0nKP0BGbnxTqakhDZAUhGKxPiPiN5gSDqKUmGGc= kubevirt.io/controller-lifecycle-operator-sdk/api v0.0.0-20220329064328-f3cc58c6ed90/go.mod h1:018lASpFYBsYN6XwmA2TIrPCx6e0gviTd/ZNtSitKgc= +sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= +sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= @@ -603,7 +605,6 @@ sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxO sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= -sigs.k8s.io/structured-merge-diff/v6 v6.2.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= diff --git a/templates/virtualization-controller/rbac-for-us.yaml b/templates/virtualization-controller/rbac-for-us.yaml index c80363ec94..5355971813 100644 --- a/templates/virtualization-controller/rbac-for-us.yaml +++ b/templates/virtualization-controller/rbac-for-us.yaml @@ -249,6 +249,14 @@ rules: - get - list - watch +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get + - update + - patch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/templates/virtualization-controller/validation-webhook.yaml b/templates/virtualization-controller/validation-webhook.yaml index 87d3655cb5..9ca31adaa8 100644 --- a/templates/virtualization-controller/validation-webhook.yaml +++ b/templates/virtualization-controller/validation-webhook.yaml @@ -209,7 +209,7 @@ webhooks: {{ .Values.virtualization.internal.controller.cert.ca | b64enc }} admissionReviewVersions: ["v1"] sideEffects: None - - name: "vmclass.virtualization-controller.validate.d8-virtualization" + - name: "vmclass-v1alpha2.virtualization-controller.validate.d8-virtualization" rules: - apiGroups: ["virtualization.deckhouse.io"] apiVersions: ["v1alpha2"] @@ -226,6 +226,25 @@ webhooks: {{ .Values.virtualization.internal.controller.cert.ca | b64enc }} admissionReviewVersions: ["v1"] sideEffects: None + matchPolicy: Exact + - name: "vmclass-v1alpha3.virtualization-controller.validate.d8-virtualization" + rules: + - apiGroups: ["virtualization.deckhouse.io"] + apiVersions: ["v1alpha3"] + operations: ["CREATE", "UPDATE"] + resources: ["virtualmachineclasses"] + scope: "Cluster" + clientConfig: + service: + namespace: d8-{{ .Chart.Name }} + name: virtualization-controller + path: /validate-virtualization-deckhouse-io-v1alpha3-virtualmachineclass + port: 443 + caBundle: | + {{ .Values.virtualization.internal.controller.cert.ca | b64enc }} + admissionReviewVersions: ["v1"] + sideEffects: None + matchPolicy: Exact - name: "moduleconfig.virtualization-controller.validate.d8-virtualization" rules: - apiGroups: ["deckhouse.io"] diff --git a/test/e2e/go.mod b/test/e2e/go.mod index 34da7ca6ef..03f982b199 100644 --- a/test/e2e/go.mod +++ b/test/e2e/go.mod @@ -9,7 +9,6 @@ require ( github.com/deckhouse/virtualization/api v0.0.0-20240923080356-bb5809dba578 github.com/onsi/ginkgo/v2 v2.22.0 github.com/onsi/gomega v1.36.1 - golang.org/x/sync v0.14.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.33.3 k8s.io/apimachinery v0.33.3 @@ -38,7 +37,7 @@ require ( github.com/google/btree v1.1.3 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect + github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect @@ -64,6 +63,7 @@ require ( go.yaml.in/yaml/v3 v3.0.3 // indirect golang.org/x/net v0.39.0 // indirect golang.org/x/oauth2 v0.27.0 // indirect + golang.org/x/sync v0.14.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/term v0.32.0 // indirect golang.org/x/text v0.25.0 // indirect diff --git a/test/e2e/go.sum b/test/e2e/go.sum index f59519457d..bcef76dedb 100644 --- a/test/e2e/go.sum +++ b/test/e2e/go.sum @@ -111,8 +111,8 @@ github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= diff --git a/test/e2e/legacy/sizing_policy.go b/test/e2e/legacy/sizing_policy.go index a2d644908e..90539c832a 100644 --- a/test/e2e/legacy/sizing_policy.go +++ b/test/e2e/legacy/sizing_policy.go @@ -19,7 +19,6 @@ package legacy import ( "fmt" "slices" - "strconv" "strings" . "github.com/onsi/ginkgo/v2" @@ -28,6 +27,7 @@ import ( "github.com/deckhouse/virtualization/api/core/v1alpha2" "github.com/deckhouse/virtualization/api/core/v1alpha2/vmcondition" + "github.com/deckhouse/virtualization/api/core/v1alpha3" kc "github.com/deckhouse/virtualization/test/e2e/internal/kubectl" "github.com/deckhouse/virtualization/test/e2e/internal/util" ) @@ -177,7 +177,7 @@ var _ = Describe("SizingPolicy", Ordered, func() { }) It("creates new `VirtualMachineClass`", func() { - vmClass := v1alpha2.VirtualMachineClass{} + vmClass := v1alpha3.VirtualMachineClass{} err := GetObject(kc.ResourceVMClass, vmClassDiscovery, &vmClass, kc.GetOptions{}) Expect(err).NotTo(HaveOccurred()) vmClass.Name = vmClassDiscoveryCopy @@ -214,7 +214,7 @@ var _ = Describe("SizingPolicy", Ordered, func() { Expect(res.Error()).NotTo(HaveOccurred(), res.StdErr()) vms := strings.Split(res.StdOut(), " ") - vmClass := v1alpha2.VirtualMachineClass{} + vmClass := v1alpha3.VirtualMachineClass{} err := GetObject(kc.ResourceVMClass, vmClassDiscovery, &vmClass, kc.GetOptions{}) Expect(err).NotTo(HaveOccurred()) @@ -238,8 +238,8 @@ var _ = Describe("SizingPolicy", Ordered, func() { }) }) -func ValidateVirtualMachineByClass(virtualMachineClass *v1alpha2.VirtualMachineClass, virtualMachine *v1alpha2.VirtualMachine) { - var sizingPolicy v1alpha2.SizingPolicy +func ValidateVirtualMachineByClass(virtualMachineClass *v1alpha3.VirtualMachineClass, virtualMachine *v1alpha2.VirtualMachine) { + var sizingPolicy v1alpha3.SizingPolicy for _, p := range virtualMachineClass.Spec.SizingPolicies { if virtualMachine.Spec.CPU.Cores >= p.Cores.Min && virtualMachine.Spec.CPU.Cores <= p.Cores.Max { sizingPolicy = *p.DeepCopy() @@ -252,9 +252,7 @@ func ValidateVirtualMachineByClass(virtualMachineClass *v1alpha2.VirtualMachineC checkMemory := checkMinMemory && checkMaxMemory Expect(checkMemory).To(BeTrue(), fmt.Errorf("memory size outside of possible interval '%v - %v': %v", sizingPolicy.Memory.Min, sizingPolicy.Memory.Max, virtualMachine.Spec.Memory.Size)) - coreFraction, err := strconv.Atoi(strings.ReplaceAll(virtualMachine.Spec.CPU.CoreFraction, "%", "")) - Expect(err).NotTo(HaveOccurred(), "cannot convert CoreFraction value to integer: %s", err) - checkCoreFraction := slices.Contains(sizingPolicy.CoreFractions, v1alpha2.CoreFractionValue(coreFraction)) + checkCoreFraction := slices.Contains(sizingPolicy.CoreFractions, v1alpha3.CoreFractionValue(virtualMachine.Spec.CPU.CoreFraction)) Expect(checkCoreFraction).To(BeTrue(), fmt.Errorf("sizing policy core fraction list does not contain value from spec: %s\n%v", virtualMachine.Spec.CPU.CoreFraction, sizingPolicy.CoreFractions)) } diff --git a/test/e2e/legacy/testdata/affinity-toleration/transformer.yaml b/test/e2e/legacy/testdata/affinity-toleration/transformer.yaml index e827a19238..ec70d37fcd 100644 --- a/test/e2e/legacy/testdata/affinity-toleration/transformer.yaml +++ b/test/e2e/legacy/testdata/affinity-toleration/transformer.yaml @@ -46,7 +46,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/affinity-toleration/vm/base/transformer.yaml b/test/e2e/legacy/testdata/affinity-toleration/vm/base/transformer.yaml index c70c289afa..1dc146a3af 100644 --- a/test/e2e/legacy/testdata/affinity-toleration/vm/base/transformer.yaml +++ b/test/e2e/legacy/testdata/affinity-toleration/vm/base/transformer.yaml @@ -48,7 +48,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/affinity-toleration/vmc.yaml b/test/e2e/legacy/testdata/affinity-toleration/vmc.yaml index d5a4a38b29..68c1540fca 100644 --- a/test/e2e/legacy/testdata/affinity-toleration/vmc.yaml +++ b/test/e2e/legacy/testdata/affinity-toleration/vmc.yaml @@ -1,4 +1,4 @@ -apiVersion: virtualization.deckhouse.io/v1alpha2 +apiVersion: virtualization.deckhouse.io/v1alpha3 kind: VirtualMachineClass metadata: name: affinity-toleration-discovery diff --git a/test/e2e/legacy/testdata/complex-test/transformer.yaml b/test/e2e/legacy/testdata/complex-test/transformer.yaml index e827a19238..ec70d37fcd 100644 --- a/test/e2e/legacy/testdata/complex-test/transformer.yaml +++ b/test/e2e/legacy/testdata/complex-test/transformer.yaml @@ -46,7 +46,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/complex-test/vm/base/transformer.yaml b/test/e2e/legacy/testdata/complex-test/vm/base/transformer.yaml index c70c289afa..1dc146a3af 100644 --- a/test/e2e/legacy/testdata/complex-test/vm/base/transformer.yaml +++ b/test/e2e/legacy/testdata/complex-test/vm/base/transformer.yaml @@ -48,7 +48,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/complex-test/vmc.yaml b/test/e2e/legacy/testdata/complex-test/vmc.yaml index 7ef9aaf78b..672336653a 100644 --- a/test/e2e/legacy/testdata/complex-test/vmc.yaml +++ b/test/e2e/legacy/testdata/complex-test/vmc.yaml @@ -1,4 +1,4 @@ -apiVersion: virtualization.deckhouse.io/v1alpha2 +apiVersion: virtualization.deckhouse.io/v1alpha3 kind: VirtualMachineClass metadata: name: complex-discovery diff --git a/test/e2e/legacy/testdata/connectivity/transformer.yaml b/test/e2e/legacy/testdata/connectivity/transformer.yaml index e827a19238..ec70d37fcd 100644 --- a/test/e2e/legacy/testdata/connectivity/transformer.yaml +++ b/test/e2e/legacy/testdata/connectivity/transformer.yaml @@ -46,7 +46,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/disk-resizing/base/transformer.yaml b/test/e2e/legacy/testdata/disk-resizing/base/transformer.yaml index c70c289afa..1dc146a3af 100644 --- a/test/e2e/legacy/testdata/disk-resizing/base/transformer.yaml +++ b/test/e2e/legacy/testdata/disk-resizing/base/transformer.yaml @@ -48,7 +48,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/disk-resizing/transformer.yaml b/test/e2e/legacy/testdata/disk-resizing/transformer.yaml index e827a19238..ec70d37fcd 100644 --- a/test/e2e/legacy/testdata/disk-resizing/transformer.yaml +++ b/test/e2e/legacy/testdata/disk-resizing/transformer.yaml @@ -46,7 +46,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/image-hotplug/base/transformer.yaml b/test/e2e/legacy/testdata/image-hotplug/base/transformer.yaml index c70c289afa..1dc146a3af 100644 --- a/test/e2e/legacy/testdata/image-hotplug/base/transformer.yaml +++ b/test/e2e/legacy/testdata/image-hotplug/base/transformer.yaml @@ -48,7 +48,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/image-hotplug/transformer.yaml b/test/e2e/legacy/testdata/image-hotplug/transformer.yaml index e827a19238..ec70d37fcd 100644 --- a/test/e2e/legacy/testdata/image-hotplug/transformer.yaml +++ b/test/e2e/legacy/testdata/image-hotplug/transformer.yaml @@ -46,7 +46,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/sizing-policy/base/transformer.yaml b/test/e2e/legacy/testdata/sizing-policy/base/transformer.yaml index c70c289afa..1dc146a3af 100644 --- a/test/e2e/legacy/testdata/sizing-policy/base/transformer.yaml +++ b/test/e2e/legacy/testdata/sizing-policy/base/transformer.yaml @@ -48,7 +48,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/sizing-policy/transformer.yaml b/test/e2e/legacy/testdata/sizing-policy/transformer.yaml index e827a19238..ec70d37fcd 100644 --- a/test/e2e/legacy/testdata/sizing-policy/transformer.yaml +++ b/test/e2e/legacy/testdata/sizing-policy/transformer.yaml @@ -46,7 +46,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/sizing-policy/vmc.yaml b/test/e2e/legacy/testdata/sizing-policy/vmc.yaml index 48baa39771..50c098e2f8 100644 --- a/test/e2e/legacy/testdata/sizing-policy/vmc.yaml +++ b/test/e2e/legacy/testdata/sizing-policy/vmc.yaml @@ -1,4 +1,4 @@ -apiVersion: virtualization.deckhouse.io/v1alpha2 +apiVersion: virtualization.deckhouse.io/v1alpha3 kind: VirtualMachineClass metadata: name: sizing-policy-discovery @@ -18,11 +18,11 @@ spec: - worker sizingPolicies: - coreFractions: - - 5 - - 10 - - 20 - - 50 - - 100 + - "5%" + - "10%" + - "20%" + - "50%" + - "100%" cores: max: 4 min: 1 @@ -33,9 +33,9 @@ spec: min: 1Gi step: 512Mi - coreFractions: - - 20 - - 50 - - 100 + - "20%" + - "50%" + - "100%" cores: max: 8 min: 5 @@ -46,8 +46,8 @@ spec: min: 5Gi step: 1Gi - coreFractions: - - 50 - - 100 + - "50%" + - "100%" cores: max: 16 min: 9 @@ -59,7 +59,7 @@ spec: min: 9Gi step: 1Gi - coreFractions: - - 100 + - "100%" cores: max: 1024 min: 17 diff --git a/test/e2e/legacy/testdata/templates/transformer.yaml b/test/e2e/legacy/testdata/templates/transformer.yaml index e827a19238..ec70d37fcd 100644 --- a/test/e2e/legacy/testdata/templates/transformer.yaml +++ b/test/e2e/legacy/testdata/templates/transformer.yaml @@ -46,7 +46,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vd-snapshots/transformer.yaml b/test/e2e/legacy/testdata/vd-snapshots/transformer.yaml index e827a19238..ec70d37fcd 100644 --- a/test/e2e/legacy/testdata/vd-snapshots/transformer.yaml +++ b/test/e2e/legacy/testdata/vd-snapshots/transformer.yaml @@ -46,7 +46,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vd-snapshots/vm/base/transformer.yaml b/test/e2e/legacy/testdata/vd-snapshots/vm/base/transformer.yaml index c70c289afa..1dc146a3af 100644 --- a/test/e2e/legacy/testdata/vd-snapshots/vm/base/transformer.yaml +++ b/test/e2e/legacy/testdata/vd-snapshots/vm/base/transformer.yaml @@ -48,7 +48,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-configuration/base/transformer.yaml b/test/e2e/legacy/testdata/vm-configuration/base/transformer.yaml index c70c289afa..1dc146a3af 100644 --- a/test/e2e/legacy/testdata/vm-configuration/base/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-configuration/base/transformer.yaml @@ -48,7 +48,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-configuration/transformer.yaml b/test/e2e/legacy/testdata/vm-configuration/transformer.yaml index e827a19238..ec70d37fcd 100644 --- a/test/e2e/legacy/testdata/vm-configuration/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-configuration/transformer.yaml @@ -46,7 +46,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-disk-attachment/base/transformer.yaml b/test/e2e/legacy/testdata/vm-disk-attachment/base/transformer.yaml index c70c289afa..1dc146a3af 100644 --- a/test/e2e/legacy/testdata/vm-disk-attachment/base/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-disk-attachment/base/transformer.yaml @@ -48,7 +48,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-disk-attachment/transformer.yaml b/test/e2e/legacy/testdata/vm-disk-attachment/transformer.yaml index e827a19238..ec70d37fcd 100644 --- a/test/e2e/legacy/testdata/vm-disk-attachment/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-disk-attachment/transformer.yaml @@ -46,7 +46,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-evacuation/transformer.yaml b/test/e2e/legacy/testdata/vm-evacuation/transformer.yaml index e827a19238..ec70d37fcd 100644 --- a/test/e2e/legacy/testdata/vm-evacuation/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-evacuation/transformer.yaml @@ -46,7 +46,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-evacuation/vm/base/transformer.yaml b/test/e2e/legacy/testdata/vm-evacuation/vm/base/transformer.yaml index c70c289afa..1dc146a3af 100644 --- a/test/e2e/legacy/testdata/vm-evacuation/vm/base/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-evacuation/vm/base/transformer.yaml @@ -48,7 +48,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-label-annotation/base/transformer.yaml b/test/e2e/legacy/testdata/vm-label-annotation/base/transformer.yaml index c70c289afa..1dc146a3af 100644 --- a/test/e2e/legacy/testdata/vm-label-annotation/base/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-label-annotation/base/transformer.yaml @@ -48,7 +48,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-label-annotation/transformer.yaml b/test/e2e/legacy/testdata/vm-label-annotation/transformer.yaml index e827a19238..ec70d37fcd 100644 --- a/test/e2e/legacy/testdata/vm-label-annotation/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-label-annotation/transformer.yaml @@ -46,7 +46,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-migration-cancel/transformer.yaml b/test/e2e/legacy/testdata/vm-migration-cancel/transformer.yaml index e827a19238..ec70d37fcd 100644 --- a/test/e2e/legacy/testdata/vm-migration-cancel/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-migration-cancel/transformer.yaml @@ -46,7 +46,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-migration-cancel/vm/base/transformer.yaml b/test/e2e/legacy/testdata/vm-migration-cancel/vm/base/transformer.yaml index c70c289afa..1dc146a3af 100644 --- a/test/e2e/legacy/testdata/vm-migration-cancel/vm/base/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-migration-cancel/vm/base/transformer.yaml @@ -48,7 +48,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-migration/transformer.yaml b/test/e2e/legacy/testdata/vm-migration/transformer.yaml index e827a19238..ec70d37fcd 100644 --- a/test/e2e/legacy/testdata/vm-migration/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-migration/transformer.yaml @@ -46,7 +46,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-migration/vm/base/transformer.yaml b/test/e2e/legacy/testdata/vm-migration/vm/base/transformer.yaml index c70c289afa..1dc146a3af 100644 --- a/test/e2e/legacy/testdata/vm-migration/vm/base/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-migration/vm/base/transformer.yaml @@ -48,7 +48,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-restore-force/transformer.yaml b/test/e2e/legacy/testdata/vm-restore-force/transformer.yaml index e827a19238..ec70d37fcd 100644 --- a/test/e2e/legacy/testdata/vm-restore-force/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-restore-force/transformer.yaml @@ -46,7 +46,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-restore-force/vm/base/transformer.yaml b/test/e2e/legacy/testdata/vm-restore-force/vm/base/transformer.yaml index 2a24df6226..2659eb381a 100644 --- a/test/e2e/legacy/testdata/vm-restore-force/vm/base/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-restore-force/vm/base/transformer.yaml @@ -48,7 +48,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-restore-safe/transformer.yaml b/test/e2e/legacy/testdata/vm-restore-safe/transformer.yaml index e827a19238..ec70d37fcd 100644 --- a/test/e2e/legacy/testdata/vm-restore-safe/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-restore-safe/transformer.yaml @@ -46,7 +46,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-restore-safe/vm/base/transformer.yaml b/test/e2e/legacy/testdata/vm-restore-safe/vm/base/transformer.yaml index 2a24df6226..2659eb381a 100644 --- a/test/e2e/legacy/testdata/vm-restore-safe/vm/base/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-restore-safe/vm/base/transformer.yaml @@ -48,7 +48,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-versions/transformer.yaml b/test/e2e/legacy/testdata/vm-versions/transformer.yaml index e827a19238..ec70d37fcd 100644 --- a/test/e2e/legacy/testdata/vm-versions/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-versions/transformer.yaml @@ -46,7 +46,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-versions/vm/transformer.yaml b/test/e2e/legacy/testdata/vm-versions/vm/transformer.yaml index c70c289afa..1dc146a3af 100644 --- a/test/e2e/legacy/testdata/vm-versions/vm/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-versions/vm/transformer.yaml @@ -48,7 +48,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-vpc/transformer.yaml b/test/e2e/legacy/testdata/vm-vpc/transformer.yaml index e827a19238..ec70d37fcd 100644 --- a/test/e2e/legacy/testdata/vm-vpc/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-vpc/transformer.yaml @@ -46,7 +46,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-vpc/vm-bar/transformer.yaml b/test/e2e/legacy/testdata/vm-vpc/vm-bar/transformer.yaml index c70c289afa..1dc146a3af 100644 --- a/test/e2e/legacy/testdata/vm-vpc/vm-bar/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-vpc/vm-bar/transformer.yaml @@ -48,7 +48,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine diff --git a/test/e2e/legacy/testdata/vm-vpc/vm-foo/transformer.yaml b/test/e2e/legacy/testdata/vm-vpc/vm-foo/transformer.yaml index c70c289afa..1dc146a3af 100644 --- a/test/e2e/legacy/testdata/vm-vpc/vm-foo/transformer.yaml +++ b/test/e2e/legacy/testdata/vm-vpc/vm-foo/transformer.yaml @@ -48,7 +48,7 @@ nameReference: - path: spec/virtualMachineName kind: VirtualMachineBlockDeviceAttachment - kind: VirtualMachineClass - version: v1alpha2 + version: v1alpha3 fieldSpecs: - path: spec/virtualMachineClassName kind: VirtualMachine