Skip to content

Commit 277d3da

Browse files
committed
feat: add steps and resources
Signed-off-by: Daniil Antoshin <[email protected]>
1 parent b5611ab commit 277d3da

File tree

9 files changed

+614
-41
lines changed

9 files changed

+614
-41
lines changed

api/core/v1alpha2/virtual_machine_operation.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,47 @@ type VirtualMachineOperationStatus struct {
8686
Conditions []metav1.Condition `json:"conditions,omitempty"`
8787
// Resource generation last processed by the controller.
8888
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
89+
// Resources contains the list of resources that are affected by the snapshot operation.
90+
Resources []VirtualMachineOperationResource `json:"resources,omitempty"`
91+
}
92+
93+
// VMOPResourceKind defines the kind of the resource affected by the operation.
94+
// * `VirtualDisk`: VirtualDisk resource.
95+
// * `VirtualMachine`: VirtualMachine resource.
96+
// * `VirtualImage`: VirtualImage resource.
97+
// * `ClusterVirtualImage`: ClusterVirtualImage resource.
98+
// * `VirtualMachineIPAddress`: VirtualMachineIPAddress resource.
99+
// * `VirtualMachineIPAddressLease`: VirtualMachineIPAddressLease resource.
100+
// * `VirtualMachineClass`: VirtualMachineClass resource.
101+
// * `VirtualMachineOperation`: VirtualMachineOperation resource.
102+
// +kubebuilder:validation:Enum={VMOPResourceSecret,VMOPResourceNetwork,VMOPResourceVirtualDisk,VMOPResourceVirtualImage,VMOPResourceVirtualMachine,VMOPResourceClusterNetwork,VMOPResourceClusterVirtualImage,VMOPResourceVirtualMachineIPAddress,VMOPResourceVirtualMachineMacAddress,VMOPResourceVirtualMachineBlockDeviceAttachment}
103+
type VMOPResourceKind string
104+
105+
const (
106+
VMOPResourceSecret VMOPResourceKind = "Secret"
107+
VMOPResourceNetwork VMOPResourceKind = "Network"
108+
VMOPResourceVirtualDisk VMOPResourceKind = "VirtualDisk"
109+
VMOPResourceVirtualImage VMOPResourceKind = "VirtualImage"
110+
VMOPResourceVirtualMachine VMOPResourceKind = "VirtualMachine"
111+
VMOPResourceClusterNetwork VMOPResourceKind = "ClusterNetwork"
112+
VMOPResourceClusterVirtualImage VMOPResourceKind = "ClusterVirtualImage"
113+
VMOPResourceVirtualMachineIPAddress VMOPResourceKind = "VirtualMachineIPAddress"
114+
VMOPResourceVirtualMachineMacAddress VMOPResourceKind = "VirtualMachineIPAddress"
115+
VMOPResourceVirtualMachineBlockDeviceAttachment VMOPResourceKind = "VirtualMachineBlockDeviceAttachment"
116+
)
117+
118+
// VirtualMachineOperationResource defines the resource affected by the operation.
119+
type VirtualMachineOperationResource struct {
120+
// API version of the resource.
121+
APIVersion string `json:"apiVersion"`
122+
// Name of the resource.
123+
Name string `json:"name"`
124+
// Kind of the resource.
125+
Kind string `json:"kind"`
126+
// Status of the resource.
127+
Status string `json:"status"`
128+
// Message about the resource.
129+
Message string `json:"message"`
89130
}
90131

91132
// VirtualMachineOperationList contains a list of VirtualMachineOperation resources.

api/core/v1alpha2/zz_generated.deepcopy.go

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/pkg/apiserver/api/generated/openapi/zz_generated.openapi.go

