diff --git a/api/Taskfile.dist.yaml b/api/Taskfile.dist.yaml index a6d9f2573..dccea27f2 100644 --- a/api/Taskfile.dist.yaml +++ b/api/Taskfile.dist.yaml @@ -6,6 +6,9 @@ env: K8S_CODEGEN: sh: echo $(go env GOMODCACHE)/$(go list -f '{{`{{.Path}}@{{.Version}}`}}' -m k8s.io/code-generator) +vars: + CONTROLLER_GEN_VERSION: 0.14.0 + tasks: generate: desc: "Regenerate all" @@ -33,6 +36,12 @@ tasks: - task: generate - task: _ci:verify-gen + generate:crds: + desc: "Regenerate crds" + deps: + - _ensure:k8s-controller-gen + cmd: ./scripts/update-codegen.sh crds + _ci:verify-gen: desc: "Check generated files are up-to-date." internal: true @@ -58,3 +67,12 @@ tasks: status: - | ls $GOPATH/bin/openapi-gen + + _ensure:k8s-controller-gen: + desc: "Ensure sigs.k8s.io/controller-tools/cmd/controller-gen" + internal: true + cmds: + - go install -mod=readonly sigs.k8s.io/controller-tools/cmd/controller-gen@v{{ .CONTROLLER_GEN_VERSION }} + status: + - | + ls $GOPATH/bin/controller-gen diff --git a/api/client/generated/clientset/versioned/typed/core/v1alpha2/core_client.go b/api/client/generated/clientset/versioned/typed/core/v1alpha2/core_client.go index 4a8c5530f..44c441691 100644 --- a/api/client/generated/clientset/versioned/typed/core/v1alpha2/core_client.go +++ b/api/client/generated/clientset/versioned/typed/core/v1alpha2/core_client.go @@ -32,7 +32,7 @@ type VirtualizationV1alpha2Interface interface { VirtualImagesGetter VirtualMachinesGetter VirtualMachineBlockDeviceAttachmentsGetter - VirtualMachineCPUModelsGetter + VirtualMachineClassesGetter VirtualMachineIPAddressesGetter VirtualMachineIPAddressLeasesGetter VirtualMachineOperationsGetter @@ -63,8 +63,8 @@ func (c *VirtualizationV1alpha2Client) VirtualMachineBlockDeviceAttachments(name return newVirtualMachineBlockDeviceAttachments(c, namespace) } -func (c *VirtualizationV1alpha2Client) VirtualMachineCPUModels() VirtualMachineCPUModelInterface { - return newVirtualMachineCPUModels(c) +func (c *VirtualizationV1alpha2Client) VirtualMachineClasses() VirtualMachineClassInterface { + return newVirtualMachineClasses(c) } func (c *VirtualizationV1alpha2Client) VirtualMachineIPAddresses(namespace string) VirtualMachineIPAddressInterface { diff --git a/api/client/generated/clientset/versioned/typed/core/v1alpha2/fake/fake_core_client.go b/api/client/generated/clientset/versioned/typed/core/v1alpha2/fake/fake_core_client.go index 02dfd87b7..eca3d2eca 100644 --- a/api/client/generated/clientset/versioned/typed/core/v1alpha2/fake/fake_core_client.go +++ b/api/client/generated/clientset/versioned/typed/core/v1alpha2/fake/fake_core_client.go @@ -47,8 +47,8 @@ func (c *FakeVirtualizationV1alpha2) VirtualMachineBlockDeviceAttachments(namesp return &FakeVirtualMachineBlockDeviceAttachments{c, namespace} } -func (c *FakeVirtualizationV1alpha2) VirtualMachineCPUModels() v1alpha2.VirtualMachineCPUModelInterface { - return &FakeVirtualMachineCPUModels{c} +func (c *FakeVirtualizationV1alpha2) VirtualMachineClasses() v1alpha2.VirtualMachineClassInterface { + return &FakeVirtualMachineClasses{c} } func (c *FakeVirtualizationV1alpha2) VirtualMachineIPAddresses(namespace string) v1alpha2.VirtualMachineIPAddressInterface { diff --git a/api/client/generated/clientset/versioned/typed/core/v1alpha2/fake/fake_virtualmachineclass.go b/api/client/generated/clientset/versioned/typed/core/v1alpha2/fake/fake_virtualmachineclass.go new file mode 100644 index 000000000..b9dd855b4 --- /dev/null +++ b/api/client/generated/clientset/versioned/typed/core/v1alpha2/fake/fake_virtualmachineclass.go @@ -0,0 +1,131 @@ +/* +Copyright 2022 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 ( + "context" + + v1alpha2 "github.com/deckhouse/virtualization/api/core/v1alpha2" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeVirtualMachineClasses implements VirtualMachineClassInterface +type FakeVirtualMachineClasses struct { + Fake *FakeVirtualizationV1alpha2 +} + +var virtualmachineclassesResource = v1alpha2.SchemeGroupVersion.WithResource("virtualmachineclasses") + +var virtualmachineclassesKind = v1alpha2.SchemeGroupVersion.WithKind("VirtualMachineClass") + +// Get takes name of the virtualMachineClass, and returns the corresponding virtualMachineClass object, and an error if there is any. +func (c *FakeVirtualMachineClasses) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha2.VirtualMachineClass, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(virtualmachineclassesResource, name), &v1alpha2.VirtualMachineClass{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.VirtualMachineClass), err +} + +// List takes label and field selectors, and returns the list of VirtualMachineClasses that match those selectors. +func (c *FakeVirtualMachineClasses) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha2.VirtualMachineClassList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(virtualmachineclassesResource, virtualmachineclassesKind, opts), &v1alpha2.VirtualMachineClassList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha2.VirtualMachineClassList{ListMeta: obj.(*v1alpha2.VirtualMachineClassList).ListMeta} + for _, item := range obj.(*v1alpha2.VirtualMachineClassList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested virtualMachineClasses. +func (c *FakeVirtualMachineClasses) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(virtualmachineclassesResource, opts)) +} + +// Create takes the representation of a virtualMachineClass and creates it. Returns the server's representation of the virtualMachineClass, and an error, if there is any. +func (c *FakeVirtualMachineClasses) Create(ctx context.Context, virtualMachineClass *v1alpha2.VirtualMachineClass, opts v1.CreateOptions) (result *v1alpha2.VirtualMachineClass, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(virtualmachineclassesResource, virtualMachineClass), &v1alpha2.VirtualMachineClass{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.VirtualMachineClass), err +} + +// Update takes the representation of a virtualMachineClass and updates it. Returns the server's representation of the virtualMachineClass, and an error, if there is any. +func (c *FakeVirtualMachineClasses) Update(ctx context.Context, virtualMachineClass *v1alpha2.VirtualMachineClass, opts v1.UpdateOptions) (result *v1alpha2.VirtualMachineClass, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(virtualmachineclassesResource, virtualMachineClass), &v1alpha2.VirtualMachineClass{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.VirtualMachineClass), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeVirtualMachineClasses) UpdateStatus(ctx context.Context, virtualMachineClass *v1alpha2.VirtualMachineClass, opts v1.UpdateOptions) (*v1alpha2.VirtualMachineClass, error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(virtualmachineclassesResource, "status", virtualMachineClass), &v1alpha2.VirtualMachineClass{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.VirtualMachineClass), err +} + +// Delete takes name of the virtualMachineClass and deletes it. Returns an error if one occurs. +func (c *FakeVirtualMachineClasses) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteActionWithOptions(virtualmachineclassesResource, name, opts), &v1alpha2.VirtualMachineClass{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeVirtualMachineClasses) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(virtualmachineclassesResource, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha2.VirtualMachineClassList{}) + return err +} + +// Patch applies the patch and returns the patched virtualMachineClass. +func (c *FakeVirtualMachineClasses) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha2.VirtualMachineClass, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(virtualmachineclassesResource, name, pt, data, subresources...), &v1alpha2.VirtualMachineClass{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.VirtualMachineClass), err +} diff --git a/api/client/generated/clientset/versioned/typed/core/v1alpha2/fake/fake_virtualmachinecpumodel.go b/api/client/generated/clientset/versioned/typed/core/v1alpha2/fake/fake_virtualmachinecpumodel.go deleted file mode 100644 index 4b8c78f92..000000000 --- a/api/client/generated/clientset/versioned/typed/core/v1alpha2/fake/fake_virtualmachinecpumodel.go +++ /dev/null @@ -1,131 +0,0 @@ -/* -Copyright 2022 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 ( - "context" - - v1alpha2 "github.com/deckhouse/virtualization/api/core/v1alpha2" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeVirtualMachineCPUModels implements VirtualMachineCPUModelInterface -type FakeVirtualMachineCPUModels struct { - Fake *FakeVirtualizationV1alpha2 -} - -var virtualmachinecpumodelsResource = v1alpha2.SchemeGroupVersion.WithResource("virtualmachinecpumodels") - -var virtualmachinecpumodelsKind = v1alpha2.SchemeGroupVersion.WithKind("VirtualMachineCPUModel") - -// Get takes name of the virtualMachineCPUModel, and returns the corresponding virtualMachineCPUModel object, and an error if there is any. -func (c *FakeVirtualMachineCPUModels) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha2.VirtualMachineCPUModel, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootGetAction(virtualmachinecpumodelsResource, name), &v1alpha2.VirtualMachineCPUModel{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.VirtualMachineCPUModel), err -} - -// List takes label and field selectors, and returns the list of VirtualMachineCPUModels that match those selectors. -func (c *FakeVirtualMachineCPUModels) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha2.VirtualMachineCPUModelList, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootListAction(virtualmachinecpumodelsResource, virtualmachinecpumodelsKind, opts), &v1alpha2.VirtualMachineCPUModelList{}) - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha2.VirtualMachineCPUModelList{ListMeta: obj.(*v1alpha2.VirtualMachineCPUModelList).ListMeta} - for _, item := range obj.(*v1alpha2.VirtualMachineCPUModelList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested virtualMachineCPUModels. -func (c *FakeVirtualMachineCPUModels) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewRootWatchAction(virtualmachinecpumodelsResource, opts)) -} - -// Create takes the representation of a virtualMachineCPUModel and creates it. Returns the server's representation of the virtualMachineCPUModel, and an error, if there is any. -func (c *FakeVirtualMachineCPUModels) Create(ctx context.Context, virtualMachineCPUModel *v1alpha2.VirtualMachineCPUModel, opts v1.CreateOptions) (result *v1alpha2.VirtualMachineCPUModel, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootCreateAction(virtualmachinecpumodelsResource, virtualMachineCPUModel), &v1alpha2.VirtualMachineCPUModel{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.VirtualMachineCPUModel), err -} - -// Update takes the representation of a virtualMachineCPUModel and updates it. Returns the server's representation of the virtualMachineCPUModel, and an error, if there is any. -func (c *FakeVirtualMachineCPUModels) Update(ctx context.Context, virtualMachineCPUModel *v1alpha2.VirtualMachineCPUModel, opts v1.UpdateOptions) (result *v1alpha2.VirtualMachineCPUModel, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateAction(virtualmachinecpumodelsResource, virtualMachineCPUModel), &v1alpha2.VirtualMachineCPUModel{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.VirtualMachineCPUModel), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeVirtualMachineCPUModels) UpdateStatus(ctx context.Context, virtualMachineCPUModel *v1alpha2.VirtualMachineCPUModel, opts v1.UpdateOptions) (*v1alpha2.VirtualMachineCPUModel, error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateSubresourceAction(virtualmachinecpumodelsResource, "status", virtualMachineCPUModel), &v1alpha2.VirtualMachineCPUModel{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.VirtualMachineCPUModel), err -} - -// Delete takes name of the virtualMachineCPUModel and deletes it. Returns an error if one occurs. -func (c *FakeVirtualMachineCPUModels) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewRootDeleteActionWithOptions(virtualmachinecpumodelsResource, name, opts), &v1alpha2.VirtualMachineCPUModel{}) - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeVirtualMachineCPUModels) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewRootDeleteCollectionAction(virtualmachinecpumodelsResource, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha2.VirtualMachineCPUModelList{}) - return err -} - -// Patch applies the patch and returns the patched virtualMachineCPUModel. -func (c *FakeVirtualMachineCPUModels) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha2.VirtualMachineCPUModel, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(virtualmachinecpumodelsResource, name, pt, data, subresources...), &v1alpha2.VirtualMachineCPUModel{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.VirtualMachineCPUModel), err -} diff --git a/api/client/generated/clientset/versioned/typed/core/v1alpha2/generated_expansion.go b/api/client/generated/clientset/versioned/typed/core/v1alpha2/generated_expansion.go index 373d331ae..d0cf841a8 100644 --- a/api/client/generated/clientset/versioned/typed/core/v1alpha2/generated_expansion.go +++ b/api/client/generated/clientset/versioned/typed/core/v1alpha2/generated_expansion.go @@ -27,7 +27,7 @@ type VirtualMachineExpansion interface{} type VirtualMachineBlockDeviceAttachmentExpansion interface{} -type VirtualMachineCPUModelExpansion interface{} +type VirtualMachineClassExpansion interface{} type VirtualMachineIPAddressExpansion interface{} diff --git a/api/client/generated/clientset/versioned/typed/core/v1alpha2/virtualmachineclass.go b/api/client/generated/clientset/versioned/typed/core/v1alpha2/virtualmachineclass.go new file mode 100644 index 000000000..e69da5770 --- /dev/null +++ b/api/client/generated/clientset/versioned/typed/core/v1alpha2/virtualmachineclass.go @@ -0,0 +1,183 @@ +/* +Copyright 2022 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 v1alpha2 + +import ( + "context" + "time" + + scheme "github.com/deckhouse/virtualization/api/client/generated/clientset/versioned/scheme" + v1alpha2 "github.com/deckhouse/virtualization/api/core/v1alpha2" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// 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 *v1alpha2.VirtualMachineClass, opts v1.CreateOptions) (*v1alpha2.VirtualMachineClass, error) + Update(ctx context.Context, virtualMachineClass *v1alpha2.VirtualMachineClass, opts v1.UpdateOptions) (*v1alpha2.VirtualMachineClass, error) + UpdateStatus(ctx context.Context, virtualMachineClass *v1alpha2.VirtualMachineClass, opts v1.UpdateOptions) (*v1alpha2.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) (*v1alpha2.VirtualMachineClass, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha2.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 *v1alpha2.VirtualMachineClass, err error) + VirtualMachineClassExpansion +} + +// virtualMachineClasses implements VirtualMachineClassInterface +type virtualMachineClasses struct { + client rest.Interface +} + +// newVirtualMachineClasses returns a VirtualMachineClasses +func newVirtualMachineClasses(c *VirtualizationV1alpha2Client) *virtualMachineClasses { + return &virtualMachineClasses{ + client: c.RESTClient(), + } +} + +// Get takes name of the virtualMachineClass, and returns the corresponding virtualMachineClass object, and an error if there is any. +func (c *virtualMachineClasses) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha2.VirtualMachineClass, err error) { + result = &v1alpha2.VirtualMachineClass{} + err = c.client.Get(). + Resource("virtualmachineclasses"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of VirtualMachineClasses that match those selectors. +func (c *virtualMachineClasses) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha2.VirtualMachineClassList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha2.VirtualMachineClassList{} + err = c.client.Get(). + Resource("virtualmachineclasses"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested virtualMachineClasses. +func (c *virtualMachineClasses) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("virtualmachineclasses"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a virtualMachineClass and creates it. Returns the server's representation of the virtualMachineClass, and an error, if there is any. +func (c *virtualMachineClasses) Create(ctx context.Context, virtualMachineClass *v1alpha2.VirtualMachineClass, opts v1.CreateOptions) (result *v1alpha2.VirtualMachineClass, err error) { + result = &v1alpha2.VirtualMachineClass{} + err = c.client.Post(). + Resource("virtualmachineclasses"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(virtualMachineClass). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a virtualMachineClass and updates it. Returns the server's representation of the virtualMachineClass, and an error, if there is any. +func (c *virtualMachineClasses) Update(ctx context.Context, virtualMachineClass *v1alpha2.VirtualMachineClass, opts v1.UpdateOptions) (result *v1alpha2.VirtualMachineClass, err error) { + result = &v1alpha2.VirtualMachineClass{} + err = c.client.Put(). + Resource("virtualmachineclasses"). + Name(virtualMachineClass.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(virtualMachineClass). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *virtualMachineClasses) UpdateStatus(ctx context.Context, virtualMachineClass *v1alpha2.VirtualMachineClass, opts v1.UpdateOptions) (result *v1alpha2.VirtualMachineClass, err error) { + result = &v1alpha2.VirtualMachineClass{} + err = c.client.Put(). + Resource("virtualmachineclasses"). + Name(virtualMachineClass.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(virtualMachineClass). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the virtualMachineClass and deletes it. Returns an error if one occurs. +func (c *virtualMachineClasses) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Resource("virtualmachineclasses"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *virtualMachineClasses) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("virtualmachineclasses"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched virtualMachineClass. +func (c *virtualMachineClasses) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha2.VirtualMachineClass, err error) { + result = &v1alpha2.VirtualMachineClass{} + err = c.client.Patch(pt). + Resource("virtualmachineclasses"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/api/client/generated/clientset/versioned/typed/core/v1alpha2/virtualmachinecpumodel.go b/api/client/generated/clientset/versioned/typed/core/v1alpha2/virtualmachinecpumodel.go deleted file mode 100644 index b86cf4976..000000000 --- a/api/client/generated/clientset/versioned/typed/core/v1alpha2/virtualmachinecpumodel.go +++ /dev/null @@ -1,183 +0,0 @@ -/* -Copyright 2022 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 v1alpha2 - -import ( - "context" - "time" - - scheme "github.com/deckhouse/virtualization/api/client/generated/clientset/versioned/scheme" - v1alpha2 "github.com/deckhouse/virtualization/api/core/v1alpha2" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// VirtualMachineCPUModelsGetter has a method to return a VirtualMachineCPUModelInterface. -// A group's client should implement this interface. -type VirtualMachineCPUModelsGetter interface { - VirtualMachineCPUModels() VirtualMachineCPUModelInterface -} - -// VirtualMachineCPUModelInterface has methods to work with VirtualMachineCPUModel resources. -type VirtualMachineCPUModelInterface interface { - Create(ctx context.Context, virtualMachineCPUModel *v1alpha2.VirtualMachineCPUModel, opts v1.CreateOptions) (*v1alpha2.VirtualMachineCPUModel, error) - Update(ctx context.Context, virtualMachineCPUModel *v1alpha2.VirtualMachineCPUModel, opts v1.UpdateOptions) (*v1alpha2.VirtualMachineCPUModel, error) - UpdateStatus(ctx context.Context, virtualMachineCPUModel *v1alpha2.VirtualMachineCPUModel, opts v1.UpdateOptions) (*v1alpha2.VirtualMachineCPUModel, 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) (*v1alpha2.VirtualMachineCPUModel, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha2.VirtualMachineCPUModelList, 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 *v1alpha2.VirtualMachineCPUModel, err error) - VirtualMachineCPUModelExpansion -} - -// virtualMachineCPUModels implements VirtualMachineCPUModelInterface -type virtualMachineCPUModels struct { - client rest.Interface -} - -// newVirtualMachineCPUModels returns a VirtualMachineCPUModels -func newVirtualMachineCPUModels(c *VirtualizationV1alpha2Client) *virtualMachineCPUModels { - return &virtualMachineCPUModels{ - client: c.RESTClient(), - } -} - -// Get takes name of the virtualMachineCPUModel, and returns the corresponding virtualMachineCPUModel object, and an error if there is any. -func (c *virtualMachineCPUModels) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha2.VirtualMachineCPUModel, err error) { - result = &v1alpha2.VirtualMachineCPUModel{} - err = c.client.Get(). - Resource("virtualmachinecpumodels"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of VirtualMachineCPUModels that match those selectors. -func (c *virtualMachineCPUModels) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha2.VirtualMachineCPUModelList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha2.VirtualMachineCPUModelList{} - err = c.client.Get(). - Resource("virtualmachinecpumodels"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested virtualMachineCPUModels. -func (c *virtualMachineCPUModels) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Resource("virtualmachinecpumodels"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a virtualMachineCPUModel and creates it. Returns the server's representation of the virtualMachineCPUModel, and an error, if there is any. -func (c *virtualMachineCPUModels) Create(ctx context.Context, virtualMachineCPUModel *v1alpha2.VirtualMachineCPUModel, opts v1.CreateOptions) (result *v1alpha2.VirtualMachineCPUModel, err error) { - result = &v1alpha2.VirtualMachineCPUModel{} - err = c.client.Post(). - Resource("virtualmachinecpumodels"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(virtualMachineCPUModel). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a virtualMachineCPUModel and updates it. Returns the server's representation of the virtualMachineCPUModel, and an error, if there is any. -func (c *virtualMachineCPUModels) Update(ctx context.Context, virtualMachineCPUModel *v1alpha2.VirtualMachineCPUModel, opts v1.UpdateOptions) (result *v1alpha2.VirtualMachineCPUModel, err error) { - result = &v1alpha2.VirtualMachineCPUModel{} - err = c.client.Put(). - Resource("virtualmachinecpumodels"). - Name(virtualMachineCPUModel.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(virtualMachineCPUModel). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *virtualMachineCPUModels) UpdateStatus(ctx context.Context, virtualMachineCPUModel *v1alpha2.VirtualMachineCPUModel, opts v1.UpdateOptions) (result *v1alpha2.VirtualMachineCPUModel, err error) { - result = &v1alpha2.VirtualMachineCPUModel{} - err = c.client.Put(). - Resource("virtualmachinecpumodels"). - Name(virtualMachineCPUModel.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(virtualMachineCPUModel). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the virtualMachineCPUModel and deletes it. Returns an error if one occurs. -func (c *virtualMachineCPUModels) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Resource("virtualmachinecpumodels"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *virtualMachineCPUModels) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Resource("virtualmachinecpumodels"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched virtualMachineCPUModel. -func (c *virtualMachineCPUModels) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha2.VirtualMachineCPUModel, err error) { - result = &v1alpha2.VirtualMachineCPUModel{} - err = c.client.Patch(pt). - Resource("virtualmachinecpumodels"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/api/client/generated/informers/externalversions/core/v1alpha2/interface.go b/api/client/generated/informers/externalversions/core/v1alpha2/interface.go index bf1f8e1bc..1fd4e0df5 100644 --- a/api/client/generated/informers/externalversions/core/v1alpha2/interface.go +++ b/api/client/generated/informers/externalversions/core/v1alpha2/interface.go @@ -33,8 +33,8 @@ type Interface interface { VirtualMachines() VirtualMachineInformer // VirtualMachineBlockDeviceAttachments returns a VirtualMachineBlockDeviceAttachmentInformer. VirtualMachineBlockDeviceAttachments() VirtualMachineBlockDeviceAttachmentInformer - // VirtualMachineCPUModels returns a VirtualMachineCPUModelInformer. - VirtualMachineCPUModels() VirtualMachineCPUModelInformer + // VirtualMachineClasses returns a VirtualMachineClassInformer. + VirtualMachineClasses() VirtualMachineClassInformer // VirtualMachineIPAddresses returns a VirtualMachineIPAddressInformer. VirtualMachineIPAddresses() VirtualMachineIPAddressInformer // VirtualMachineIPAddressLeases returns a VirtualMachineIPAddressLeaseInformer. @@ -79,9 +79,9 @@ func (v *version) VirtualMachineBlockDeviceAttachments() VirtualMachineBlockDevi return &virtualMachineBlockDeviceAttachmentInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } -// VirtualMachineCPUModels returns a VirtualMachineCPUModelInformer. -func (v *version) VirtualMachineCPUModels() VirtualMachineCPUModelInformer { - return &virtualMachineCPUModelInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +// VirtualMachineClasses returns a VirtualMachineClassInformer. +func (v *version) VirtualMachineClasses() VirtualMachineClassInformer { + return &virtualMachineClassInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} } // VirtualMachineIPAddresses returns a VirtualMachineIPAddressInformer. diff --git a/api/client/generated/informers/externalversions/core/v1alpha2/virtualmachinecpumodel.go b/api/client/generated/informers/externalversions/core/v1alpha2/virtualmachineclass.go similarity index 54% rename from api/client/generated/informers/externalversions/core/v1alpha2/virtualmachinecpumodel.go rename to api/client/generated/informers/externalversions/core/v1alpha2/virtualmachineclass.go index aef538925..2a2cbc9f8 100644 --- a/api/client/generated/informers/externalversions/core/v1alpha2/virtualmachinecpumodel.go +++ b/api/client/generated/informers/externalversions/core/v1alpha2/virtualmachineclass.go @@ -31,58 +31,58 @@ import ( cache "k8s.io/client-go/tools/cache" ) -// VirtualMachineCPUModelInformer provides access to a shared informer and lister for -// VirtualMachineCPUModels. -type VirtualMachineCPUModelInformer interface { +// VirtualMachineClassInformer provides access to a shared informer and lister for +// VirtualMachineClasses. +type VirtualMachineClassInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha2.VirtualMachineCPUModelLister + Lister() v1alpha2.VirtualMachineClassLister } -type virtualMachineCPUModelInformer struct { +type virtualMachineClassInformer struct { factory internalinterfaces.SharedInformerFactory tweakListOptions internalinterfaces.TweakListOptionsFunc } -// NewVirtualMachineCPUModelInformer constructs a new informer for VirtualMachineCPUModel type. +// 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 NewVirtualMachineCPUModelInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredVirtualMachineCPUModelInformer(client, resyncPeriod, indexers, nil) +func NewVirtualMachineClassInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredVirtualMachineClassInformer(client, resyncPeriod, indexers, nil) } -// NewFilteredVirtualMachineCPUModelInformer constructs a new informer for VirtualMachineCPUModel type. +// 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 NewFilteredVirtualMachineCPUModelInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { +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.VirtualizationV1alpha2().VirtualMachineCPUModels().List(context.TODO(), options) + return client.VirtualizationV1alpha2().VirtualMachineClasses().List(context.TODO(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.VirtualizationV1alpha2().VirtualMachineCPUModels().Watch(context.TODO(), options) + return client.VirtualizationV1alpha2().VirtualMachineClasses().Watch(context.TODO(), options) }, }, - &corev1alpha2.VirtualMachineCPUModel{}, + &corev1alpha2.VirtualMachineClass{}, resyncPeriod, indexers, ) } -func (f *virtualMachineCPUModelInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredVirtualMachineCPUModelInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +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 *virtualMachineCPUModelInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&corev1alpha2.VirtualMachineCPUModel{}, f.defaultInformer) +func (f *virtualMachineClassInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&corev1alpha2.VirtualMachineClass{}, f.defaultInformer) } -func (f *virtualMachineCPUModelInformer) Lister() v1alpha2.VirtualMachineCPUModelLister { - return v1alpha2.NewVirtualMachineCPUModelLister(f.Informer().GetIndexer()) +func (f *virtualMachineClassInformer) Lister() v1alpha2.VirtualMachineClassLister { + return v1alpha2.NewVirtualMachineClassLister(f.Informer().GetIndexer()) } diff --git a/api/client/generated/informers/externalversions/generic.go b/api/client/generated/informers/externalversions/generic.go index 4a3ee2e38..ff7e015d9 100644 --- a/api/client/generated/informers/externalversions/generic.go +++ b/api/client/generated/informers/externalversions/generic.go @@ -62,8 +62,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Virtualization().V1alpha2().VirtualMachines().Informer()}, nil case v1alpha2.SchemeGroupVersion.WithResource("virtualmachineblockdeviceattachments"): return &genericInformer{resource: resource.GroupResource(), informer: f.Virtualization().V1alpha2().VirtualMachineBlockDeviceAttachments().Informer()}, nil - case v1alpha2.SchemeGroupVersion.WithResource("virtualmachinecpumodels"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Virtualization().V1alpha2().VirtualMachineCPUModels().Informer()}, nil + case v1alpha2.SchemeGroupVersion.WithResource("virtualmachineclasses"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Virtualization().V1alpha2().VirtualMachineClasses().Informer()}, nil case v1alpha2.SchemeGroupVersion.WithResource("virtualmachineipaddresses"): return &genericInformer{resource: resource.GroupResource(), informer: f.Virtualization().V1alpha2().VirtualMachineIPAddresses().Informer()}, nil case v1alpha2.SchemeGroupVersion.WithResource("virtualmachineipaddressleases"): diff --git a/api/client/generated/listers/core/v1alpha2/expansion_generated.go b/api/client/generated/listers/core/v1alpha2/expansion_generated.go index 624baff1d..cbdf66178 100644 --- a/api/client/generated/listers/core/v1alpha2/expansion_generated.go +++ b/api/client/generated/listers/core/v1alpha2/expansion_generated.go @@ -53,9 +53,9 @@ type VirtualMachineBlockDeviceAttachmentListerExpansion interface{} // VirtualMachineBlockDeviceAttachmentNamespaceLister. type VirtualMachineBlockDeviceAttachmentNamespaceListerExpansion interface{} -// VirtualMachineCPUModelListerExpansion allows custom methods to be added to -// VirtualMachineCPUModelLister. -type VirtualMachineCPUModelListerExpansion interface{} +// VirtualMachineClassListerExpansion allows custom methods to be added to +// VirtualMachineClassLister. +type VirtualMachineClassListerExpansion interface{} // VirtualMachineIPAddressListerExpansion allows custom methods to be added to // VirtualMachineIPAddressLister. diff --git a/api/client/generated/listers/core/v1alpha2/virtualmachinecpumodel.go b/api/client/generated/listers/core/v1alpha2/virtualmachineclass.go similarity index 52% rename from api/client/generated/listers/core/v1alpha2/virtualmachinecpumodel.go rename to api/client/generated/listers/core/v1alpha2/virtualmachineclass.go index d1cc47b5d..717c69381 100644 --- a/api/client/generated/listers/core/v1alpha2/virtualmachinecpumodel.go +++ b/api/client/generated/listers/core/v1alpha2/virtualmachineclass.go @@ -24,44 +24,44 @@ import ( "k8s.io/client-go/tools/cache" ) -// VirtualMachineCPUModelLister helps list VirtualMachineCPUModels. +// VirtualMachineClassLister helps list VirtualMachineClasses. // All objects returned here must be treated as read-only. -type VirtualMachineCPUModelLister interface { - // List lists all VirtualMachineCPUModels in the indexer. +type VirtualMachineClassLister interface { + // List lists all VirtualMachineClasses in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha2.VirtualMachineCPUModel, err error) - // Get retrieves the VirtualMachineCPUModel from the index for a given name. + List(selector labels.Selector) (ret []*v1alpha2.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) (*v1alpha2.VirtualMachineCPUModel, error) - VirtualMachineCPUModelListerExpansion + Get(name string) (*v1alpha2.VirtualMachineClass, error) + VirtualMachineClassListerExpansion } -// virtualMachineCPUModelLister implements the VirtualMachineCPUModelLister interface. -type virtualMachineCPUModelLister struct { +// virtualMachineClassLister implements the VirtualMachineClassLister interface. +type virtualMachineClassLister struct { indexer cache.Indexer } -// NewVirtualMachineCPUModelLister returns a new VirtualMachineCPUModelLister. -func NewVirtualMachineCPUModelLister(indexer cache.Indexer) VirtualMachineCPUModelLister { - return &virtualMachineCPUModelLister{indexer: indexer} +// NewVirtualMachineClassLister returns a new VirtualMachineClassLister. +func NewVirtualMachineClassLister(indexer cache.Indexer) VirtualMachineClassLister { + return &virtualMachineClassLister{indexer: indexer} } -// List lists all VirtualMachineCPUModels in the indexer. -func (s *virtualMachineCPUModelLister) List(selector labels.Selector) (ret []*v1alpha2.VirtualMachineCPUModel, err error) { +// List lists all VirtualMachineClasses in the indexer. +func (s *virtualMachineClassLister) List(selector labels.Selector) (ret []*v1alpha2.VirtualMachineClass, err error) { err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha2.VirtualMachineCPUModel)) + ret = append(ret, m.(*v1alpha2.VirtualMachineClass)) }) return ret, err } -// Get retrieves the VirtualMachineCPUModel from the index for a given name. -func (s *virtualMachineCPUModelLister) Get(name string) (*v1alpha2.VirtualMachineCPUModel, error) { +// Get retrieves the VirtualMachineClass from the index for a given name. +func (s *virtualMachineClassLister) Get(name string) (*v1alpha2.VirtualMachineClass, error) { obj, exists, err := s.indexer.GetByKey(name) if err != nil { return nil, err } if !exists { - return nil, errors.NewNotFound(v1alpha2.Resource("virtualmachinecpumodel"), name) + return nil, errors.NewNotFound(v1alpha2.Resource("virtualmachineclass"), name) } - return obj.(*v1alpha2.VirtualMachineCPUModel), nil + return obj.(*v1alpha2.VirtualMachineClass), nil } diff --git a/api/core/v1alpha2/events.go b/api/core/v1alpha2/events.go index 7b3881356..e7a6c58e9 100644 --- a/api/core/v1alpha2/events.go +++ b/api/core/v1alpha2/events.go @@ -80,4 +80,7 @@ const ( // ReasonVMOPSucceeded is event reason that the operation is successfully completed ReasonVMOPSucceeded = "VirtualMachineOperationSucceeded" + + // ReasonVMClassInUse is event reason that VMClass is used by virtual machine. + ReasonVMClassInUse = "VirtualMachineClassInUse" ) diff --git a/api/core/v1alpha2/finalizers.go b/api/core/v1alpha2/finalizers.go index 8419431f9..45bb8504a 100644 --- a/api/core/v1alpha2/finalizers.go +++ b/api/core/v1alpha2/finalizers.go @@ -34,4 +34,5 @@ const ( FinalizerIPAddressLeaseCleanup = "virtualization.deckhouse.io/vmipl-cleanup" FinalizerVMBDACleanup = "virtualization.deckhouse.io/vmbda-cleanup" FinalizerVMOPCleanup = "virtualization.deckhouse.io/vmop-cleanup" + FinalizerVMClassCleanup = "virtualization.deckhouse.io/vmclass-cleanup" ) diff --git a/api/core/v1alpha2/register.go b/api/core/v1alpha2/register.go index 2df73ce9b..d841323a0 100644 --- a/api/core/v1alpha2/register.go +++ b/api/core/v1alpha2/register.go @@ -72,8 +72,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { &VirtualMachineList{}, &VirtualMachineBlockDeviceAttachment{}, &VirtualMachineBlockDeviceAttachmentList{}, - &VirtualMachineCPUModel{}, - &VirtualMachineCPUModelList{}, + &VirtualMachineClass{}, + &VirtualMachineClassList{}, &VirtualMachineIPAddress{}, &VirtualMachineIPAddressList{}, &VirtualMachineIPAddressLease{}, diff --git a/api/core/v1alpha2/virtual_machine.go b/api/core/v1alpha2/virtual_machine.go index cdbdfdf94..80208e5c6 100644 --- a/api/core/v1alpha2/virtual_machine.go +++ b/api/core/v1alpha2/virtual_machine.go @@ -19,6 +19,7 @@ package v1alpha2 import ( corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" virtv1 "kubevirt.io/api/core/v1" ) @@ -73,12 +74,13 @@ type VirtualMachineSpec struct { // Default value is true, so omitempty is not specified. EnableParavirtualization bool `json:"enableParavirtualization"` - OsType OsType `json:"osType,omitempty"` - Bootloader BootloaderType `json:"bootloader,omitempty"` - CPU CPUSpec `json:"cpu"` - Memory MemorySpec `json:"memory"` - BlockDeviceRefs []BlockDeviceSpecRef `json:"blockDeviceRefs"` - Provisioning *Provisioning `json:"provisioning"` + OsType OsType `json:"osType,omitempty"` + Bootloader BootloaderType `json:"bootloader,omitempty"` + VirtualMachineClassName string `json:"virtualMachineClassName,omitempty"` + CPU CPUSpec `json:"cpu"` + Memory MemorySpec `json:"memory"` + BlockDeviceRefs []BlockDeviceSpecRef `json:"blockDeviceRefs"` + Provisioning *Provisioning `json:"provisioning"` } type RunPolicy string @@ -107,13 +109,12 @@ const ( ) type CPUSpec struct { - VirtualMachineCPUModel string `json:"virtualMachineCPUModelName"` - Cores int `json:"cores"` - CoreFraction string `json:"coreFraction"` + Cores int `json:"cores"` + CoreFraction string `json:"coreFraction"` } type MemorySpec struct { - Size string `json:"size"` + Size resource.Quantity `json:"size"` } type RestartApprovalMode string diff --git a/api/core/v1alpha2/virtual_machine_class.go b/api/core/v1alpha2/virtual_machine_class.go new file mode 100644 index 000000000..4afb59273 --- /dev/null +++ b/api/core/v1alpha2/virtual_machine_class.go @@ -0,0 +1,222 @@ +/* +Copyright 2024 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 v1alpha2 + +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 a 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:subresource:status +// +kubebuilder:resource:categories=virtualization,scope=Cluster,shortName={vmc,vmcs},singular=virtualmachineclass +// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="VirtualMachineClass phase." +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time of creation resource." +// +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 VirtualMachineClass +// +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 CDIs + Items []VirtualMachineClass `json:"items"` +} + +type VirtualMachineClassSpec struct { + NodeSelector NodeSelector `json:"nodeSelector,omitempty"` + // +kubebuilder:validation:Required + CPU CPU `json:"cpu"` + SizingPolicies []SizingPolicy `json:"sizingPolicies,omitempty"` +} + +// NodeSelector defines selects the nodes that are targeted to VM scheduling. +type NodeSelector struct { + // 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. + MatchLabels map[string]string `json:"matchLabels,omitempty"` + MatchExpressions []corev1.NodeSelectorRequirement `json:"matchExpressions,omitempty"` +} + +// CPU defines the requirements for the virtual CPU model. +// +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"` + // The name of CPU model. More information about models [here](https://libvirt.org/formatdomain.html#cpu-model-and-topology) + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:example=IvyBridge + Model string `json:"model,omitempty"` + // A list of CPU instructions (features) required when type=Features. + // More information about features [here](https://libvirt.org/formatdomain.html#cpu-model-and-topology) + // + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:example={mmx, vmx, sse2} + Features []string `json:"features,omitempty"` + // Create CPU model based on an intersection CPU features for selected nodes. + Discovery metav1.LabelSelector `json:"discovery,omitempty"` +} + +// SizingPolicy define 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. + 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"` +} + +// +kubebuilder:validation:Enum={5,10,20,50,100} +type CoreFractionValue int + +type SizingPolicyMemory struct { + MemoryMinMax `json:",inline"` + // Memory size discretization step. I.e. min=2Gi, max=4Gi, step=1Gi allows to set virtual machine memory size to 2Gi, 3Gi, or 4Gi. + // + // +kubebuilder:example="512Mi" + Step resource.Quantity `json:"step"` + + // Amount of memory per CPU core. + PerCore SizingPolicyMemoryPerCore `json:"perCore"` +} + +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 cpu core count. + // + // +kubebuilder:validation:Required + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:example=1 + Min int `json:"min"` + // Maximum cpu core count. + // + // +kubebuilder:validation:Required + // +kubebuilder:validation:Maximum=1024 + // +kubebuilder:example=10 + Max int `json:"max"` + // Cpu cores count discretization step. I.e. min=2, max=10, step=4 allows to set virtual machine cpu cores to 2, 6, or 10. + // + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:example=1 + Step int `json:"step,omitempty"` +} + +// CPUType defines cpu type, the following options are supported: +// * `Host` - use the virtual CPU closest to the host. This offers maximum functionality and performance, includes crucial guest CPU flags for security, and assesses live migration compatibility. +// * `HostPassthrough` - use the host's physical CPU directly with no modifications. In `HostPassthrough` mode, the guest can only be live-migrated to a target host that matches the source host extremely closely. +// * `Discovery` - create a CPU model based on an intersecton CPU features for selected nodes. +// * `Model` - CPU model name. A CPU model is a named and previously defined set of supported CPU instructions. +// * `Features` - the 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"` + // A list of nodes that support this CPU model. + // It is not displayed for the types: `Host`, `HostPassthrough` + // + // +kubebuilder:example={node-1, node-2} + AvailableNodes []string `json:"availableNodes,omitempty"` + Conditions []metav1.Condition `json:"conditions,omitempty"` + // The generation last processed by the controller + ObservedGeneration int64 `json:"observedGeneration,omitempty"` +} + +// CpuFeatures +// Information on CPU features for this model. +// Shown only for types `Features` or `Discovery`. +type CpuFeatures struct { + // A list of CPU features for this model. + // + // +kubebuilder:example={mmx, vmx, sse2} + Enabled []string `json:"enabled,omitempty"` + // A list of unused processor features additionally available for a given group of nodes. + // + // +kubebuilder:example={ssse3, vme} + NotEnabledCommon []string `json:"notEnabledCommon,omitempty"` +} + +// VirtualMachineClassPhase defines current status of resource: +// * Pending - resource is not ready, waits until suitable nodes supporting the required CPU model become 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/v1alpha2/virtual_machine_cpu_model.go b/api/core/v1alpha2/virtual_machine_cpu_model.go deleted file mode 100644 index a20b90791..000000000 --- a/api/core/v1alpha2/virtual_machine_cpu_model.go +++ /dev/null @@ -1,81 +0,0 @@ -/* -Copyright 2024 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 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - -const ( - VMCPUKind = "VirtualMachineCPUModel" - VMCPUResource = "virtualmachinecpumodels" -) - -// VirtualMachineCPUModel an immutable resource describing the processor that will be used in the VM. -// +genclient -// +genclient:nonNamespaced -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type VirtualMachineCPUModel struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec VirtualMachineCPUModelSpec `json:"spec"` - Status VirtualMachineCPUModelStatus `json:"status"` -} - -// VirtualMachineCPUModelList contains a list of VirtualMachineCPUModel -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type VirtualMachineCPUModelList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - - // Items provides a list of CDIs - Items []VirtualMachineCPUModel `json:"items"` -} - -type VirtualMachineCPUModelSpec struct { - Type VirtualMachineCPUModelSpecType `json:"type"` - Model string `json:"model"` - Features []string `json:"features"` -} - -type VirtualMachineCPUModelSpecType string - -const ( - Host VirtualMachineCPUModelSpecType = "Host" - Model VirtualMachineCPUModelSpecType = "Model" - Features VirtualMachineCPUModelSpecType = "Features" -) - -type VirtualMachineCPUModelStatus struct { - Features *VirtualMachineCPUModelStatusFeatures `json:"features,omitempty"` - Nodes *[]string `json:"nodes,omitempty"` - Phase VirtualMachineCPUModelStatusPhase `json:"phase"` -} - -type VirtualMachineCPUModelStatusFeatures struct { - Enabled []string `json:"enabled"` - NotEnabledCommon []string `json:"notEnabledCommon"` -} - -type VirtualMachineCPUModelStatusPhase string - -const ( - VMCPUPhasePending VirtualMachineCPUModelStatusPhase = "Pending" - VMCPUPhaseInProgress VirtualMachineCPUModelStatusPhase = "InProgress" - VMCPUPhaseReady VirtualMachineCPUModelStatusPhase = "Ready" - VMCPUPhaseFailed VirtualMachineCPUModelStatusPhase = "Failed" - VMCPUPhaseTerminating VirtualMachineCPUModelStatusPhase = "Terminating" -) diff --git a/api/core/v1alpha2/vmclasscondition/condition.go b/api/core/v1alpha2/vmclasscondition/condition.go new file mode 100644 index 000000000..3d88e331b --- /dev/null +++ b/api/core/v1alpha2/vmclasscondition/condition.go @@ -0,0 +1,49 @@ +/* +Copyright 2024 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" +) + +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" + + ReasonUnknown Reason = "Unknown" +) diff --git a/api/core/v1alpha2/vmcondition/condition.go b/api/core/v1alpha2/vmcondition/condition.go index b5fe00f37..add810115 100644 --- a/api/core/v1alpha2/vmcondition/condition.go +++ b/api/core/v1alpha2/vmcondition/condition.go @@ -23,8 +23,8 @@ func (t Type) String() string { } const ( - TypeCPUModelReady Type = "CPUModelReady" TypeIPAddressReady Type = "VirtualMachineIPAddressReady" + TypeClassReady Type = "ClassReady" TypeBlockDevicesReady Type = "BlockDevicesReady" TypeRunning Type = "Running" TypeMigrating Type = "Migrating" @@ -45,8 +45,8 @@ func (r Reason) String() string { const ( ReasonAgentNotReady Reason = "AgentNotReady" - ReasonCPUModelReady Reason = "CPUModelReady" - ReasonCPUModelNotReady Reason = "CPUModelNotReady" + ReasonClassReady Reason = "ClassReady" + ReasonClassNotReady Reason = "ClassNotReady" ReasonIPAddressReady Reason = "VirtualMachineIPAddressReady" ReasonIPAddressNotReady Reason = "VirtualMachineIPAddressNotReady" diff --git a/api/core/v1alpha2/zz_generated.deepcopy.go b/api/core/v1alpha2/zz_generated.deepcopy.go index 3e98e70ed..73736a0ac 100644 --- a/api/core/v1alpha2/zz_generated.deepcopy.go +++ b/api/core/v1alpha2/zz_generated.deepcopy.go @@ -76,6 +76,28 @@ func (in *BlockDeviceStatusRef) DeepCopy() *BlockDeviceStatusRef { return out } +// 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) + } + in.Discovery.DeepCopyInto(&out.Discovery) + 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 *CPUSpec) DeepCopyInto(out *CPUSpec) { *out = *in @@ -257,6 +279,32 @@ func (in *ClusterVirtualImageStatus) DeepCopy() *ClusterVirtualImageStatus { 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 *DataSourceContainerRegistry) DeepCopyInto(out *DataSourceContainerRegistry) { *out = *in @@ -413,9 +461,28 @@ func (in *ImageStatusTarget) DeepCopy() *ImageStatusTarget { 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 *MemorySpec) DeepCopyInto(out *MemorySpec) { *out = *in + out.Size = in.Size.DeepCopy() return } @@ -429,6 +496,36 @@ func (in *MemorySpec) DeepCopy() *MemorySpec { 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([]corev1.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 *Provisioning) DeepCopyInto(out *Provisioning) { *out = *in @@ -455,6 +552,94 @@ func (in *Provisioning) DeepCopy() *Provisioning { 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 *StatusSpeed) DeepCopyInto(out *StatusSpeed) { *out = *in @@ -1196,7 +1381,7 @@ func (in *VirtualMachineBlockDeviceAttachmentStatus) DeepCopy() *VirtualMachineB } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VirtualMachineCPUModel) DeepCopyInto(out *VirtualMachineCPUModel) { +func (in *VirtualMachineClass) DeepCopyInto(out *VirtualMachineClass) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) @@ -1205,18 +1390,18 @@ func (in *VirtualMachineCPUModel) DeepCopyInto(out *VirtualMachineCPUModel) { return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualMachineCPUModel. -func (in *VirtualMachineCPUModel) DeepCopy() *VirtualMachineCPUModel { +// 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(VirtualMachineCPUModel) + out := new(VirtualMachineClass) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *VirtualMachineCPUModel) DeepCopyObject() runtime.Object { +func (in *VirtualMachineClass) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -1224,13 +1409,13 @@ func (in *VirtualMachineCPUModel) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VirtualMachineCPUModelList) DeepCopyInto(out *VirtualMachineCPUModelList) { +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([]VirtualMachineCPUModel, len(*in)) + *out = make([]VirtualMachineClass, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1238,18 +1423,18 @@ func (in *VirtualMachineCPUModelList) DeepCopyInto(out *VirtualMachineCPUModelLi return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualMachineCPUModelList. -func (in *VirtualMachineCPUModelList) DeepCopy() *VirtualMachineCPUModelList { +// 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(VirtualMachineCPUModelList) + out := new(VirtualMachineClassList) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *VirtualMachineCPUModelList) DeepCopyObject() runtime.Object { +func (in *VirtualMachineClassList) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -1257,78 +1442,55 @@ func (in *VirtualMachineCPUModelList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VirtualMachineCPUModelSpec) DeepCopyInto(out *VirtualMachineCPUModelSpec) { - *out = *in - if in.Features != nil { - in, out := &in.Features, &out.Features - *out = make([]string, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualMachineCPUModelSpec. -func (in *VirtualMachineCPUModelSpec) DeepCopy() *VirtualMachineCPUModelSpec { - if in == nil { - return nil - } - out := new(VirtualMachineCPUModelSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VirtualMachineCPUModelStatus) DeepCopyInto(out *VirtualMachineCPUModelStatus) { +func (in *VirtualMachineClassSpec) DeepCopyInto(out *VirtualMachineClassSpec) { *out = *in - if in.Features != nil { - in, out := &in.Features, &out.Features - *out = new(VirtualMachineCPUModelStatusFeatures) - (*in).DeepCopyInto(*out) - } - if in.Nodes != nil { - in, out := &in.Nodes, &out.Nodes - *out = new([]string) - if **in != nil { - in, out := *in, *out - *out = make([]string, len(*in)) - copy(*out, *in) + in.NodeSelector.DeepCopyInto(&out.NodeSelector) + 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 VirtualMachineCPUModelStatus. -func (in *VirtualMachineCPUModelStatus) DeepCopy() *VirtualMachineCPUModelStatus { +// 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(VirtualMachineCPUModelStatus) + 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 *VirtualMachineCPUModelStatusFeatures) DeepCopyInto(out *VirtualMachineCPUModelStatusFeatures) { +func (in *VirtualMachineClassStatus) DeepCopyInto(out *VirtualMachineClassStatus) { *out = *in - if in.Enabled != nil { - in, out := &in.Enabled, &out.Enabled + 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.NotEnabledCommon != nil { - in, out := &in.NotEnabledCommon, &out.NotEnabledCommon - *out = make([]string, len(*in)) - copy(*out, *in) + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.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 VirtualMachineCPUModelStatusFeatures. -func (in *VirtualMachineCPUModelStatusFeatures) DeepCopy() *VirtualMachineCPUModelStatusFeatures { +// 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(VirtualMachineCPUModelStatusFeatures) + out := new(VirtualMachineClassStatus) in.DeepCopyInto(out) return out } @@ -1810,7 +1972,7 @@ func (in *VirtualMachineSpec) DeepCopyInto(out *VirtualMachineSpec) { **out = **in } out.CPU = in.CPU - out.Memory = in.Memory + in.Memory.DeepCopyInto(&out.Memory) if in.BlockDeviceRefs != nil { in, out := &in.BlockDeviceRefs, &out.BlockDeviceRefs *out = make([]BlockDeviceSpecRef, len(*in)) diff --git a/api/pkg/apiserver/api/generated/openapi/zz_generated.openapi.go b/api/pkg/apiserver/api/generated/openapi/zz_generated.openapi.go index 82d37a2b3..436545ff6 100644 --- a/api/pkg/apiserver/api/generated/openapi/zz_generated.openapi.go +++ b/api/pkg/apiserver/api/generated/openapi/zz_generated.openapi.go @@ -35,6 +35,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/deckhouse/virtualization/api/core/v1alpha2.AttachedVirtualMachine": schema_virtualization_api_core_v1alpha2_AttachedVirtualMachine(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.BlockDeviceSpecRef": schema_virtualization_api_core_v1alpha2_BlockDeviceSpecRef(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.BlockDeviceStatusRef": schema_virtualization_api_core_v1alpha2_BlockDeviceStatusRef(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha2.CPU": schema_virtualization_api_core_v1alpha2_CPU(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.CPUSpec": schema_virtualization_api_core_v1alpha2_CPUSpec(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.Checksum": schema_virtualization_api_core_v1alpha2_Checksum(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.ClusterVirtualImage": schema_virtualization_api_core_v1alpha2_ClusterVirtualImage(ref), @@ -43,6 +44,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/deckhouse/virtualization/api/core/v1alpha2.ClusterVirtualImageObjectRef": schema_virtualization_api_core_v1alpha2_ClusterVirtualImageObjectRef(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.ClusterVirtualImageSpec": schema_virtualization_api_core_v1alpha2_ClusterVirtualImageSpec(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.ClusterVirtualImageStatus": schema_virtualization_api_core_v1alpha2_ClusterVirtualImageStatus(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha2.CpuFeatures": schema_virtualization_api_core_v1alpha2_CpuFeatures(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.DataSourceContainerRegistry": schema_virtualization_api_core_v1alpha2_DataSourceContainerRegistry(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.DataSourceHTTP": schema_virtualization_api_core_v1alpha2_DataSourceHTTP(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.DiskTarget": schema_virtualization_api_core_v1alpha2_DiskTarget(ref), @@ -51,8 +53,14 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/deckhouse/virtualization/api/core/v1alpha2.ImageStatus": schema_virtualization_api_core_v1alpha2_ImageStatus(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.ImageStatusSize": schema_virtualization_api_core_v1alpha2_ImageStatusSize(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.ImageStatusTarget": schema_virtualization_api_core_v1alpha2_ImageStatusTarget(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha2.MemoryMinMax": schema_virtualization_api_core_v1alpha2_MemoryMinMax(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.MemorySpec": schema_virtualization_api_core_v1alpha2_MemorySpec(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha2.NodeSelector": schema_virtualization_api_core_v1alpha2_NodeSelector(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.Provisioning": schema_virtualization_api_core_v1alpha2_Provisioning(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha2.SizingPolicy": schema_virtualization_api_core_v1alpha2_SizingPolicy(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha2.SizingPolicyCores": schema_virtualization_api_core_v1alpha2_SizingPolicyCores(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha2.SizingPolicyMemory": schema_virtualization_api_core_v1alpha2_SizingPolicyMemory(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha2.SizingPolicyMemoryPerCore": schema_virtualization_api_core_v1alpha2_SizingPolicyMemoryPerCore(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.StatusSpeed": schema_virtualization_api_core_v1alpha2_StatusSpeed(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.SysprepRef": schema_virtualization_api_core_v1alpha2_SysprepRef(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.UserDataRef": schema_virtualization_api_core_v1alpha2_UserDataRef(ref), @@ -82,11 +90,10 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineBlockDeviceAttachmentList": schema_virtualization_api_core_v1alpha2_VirtualMachineBlockDeviceAttachmentList(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineBlockDeviceAttachmentSpec": schema_virtualization_api_core_v1alpha2_VirtualMachineBlockDeviceAttachmentSpec(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineBlockDeviceAttachmentStatus": schema_virtualization_api_core_v1alpha2_VirtualMachineBlockDeviceAttachmentStatus(ref), - "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineCPUModel": schema_virtualization_api_core_v1alpha2_VirtualMachineCPUModel(ref), - "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineCPUModelList": schema_virtualization_api_core_v1alpha2_VirtualMachineCPUModelList(ref), - "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineCPUModelSpec": schema_virtualization_api_core_v1alpha2_VirtualMachineCPUModelSpec(ref), - "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineCPUModelStatus": schema_virtualization_api_core_v1alpha2_VirtualMachineCPUModelStatus(ref), - "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineCPUModelStatusFeatures": schema_virtualization_api_core_v1alpha2_VirtualMachineCPUModelStatusFeatures(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineClass": schema_virtualization_api_core_v1alpha2_VirtualMachineClass(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineClassList": schema_virtualization_api_core_v1alpha2_VirtualMachineClassList(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineClassSpec": schema_virtualization_api_core_v1alpha2_VirtualMachineClassSpec(ref), + "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineClassStatus": schema_virtualization_api_core_v1alpha2_VirtualMachineClassStatus(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineIPAddress": schema_virtualization_api_core_v1alpha2_VirtualMachineIPAddress(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineIPAddressLease": schema_virtualization_api_core_v1alpha2_VirtualMachineIPAddressLease(ref), "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineIPAddressLeaseIpAddressRef": schema_virtualization_api_core_v1alpha2_VirtualMachineIPAddressLeaseIpAddressRef(ref), @@ -742,19 +749,64 @@ func schema_virtualization_api_core_v1alpha2_BlockDeviceStatusRef(ref common.Ref } } -func schema_virtualization_api_core_v1alpha2_CPUSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_virtualization_api_core_v1alpha2_CPU(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, + Description: "CPU defines the requirements for the virtual CPU model.", + Type: []string{"object"}, Properties: map[string]spec.Schema{ - "virtualMachineCPUModelName": { + "type": { SchemaProps: spec.SchemaProps{ Default: "", Type: []string{"string"}, Format: "", }, }, + "model": { + SchemaProps: spec.SchemaProps{ + Description: "The name of CPU model. More information about models [here](https://libvirt.org/formatdomain.html#cpu-model-and-topology)", + Type: []string{"string"}, + Format: "", + }, + }, + "features": { + SchemaProps: spec.SchemaProps{ + Description: "A list of CPU instructions (features) required when type=Features. More information about features [here](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 CPU model based on an intersection CPU features for selected nodes.", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), + }, + }, + }, + Required: []string{"type"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, + } +} + +func schema_virtualization_api_core_v1alpha2_CPUSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ "cores": { SchemaProps: spec.SchemaProps{ Default: 0, @@ -770,7 +822,7 @@ func schema_virtualization_api_core_v1alpha2_CPUSpec(ref common.ReferenceCallbac }, }, }, - Required: []string{"virtualMachineCPUModelName", "cores", "coreFraction"}, + Required: []string{"cores", "coreFraction"}, }, }, } @@ -1072,6 +1124,50 @@ func schema_virtualization_api_core_v1alpha2_ClusterVirtualImageStatus(ref commo } } +func schema_virtualization_api_core_v1alpha2_CpuFeatures(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "CpuFeatures Information on CPU features for this model. Shown only for types `Features` or `Discovery`.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "enabled": { + SchemaProps: spec.SchemaProps{ + Description: "A 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: "A 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: "", + }, + }, + }, + }, + }, + }, + Required: []string{"enabled", "notEnabledCommon"}, + }, + }, + } +} + func schema_virtualization_api_core_v1alpha2_DataSourceContainerRegistry(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -1335,6 +1431,32 @@ func schema_virtualization_api_core_v1alpha2_ImageStatusTarget(ref common.Refere } } +func schema_virtualization_api_core_v1alpha2_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_v1alpha2_MemorySpec(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -1343,15 +1465,59 @@ func schema_virtualization_api_core_v1alpha2_MemorySpec(ref common.ReferenceCall Properties: map[string]spec.Schema{ "size": { SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), }, }, }, Required: []string{"size"}, }, }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_virtualization_api_core_v1alpha2_NodeSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeSelector defines selects the nodes that are targeted to 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} 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: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "matchExpressions": { + SchemaProps: spec.SchemaProps{ + 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"}, } } @@ -1393,6 +1559,165 @@ func schema_virtualization_api_core_v1alpha2_Provisioning(ref common.ReferenceCa } } +func schema_virtualization_api_core_v1alpha2_SizingPolicy(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SizingPolicy define 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/v1alpha2.SizingPolicyMemory"), + }, + }, + "coreFractions": { + SchemaProps: spec.SchemaProps{ + Description: "Allowed values of the `coreFraction` parameter.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + "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/v1alpha2.SizingPolicyCores"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/deckhouse/virtualization/api/core/v1alpha2.SizingPolicyCores", "github.com/deckhouse/virtualization/api/core/v1alpha2.SizingPolicyMemory"}, + } +} + +func schema_virtualization_api_core_v1alpha2_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 cpu core count.", + Default: 0, + Type: []string{"integer"}, + Format: "int32", + }, + }, + "max": { + SchemaProps: spec.SchemaProps{ + Description: "Maximum cpu core count.", + Default: 0, + Type: []string{"integer"}, + Format: "int32", + }, + }, + "step": { + SchemaProps: spec.SchemaProps{ + Description: "Cpu cores count discretization step. I.e. min=2, max=10, step=4 allows to set virtual machine cpu cores to 2, 6, or 10.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"min", "max"}, + }, + }, + } +} + +func schema_virtualization_api_core_v1alpha2_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. I.e. min=2Gi, max=4Gi, step=1Gi allows to set 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/v1alpha2.SizingPolicyMemoryPerCore"), + }, + }, + }, + Required: []string{"step", "perCore"}, + }, + }, + Dependencies: []string{ + "github.com/deckhouse/virtualization/api/core/v1alpha2.SizingPolicyMemoryPerCore", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_virtualization_api_core_v1alpha2_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_v1alpha2_StatusSpeed(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -2552,11 +2877,11 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineBlockDeviceAttachment } } -func schema_virtualization_api_core_v1alpha2_VirtualMachineCPUModel(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_virtualization_api_core_v1alpha2_VirtualMachineClass(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "VirtualMachineCPUModel an immutable resource describing the processor that will be used in the VM.", + Description: "VirtualMachineClass resource describes a 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": { @@ -2582,29 +2907,29 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineCPUModel(ref common.R "spec": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineCPUModelSpec"), + Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineClassSpec"), }, }, "status": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineCPUModelStatus"), + Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineClassStatus"), }, }, }, - Required: []string{"spec", "status"}, + Required: []string{"spec"}, }, }, Dependencies: []string{ - "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineCPUModelSpec", "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineCPUModelStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineClassSpec", "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineClassStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, } } -func schema_virtualization_api_core_v1alpha2_VirtualMachineCPUModelList(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_virtualization_api_core_v1alpha2_VirtualMachineClassList(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "VirtualMachineCPUModelList contains a list of VirtualMachineCPUModel", + Description: "VirtualMachineClassList contains a list of VirtualMachineClass", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -2635,7 +2960,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineCPUModelList(ref comm Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineCPUModel"), + Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineClass"), }, }, }, @@ -2646,76 +2971,56 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineCPUModelList(ref comm }, }, Dependencies: []string{ - "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineCPUModel", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineClass", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, } } -func schema_virtualization_api_core_v1alpha2_VirtualMachineCPUModelSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_virtualization_api_core_v1alpha2_VirtualMachineClassSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ Type: []string{"object"}, Properties: map[string]spec.Schema{ - "type": { + "nodeSelector": { SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", + Default: map[string]interface{}{}, + Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha2.NodeSelector"), }, }, - "model": { + "cpu": { SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", + Default: map[string]interface{}{}, + Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha2.CPU"), }, }, - "features": { + "sizingPolicies": { SchemaProps: spec.SchemaProps{ Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", + Default: map[string]interface{}{}, + Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha2.SizingPolicy"), }, }, }, }, }, }, - Required: []string{"type", "model", "features"}, + Required: []string{"cpu"}, }, }, + Dependencies: []string{ + "github.com/deckhouse/virtualization/api/core/v1alpha2.CPU", "github.com/deckhouse/virtualization/api/core/v1alpha2.NodeSelector", "github.com/deckhouse/virtualization/api/core/v1alpha2.SizingPolicy"}, } } -func schema_virtualization_api_core_v1alpha2_VirtualMachineCPUModelStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_virtualization_api_core_v1alpha2_VirtualMachineClassStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ Type: []string{"object"}, Properties: map[string]spec.Schema{ - "features": { - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineCPUModelStatusFeatures"), - }, - }, - "nodes": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, "phase": { SchemaProps: spec.SchemaProps{ Default: "", @@ -2723,24 +3028,16 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineCPUModelStatus(ref co Format: "", }, }, - }, - Required: []string{"phase"}, - }, - }, - Dependencies: []string{ - "github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualMachineCPUModelStatusFeatures"}, - } -} - -func schema_virtualization_api_core_v1alpha2_VirtualMachineCPUModelStatusFeatures(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "enabled": { + "cpuFeatures": { SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, + Default: map[string]interface{}{}, + Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha2.CpuFeatures"), + }, + }, + "availableNodes": { + SchemaProps: spec.SchemaProps{ + Description: "A list of nodes that support this CPU model. It is not displayed for the types: `Host`, `HostPassthrough`", + Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -2752,24 +3049,32 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineCPUModelStatusFeature }, }, }, - "notEnabledCommon": { + "conditions": { SchemaProps: spec.SchemaProps{ Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"), }, }, }, }, }, + "observedGeneration": { + SchemaProps: spec.SchemaProps{ + Description: "The generation last processed by the controller", + Type: []string{"integer"}, + Format: "int64", + }, + }, }, - Required: []string{"enabled", "notEnabledCommon"}, + Required: []string{"phase", "cpuFeatures", "availableNodes"}, }, }, + Dependencies: []string{ + "github.com/deckhouse/virtualization/api/core/v1alpha2.CpuFeatures", "k8s.io/apimachinery/pkg/apis/meta/v1.Condition"}, } } @@ -3094,35 +3399,36 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineIPAddressStatus(ref c Properties: map[string]spec.Schema{ "virtualMachineName": { SchemaProps: spec.SchemaProps{ - Description: "Represents the virtual machine that currently uses this IP address.", + Description: "VirtualMachine represents the virtual machine that currently uses this IP address. It's the name of the virtual machine instance.", Type: []string{"string"}, Format: "", }, }, "address": { SchemaProps: spec.SchemaProps{ - Description: "Assigned IP address.", + Description: "Address is the assigned IP address allocated to the virtual machine.", Type: []string{"string"}, Format: "", }, }, "phase": { SchemaProps: spec.SchemaProps{ - Description: "Represents the current state of IP address.", + Description: "Phase represents the current state of the IP address. It could indicate whether the IP address is in use, available, or in any other defined state.", Type: []string{"string"}, Format: "", }, }, "observedGeneration": { SchemaProps: spec.SchemaProps{ - Description: "Detailed description of the error.", + Description: "ObservedGeneration is the most recent generation observed by the controller. This is used to identify changes that have been recently observed and handled.", Type: []string{"integer"}, Format: "int64", }, }, "conditions": { SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, + Description: "Conditions represents the latest available observations of the object's state. They provide detailed status and information, such as whether the IP address allocation was successful, in progress, etc.", + Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -3586,6 +3892,12 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineSpec(ref common.Refer Format: "", }, }, + "virtualMachineClassName": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, "cpu": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, diff --git a/api/scripts/update-codegen.sh b/api/scripts/update-codegen.sh index 8f5e326d5..ee41a36f1 100755 --- a/api/scripts/update-codegen.sh +++ b/api/scripts/update-codegen.sh @@ -1,13 +1,13 @@ #!/bin/bash # Copyright 2024 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. @@ -19,9 +19,9 @@ set -o pipefail function usage { cat < +Usage: $(basename "$0") { core | subresources | crds | all } Example: - $(basename "$0") controller + $(basename "$0") core EOF } @@ -30,6 +30,9 @@ function source::settings { SCRIPT_ROOT="${SCRIPT_DIR}/.." CODEGEN_PKG="$(go env GOMODCACHE)/$(go list -f '{{.Path}}@{{.Version}}' -m k8s.io/code-generator)" MODULE="github.com/deckhouse/virtualization/api" + PREFIX_GROUP="virtualization.deckhouse.io_" + # TODO: Temporary filter until all CRDs become auto-generated. + ALLOWED_RESOURCE_GEN_CRD=("VirtualMachineClass" "ExampleKind1" "ExampleKind2") source "${CODEGEN_PKG}/kube_codegen.sh" } @@ -72,6 +75,24 @@ function generate::core { cp -R "${OUTPUT_BASE}/${MODULE}/." "${SCRIPT_ROOT}" } +function generate::crds() { + cd "${SCRIPT_ROOT}/.." + OUTPUT_BASE=$(mktemp -d) + trap 'rm -rf "${OUTPUT_BASE}"' ERR EXIT + + "${GOPATH}/bin/controller-gen" crd paths=./api/core/v1alpha2/... output:crd:dir="${OUTPUT_BASE}" + + # shellcheck disable=SC2044 + for file in $(find "${OUTPUT_BASE}"/* -type f -iname "*.yaml"); do + # TODO: Temporary filter until all CRDs become auto-generated. + # shellcheck disable=SC2002 + if ! [[ " ${ALLOWED_RESOURCE_GEN_CRD[*]} " =~ [[:space:]]$(cat "$file" | yq '.spec.names.kind')[[:space:]] ]]; then + continue + fi + cp "$file" "./crds/${file/#"$OUTPUT_BASE/$PREFIX_GROUP"/}" + done +} + WHAT=$1 if [ "$#" != 1 ] || [ "${WHAT}" == "--help" ] ; then @@ -88,10 +109,15 @@ case "$WHAT" in source::settings generate::subresources ;; + crds) + source::settings + generate::crds + ;; all) source::settings generate::core generate::subresources + generate::crds ;; *) echo "Invalid argument: $WHAT" diff --git a/crds/doc-ru-virtualmachine.yaml b/crds/doc-ru-virtualmachine.yaml index 81dad1601..c81dd8b2e 100644 --- a/crds/doc-ru-virtualmachine.yaml +++ b/crds/doc-ru-virtualmachine.yaml @@ -367,6 +367,9 @@ spec: * BIOS — использовать BIOS. * EFI — использовать Unified Extensible Firmware (EFI/UEFI). * EFIWithSecureBoot — использовать UEFI/EFI с поддержкой SecureBoot. + virtualMachineClassName: + description: | + Имя ресурса `VirtualMachineClass`, который описывает требования к виртуальному CPU, памяти и политику размещения ресурсов. cpu: description: | Блок определяет настройки ЦП для виртуальной машины. diff --git a/crds/doc-ru-virtualmachineclasses.yaml b/crds/doc-ru-virtualmachineclasses.yaml new file mode 100644 index 000000000..02aa2bb75 --- /dev/null +++ b/crds/doc-ru-virtualmachineclasses.yaml @@ -0,0 +1,198 @@ +spec: + versions: + - name: v1alpha2 + schema: + openAPIV3Schema: + description: | + Данный ресурс описывает: + - требования к виртуальному CPU + - требования размещения ВМ на узлах + - политику определения размера ресурсов ВМ. + + Этот ресурс не может быть удален, пока он используется в одной из ВМ. + properties: + spec: + properties: + cpu: + description: | + Блок описания требований к виртуальному CPU. + properties: + discovery: + description: | + Создать универсальную модель виртуального CPU на основе наборов инструкций, которые поддерживаются на всех физических CPU узлов выборки. + + Допустим, у нас есть выборка из трех узлов, где на каждом узле есть физические CPU с различными наборами инструкций. Набор инструкций физического CPU узла 1: [f1, f2, f3, f4], для узла 2: [f1, f2], и для узла 3: [f1, f2, f3]. Для создания универсальной модели CPU для этой выборки узлов, необходимо объединить общие инструкции, присущие всем узлам, то есть [f1, f2]. В результате мы получим виртуальный CPU с набором инструкций [f1, f2] + + properties: + nodeSelector: + description: | + Выборка узлов, на основе которой будет создана универсальная модель CPU. + properties: + matchExpressions: + description: | + Список селекторов лейблов. Требования селекторов объединяются. Т.е. должны выполнятся все условия списка. + Под лейблом понимается пара: `ключ = значение`. + + items: + description: | + Описание селекторов лейблов. + properties: + key: + description: | + Название ключа лейбла, к которому применятся селектор. + operator: + description: | + Оператор определяет отношение ключа лейбла к набору его значений. + Допустимые операторы: `In`, `NotIn`, `Exists` и `DoesNotExist`: + `In` - значение ключа лейбла входит в заданный список лейблов `values`. + `NotIn` - значение ключа лейбла не входит в заданный список лейблов `values`. + `Exist` - ключ лейбла существует. + `DoesNotExist` - ключа лейблане существует. + + values: + description: | + Строковый список значений. Если оператор `In` или `NotIn`, список значений должен быть непустым. Если оператор `Exists` или `DoesNotExist`, список значений должен быть пустым. + matchLabels: + description: | + Карта пар `ключ=значение`. Один `ключ=значение` для matchLabels эквивалентен элементу matchExpressions, ключевым полем которого является «ключ», оператором - «In», а список значений содержит только «значение». + + Для выборки по данному критерию все значения логически суммируются. + features: + description: | + Необходимые инструкции для процессора в виде списка Дополнительную информацию о поддерживаемых инструкциях можно найти [здесь](https://libvirt.org/formatdomain.html#cpu-model-and-topology) + model: + description: | + Название модели процессора. Дополнительная информация о моделях [здесь](https://libvirt.org/formatdomain.html#cpu-model-and-topology) + type: + description: | + В качестве типа ресурса, поддерживаются следующие варианты: + * `Хост` - использование виртуального CPU, ближайшего к хосту. Это обеспечивает максимальную функциональность и производительность, включает критические флаги гостевого CPU для безопасности и оценивает совместимость с живой миграцией. + * `HostPassthrough` - использование физического CPU хоста напрямую без каких-либо изменений. В режиме `HostPassthrough` гость может быть перенесен только на целевой хост, который очень точно соответствует исходному хосту. + * `Discovery` - создание виртуального CPU на основе наборов инструкций физических CPU для заданного набора узлов. + * `Model` - модель процессора. Модель процессора - это именованный и предварительно определенный набор поддерживаемых инструкций процессора. + * `Features` - необходимый набор поддерживаемых инструкций для процессора. + nodeSelector: + description: | + Селектор узлов, на которые разрешено планировать ВМ для запуска. + properties: + matchExpressions: + description: | + Список селекторов лейблов. Требования селекторов объединяются. Т.е. должны выполнятся все условия списка. + Под лейблом понимается пара: `ключ = значение`. + + items: + description: | + Описание селекторов лейблов. + properties: + key: + description: | + Название ключа лейбла, к которому применятся селектор. + operator: + description: | + Оператор определяет отношение ключа лейбла к набору его значений. + Допустимые операторы: `In`, `NotIn`, `Exists` и `DoesNotExist`: + `In` - значение ключа лейбла входит в заданный список лейблов `values`. + `NotIn` - значение ключа лейбла не входит в заданный список лейблов `values`. + `Exist` - ключ лейбла существует. + `DoesNotExist` - ключа лейблане существует. + + values: + description: | + Строковый список значений. Если оператор `In` или `NotIn`, список значений должен быть непустым. Если оператор `Exists` или `DoesNotExist`, список значений должен быть пустым. + matchLabels: + description: | + Карта пар `ключ=значение`. Один `ключ=значение` для matchLabels эквивалентен элементу matchExpressions, ключевым полем которого является «ключ», оператором - «In», а список значений содержит только «значение». + + Для выборки по данному критерию все значения логически суммируются. + + sizingPolicies: + description: | + Политика выделения вычислительных ресурсов ВМ. Представлена в виде списка. Диапазоны cores.min - cores.max для разных элементов списка не должны пересекаться. + items: + properties: + coreFractions: + description: | + Допустимые значения параметра `coreFraction`. + cores: + description: | + Политика применяется для заданного диапазона числа ядер CPU. + properties: + max: + description: | + Максимальное число ядер CPU. + min: + description: | + Минимальное число ядер CPU. + step: + description: | + Шаг увеличения числа ядер CPU. + dedicatedCores: + description: | + Допустимые значения параметра `dedicatedCores`. + memory: + description: | + Политика определения размера памяти. + properties: + max: + description: | + Максимальный объем памяти. + min: + description: | + Минимальный объем памяти. + perCore: + description: | + Объем памяти на одно ядро процессора. + properties: + max: + description: | + Максимальный объем памяти. + min: + description: | + Минимальный объем памяти. + step: + description: | + Шаг увеличения объема памяти. + status: + properties: + availableNodes: + description: | + Список узлов, поддерживающих эту модель процессора. + Не отображается для типов: `Host`, `HostPassthrough`. + conditions: + description: | + Последнее подтвержденное состояние данного ресурса. + items: + properties: + lastProbeTime: + description: Время проверки условия. + lastTransitionTime: + description: Время перехода условия из одного состояния в другое. + message: + description: Удобочитаемое сообщение с подробной информацией о последнем переходе. + reason: + description: Краткая причина последнего перехода состояния. + status: + description: | + Статус условия. Возможные значения: `True`, `False`, `Unknown`. + type: + description: Тип условия. + cpuFeatures: + description: | + Информация о поддерживаемых наборах инструкций процессора для данной модели. + Отображается только для `type=Features`. + properties: + enabled: + description: | + Список наборов инструкций CPU для данной модели. + notEnabledCommon: + description: | + Список неиспользуемых инструкций CPU, дополнительно доступных для данной группы узлов. + observedGeneration: + description: | + Поколение ресурса, которое в последний раз обрабатывалось контроллером + phase: + description: | + Текущий статус ресурса: + * Pending - Ресурс не готов, ожидает, пока станут доступны подходящие узлы, поддерживающие требуемую модель процессора. + * Ready - Ресурс готов и доступен для использования. + * Terminating - Русурс находится в процессе удаления. diff --git a/crds/doc-ru-virtualmachinecpumodel.yaml b/crds/doc-ru-virtualmachinecpumodel.yaml deleted file mode 100644 index a1c1743ec..000000000 --- a/crds/doc-ru-virtualmachinecpumodel.yaml +++ /dev/null @@ -1,46 +0,0 @@ -spec: - versions: - - name: v1alpha2 - schema: - openAPIV3Schema: - description: | - Неизменяемый ресурс описывает модель процессора для использования в ВМ. Ресурс не может быть удален, пока он используется в одной из ВМ. - properties: - spec: - properties: - features: - description: | - Необходимые инструкции для процессора в виде списка Дополнительную информацию о поддерживаемых инструкциях можно найти [здесь](https://libvirt.org/formatdomain.html#cpu-model-and-topology) - model: - description: | - Название модели процессора. Дополнительная информация о моделях [здесь](https://libvirt.org/formatdomain.html#cpu-model-and-topology) - type: - description: | - В качестве типа ресурса, поддерживаются следующие варианты: - * Host - использовать физический процессор хоста. - * Модель - модель процессора. Модель процессора - это именованный и предварительно определенный набор поддерживаемых инструкций процессора. - * Features - необходимый набор поддерживаемых инструкций для процессора. - status: - properties: - features: - description: | - Информация о поддерживаемых наборах инструкций процессора для данной модели. - Отображается только для `type=Features`. - properties: - enabled: - description: | - Список наборов инструкций процессора для данной модели. - notEnabledCommon: - description: | - Список неиспользуемых инструкций процессора, дополнительно доступных для данной группы узлов. - nodes: - description: | - Список узлов, поддерживающих данную модель процессора. - Не отображается для `type=Host`. - phase: - description: | - Текущий статус ресурса `VirtualMachineCPUModel`: - * Pending - ресурс находится в очереди на обработку. - * InProgress - ресурс находится в процессе создания. - * Ready - ресурс успешно создан. - * Failed - в процессе создания ресурса произошла ошибка. diff --git a/crds/virtualmachine.yaml b/crds/virtualmachine.yaml index c97875b20..f4f77caa4 100644 --- a/crds/virtualmachine.yaml +++ b/crds/virtualmachine.yaml @@ -856,7 +856,10 @@ spec: * BIOS - use legacy BIOS. * EFI - use Unified Extensible Firmware (EFI/UEFI). * EFIWithSecureBoot - use UEFI/EFI with SecureBoot support. - + virtualMachineClassName: + type: string + description: | + Name of the `VirtualMachineClass` resource describing the requirements for a virtual CPU, memory and the resource allocation policy. cpu: type: object description: Specifies the CPU settings for the VM. diff --git a/crds/virtualmachineclasses.yaml b/crds/virtualmachineclasses.yaml new file mode 100644 index 000000000..9bedd5427 --- /dev/null +++ b/crds/virtualmachineclasses.yaml @@ -0,0 +1,454 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: virtualmachineclasses.virtualization.deckhouse.io +spec: + group: virtualization.deckhouse.io + names: + categories: + - virtualization + kind: VirtualMachineClass + listKind: VirtualMachineClassList + plural: virtualmachineclasses + shortNames: + - vmc + - vmcs + singular: virtualmachineclass + scope: Cluster + versions: + - additionalPrinterColumns: + - description: VirtualMachineClass phase. + jsonPath: .status.phase + name: Phase + type: string + - description: Time of creation resource. + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: |- + VirtualMachineClass resource describes a 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 CPU model based on an intersection CPU features + for selected nodes. + 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 + required: + - key + - operator + type: object + type: array + 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 + features: + description: |- + A list of CPU instructions (features) required when type=Features. + More information about features [here](https://libvirt.org/formatdomain.html#cpu-model-and-topology) + example: + - mmx + - vmx + - sse2 + items: + type: string + minItems: 1 + type: array + model: + description: + The name of CPU model. More information about models + [here](https://libvirt.org/formatdomain.html#cpu-model-and-topology) + example: IvyBridge + minLength: 1 + type: string + type: + description: |- + CPUType defines cpu type, the following options are supported: + * `Host` - use the virtual CPU closest to the host. This offers maximum functionality and performance, includes crucial guest CPU flags for security, and assesses live migration compatibility. + * `HostPassthrough` - use the host's physical CPU directly with no modifications. In `HostPassthrough` mode, the guest can only be live-migrated to a target host that matches the source host extremely closely. + * `Discovery` - create a CPU model based on an intersecton CPU features for selected nodes. + * `Model` - CPU model name. A CPU model is a named and previously defined set of supported CPU instructions. + * `Features` - the required set of supported instructions for the CPU. + enum: + - Host + - HostPassthrough + - Discovery + - Model + - Features + type: string + required: + - type + type: object + x-kubernetes-validations: + - 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 selects the nodes that are targeted + to VM scheduling. + properties: + matchExpressions: + 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 + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + 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 + sizingPolicies: + items: + description: |- + SizingPolicy define 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. + items: + enum: + - 5 + - 10 + - 20 + - 50 + - 100 + type: integer + type: array + cores: + description: + The policy applies for a specified range of the + number of CPU cores. + properties: + max: + description: Maximum cpu core count. + example: 10 + maximum: 1024 + type: integer + min: + description: Minimum cpu core count. + example: 1 + minimum: 1 + type: integer + step: + description: + Cpu cores count discretization step. I.e. min=2, + max=10, step=4 allows to set 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. I.e. min=2Gi, + max=4Gi, step=1Gi allows to set 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 + required: + - perCore + - step + type: object + type: object + type: array + required: + - cpu + type: object + status: + properties: + availableNodes: + description: |- + A list of nodes that support this CPU model. + It is not displayed for the types: `Host`, `HostPassthrough` + example: + - node-1 + - node-2 + items: + type: string + type: array + conditions: + items: + description: + "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + 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. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + 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 for this model. + Shown only for types `Features` or `Discovery`. + properties: + enabled: + description: " A list of CPU features for this model." + example: + - mmx + - vmx + - sse2 + items: + type: string + type: array + notEnabledCommon: + description: + A list of unused processor features additionally + available for a given group of nodes. + example: + - ssse3 + - vme + items: + type: string + type: array + type: object + observedGeneration: + description: The generation last processed by the controller + format: int64 + type: integer + phase: + description: |- + VirtualMachineClassPhase defines current status of resource: + * Pending - resource is not ready, waits until suitable nodes supporting the required CPU model become 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/virtualmachinecpumodel.yaml b/crds/virtualmachinecpumodel.yaml deleted file mode 100644 index 08cc9218a..000000000 --- a/crds/virtualmachinecpumodel.yaml +++ /dev/null @@ -1,109 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: virtualmachinecpumodels.virtualization.deckhouse.io - labels: - heritage: deckhouse - module: virtualization -spec: - group: virtualization.deckhouse.io - scope: Cluster - names: - categories: - - virtualization - plural: virtualmachinecpumodels - singular: virtualmachinecpumodel - kind: VirtualMachineCPUModel - shortNames: - - vmcpu - - vmcpus - preserveUnknownFields: false - versions: - - name: v1alpha2 - served: true - storage: true - schema: - openAPIV3Schema: - type: object - description: | - The immutable resource describes a CPU model for use in a VM. A resource cannot be deleted as long as it is used in one of the VMs. - required: - - spec - properties: - spec: - type: object - required: - - type - properties: - type: - type: string - description: | - Resource type, the following options are supported: - * Host - use the host's physical CPU directly. - * Model - CPU model. A CPU model is a named and previously defined set of supported processor instructions. - * Features - the required set of supported instructions for the processor. - enum: - - "Host" - - "Model" - - "Features" - model: - type: string - description: | - The name of CPU model. More information about models [here](https://libvirt.org/formatdomain.html#cpu-model-and-topology) - example: IvyBridge - features: - type: array - description: | - Required instructions for the CPU as a list More information about features [here](https://libvirt.org/formatdomain.html#cpu-model-and-topology) - example: ["mmx", "vmx", "sse2"] - items: - type: string - status: - type: object - properties: - phase: - type: string - enum: ["Pending", "InProgress", "Ready", "Failed"] - description: | - Current status of `VirtualMachineCPUModel` resource: - * Pending - the resource is in the queue for processing. - * InProgress - the resource is in the process of being created. - * Ready - the resource has been created successfully. - * Failed - an error occurred during the resource creation process. - features: - type: object - description: | - Information on CPU features for this model. - Shown only for `type=Features`. - properties: - enabled: - type: array - description: | - A list of CPU features for this model. - example: ["mmx", "vmx", "sse2"] - items: - type: string - notEnabledCommon: - type: array - description: | - A list of unused processor features additionally available for a given group of nodes. - example: ["ssse3", "vme"] - items: - type: string - nodes: - type: array - description: | - List of nodes supporting this CPU model. - Not shown for `type=Host`. - example: ["node-1", "node-2"] - items: - type: string - additionalPrinterColumns: - - name: Phase - type: string - jsonPath: .status.phase - - name: Age - type: date - jsonPath: .metadata.creationTimestamp - subresources: - status: {} diff --git a/images/virtualization-artifact/cmd/virtualization-controller/main.go b/images/virtualization-artifact/cmd/virtualization-controller/main.go index a39feaf61..187a397ac 100644 --- a/images/virtualization-artifact/cmd/virtualization-controller/main.go +++ b/images/virtualization-artifact/cmd/virtualization-controller/main.go @@ -40,12 +40,13 @@ import ( "github.com/deckhouse/virtualization-controller/pkg/common" appconfig "github.com/deckhouse/virtualization-controller/pkg/config" - "github.com/deckhouse/virtualization-controller/pkg/controller/cpu" "github.com/deckhouse/virtualization-controller/pkg/controller/cvi" + "github.com/deckhouse/virtualization-controller/pkg/controller/indexer" "github.com/deckhouse/virtualization-controller/pkg/controller/vd" "github.com/deckhouse/virtualization-controller/pkg/controller/vi" "github.com/deckhouse/virtualization-controller/pkg/controller/vm" "github.com/deckhouse/virtualization-controller/pkg/controller/vmbda" + "github.com/deckhouse/virtualization-controller/pkg/controller/vmclass" "github.com/deckhouse/virtualization-controller/pkg/controller/vmip" "github.com/deckhouse/virtualization-controller/pkg/controller/vmiplease" "github.com/deckhouse/virtualization-controller/pkg/controller/vmop" @@ -191,6 +192,11 @@ func main() { // Setup context to gracefully handle termination. ctx := signals.SetupSignalHandler() + if err = indexer.IndexALL(ctx, mgr); err != nil { + log.Error(err, "") + os.Exit(1) + } + if _, err = cvi.NewController(ctx, mgr, log, importerImage, uploaderImage, dvcrSettings, controllerNamespace); err != nil { log.Error(err, "") os.Exit(1) @@ -225,7 +231,7 @@ func main() { os.Exit(1) } - if _, err := cpu.NewVMCPUController(ctx, mgr, log); err != nil { + if _, err := vmclass.NewController(ctx, mgr, slog.Default()); err != nil { log.Error(err, "") os.Exit(1) } diff --git a/images/virtualization-artifact/go.mod b/images/virtualization-artifact/go.mod index e9bed3580..51c126549 100644 --- a/images/virtualization-artifact/go.mod +++ b/images/virtualization-artifact/go.mod @@ -126,6 +126,7 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.5.1 // indirect + k8s.io/component-helpers v0.29.2 // indirect k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 // indirect k8s.io/kms v0.29.2 // indirect kubevirt.io/controller-lifecycle-operator-sdk/api v0.0.0-20220329064328-f3cc58c6ed90 // indirect diff --git a/images/virtualization-artifact/go.sum b/images/virtualization-artifact/go.sum index 4ee5de48d..4c6a61898 100644 --- a/images/virtualization-artifact/go.sum +++ b/images/virtualization-artifact/go.sum @@ -517,6 +517,8 @@ k8s.io/code-generator v0.29.2 h1:c9/iw2KnNpw2IRV+wwuG/Wns2TjPSgjWzbbjTevyiHI= k8s.io/code-generator v0.29.2/go.mod h1:FwFi3C9jCrmbPjekhaCYcYG1n07CYiW1+PAPCockaos= k8s.io/component-base v0.29.2 h1:lpiLyuvPA9yV1aQwGLENYyK7n/8t6l3nn3zAtFTJYe8= k8s.io/component-base v0.29.2/go.mod h1:BfB3SLrefbZXiBfbM+2H1dlat21Uewg/5qtKOl8degM= +k8s.io/component-helpers v0.29.2 h1:1kTIanIdqUVG2nW3e2ENVEaYbZKphqPgEdCmJvk71aw= +k8s.io/component-helpers v0.29.2/go.mod h1:gFc/p60rYtpD8UCcNfPCmbokHT2uy0yDpmr/KKUMNAw= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 h1:pWEwq4Asjm4vjW7vcsmijwBhOr1/shsbSYiWXmNGlks= diff --git a/images/virtualization-artifact/pkg/controller/common/util.go b/images/virtualization-artifact/pkg/controller/common/util.go index d122f5e60..11b8386a5 100644 --- a/images/virtualization-artifact/pkg/controller/common/util.go +++ b/images/virtualization-artifact/pkg/controller/common/util.go @@ -23,6 +23,7 @@ import ( "errors" "fmt" "reflect" + "slices" "strings" "sync" "time" @@ -551,3 +552,44 @@ func SetRecommendedLabels(obj metav1.Object, installerLabels map[string]string, obj.SetLabels(mergedLabels) } + +func NamespacedName(obj client.Object) types.NamespacedName { + return types.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()} +} + +func MatchLabelSelector(labels map[string]string, selector metav1.LabelSelector) bool { + return MatchLabels(labels, selector.MatchLabels) && MatchExpressions(labels, selector.MatchExpressions) +} + +func MatchLabels(labels, matchLabels map[string]string) bool { + for key, value := range matchLabels { + if labels[key] != value { + return false + } + } + return true +} + +func MatchExpressions(labels map[string]string, expressions []metav1.LabelSelectorRequirement) bool { + for _, expr := range expressions { + switch expr.Operator { + case metav1.LabelSelectorOpIn: + if !slices.Contains(expr.Values, labels[expr.Key]) { + return false + } + case metav1.LabelSelectorOpNotIn: + if slices.Contains(expr.Values, labels[expr.Key]) { + return false + } + case metav1.LabelSelectorOpExists: + if _, ok := labels[expr.Key]; !ok { + return false + } + case metav1.LabelSelectorOpDoesNotExist: + if _, ok := labels[expr.Key]; ok { + return false + } + } + } + return true +} diff --git a/images/virtualization-artifact/pkg/controller/cpu/cpu_controller.go b/images/virtualization-artifact/pkg/controller/cpu/cpu_controller.go deleted file mode 100644 index 46e3ee860..000000000 --- a/images/virtualization-artifact/pkg/controller/cpu/cpu_controller.go +++ /dev/null @@ -1,69 +0,0 @@ -/* -Copyright 2024 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 cpu - -import ( - "context" - - "github.com/go-logr/logr" - "sigs.k8s.io/controller-runtime/pkg/builder" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/manager" - - "github.com/deckhouse/virtualization-controller/pkg/sdk/framework/two_phase_reconciler" - "github.com/deckhouse/virtualization/api/core/v1alpha2" -) - -const VMCPUControllerName = "vmcpu-controller" - -func NewVMCPUController( - ctx context.Context, - mgr manager.Manager, - log logr.Logger, -) (controller.Controller, error) { - reconciler := NewVMCPUReconciler() - - reconcilerCore := two_phase_reconciler.NewReconcilerCore[*VMCPUReconcilerState]( - reconciler, - NewVMCPUReconcilerState, - two_phase_reconciler.ReconcilerOptions{ - Client: mgr.GetClient(), - Cache: mgr.GetCache(), - Recorder: mgr.GetEventRecorderFor(VMCPUControllerName), - Scheme: mgr.GetScheme(), - Log: log.WithName(VMCPUControllerName), - }) - - vmcpuController, err := controller.New(VMCPUControllerName, mgr, controller.Options{Reconciler: reconcilerCore}) - if err != nil { - return nil, err - } - if err = reconciler.SetupController(ctx, mgr, vmcpuController); err != nil { - return nil, err - } - - if err = builder.WebhookManagedBy(mgr). - For(&v1alpha2.VirtualMachineCPUModel{}). - WithValidator(NewVMCPUValidator(log)). - Complete(); err != nil { - return nil, err - } - - log.Info("Initialized VirtualMachineCPUModel controller") - - return vmcpuController, nil -} diff --git a/images/virtualization-artifact/pkg/controller/cpu/cpu_reconciler.go b/images/virtualization-artifact/pkg/controller/cpu/cpu_reconciler.go deleted file mode 100644 index b050b58cb..000000000 --- a/images/virtualization-artifact/pkg/controller/cpu/cpu_reconciler.go +++ /dev/null @@ -1,262 +0,0 @@ -/* -Copyright 2024 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 cpu - -import ( - "context" - "fmt" - "strings" - - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - v1 "kubevirt.io/api/core/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/predicate" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" - - "github.com/deckhouse/virtualization-controller/pkg/controller/common" - "github.com/deckhouse/virtualization-controller/pkg/controller/vmattachee" - "github.com/deckhouse/virtualization-controller/pkg/sdk/framework/two_phase_reconciler" - virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" -) - -type VMCPUReconciler struct { - *vmattachee.AttacheeReconciler[*virtv2.VirtualMachineCPUModel, virtv2.VirtualMachineCPUModelStatus] -} - -func NewVMCPUReconciler() *VMCPUReconciler { - return &VMCPUReconciler{ - AttacheeReconciler: vmattachee.NewAttacheeReconciler[ - *virtv2.VirtualMachineCPUModel, - virtv2.VirtualMachineCPUModelStatus, - ](), - } -} - -func (r *VMCPUReconciler) SetupController(ctx context.Context, mgr manager.Manager, ctr controller.Controller) error { - err := ctr.Watch(source.Kind(mgr.GetCache(), &virtv2.VirtualMachineCPUModel{}), &handler.EnqueueRequestForObject{}) - if err != nil { - return err - } - - err = ctr.Watch( - source.Kind(mgr.GetCache(), &corev1.Node{}), - handler.EnqueueRequestsFromMapFunc(r.enqueueRequestsFromMapFunc(mgr.GetClient(), mgr.GetLogger())), - predicate.Funcs{ - CreateFunc: func(e event.CreateEvent) bool { return true }, - DeleteFunc: func(e event.DeleteEvent) bool { return true }, - UpdateFunc: func(e event.UpdateEvent) bool { - oldNode, ok := e.ObjectOld.(*corev1.Node) - if !ok { - return false - } - - newNode, ok := e.ObjectNew.(*corev1.Node) - if !ok { - return false - } - - // TODO replace with maps.Equal() in go1.22.1. - return !isEqualMaps(oldNode.GetLabels(), newNode.GetLabels()) - }, - }, - ) - if err != nil { - return fmt.Errorf("error setting watch on Pod: %w", err) - } - - return r.AttacheeReconciler.SetupController(mgr, ctr, r) -} - -func (r *VMCPUReconciler) Sync(ctx context.Context, _ reconcile.Request, state *VMCPUReconcilerState, opts two_phase_reconciler.ReconcilerOptions) error { - if r.AttacheeReconciler.Sync(ctx, state.AttacheeState, opts) { - return nil - } - - return nil -} - -func (r *VMCPUReconciler) UpdateStatus(_ context.Context, _ reconcile.Request, state *VMCPUReconcilerState, _ two_phase_reconciler.ReconcilerOptions) error { - if state.isDeletion() { - state.VMCPU.Changed().Status.Phase = virtv2.VMCPUPhaseTerminating - - return nil - } - - modelNodes := r.getModelNodes(state.VMCPU.Current().Spec.Model, state.Nodes) - features := r.getCommonNodeFeatures(state.Nodes) - isReady := true - - switch state.VMCPU.Current().Spec.Type { - case virtv2.Host: - case virtv2.Model: - state.VMCPU.Changed().Status.Nodes = &modelNodes - isReady = len(modelNodes) > 0 - case virtv2.Features: - state.VMCPU.Changed().Status.Nodes = &modelNodes - state.VMCPU.Changed().Status.Features = &features - isReady = containAll(features.Enabled, state.VMCPU.Current().Spec.Features...) - default: - return fmt.Errorf("%s: %w", state.VMCPU.Current().Spec.Type, common.ErrUnknownType) - } - - if isReady { - state.VMCPU.Changed().Status.Phase = virtv2.VMCPUPhaseReady - - return nil - } - - state.VMCPU.Changed().Status.Phase = virtv2.VMCPUPhasePending - - return nil -} - -func (r *VMCPUReconciler) FilterAttachedVM(vm *virtv2.VirtualMachine) bool { - return vm.Spec.CPU.VirtualMachineCPUModel != "" -} - -func (r *VMCPUReconciler) EnqueueFromAttachedVM(vm *virtv2.VirtualMachine) []reconcile.Request { - return []reconcile.Request{{ - NamespacedName: types.NamespacedName{ - Name: vm.Spec.CPU.VirtualMachineCPUModel, - }, - }} -} - -func (r *VMCPUReconciler) getModelNodes(model string, nodes []corev1.Node) []string { - var availableNodes []string - - for _, node := range nodes { - for label, enabled := range node.Labels { - if label != v1.CPUModelLabel+model || enabled != "true" { - continue - } - - availableNodes = append(availableNodes, node.Name) - } - } - - return availableNodes -} - -func (r *VMCPUReconciler) getCommonNodeFeatures(nodes []corev1.Node) virtv2.VirtualMachineCPUModelStatusFeatures { - enabledFeatures := make(map[string]int) - disabledFeatures := make(map[string]int) - - for _, node := range nodes { - for label, enabled := range node.Labels { - if !strings.HasPrefix(label, v1.CPUFeatureLabel) { - continue - } - - if enabled == "true" { - enabledFeatures[label] += 1 - } else { - disabledFeatures[label] += 1 - } - } - } - - var features virtv2.VirtualMachineCPUModelStatusFeatures - - for feature, nodeCount := range enabledFeatures { - if nodeCount != len(nodes) { - continue - } - - features.Enabled = append( - features.Enabled, - strings.TrimPrefix(feature, v1.CPUFeatureLabel), - ) - } - - for feature, nodeCount := range disabledFeatures { - if nodeCount != len(nodes) { - continue - } - - features.NotEnabledCommon = append( - features.NotEnabledCommon, - strings.TrimPrefix(feature, v1.CPUFeatureLabel), - ) - } - - return features -} - -func (r *VMCPUReconciler) enqueueRequestsFromMapFunc(c client.Client, logger logr.Logger) handler.MapFunc { - return func(ctx context.Context, node client.Object) []reconcile.Request { - var cpus virtv2.VirtualMachineCPUModelList - err := c.List(ctx, &cpus) - if err != nil { - logger.Error(err, "Failed to list cpus") - return nil - } - - requests := make([]reconcile.Request, len(cpus.Items)) - for i, cpu := range cpus.Items { - requests[i] = reconcile.Request{ - NamespacedName: types.NamespacedName{ - Name: cpu.Name, - }, - } - } - - return requests - } -} - -func containAll(src []string, items ...string) bool { - search := make(map[string]struct{}, len(items)) - for _, s := range src { - search[s] = struct{}{} - } - - for _, item := range items { - _, ok := search[item] - if !ok { - return false - } - } - - return true -} - -func isEqualMaps(a, b map[string]string) bool { - if len(a) != len(b) { - return false - } - - for aKey, aValue := range a { - bValue, ok := b[aKey] - if !ok { - return false - } - - if aValue != bValue { - return false - } - } - - return true -} diff --git a/images/virtualization-artifact/pkg/controller/cpu/cpu_reconciler_state.go b/images/virtualization-artifact/pkg/controller/cpu/cpu_reconciler_state.go deleted file mode 100644 index 63eefd55a..000000000 --- a/images/virtualization-artifact/pkg/controller/cpu/cpu_reconciler_state.go +++ /dev/null @@ -1,131 +0,0 @@ -/* -Copyright 2024 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 cpu - -import ( - "context" - "fmt" - - "github.com/go-logr/logr" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/cache" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - "github.com/deckhouse/virtualization-controller/pkg/controller/vmattachee" - "github.com/deckhouse/virtualization-controller/pkg/sdk/framework/helper" - virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" -) - -type VMCPUReconcilerState struct { - *vmattachee.AttacheeState[*virtv2.VirtualMachineCPUModel, virtv2.VirtualMachineCPUModelStatus] - - Client client.Client - VMCPU *helper.Resource[*virtv2.VirtualMachineCPUModel, virtv2.VirtualMachineCPUModelStatus] - Nodes []v1.Node - - Result *reconcile.Result -} - -func NewVMCPUReconcilerState(name types.NamespacedName, log logr.Logger, client client.Client, cache cache.Cache) *VMCPUReconcilerState { - state := &VMCPUReconcilerState{ - Client: client, - VMCPU: helper.NewResource( - name, log, client, cache, - func() *virtv2.VirtualMachineCPUModel { - return &virtv2.VirtualMachineCPUModel{} - }, - func(obj *virtv2.VirtualMachineCPUModel) virtv2.VirtualMachineCPUModelStatus { - return obj.Status - }, - ), - } - - state.AttacheeState = vmattachee.NewAttacheeState( - state, - virtv2.FinalizerVMCPUProtection, - state.VMCPU, - ) - - return state -} - -func (state *VMCPUReconcilerState) ApplySync(ctx context.Context, _ logr.Logger) error { - if err := state.VMCPU.UpdateMeta(ctx); err != nil { - return fmt.Errorf("unable to update VMCPU %q meta: %w", state.VMCPU.Name(), err) - } - return nil -} - -func (state *VMCPUReconcilerState) ApplyUpdateStatus(ctx context.Context, _ logr.Logger) error { - return state.VMCPU.UpdateStatus(ctx) -} - -func (state *VMCPUReconcilerState) SetReconcilerResult(result *reconcile.Result) { - state.Result = result -} - -func (state *VMCPUReconcilerState) GetReconcilerResult() *reconcile.Result { - return state.Result -} - -func (state *VMCPUReconcilerState) Reload(ctx context.Context, req reconcile.Request, log logr.Logger, client client.Client) error { - err := state.VMCPU.Fetch(ctx) - if err != nil { - return fmt.Errorf("unable to get VMCPU %s: %w", req.NamespacedName, err) - } - - if state.VMCPU.IsEmpty() { - log.Info("Reconcile observe an absent VMCPU: it may be deleted", "vmcpu.name", req.NamespacedName) - return nil - } - - var nodes v1.NodeList - err = client.List(ctx, &nodes) - if err != nil { - return err - } - - state.Nodes = nodes.Items - - return state.AttacheeState.Reload(ctx, req, log, client) -} - -func (state *VMCPUReconcilerState) ShouldReconcile(log logr.Logger) bool { - if state.VMCPU.IsEmpty() { - return false - } - - if state.AttacheeState.ShouldReconcile(log) { - return true - } - - return true -} - -func (state *VMCPUReconcilerState) IsAttachedToVM(vm virtv2.VirtualMachine) bool { - if state.VMCPU.IsEmpty() { - return false - } - - return state.VMCPU.Name().Name == vm.Spec.CPU.VirtualMachineCPUModel -} - -func (state *VMCPUReconcilerState) isDeletion() bool { - return !state.VMCPU.Current().ObjectMeta.DeletionTimestamp.IsZero() -} diff --git a/images/virtualization-artifact/pkg/controller/cpu/cpu_webhook.go b/images/virtualization-artifact/pkg/controller/cpu/cpu_webhook.go deleted file mode 100644 index 11337d649..000000000 --- a/images/virtualization-artifact/pkg/controller/cpu/cpu_webhook.go +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright 2024 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 cpu - -import ( - "context" - "errors" - "fmt" - - "github.com/go-logr/logr" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/utils/strings/slices" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - - "github.com/deckhouse/virtualization/api/core/v1alpha2" -) - -func NewVMCPUValidator(log logr.Logger) *VMCPUValidator { - return &VMCPUValidator{log: log} -} - -type VMCPUValidator struct { - log logr.Logger -} - -func (v *VMCPUValidator) ValidateCreate(_ context.Context, _ runtime.Object) (admission.Warnings, error) { - err := fmt.Errorf("misconfigured webhook rules: create operation not implemented") - v.log.Error(err, "Ensure the correctness of ValidatingWebhookConfiguration") - return nil, nil -} - -func (v *VMCPUValidator) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { - newVMCPU, ok := newObj.(*v1alpha2.VirtualMachineCPUModel) - if !ok { - return nil, fmt.Errorf("expected a new VirtualMachineCPUModel but got a %T", newObj) - } - - oldVMCPU, ok := oldObj.(*v1alpha2.VirtualMachineCPUModel) - if !ok { - return nil, fmt.Errorf("expected an old VirtualMachineCPUModel but got a %T", oldObj) - } - - if newVMCPU.Spec.Type != oldVMCPU.Spec.Type { - return nil, errors.New("virtual machine CPU type cannot be changed once set") - } - - if newVMCPU.Spec.Model != oldVMCPU.Spec.Model { - return nil, errors.New("virtual machine CPU model cannot be changed once set") - } - - if !slices.Equal(newVMCPU.Spec.Features, oldVMCPU.Spec.Features) { - return nil, errors.New("virtual machine CPU features cannot be changed once set") - } - - return nil, nil -} - -func (v *VMCPUValidator) ValidateDelete(_ context.Context, _ runtime.Object) (admission.Warnings, error) { - err := fmt.Errorf("misconfigured webhook rules: delete operation not implemented") - v.log.Error(err, "Ensure the correctness of ValidatingWebhookConfiguration") - return nil, nil -} diff --git a/images/virtualization-artifact/pkg/controller/indexer/indexer.go b/images/virtualization-artifact/pkg/controller/indexer/indexer.go new file mode 100644 index 000000000..9710f1aad --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/indexer/indexer.go @@ -0,0 +1,92 @@ +/* +Copyright 2024 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 indexer + +import ( + "context" + + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/manager" + + virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" +) + +const ( + IndexFieldVMByClass = "spec.virtualMachineClassName" + IndexFieldVMByVD = "spec.blockDeviceRefs.VirtualDisk" + IndexFieldVMByVI = "spec.blockDeviceRefs.VirtualImage" + IndexFieldVMByCVI = "spec.blockDeviceRefs.ClusterVirtualImage" +) + +type indexFunc func(ctx context.Context, mgr manager.Manager) error + +func IndexALL(ctx context.Context, mgr manager.Manager) error { + for _, fn := range []indexFunc{ + IndexVMByClass, + IndexVMByVD, + IndexVMByVI, + IndexVMByCVI, + } { + if err := fn(ctx, mgr); err != nil { + return err + } + } + return nil +} + +func IndexVMByClass(ctx context.Context, mgr manager.Manager) error { + return mgr.GetFieldIndexer().IndexField(ctx, &virtv2.VirtualMachine{}, IndexFieldVMByClass, func(object client.Object) []string { + vm, ok := object.(*virtv2.VirtualMachine) + if !ok || vm == nil { + return nil + } + return []string{vm.Spec.VirtualMachineClassName} + }) +} + +func IndexVMByVD(ctx context.Context, mgr manager.Manager) error { + return mgr.GetFieldIndexer().IndexField(ctx, &virtv2.VirtualMachine{}, IndexFieldVMByVD, func(object client.Object) []string { + return getBlockDeviceNamesByKind(object, virtv2.DiskDevice) + }) +} + +func IndexVMByVI(ctx context.Context, mgr manager.Manager) error { + return mgr.GetFieldIndexer().IndexField(ctx, &virtv2.VirtualMachine{}, IndexFieldVMByVI, func(object client.Object) []string { + return getBlockDeviceNamesByKind(object, virtv2.ImageDevice) + }) +} + +func IndexVMByCVI(ctx context.Context, mgr manager.Manager) error { + return mgr.GetFieldIndexer().IndexField(ctx, &virtv2.VirtualMachine{}, IndexFieldVMByCVI, func(object client.Object) []string { + return getBlockDeviceNamesByKind(object, virtv2.ClusterImageDevice) + }) +} + +func getBlockDeviceNamesByKind(obj client.Object, kind virtv2.BlockDeviceKind) []string { + vm, ok := obj.(*virtv2.VirtualMachine) + if !ok || vm == nil { + return nil + } + var res []string + for _, bdr := range vm.Spec.BlockDeviceRefs { + if bdr.Kind != kind { + continue + } + res = append(res, bdr.Name) + } + return res +} diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go index 1b2586815..6202e04c4 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go @@ -18,6 +18,7 @@ package kvbuilder import ( "fmt" + "maps" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" @@ -94,24 +95,26 @@ func (b *KVVM) SetKVVMIAnnotation(annoKey, annoValue string) { b.Resource.Spec.Template.ObjectMeta.SetAnnotations(anno) } -func (b *KVVM) SetCPUModel(cpuModelSpec virtv2.VirtualMachineCPUModelSpec) error { +func (b *KVVM) SetCPUModel(class *virtv2.VirtualMachineClass) error { var cpu virtv1.CPU - switch cpuModelSpec.Type { - case virtv2.Host: + switch class.Spec.CPU.Type { + case virtv2.CPUTypeHost: + cpu.Model = virtv1.CPUModeHostModel + case virtv2.CPUTypeHostPassthrough: cpu.Model = virtv1.CPUModeHostPassthrough - case virtv2.Model: - cpu.Model = cpuModelSpec.Model - case virtv2.Features: - cpu.Features = make([]virtv1.CPUFeature, len(cpuModelSpec.Features)) - for i, feature := range cpuModelSpec.Features { + case virtv2.CPUTypeModel: + cpu.Model = class.Spec.CPU.Model + case virtv2.CPUTypeFeatures, virtv2.CPUTypeDiscovery: + cpu.Features = make([]virtv1.CPUFeature, len(class.Status.CpuFeatures.Enabled)) + for i, feature := range class.Status.CpuFeatures.Enabled { cpu.Features[i] = virtv1.CPUFeature{ Name: feature, Policy: "require", } } default: - return fmt.Errorf("unexpected cpu type: %s", cpuModelSpec.Type) + return fmt.Errorf("unexpected cpu type: %q", class.Spec.CPU.Type) } b.Resource.Spec.Template.Spec.Domain.CPU = &cpu @@ -141,8 +144,11 @@ func (b *KVVM) SetRunPolicy(runPolicy virtv2.RunPolicy) error { return nil } -func (b *KVVM) SetNodeSelector(nodeSelector map[string]string) { - b.Resource.Spec.Template.Spec.NodeSelector = nodeSelector +func (b *KVVM) SetNodeSelector(vmNodeSelector, classNodeSelector map[string]string) { + var selector map[string]string + maps.Copy(selector, vmNodeSelector) + maps.Copy(selector, classNodeSelector) + b.Resource.Spec.Template.Spec.NodeSelector = selector } func (b *KVVM) SetTolerations(tolerations []corev1.Toleration) { @@ -153,8 +159,27 @@ func (b *KVVM) SetPriorityClassName(priorityClassName string) { b.Resource.Spec.Template.Spec.PriorityClassName = priorityClassName } -func (b *KVVM) SetAffinity(affinity *corev1.Affinity) { - b.Resource.Spec.Template.Spec.Affinity = affinity +func (b *KVVM) SetAffinity(vmAffinity *corev1.Affinity, classMatchExpressions []corev1.NodeSelectorRequirement) { + if vmAffinity == nil && len(classMatchExpressions) == 0 { + b.Resource.Spec.Template.Spec.Affinity = nil + return + } + if vmAffinity == nil { + vmAffinity = &corev1.Affinity{ + NodeAffinity: &corev1.NodeAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{ + NodeSelectorTerms: []corev1.NodeSelectorTerm{}, + }, + }, + } + } + if len(classMatchExpressions) > 0 { + vmAffinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms = append( + vmAffinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms, + corev1.NodeSelectorTerm{MatchExpressions: classMatchExpressions}, + ) + } + b.Resource.Spec.Template.Spec.Affinity = vmAffinity } func (b *KVVM) SetTerminationGracePeriod(period *int64) { @@ -165,7 +190,7 @@ func (b *KVVM) SetTopologySpreadConstraint(topology []corev1.TopologySpreadConst b.Resource.Spec.Template.Spec.TopologySpreadConstraints = topology } -func (b *KVVM) SetResourceRequirements(cores int, coreFraction, memorySize string) error { +func (b *KVVM) SetResourceRequirements(cores int, coreFraction string, memorySize resource.Quantity) error { cpuRequest, err := b.getCPURequest(cores, coreFraction) if err != nil { return err @@ -173,11 +198,11 @@ func (b *KVVM) SetResourceRequirements(cores int, coreFraction, memorySize strin b.Resource.Spec.Template.Spec.Domain.Resources = virtv1.ResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceCPU: *cpuRequest, - corev1.ResourceMemory: resource.MustParse(memorySize), + corev1.ResourceMemory: memorySize, }, Limits: corev1.ResourceList{ corev1.ResourceCPU: *b.getCPULimit(cores), - corev1.ResourceMemory: resource.MustParse(memorySize), + corev1.ResourceMemory: memorySize, }, } return nil diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go index 4c7afaf87..4ab2b97a2 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go @@ -70,7 +70,7 @@ func ApplyVirtualMachineSpec( vmiByName map[string]*virtv2.VirtualImage, cvmiByName map[string]*virtv2.ClusterVirtualImage, dvcrSettings *dvcr.Settings, - cpu virtv2.VirtualMachineCPUModelSpec, + class *virtv2.VirtualMachineClass, ipAddress string, ) error { if err := kvvm.SetRunPolicy(vm.Spec.RunPolicy); err != nil { @@ -82,15 +82,15 @@ func ApplyVirtualMachineSpec( if err := kvvm.SetBootloader(vm.Spec.Bootloader); err != nil { return err } - if err := kvvm.SetCPUModel(cpu); err != nil { + if err := kvvm.SetCPUModel(class); err != nil { return err } kvvm.SetNetworkInterface(NetworkInterfaceName) kvvm.SetTablet("default-0") - kvvm.SetNodeSelector(vm.Spec.NodeSelector) + kvvm.SetNodeSelector(vm.Spec.NodeSelector, class.Spec.NodeSelector.MatchLabels) kvvm.SetTolerations(vm.Spec.Tolerations) - kvvm.SetAffinity(virtv2.NewAffinityFromVMAffinity(vm.Spec.Affinity)) + kvvm.SetAffinity(virtv2.NewAffinityFromVMAffinity(vm.Spec.Affinity), class.Spec.NodeSelector.MatchExpressions) kvvm.SetPriorityClassName(vm.Spec.PriorityClassName) kvvm.SetTerminationGracePeriod(vm.Spec.TerminationGracePeriodSeconds) kvvm.SetTopologySpreadConstraint(vm.Spec.TopologySpreadConstraints) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/cpu.go b/images/virtualization-artifact/pkg/controller/vm/internal/class.go similarity index 51% rename from images/virtualization-artifact/pkg/controller/vm/internal/cpu.go rename to images/virtualization-artifact/pkg/controller/vm/internal/class.go index 961f91a12..296d2fd8c 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/cpu.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/class.go @@ -29,71 +29,72 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" - "github.com/deckhouse/virtualization-controller/pkg/controller/service" "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/state" virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" "github.com/deckhouse/virtualization/api/core/v1alpha2/vmcondition" ) -const nameCpuHandler = "CPUHandler" +const nameClassHandler = "ClassHandler" -func NewCPUHandler(client client.Client, recorder record.EventRecorder, logger *slog.Logger) *CPUHandler { - service.NewProtectionService(client, virtv2.FinalizerVMCPUProtection) - return &CPUHandler{ - client: client, - recorder: recorder, - logger: logger.With("handler", nameCpuHandler), - protection: service.NewProtectionService(client, virtv2.FinalizerVMCPUProtection), +func NewClassHandler(client client.Client, recorder record.EventRecorder, logger *slog.Logger) *ClassHandler { + return &ClassHandler{ + client: client, + recorder: recorder, + logger: logger.With("handler", nameClassHandler), } } -type CPUHandler struct { - client client.Client - recorder record.EventRecorder - logger *slog.Logger - protection *service.ProtectionService +type ClassHandler struct { + client client.Client + recorder record.EventRecorder + logger *slog.Logger } -func (h *CPUHandler) Handle(ctx context.Context, s state.VirtualMachineState) (reconcile.Result, error) { +func (h *ClassHandler) Handle(ctx context.Context, s state.VirtualMachineState) (reconcile.Result, error) { if s.VirtualMachine().IsEmpty() { return reconcile.Result{}, nil } current := s.VirtualMachine().Current() changed := s.VirtualMachine().Changed() - if update := addAllUnknown(changed, string(vmcondition.TypeCPUModelReady)); update { + if update := addAllUnknown(changed, string(vmcondition.TypeClassReady)); update { return reconcile.Result{Requeue: true}, nil } - model, err := s.CPUModel(ctx) - if err != nil { - return reconcile.Result{}, err - } - if isDeletion(current) { - return reconcile.Result{}, h.protection.RemoveProtection(ctx, model) + return reconcile.Result{}, nil } - err = h.protection.AddProtection(ctx, model) + + class, err := s.Class(ctx) if err != nil { return reconcile.Result{}, err } mgr := conditions.NewManager(changed.Status.Conditions) - cb := conditions.NewConditionBuilder2(vmcondition.TypeCPUModelReady). + cb := conditions.NewConditionBuilder2(vmcondition.TypeClassReady). Generation(current.GetGeneration()) - if model != nil && model.Status.Phase == virtv2.VMCPUPhaseReady { - mgr.Update(cb.Reason2(vmcondition.ReasonCPUModelReady).Status(metav1.ConditionTrue).Condition()) + if class != nil && class.Status.Phase == virtv2.ClassPhaseReady { + if (class.Spec.CPU.Type == virtv2.CPUTypeDiscovery || class.Spec.CPU.Type == virtv2.CPUTypeFeatures) && len(class.Status.CpuFeatures.Enabled) == 0 { + mgr.Update(cb. + Message("No enabled processor features found"). + Reason2(vmcondition.ReasonClassNotReady). + Status(metav1.ConditionFalse). + Condition()) + changed.Status.Conditions = mgr.Generate() + return reconcile.Result{RequeueAfter: 2 * time.Second}, nil + } + mgr.Update(cb.Reason2(vmcondition.ReasonClassReady).Status(metav1.ConditionTrue).Condition()) changed.Status.Conditions = mgr.Generate() return reconcile.Result{}, nil } - modelName := current.Spec.CPU.VirtualMachineCPUModel - msg := fmt.Sprintf("VirtualMachineCPUModelName %q is not ready", modelName) - reason := vmcondition.ReasonCPUModelNotReady - if model == nil { - msg = fmt.Sprintf("VirtualMachineCPUModelName %q not found", modelName) - h.recorder.Event(changed, corev1.EventTypeWarning, reason.String(), "CPU model not available: waiting for the CPU model") - h.logger.Error("CPU model not available: waiting for the CPU model") + className := current.Spec.VirtualMachineClassName + msg := fmt.Sprintf("VirtualMachineClassName %q is not ready", className) + reason := vmcondition.ReasonClassNotReady + if class == nil { + msg = fmt.Sprintf("VirtualMachineClassName %q not found", className) + h.recorder.Event(changed, corev1.EventTypeWarning, reason.String(), "VirtualMachineClass not available: waiting for the VirtualMachineClass") + h.logger.Error("VirtualMachineClass not available: waiting for the CPU model") } mgr.Update(cb.Status(metav1.ConditionFalse). Message(msg). @@ -103,6 +104,6 @@ func (h *CPUHandler) Handle(ctx context.Context, s state.VirtualMachineState) (r return reconcile.Result{RequeueAfter: 2 * time.Second}, nil } -func (h *CPUHandler) Name() string { - return nameCpuHandler +func (h *ClassHandler) Name() string { + return nameClassHandler } diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/deletion_handler.go b/images/virtualization-artifact/pkg/controller/vm/internal/deletion_handler.go index 3a6673731..bf0fe4c34 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/deletion_handler.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/deletion_handler.go @@ -37,7 +37,7 @@ const nameDeletionHandler = "DeletionHandler" func NewDeletionHandler(client client.Client, logger *slog.Logger) *DeletionHandler { return &DeletionHandler{ client: client, - logger: logger, + logger: logger.With("handler", nameDeletionHandler), protection: service.NewProtectionService(client, virtv2.FinalizerKVVMProtection), } } diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/state/state.go b/images/virtualization-artifact/pkg/controller/vm/internal/state/state.go index 41b4f4984..806555718 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/state/state.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/state/state.go @@ -48,7 +48,7 @@ type VirtualMachineState interface { VirtualImagesByName(ctx context.Context) (map[string]*virtv2.VirtualImage, error) ClusterVirtualImagesByName(ctx context.Context) (map[string]*virtv2.ClusterVirtualImage, error) IPAddress(ctx context.Context) (*virtv2.VirtualMachineIPAddress, error) - CPUModel(ctx context.Context) (*virtv2.VirtualMachineCPUModel, error) + Class(ctx context.Context) (*virtv2.VirtualMachineClass, error) Shared(fn func(s *Shared)) } @@ -68,7 +68,7 @@ type state struct { viByName map[string]*virtv2.VirtualImage cviByName map[string]*virtv2.ClusterVirtualImage ipAddress *virtv2.VirtualMachineIPAddress - cpuModel *virtv2.VirtualMachineCPUModel + vmClass *virtv2.VirtualMachineClass shared Shared } @@ -330,21 +330,19 @@ func (s *state) IPAddress(ctx context.Context) (*virtv2.VirtualMachineIPAddress, return s.ipAddress, nil } -func (s *state) CPUModel(ctx context.Context) (*virtv2.VirtualMachineCPUModel, error) { +func (s *state) Class(ctx context.Context) (*virtv2.VirtualMachineClass, error) { if s.vm == nil { return nil, nil } - if s.cpuModel != nil { - return s.cpuModel, nil + if s.vmClass != nil { + return s.vmClass, nil } - s.mu.Lock() - defer s.mu.Unlock() - - vmCpuKey := types.NamespacedName{Name: s.vm.Current().Spec.CPU.VirtualMachineCPUModel} - model, err := helper.FetchObject(ctx, vmCpuKey, s.client, &virtv2.VirtualMachineCPUModel{}) + className := s.vm.Current().Spec.VirtualMachineClassName + classKey := types.NamespacedName{Name: className} + class, err := helper.FetchObject(ctx, classKey, s.client, &virtv2.VirtualMachineClass{}) if err != nil { - return nil, fmt.Errorf("failed to fetch cpumodel: %w", err) + return nil, fmt.Errorf("failed to fetch VirtualMachineClass: %w", err) } - s.cpuModel = model - return s.cpuModel, nil + s.vmClass = class + return s.vmClass, nil } diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go b/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go index abcd66686..4a894ae34 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go @@ -35,6 +35,7 @@ import ( kvvmutil "github.com/deckhouse/virtualization-controller/pkg/common/kvvm" vmutil "github.com/deckhouse/virtualization-controller/pkg/common/vm" + "github.com/deckhouse/virtualization-controller/pkg/controller/common" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" "github.com/deckhouse/virtualization-controller/pkg/controller/kvbuilder" "github.com/deckhouse/virtualization-controller/pkg/controller/powerstate" @@ -120,7 +121,7 @@ func (h *SyncKvvmHandler) isWaiting(vm *virtv2.VirtualMachine) bool { case vmcondition.TypeBlockDevicesReady, vmcondition.TypeIPAddressReady, vmcondition.TypeProvisioningReady, - vmcondition.TypeCPUModelReady: + vmcondition.TypeClassReady: if c.Status != metav1.ConditionTrue { return true } @@ -308,7 +309,7 @@ func (h *SyncKvvmHandler) makeKVVMFromVMSpec(ctx context.Context, s state.Virtua return nil, nil } current := s.VirtualMachine().Current() - kvvmName := namespacedName(current) + kvvmName := common.NamespacedName(current) kvvmOpts := kvbuilder.KVVMOptions{ EnableParavirtualization: current.Spec.EnableParavirtualization, @@ -332,7 +333,7 @@ func (h *SyncKvvmHandler) makeKVVMFromVMSpec(ctx context.Context, s state.Virtua if err != nil { return nil, fmt.Errorf("failed to relaod blockdevice state for vm %q: %w", current.GetName(), err) } - model, err := s.CPUModel(ctx) + class, err := s.Class(ctx) if err != nil { return nil, err } @@ -346,7 +347,7 @@ func (h *SyncKvvmHandler) makeKVVMFromVMSpec(ctx context.Context, s state.Virtua } // Create kubevirt VirtualMachine resource from d8 VirtualMachine spec. - err = kvbuilder.ApplyVirtualMachineSpec(kvvmBuilder, current, bdState.VDByName, bdState.VIByName, bdState.CVIByName, h.dvcrSettings, model.Spec, ip.Status.Address) + err = kvbuilder.ApplyVirtualMachineSpec(kvvmBuilder, current, bdState.VDByName, bdState.VIByName, bdState.CVIByName, h.dvcrSettings, class, ip.Status.Address) if err != nil { return nil, err } diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/util.go b/images/virtualization-artifact/pkg/controller/vm/internal/util.go index 08d2e40b3..4ba112cde 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/util.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/util.go @@ -22,7 +22,6 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" virtv1 "kubevirt.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" @@ -63,10 +62,6 @@ func conditionStatus(status string) metav1.ConditionStatus { } } -func namespacedName(obj client.Object) types.NamespacedName { - return types.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()} -} - func vmIsDegraded(kvvm *virtv1.VirtualMachine) bool { return getPhase(kvvm) == virtv2.MachineDegraded } diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_controller.go b/images/virtualization-artifact/pkg/controller/vm/vm_controller.go index 7a93506ba..92747eeda 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_controller.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_controller.go @@ -52,7 +52,7 @@ func NewController( client := mgr.GetClient() handlers := []Handler{ internal.NewDeletionHandler(client, logger), - internal.NewCPUHandler(client, recorder, logger), + internal.NewClassHandler(client, recorder, logger), internal.NewIPAMHandler(ipam.New(), client, recorder, logger), internal.NewBlockDeviceHandler(client, recorder, logger), internal.NewProvisioningHandler(client), @@ -63,7 +63,7 @@ func NewController( internal.NewLifeCycleHandler(client, recorder, logger), internal.NewStatisticHandler(client), } - r := NewReconciler(mgr.GetClient(), logger, handlers...) + r := NewReconciler(client, logger, handlers...) c, err := controller.New(controllerName, mgr, controller.Options{Reconciler: r}) if err != nil { diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index 563de50b4..c69fdb739 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -35,6 +35,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" + "github.com/deckhouse/virtualization-controller/pkg/controller/indexer" "github.com/deckhouse/virtualization-controller/pkg/controller/service" "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/state" "github.com/deckhouse/virtualization-controller/pkg/log" @@ -253,16 +254,25 @@ func (r *Reconciler) enqueueRequestsBlockDevice(cl client.Client, kind virtv2.Bl if _, ok := obj.(*virtv2.VirtualImage); !ok { return nil } - opts = append(opts, client.InNamespace(obj.GetNamespace())) + opts = append(opts, + client.InNamespace(obj.GetNamespace()), + client.MatchingFields{indexer.IndexFieldVMByVI: obj.GetName()}, + ) case virtv2.ClusterImageDevice: if _, ok := obj.(*virtv2.ClusterVirtualImage); !ok { return nil } + opts = append(opts, + client.MatchingFields{indexer.IndexFieldVMByCVI: obj.GetName()}, + ) case virtv2.DiskDevice: if _, ok := obj.(*virtv2.VirtualDisk); !ok { return nil } - opts = append(opts, client.InNamespace(obj.GetNamespace())) + opts = append(opts, + client.InNamespace(obj.GetNamespace()), + client.MatchingFields{indexer.IndexFieldVMByVD: obj.GetName()}, + ) default: return nil } @@ -272,17 +282,12 @@ func (r *Reconciler) enqueueRequestsBlockDevice(cl client.Client, kind virtv2.Bl } var result []reconcile.Request for _, vm := range vms.Items { - for _, bd := range vm.Spec.BlockDeviceRefs { - if bd.Kind == kind && bd.Name == obj.GetName() { - result = append(result, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Name: vm.GetName(), - Namespace: vm.GetNamespace(), - }, - }) - break - } - } + result = append(result, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: vm.GetName(), + Namespace: vm.GetNamespace(), + }, + }) } return result } diff --git a/images/virtualization-artifact/pkg/controller/vmchange/comparator_operations.go b/images/virtualization-artifact/pkg/controller/vmchange/comparator_operations.go index 228ed3dbd..59c3f2819 100644 --- a/images/virtualization-artifact/pkg/controller/vmchange/comparator_operations.go +++ b/images/virtualization-artifact/pkg/controller/vmchange/comparator_operations.go @@ -16,6 +16,8 @@ limitations under the License. package vmchange +import "k8s.io/apimachinery/pkg/api/resource" + func compareStrings(path, current, desired, defaultValue string, onChange ActionType) []FieldChange { currentValue := NewStringValue(current, defaultValue) desiredValue := NewStringValue(desired, defaultValue) @@ -24,6 +26,14 @@ func compareStrings(path, current, desired, defaultValue string, onChange Action return compareValues(path, currentValue, desiredValue, isEqual, onChange) } +func compareQuantity(path string, current, desired, defaultValue resource.Quantity, onChange ActionType) []FieldChange { + currentValue := NewQuantityValue(current, defaultValue) + desiredValue := NewQuantityValue(desired, defaultValue) + isEqual := current.Equal(desired) + + return compareValues(path, currentValue, desiredValue, isEqual, onChange) +} + func compareInts(path string, current, desired, defaultValue int, onChange ActionType) []FieldChange { currentValue := NewIntValue(current, defaultValue) desiredValue := NewIntValue(desired, defaultValue) @@ -150,6 +160,12 @@ func NewStringValue(value, defaultValue string) Value { return NewValue(value, isEmpty, isDefault) } +func NewQuantityValue(value, defaultValue resource.Quantity) Value { + isEmpty := value.IsZero() + isDefault := !isEmpty && value.Cmp(defaultValue) == 0 + return NewValue(value, isEmpty, isDefault) +} + func NewIntValue(value, defaultValue int) Value { isEmpty := value == 0 isDefault := !isEmpty && value == defaultValue diff --git a/images/virtualization-artifact/pkg/controller/vmchange/comparators.go b/images/virtualization-artifact/pkg/controller/vmchange/comparators.go index 683452716..c3585e5d6 100644 --- a/images/virtualization-artifact/pkg/controller/vmchange/comparators.go +++ b/images/virtualization-artifact/pkg/controller/vmchange/comparators.go @@ -17,6 +17,8 @@ limitations under the License. package vmchange import ( + "k8s.io/apimachinery/pkg/api/resource" + "github.com/deckhouse/virtualization/api/core/v1alpha2" ) @@ -30,6 +32,15 @@ const ( DefaultTerminationGracePeriodSeconds = int64(60) ) +func compareVirtualMachineClass(current, desired *v1alpha2.VirtualMachineSpec) []FieldChange { + return compareStrings( + "virtualMachineClassName", + current.VirtualMachineClassName, + desired.VirtualMachineClassName, + "", + ActionRestart) +} + // compareRunPolicy func compareRunPolicy(current, desired *v1alpha2.VirtualMachineSpec) []FieldChange { return compareStrings( @@ -139,17 +150,12 @@ func compareCPU(current, desired *v1alpha2.VirtualMachineSpec) []FieldChange { return fractionChanges } - modelChanges := compareStrings("cpu.virtualMachineCPUModelName", current.CPU.VirtualMachineCPUModel, desired.CPU.VirtualMachineCPUModel, DefaultCPUModelName, ActionRestart) - if HasChanges(modelChanges) { - return modelChanges - } - return nil } // compareMemory returns changes in the memory section. func compareMemory(current, desired *v1alpha2.VirtualMachineSpec) []FieldChange { - return compareStrings("memory.size", current.Memory.Size, desired.Memory.Size, "", ActionRestart) + return compareQuantity("memory.size", current.Memory.Size, desired.Memory.Size, resource.Quantity{}, ActionRestart) } func compareProvisioning(current, desired *v1alpha2.VirtualMachineSpec) []FieldChange { diff --git a/images/virtualization-artifact/pkg/controller/vmchange/compare.go b/images/virtualization-artifact/pkg/controller/vmchange/compare.go index aef375a7c..194ca7f6e 100644 --- a/images/virtualization-artifact/pkg/controller/vmchange/compare.go +++ b/images/virtualization-artifact/pkg/controller/vmchange/compare.go @@ -23,6 +23,7 @@ import ( type SpecFieldsComparator func(prev, next *v1alpha2.VirtualMachineSpec) []FieldChange var specComparators = []SpecFieldsComparator{ + compareVirtualMachineClass, compareRunPolicy, compareVirtualMachineIPAddress, compareTopologySpreadConstraints, diff --git a/images/virtualization-artifact/pkg/controller/vmclass/internal/deletion.go b/images/virtualization-artifact/pkg/controller/vmclass/internal/deletion.go new file mode 100644 index 000000000..9acb1d957 --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/vmclass/internal/deletion.go @@ -0,0 +1,78 @@ +/* +Copyright 2024 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 internal + +import ( + "context" + "fmt" + "log/slog" + "time" + + corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/deckhouse/virtualization-controller/pkg/controller/common" + "github.com/deckhouse/virtualization-controller/pkg/controller/vmclass/internal/state" + virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" +) + +const nameDeletionHandler = "DeletionHandler" + +func NewDeletionHandler(client client.Client, recorder record.EventRecorder, logger *slog.Logger) *DeletionHandler { + return &DeletionHandler{ + client: client, + recorder: recorder, + logger: logger.With("handler", nameDeletionHandler), + } +} + +type DeletionHandler struct { + client client.Client + recorder record.EventRecorder + logger *slog.Logger +} + +func (h *DeletionHandler) Handle(ctx context.Context, s state.VirtualMachineClassState) (reconcile.Result, error) { + if s.VirtualMachineClass().IsEmpty() { + return reconcile.Result{}, nil + } + changed := s.VirtualMachineClass().Changed() + if s.VirtualMachineClass().Current().GetDeletionTimestamp().IsZero() { + controllerutil.AddFinalizer(changed, virtv2.FinalizerVMCleanup) + return reconcile.Result{}, nil + } + vms, err := s.VirtualMachines(ctx) + if err != nil { + return reconcile.Result{}, err + } + if len(vms) > 0 { + msg := fmt.Sprintf("VirtualMachineClass cannot be deleted, there are VMs that use it. %s...", common.NamespacedName(&vms[0])) + h.logger.Info(msg) + h.recorder.Event(changed, corev1.EventTypeWarning, virtv2.ReasonVMClassInUse, msg) + return reconcile.Result{RequeueAfter: 60 * time.Second}, nil + } + h.logger.Info("Delete VmClass, remove protection finalizers") + controllerutil.RemoveFinalizer(changed, virtv2.FinalizerVMCleanup) + return reconcile.Result{}, nil +} + +func (h *DeletionHandler) Name() string { + return nameDeletionHandler +} diff --git a/images/virtualization-artifact/pkg/controller/vmclass/internal/discovery.go b/images/virtualization-artifact/pkg/controller/vmclass/internal/discovery.go new file mode 100644 index 000000000..76a723611 --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/vmclass/internal/discovery.go @@ -0,0 +1,157 @@ +/* +Copyright 2024 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 internal + +import ( + "context" + "fmt" + "slices" + "sort" + "strings" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + virtv1 "kubevirt.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" + "github.com/deckhouse/virtualization-controller/pkg/controller/vmclass/internal/state" + virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" + "github.com/deckhouse/virtualization/api/core/v1alpha2/vmclasscondition" +) + +const nameDiscoveryHandler = "DiscoveryHandler" + +func NewDiscoveryHandler() *DiscoveryHandler { + return &DiscoveryHandler{} +} + +type DiscoveryHandler struct{} + +func (h *DiscoveryHandler) Handle(ctx context.Context, s state.VirtualMachineClassState) (reconcile.Result, error) { + if s.VirtualMachineClass().IsEmpty() { + return reconcile.Result{}, nil + } + current := s.VirtualMachineClass().Current() + changed := s.VirtualMachineClass().Changed() + + if updated := addAllUnknown(changed, vmclasscondition.TypeDiscovered.String()); updated { + return reconcile.Result{Requeue: true}, nil + } + + if isDeletion(current) { + return reconcile.Result{}, nil + } + + cpuType := current.Spec.CPU.Type + mgr := conditions.NewManager(changed.Status.Conditions) + if cpuType == virtv2.CPUTypeHostPassthrough || cpuType == virtv2.CPUTypeHost { + mgr.Update(conditions.NewConditionBuilder2(vmclasscondition.TypeDiscovered). + Generation(current.GetGeneration()). + Message(fmt.Sprintf("Discovery not needed for cpu.type %q", cpuType)). + Reason2(vmclasscondition.ReasonDiscoverySkip). + Status(metav1.ConditionFalse).Condition()) + changed.Status.Conditions = mgr.Generate() + return reconcile.Result{}, nil + } + + nodes, err := s.Nodes(ctx) + if err != nil { + return reconcile.Result{}, err + } + availableNodes := make([]string, len(nodes)) + for i, n := range nodes { + availableNodes[i] = n.GetName() + } + + var featuresEnabled []string + switch cpuType { + case virtv2.CPUTypeDiscovery: + featuresEnabled = h.discoveryCommonFeatures(nodes) + case virtv2.CPUTypeFeatures: + featuresEnabled = current.Spec.CPU.Features + } + + var featuresNotEnabled []string + if cpuType == virtv2.CPUTypeDiscovery || cpuType == virtv2.CPUTypeFeatures { + selectedNodes, err := s.NodesByVMNodeSelector(ctx) + if err != nil { + return reconcile.Result{}, err + } + commonFeatures := h.discoveryCommonFeatures(selectedNodes) + for _, cf := range commonFeatures { + if !slices.Contains(featuresEnabled, cf) { + featuresNotEnabled = append(featuresNotEnabled, cf) + } + } + } + cb := conditions.NewConditionBuilder2(vmclasscondition.TypeDiscovered).Generation(current.GetGeneration()) + switch cpuType { + case virtv2.CPUTypeDiscovery: + if len(featuresEnabled) > 0 { + cb.Message("").Reason2(vmclasscondition.ReasonDiscoverySucceeded).Status(metav1.ConditionTrue) + break + } + cb.Message("No common features are discovered on nodes."). + Reason2(vmclasscondition.ReasonDiscoveryFailed). + Status(metav1.ConditionFalse) + default: + cb.Message(fmt.Sprintf("Discovery not needed for cpu.type %q", cpuType)). + Reason2(vmclasscondition.ReasonDiscoverySkip). + Status(metav1.ConditionFalse) + } + + mgr.Update(cb.Condition()) + + sort.Strings(availableNodes) + sort.Strings(featuresEnabled) + sort.Strings(featuresNotEnabled) + + changed.Status.Conditions = mgr.Generate() + changed.Status.AvailableNodes = availableNodes + changed.Status.CpuFeatures = virtv2.CpuFeatures{ + Enabled: featuresEnabled, + NotEnabledCommon: featuresNotEnabled, + } + + return reconcile.Result{}, nil +} + +func (h *DiscoveryHandler) Name() string { + return nameDiscoveryHandler +} + +func (h *DiscoveryHandler) discoveryCommonFeatures(nodes []corev1.Node) []string { + if len(nodes) == 0 { + return nil + } + featuresCount := make(map[string]int) + for _, n := range nodes { + for k, v := range n.GetLabels() { + if strings.HasPrefix(k, virtv1.CPUFeatureLabel) && v == "true" { + featuresCount[strings.TrimPrefix(k, virtv1.CPUFeatureLabel)]++ + } + } + } + var features []string + for k, v := range featuresCount { + if v == len(nodes) { + features = append(features, k) + } + } + return features +} diff --git a/images/virtualization-artifact/pkg/controller/vmclass/internal/lifecycle.go b/images/virtualization-artifact/pkg/controller/vmclass/internal/lifecycle.go new file mode 100644 index 000000000..4c3da131c --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/vmclass/internal/lifecycle.go @@ -0,0 +1,115 @@ +/* +Copyright 2024 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 internal + +import ( + "context" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" + "github.com/deckhouse/virtualization-controller/pkg/controller/vmclass/internal/state" + virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" + "github.com/deckhouse/virtualization/api/core/v1alpha2/vmclasscondition" +) + +const nameLifeCycleHandler = "LifeCycleHandler" + +func NewLifeCycleHandler(client client.Client) *LifeCycleHandler { + return &LifeCycleHandler{ + client: client, + } +} + +type LifeCycleHandler struct { + client client.Client +} + +func (h *LifeCycleHandler) Handle(_ context.Context, s state.VirtualMachineClassState) (reconcile.Result, error) { + if s.VirtualMachineClass().IsEmpty() { + return reconcile.Result{}, nil + } + current := s.VirtualMachineClass().Current() + changed := s.VirtualMachineClass().Changed() + if isDeletion(current) { + changed.Status.Phase = virtv2.ClassPhaseTerminating + return reconcile.Result{}, nil + } + + if updated := addAllUnknown(changed, vmclasscondition.TypeReady.String()); updated { + changed.Status.Phase = virtv2.ClassPhasePending + return reconcile.Result{Requeue: true}, nil + } + + mgr := conditions.NewManager(changed.Status.Conditions) + cb := conditions.NewConditionBuilder2(vmclasscondition.TypeReady). + Generation(current.GetGeneration()) + var phase virtv2.VirtualMachineClassPhase + + switch current.Spec.CPU.Type { + case virtv2.CPUTypeHostPassthrough, virtv2.CPUTypeHost: + cb.Message(""). + Reason2(vmclasscondition.ReasonSuitableNodesFound). + Status(metav1.ConditionTrue) + phase = virtv2.ClassPhaseReady + case virtv2.CPUTypeDiscovery: + var notReady bool + if len(changed.Status.AvailableNodes) == 0 { + cb.Message("No matching nodes found.") + cb.Reason2(vmclasscondition.ReasonNoSuitableNodesFound) + notReady = true + } + if len(changed.Status.CpuFeatures.Enabled) == 0 { + cb.Message("No cpu feature enabled.") + cb.Reason2(vmclasscondition.ReasonNoCpuFeaturesEnabled) + notReady = true + } + if notReady { + phase = virtv2.ClassPhasePending + cb.Status(metav1.ConditionFalse) + break + } + phase = virtv2.ClassPhaseReady + cb.Message(""). + Reason2(vmclasscondition.ReasonSuitableNodesFound). + Status(metav1.ConditionTrue) + default: + if len(changed.Status.AvailableNodes) == 0 { + phase = virtv2.ClassPhasePending + cb.Message("No matching nodes found."). + Reason2(vmclasscondition.ReasonNoSuitableNodesFound). + Status(metav1.ConditionFalse) + break + } + phase = virtv2.ClassPhaseReady + cb.Message(""). + Reason2(vmclasscondition.ReasonSuitableNodesFound). + Status(metav1.ConditionTrue) + } + + mgr.Update(cb.Condition()) + changed.Status.Conditions = mgr.Generate() + changed.Status.Phase = phase + + return reconcile.Result{}, nil +} + +func (h *LifeCycleHandler) Name() string { + return nameLifeCycleHandler +} diff --git a/images/virtualization-artifact/pkg/controller/vmclass/internal/state/state.go b/images/virtualization-artifact/pkg/controller/vmclass/internal/state/state.go new file mode 100644 index 000000000..41bd15e0f --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/vmclass/internal/state/state.go @@ -0,0 +1,158 @@ +/* +Copyright 2024 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 state + +import ( + "context" + "fmt" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/component-helpers/scheduling/corev1/nodeaffinity" + virtv1 "kubevirt.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/deckhouse/virtualization-controller/pkg/controller/common" + "github.com/deckhouse/virtualization-controller/pkg/controller/indexer" + "github.com/deckhouse/virtualization-controller/pkg/controller/service" + virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" +) + +type VirtualMachineClassState interface { + VirtualMachineClass() *service.Resource[*virtv2.VirtualMachineClass, virtv2.VirtualMachineClassStatus] + VirtualMachines(ctx context.Context) ([]virtv2.VirtualMachine, error) + Nodes(ctx context.Context) ([]corev1.Node, error) + NodesByVMNodeSelector(ctx context.Context) ([]corev1.Node, error) +} + +type state struct { + client client.Client + vmClass *service.Resource[*virtv2.VirtualMachineClass, virtv2.VirtualMachineClassStatus] +} + +func New(c client.Client, vmClass *service.Resource[*virtv2.VirtualMachineClass, virtv2.VirtualMachineClassStatus]) VirtualMachineClassState { + return &state{client: c, vmClass: vmClass} +} + +func (s *state) VirtualMachineClass() *service.Resource[*virtv2.VirtualMachineClass, virtv2.VirtualMachineClassStatus] { + return s.vmClass +} + +func (s *state) VirtualMachines(ctx context.Context) ([]virtv2.VirtualMachine, error) { + if s.vmClass == nil || s.vmClass.IsEmpty() { + return nil, nil + } + name := s.vmClass.Current().GetName() + vms := &virtv2.VirtualMachineList{} + err := s.client.List(ctx, vms, client.MatchingFields{ + indexer.IndexFieldVMByClass: name, + }) + if err != nil { + return nil, fmt.Errorf("failed to list virtual machines by vmclass %s: %w", name, err) + } + return vms.Items, nil +} + +type filterFunc func([]corev1.Node) []corev1.Node + +func (s *state) Nodes(ctx context.Context) ([]corev1.Node, error) { + if s.vmClass == nil || s.vmClass.IsEmpty() { + return nil, nil + } + curr := s.vmClass.Current() + + var matchLabels map[string]string + var filter filterFunc + + switch curr.Spec.CPU.Type { + case virtv2.CPUTypeHost, virtv2.CPUTypeHostPassthrough: + return nil, nil + case virtv2.CPUTypeDiscovery: + matchLabels = curr.Spec.CPU.Discovery.MatchLabels + filter = func(nodes []corev1.Node) []corev1.Node { + var filtered []corev1.Node + for _, node := range nodes { + if common.MatchExpressions(node.GetLabels(), curr.Spec.CPU.Discovery.MatchExpressions) { + filtered = append(filtered, node) + } + } + return filtered + } + case virtv2.CPUTypeModel: + matchLabels = map[string]string{virtv1.CPUModelLabel + curr.Spec.CPU.Model: "true"} + case virtv2.CPUTypeFeatures: + ml := make(map[string]string, len(curr.Spec.CPU.Features)) + for _, feature := range curr.Spec.CPU.Features { + ml[virtv1.CPUFeatureLabel+feature] = "true" + } + matchLabels = ml + default: + return nil, fmt.Errorf("unexpected cpu type %s", curr.Spec.CPU.Type) + } + nodes := &corev1.NodeList{} + err := s.client.List( + ctx, + nodes, + client.MatchingLabelsSelector{Selector: labels.SelectorFromSet(matchLabels)}) + if err != nil { + return nil, fmt.Errorf("failed to list nodes: %w", err) + } + result := nodes.Items + if filter != nil { + result = filter(result) + } + return result, nil +} + +func (s *state) NodesByVMNodeSelector(ctx context.Context) ([]corev1.Node, error) { + if s.vmClass == nil || s.vmClass.IsEmpty() { + return nil, nil + } + nodeSelector := s.vmClass.Current().Spec.NodeSelector + nodes := &corev1.NodeList{} + err := s.client.List( + ctx, + nodes, + client.MatchingLabelsSelector{Selector: labels.SelectorFromSet(nodeSelector.MatchLabels)}) + if err != nil { + return nil, fmt.Errorf("failed to list nodes: %w", err) + } + result := nodes.Items + var filter filterFunc + + if len(nodeSelector.MatchExpressions) > 0 { + ns, err := nodeaffinity.NewNodeSelector(&corev1.NodeSelector{ + NodeSelectorTerms: []corev1.NodeSelectorTerm{{MatchExpressions: nodeSelector.MatchExpressions}}, + }) + if err != nil { + return nil, fmt.Errorf("failed to create NodeSelector: %w", err) + } + filter = func(nodes []corev1.Node) []corev1.Node { + var filtered []corev1.Node + for _, node := range nodes { + if ns.Match(&node) { + filtered = append(filtered, node) + } + } + return filtered + } + } + if filter != nil { + result = filter(result) + } + return result, nil +} diff --git a/images/virtualization-artifact/pkg/controller/vmclass/internal/util.go b/images/virtualization-artifact/pkg/controller/vmclass/internal/util.go new file mode 100644 index 000000000..950273299 --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/vmclass/internal/util.go @@ -0,0 +1,44 @@ +/* +Copyright 2024 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 internal + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" + virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" + "github.com/deckhouse/virtualization/api/core/v1alpha2/vmclasscondition" +) + +func isDeletion(class *virtv2.VirtualMachineClass) bool { + return class == nil || !class.GetDeletionTimestamp().IsZero() +} + +func addAllUnknown(class *virtv2.VirtualMachineClass, conds ...string) (update bool) { + mgr := conditions.NewManager(class.Status.Conditions) + for _, c := range conds { + if add := mgr.Add(conditions.NewConditionBuilder(c). + Generation(class.GetGeneration()). + Reason2(vmclasscondition.ReasonUnknown). + Status(metav1.ConditionUnknown). + Condition()); add { + update = true + } + } + class.Status.Conditions = mgr.Generate() + return +} diff --git a/images/virtualization-artifact/pkg/controller/vmclass/vmclass_controller.go b/images/virtualization-artifact/pkg/controller/vmclass/vmclass_controller.go new file mode 100644 index 000000000..ba5025e45 --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/vmclass/vmclass_controller.go @@ -0,0 +1,63 @@ +/* +Copyright 2024 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 vmclass + +import ( + "context" + "log/slog" + + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/manager" + + "github.com/deckhouse/virtualization-controller/pkg/controller/vmclass/internal" +) + +const ( + controllerName = "vmclass-controller" +) + +func NewController( + ctx context.Context, + mgr manager.Manager, + log *slog.Logger, +) (controller.Controller, error) { + if log == nil { + log = slog.Default() + } + logger := log.With("controller", controllerName) + + recorder := mgr.GetEventRecorderFor(controllerName) + client := mgr.GetClient() + handlers := []Handler{ + internal.NewDeletionHandler(client, recorder, logger), + internal.NewDiscoveryHandler(), + internal.NewLifeCycleHandler(client), + } + r := NewReconciler(client, logger, handlers...) + + c, err := controller.New(controllerName, mgr, controller.Options{Reconciler: r}) + if err != nil { + return nil, err + } + + if err = r.SetupController(ctx, mgr, c); err != nil { + return nil, err + } + + log.Info("Initialized VirtualMachineClass controller") + return c, nil +} diff --git a/images/virtualization-artifact/pkg/controller/vmclass/vmclass_reconciler.go b/images/virtualization-artifact/pkg/controller/vmclass/vmclass_reconciler.go new file mode 100644 index 000000000..8e3c6a65d --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/vmclass/vmclass_reconciler.go @@ -0,0 +1,143 @@ +/* +Copyright 2024 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 vmclass + +import ( + "context" + "errors" + "fmt" + "log/slog" + + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" + + "github.com/deckhouse/virtualization-controller/pkg/controller/common" + "github.com/deckhouse/virtualization-controller/pkg/controller/service" + "github.com/deckhouse/virtualization-controller/pkg/controller/vmclass/internal/state" + "github.com/deckhouse/virtualization-controller/pkg/log" + virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" +) + +type Handler interface { + Handle(ctx context.Context, s state.VirtualMachineClassState) (reconcile.Result, error) + Name() string +} + +func NewReconciler(client client.Client, logger *slog.Logger, handlers ...Handler) *Reconciler { + return &Reconciler{ + client: client, + logger: logger, + handlers: handlers, + } +} + +type Reconciler struct { + client client.Client + logger *slog.Logger + handlers []Handler +} + +func (r *Reconciler) SetupController(_ context.Context, mgr manager.Manager, ctr controller.Controller) error { + if err := ctr.Watch(source.Kind(mgr.GetCache(), + &virtv2.VirtualMachineClass{}), + &handler.EnqueueRequestForObject{}); err != nil { + return fmt.Errorf("error setting watch on VMClass: %w", err) + } + if err := ctr.Watch( + source.Kind(mgr.GetCache(), &corev1.Node{}), + handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, object client.Object) []reconcile.Request { + node, ok := object.(*corev1.Node) + if !ok { + return nil + } + var result []reconcile.Request + + classList := &virtv2.VirtualMachineClassList{} + err := mgr.GetClient().List(ctx, classList) + if err != nil { + return nil + } + for _, class := range classList.Items { + if common.MatchLabelSelector(node.GetLabels(), class.Spec.CPU.Discovery) { + result = append(result, reconcile.Request{NamespacedName: common.NamespacedName(&class)}) + } + } + return result + }), + predicate.LabelChangedPredicate{}); err != nil { + return fmt.Errorf("error setting watch on Node: %w", err) + } + return nil +} + +func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { + class := service.NewResource(req.NamespacedName, r.client, r.factory, r.statusGetter) + + err := class.Fetch(ctx) + if err != nil { + return reconcile.Result{}, err + } + + if class.IsEmpty() { + r.logger.Info("Reconcile observe an absent VirtualMachineClass: it may be deleted", slog.String("namespacedName", req.String())) + return reconcile.Result{}, nil + } + s := state.New(r.client, class) + + r.logger.Info("Start reconcile VMClass", slog.String("namespacedName", req.String())) + + var result reconcile.Result + var handlerErr error + + for _, h := range r.handlers { + r.logger.Debug("Run handler", slog.String("name", h.Name())) + res, err := h.Handle(ctx, s) + if err != nil { + r.logger.Error("The handler failed with an error", slog.String("name", h.Name()), log.SlogErr(err)) + handlerErr = errors.Join(handlerErr, err) + } + result = service.MergeResults(result, res) + } + if handlerErr != nil { + err = class.Update(ctx) + if err != nil { + r.logger.Error("Failed to update VirtualMachineClass", slog.String("namespacedName", req.String())) + } + return reconcile.Result{}, handlerErr + } + err = class.Update(ctx) + if err != nil { + r.logger.Error("Failed to update VirtualMachineClass", slog.String("namespacedName", req.String())) + return reconcile.Result{}, err + } + r.logger.Info("Finished reconcile VM", slog.String("namespacedName", req.String())) + return result, nil +} + +func (r *Reconciler) factory() *virtv2.VirtualMachineClass { + return &virtv2.VirtualMachineClass{} +} + +func (r *Reconciler) statusGetter(obj *virtv2.VirtualMachineClass) virtv2.VirtualMachineClassStatus { + return obj.Status +} diff --git a/templates/virtualization-controller/rbac-for-us.yaml b/templates/virtualization-controller/rbac-for-us.yaml index b2c50377d..b03b2546a 100644 --- a/templates/virtualization-controller/rbac-for-us.yaml +++ b/templates/virtualization-controller/rbac-for-us.yaml @@ -152,13 +152,13 @@ rules: - virtualdisks - virtualmachinedisksnapshots - virtualimages - - virtualmachinecpumodels - virtualmachineipaddressleases - virtualmachineipaddresses - virtualmachineblockdeviceattachments - virtualmachines - clustervirtualimages - virtualmachineoperations + - virtualmachineclasses verbs: - create - delete @@ -176,12 +176,11 @@ rules: - virtualmachineblockdeviceattachments/finalizers - virtualmachines/finalizers - clustervirtualimages/finalizers - - virtualmachinecpumodels/finalizers - virtualmachineipaddressleases/finalizers - virtualmachineipaddresses/finalizers - virtualmachineoperations/finalizers + - virtualmachineclasses/finalizers - virtualmachineipaddresses/status - - virtualmachinecpumodels/status - virtualmachineipaddressleases/status - virtualdisks/status - virtualmachinedisksnapshots/status @@ -190,6 +189,7 @@ rules: - virtualmachines/status - clustervirtualimages/status - virtualmachineoperations/status + - virtualmachineclasses/status verbs: - patch - update diff --git a/templates/virtualization-controller/vmclasses-default.yaml b/templates/virtualization-controller/vmclasses-default.yaml new file mode 100644 index 000000000..ab9d0056f --- /dev/null +++ b/templates/virtualization-controller/vmclasses-default.yaml @@ -0,0 +1,101 @@ +--- +apiVersion: virtualization.deckhouse.io/v1alpha2 +kind: VirtualMachineClass +metadata: + name: host +spec: + nodeSelector: + matchExpressions: + - key: node-role.kubernetes.io/control-plane + operator: DoesNotExist + cpu: + type: Host + sizingPolicies: + - cores: + min: 1 + max: 4 + dedicatedCores: [false] + coreFractions: [5, 10, 20, 50, 100] + - cores: + min: 5 + max: 8 + dedicatedCores: [false] + coreFractions: [20, 50, 100] + - cores: + min: 9 + max: 16 + dedicatedCores: [true, false] + coreFractions: [50, 100] + - cores: + min: 17 + max: 1024 + dedicatedCores: [true, false] + coreFractions: [100] +--- +apiVersion: virtualization.deckhouse.io/v1alpha2 +kind: VirtualMachineClass # vmc +metadata: + name: host-passthrough +spec: + nodeSelector: + matchExpressions: + - key: node-role.kubernetes.io/control-plane + operator: DoesNotExist + cpu: + type: HostPassthrough + sizingPolicies: + - cores: + min: 1 + max: 4 + dedicatedCores: [false] + coreFractions: [5, 10, 20, 50, 100] + - cores: + min: 5 + max: 8 + dedicatedCores: [false] + coreFractions: [20, 50, 100] + - cores: + min: 9 + max: 16 + dedicatedCores: [true, false] + coreFractions: [50, 100] + - cores: + min: 17 + max: 1024 + dedicatedCores: [true, false] + coreFractions: [100] +--- +apiVersion: virtualization.deckhouse.io/v1alpha2 +kind: VirtualMachineClass # vmc +metadata: + name: generic +spec: + nodeSelector: + matchExpressions: + - key: node-role.kubernetes.io/control-plane + operator: DoesNotExist + cpu: + type: Model + model: Nehalem + sizingPolicies: + - cores: + min: 1 + max: 4 + dedicatedCores: [false] + coreFractions: [5, 10, 20, 50, 100] + - cores: + min: 5 + max: 8 + dedicatedCores: [false] + coreFractions: [20, 50, 100] + - cores: + min: 9 + max: 16 + dedicatedCores: [true, false] + coreFractions: [50, 100] + - cores: + min: 17 + max: 1024 + dedicatedCores: [true, false] + coreFractions: [100] + diff --git a/templates/virtualization-controller/vmcpu-default.yaml b/templates/virtualization-controller/vmcpu-default.yaml deleted file mode 100644 index d4d5adfdc..000000000 --- a/templates/virtualization-controller/vmcpu-default.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: virtualization.deckhouse.io/v1alpha2 -kind: VirtualMachineCPUModel -metadata: - name: generic-v1 -spec: - type: Model - model: "Nehalem" \ No newline at end of file