From 17447c515883124a78b156a1897462e5f9b39038 Mon Sep 17 00:00:00 2001 From: Daniil Antoshin Date: Fri, 21 Nov 2025 12:06:17 +0200 Subject: [PATCH 1/5] feat(vmsop): clone crds Signed-off-by: Daniil Antoshin --- .../typed/core/v1alpha2/core_client.go | 5 + .../core/v1alpha2/fake/fake_core_client.go | 4 + .../fake_virtualmachinesnapshotoperation.go | 54 ++++ .../core/v1alpha2/generated_expansion.go | 2 + .../virtualmachinesnapshotoperation.go | 74 +++++ .../core/v1alpha2/interface.go | 7 + .../virtualmachinesnapshotoperation.go | 102 ++++++ .../informers/externalversions/generic.go | 2 + .../core/v1alpha2/expansion_generated.go | 8 + .../virtualmachinesnapshotoperation.go | 70 ++++ api/core/v1alpha2/events.go | 12 + api/core/v1alpha2/finalizers.go | 1 + api/core/v1alpha2/register.go | 2 + api/core/v1alpha2/snapshot_operation.go | 31 ++ .../v1alpha2/virtual_machine_operation.go | 62 +--- .../virtual_machine_snapshot_operation.go | 121 +++++++ api/core/v1alpha2/vmsopcondition/condition.go | 76 +++++ api/core/v1alpha2/zz_generated.deepcopy.go | 186 ++++++++++- api/scripts/update-codegen.sh | 1 + crds/doc-ru-virtualmachineoperations.yaml | 2 +- ...c-ru-virtualmachinesnapshotoperations.yaml | 114 +++++++ crds/virtualmachineoperations.yaml | 16 +- crds/virtualmachinesnapshotoperations.yaml | 303 ++++++++++++++++++ .../rbac-for-us.yaml | 3 + 24 files changed, 1183 insertions(+), 75 deletions(-) create mode 100644 api/client/generated/clientset/versioned/typed/core/v1alpha2/fake/fake_virtualmachinesnapshotoperation.go create mode 100644 api/client/generated/clientset/versioned/typed/core/v1alpha2/virtualmachinesnapshotoperation.go create mode 100644 api/client/generated/informers/externalversions/core/v1alpha2/virtualmachinesnapshotoperation.go create mode 100644 api/client/generated/listers/core/v1alpha2/virtualmachinesnapshotoperation.go create mode 100644 api/core/v1alpha2/snapshot_operation.go create mode 100644 api/core/v1alpha2/virtual_machine_snapshot_operation.go create mode 100644 api/core/v1alpha2/vmsopcondition/condition.go create mode 100644 crds/doc-ru-virtualmachinesnapshotoperations.yaml create mode 100644 crds/virtualmachinesnapshotoperations.yaml 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 3c7a39d1cb..5fc598681d 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 @@ -42,6 +42,7 @@ type VirtualizationV1alpha2Interface interface { VirtualMachineOperationsGetter VirtualMachineRestoresGetter VirtualMachineSnapshotsGetter + VirtualMachineSnapshotOperationsGetter } // VirtualizationV1alpha2Client is used to interact with features provided by the virtualization.deckhouse.io group. @@ -105,6 +106,10 @@ func (c *VirtualizationV1alpha2Client) VirtualMachineSnapshots(namespace string) return newVirtualMachineSnapshots(c, namespace) } +func (c *VirtualizationV1alpha2Client) VirtualMachineSnapshotOperations(namespace string) VirtualMachineSnapshotOperationInterface { + return newVirtualMachineSnapshotOperations(c, namespace) +} + // NewForConfig creates a new VirtualizationV1alpha2Client for the given config. // NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), // where httpClient was generated with rest.HTTPClientFor(c). 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 5b6a581117..816406b63d 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 @@ -84,6 +84,10 @@ func (c *FakeVirtualizationV1alpha2) VirtualMachineSnapshots(namespace string) v return newFakeVirtualMachineSnapshots(c, namespace) } +func (c *FakeVirtualizationV1alpha2) VirtualMachineSnapshotOperations(namespace string) v1alpha2.VirtualMachineSnapshotOperationInterface { + return newFakeVirtualMachineSnapshotOperations(c, namespace) +} + // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. func (c *FakeVirtualizationV1alpha2) RESTClient() rest.Interface { diff --git a/api/client/generated/clientset/versioned/typed/core/v1alpha2/fake/fake_virtualmachinesnapshotoperation.go b/api/client/generated/clientset/versioned/typed/core/v1alpha2/fake/fake_virtualmachinesnapshotoperation.go new file mode 100644 index 0000000000..190be6181e --- /dev/null +++ b/api/client/generated/clientset/versioned/typed/core/v1alpha2/fake/fake_virtualmachinesnapshotoperation.go @@ -0,0 +1,54 @@ +/* +Copyright Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + corev1alpha2 "github.com/deckhouse/virtualization/api/client/generated/clientset/versioned/typed/core/v1alpha2" + v1alpha2 "github.com/deckhouse/virtualization/api/core/v1alpha2" + gentype "k8s.io/client-go/gentype" +) + +// fakeVirtualMachineSnapshotOperations implements VirtualMachineSnapshotOperationInterface +type fakeVirtualMachineSnapshotOperations struct { + *gentype.FakeClientWithList[*v1alpha2.VirtualMachineSnapshotOperation, *v1alpha2.VirtualMachineSnapshotOperationList] + Fake *FakeVirtualizationV1alpha2 +} + +func newFakeVirtualMachineSnapshotOperations(fake *FakeVirtualizationV1alpha2, namespace string) corev1alpha2.VirtualMachineSnapshotOperationInterface { + return &fakeVirtualMachineSnapshotOperations{ + gentype.NewFakeClientWithList[*v1alpha2.VirtualMachineSnapshotOperation, *v1alpha2.VirtualMachineSnapshotOperationList]( + fake.Fake, + namespace, + v1alpha2.SchemeGroupVersion.WithResource("virtualmachinesnapshotoperations"), + v1alpha2.SchemeGroupVersion.WithKind("VirtualMachineSnapshotOperation"), + func() *v1alpha2.VirtualMachineSnapshotOperation { return &v1alpha2.VirtualMachineSnapshotOperation{} }, + func() *v1alpha2.VirtualMachineSnapshotOperationList { + return &v1alpha2.VirtualMachineSnapshotOperationList{} + }, + func(dst, src *v1alpha2.VirtualMachineSnapshotOperationList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha2.VirtualMachineSnapshotOperationList) []*v1alpha2.VirtualMachineSnapshotOperation { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v1alpha2.VirtualMachineSnapshotOperationList, items []*v1alpha2.VirtualMachineSnapshotOperation) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, + } +} 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 670940dadd..944819c8d7 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 @@ -43,3 +43,5 @@ type VirtualMachineOperationExpansion interface{} type VirtualMachineRestoreExpansion interface{} type VirtualMachineSnapshotExpansion interface{} + +type VirtualMachineSnapshotOperationExpansion interface{} diff --git a/api/client/generated/clientset/versioned/typed/core/v1alpha2/virtualmachinesnapshotoperation.go b/api/client/generated/clientset/versioned/typed/core/v1alpha2/virtualmachinesnapshotoperation.go new file mode 100644 index 0000000000..69970a2c38 --- /dev/null +++ b/api/client/generated/clientset/versioned/typed/core/v1alpha2/virtualmachinesnapshotoperation.go @@ -0,0 +1,74 @@ +/* +Copyright Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + context "context" + + scheme "github.com/deckhouse/virtualization/api/client/generated/clientset/versioned/scheme" + corev1alpha2 "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" + gentype "k8s.io/client-go/gentype" +) + +// VirtualMachineSnapshotOperationsGetter has a method to return a VirtualMachineSnapshotOperationInterface. +// A group's client should implement this interface. +type VirtualMachineSnapshotOperationsGetter interface { + VirtualMachineSnapshotOperations(namespace string) VirtualMachineSnapshotOperationInterface +} + +// VirtualMachineSnapshotOperationInterface has methods to work with VirtualMachineSnapshotOperation resources. +type VirtualMachineSnapshotOperationInterface interface { + Create(ctx context.Context, virtualMachineSnapshotOperation *corev1alpha2.VirtualMachineSnapshotOperation, opts v1.CreateOptions) (*corev1alpha2.VirtualMachineSnapshotOperation, error) + Update(ctx context.Context, virtualMachineSnapshotOperation *corev1alpha2.VirtualMachineSnapshotOperation, opts v1.UpdateOptions) (*corev1alpha2.VirtualMachineSnapshotOperation, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, virtualMachineSnapshotOperation *corev1alpha2.VirtualMachineSnapshotOperation, opts v1.UpdateOptions) (*corev1alpha2.VirtualMachineSnapshotOperation, 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) (*corev1alpha2.VirtualMachineSnapshotOperation, error) + List(ctx context.Context, opts v1.ListOptions) (*corev1alpha2.VirtualMachineSnapshotOperationList, 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 *corev1alpha2.VirtualMachineSnapshotOperation, err error) + VirtualMachineSnapshotOperationExpansion +} + +// virtualMachineSnapshotOperations implements VirtualMachineSnapshotOperationInterface +type virtualMachineSnapshotOperations struct { + *gentype.ClientWithList[*corev1alpha2.VirtualMachineSnapshotOperation, *corev1alpha2.VirtualMachineSnapshotOperationList] +} + +// newVirtualMachineSnapshotOperations returns a VirtualMachineSnapshotOperations +func newVirtualMachineSnapshotOperations(c *VirtualizationV1alpha2Client, namespace string) *virtualMachineSnapshotOperations { + return &virtualMachineSnapshotOperations{ + gentype.NewClientWithList[*corev1alpha2.VirtualMachineSnapshotOperation, *corev1alpha2.VirtualMachineSnapshotOperationList]( + "virtualmachinesnapshotoperations", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *corev1alpha2.VirtualMachineSnapshotOperation { + return &corev1alpha2.VirtualMachineSnapshotOperation{} + }, + func() *corev1alpha2.VirtualMachineSnapshotOperationList { + return &corev1alpha2.VirtualMachineSnapshotOperationList{} + }, + ), + } +} diff --git a/api/client/generated/informers/externalversions/core/v1alpha2/interface.go b/api/client/generated/informers/externalversions/core/v1alpha2/interface.go index f8dc88f224..c3126a2c07 100644 --- a/api/client/generated/informers/externalversions/core/v1alpha2/interface.go +++ b/api/client/generated/informers/externalversions/core/v1alpha2/interface.go @@ -52,6 +52,8 @@ type Interface interface { VirtualMachineRestores() VirtualMachineRestoreInformer // VirtualMachineSnapshots returns a VirtualMachineSnapshotInformer. VirtualMachineSnapshots() VirtualMachineSnapshotInformer + // VirtualMachineSnapshotOperations returns a VirtualMachineSnapshotOperationInformer. + VirtualMachineSnapshotOperations() VirtualMachineSnapshotOperationInformer } type version struct { @@ -134,3 +136,8 @@ func (v *version) VirtualMachineRestores() VirtualMachineRestoreInformer { func (v *version) VirtualMachineSnapshots() VirtualMachineSnapshotInformer { return &virtualMachineSnapshotInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } + +// VirtualMachineSnapshotOperations returns a VirtualMachineSnapshotOperationInformer. +func (v *version) VirtualMachineSnapshotOperations() VirtualMachineSnapshotOperationInformer { + return &virtualMachineSnapshotOperationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/api/client/generated/informers/externalversions/core/v1alpha2/virtualmachinesnapshotoperation.go b/api/client/generated/informers/externalversions/core/v1alpha2/virtualmachinesnapshotoperation.go new file mode 100644 index 0000000000..ec04fc0f50 --- /dev/null +++ b/api/client/generated/informers/externalversions/core/v1alpha2/virtualmachinesnapshotoperation.go @@ -0,0 +1,102 @@ +/* +Copyright Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + context "context" + time "time" + + versioned "github.com/deckhouse/virtualization/api/client/generated/clientset/versioned" + internalinterfaces "github.com/deckhouse/virtualization/api/client/generated/informers/externalversions/internalinterfaces" + corev1alpha2 "github.com/deckhouse/virtualization/api/client/generated/listers/core/v1alpha2" + apicorev1alpha2 "github.com/deckhouse/virtualization/api/core/v1alpha2" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// VirtualMachineSnapshotOperationInformer provides access to a shared informer and lister for +// VirtualMachineSnapshotOperations. +type VirtualMachineSnapshotOperationInformer interface { + Informer() cache.SharedIndexInformer + Lister() corev1alpha2.VirtualMachineSnapshotOperationLister +} + +type virtualMachineSnapshotOperationInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewVirtualMachineSnapshotOperationInformer constructs a new informer for VirtualMachineSnapshotOperation 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 NewVirtualMachineSnapshotOperationInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredVirtualMachineSnapshotOperationInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredVirtualMachineSnapshotOperationInformer constructs a new informer for VirtualMachineSnapshotOperation 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 NewFilteredVirtualMachineSnapshotOperationInformer(client versioned.Interface, namespace string, 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().VirtualMachineSnapshotOperations(namespace).List(context.Background(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.VirtualizationV1alpha2().VirtualMachineSnapshotOperations(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.VirtualizationV1alpha2().VirtualMachineSnapshotOperations(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.VirtualizationV1alpha2().VirtualMachineSnapshotOperations(namespace).Watch(ctx, options) + }, + }, + &apicorev1alpha2.VirtualMachineSnapshotOperation{}, + resyncPeriod, + indexers, + ) +} + +func (f *virtualMachineSnapshotOperationInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredVirtualMachineSnapshotOperationInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *virtualMachineSnapshotOperationInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&apicorev1alpha2.VirtualMachineSnapshotOperation{}, f.defaultInformer) +} + +func (f *virtualMachineSnapshotOperationInformer) Lister() corev1alpha2.VirtualMachineSnapshotOperationLister { + return corev1alpha2.NewVirtualMachineSnapshotOperationLister(f.Informer().GetIndexer()) +} diff --git a/api/client/generated/informers/externalversions/generic.go b/api/client/generated/informers/externalversions/generic.go index 938b674b16..e2b56006b0 100644 --- a/api/client/generated/informers/externalversions/generic.go +++ b/api/client/generated/informers/externalversions/generic.go @@ -82,6 +82,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Virtualization().V1alpha2().VirtualMachineRestores().Informer()}, nil case v1alpha2.SchemeGroupVersion.WithResource("virtualmachinesnapshots"): return &genericInformer{resource: resource.GroupResource(), informer: f.Virtualization().V1alpha2().VirtualMachineSnapshots().Informer()}, nil + case v1alpha2.SchemeGroupVersion.WithResource("virtualmachinesnapshotoperations"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Virtualization().V1alpha2().VirtualMachineSnapshotOperations().Informer()}, nil // Group=virtualization.deckhouse.io, Version=v1alpha3 case v1alpha3.SchemeGroupVersion.WithResource("virtualmachineclasses"): diff --git a/api/client/generated/listers/core/v1alpha2/expansion_generated.go b/api/client/generated/listers/core/v1alpha2/expansion_generated.go index 828def4066..c3daaded06 100644 --- a/api/client/generated/listers/core/v1alpha2/expansion_generated.go +++ b/api/client/generated/listers/core/v1alpha2/expansion_generated.go @@ -113,3 +113,11 @@ type VirtualMachineSnapshotListerExpansion interface{} // VirtualMachineSnapshotNamespaceListerExpansion allows custom methods to be added to // VirtualMachineSnapshotNamespaceLister. type VirtualMachineSnapshotNamespaceListerExpansion interface{} + +// VirtualMachineSnapshotOperationListerExpansion allows custom methods to be added to +// VirtualMachineSnapshotOperationLister. +type VirtualMachineSnapshotOperationListerExpansion interface{} + +// VirtualMachineSnapshotOperationNamespaceListerExpansion allows custom methods to be added to +// VirtualMachineSnapshotOperationNamespaceLister. +type VirtualMachineSnapshotOperationNamespaceListerExpansion interface{} diff --git a/api/client/generated/listers/core/v1alpha2/virtualmachinesnapshotoperation.go b/api/client/generated/listers/core/v1alpha2/virtualmachinesnapshotoperation.go new file mode 100644 index 0000000000..cfa06ed30a --- /dev/null +++ b/api/client/generated/listers/core/v1alpha2/virtualmachinesnapshotoperation.go @@ -0,0 +1,70 @@ +/* +Copyright Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + corev1alpha2 "github.com/deckhouse/virtualization/api/core/v1alpha2" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" +) + +// VirtualMachineSnapshotOperationLister helps list VirtualMachineSnapshotOperations. +// All objects returned here must be treated as read-only. +type VirtualMachineSnapshotOperationLister interface { + // List lists all VirtualMachineSnapshotOperations in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*corev1alpha2.VirtualMachineSnapshotOperation, err error) + // VirtualMachineSnapshotOperations returns an object that can list and get VirtualMachineSnapshotOperations. + VirtualMachineSnapshotOperations(namespace string) VirtualMachineSnapshotOperationNamespaceLister + VirtualMachineSnapshotOperationListerExpansion +} + +// virtualMachineSnapshotOperationLister implements the VirtualMachineSnapshotOperationLister interface. +type virtualMachineSnapshotOperationLister struct { + listers.ResourceIndexer[*corev1alpha2.VirtualMachineSnapshotOperation] +} + +// NewVirtualMachineSnapshotOperationLister returns a new VirtualMachineSnapshotOperationLister. +func NewVirtualMachineSnapshotOperationLister(indexer cache.Indexer) VirtualMachineSnapshotOperationLister { + return &virtualMachineSnapshotOperationLister{listers.New[*corev1alpha2.VirtualMachineSnapshotOperation](indexer, corev1alpha2.Resource("virtualmachinesnapshotoperation"))} +} + +// VirtualMachineSnapshotOperations returns an object that can list and get VirtualMachineSnapshotOperations. +func (s *virtualMachineSnapshotOperationLister) VirtualMachineSnapshotOperations(namespace string) VirtualMachineSnapshotOperationNamespaceLister { + return virtualMachineSnapshotOperationNamespaceLister{listers.NewNamespaced[*corev1alpha2.VirtualMachineSnapshotOperation](s.ResourceIndexer, namespace)} +} + +// VirtualMachineSnapshotOperationNamespaceLister helps list and get VirtualMachineSnapshotOperations. +// All objects returned here must be treated as read-only. +type VirtualMachineSnapshotOperationNamespaceLister interface { + // List lists all VirtualMachineSnapshotOperations in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*corev1alpha2.VirtualMachineSnapshotOperation, err error) + // Get retrieves the VirtualMachineSnapshotOperation from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*corev1alpha2.VirtualMachineSnapshotOperation, error) + VirtualMachineSnapshotOperationNamespaceListerExpansion +} + +// virtualMachineSnapshotOperationNamespaceLister implements the VirtualMachineSnapshotOperationNamespaceLister +// interface. +type virtualMachineSnapshotOperationNamespaceLister struct { + listers.ResourceIndexer[*corev1alpha2.VirtualMachineSnapshotOperation] +} diff --git a/api/core/v1alpha2/events.go b/api/core/v1alpha2/events.go index b5b4cd1a17..f2aec3b721 100644 --- a/api/core/v1alpha2/events.go +++ b/api/core/v1alpha2/events.go @@ -73,6 +73,18 @@ const ( // ReasonVMOPInProgress is event reason that the operation is in progress ReasonVMOPInProgress = "VirtualMachineOperationInProgress" + // ReasonVMSOPStarted is event reason that the operation is started + ReasonVMSOPStarted = "VirtualMachineSnaphotOperationStarted" + + // ReasonErrVMSOPFailed is event reason that operation is failed + ReasonErrVMSOPFailed = "VirtualMachineSnapshotOperationFailed" + + // ReasonVMSOPSucceeded is event reason that the operation is successfully completed + ReasonVMSOPSucceeded = "VirtualMachineSnapshotOperationSucceeded" + + // ReasonVMSOPInProgress is event reason that the operation is in progress + ReasonVMSOPInProgress = "VirtualMachineSnapshotOperationInProgress" + // ReasonVDSpecHasBeenChanged is event reason that spec of virtual disk has been changed. ReasonVDSpecHasBeenChanged = "VirtualDiskSpecHasBeenChanged" // ReasonVISpecHasBeenChanged is event reason that spec of virtual image has been changed. diff --git a/api/core/v1alpha2/finalizers.go b/api/core/v1alpha2/finalizers.go index fbbaf5b267..e2038aff5f 100644 --- a/api/core/v1alpha2/finalizers.go +++ b/api/core/v1alpha2/finalizers.go @@ -36,6 +36,7 @@ const ( FinalizerIPAddressLeaseCleanup = "virtualization.deckhouse.io/vmipl-cleanup" FinalizerVDSnapshotCleanup = "virtualization.deckhouse.io/vdsnapshot-cleanup" FinalizerVMOPCleanup = "virtualization.deckhouse.io/vmop-cleanup" + FinalizerVMSOPCleanup = "virtualization.deckhouse.io/vmsop-cleanup" FinalizerVMClassCleanup = "virtualization.deckhouse.io/vmclass-cleanup" FinalizerVMBDACleanup = "virtualization.deckhouse.io/vmbda-cleanup" FinalizerMACAddressCleanup = "virtualization.deckhouse.io/vmmac-cleanup" diff --git a/api/core/v1alpha2/register.go b/api/core/v1alpha2/register.go index 06e1c7e9b3..9d113aae35 100644 --- a/api/core/v1alpha2/register.go +++ b/api/core/v1alpha2/register.go @@ -84,6 +84,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { &VirtualDiskSnapshotList{}, &VirtualMachineSnapshot{}, &VirtualMachineSnapshotList{}, + &VirtualMachineSnapshotOperation{}, + &VirtualMachineSnapshotOperationList{}, &VirtualMachineRestore{}, &VirtualMachineRestoreList{}, &VirtualMachineMACAddress{}, diff --git a/api/core/v1alpha2/snapshot_operation.go b/api/core/v1alpha2/snapshot_operation.go new file mode 100644 index 0000000000..14d2670880 --- /dev/null +++ b/api/core/v1alpha2/snapshot_operation.go @@ -0,0 +1,31 @@ +/* +Copyright 2025 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha2 + +// SnapshotOperationMode defines the kind of the clone operation. +// * `DryRun`: DryRun run without any changes. Compatibility shows in status. +// * `Strict`: Strict clone as is in the snapshot. +// * `BestEffort`: BestEffort process without deleted external missing dependencies. +// +kubebuilder:validation:Enum={DryRun,Strict,BestEffort} +type SnapshotOperationMode string + +const ( + SnapshotOperationModeDryRun SnapshotOperationMode = "DryRun" + SnapshotOperationModeStrict SnapshotOperationMode = "Strict" + SnapshotOperationModeBestEffort SnapshotOperationMode = "BestEffort" +) + diff --git a/api/core/v1alpha2/virtual_machine_operation.go b/api/core/v1alpha2/virtual_machine_operation.go index 40a585ba05..01a564a9df 100644 --- a/api/core/v1alpha2/virtual_machine_operation.go +++ b/api/core/v1alpha2/virtual_machine_operation.go @@ -16,7 +16,9 @@ limitations under the License. package v1alpha2 -import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) const ( VirtualMachineOperationKind = "VirtualMachineOperation" @@ -64,7 +66,7 @@ type VirtualMachineOperationSpec struct { // VirtualMachineOperationRestoreSpec defines the restore operation. type VirtualMachineOperationRestoreSpec struct { - Mode VMOPRestoreMode `json:"mode"` + Mode SnapshotOperationMode `json:"mode"` // VirtualMachineSnapshotName defines the source of the restore operation. VirtualMachineSnapshotName string `json:"virtualMachineSnapshotName"` } @@ -72,7 +74,7 @@ type VirtualMachineOperationRestoreSpec struct { // +kubebuilder:validation:XValidation:rule="(has(self.customization) && ((has(self.customization.namePrefix) && size(self.customization.namePrefix) > 0) || (has(self.customization.nameSuffix) && size(self.customization.nameSuffix) > 0))) || (has(self.nameReplacement) && size(self.nameReplacement) > 0)",message="At least one of customization.namePrefix, customization.nameSuffix, or nameReplacement must be set" // VirtualMachineOperationCloneSpec defines the clone operation. type VirtualMachineOperationCloneSpec struct { - Mode VMOPRestoreMode `json:"mode"` + Mode SnapshotOperationMode `json:"mode"` // NameReplacement defines rules for renaming resources during cloning. // +kubebuilder:validation:XValidation:rule="self.all(nr, has(nr.to) && size(nr.to) >= 1 && size(nr.to) <= 59)",message="Each nameReplacement.to must be between 1 and 59 characters" NameReplacement []NameReplacement `json:"nameReplacement,omitempty"` @@ -92,19 +94,6 @@ type VirtualMachineOperationCloneCustomization struct { NameSuffix string `json:"nameSuffix,omitempty"` } -// VMOPRestoreMode defines the kind of the restore operation. -// * `DryRun`: DryRun run without any changes. Compatibility shows in status. -// * `Strict`: Strict restore as is in the snapshot. -// * `BestEffort`: BestEffort restore without deleted external missing dependencies. -// +kubebuilder:validation:Enum={DryRun,Strict,BestEffort} -type VMOPRestoreMode string - -const ( - VMOPRestoreModeDryRun VMOPRestoreMode = "DryRun" - VMOPRestoreModeStrict VMOPRestoreMode = "Strict" - VMOPRestoreModeBestEffort VMOPRestoreMode = "BestEffort" -) - type VirtualMachineOperationStatus struct { Phase VMOPPhase `json:"phase"` // The latest detailed observations of the VirtualMachineOperation resource. @@ -112,38 +101,13 @@ type VirtualMachineOperationStatus struct { // Resource generation last processed by the controller. ObservedGeneration int64 `json:"observedGeneration,omitempty"` // Resources contains the list of resources that are affected by the snapshot operation. - Resources []VirtualMachineOperationResource `json:"resources,omitempty"` + Resources []SnapshotResourceStatus `json:"resources,omitempty"` } -// VMOPResourceKind defines the kind of the resource affected by the operation. -// * `VirtualDisk`: VirtualDisk resource. -// * `VirtualMachine`: VirtualMachine resource. -// * `VirtualImage`: VirtualImage resource. -// * `ClusterVirtualImage`: ClusterVirtualImage resource. -// * `VirtualMachineIPAddress`: VirtualMachineIPAddress resource. -// * `VirtualMachineIPAddressLease`: VirtualMachineIPAddressLease resource. -// * `VirtualMachineClass`: VirtualMachineClass resource. -// * `VirtualMachineOperation`: VirtualMachineOperation resource. -// +kubebuilder:validation:Enum={VMOPResourceSecret,VMOPResourceNetwork,VMOPResourceVirtualDisk,VMOPResourceVirtualImage,VMOPResourceVirtualMachine,VMOPResourceClusterNetwork,VMOPResourceClusterVirtualImage,VMOPResourceVirtualMachineIPAddress,VMOPResourceVirtualMachineMacAddress,VMOPResourceVirtualMachineBlockDeviceAttachment} -type VMOPResourceKind string - -const ( - VMOPResourceSecret VMOPResourceKind = "Secret" - VMOPResourceNetwork VMOPResourceKind = "Network" - VMOPResourceVirtualDisk VMOPResourceKind = "VirtualDisk" - VMOPResourceVirtualImage VMOPResourceKind = "VirtualImage" - VMOPResourceVirtualMachine VMOPResourceKind = "VirtualMachine" - VMOPResourceClusterNetwork VMOPResourceKind = "ClusterNetwork" - VMOPResourceClusterVirtualImage VMOPResourceKind = "ClusterVirtualImage" - VMOPResourceVirtualMachineIPAddress VMOPResourceKind = "VirtualMachineIPAddress" - VMOPResourceVirtualMachineMacAddress VMOPResourceKind = "VirtualMachineMacAddress" - VMOPResourceVirtualMachineBlockDeviceAttachment VMOPResourceKind = "VirtualMachineBlockDeviceAttachment" -) - const ( - VMOPResourceStatusInProgress VMOPResourceStatusPhase = "InProgress" - VMOPResourceStatusCompleted VMOPResourceStatusPhase = "Completed" - VMOPResourceStatusFailed VMOPResourceStatusPhase = "Failed" + SnapshotResourceStatusInProgress SnapshotResourceStatusPhase = "InProgress" + SnapshotResourceStatusCompleted SnapshotResourceStatusPhase = "Completed" + SnapshotResourceStatusFailed SnapshotResourceStatusPhase = "Failed" ) // Current phase of the resource: @@ -151,10 +115,10 @@ const ( // * `Completed`: The operation for resource has been completed successfully. // * `Failed`: The operation for resource failed. For details, refer to the `Message` field. // +kubebuilder:validation:Enum={InProgress,Completed,Failed} -type VMOPResourceStatusPhase string +type SnapshotResourceStatusPhase string -// VirtualMachineOperationResource defines the resource affected by the operation. -type VirtualMachineOperationResource struct { +// SnapshotResourceStatus defines the resource affected by the operation. +type SnapshotResourceStatus struct { // API version of the resource. APIVersion string `json:"apiVersion"` // Name of the resource. @@ -162,7 +126,7 @@ type VirtualMachineOperationResource struct { // Kind of the resource. Kind string `json:"kind"` // Status of the resource. - Status VMOPResourceStatusPhase `json:"status"` + Status SnapshotResourceStatusPhase `json:"status"` // Message about the resource. Message string `json:"message"` } diff --git a/api/core/v1alpha2/virtual_machine_snapshot_operation.go b/api/core/v1alpha2/virtual_machine_snapshot_operation.go new file mode 100644 index 0000000000..5df0791fe3 --- /dev/null +++ b/api/core/v1alpha2/virtual_machine_snapshot_operation.go @@ -0,0 +1,121 @@ +/* +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 ( + VirtualMachineSnapshotOperationKind = "VirtualMachineSnapshotOperation" + VirtualmachineSnapshotOperationResource = "virtualmachinesnapshotoperations" +) + +// VirtualMachineSnapshotOperation enables declarative management of virtual machine snapshot state changes. +// +kubebuilder:object:root=true +// +kubebuilder:metadata:labels={heritage=deckhouse,module=virtualization} +// +kubebuilder:subresource:status +// +kubebuilder:resource:categories={virtualization},scope=Namespaced,shortName={vmsop},singular=virtualmachinesnapshotoperation +// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="VirtualMachineSnapshotOperation phase." +// +kubebuilder:printcolumn:name="Type",type="string",JSONPath=".spec.type",description="VirtualMachineSnapshotOperation type." +// +kubebuilder:printcolumn:name="VirtualMachineSnapshot",type="string",JSONPath=".spec.virtualMachineSnapshotName",description="VirtualMachineSnapshot name." +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time of resource creation." +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type VirtualMachineSnapshotOperation struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec VirtualMachineSnapshotOperationSpec `json:"spec"` + Status VirtualMachineSnapshotOperationStatus `json:"status,omitempty"` +} + +// +kubebuilder:validation:XValidation:rule="self == oldSelf",message=".spec is immutable" +// +kubebuilder:validation:XValidation:rule="self.type == 'CreateVirtualMachineName' ? has(self.createVirtualMachine) : true",message="CreateVirtualMachineName requires clone field." +type VirtualMachineSnapshotOperationSpec struct { + Type VMSOPType `json:"type"` + // Name of the virtual machine snapshot the operation is performed for. + VirtualMachineSnapshotName string `json:"virtualMachineSnapshotName"` + // CreateVirtualMachine defines the clone operation. + CreateVirtualMachine *VMSOPCreateVirtualMachineSpec `json:"createVirtualMachine,omitempty"` +} + +// +kubebuilder:validation:XValidation:rule="(has(self.customization) && ((has(self.customization.namePrefix) && size(self.customization.namePrefix) > 0) || (has(self.customization.nameSuffix) && size(self.customization.nameSuffix) > 0))) || (has(self.nameReplacement) && size(self.nameReplacement) > 0)",message="At least one of customization.namePrefix, customization.nameSuffix, or nameReplacement must be set" +// VMSOPCreateVirtualMachineSpec defines the clone operation. +type VMSOPCreateVirtualMachineSpec struct { + Mode SnapshotOperationMode `json:"mode"` + // NameReplacement defines rules for renaming resources during cloning. + // +kubebuilder:validation:XValidation:rule="self.all(nr, has(nr.to) && size(nr.to) >= 1 && size(nr.to) <= 59)",message="Each nameReplacement.to must be between 1 and 59 characters" + NameReplacement []NameReplacement `json:"nameReplacement,omitempty"` + // Customization defines customization options for cloning. + Customization *VMSOPCreateVirtualMachineCustomization `json:"customization,omitempty"` +} + +// +kubebuilder:validation:XValidation:rule="!has(self.namePrefix) || (size(self.namePrefix) >= 1 && size(self.namePrefix) <= 59)",message="namePrefix length must be between 1 and 59 characters if set" +// +kubebuilder:validation:XValidation:rule="!has(self.nameSuffix) || (size(self.nameSuffix) >= 1 && size(self.nameSuffix) <= 59)",message="nameSuffix length must be between 1 and 59 characters if set" +// VMSOPCreateVirtualMachineCustomization defines customization options for cloning. +type VMSOPCreateVirtualMachineCustomization struct { + // NamePrefix adds a prefix to resource names during cloning. + // Applied to VirtualMachine, VirtualDisk, VirtualMachineBlockDeviceAttachment, and Secret resources. + NamePrefix string `json:"namePrefix,omitempty"` + // NameSuffix adds a suffix to resource names during cloning. + // Applied to VirtualMachine, VirtualDisk, VirtualMachineBlockDeviceAttachment, and Secret resources. + NameSuffix string `json:"nameSuffix,omitempty"` +} + +type VirtualMachineSnapshotOperationStatus struct { + Phase VMSOPPhase `json:"phase"` + // The latest detailed observations of the VirtualMachineSnapshotOperation resource. + Conditions []metav1.Condition `json:"conditions,omitempty"` + // Resource generation last processed by the controller. + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + // Resources contains the list of resources that are affected by the snapshot operation. + Resources []SnapshotResourceStatus `json:"resources,omitempty"` +} + + +// VirtualMachineSnapshotOperationList contains a list of VirtualMachineSnapshotOperation resources. +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type VirtualMachineSnapshotOperationList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + Items []VirtualMachineSnapshotOperation `json:"items"` +} + +// Current phase of the resource: +// * `Pending`: The operation is queued for execution. +// * `InProgress`: The operation is in progress. +// * `Completed`: The operation has been completed successfully. +// * `Failed`: The operation failed. For details, refer to the `conditions` field and events. +// * `Terminating`: The operation is being deleted. +// +kubebuilder:validation:Enum={Pending,InProgress,Completed,Failed,Terminating} +type VMSOPPhase string + +const ( + VMSOPPhasePending VMSOPPhase = "Pending" + VMSOPPhaseInProgress VMSOPPhase = "InProgress" + VMSOPPhaseCompleted VMSOPPhase = "Completed" + VMSOPPhaseFailed VMSOPPhase = "Failed" + VMSOPPhaseTerminating VMSOPPhase = "Terminating" +) + +// Type of the operation to execute on a virtual machine: +// * `CreateVirtualMachine`: CreateVirtualMachine the virtual machine to a new virtual machine. +// +kubebuilder:validation:Enum={CreateVirtualMachine} +type VMSOPType string + +const ( + VMSOPTypeCreateVirtualMachine VMSOPType = "CreateVirtualMachine" +) diff --git a/api/core/v1alpha2/vmsopcondition/condition.go b/api/core/v1alpha2/vmsopcondition/condition.go new file mode 100644 index 0000000000..79aa96c2e5 --- /dev/null +++ b/api/core/v1alpha2/vmsopcondition/condition.go @@ -0,0 +1,76 @@ +/* +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 vmsopcondition + +type Type string + +func (t Type) String() string { + return string(t) +} + +const ( + // TypeCompleted is a type for condition that indicates operation is complete. + TypeCompleted Type = "Completed" + + // TypeCreateVirtualMachineCompleted is a type for condition that indicates success of clone. + TypeCreateVirtualMachineCompleted Type = "CreateVirtualMachineCompleted" +) + +// ReasonCompleted represents specific reasons for the 'Completed' condition type. +type ReasonCompleted string + +func (r ReasonCompleted) String() string { + return string(r) +} + +const ( + // ReasonVirtualMachineSnapshotNotFound is a ReasonCompleted indicating that the specified virtual machine is absent. + ReasonVirtualMachineSnapshotNotFound ReasonCompleted = "VirtualMachineSnapshotNotFound" + + // ReasonNotApplicableForVMSPhase is a ReasonCompleted indicating that the specified operation type is not applicable for the virtual machine phase. + ReasonNotApplicableForVMSPhase ReasonCompleted = "NotApplicableForVirtualMachinePhase" + + // ReasonNotReadyToBeExecuted is a ReasonCompleted indicating that the operation is not ready to be executed. + ReasonNotReadyToBeExecuted ReasonCompleted = "NotReadyToBeExecuted" + + // ReasonCreateVirtualMachineInProgress is a ReasonCompleted indicating that the clone operation is in progress. + ReasonCreateVirtualMachineInProgress ReasonCompleted = "CreateVirtualMachineInProgress" + + // ReasonOperationFailed is a ReasonCompleted indicating that operation has failed. + ReasonOperationFailed ReasonCompleted = "OperationFailed" + + // ReasonOperationCompleted is a ReasonCompleted indicating that operation is completed. + ReasonOperationCompleted ReasonCompleted = "OperationCompleted" +) + +// ReasonCreateVirtualMachineCompleted represents specific reasons for the 'CreateVirtualMachineCompleted' condition type. +type ReasonCreateVirtualMachineCompleted string + +func (r ReasonCreateVirtualMachineCompleted) String() string { + return string(r) +} + +const ( + // ReasonCreateVirtualMachineOperationInProgress is a ReasonCreateVirtualMachineCompleted indicating that the clone operation is in progress. + ReasonCreateVirtualMachineOperationInProgress ReasonCreateVirtualMachineCompleted = "CreateVirtualMachineInProgress" + + // ReasonCreateVirtualMachineOperationCompleted is a ReasonCreateVirtualMachineCompleted indicating that the clone operation has completed successfully. + ReasonCreateVirtualMachineOperationCompleted ReasonCreateVirtualMachineCompleted = "CreateVirtualMachineCompleted" + + // ReasonCreateVirtualMachineOperationFailed is a ReasonCreateVirtualMachineCompleted indicating that clone operation has failed. + ReasonCreateVirtualMachineOperationFailed ReasonCreateVirtualMachineCompleted = "CreateVirtualMachineFailed" +) diff --git a/api/core/v1alpha2/zz_generated.deepcopy.go b/api/core/v1alpha2/zz_generated.deepcopy.go index 280297e597..ea6a55772c 100644 --- a/api/core/v1alpha2/zz_generated.deepcopy.go +++ b/api/core/v1alpha2/zz_generated.deepcopy.go @@ -823,6 +823,22 @@ func (in *SizingPolicyMemoryPerCore) DeepCopy() *SizingPolicyMemoryPerCore { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SnapshotResourceStatus) DeepCopyInto(out *SnapshotResourceStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SnapshotResourceStatus. +func (in *SnapshotResourceStatus) DeepCopy() *SnapshotResourceStatus { + if in == nil { + return nil + } + out := new(SnapshotResourceStatus) + 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 @@ -934,6 +950,48 @@ func (in *VMBDAObjectRef) DeepCopy() *VMBDAObjectRef { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VMSOPCreateVirtualMachineCustomization) DeepCopyInto(out *VMSOPCreateVirtualMachineCustomization) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VMSOPCreateVirtualMachineCustomization. +func (in *VMSOPCreateVirtualMachineCustomization) DeepCopy() *VMSOPCreateVirtualMachineCustomization { + if in == nil { + return nil + } + out := new(VMSOPCreateVirtualMachineCustomization) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VMSOPCreateVirtualMachineSpec) DeepCopyInto(out *VMSOPCreateVirtualMachineSpec) { + *out = *in + if in.NameReplacement != nil { + in, out := &in.NameReplacement, &out.NameReplacement + *out = make([]NameReplacement, len(*in)) + copy(*out, *in) + } + if in.Customization != nil { + in, out := &in.Customization, &out.Customization + *out = new(VMSOPCreateVirtualMachineCustomization) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VMSOPCreateVirtualMachineSpec. +func (in *VMSOPCreateVirtualMachineSpec) DeepCopy() *VMSOPCreateVirtualMachineSpec { + if in == nil { + return nil + } + out := new(VMSOPCreateVirtualMachineSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Versions) DeepCopyInto(out *Versions) { *out = *in @@ -2580,22 +2638,6 @@ func (in *VirtualMachineOperationList) DeepCopyObject() runtime.Object { return nil } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VirtualMachineOperationResource) DeepCopyInto(out *VirtualMachineOperationResource) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualMachineOperationResource. -func (in *VirtualMachineOperationResource) DeepCopy() *VirtualMachineOperationResource { - if in == nil { - return nil - } - out := new(VirtualMachineOperationResource) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VirtualMachineOperationRestoreSpec) DeepCopyInto(out *VirtualMachineOperationRestoreSpec) { *out = *in @@ -2655,7 +2697,7 @@ func (in *VirtualMachineOperationStatus) DeepCopyInto(out *VirtualMachineOperati } if in.Resources != nil { in, out := &in.Resources, &out.Resources - *out = make([]VirtualMachineOperationResource, len(*in)) + *out = make([]SnapshotResourceStatus, len(*in)) copy(*out, *in) } return @@ -2870,6 +2912,116 @@ func (in *VirtualMachineSnapshotList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VirtualMachineSnapshotOperation) DeepCopyInto(out *VirtualMachineSnapshotOperation) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualMachineSnapshotOperation. +func (in *VirtualMachineSnapshotOperation) DeepCopy() *VirtualMachineSnapshotOperation { + if in == nil { + return nil + } + out := new(VirtualMachineSnapshotOperation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VirtualMachineSnapshotOperation) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VirtualMachineSnapshotOperationList) DeepCopyInto(out *VirtualMachineSnapshotOperationList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]VirtualMachineSnapshotOperation, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualMachineSnapshotOperationList. +func (in *VirtualMachineSnapshotOperationList) DeepCopy() *VirtualMachineSnapshotOperationList { + if in == nil { + return nil + } + out := new(VirtualMachineSnapshotOperationList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VirtualMachineSnapshotOperationList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VirtualMachineSnapshotOperationSpec) DeepCopyInto(out *VirtualMachineSnapshotOperationSpec) { + *out = *in + if in.CreateVirtualMachine != nil { + in, out := &in.CreateVirtualMachine, &out.CreateVirtualMachine + *out = new(VMSOPCreateVirtualMachineSpec) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualMachineSnapshotOperationSpec. +func (in *VirtualMachineSnapshotOperationSpec) DeepCopy() *VirtualMachineSnapshotOperationSpec { + if in == nil { + return nil + } + out := new(VirtualMachineSnapshotOperationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VirtualMachineSnapshotOperationStatus) DeepCopyInto(out *VirtualMachineSnapshotOperationStatus) { + *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]) + } + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = make([]SnapshotResourceStatus, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualMachineSnapshotOperationStatus. +func (in *VirtualMachineSnapshotOperationStatus) DeepCopy() *VirtualMachineSnapshotOperationStatus { + if in == nil { + return nil + } + out := new(VirtualMachineSnapshotOperationStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VirtualMachineSnapshotSpec) DeepCopyInto(out *VirtualMachineSnapshotSpec) { *out = *in diff --git a/api/scripts/update-codegen.sh b/api/scripts/update-codegen.sh index 7cdbb054b4..8214b2f44b 100755 --- a/api/scripts/update-codegen.sh +++ b/api/scripts/update-codegen.sh @@ -37,6 +37,7 @@ function source::settings { "VirtualMachineSnapshot" "VirtualMachineRestore" "VirtualMachineOperation" + "VirtualMachineSnapshotOperation" "VirtualDisk" "VirtualImage" "ClusterVirtualImage") diff --git a/crds/doc-ru-virtualmachineoperations.yaml b/crds/doc-ru-virtualmachineoperations.yaml index e43138b3c5..b471a20560 100644 --- a/crds/doc-ru-virtualmachineoperations.yaml +++ b/crds/doc-ru-virtualmachineoperations.yaml @@ -123,7 +123,7 @@ spec: Поколение ресурса, которое в последний раз обрабатывалось контроллером. resources: description: | - Содержит список ресурсов, затронутых операцией восстановления снимка. + Содержит список ресурсов, затронутых операцией снимка. items: description: | Определяет ресурс, затронутый операцией. diff --git a/crds/doc-ru-virtualmachinesnapshotoperations.yaml b/crds/doc-ru-virtualmachinesnapshotoperations.yaml new file mode 100644 index 0000000000..a25af5f30c --- /dev/null +++ b/crds/doc-ru-virtualmachinesnapshotoperations.yaml @@ -0,0 +1,114 @@ +spec: + versions: + - name: v1alpha2 + schema: + openAPIV3Schema: + description: | + Данный ресурс позволяет декларативно управлять изменением состояний виртуальных машин (ВМ). + properties: + spec: + properties: + type: + description: | + Тип операции, выполняемой над снимком виртуальной машины: + + * `CreateVirtualMachine` — создать виртуальную машину из снимка. + virtualMachineSnapshotName: + description: | + Имя снимка виртуальной машины, для которого выполняется операция. + createVirtualMachine: + description: | + Определяет параметры операции создания виртуальной машины из снимка. + properties: + mode: + description: | + Режим клонирования: + + * `DryRun` — запуск без выполнения создания. Конфликты и несоответствия фиксируются в статусе операции. + * `Strict` — строгий режим клонирования «как в исходной ВМ». Отсутствие внешних зависимостей может привести к тому, что клонированная виртуальная машина после создания будет находиться в состоянии `Pending`; + * `BestEffort` — режим клонирования с удалением отсутствующих внешних зависимостей (ClusterVirtualImage, VirtualImage) из спецификации виртуальной машины. + customization: + description: | + Определяет параметры кастомизации для создания виртуальной машины. + properties: + namePrefix: + description: | + Добавляет префикс к именам ресурсов при создании. + Применяется к ресурсам VirtualMachine, VirtualDisk, VirtualMachineBlockDeviceAttachment и Secret. + nameSuffix: + description: | + Добавляет суффикс к именам ресурсов при создании. + Применяется к ресурсам VirtualMachine, VirtualDisk, VirtualMachineBlockDeviceAttachment и Secret. + nameReplacement: + description: | + Определяет правила переименования ресурсов при создании виртуальной машины. + items: + description: | + Представляет правило для переопределения имён ресурсов виртуальной машины. + properties: + from: + description: | + Селектор для выбора ресурсов для переименования. + properties: + kind: + description: | + Тип ресурса для переименования. + name: + description: | + Текущее имя ресурса для переименования. + to: + description: | + Новое имя ресурса. + status: + properties: + conditions: + description: | + Последнее подтверждённое состояние данного ресурса. + items: + description: | + Подробные сведения об одном аспекте текущего состояния данного API-ресурса. + properties: + lastTransitionTime: + description: Время перехода условия из одного состояния в другое. + message: + description: Удобочитаемое сообщение с подробной информацией о последнем переходе. + observedGeneration: + description: | + `.metadata.generation`, на основе которого было установлено условие. + Например, если `.metadata.generation` в настоящее время имеет значение `12`, а `.status.conditions[x].observedgeneration` имеет значение `9`, то условие устарело. + reason: + description: Краткая причина последнего перехода состояния. + status: + description: | + Статус условия. + type: + description: Тип условия. + phase: + description: | + Представляет текущее состояние ресурса: + + * `Pending` — операция поставлена в очередь на выполнение; + * `InProgress` — операция в процессе выполнения; + * `Completed` — операция прошла успешно; + * `Failed` — операция завершилась неудачно. За подробностями обратитесь к полю `conditions` и событиям; + * `Terminating` — операция удаляется. + observedGeneration: + description: | + Поколение ресурса, которое в последний раз обрабатывалось контроллером. + resources: + description: | + Содержит список ресурсов, затронутых операцией создания из снимка. + items: + description: | + Определяет ресурс, затронутый операцией. + properties: + apiVersion: + description: API версия ресурса. + kind: + description: Тип ресурса. + message: + description: Сообщение о ресурсе. + name: + description: Имя ресурса. + status: + description: Статус ресурса. diff --git a/crds/virtualmachineoperations.yaml b/crds/virtualmachineoperations.yaml index c96023b308..e1d77838ac 100644 --- a/crds/virtualmachineoperations.yaml +++ b/crds/virtualmachineoperations.yaml @@ -96,10 +96,10 @@ spec: && size(self.nameSuffix) <= 59)" mode: description: |- - VMOPRestoreMode defines the kind of the restore operation. + SnapshotOperationMode defines the kind of the clone operation. * `DryRun`: DryRun run without any changes. Compatibility shows in status. - * `Strict`: Strict restore as is in the snapshot. - * `BestEffort`: BestEffort restore without deleted external missing dependencies. + * `Strict`: Strict clone as is in the snapshot. + * `BestEffort`: BestEffort process without deleted external missing dependencies. enum: - DryRun - Strict @@ -163,10 +163,10 @@ spec: properties: mode: description: |- - VMOPRestoreMode defines the kind of the restore operation. + SnapshotOperationMode defines the kind of the clone operation. * `DryRun`: DryRun run without any changes. Compatibility shows in status. - * `Strict`: Strict restore as is in the snapshot. - * `BestEffort`: BestEffort restore without deleted external missing dependencies. + * `Strict`: Strict clone as is in the snapshot. + * `BestEffort`: BestEffort process without deleted external missing dependencies. enum: - DryRun - Strict @@ -309,8 +309,8 @@ spec: by the snapshot operation. items: description: - VirtualMachineOperationResource defines the resource - affected by the operation. + SnapshotResourceStatus defines the resource affected + by the operation. properties: apiVersion: description: API version of the resource. diff --git a/crds/virtualmachinesnapshotoperations.yaml b/crds/virtualmachinesnapshotoperations.yaml new file mode 100644 index 0000000000..ab557ff723 --- /dev/null +++ b/crds/virtualmachinesnapshotoperations.yaml @@ -0,0 +1,303 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + labels: + heritage: deckhouse + module: virtualization + name: virtualmachinesnapshotoperations.virtualization.deckhouse.io +spec: + group: virtualization.deckhouse.io + names: + categories: + - virtualization + kind: VirtualMachineSnapshotOperation + listKind: VirtualMachineSnapshotOperationList + plural: virtualmachinesnapshotoperations + shortNames: + - vmsop + singular: virtualmachinesnapshotoperation + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: VirtualMachineSnapshotOperation phase. + jsonPath: .status.phase + name: Phase + type: string + - description: VirtualMachineSnapshotOperation type. + jsonPath: .spec.type + name: Type + type: string + - description: VirtualMachineSnapshot name. + jsonPath: .spec.virtualMachineSnapshotName + name: VirtualMachineSnapshot + type: string + - description: Time of resource creation. + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: + VirtualMachineSnapshotOperation enables declarative management + of virtual machine snapshot state changes. + 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: + createVirtualMachine: + description: CreateVirtualMachine defines the clone operation. + properties: + customization: + description: Customization defines customization options for cloning. + properties: + namePrefix: + description: |- + NamePrefix adds a prefix to resource names during cloning. + Applied to VirtualMachine, VirtualDisk, VirtualMachineBlockDeviceAttachment, and Secret resources. + type: string + nameSuffix: + description: |- + NameSuffix adds a suffix to resource names during cloning. + Applied to VirtualMachine, VirtualDisk, VirtualMachineBlockDeviceAttachment, and Secret resources. + type: string + type: object + x-kubernetes-validations: + - message: + namePrefix length must be between 1 and 59 characters + if set + rule: + "!has(self.namePrefix) || (size(self.namePrefix) >= 1 + && size(self.namePrefix) <= 59)" + - message: + nameSuffix length must be between 1 and 59 characters + if set + rule: + "!has(self.nameSuffix) || (size(self.nameSuffix) >= 1 + && size(self.nameSuffix) <= 59)" + mode: + description: |- + SnapshotOperationMode defines the kind of the clone operation. + * `DryRun`: DryRun run without any changes. Compatibility shows in status. + * `Strict`: Strict clone as is in the snapshot. + * `BestEffort`: BestEffort process without deleted external missing dependencies. + enum: + - DryRun + - Strict + - BestEffort + type: string + nameReplacement: + description: + NameReplacement defines rules for renaming resources + during cloning. + items: + description: + NameReplacement represents a rule for redefining + the virtual machine resource names. + properties: + from: + description: Selector to choose resources for name replacement. + properties: + kind: + description: Kind of a resource to rename. + type: string + name: + description: Current name of a resource to rename. + type: string + required: + - name + type: object + to: + description: New resource name. + type: string + required: + - from + - to + type: object + type: array + x-kubernetes-validations: + - message: Each nameReplacement.to must be between 1 and 59 characters + rule: + self.all(nr, has(nr.to) && size(nr.to) >= 1 && size(nr.to) + <= 59) + required: + - mode + type: object + x-kubernetes-validations: + - message: + At least one of customization.namePrefix, customization.nameSuffix, + or nameReplacement must be set + rule: + (has(self.customization) && ((has(self.customization.namePrefix) + && size(self.customization.namePrefix) > 0) || (has(self.customization.nameSuffix) + && size(self.customization.nameSuffix) > 0))) || (has(self.nameReplacement) + && size(self.nameReplacement) > 0) + type: + description: |- + Type of the operation to execute on a virtual machine: + * `CreateVirtualMachine`: CreateVirtualMachine the virtual machine to a new virtual machine. + enum: + - CreateVirtualMachine + type: string + virtualMachineSnapshotName: + description: + Name of the virtual machine snapshot the operation is + performed for. + type: string + required: + - type + - virtualMachineSnapshotName + type: object + x-kubernetes-validations: + - message: .spec is immutable + rule: self == oldSelf + - message: CreateVirtualMachineName requires clone field. + rule: + "self.type == 'CreateVirtualMachineName' ? has(self.createVirtualMachine) + : true" + status: + properties: + conditions: + description: + The latest detailed observations of the VirtualMachineSnapshotOperation + resource. + items: + description: + Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + observedGeneration: + description: " Resource generation last processed by the controller." + format: int64 + type: integer + phase: + description: |- + Current phase of the resource: + * `Pending`: The operation is queued for execution. + * `InProgress`: The operation is in progress. + * `Completed`: The operation has been completed successfully. + * `Failed`: The operation failed. For details, refer to the `conditions` field and events. + * `Terminating`: The operation is being deleted. + enum: + - Pending + - InProgress + - Completed + - Failed + - Terminating + type: string + resources: + description: + Resources contains the list of resources that are affected + by the snapshot operation. + items: + description: + SnapshotResourceStatus defines the resource affected + by the operation. + properties: + apiVersion: + description: API version of the resource. + type: string + kind: + description: Kind of the resource. + type: string + message: + description: Message about the resource. + type: string + name: + description: Name of the resource. + type: string + status: + description: Status of the resource. + enum: + - InProgress + - Completed + - Failed + type: string + required: + - apiVersion + - kind + - message + - name + - status + type: object + type: array + required: + - phase + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/templates/virtualization-controller/rbac-for-us.yaml b/templates/virtualization-controller/rbac-for-us.yaml index 5355971813..5c554957d5 100644 --- a/templates/virtualization-controller/rbac-for-us.yaml +++ b/templates/virtualization-controller/rbac-for-us.yaml @@ -187,6 +187,7 @@ rules: - virtualmachines - clustervirtualimages - virtualmachineoperations + - virtualmachinesnapshotoperations - virtualmachineclasses - virtualdisksnapshots - virtualmachinesnapshots @@ -212,6 +213,7 @@ rules: - virtualmachinemacaddressleases/finalizers - virtualmachinemacaddresses/finalizers - virtualmachineoperations/finalizers + - virtualmachinesnapshotoperations/finalizers - virtualmachineclasses/finalizers - virtualdisksnapshots/finalizers - virtualmachinesnapshots/finalizers @@ -226,6 +228,7 @@ rules: - virtualmachines/status - clustervirtualimages/status - virtualmachineoperations/status + - virtualmachinesnapshotoperations/status - virtualmachineclasses/status - virtualdisksnapshots/status - virtualmachinesnapshots/status From edd8c2c8534e17b5338cdedacfc9a6622908d61d Mon Sep 17 00:00:00 2001 From: Daniil Antoshin Date: Fri, 21 Nov 2025 12:16:30 +0200 Subject: [PATCH 2/5] feat(vmop): separate snapshot resources Signed-off-by: Daniil Antoshin --- .../service/restorer/restorers/vd_restorer.go | 23 +------ .../service/restorer/restorers/vm_restorer.go | 14 +++-- .../restorer/restorers/vm_restorer_test.go | 4 +- .../restorer/restorers/vmbda_restorer_test.go | 10 +-- .../restorer/restorers/vmip_restorer_test.go | 18 +++--- .../restorer/restorers/vmmac_restorer_test.go | 18 +++--- .../service/restorer/snapshot_resources.go | 62 ++++++++++++------- .../vmop/snapshot/internal/common/common.go | 14 ----- .../vmop/snapshot/internal/service/restore.go | 2 +- .../internal/step/cleanup_snapshot_step.go | 2 +- .../internal/step/enter_maintenance_step.go | 2 +- .../internal/step/exit_maintenance_step.go | 4 +- .../internal/step/process_clone_step.go | 16 +++-- .../snapshot/internal/step/process_step.go | 8 +-- 14 files changed, 92 insertions(+), 105 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vd_restorer.go b/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vd_restorer.go index a47326ffe0..36fb96c3fd 100644 --- a/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vd_restorer.go +++ b/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vd_restorer.go @@ -20,7 +20,6 @@ import ( "context" "fmt" - apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -142,25 +141,7 @@ func (v *VirtualDiskHandler) ProcessRestore(ctx context.Context) error { } if vdObj != nil { - if value, ok := vdObj.Annotations[annotations.AnnVMOPRestore]; ok && value == v.restoreUID { - return nil - } - - if vdObj.Annotations == nil { - vdObj.Annotations = make(map[string]string) - } - vdObj.Annotations[annotations.AnnVMOPRestoreDeleted] = v.restoreUID - - // Phase 1: Set annotation to trigger find right VMOP for reconciliation - err := v.client.Update(ctx, vdObj) - if err != nil { - if apierrors.IsConflict(err) { - return fmt.Errorf("waiting for the `VirtualDisk` %w", common.ErrUpdating) - } - return fmt.Errorf("failed to update the `VirtualDisk`: %w", err) - } - - // Phase 2: Initiate deletion and wait for completion + // Phase 1: Initiate deletion and wait for completion if !object.IsTerminating(vdObj) { err := v.client.Delete(ctx, vdObj) if err != nil { @@ -168,7 +149,7 @@ func (v *VirtualDiskHandler) ProcessRestore(ctx context.Context) error { } } - // Phase 3: Wait for deletion to complete before creating new disk + // Phase 2: Wait for deletion to complete before creating new disk return fmt.Errorf("waiting for deletion of VirtualDisk %s %w", vdObj.Name, common.ErrWaitingForDeletion) } else { err = v.client.Create(ctx, v.vd) diff --git a/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vm_restorer.go b/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vm_restorer.go index 61c25d46b4..937fec7c08 100644 --- a/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vm_restorer.go +++ b/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vm_restorer.go @@ -40,10 +40,10 @@ type VirtualMachineHandler struct { vm *v1alpha2.VirtualMachine client client.Client restoreUID string - mode v1alpha2.VMOPRestoreMode + mode v1alpha2.SnapshotOperationMode } -func NewVirtualMachineHandler(client client.Client, vmTmpl v1alpha2.VirtualMachine, vmopRestoreUID string, mode v1alpha2.VMOPRestoreMode) *VirtualMachineHandler { +func NewVirtualMachineHandler(client client.Client, vmTmpl v1alpha2.VirtualMachine, vmopRestoreUID string, mode v1alpha2.SnapshotOperationMode) *VirtualMachineHandler { if vmTmpl.Annotations != nil { vmTmpl.Annotations[annotations.AnnVMOPRestore] = vmopRestoreUID } else { @@ -267,6 +267,11 @@ func (v *VirtualMachineHandler) validateImageDependencies(ctx context.Context) e continue } + if v.mode == v1alpha2.SnapshotOperationModeStrict { + filteredRefs = append(filteredRefs, ref) + continue + } + exists, err := v.imageExists(ctx, ref) if err != nil { return err @@ -302,10 +307,7 @@ func (v *VirtualMachineHandler) imageExists(ctx context.Context, ref v1alpha2.Bl } if fetchedObj == nil { - if v.mode == v1alpha2.VMOPRestoreModeBestEffort { - return false, nil - } - return false, fmt.Errorf("%s %q not found", ref.Kind, ref.Name) + return false, nil } return true, nil diff --git a/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vm_restorer_test.go b/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vm_restorer_test.go index 146192076b..35267c7eb7 100644 --- a/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vm_restorer_test.go +++ b/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vm_restorer_test.go @@ -214,7 +214,7 @@ var _ = Describe("VirtualMachineRestorer", func() { Expect(err).ToNot(HaveOccurred()) Expect(fakeClient).ToNot(BeNil()) - handler = NewVirtualMachineHandler(fakeClient, vm, restoreUID, v1alpha2.VMOPRestoreModeStrict) + handler = NewVirtualMachineHandler(fakeClient, vm, restoreUID, v1alpha2.SnapshotOperationModeStrict) Expect(handler).ToNot(BeNil()) // Verify that restore annotation was added @@ -375,7 +375,7 @@ var _ = Describe("VirtualMachineRestorer", func() { fakeClient, err = testutil.NewFakeClientWithInterceptorWithObjects(intercept) Expect(err).ToNot(HaveOccurred()) - handler = NewVirtualMachineHandler(fakeClient, vm, restoreUID, v1alpha2.VMOPRestoreModeStrict) + handler = NewVirtualMachineHandler(fakeClient, vm, restoreUID, v1alpha2.SnapshotOperationModeStrict) }) It("should override VM name", func() { diff --git a/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vmbda_restorer_test.go b/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vmbda_restorer_test.go index 496ac430bf..2c7d83c7c8 100644 --- a/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vmbda_restorer_test.go +++ b/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vmbda_restorer_test.go @@ -32,7 +32,7 @@ import ( ) type VMBlockDeviceAttachmentTestArgs struct { - mode v1alpha2.VMOPRestoreMode + mode v1alpha2.SnapshotOperationMode vmbdaExists bool vmbdaUsedByDiffVM bool @@ -140,7 +140,7 @@ var _ = Describe("VMBlockDeviceAttachmentRestorer", func() { Expect(vmbdaCreated).To(Equal(args.shouldBeCreated)) }, Entry("vmbda exists; used by different VM", VMBlockDeviceAttachmentTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmbdaExists: true, vmbdaUsedByDiffVM: true, @@ -152,7 +152,7 @@ var _ = Describe("VMBlockDeviceAttachmentRestorer", func() { shouldBeCreated: false, }), Entry("vmbda exists; not used by different VM", VMBlockDeviceAttachmentTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmbdaExists: true, vmbdaUsedByDiffVM: false, @@ -164,7 +164,7 @@ var _ = Describe("VMBlockDeviceAttachmentRestorer", func() { shouldBeCreated: false, }), Entry("vmbda doesn't exist", VMBlockDeviceAttachmentTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmbdaExists: false, vmbdaUsedByDiffVM: false, @@ -176,7 +176,7 @@ var _ = Describe("VMBlockDeviceAttachmentRestorer", func() { shouldBeCreated: true, }), Entry("vmbda deletion completed; ready to create", VMBlockDeviceAttachmentTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmbdaExists: false, vmbdaUsedByDiffVM: false, diff --git a/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vmip_restorer_test.go b/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vmip_restorer_test.go index e3d1675501..e4fe4225ed 100644 --- a/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vmip_restorer_test.go +++ b/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vmip_restorer_test.go @@ -30,7 +30,7 @@ import ( ) type VMIPTestArgs struct { - mode v1alpha2.VMOPRestoreMode + mode v1alpha2.SnapshotOperationMode vmipType v1alpha2.VirtualMachineIPAddressType vmipExists bool @@ -171,7 +171,7 @@ var _ = Describe("VirtualMachineIPAddressRestorer", func() { Expect(vmipCreated).To(Equal(args.shouldBeCreated)) }, Entry("vmip exists; vmip has Auto type; vmip used by different VM", VMIPTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmipExists: true, vmipType: v1alpha2.VirtualMachineIPAddressTypeAuto, vmipUsedByDiffVM: true, @@ -184,7 +184,7 @@ var _ = Describe("VirtualMachineIPAddressRestorer", func() { shouldBeCreated: false, }), Entry("vmip exists; vmip has Auto type; vmip doesn't used by different VM", VMIPTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmipExists: true, vmipType: v1alpha2.VirtualMachineIPAddressTypeAuto, vmipUsedByDiffVM: false, @@ -197,7 +197,7 @@ var _ = Describe("VirtualMachineIPAddressRestorer", func() { shouldBeCreated: false, }), Entry("vmip exists; vmip has StaticIP type; vmip used by different VM", VMIPTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmipExists: true, vmipType: v1alpha2.VirtualMachineIPAddressTypeStatic, vmipUsedByDiffVM: true, @@ -210,7 +210,7 @@ var _ = Describe("VirtualMachineIPAddressRestorer", func() { shouldBeCreated: false, }), Entry("vmip exists; vmip has StaticIP type; staticIP used by different VM", VMIPTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmipExists: true, vmipType: v1alpha2.VirtualMachineIPAddressTypeStatic, vmipUsedByDiffVM: false, @@ -223,7 +223,7 @@ var _ = Describe("VirtualMachineIPAddressRestorer", func() { shouldBeCreated: false, }), Entry("vmip exists; vmip has StaticIP type; vmip doesn't used by different VM", VMIPTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmipExists: true, vmipType: v1alpha2.VirtualMachineIPAddressTypeStatic, vmipUsedByDiffVM: false, @@ -237,7 +237,7 @@ var _ = Describe("VirtualMachineIPAddressRestorer", func() { }), Entry("vmip doesn't exist; vmip has Auto type", VMIPTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmipExists: false, vmipType: v1alpha2.VirtualMachineIPAddressTypeAuto, vmipUsedByDiffVM: false, @@ -250,7 +250,7 @@ var _ = Describe("VirtualMachineIPAddressRestorer", func() { shouldBeCreated: true, }), Entry("vmip doesn't exist; vmip has StaticIP type; staticIP used by different VM", VMIPTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmipExists: false, vmipType: v1alpha2.VirtualMachineIPAddressTypeStatic, vmipUsedByDiffVM: false, @@ -263,7 +263,7 @@ var _ = Describe("VirtualMachineIPAddressRestorer", func() { shouldBeCreated: false, }), Entry("vmip doesn't exist; vmip has StaticIP type; staticIP doesn't used by different VM", VMIPTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmipExists: false, vmipType: v1alpha2.VirtualMachineIPAddressTypeStatic, vmipUsedByDiffVM: false, diff --git a/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vmmac_restorer_test.go b/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vmmac_restorer_test.go index ef141c4c9f..b98551050f 100644 --- a/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vmmac_restorer_test.go +++ b/images/virtualization-artifact/pkg/controller/service/restorer/restorers/vmmac_restorer_test.go @@ -30,7 +30,7 @@ import ( ) type VMMACTestArgs struct { - mode v1alpha2.VMOPRestoreMode + mode v1alpha2.SnapshotOperationMode vmmacExists bool vmmacUsedByDiffVM bool @@ -167,7 +167,7 @@ var _ = Describe("VirtualMachineMACAddressRestorer", func() { Expect(vmmacCreated).To(Equal(args.shouldBeCreated)) }, Entry("vmmac exists; vmmac has auto address; vmmac used by different VM", VMMACTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmmacExists: true, hasAddress: false, vmmacUsedByDiffVM: true, @@ -180,7 +180,7 @@ var _ = Describe("VirtualMachineMACAddressRestorer", func() { shouldBeCreated: false, }), Entry("vmmac exists; vmmac has auto address; vmmac doesn't used by different VM", VMMACTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmmacExists: true, hasAddress: false, vmmacUsedByDiffVM: false, @@ -193,7 +193,7 @@ var _ = Describe("VirtualMachineMACAddressRestorer", func() { shouldBeCreated: false, }), Entry("vmmac exists; vmmac has address; vmmac used by different VM", VMMACTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmmacExists: true, hasAddress: true, vmmacUsedByDiffVM: true, @@ -206,7 +206,7 @@ var _ = Describe("VirtualMachineMACAddressRestorer", func() { shouldBeCreated: false, }), Entry("vmmac exists; vmmac has address; address used by different VM", VMMACTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmmacExists: true, hasAddress: true, vmmacUsedByDiffVM: false, @@ -219,7 +219,7 @@ var _ = Describe("VirtualMachineMACAddressRestorer", func() { shouldBeCreated: false, }), Entry("vmmac exists; vmmac has address; vmmac doesn't used by different VM", VMMACTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmmacExists: true, hasAddress: true, vmmacUsedByDiffVM: false, @@ -233,7 +233,7 @@ var _ = Describe("VirtualMachineMACAddressRestorer", func() { }), Entry("vmmac doesn't exist; vmmac has auto address", VMMACTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmmacExists: false, hasAddress: false, vmmacUsedByDiffVM: false, @@ -246,7 +246,7 @@ var _ = Describe("VirtualMachineMACAddressRestorer", func() { shouldBeCreated: true, }), Entry("vmmac doesn't exist; vmmac has address; address used by different VM", VMMACTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmmacExists: false, hasAddress: true, vmmacUsedByDiffVM: false, @@ -259,7 +259,7 @@ var _ = Describe("VirtualMachineMACAddressRestorer", func() { shouldBeCreated: false, }), Entry("vmmac doesn't exist; vmmac has address; address doesn't used by different VM", VMMACTestArgs{ - mode: v1alpha2.VMOPRestoreModeStrict, + mode: v1alpha2.SnapshotOperationModeStrict, vmmacExists: false, hasAddress: true, vmmacUsedByDiffVM: false, diff --git a/images/virtualization-artifact/pkg/controller/service/restorer/snapshot_resources.go b/images/virtualization-artifact/pkg/controller/service/restorer/snapshot_resources.go index 9fa43bae04..fdb401e470 100644 --- a/images/virtualization-artifact/pkg/controller/service/restorer/snapshot_resources.go +++ b/images/virtualization-artifact/pkg/controller/service/restorer/snapshot_resources.go @@ -33,6 +33,22 @@ import ( "github.com/deckhouse/virtualization/api/core/v1alpha2" ) +type ResourceStatusPhase string + +const ( + ResourceStatusInProgress ResourceStatusPhase = "InProgress" + ResourceStatusCompleted ResourceStatusPhase = "Completed" + ResourceStatusFailed ResourceStatusPhase = "Failed" +) + +type SnapshotResourceStatus struct { + APIVersion string + Kind string + Name string + Status ResourceStatusPhase + Message string +} + type SnapshotResources struct { uuid string client client.Client @@ -40,12 +56,12 @@ type SnapshotResources struct { restorerSecret *corev1.Secret vmSnapshot *v1alpha2.VirtualMachineSnapshot objectHandlers []ObjectHandler - statuses []v1alpha2.VirtualMachineOperationResource - mode v1alpha2.VMOPRestoreMode + statuses []v1alpha2.SnapshotResourceStatus + mode v1alpha2.SnapshotOperationMode kind v1alpha2.VMOPType } -func NewSnapshotResources(client client.Client, kind v1alpha2.VMOPType, mode v1alpha2.VMOPRestoreMode, restorerSecret *corev1.Secret, vmSnapshot *v1alpha2.VirtualMachineSnapshot, uuid string) SnapshotResources { +func NewSnapshotResources(client client.Client, kind v1alpha2.VMOPType, mode v1alpha2.SnapshotOperationMode, restorerSecret *corev1.Secret, vmSnapshot *v1alpha2.VirtualMachineSnapshot, uuid string) SnapshotResources { return SnapshotResources{ mode: mode, kind: kind, @@ -58,6 +74,10 @@ func NewSnapshotResources(client client.Client, kind v1alpha2.VMOPType, mode v1a } func (r *SnapshotResources) Prepare(ctx context.Context) error { + if r.restorerSecret == nil { + return fmt.Errorf("restorer secret %q is not found", r.restorerSecret.Name) + } + provisioner, err := r.restorer.RestoreProvisioner(ctx, r.restorerSecret) if err != nil { return err @@ -153,19 +173,19 @@ func (r *SnapshotResources) Customize(prefix, suffix string) { } } -func (r *SnapshotResources) Validate(ctx context.Context) ([]v1alpha2.VirtualMachineOperationResource, error) { +func (r *SnapshotResources) Validate(ctx context.Context) ([]v1alpha2.SnapshotResourceStatus, error) { var hasErrors bool - r.statuses = make([]v1alpha2.VirtualMachineOperationResource, 0, len(r.objectHandlers)) + r.statuses = make([]v1alpha2.SnapshotResourceStatus, 0, len(r.objectHandlers)) for _, ov := range r.objectHandlers { obj := ov.Object() - status := v1alpha2.VirtualMachineOperationResource{ + status := v1alpha2.SnapshotResourceStatus{ APIVersion: obj.GetObjectKind().GroupVersionKind().Version, Kind: obj.GetObjectKind().GroupVersionKind().Kind, Name: obj.GetName(), - Status: v1alpha2.VMOPResourceStatusCompleted, + Status: v1alpha2.SnapshotResourceStatusCompleted, Message: obj.GetName() + " is valid for restore", } @@ -177,14 +197,14 @@ func (r *SnapshotResources) Validate(ctx context.Context) ([]v1alpha2.VirtualMac case shouldIgnoreError(r.mode, err): default: hasErrors = true - status.Status = v1alpha2.VMOPResourceStatusFailed + status.Status = v1alpha2.SnapshotResourceStatusFailed status.Message = err.Error() } case v1alpha2.VMOPTypeClone: err := ov.ValidateClone(ctx) if err != nil { hasErrors = true - status.Status = v1alpha2.VMOPResourceStatusFailed + status.Status = v1alpha2.SnapshotResourceStatusFailed status.Message = err.Error() } } @@ -198,23 +218,23 @@ func (r *SnapshotResources) Validate(ctx context.Context) ([]v1alpha2.VirtualMac return r.statuses, nil } -func (r *SnapshotResources) Process(ctx context.Context) ([]v1alpha2.VirtualMachineOperationResource, error) { +func (r *SnapshotResources) Process(ctx context.Context) ([]v1alpha2.SnapshotResourceStatus, error) { var hasErrors bool - r.statuses = make([]v1alpha2.VirtualMachineOperationResource, 0, len(r.objectHandlers)) + r.statuses = make([]v1alpha2.SnapshotResourceStatus, 0, len(r.objectHandlers)) - if r.mode == v1alpha2.VMOPRestoreModeDryRun { + if r.mode == v1alpha2.SnapshotOperationModeDryRun { return r.statuses, errors.New("cannot Process with DryRun operation") } for _, ov := range r.objectHandlers { obj := ov.Object() - status := v1alpha2.VirtualMachineOperationResource{ + status := v1alpha2.SnapshotResourceStatus{ APIVersion: obj.GetObjectKind().GroupVersionKind().Version, Kind: obj.GetObjectKind().GroupVersionKind().Kind, Name: obj.GetName(), - Status: v1alpha2.VMOPResourceStatusCompleted, + Status: v1alpha2.SnapshotResourceStatusCompleted, Message: "Successfully processed", } @@ -225,11 +245,11 @@ func (r *SnapshotResources) Process(ctx context.Context) ([]v1alpha2.VirtualMach case err == nil: case shouldIgnoreError(r.mode, err): case isRetryError(err): - status.Status = v1alpha2.VMOPResourceStatusInProgress + status.Status = v1alpha2.SnapshotResourceStatusInProgress status.Message = err.Error() default: hasErrors = true - status.Status = v1alpha2.VMOPResourceStatusFailed + status.Status = v1alpha2.SnapshotResourceStatusFailed status.Message = err.Error() } case v1alpha2.VMOPTypeClone: @@ -237,11 +257,11 @@ func (r *SnapshotResources) Process(ctx context.Context) ([]v1alpha2.VirtualMach switch { case err == nil: case isRetryError(err): - status.Status = v1alpha2.VMOPResourceStatusInProgress + status.Status = v1alpha2.SnapshotResourceStatusInProgress status.Message = err.Error() default: hasErrors = true - status.Status = v1alpha2.VMOPResourceStatusFailed + status.Status = v1alpha2.SnapshotResourceStatusFailed status.Message = err.Error() } } @@ -272,15 +292,15 @@ var RetryErrors = []error{ common.ErrWaitingForDeletion, } -func shouldIgnoreError(mode v1alpha2.VMOPRestoreMode, err error) bool { +func shouldIgnoreError(mode v1alpha2.SnapshotOperationMode, err error) bool { switch mode { - case v1alpha2.VMOPRestoreModeDryRun: + case v1alpha2.SnapshotOperationModeDryRun: for _, e := range DryRunIgnoredErrors { if errors.Is(err, e) { return true } } - case v1alpha2.VMOPRestoreModeBestEffort: + case v1alpha2.SnapshotOperationModeBestEffort: for _, e := range BestEffortIgnoredErrors { if errors.Is(err, e) { return true diff --git a/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/common/common.go b/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/common/common.go index e5ef18f051..b01e6ab86e 100644 --- a/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/common/common.go +++ b/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/common/common.go @@ -48,17 +48,3 @@ func SetPhaseConditionCompleted(cb *conditions.ConditionBuilder, phase *v1alpha2 Reason(reason). Message(service.CapitalizeFirstLetter(msg) + ".") } - -func FillResourcesStatuses(vmop *v1alpha2.VirtualMachineOperation, statuses []v1alpha2.VirtualMachineOperationResource) { - vmop.Status.Resources = nil - - for _, status := range statuses { - vmop.Status.Resources = append(vmop.Status.Resources, v1alpha2.VirtualMachineOperationResource{ - APIVersion: status.APIVersion, - Kind: status.Kind, - Name: status.Name, - Message: status.Message, - Status: status.Status, - }) - } -} diff --git a/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/service/restore.go b/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/service/restore.go index e8053c9449..ea7fd58d76 100644 --- a/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/service/restore.go +++ b/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/service/restore.go @@ -128,7 +128,7 @@ func (o RestoreOperation) IsComplete() (bool, string) { return false, "" } - if o.vmop.Spec.Restore.Mode == v1alpha2.VMOPRestoreModeDryRun { + if o.vmop.Spec.Restore.Mode == v1alpha2.SnapshotOperationModeDryRun { return rc.Status == metav1.ConditionTrue, "" } diff --git a/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/cleanup_snapshot_step.go b/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/cleanup_snapshot_step.go index ba2519cd84..9871655a2e 100644 --- a/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/cleanup_snapshot_step.go +++ b/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/cleanup_snapshot_step.go @@ -71,7 +71,7 @@ func (s CleanupSnapshotStep) Take(ctx context.Context, vmop *v1alpha2.VirtualMac } for _, status := range vmop.Status.Resources { - if status.Status == v1alpha2.VMOPResourceStatusInProgress { + if status.Status == v1alpha2.SnapshotResourceStatusInProgress { return nil, nil } } diff --git a/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/enter_maintenance_step.go b/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/enter_maintenance_step.go index 04fdd321b9..923bf715ed 100644 --- a/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/enter_maintenance_step.go +++ b/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/enter_maintenance_step.go @@ -55,7 +55,7 @@ func NewEnterMaintenanceStep( } func (s EnterMaintenanceStep) Take(ctx context.Context, vmop *v1alpha2.VirtualMachineOperation) (*reconcile.Result, error) { - if vmop.Spec.Restore.Mode == v1alpha2.VMOPRestoreModeDryRun { + if vmop.Spec.Restore.Mode == v1alpha2.SnapshotOperationModeDryRun { return nil, nil } diff --git a/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/exit_maintenance_step.go b/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/exit_maintenance_step.go index 57aa6f3b20..3dd48d890e 100644 --- a/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/exit_maintenance_step.go +++ b/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/exit_maintenance_step.go @@ -55,7 +55,7 @@ func NewExitMaintenanceStep( } func (s ExitMaintenanceStep) Take(ctx context.Context, vmop *v1alpha2.VirtualMachineOperation) (*reconcile.Result, error) { - if vmop.Spec.Restore.Mode == v1alpha2.VMOPRestoreModeDryRun { + if vmop.Spec.Restore.Mode == v1alpha2.SnapshotOperationModeDryRun { return &reconcile.Result{}, nil } @@ -81,7 +81,7 @@ func (s ExitMaintenanceStep) Take(ctx context.Context, vmop *v1alpha2.VirtualMac } for _, status := range vmop.Status.Resources { - if status.Status == v1alpha2.VMOPResourceStatusInProgress { + if status.Status == v1alpha2.SnapshotResourceStatusInProgress { return &reconcile.Result{}, nil } } diff --git a/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/process_clone_step.go b/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/process_clone_step.go index 08dcb57d9c..2f16ad2ee5 100644 --- a/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/process_clone_step.go +++ b/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/process_clone_step.go @@ -89,7 +89,7 @@ func (s ProcessCloneStep) Take(ctx context.Context, vmop *v1alpha2.VirtualMachin return &reconcile.Result{}, err } - snapshotResources := restorer.NewSnapshotResources(s.client, v1alpha2.VMOPTypeClone, v1alpha2.VMOPRestoreModeStrict, restorerSecret, vmSnapshot, string(vmop.UID)) + snapshotResources := restorer.NewSnapshotResources(s.client, v1alpha2.VMOPTypeClone, vmop.Spec.Clone.Mode, restorerSecret, vmSnapshot, string(vmop.UID)) err = snapshotResources.Prepare(ctx) if err != nil { @@ -107,19 +107,19 @@ func (s ProcessCloneStep) Take(ctx context.Context, vmop *v1alpha2.VirtualMachin } statuses, err := snapshotResources.Validate(ctx) - common.FillResourcesStatuses(vmop, statuses) + vmop.Status.Resources = statuses if err != nil { s.cb.Status(metav1.ConditionFalse).Reason(vmopcondition.ReasonCloneOperationFailed).Message(service.CapitalizeFirstLetter(err.Error())) return &reconcile.Result{}, nil } - if vmop.Spec.Clone.Mode == v1alpha2.VMOPRestoreModeDryRun { + if vmop.Spec.Clone.Mode == v1alpha2.SnapshotOperationModeDryRun { s.cb.Status(metav1.ConditionTrue).Reason(vmopcondition.ReasonCloneOperationCompleted).Message("The virtual machine can be cloned from the snapshot.") return &reconcile.Result{}, nil } statuses, err = snapshotResources.Process(ctx) - common.FillResourcesStatuses(vmop, statuses) + vmop.Status.Resources = statuses if err != nil { common.SetPhaseCloneConditionToFailed(s.cb, &vmop.Status.Phase, err) return &reconcile.Result{}, err @@ -142,15 +142,13 @@ func (s ProcessCloneStep) Take(ctx context.Context, vmop *v1alpha2.VirtualMachin } if vd.Status.Phase == v1alpha2.DiskFailed { - conditions.SetCondition( - s.cb.Status(metav1.ConditionFalse).Reason(vmopcondition.ReasonSnapshotFailed).Message("Snapshot is failed."), - &vmop.Status.Conditions, - ) + err = fmt.Errorf("virtual disk %q is in failed phase", vdKey.Name) + conditions.SetCondition(s.cb.Status(metav1.ConditionFalse).Reason(vmopcondition.ReasonSnapshotFailed).Message(service.CapitalizeFirstLetter(err.Error())), &vmop.Status.Conditions) common.SetPhaseCloneConditionToFailed(s.cb, &vmop.Status.Phase, err) return &reconcile.Result{}, fmt.Errorf("virtual disk %q is in failed phase", vdKey.Name) } - if vd.Status.Phase != v1alpha2.DiskReady { + if vd.Status.Phase != v1alpha2.DiskReady && vd.Status.Phase != v1alpha2.DiskWaitForFirstConsumer { return &reconcile.Result{}, nil } } diff --git a/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/process_step.go b/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/process_step.go index d86237cd10..eff8387e24 100644 --- a/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/process_step.go +++ b/images/virtualization-artifact/pkg/controller/vmop/snapshot/internal/step/process_step.go @@ -88,27 +88,27 @@ func (s ProcessRestoreStep) Take(ctx context.Context, vmop *v1alpha2.VirtualMach } statuses, err := snapshotResources.Validate(ctx) - common.FillResourcesStatuses(vmop, statuses) + vmop.Status.Resources = statuses if err != nil { common.SetPhaseConditionToFailed(s.cb, &vmop.Status.Phase, err) return &reconcile.Result{}, err } - if vmop.Spec.Restore.Mode == v1alpha2.VMOPRestoreModeDryRun { + if vmop.Spec.Restore.Mode == v1alpha2.SnapshotOperationModeDryRun { s.cb.Status(metav1.ConditionTrue).Reason(vmopcondition.ReasonRestoreOperationCompleted).Message("The virtual machine can be restored from the snapshot.") common.SetPhaseConditionCompleted(s.cb, &vmop.Status.Phase, vmopcondition.ReasonDryRunOperationCompleted, "The virtual machine can be restored from the snapshot") return &reconcile.Result{}, nil } statuses, err = snapshotResources.Process(ctx) - common.FillResourcesStatuses(vmop, statuses) + vmop.Status.Resources = statuses if err != nil { common.SetPhaseConditionToFailed(s.cb, &vmop.Status.Phase, err) return &reconcile.Result{}, err } for _, status := range statuses { - if status.Status == v1alpha2.VMOPResourceStatusInProgress { + if status.Status == v1alpha2.SnapshotResourceStatusInProgress { return &reconcile.Result{}, nil } } From 3955cf8269ce282ffa4bad54b75aac24160c1e73 Mon Sep 17 00:00:00 2001 From: Daniil Antoshin Date: Fri, 21 Nov 2025 12:17:21 +0200 Subject: [PATCH 3/5] feat(vmsop): e2e add builders Signed-off-by: Daniil Antoshin --- .../pkg/builder/vmsnapshot/option.go | 65 +++++++++++++++++++ .../pkg/builder/vmsnapshot/vdsnapshot.go | 51 +++++++++++++++ .../pkg/builder/vmsop/option.go | 54 +++++++++++++++ .../pkg/builder/vmsop/vmsop.go | 51 +++++++++++++++ 4 files changed, 221 insertions(+) create mode 100644 images/virtualization-artifact/pkg/builder/vmsnapshot/option.go create mode 100644 images/virtualization-artifact/pkg/builder/vmsnapshot/vdsnapshot.go create mode 100644 images/virtualization-artifact/pkg/builder/vmsop/option.go create mode 100644 images/virtualization-artifact/pkg/builder/vmsop/vmsop.go diff --git a/images/virtualization-artifact/pkg/builder/vmsnapshot/option.go b/images/virtualization-artifact/pkg/builder/vmsnapshot/option.go new file mode 100644 index 0000000000..c39fac9a13 --- /dev/null +++ b/images/virtualization-artifact/pkg/builder/vmsnapshot/option.go @@ -0,0 +1,65 @@ +/* +Copyright 2025 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vmsnapshot + +import ( + "github.com/deckhouse/virtualization-controller/pkg/builder/meta" + "github.com/deckhouse/virtualization/api/core/v1alpha2" +) + +type Option func(vdsnapshot *v1alpha2.VirtualMachineSnapshot) + +var ( + WithName = meta.WithName[*v1alpha2.VirtualMachineSnapshot] + WithNamespace = meta.WithNamespace[*v1alpha2.VirtualMachineSnapshot] + WithGenerateName = meta.WithGenerateName[*v1alpha2.VirtualMachineSnapshot] + WithLabel = meta.WithLabel[*v1alpha2.VirtualMachineSnapshot] + WithLabels = meta.WithLabels[*v1alpha2.VirtualMachineSnapshot] + WithAnnotation = meta.WithAnnotation[*v1alpha2.VirtualMachineSnapshot] + WithAnnotations = meta.WithAnnotations[*v1alpha2.VirtualMachineSnapshot] + WithFinalizer = meta.WithFinalizer[*v1alpha2.VirtualMachineSnapshot] +) + +func WithVirtualMachineName(vmName string) Option { + return func(vmsnapshot *v1alpha2.VirtualMachineSnapshot) { + vmsnapshot.Spec.VirtualMachineName = vmName + } +} + +func WithKeepIPAddress(keepIP v1alpha2.KeepIPAddress) Option { + return func(vmsnapshot *v1alpha2.VirtualMachineSnapshot) { + vmsnapshot.Spec.KeepIPAddress = keepIP + } +} + +func WithRequiredConsistency(requiredConsistency bool) Option { + return func(vmsnapshot *v1alpha2.VirtualMachineSnapshot) { + vmsnapshot.Spec.RequiredConsistency = requiredConsistency + } +} + +func WithVirtualMachineSnapshotSecretName(secretName string) Option { + return func(vmsnapshot *v1alpha2.VirtualMachineSnapshot) { + vmsnapshot.Status.VirtualMachineSnapshotSecretName = secretName + } +} + +func WithVirtualMachineSnapshotPhase(phase v1alpha2.VirtualMachineSnapshotPhase) Option { + return func(vmsnapshot *v1alpha2.VirtualMachineSnapshot) { + vmsnapshot.Status.Phase = phase + } +} diff --git a/images/virtualization-artifact/pkg/builder/vmsnapshot/vdsnapshot.go b/images/virtualization-artifact/pkg/builder/vmsnapshot/vdsnapshot.go new file mode 100644 index 0000000000..0cf19c0e6d --- /dev/null +++ b/images/virtualization-artifact/pkg/builder/vmsnapshot/vdsnapshot.go @@ -0,0 +1,51 @@ +/* +Copyright 2025 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vmsnapshot + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/deckhouse/virtualization/api/core/v1alpha2" +) + +func New(options ...Option) *v1alpha2.VirtualMachineSnapshot { + vdsnapshot := NewEmpty("", "") + ApplyOptions(vdsnapshot, options...) + return vdsnapshot +} + +func ApplyOptions(vmsnapshot *v1alpha2.VirtualMachineSnapshot, opts ...Option) { + if vmsnapshot == nil { + return + } + for _, opt := range opts { + opt(vmsnapshot) + } +} + +func NewEmpty(name, namespace string) *v1alpha2.VirtualMachineSnapshot { + return &v1alpha2.VirtualMachineSnapshot{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1alpha2.SchemeGroupVersion.String(), + Kind: v1alpha2.VirtualMachineSnapshotKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + } +} diff --git a/images/virtualization-artifact/pkg/builder/vmsop/option.go b/images/virtualization-artifact/pkg/builder/vmsop/option.go new file mode 100644 index 0000000000..4f06446639 --- /dev/null +++ b/images/virtualization-artifact/pkg/builder/vmsop/option.go @@ -0,0 +1,54 @@ +/* +Copyright 2025 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vmsop + +import ( + "github.com/deckhouse/virtualization-controller/pkg/builder/meta" + "github.com/deckhouse/virtualization/api/core/v1alpha2" +) + +type Option func(vmsop *v1alpha2.VirtualMachineSnapshotOperation) + +var ( + WithName = meta.WithName[*v1alpha2.VirtualMachineSnapshotOperation] + WithGenerateName = meta.WithGenerateName[*v1alpha2.VirtualMachineSnapshotOperation] + WithNamespace = meta.WithNamespace[*v1alpha2.VirtualMachineSnapshotOperation] + WithLabel = meta.WithLabel[*v1alpha2.VirtualMachineSnapshotOperation] + WithLabels = meta.WithLabels[*v1alpha2.VirtualMachineSnapshotOperation] + WithAnnotation = meta.WithAnnotation[*v1alpha2.VirtualMachineSnapshotOperation] + WithAnnotations = meta.WithAnnotations[*v1alpha2.VirtualMachineSnapshotOperation] + WithFinalizer = meta.WithFinalizer[*v1alpha2.VirtualMachineSnapshotOperation] +) + +func WithType(t v1alpha2.VMSOPType) Option { + return func(vmsop *v1alpha2.VirtualMachineSnapshotOperation) { + vmsop.Spec.Type = t + } +} + +func WithVirtualMachineSnapshotName(name string) Option { + return func(vmsop *v1alpha2.VirtualMachineSnapshotOperation) { + vmsop.Spec.VirtualMachineSnapshotName = name + } +} + +func WithCreateVirtualMachine(create *v1alpha2.VMSOPCreateVirtualMachineSpec) Option { + return func(vmsop *v1alpha2.VirtualMachineSnapshotOperation) { + vmsop.Spec.Type = v1alpha2.VMSOPTypeCreateVirtualMachine + vmsop.Spec.CreateVirtualMachine = create + } +} diff --git a/images/virtualization-artifact/pkg/builder/vmsop/vmsop.go b/images/virtualization-artifact/pkg/builder/vmsop/vmsop.go new file mode 100644 index 0000000000..4cc1a84616 --- /dev/null +++ b/images/virtualization-artifact/pkg/builder/vmsop/vmsop.go @@ -0,0 +1,51 @@ +/* +Copyright 2025 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vmsop + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/deckhouse/virtualization/api/core/v1alpha2" +) + +func New(options ...Option) *v1alpha2.VirtualMachineSnapshotOperation { + vmsop := NewEmpty("", "") + ApplyOptions(vmsop, options...) + return vmsop +} + +func ApplyOptions(vmsop *v1alpha2.VirtualMachineSnapshotOperation, opts ...Option) { + if vmsop == nil { + return + } + for _, opt := range opts { + opt(vmsop) + } +} + +func NewEmpty(name, namespace string) *v1alpha2.VirtualMachineSnapshotOperation { + return &v1alpha2.VirtualMachineSnapshotOperation{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1alpha2.SchemeGroupVersion.String(), + Kind: v1alpha2.VirtualMachineSnapshotOperationKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + } +} From 80649e1d6209f416009eb096fdc4115b8b69a084 Mon Sep 17 00:00:00 2001 From: Daniil Antoshin Date: Fri, 21 Nov 2025 15:23:22 +0200 Subject: [PATCH 4/5] fix year Signed-off-by: Daniil Antoshin --- api/core/v1alpha2/virtual_machine_snapshot_operation.go | 2 +- api/core/v1alpha2/vmsopcondition/condition.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/core/v1alpha2/virtual_machine_snapshot_operation.go b/api/core/v1alpha2/virtual_machine_snapshot_operation.go index 5df0791fe3..be60efcb94 100644 --- a/api/core/v1alpha2/virtual_machine_snapshot_operation.go +++ b/api/core/v1alpha2/virtual_machine_snapshot_operation.go @@ -1,5 +1,5 @@ /* -Copyright 2024 Flant JSC +Copyright 2025 Flant JSC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/core/v1alpha2/vmsopcondition/condition.go b/api/core/v1alpha2/vmsopcondition/condition.go index 79aa96c2e5..6d6c422052 100644 --- a/api/core/v1alpha2/vmsopcondition/condition.go +++ b/api/core/v1alpha2/vmsopcondition/condition.go @@ -1,5 +1,5 @@ /* -Copyright 2024 Flant JSC +Copyright 2025 Flant JSC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From e242eea46cdb5aa1bf71df5a2cb57421d1d9bd3f Mon Sep 17 00:00:00 2001 From: Daniil Antoshin Date: Fri, 21 Nov 2025 15:24:41 +0200 Subject: [PATCH 5/5] remove unused Signed-off-by: Daniil Antoshin --- api/core/v1alpha2/vmsopcondition/condition.go | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/api/core/v1alpha2/vmsopcondition/condition.go b/api/core/v1alpha2/vmsopcondition/condition.go index 6d6c422052..891b642196 100644 --- a/api/core/v1alpha2/vmsopcondition/condition.go +++ b/api/core/v1alpha2/vmsopcondition/condition.go @@ -25,9 +25,6 @@ func (t Type) String() string { const ( // TypeCompleted is a type for condition that indicates operation is complete. TypeCompleted Type = "Completed" - - // TypeCreateVirtualMachineCompleted is a type for condition that indicates success of clone. - TypeCreateVirtualMachineCompleted Type = "CreateVirtualMachineCompleted" ) // ReasonCompleted represents specific reasons for the 'Completed' condition type. @@ -56,21 +53,3 @@ const ( // ReasonOperationCompleted is a ReasonCompleted indicating that operation is completed. ReasonOperationCompleted ReasonCompleted = "OperationCompleted" ) - -// ReasonCreateVirtualMachineCompleted represents specific reasons for the 'CreateVirtualMachineCompleted' condition type. -type ReasonCreateVirtualMachineCompleted string - -func (r ReasonCreateVirtualMachineCompleted) String() string { - return string(r) -} - -const ( - // ReasonCreateVirtualMachineOperationInProgress is a ReasonCreateVirtualMachineCompleted indicating that the clone operation is in progress. - ReasonCreateVirtualMachineOperationInProgress ReasonCreateVirtualMachineCompleted = "CreateVirtualMachineInProgress" - - // ReasonCreateVirtualMachineOperationCompleted is a ReasonCreateVirtualMachineCompleted indicating that the clone operation has completed successfully. - ReasonCreateVirtualMachineOperationCompleted ReasonCreateVirtualMachineCompleted = "CreateVirtualMachineCompleted" - - // ReasonCreateVirtualMachineOperationFailed is a ReasonCreateVirtualMachineCompleted indicating that clone operation has failed. - ReasonCreateVirtualMachineOperationFailed ReasonCreateVirtualMachineCompleted = "CreateVirtualMachineFailed" -)