Lines changed: 70 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crds/virtualmachineoperations.yaml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,38 @@ spec:
208208
- Failed
209209
- Terminating
210210
type: string
211+
resources:
212+
description:
213+
Resources contains the list of resources that are affected
214+
by the snapshot operation.
215+
items:
216+
description:
217+
VirtualMachineOperationResource defines the resource
218+
affected by the operation.
219+
properties:
220+
apiVersion:
221+
description: API version of the resource.
222+
type: string
223+
kind:
224+
description: Kind of the resource.
225+
type: string
226+
message:
227+
description: Message about the resource.
228+
type: string
229+
name:
230+
description: Name of the resource.
231+
type: string
232+
status:
233+
description: Status of the resource.
234+
type: string
235+
required:
236+
- apiVersion
237+
- kind
238+
- message
239+
- name
240+
- status
241+
type: object
242+
type: array
211243
required:
212244
- phase
213245
type: object
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*
2+
Copyright 2024 Flant JSC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package step
18+
19+
import (
20+
"context"
21+
"errors"
22+
"fmt"
23+
24+
corev1 "k8s.io/api/core/v1"
25+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26+
"k8s.io/apimachinery/pkg/types"
27+
"sigs.k8s.io/controller-runtime/pkg/client"
28+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
29+
30+
"github.com/deckhouse/virtualization-controller/pkg/common/object"
31+
"github.com/deckhouse/virtualization-controller/pkg/controller/conditions"
32+
"github.com/deckhouse/virtualization-controller/pkg/controller/service"
33+
"github.com/deckhouse/virtualization-controller/pkg/controller/vmop/internal/snapshot/restorer"
34+
"github.com/deckhouse/virtualization-controller/pkg/eventrecord"
35+
virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2"
36+
vmrestorecondition "github.com/deckhouse/virtualization/api/core/v1alpha2/vm-restore-condition"
37+
)
38+
39+
type Restorer interface {
40+
RestoreVirtualMachine(ctx context.Context, secret *corev1.Secret) (*virtv2.VirtualMachine, error)
41+
RestoreProvisioner(ctx context.Context, secret *corev1.Secret) (*corev1.Secret, error)
42+
RestoreVirtualMachineIPAddress(ctx context.Context, secret *corev1.Secret) (*virtv2.VirtualMachineIPAddress, error)
43+
RestoreVirtualMachineBlockDeviceAttachments(ctx context.Context, secret *corev1.Secret) ([]*virtv2.VirtualMachineBlockDeviceAttachment, error)
44+
}
45+
46+
type ResoreForcedStep struct {
47+
client client.Client
48+
recorder eventrecord.EventRecorderLogger
49+
restorer Restorer
50+
cb *conditions.ConditionBuilder
51+
vmop *virtv2.VirtualMachineOperation
52+
}
53+
54+
func NewResoreForcedStep(
55+
client client.Client,
56+
recorder eventrecord.EventRecorderLogger,
57+
cb *conditions.ConditionBuilder,
58+
vmop *virtv2.VirtualMachineOperation,
59+
) *ResoreForcedStep {
60+
return &ResoreForcedStep{
61+
client: client,
62+
recorder: recorder,
63+
cb: cb,
64+
vmop: vmop,
65+
}
66+
}
67+
68+
func (s ResoreForcedStep) Take(ctx context.Context, vm *virtv2.VirtualMachine) (*reconcile.Result, error) {
69+
vmRestore := &virtv2.VirtualMachineRestore{}
70+
71+
switch vmRestore.Status.Phase {
72+
case virtv2.VirtualMachineRestorePhaseReady,
73+
virtv2.VirtualMachineRestorePhaseFailed,
74+
virtv2.VirtualMachineRestorePhaseTerminating:
75+
return &reconcile.Result{}, nil
76+
}
77+
78+
cb := conditions.NewConditionBuilder(vmrestorecondition.VirtualMachineRestoreReadyType)
79+
defer func() { conditions.SetCondition(cb.Generation(vmRestore.Generation), &vmRestore.Status.Conditions) }()
80+
81+
if !conditions.HasCondition(cb.GetType(), vmRestore.Status.Conditions) {
82+
cb.Status(metav1.ConditionUnknown).Reason(conditions.ReasonUnknown)
83+
}
84+
85+
var tocreate []client.object
86+
87+
if vmRestore.Spec.RestoreMode == virtv2.RestoreModeForced {
88+
for _, ov := range overrideValidators {
89+
ov.Override(vmRestore.Spec.NameReplacements)
90+
91+
err := ov.ValidateWithForce(ctx)
92+
switch {
93+
case err == nil:
94+
toCreate = append(toCreate, ov.Object())
95+
case errors.Is(err, restorer.ErrAlreadyInUse), errors.Is(err, restorer.ErrAlreadyExistsAndHasDiff):
96+
vmRestore.Status.Phase = virtv2.VirtualMachineRestorePhaseFailed
97+
cb.
98+
Status(metav1.ConditionFalse).
99+
Reason(vmrestorecondition.VirtualMachineRestoreConflict).
100+
Message(service.CapitalizeFirstLetter(err.Error()) + ".")
101+
return &reconcile.Result{}, nil
102+
case errors.Is(err, restorer.ErrAlreadyExists):
103+
default:
104+
setPhaseConditionToFailed(cb, &vmRestore.Status.Phase, err)
105+
return &reconcile.Result{}, err
106+
}
107+
}
108+
109+
vmObj, err := object.FetchObject(ctx, types.NamespacedName{Name: overridedVMName, Namespace: restoredVM.Namespace}, s.client, &virtv2.VirtualMachine{})
110+
if err != nil {
111+
setPhaseConditionToFailed(cb, &vmRestore.Status.Phase, err)
112+
return &reconcile.Result{}, fmt.Errorf("failed to fetch the `VirtualMachine`: %w", err)
113+
}
114+
115+
if vmObj == nil {
116+
err := errors.New("restoration with `Forced` mode can be applied only to an existing virtual machine; you can restore the virtual machine with `Safe` mode")
117+
setPhaseConditionToFailed(cb, &vmRestore.Status.Phase, err)
118+
return &reconcile.Result{}, err
119+
} else {
120+
switch vmObj.Status.Phase {
121+
case virtv2.MachinePending:
122+
err := errors.New("a virtual machine cannot be restored from the pending phase with `Forced` mode; you can delete the virtual machine and restore it with `Safe` mode")
123+
setPhaseConditionToFailed(cb, &vmRestore.Status.Phase, err)
124+
return &reconcile.Result{}, err
125+
case virtv2.MachineStopped:
126+
default:
127+
if runPolicy != virtv2.AlwaysOffPolicy {
128+
err := updateVMRunPolicy(ctx, s.client, vmObj, virtv2.AlwaysOffPolicy)
129+
if err != nil {
130+
if errors.Is(err, restorer.ErrUpdating) {
131+
setPhaseConditionToPending(cb, &vmRestore.Status.Phase, vmrestorecondition.VirtualMachineIsNotStopped, err.Error())
132+
return &reconcile.Result{}, nil
133+
}
134+
setPhaseConditionToFailed(cb, &vmRestore.Status.Phase, err)
135+
return &reconcile.Result{}, err
136+
}
137+
setPhaseConditionToPending(cb, &vmRestore.Status.Phase, vmrestorecondition.VirtualMachineIsNotStopped, "waiting for the virtual machine run policy will be updated")
138+
return &reconcile.Result{}, nil
139+
}
140+
141+
err := stopVirtualMachine(ctx, s.client, restoredVM.Name, restoredVM.Namespace, string(vmRestore.UID))
142+
if err != nil {
143+
if errors.Is(err, restorer.ErrIncomplete) {
144+
setPhaseConditionToPending(cb, &vmRestore.Status.Phase, vmrestorecondition.VirtualMachineIsNotStopped, "waiting for the virtual machine will be stopped")
145+
return &reconcile.Result{}, nil
146+
}
147+
setPhaseConditionToFailed(cb, &vmRestore.Status.Phase, err)
148+
return &reconcile.Result{}, err
149+
}
150+
}
151+
}
152+
153+
for _, ov := range overrideValidators {
154+
err := ov.ProcessWithForce(ctx)
155+
switch {
156+
case err == nil:
157+
case errors.Is(err, restorer.ErrRestoring), errors.Is(err, restorer.ErrUpdating):
158+
setPhaseConditionToPending(cb, &vmRestore.Status.Phase, vmrestorecondition.VirtualMachineResourcesAreNotReady, err.Error())
159+
return &reconcile.Result{}, nil
160+
default:
161+
setPhaseConditionToPending(cb, &vmRestore.Status.Phase, vmrestorecondition.VirtualMachineResourcesAreNotReady, err.Error())
162+
return &reconcile.Result{}, err
163+
}
164+
}
165+
}
166+
167+
return &reconcile.Result{}, nil
168+
}

0 commit comments

Comments
 (0)