Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(cvi,vi): unlock pending vi/cvi from vd ref #416

Open
wants to merge 64 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
91e158d
fix(vi): unlock pending vi from vd ref
LopatinDmitr Oct 2, 2024
8dc06f6
fix(vi): unlock pending vi from vd ref
LopatinDmitr Oct 5, 2024
ede1b4a
fix(cvi): unlock pending cvi from vd ref
LopatinDmitr Oct 5, 2024
230cba7
chore(vd): add condition 'Locked' when vd attached to running VM
LopatinDmitr Oct 5, 2024
0daee16
chore(vd): add condition 'Locked' when vd attached to running VM
LopatinDmitr Oct 5, 2024
0d2816f
fix(vi,cvi): add check locked condition when filter VD
LopatinDmitr Oct 5, 2024
053c5b0
fix(vi,cvi): add check locked condition when filter VD
LopatinDmitr Oct 5, 2024
fbb9f0d
test
LopatinDmitr Oct 6, 2024
b27c646
test
LopatinDmitr Oct 6, 2024
1f85a9b
test
LopatinDmitr Oct 6, 2024
e86f471
test
LopatinDmitr Oct 6, 2024
57bcac5
test
LopatinDmitr Oct 6, 2024
a96b376
add logs
LopatinDmitr Oct 6, 2024
2c41680
add logs
LopatinDmitr Oct 6, 2024
3c14cea
fix
LopatinDmitr Oct 6, 2024
2d72a7e
fix2
LopatinDmitr Oct 6, 2024
89ae9cd
add fix for cvi
LopatinDmitr Oct 6, 2024
ff59df2
rename locked -> employed
LopatinDmitr Oct 7, 2024
0ac9da1
add abort provisioning if disk was employed
LopatinDmitr Oct 8, 2024
2ccebc9
add abort provisioning if disk was employed
LopatinDmitr Oct 8, 2024
402c944
add abort provisioning if disk was employed
LopatinDmitr Oct 8, 2024
675b7b7
add abort provisioning if disk was employed
LopatinDmitr Oct 8, 2024
9222bc6
resolve comments
LopatinDmitr Oct 8, 2024
fdf9184
change condition Employed -> InUse
LopatinDmitr Oct 9, 2024
2cec18a
add change inuse cond if creating vi from this vd
LopatinDmitr Oct 10, 2024
27dc027
fix
LopatinDmitr Oct 10, 2024
e32d1f6
add condition restarting
LopatinDmitr Oct 10, 2024
a5df5d3
remove condition restarting
LopatinDmitr Oct 11, 2024
7951b68
fix
LopatinDmitr Oct 12, 2024
86434b6
fix
LopatinDmitr Oct 12, 2024
9e5f6f9
fix
LopatinDmitr Oct 12, 2024
730aa21
fix
LopatinDmitr Oct 12, 2024
6cd2931
fix
LopatinDmitr Oct 12, 2024
052549d
fix
LopatinDmitr Oct 12, 2024
9d3fca0
fix
LopatinDmitr Oct 12, 2024
2023ea4
add logs
LopatinDmitr Oct 12, 2024
8e5b4f1
add logs
LopatinDmitr Oct 12, 2024
ac0fa17
fix
LopatinDmitr Oct 13, 2024
3d6ffc7
add blocking vm if disk in use for create image
LopatinDmitr Oct 13, 2024
fb50232
add blocking vm if disk if vm restarting
LopatinDmitr Oct 13, 2024
8397d1e
fix
LopatinDmitr Oct 13, 2024
0ba4b26
fix
LopatinDmitr Oct 13, 2024
78d08eb
fix unit tests
LopatinDmitr Oct 13, 2024
5fc35de
fix go linter
LopatinDmitr Oct 13, 2024
4905c6a
fix condition.go
LopatinDmitr Oct 20, 2024
21c9fb8
fix condition.go
LopatinDmitr Oct 20, 2024
3f15157
fix condition.go
LopatinDmitr Oct 20, 2024
c96a4d9
fix attachee.go & inuse.go
LopatinDmitr Oct 20, 2024
32b5e88
fix inuse.go
LopatinDmitr Oct 20, 2024
6038ea9
fix inuse.go
LopatinDmitr Oct 20, 2024
caeecbe
fix block_device.go, object_ref_vd.go
LopatinDmitr Oct 20, 2024
efc57c1
fix inuse.go
LopatinDmitr Oct 20, 2024
806bf11
fix object_ref_vd.go
LopatinDmitr Oct 20, 2024
6d4987a
fix inuse.go
LopatinDmitr Oct 20, 2024
fb9070d
fix inuse.go, object_ref_vd.go
LopatinDmitr Oct 20, 2024
fd3ee61
fix inuse.go
LopatinDmitr Oct 20, 2024
88b6e5e
fix inuse.go
LopatinDmitr Oct 21, 2024
896624e
fix inuse.go
LopatinDmitr Oct 21, 2024
f209bec
fix inuse.go
LopatinDmitr Oct 21, 2024
f8134b5
fix inuse.go
LopatinDmitr Oct 21, 2024
9b6a40d
fix block_device.go
LopatinDmitr Oct 21, 2024
701463b
fix block_device.go & block_devices_test.go
LopatinDmitr Oct 21, 2024
0e84908
fix vd_enqueuer.go, vd_reconciler.go
LopatinDmitr Oct 23, 2024
117bcd0
fix
LopatinDmitr Oct 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/core/v1alpha2/cvicondition/condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ const (
ClusterImageNotReady DatasourceReadyReason = "ClusterImageNotReady"
// VirtualDiskNotReady indicates that the `VirtualDisk` datasource is not ready, which prevents the import process from starting.
VirtualDiskNotReady DatasourceReadyReason = "VirtualDiskNotReady"
// VirtualDiskInUseInRunningVirtualMachine indicates that the `VirtualDisk` attached to running `VirtualMachine`
VirtualDiskInUseInRunningVirtualMachine DatasourceReadyReason = "VirtualDiskInUseInRunningVirtualMachine"

// WaitForUserUpload indicates that the `ClusterVirtualImage` is waiting for the user to upload a datasource for the import process to continue.
WaitForUserUpload ReadyReason = "WaitForUserUpload"
Expand Down
11 changes: 11 additions & 0 deletions api/core/v1alpha2/vdcondition/condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const (
ResizedType Type = "Resized"
// SnapshottingType indicates whether the disk snapshotting operation is in progress.
SnapshottingType Type = "Snapshotting"
// InUseType indicates whether the VirtualDisk is attached to a running VirtualMachine or is being used in a process of a VirtualImage creation
InUseType Type = "InUse"
)

type (
Expand All @@ -39,6 +41,8 @@ type (
ResizedReason = string
// SnapshottingReason represents the various reasons for the Snapshotting condition type.
SnapshottingReason = string
// InUseReason represents the various reasons for the InUse condition type.
InUseReason = string
)

const (
Expand Down Expand Up @@ -83,4 +87,11 @@ const (
Snapshotting SnapshottingReason = "Snapshotting"
// SnapshottingNotAvailable indicates that the snapshotting operation is not available for now.
SnapshottingNotAvailable SnapshottingReason = "NotAvailable"

// InUseInRunningVirtualMachine indicates that this `VirtualDisk` attached to running `VirtualMachine`
InUseInRunningVirtualMachine InUseReason = "InUseInRunningVirtualMachine"
// InUseForCreateImage indicates running a process to create a `VirtualImage` or `ClusterVirtualImage` from this `VirtualDisk`
InUseForCreateImage InUseReason = "InUseForCreateImage"
// NotInUse indicates that this `VirtualDisk` is available.
NotInUse InUseReason = "NotInUse"
)
2 changes: 2 additions & 0 deletions api/core/v1alpha2/vicondition/condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ const (
ClusterImageNotReady DatasourceReadyReason = "ClusterImageNotReady"
// VirtualDiskNotReady indicates that the `VirtualDisk` datasource is not ready, which prevents the import process from starting.
VirtualDiskNotReady DatasourceReadyReason = "VirtualDiskNotReady"
// VirtualDiskInUseInRunningVirtualMachine indicates that the `VirtualDisk` attached to running `VirtualMachine`
VirtualDiskInUseInRunningVirtualMachine DatasourceReadyReason = "VirtualDiskInUseInRunningVirtualMachine"

// WaitForUserUpload indicates that the `VirtualImage` is waiting for the user to upload a datasource for the import process to continue.
WaitForUserUpload ReadyReason = "WaitForUserUpload"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"github.com/deckhouse/virtualization-controller/pkg/controller/watchers"
"github.com/deckhouse/virtualization-controller/pkg/logger"
virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2"
"github.com/deckhouse/virtualization/api/core/v1alpha2/vdcondition"
)

type Handler interface {
Expand Down Expand Up @@ -165,7 +166,14 @@ func (r *Reconciler) SetupController(_ context.Context, mgr manager.Manager, ctr
return false
}

return oldVD.Status.Phase != newVD.Status.Phase
oldInUseCondition, _ := service.GetCondition(vdcondition.InUseType, oldVD.Status.Conditions)
newInUseCondition, _ := service.GetCondition(vdcondition.InUseType, newVD.Status.Conditions)

if oldVD.Status.Phase != newVD.Status.Phase || len(oldVD.Status.AttachedToVirtualMachines) != len(newVD.Status.AttachedToVirtualMachines) || oldInUseCondition.Status != newInUseCondition.Status {
return true
}

return false
},
},
); err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ func (h DatasourceReadyHandler) Handle(ctx context.Context, cvi *virtv2.ClusterV
condition.Reason = cvicondition.VirtualDiskNotReady
condition.Message = service.CapitalizeFirstLetter(err.Error())
return reconcile.Result{}, nil
case errors.As(err, &source.VirtualDiskAttachedToRunningVMError{}):
case errors.As(err, &source.VirtualDiskInUseError{}):
condition.Status = metav1.ConditionFalse
condition.Reason = cvicondition.VirtualDiskNotReady
condition.Reason = cvicondition.VirtualDiskInUseInRunningVirtualMachine
condition.Message = service.CapitalizeFirstLetter(err.Error())
return reconcile.Result{}, nil
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,16 @@ func NewVirtualDiskNotReadyError(name string) error {
}
}

type VirtualDiskAttachedToRunningVMError struct {
name string
vmName string
type VirtualDiskInUseError struct {
name string
}

func (e VirtualDiskAttachedToRunningVMError) Error() string {
return fmt.Sprintf("VirtualDisk %q attached to running VirtualMachine %q", e.name, e.vmName)
func (e VirtualDiskInUseError) Error() string {
return fmt.Sprintf("VirtualDisk %q attached to running `VirtualMachine`", e.name)
}

func NewVirtualDiskAttachedToRunningVMError(name, vmName string) error {
return VirtualDiskAttachedToRunningVMError{
name: name,
vmName: vmName,
func NewVirtualDiskInUseError(name string) error {
return VirtualDiskInUseError{
name: name,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/deckhouse/virtualization-controller/pkg/logger"
"github.com/deckhouse/virtualization-controller/pkg/sdk/framework/helper"
virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2"
"github.com/deckhouse/virtualization/api/core/v1alpha2/vdcondition"
"github.com/deckhouse/virtualization/api/core/v1alpha2/vicondition"
)

Expand Down Expand Up @@ -207,16 +208,10 @@ func (ds ObjectRefVirtualDisk) Validate(ctx context.Context, cvi *virtv2.Cluster
return NewVirtualDiskNotReadyError(cvi.Spec.DataSource.ObjectRef.Name)
}

if len(vd.Status.AttachedToVirtualMachines) != 0 {
vmName := vd.Status.AttachedToVirtualMachines[0]

vm, err := helper.FetchObject(ctx, types.NamespacedName{Name: vmName.Name, Namespace: vd.Namespace}, ds.client, &virtv2.VirtualMachine{})
if err != nil {
return err
}

if vm.Status.Phase != virtv2.MachineStopped {
return NewVirtualDiskAttachedToRunningVMError(vd.Name, vmName.Name)
inUseCondition, _ := service.GetCondition(vdcondition.InUseType, vd.Status.Conditions)
if inUseCondition.Status != metav1.ConditionFalse {
if inUseCondition.Reason == vdcondition.InUseInRunningVirtualMachine {
return NewVirtualDiskInUseError(vd.Name)
}
}

Expand Down
153 changes: 153 additions & 0 deletions images/virtualization-artifact/pkg/controller/vd/internal/inuse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
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"

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"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

"github.com/deckhouse/virtualization-controller/pkg/controller/service"
"github.com/deckhouse/virtualization-controller/pkg/sdk/framework/helper"
virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2"
"github.com/deckhouse/virtualization/api/core/v1alpha2/cvicondition"
"github.com/deckhouse/virtualization/api/core/v1alpha2/vdcondition"
"github.com/deckhouse/virtualization/api/core/v1alpha2/vicondition"
"github.com/deckhouse/virtualization/api/core/v1alpha2/vmcondition"
)

type InUseHandler struct {
client client.Client
}

func NewInUseHandler(client client.Client) *InUseHandler {
LopatinDmitr marked this conversation as resolved.
Show resolved Hide resolved
return &InUseHandler{
client: client,
}
}

func (h InUseHandler) Handle(ctx context.Context, vd *virtv2.VirtualDisk) (reconcile.Result, error) {
inUseCondition, ok := service.GetCondition(vdcondition.InUseType, vd.Status.Conditions)
if !ok {
inUseCondition = metav1.Condition{
Type: vdcondition.InUseType,
Status: metav1.ConditionUnknown,
}

service.SetCondition(inUseCondition, &vd.Status.Conditions)
}

var inUseForCreateImage, inUseInRunningVirtualMachine bool

var vms virtv2.VirtualMachineList
err := h.client.List(ctx, &vms, &client.ListOptions{
Namespace: vd.GetNamespace(),
})
if err != nil {
return reconcile.Result{}, fmt.Errorf("error getting virtual machines: %w", err)
}

for _, vm := range vms.Items {
for _, bda := range vm.Status.BlockDeviceRefs {
if bda.Kind == virtv2.DiskDevice && bda.Name == vd.GetName() {
runningCondition, _ := service.GetCondition(vmcondition.TypeRunning.String(), vm.Status.Conditions)
if runningCondition.Status == metav1.ConditionTrue || vm.Status.Phase == virtv2.MachineStarting {
inUseInRunningVirtualMachine = true
} else {
kvvm, err := helper.FetchObject(ctx, types.NamespacedName{Namespace: vm.GetNamespace(), Name: vm.GetName()}, h.client, &virtv1.VirtualMachine{})
if err != nil {
return reconcile.Result{}, fmt.Errorf("error getting kubevirt virtual machine: %w", err)
}
if kvvm != nil {
if kvvm.Status.StateChangeRequests != nil {
inUseInRunningVirtualMachine = true
}
}
}
}
}
}

var viList virtv2.VirtualImageList
err = h.client.List(ctx, &viList, &client.ListOptions{
Namespace: vd.GetNamespace(),
})
if err != nil {
return reconcile.Result{}, fmt.Errorf("error getting virtual images: %w", err)
}

for _, vi := range viList.Items {
if vi.Spec.DataSource.Type != virtv2.DataSourceTypeObjectRef || vi.Spec.DataSource.ObjectRef == nil {
continue
}

if vi.Spec.DataSource.ObjectRef.Kind != virtv2.VirtualDiskKind || vi.Spec.DataSource.ObjectRef.Name != vd.GetName() {
continue
}

readyCondition, _ := service.GetCondition(vicondition.ReadyType, vi.Status.Conditions)
if readyCondition.Status != metav1.ConditionTrue {
inUseForCreateImage = true
}
}

var cviList virtv2.ClusterVirtualImageList
err = h.client.List(ctx, &cviList, &client.ListOptions{})
if err != nil {
return reconcile.Result{}, fmt.Errorf("error getting cluster virtual images: %w", err)
}

for _, cvi := range cviList.Items {
if cvi.Spec.DataSource.Type != virtv2.DataSourceTypeObjectRef || cvi.Spec.DataSource.ObjectRef == nil {
continue
}

if cvi.Spec.DataSource.ObjectRef.Kind != virtv2.VirtualDiskKind || cvi.Spec.DataSource.ObjectRef.Name != vd.GetName() && cvi.Spec.DataSource.ObjectRef.Namespace != vd.GetNamespace() {
continue
}

readyCondition, _ := service.GetCondition(cvicondition.ReadyType, cvi.Status.Conditions)
if readyCondition.Status != metav1.ConditionTrue {
inUseForCreateImage = true
}
}

if inUseInRunningVirtualMachine && !inUseForCreateImage {
inUseCondition.Status = metav1.ConditionTrue
inUseCondition.Reason = vdcondition.InUseInRunningVirtualMachine
}

if inUseForCreateImage && !inUseInRunningVirtualMachine {
inUseCondition.Status = metav1.ConditionTrue
inUseCondition.Reason = vdcondition.InUseForCreateImage
}

if !inUseInRunningVirtualMachine && !inUseForCreateImage {
inUseCondition.Status = metav1.ConditionFalse
inUseCondition.Reason = vdcondition.NotInUse
}

inUseCondition.Message = ""
service.SetCondition(inUseCondition, &vd.Status.Conditions)

return reconcile.Result{}, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
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 watcher

import (
"context"
"fmt"
"log/slog"
"strings"

"k8s.io/apimachinery/pkg/types"
"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"

virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2"
)

type ClusterVirtualImageWatcher struct {
logger *slog.Logger
}

func NewClusterVirtualImageWatcher() *ClusterVirtualImageWatcher {
return &ClusterVirtualImageWatcher{
logger: slog.Default().With("watcher", strings.ToLower(virtv2.ClusterVirtualImageKind)),
}
}

func (w ClusterVirtualImageWatcher) Watch(mgr manager.Manager, ctr controller.Controller) error {
return ctr.Watch(
source.Kind(mgr.GetCache(), &virtv2.ClusterVirtualImage{}),
handler.EnqueueRequestsFromMapFunc(w.enqueueRequests),
predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool { return true },
DeleteFunc: func(e event.DeleteEvent) bool { return true },
UpdateFunc: w.filterUpdateEvents,
},
)
}

func (w ClusterVirtualImageWatcher) enqueueRequests(_ context.Context, obj client.Object) (requests []reconcile.Request) {
cvi, ok := obj.(*virtv2.ClusterVirtualImage)
if !ok {
w.logger.Error(fmt.Sprintf("expected a ClusterVirtualImage but got a %T", obj))
return
}

if cvi.Spec.DataSource.Type == virtv2.DataSourceTypeObjectRef && cvi.Spec.DataSource.ObjectRef != nil && cvi.Spec.DataSource.ObjectRef.Kind == virtv2.VirtualDiskKind {
requests = append(requests, reconcile.Request{
NamespacedName: types.NamespacedName{
Name: cvi.Spec.DataSource.ObjectRef.Name,
Namespace: cvi.Spec.DataSource.ObjectRef.Namespace,
},
})
}

return
}

func (w ClusterVirtualImageWatcher) filterUpdateEvents(e event.UpdateEvent) bool {
oldCVI, ok := e.ObjectOld.(*virtv2.ClusterVirtualImage)
if !ok {
w.logger.Error(fmt.Sprintf("expected an old ClusterVirtualImage but got a %T", e.ObjectOld))
return false
}

newCVI, ok := e.ObjectNew.(*virtv2.ClusterVirtualImage)
if !ok {
w.logger.Error(fmt.Sprintf("expected a new ClusterVirtualImage but got a %T", e.ObjectNew))
return false
}

return oldCVI.Status.Phase != newCVI.Status.Phase
}
Loading
Loading