From fdca2feb0662247088349ea78c12385d94ca0357 Mon Sep 17 00:00:00 2001 From: Daniil Antoshin Date: Wed, 29 Jan 2025 17:35:45 +0200 Subject: [PATCH] feat(cvi): snapshot objectRef (#640) Add the ability to create a ClusterVirtualImage from a VirtualDiskSnapshot. Signed-off-by: Daniil Antoshin --- api/core/v1alpha2/cluster_virtual_image.go | 13 +- .../generated/openapi/zz_generated.openapi.go | 252 ++++++------ crds/clustervirtualimages.yaml | 25 +- crds/doc-ru-clustervirtualimages.yaml | 10 +- .../pkg/controller/cvi/cvi_controller.go | 3 +- .../controller/cvi/internal/source/errors.go | 14 + .../cvi/internal/source/object_ref.go | 38 +- .../internal/source/object_ref_vdsnapshot.go | 372 ++++++++++++++++++ 8 files changed, 574 insertions(+), 153 deletions(-) create mode 100644 images/virtualization-artifact/pkg/controller/cvi/internal/source/object_ref_vdsnapshot.go diff --git a/api/core/v1alpha2/cluster_virtual_image.go b/api/core/v1alpha2/cluster_virtual_image.go index 2f5b01c44..cfffb472d 100644 --- a/api/core/v1alpha2/cluster_virtual_image.go +++ b/api/core/v1alpha2/cluster_virtual_image.go @@ -95,15 +95,15 @@ type ClusterVirtualImageContainerImage struct { CABundle []byte `json:"caBundle,omitempty"` } -// Use an existing VirtualImage, ClusterVirtualImage, or VirtualDisk resource to create an image. +// Use an existing VirtualImage, ClusterVirtualImage, VirtualDisk or VirtualDiskSnapshot resource to create an image. // -// +kubebuilder:validation:XValidation:rule="self.kind == 'VirtualImage' || self.kind == 'VirtualDisk' ? has(self.__namespace__) && size(self.__namespace__) > 0 : true",message="The namespace is required for VirtualDisk and VirtualImage." -// +kubebuilder:validation:XValidation:rule="self.kind == 'VirtualImage' || self.kind == 'VirtualDisk' ? has(self.__namespace__) && size(self.__namespace__) < 64 : true",message="The namespace must be no longer than 63 characters." +// +kubebuilder:validation:XValidation:rule="self.kind == 'VirtualImage' || self.kind == 'VirtualDisk' || self.kind == 'VirtualDiskSnapshot' ? has(self.__namespace__) && size(self.__namespace__) > 0 : true",message="The namespace is required for VirtualDisk, VirtualImage and VirtualDiskSnapshot" +// +kubebuilder:validation:XValidation:rule="self.kind == 'VirtualImage' || self.kind == 'VirtualDisk' || self.kind == 'VirtualDiskSnapshot' ? has(self.__namespace__) && size(self.__namespace__) < 64 : true",message="The namespace must be no longer than 63 characters." type ClusterVirtualImageObjectRef struct { Kind ClusterVirtualImageObjectRefKind `json:"kind"` - // Name of the existing VirtualImage, ClusterVirtualImage, or VirtualDisk resource. + // Name of the existing VirtualImage, ClusterVirtualImage, VirtualDisk or VirtualDiskSnapshot resource. Name string `json:"name"` - // Namespace where the VirtualImage or VirtualDisk resource is located. + // Namespace where the VirtualImage, VirtualDisk or VirtualDiskSnapshot resource is located. Namespace string `json:"namespace,omitempty"` } @@ -115,6 +115,7 @@ const ( ClusterVirtualImageObjectRefKindVirtualImage ClusterVirtualImageObjectRefKind = "VirtualImage" ClusterVirtualImageObjectRefKindClusterVirtualImage ClusterVirtualImageObjectRefKind = "ClusterVirtualImage" ClusterVirtualImageObjectRefKindVirtualDisk ClusterVirtualImageObjectRefKind = "VirtualDisk" + ClusterVirtualImageObjectRefKindVirtualDiskSnapshot ClusterVirtualImageObjectRefKind = "VirtualDiskSnapshot" ) type ClusterVirtualImageStatus struct { @@ -137,7 +138,7 @@ type ClusterVirtualImageStatus struct { Phase ImagePhase `json:"phase,omitempty"` // Progress of copying an image from the source to DVCR. Appears only during the `Provisioning' phase. Progress string `json:"progress,omitempty"` - // UID of the source (VirtualImage, ClusterVirtualImage, or VirtualDisk) used when creating the cluster virtual image. + // UID of the source (VirtualImage, ClusterVirtualImage, VirtualDisk or VirtualDiskSnapshot) used when creating the cluster virtual image. SourceUID *types.UID `json:"sourceUID,omitempty"` // The latest available observations of an object's current state. Conditions []metav1.Condition `json:"conditions,omitempty"` diff --git a/api/pkg/apiserver/api/generated/openapi/zz_generated.openapi.go b/api/pkg/apiserver/api/generated/openapi/zz_generated.openapi.go index 546666405..96c1d4e98 100644 --- a/api/pkg/apiserver/api/generated/openapi/zz_generated.openapi.go +++ b/api/pkg/apiserver/api/generated/openapi/zz_generated.openapi.go @@ -674,7 +674,7 @@ func schema_virtualization_api_core_v1alpha2_AttachedVirtualMachine(ref common.R return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "A list of `VirtualMachines` that use the disk", + Description: "List of VirtualMachines that use the disk.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "name": { @@ -798,14 +798,14 @@ func schema_virtualization_api_core_v1alpha2_CPU(ref common.ReferenceCallback) c }, "model": { SchemaProps: spec.SchemaProps{ - Description: "The name of CPU model. More information about models [here](https://libvirt.org/formatdomain.html#cpu-model-and-topology)", + Description: "CPU model name. For more information about CPU models and topology, refer to the [libvirt docs](https://libvirt.org/formatdomain.html#cpu-model-and-topology).", Type: []string{"string"}, Format: "", }, }, "features": { SchemaProps: spec.SchemaProps{ - Description: "A list of CPU instructions (features) required when type=Features. More information about features [here](https://libvirt.org/formatdomain.html#cpu-model-and-topology)", + Description: "List of CPU instructions (features) required when type=Features. For more information about CPU features, refer to the [libvirt docs](https://libvirt.org/formatdomain.html#cpu-model-and-topology).", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -820,7 +820,7 @@ func schema_virtualization_api_core_v1alpha2_CPU(ref common.ReferenceCallback) c }, "discovery": { SchemaProps: spec.SchemaProps{ - Description: "Create CPU model based on an intersection CPU features for selected nodes.", + Description: "Create a CPU model based on intersecting CPU features for selected nodes.", Default: map[string]interface{}{}, Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha2.CpuDiscovery"), }, @@ -934,7 +934,7 @@ func schema_virtualization_api_core_v1alpha2_ClusterVirtualImage(ref common.Refe return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "Describes a virtual disk image that can be used as a data source for new `VirtualDisks` or an installation image (iso) to be mounted in `Virtuals` directly. This resource type is available for all namespaces in the cluster.\n\n> This resource cannot be modified once it has been created.\n\nA container image is created under the hood of this resource, which is stored in a dedicated deckhouse virtualization container registry (DVCR).", + Description: "Describes a virtual disk image that can be used as a data source for new VirtualDisks or an installation image (iso) to be mounted in VirtualMachines directly. This resource type is available for all namespaces in the cluster.\n\n> This resource cannot be modified once it has been created.\n\nWith this resource in the cluster, a container image is created and stored in a dedicated Deckhouse Virtualization Container Registry (DVCR).", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -982,12 +982,12 @@ func schema_virtualization_api_core_v1alpha2_ClusterVirtualImageContainerImage(r return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "Use an image stored in external container registry. Only TLS enabled registries are supported. Use caBundle field to provide custom CA chain if needed.", + Description: "Use an image stored in external container registry. Only registries with enabled TLS protocol are supported. To provide a custom Certificate Authority (CA) chain, use the `caBundle` field.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "image": { SchemaProps: spec.SchemaProps{ - Description: "The container registry address of an image.", + Description: "Path to the image in the container registry.", Default: "", Type: []string{"string"}, Format: "", @@ -1001,7 +1001,7 @@ func schema_virtualization_api_core_v1alpha2_ClusterVirtualImageContainerImage(r }, "caBundle": { SchemaProps: spec.SchemaProps{ - Description: "The CA chain in base64 format to verify the container registry.", + Description: "CA chain in Base64 format to verify the container registry.", Type: []string{"string"}, Format: "byte", }, @@ -1019,7 +1019,7 @@ func schema_virtualization_api_core_v1alpha2_ClusterVirtualImageDataSource(ref c return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "An origin of the image.", + Description: "Origin of the image.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "type": { @@ -1057,7 +1057,7 @@ func schema_virtualization_api_core_v1alpha2_ClusterVirtualImageList(ref common. return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "ClusterVirtualImageList provides the needed parameters to do request a list of ClusterVirtualImages from the system.", + Description: "ClusterVirtualImageList provides the needed parameters for requesting a list of ClusterVirtualImages from the system.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -1082,7 +1082,7 @@ func schema_virtualization_api_core_v1alpha2_ClusterVirtualImageList(ref common. }, "items": { SchemaProps: spec.SchemaProps{ - Description: "Items provides a list of CDIs", + Description: "Items provides a list of CVIs.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -1107,7 +1107,7 @@ func schema_virtualization_api_core_v1alpha2_ClusterVirtualImageObjectRef(ref co return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "Use an existing `VirtualImage`, `ClusterVirtualImage` or `VirtualDisk` to create an image.", + Description: "Use an existing VirtualImage, ClusterVirtualImage, VirtualDisk or VirtualDiskSnapshot resource to create an image.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -1119,7 +1119,7 @@ func schema_virtualization_api_core_v1alpha2_ClusterVirtualImageObjectRef(ref co }, "name": { SchemaProps: spec.SchemaProps{ - Description: "A name of existing `VirtualImage`, `ClusterVirtualImage` or `VirtualDisk`.", + Description: "Name of the existing VirtualImage, ClusterVirtualImage, VirtualDisk or VirtualDiskSnapshot resource.", Default: "", Type: []string{"string"}, Format: "", @@ -1127,7 +1127,7 @@ func schema_virtualization_api_core_v1alpha2_ClusterVirtualImageObjectRef(ref co }, "namespace": { SchemaProps: spec.SchemaProps{ - Description: "A namespace where `VirtualImage` or `VirtualDisk` is located.", + Description: "Namespace where the VirtualImage, VirtualDisk or VirtualDiskSnapshot resource is located.", Type: []string{"string"}, Format: "", }, @@ -1174,42 +1174,42 @@ func schema_virtualization_api_core_v1alpha2_ClusterVirtualImageStatus(ref commo }, "size": { SchemaProps: spec.SchemaProps{ - Description: "Discovered sizes of the image.", + Description: "Discovered image size data.", Default: map[string]interface{}{}, Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha2.ImageStatusSize"), }, }, "format": { SchemaProps: spec.SchemaProps{ - Description: "Discovered format of the image.", + Description: "Discovered image format.", Type: []string{"string"}, Format: "", }, }, "cdrom": { SchemaProps: spec.SchemaProps{ - Description: "Whether the image is a format that is supposed to be mounted as a cdrom, such as iso and so on.", + Description: "Defines whether the image is in a format that needs to be mounted as a CD-ROM drive, such as iso and so on.", Type: []string{"boolean"}, Format: "", }, }, "phase": { SchemaProps: spec.SchemaProps{ - Description: "Current status of `ClusterVirtualImage` resource: * Pending - The resource has been created and is on a waiting queue. * Provisioning - The process of resource creation (copying/downloading/building the image) is in progress. * WaitForUserUpload - Waiting for the user to upload the image. The endpoint to upload the image is specified in `.status.uploadCommand`. * Ready - The resource is created and ready to use. * Failed - There was a problem when creating a resource. * Terminating - The process of resource deletion is in progress.", + Description: "Current status of the ClusterVirtualImage resource: * `Pending`: The resource has been created and is on a waiting queue. * `Provisioning`: The resource is being created: copying, downloading, or building of the image is in progress. * `WaitForUserUpload`: Waiting for the user to upload the image. The endpoint to upload the image is specified in `.status.uploadCommand`. * `Ready`: The resource has been created and is ready to use. * `Failed`: There was an error when creating the resource. * `Terminating`: The resource is being deleted.", Type: []string{"string"}, Format: "", }, }, "progress": { SchemaProps: spec.SchemaProps{ - Description: "Progress of copying an image from source to DVCR. Appears only during the `Provisioning' phase.", + Description: "Progress of copying an image from the source to DVCR. Appears only during the `Provisioning' phase.", Type: []string{"string"}, Format: "", }, }, "sourceUID": { SchemaProps: spec.SchemaProps{ - Description: "The UID of the source (`VirtualImage`, `ClusterVirtualImage` or `VirtualDisk`) used when creating the cluster virtual image.", + Description: "UID of the source (VirtualImage, ClusterVirtualImage, VirtualDisk or VirtualDiskSnapshot) used when creating the cluster virtual image.", Type: []string{"string"}, Format: "", }, @@ -1230,14 +1230,14 @@ func schema_virtualization_api_core_v1alpha2_ClusterVirtualImageStatus(ref commo }, "observedGeneration": { SchemaProps: spec.SchemaProps{ - Description: "The generation last processed by the controller.", + Description: "Resource generation last processed by the controller.", Type: []string{"integer"}, Format: "int64", }, }, "uploadCommand": { SchemaProps: spec.SchemaProps{ - Description: "Deprecated. Use imageUploadURLs instead.", + Description: "Deprecated. Use `imageUploadURLs` instead.", Type: []string{"string"}, Format: "", }, @@ -1288,7 +1288,7 @@ func schema_virtualization_api_core_v1alpha2_CpuDiscovery(ref common.ReferenceCa Properties: map[string]spec.Schema{ "nodeSelector": { SchemaProps: spec.SchemaProps{ - Description: "A selection of nodes on the basis of which a universal CPU model will be created.", + Description: "A selection of nodes to be used as the basis for creating a universal CPU model.", Default: map[string]interface{}{}, Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), }, @@ -1305,12 +1305,12 @@ func schema_virtualization_api_core_v1alpha2_CpuFeatures(ref common.ReferenceCal return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "CpuFeatures Information on CPU features for this model. Shown only for types `Features` or `Discovery`.", + Description: "CpuFeatures Information on CPU features supported by this model. Shown only for `Features` or `Discovery` types.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "enabled": { SchemaProps: spec.SchemaProps{ - Description: "A list of CPU features for this model.", + Description: "List of CPU features for this model.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -1325,7 +1325,7 @@ func schema_virtualization_api_core_v1alpha2_CpuFeatures(ref common.ReferenceCal }, "notEnabledCommon": { SchemaProps: spec.SchemaProps{ - Description: "A list of unused processor features additionally available for a given group of nodes.", + Description: "List of unused processor features additionally available for a given group of nodes.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -1348,18 +1348,18 @@ func schema_virtualization_api_core_v1alpha2_DataSourceHTTP(ref common.Reference return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "Fill the image with data from some external url. Supported schemas are:\n\n* http * https\n\nFor https schema there is an option to skip TLS verification.", + Description: "Fill the image with data from an external URL. The following schemas are supported:\n\n* HTTP * HTTPS\n\nFor HTTPS schema, there is an option to skip the TLS verification.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "checksum": { SchemaProps: spec.SchemaProps{ - Description: "A checksum of the file, provided by the url, to verify if it was downloaded correctly or wasn't changed. The file should match all specified checksums.", + Description: "Checksum to verify integrity and consistency of the downloaded file. The file must match all specified checksums.", Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha2.Checksum"), }, }, "url": { SchemaProps: spec.SchemaProps{ - Description: "The http url with an image. The following formats are supported: * qcow2 * vmdk * vdi * iso * raw these formats can also be compressed with the following formats: * gz * xz", + Description: "URL of the file for creating an image. The following file formats are supported: * qcow2 * vmdk * vdi * iso * raw The file can be compressed into an archive in one of the following formats: * gz * xz", Default: "", Type: []string{"string"}, Format: "", @@ -1367,7 +1367,7 @@ func schema_virtualization_api_core_v1alpha2_DataSourceHTTP(ref common.Reference }, "caBundle": { SchemaProps: spec.SchemaProps{ - Description: "The CA chain in base64 format to verify the url.", + Description: "CA chain in Base64 format to verify the URL.", Type: []string{"string"}, Format: "byte", }, @@ -1389,7 +1389,7 @@ func schema_virtualization_api_core_v1alpha2_DiskTarget(ref common.ReferenceCall Properties: map[string]spec.Schema{ "persistentVolumeClaimName": { SchemaProps: spec.SchemaProps{ - Description: "Created PersistentVolumeClaim name for Kubernetes storage.", + Description: "Created PersistentVolumeClaim name for the Kubernetes storage.", Type: []string{"string"}, Format: "", }, @@ -1427,14 +1427,14 @@ func schema_virtualization_api_core_v1alpha2_ImagePullSecret(ref common.Referenc Properties: map[string]spec.Schema{ "name": { SchemaProps: spec.SchemaProps{ - Description: "A name of the secret containing registry credentials.", + Description: "Name of the secret keeping container registry credentials.", Type: []string{"string"}, Format: "", }, }, "namespace": { SchemaProps: spec.SchemaProps{ - Description: "A namespace where imagePullSecret is located.", + Description: "Namespace where `imagePullSecret` is located.", Type: []string{"string"}, Format: "", }, @@ -1453,7 +1453,7 @@ func schema_virtualization_api_core_v1alpha2_ImagePullSecretName(ref common.Refe Properties: map[string]spec.Schema{ "name": { SchemaProps: spec.SchemaProps{ - Description: "A name of the secret containing registry credentials which must be located in the same namespace.", + Description: "Name of the secret keeping container registry credentials, which must be located in the same namespace.", Type: []string{"string"}, Format: "", }, @@ -1610,19 +1610,19 @@ func schema_virtualization_api_core_v1alpha2_NameReplacement(ref common.Referenc return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "NameReplacement represents rule to redefine the virtual machine resource names.", + Description: "NameReplacement represents a rule for redefining the virtual machine resource names.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "from": { SchemaProps: spec.SchemaProps{ - Description: "The selector to choose resources for name replacement.", + Description: "Selector to choose resources for name replacement.", Default: map[string]interface{}{}, Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha2.NameReplacementFrom"), }, }, "to": { SchemaProps: spec.SchemaProps{ - Description: "The new resource name.", + Description: "New resource name.", Default: "", Type: []string{"string"}, Format: "", @@ -1641,19 +1641,19 @@ func schema_virtualization_api_core_v1alpha2_NameReplacementFrom(ref common.Refe return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "NameReplacementFrom represents the selector to choose resources for name replacement.", + Description: "NameReplacementFrom represents a selector to choose resources for name replacement.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { SchemaProps: spec.SchemaProps{ - Description: "The kind of resource to rename.", + Description: "Kind of a resource to rename.", Type: []string{"string"}, Format: "", }, }, "name": { SchemaProps: spec.SchemaProps{ - Description: "The current name of resource to rename.", + Description: "Current name of a resource to rename.", Default: "", Type: []string{"string"}, Format: "", @@ -1670,12 +1670,12 @@ func schema_virtualization_api_core_v1alpha2_NodeSelector(ref common.ReferenceCa return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "NodeSelector defines selects the nodes that are targeted to VM scheduling.", + Description: "NodeSelector defines the nodes targeted for VM scheduling.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "matchLabels": { SchemaProps: spec.SchemaProps{ - Description: "A map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", + Description: "A map of {key,value} pairs. A single {key,value} pair in the matchLabels map is equivalent to an element of matchExpressions whose key field is \"key\", operator is \"In\", and the value array contains only \"value\". The requirements are ANDed.", Type: []string{"object"}, AdditionalProperties: &spec.SchemaOrBool{ Allows: true, @@ -1782,7 +1782,7 @@ func schema_virtualization_api_core_v1alpha2_SizingPolicy(ref common.ReferenceCa return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "SizingPolicy define policy for allocating computational resources to VMs. It is represented as a list. The cores.min - cores.max ranges for different elements of the list must not overlap.", + Description: "SizingPolicy defines a policy for allocating computational resources to VMs. It is represented as a list. The cores.min - cores.max ranges for different elements of the list must not overlap.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "memory": { @@ -1843,7 +1843,7 @@ func schema_virtualization_api_core_v1alpha2_SizingPolicyCores(ref common.Refere Properties: map[string]spec.Schema{ "min": { SchemaProps: spec.SchemaProps{ - Description: "Minimum cpu core count.", + Description: "Minimum number of CPU cores.", Default: 0, Type: []string{"integer"}, Format: "int32", @@ -1851,7 +1851,7 @@ func schema_virtualization_api_core_v1alpha2_SizingPolicyCores(ref common.Refere }, "max": { SchemaProps: spec.SchemaProps{ - Description: "Maximum cpu core count.", + Description: "Maximum number of CPU cores.", Default: 0, Type: []string{"integer"}, Format: "int32", @@ -1859,7 +1859,7 @@ func schema_virtualization_api_core_v1alpha2_SizingPolicyCores(ref common.Refere }, "step": { SchemaProps: spec.SchemaProps{ - Description: "Cpu cores count discretization step. I.e. min=2, max=10, step=4 allows to set virtual machine cpu cores to 2, 6, or 10.", + Description: "Discretization step for the CPU core number. For example, the combination of `min=2`, `max=10`, and `step=4` allows to set the number of virtual machine CPU cores to 2, 6, or 10.", Type: []string{"integer"}, Format: "int32", }, @@ -1891,7 +1891,7 @@ func schema_virtualization_api_core_v1alpha2_SizingPolicyMemory(ref common.Refer }, "step": { SchemaProps: spec.SchemaProps{ - Description: "Memory size discretization step. I.e. min=2Gi, max=4Gi, step=1Gi allows to set virtual machine memory size to 2Gi, 3Gi, or 4Gi.", + Description: "Memory size discretization step. For example, the combination of `min=2Gi, `max=4Gi` and `step=1Gi` allows to set the virtual machine memory size to 2Gi, 3Gi, or 4Gi.", Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), }, }, @@ -2067,19 +2067,19 @@ func schema_virtualization_api_core_v1alpha2_VMBDAObjectRef(ref common.Reference return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "A block device that will be connected to the VM as a hot Plug disk.", + Description: "Block device that will be connected to the VM as a hot-plug disk.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { SchemaProps: spec.SchemaProps{ - Description: "The type of the block device. Options are: * `VirtualDisk` — use `VirtualDisk` as the disk. This type is always mounted in RW mode. * `VirtualImage` — use `VirtualImage` as the disk. This type is always mounted in RO mode. * `ClusterVirtualImage` - use `ClusterVirtualImage` as the disk. This type is always mounted in RO mode.", + Description: "Block device type. Available options: * `VirtualDisk`: Use VirtualDisk as the disk. This type is always mounted in RW mode. * `VirtualImage`: Use VirtualImage as the disk. This type is always mounted in RO mode. * `ClusterVirtualImage`: Use ClusterVirtualImage as the disk. This type is always mounted in RO mode.", Type: []string{"string"}, Format: "", }, }, "name": { SchemaProps: spec.SchemaProps{ - Description: "The name of block device to attach.", + Description: "Name of the block device to attach.", Type: []string{"string"}, Format: "", }, @@ -2094,7 +2094,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualDisk(ref common.ReferenceCal return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "The `VirtualDisk` resource describes the desired virtual machine disk configuration. A `VirtualDisk` can be mounted statically in the virtual machine by specifying it in the `.spec.blockDeviceRefs` disk list, or mounted on-the-fly using the `VirtualMachineBlockDeviceAttachments` resource.\n\nOnce `VirtualDisk` is created, only the disk size `.spec.persistentVolumeClaim.size` can be changed, all other fields are immutable.", + Description: "The VirtualDisk resource describes the desired virtual machine disk configuration. A VirtualDisk can be mounted statically in the virtual machine by specifying it in the `.spec.blockDeviceRefs` disk list, or mounted on-the-fly using the VirtualMachineBlockDeviceAttachments resource.\n\nOnce a VirtualDisk is created, only the disk size field `.spec.persistentVolumeClaim.size` can be changed. All other fields are immutable.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -2142,12 +2142,12 @@ func schema_virtualization_api_core_v1alpha2_VirtualDiskContainerImage(ref commo return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "Use an image stored in external container registry. Only TLS enabled registries are supported. Use caBundle field to provide custom CA chain if needed.", + Description: "Use an image stored in an external container registry. Only registries with enabled TLS are supported. To provide a custom Certificate Authority (CA) chain, use the `caBundle` field.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "image": { SchemaProps: spec.SchemaProps{ - Description: "The container registry address of an image.", + Description: "Path to the image in the container registry.", Default: "", Type: []string{"string"}, Format: "", @@ -2161,7 +2161,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualDiskContainerImage(ref commo }, "caBundle": { SchemaProps: spec.SchemaProps{ - Description: "The CA chain in base64 format to verify the container registry.", + Description: "CA chain in Base64 format to verify the container registry.", Type: []string{"string"}, Format: "byte", }, @@ -2214,7 +2214,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualDiskList(ref common.Referenc return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "VirtualDiskList contains a list of VirtualDisk", + Description: "VirtualDiskList contains a list of VirtualDisks.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -2263,12 +2263,12 @@ func schema_virtualization_api_core_v1alpha2_VirtualDiskObjectRef(ref common.Ref return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "Use an existing `VirtualImage`, `ClusterVirtualImage` or `VirtualDiskSnapshot` to create a disk.", + Description: "Use an existing VirtualImage, ClusterVirtualImage, or VirtualDiskSnapshot resource to create a disk.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { SchemaProps: spec.SchemaProps{ - Description: "A kind of existing `VirtualImage`, `ClusterVirtualImage` or `VirtualDiskSnapshot`.", + Description: "Kind of the existing VirtualImage, ClusterVirtualImage, or VirtualDiskSnapshot resource.", Default: "", Type: []string{"string"}, Format: "", @@ -2276,7 +2276,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualDiskObjectRef(ref common.Ref }, "name": { SchemaProps: spec.SchemaProps{ - Description: "A name of existing `VirtualImage`, `ClusterVirtualImage` or `VirtualDiskSnapshot`.", + Description: "Name of the existing VirtualImage, ClusterVirtualImage, or VirtualDiskSnapshot resource.", Default: "", Type: []string{"string"}, Format: "", @@ -2298,14 +2298,14 @@ func schema_virtualization_api_core_v1alpha2_VirtualDiskPersistentVolumeClaim(re Properties: map[string]spec.Schema{ "storageClassName": { SchemaProps: spec.SchemaProps{ - Description: "The name of the StorageClass required by the claim. More info — https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1\n\nWhen creating disks, the user can specify the required StorageClass to create the disk, or not explicitly, in which case the default StorageClass will be used.\n\nThe disk features and virtual machine behavior depend on the selected StorageClass.\n\nThe `VolumeBindingMode` parameter in the StorageClass affects the disk creation process: - `Immediate` - The disk will be created and available for use immediately after creation. - `WaitForFirstConsumer` - The disk will be created only when it is used in a virtual machine. In this case, the disk will be created on the host where the virtual machine will be started.\n\nStorageClass can support different storage settings: - Creating a block device (`Block`) or file system (`FileSystem`). - Multiple Access (`ReadWriteMany`) or Single Access (`ReadWriteOnce`). `ReadWriteMany` disks support multiple access, which enables live migration of virtual machines. In contrast, `ReadWriteOnce` disks, which are limited to access from only one host, cannot provide this capability.\n\nFor known storage types, the platform will independently determine the most effective settings when creating disks (in descending order of priority): 1. `Block` + `ReadWriteMany` 2. `FileSystem` + `ReadWriteMany` 3. `Block` + `ReadWriteOnce` 4. `FileSystem` + `ReadWriteOnce`", + Description: "StorageClass name required by the claim. For details on using StorageClass for PVC, refer to https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1.\n\nWhen creating disks, the user can specify the required StorageClass. If not specified, the default StorageClass will be used.\n\nThe disk features and virtual machine behavior depend on the selected StorageClass.\n\nThe `VolumeBindingMode` parameter in the StorageClass affects the disk creation process. The following values are allowed: - `Immediate`: The disk will be created and becomes available for use immediately after creation. - `WaitForFirstConsumer`: The disk will be created when first used on the node where the virtual machine will be started.\n\nStorageClass supports multiple storage settings: - Creating a block device (`Block`) or file system (`FileSystem`). - Multiple access (`ReadWriteMany`) or single access (`ReadWriteOnce`). The `ReadWriteMany` disks support multiple access, which enables a \"live\" migration of virtual machines. In contrast, the `ReadWriteOnce` disks, which can be accessed from only one node, don't have this feature.\n\nFor known storage types, Deckhouse automatically determines the most efficient settings when creating disks (by priority, in descending order): 1. `Block` + `ReadWriteMany` 2. `FileSystem` + `ReadWriteMany` 3. `Block` + `ReadWriteOnce` 4. `FileSystem` + `ReadWriteOnce`", Type: []string{"string"}, Format: "", }, }, "size": { SchemaProps: spec.SchemaProps{ - Description: "Desired size for PVC to store the disk. If the disk is created from an image, the size must be at least as large as the original unpacked image.\n\nThis parameter can be omitted if the `.spec.dataSource` block is specified, in which case the controller will determine the disk size automatically, based on the size of the extracted image from the source specified in `.spec.dataSource`.", + Description: "Desired size for PVC to store the disk. If the disk is created from an image, the size must be at least as large as the original unpacked image.\n\nThis parameter can be omitted if the `.spec.dataSource` section is filled out. In this case, the controller will determine the disk size automatically, based on the size of the extracted image from the source specified in `.spec.dataSource`.", Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), }, }, @@ -2530,12 +2530,12 @@ func schema_virtualization_api_core_v1alpha2_VirtualDiskStats(ref common.Referen return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "VirtualDisk statistics", + Description: "VirtualDisk statistics.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "creationDuration": { SchemaProps: spec.SchemaProps{ - Description: "The waiting time for the virtual disk creation.", + Description: "Waiting time for the virtual disk creation.", Default: map[string]interface{}{}, Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha2.VirtualDiskStatsCreationDuration"), }, @@ -2556,7 +2556,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualDiskStatsCreationDuration(re Properties: map[string]spec.Schema{ "waitingForDependencies": { SchemaProps: spec.SchemaProps{ - Description: "The waiting time for dependent resources.", + Description: "Waiting time for dependent resources.", Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"), }, }, @@ -2568,7 +2568,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualDiskStatsCreationDuration(re }, "totalProvisioning": { SchemaProps: spec.SchemaProps{ - Description: "The duration of resource creation from the moment dependencies are ready until the resource transitions to the Ready state.", + Description: "Duration of the resource creation from the moment dependencies are ready until the resource transitions to the Ready state.", Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"), }, }, @@ -2593,7 +2593,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualDiskStatus(ref common.Refere }, "capacity": { SchemaProps: spec.SchemaProps{ - Description: "Requested capacity of the PVC in human-readable format.", + Description: "Requested PVC capacity in human-readable format.", Type: []string{"string"}, Format: "", }, @@ -2606,14 +2606,14 @@ func schema_virtualization_api_core_v1alpha2_VirtualDiskStatus(ref common.Refere }, "progress": { SchemaProps: spec.SchemaProps{ - Description: "Progress of copying an image from source to PVC. Appears only during the `Provisioning' phase.", + Description: "Progress of copying an image from a source to PVC. Appears only during the `Provisioning' phase.", Type: []string{"string"}, Format: "", }, }, "uploadCommand": { SchemaProps: spec.SchemaProps{ - Description: "Deprecated: use ImageUploadURLs instead.", + Description: "Deprecated. Use `ImageUploadURLs` instead.", Type: []string{"string"}, Format: "", }, @@ -2631,7 +2631,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualDiskStatus(ref common.Refere }, "attachedToVirtualMachines": { SchemaProps: spec.SchemaProps{ - Description: "A list of `VirtualMachines` that use the disk", + Description: "List of VirtualMachines that use the disk.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -2671,14 +2671,14 @@ func schema_virtualization_api_core_v1alpha2_VirtualDiskStatus(ref common.Refere }, "observedGeneration": { SchemaProps: spec.SchemaProps{ - Description: "The generation last processed by the controller.", + Description: "Resource generation last processed by the controller.", Type: []string{"integer"}, Format: "int64", }, }, "storageClassName": { SchemaProps: spec.SchemaProps{ - Description: "The name of the StorageClass used by the PersistentVolumeClaim if `Kubernetes` storage type used.", + Description: "Name of the StorageClass used by the PersistentVolumeClaim if `Kubernetes` storage type is used.", Type: []string{"string"}, Format: "", }, @@ -2695,7 +2695,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualImage(ref common.ReferenceCa return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "This resource describes a virtual disk image or installation image (iso) that can be used as a data source for new `VirtualDisks` or can be mounted in `Virtuals`. > This resource cannot be modified once it has been created.\n\nA container image is created under the hood of this resource, which is stored in a dedicated deckhouse virtualization container registy (DVCR) or PVC, into which the data from the source is filled.", + Description: "This resource describes a virtual disk image to use as a data source for new VirtualDisk resources or an installation image (iso) that can be mounted into the VirtualMachine resource.\n\n> This resource cannot be modified once it has been created.\n\nWith this resource in the cluster, a container image is created and stored in a dedicated Deckhouse Virtualization Container Registry (DVCR) or PVC, with the data filled in from the source.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -2743,12 +2743,12 @@ func schema_virtualization_api_core_v1alpha2_VirtualImageContainerImage(ref comm return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "Use an image stored in external container registry. Only TLS enabled registries are supported. Use caBundle field to provide custom CA chain if needed.", + Description: "Use an image stored in an external container registry. Only registries with enabled TLS protocol are supported. To provide a custom Certificate Authority (CA) chain, use the `caBundle` field.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "image": { SchemaProps: spec.SchemaProps{ - Description: "The container registry address of an image.", + Description: "Path to the image in the container registry.", Default: "", Type: []string{"string"}, Format: "", @@ -2762,7 +2762,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualImageContainerImage(ref comm }, "caBundle": { SchemaProps: spec.SchemaProps{ - Description: "The CA chain in base64 format to verify the container registry.", + Description: "CA chain in Base64 format to verify the container registry.", Type: []string{"string"}, Format: "byte", }, @@ -2815,7 +2815,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualImageList(ref common.Referen return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "VirtualImageList provides the needed parameters to do request a list of VirtualImages from the system.", + Description: "VirtualImageList provides the needed parameters for requesting a list of VirtualImages from the system.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -2840,7 +2840,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualImageList(ref common.Referen }, "items": { SchemaProps: spec.SchemaProps{ - Description: "Items provides a list of CDIs", + Description: "Items provides a list of CDIs.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -2865,12 +2865,12 @@ func schema_virtualization_api_core_v1alpha2_VirtualImageObjectRef(ref common.Re return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "Use an existing `VirtualImage`, `ClusterVirtualImage` or `VirtualDisk` to create an image.", + Description: "Use an existing VirtualImage, ClusterVirtualImage, or VirtualDisk resource to create an image.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { SchemaProps: spec.SchemaProps{ - Description: "A kind of existing `VirtualImage`, `ClusterVirtualImage` or `VirtualDisk`.", + Description: "Kind of an existing VirtualImage, ClusterVirtualImage, or VirtualDisk resource.", Default: "", Type: []string{"string"}, Format: "", @@ -2878,7 +2878,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualImageObjectRef(ref common.Re }, "name": { SchemaProps: spec.SchemaProps{ - Description: "A name of existing `VirtualImage`, `ClusterVirtualImage` or `VirtualDisk`.", + Description: "Name of an existing VirtualImage, ClusterVirtualImage, or VirtualDisk resource.", Default: "", Type: []string{"string"}, Format: "", @@ -2895,12 +2895,12 @@ func schema_virtualization_api_core_v1alpha2_VirtualImagePersistentVolumeClaim(r return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "Settings for creating PVCs to store the image with storage type 'PersistentVolumeClaim'.", + Description: "Settings for creating PVCs to store an image with the storage type `PersistentVolumeClaim`.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "storageClassName": { SchemaProps: spec.SchemaProps{ - Description: "The name of the StorageClass required by the claim. More info — https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1\n\nWhen creating image with storage type 'PersistentVolumeClaim', the user can specify the required StorageClass to create the image, or not explicitly, in which case the default StorageClass will be used.", + Description: "Name of the StorageClass required by the claim. For details on using StorageClass for PVC, refer to — https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1.\n\nWhen creating an image with the `PersistentVolumeClaim` storage type, the user can specify the required StorageClass. If not specified, the default StorageClass will be used.", Type: []string{"string"}, Format: "", }, @@ -2959,42 +2959,42 @@ func schema_virtualization_api_core_v1alpha2_VirtualImageStatus(ref common.Refer }, "size": { SchemaProps: spec.SchemaProps{ - Description: "Discovered sizes of the image.", + Description: "Discovered image size data.", Default: map[string]interface{}{}, Ref: ref("github.com/deckhouse/virtualization/api/core/v1alpha2.ImageStatusSize"), }, }, "format": { SchemaProps: spec.SchemaProps{ - Description: "Discovered format of the image.", + Description: "Discovered image format.", Type: []string{"string"}, Format: "", }, }, "cdrom": { SchemaProps: spec.SchemaProps{ - Description: "Whether the image is a format that is supposed to be mounted as a cdrom, such as iso and so on.", + Description: "Whether the image is in a format that needs to be mounted as a CD-ROM drive, such as iso and so on.", Type: []string{"boolean"}, Format: "", }, }, "phase": { SchemaProps: spec.SchemaProps{ - Description: "Current status of `ClusterVirtualImage` resource: * Pending - The resource has been created and is on a waiting queue. * Provisioning - The process of resource creation (copying/downloading/building the image) is in progress. * WaitForUserUpload - Waiting for the user to upload the image. The endpoint to upload the image is specified in `.status.uploadCommand`. * Ready - The resource is created and ready to use. * Failed - There was a problem when creating a resource. * Terminating - The process of resource deletion is in progress. * PVCLost - The child PVC of the resource is missing. The resource cannot be used.", + Description: "Current status of the ClusterVirtualImage resource: * `Pending`: The resource has been created and is on a waiting queue. * `Provisioning`: The resource is being created: copying, downloading, or building the image. * `WaitForUserUpload`: Waiting for the user to upload the image. The endpoint to upload the image is specified in `.status.uploadCommand`. * `Ready`: The resource has been created and is ready to use. * `Failed`: There was an error when creating the resource. * `Terminating`: The resource is being deleted. * `PVCLost`: The child PVC of the resource is missing. The resource cannot be used.", Type: []string{"string"}, Format: "", }, }, "progress": { SchemaProps: spec.SchemaProps{ - Description: "Progress of copying an image from source to DVCR.", + Description: "Progress of copying an image from a source to DVCR.", Type: []string{"string"}, Format: "", }, }, "uploadCommand": { SchemaProps: spec.SchemaProps{ - Description: "Deprecated. Use imageUploadURLs instead.", + Description: "Deprecated. Use `imageUploadURLs` instead.", Type: []string{"string"}, Format: "", }, @@ -3012,7 +3012,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualImageStatus(ref common.Refer }, "sourceUID": { SchemaProps: spec.SchemaProps{ - Description: "The UID of the source (`VirtualImage`, `ClusterVirtualImage` or `VirtualDisk`) used when creating the virtual image.", + Description: "UID of the source (VirtualImage, ClusterVirtualImage, or VirtualDisk) used when creating the virtual image.", Type: []string{"string"}, Format: "", }, @@ -3033,14 +3033,14 @@ func schema_virtualization_api_core_v1alpha2_VirtualImageStatus(ref common.Refer }, "observedGeneration": { SchemaProps: spec.SchemaProps{ - Description: "The generation last processed by the controller.", + Description: "Resource generation last processed by the controller.", Type: []string{"integer"}, Format: "int64", }, }, "storageClassName": { SchemaProps: spec.SchemaProps{ - Description: "The name of the StorageClass used by the PersistentVolumeClaim if `Kubernetes` storage type used.", + Description: "Name of the StorageClass used by the PersistentVolumeClaim if `Kubernetes` storage type is used.", Type: []string{"string"}, Format: "", }, @@ -3068,7 +3068,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualImageStatusTarget(ref common }, "persistentVolumeClaimName": { SchemaProps: spec.SchemaProps{ - Description: "Created PersistentVolumeClaim name for PersistentVolumeClaim storage.", + Description: "Created PersistentVolumeClaim name for the PersistentVolumeClaim storage.", Type: []string{"string"}, Format: "", }, @@ -3285,7 +3285,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineBlockDeviceAttachment return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "VirtualMachineBlockDeviceAttachment provides a hot plug for connecting a disk to a virtual machine.", + Description: "VirtualMachineBlockDeviceAttachment provides a hot plug for attaching a disk to a virtual machine.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -3333,7 +3333,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineBlockDeviceAttachment return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "VirtualMachineBlockDeviceAttachmentList contains a list of VirtualMachineBlockDeviceAttachment.", + Description: "VirtualMachineBlockDeviceAttachmentList contains a list of VirtualMachineBlockDeviceAttachment resources.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -3358,7 +3358,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineBlockDeviceAttachment }, "items": { SchemaProps: spec.SchemaProps{ - Description: "Items provides a list of CDIs", + Description: "Items provides a list of CDIs.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -3387,7 +3387,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineBlockDeviceAttachment Properties: map[string]spec.Schema{ "virtualMachineName": { SchemaProps: spec.SchemaProps{ - Description: "The name of the virtual machine to which the disk or image should be connected.", + Description: "Virtual machine name the disk or image should be attached to.", Default: "", Type: []string{"string"}, Format: "", @@ -3422,14 +3422,14 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineBlockDeviceAttachment }, "virtualMachineName": { SchemaProps: spec.SchemaProps{ - Description: "The name of the virtual machine to which this disk is attached.", + Description: "Name of the virtual machine the disk is attached to.", Type: []string{"string"}, Format: "", }, }, "conditions": { SchemaProps: spec.SchemaProps{ - Description: "Contains details of the current state of this API Resource.", + Description: "Contains details of the current API resource state.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -3443,7 +3443,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineBlockDeviceAttachment }, "observedGeneration": { SchemaProps: spec.SchemaProps{ - Description: "The generation last processed by the controller", + Description: "Resource generation last processed by the controller.", Type: []string{"integer"}, Format: "int64", }, @@ -3460,7 +3460,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineClass(ref common.Refe return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "VirtualMachineClass resource describes a cpu requirements, node placement and sizing policy for VM resources. A resource cannot be deleted as long as it is used in one of the VMs.", + Description: "VirtualMachineClass resource describes CPU requirements, node placement, and sizing policy for VM resources. A resource cannot be deleted as long as it is used in one of the VMs.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -3508,7 +3508,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineClassList(ref common. return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "VirtualMachineClassList contains a list of VirtualMachineClass", + Description: "VirtualMachineClassList contains a list of VirtualMachineClasses.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -3533,7 +3533,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineClassList(ref common. }, "items": { SchemaProps: spec.SchemaProps{ - Description: "Items provides a list of VirtualMachineClasses", + Description: "Items provides a list of VirtualMachineClasses.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -3568,7 +3568,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineClassSpec(ref common. }, "tolerations": { SchemaProps: spec.SchemaProps{ - Description: "Tolerations are the same as `spec.tolerations` in the [Pod](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/). These tolerations will be merged with tolerations specified in VirtualMachine resource. VirtualMachine tolerations have higher priority.", + Description: "Tolerations are the same as `spec.tolerations` for [pods](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/). These tolerations will be merged with the tolerations specified in the VirtualMachine resource. VirtualMachine tolerations have a higher priority.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -3629,7 +3629,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineClassStatus(ref commo }, "availableNodes": { SchemaProps: spec.SchemaProps{ - Description: "A list of nodes that support this CPU model. It is not displayed for the types: `Host`, `HostPassthrough`", + Description: "List of nodes that support this CPU model. It is not displayed for the following types: `Host`, `HostPassthrough`.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -3644,7 +3644,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineClassStatus(ref commo }, "maxAllocatableResources": { SchemaProps: spec.SchemaProps{ - Description: "The maximum amount of free CPU and Memory resources observed among all available nodes.", + Description: "Maximum amount of free CPU and memory resources observed among all available nodes.", Type: []string{"object"}, AdditionalProperties: &spec.SchemaOrBool{ Allows: true, @@ -3672,7 +3672,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineClassStatus(ref commo }, "observedGeneration": { SchemaProps: spec.SchemaProps{ - Description: "The generation last processed by the controller.", + Description: "Resource generation last processed by the controller.", Type: []string{"integer"}, Format: "int64", }, @@ -4211,7 +4211,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineOperation(ref common. return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "VirtualMachineOperation resource provides the ability to declaratively manage state changes of virtual machines.", + Description: "VirtualMachineOperation enables declarative management of virtual machine state changes.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -4259,7 +4259,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineOperationList(ref com return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "VirtualMachineOperationList contains a list of VirtualMachineOperation", + Description: "VirtualMachineOperationList contains a list of VirtualMachineOperation resources.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -4319,7 +4319,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineOperationSpec(ref com }, "virtualMachineName": { SchemaProps: spec.SchemaProps{ - Description: "The name of the virtual machine for which the operation is performed.", + Description: "Name of the virtual machine the operation is performed for.", Default: "", Type: []string{"string"}, Format: "", @@ -4327,7 +4327,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineOperationSpec(ref com }, "force": { SchemaProps: spec.SchemaProps{ - Description: "Force the execution of the operation. Applies only for Restart and Stop. In this case, the action on the virtual machine is performed immediately.", + Description: "Force execution of an operation. Applies only to the `Restart` and `Stop` operations. In this case, the operation on a virtual machine is performed immediately.", Type: []string{"boolean"}, Format: "", }, @@ -4368,7 +4368,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineOperationStatus(ref c }, "observedGeneration": { SchemaProps: spec.SchemaProps{ - Description: "The generation last processed by the controller.", + Description: "Resource generation last processed by the controller.", Type: []string{"integer"}, Format: "int64", }, @@ -4442,7 +4442,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineRestore(ref common.Re return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "VirtualMachineRestore provides a resource that allows to restore a snapshot of the virtual machine and all its resources.", + Description: "VirtualMachineRestore provides a resource for restoring a virtual machine and all associated resources from a snapshot.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -4490,7 +4490,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineRestoreList(ref commo return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "VirtualMachineRestoreList contains a list of `VirtualMachineRestore`", + Description: "VirtualMachineRestoreList contains a list of VirtualMachineRestore resources.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -4543,7 +4543,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineRestoreSpec(ref commo Properties: map[string]spec.Schema{ "virtualMachineSnapshotName": { SchemaProps: spec.SchemaProps{ - Description: "The name of virtual machine snapshot to restore the virtual machine.", + Description: "Snapshot name to restore a virtual machine from.", Default: "", Type: []string{"string"}, Format: "", @@ -4551,7 +4551,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineRestoreSpec(ref commo }, "nameReplacements": { SchemaProps: spec.SchemaProps{ - Description: "Redefining the virtual machine resource names.", + Description: "Renaming conventions for virtual machine resources.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -4587,7 +4587,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineRestoreStatus(ref com }, "conditions": { SchemaProps: spec.SchemaProps{ - Description: "Contains details of the current state of this API Resource.", + Description: "Contains details of the current API resource state.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -4601,7 +4601,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineRestoreStatus(ref com }, "observedGeneration": { SchemaProps: spec.SchemaProps{ - Description: "The generation last processed by the controller.", + Description: "Resource generation last processed by the controller.", Type: []string{"integer"}, Format: "int64", }, @@ -4667,7 +4667,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineSnapshotList(ref comm return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "VirtualMachineSnapshotList contains a list of `VirtualMachineSnapshot`", + Description: "VirtualMachineSnapshotList contains a list of VirtualMachineSnapshot resources.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "kind": { @@ -4720,7 +4720,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineSnapshotSpec(ref comm Properties: map[string]spec.Schema{ "virtualMachineName": { SchemaProps: spec.SchemaProps{ - Description: "The name of virtual machine to take snapshot.", + Description: "Name of the virtual machine to take a snapshot of.", Default: "", Type: []string{"string"}, Format: "", @@ -4728,7 +4728,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineSnapshotSpec(ref comm }, "requiredConsistency": { SchemaProps: spec.SchemaProps{ - Description: "Create a snapshot of a virtual machine only if it is possible to freeze the machine through the agent.\n\nIf value is true, the snapshot of the virtual machine will be taken only in the following scenarios: - the virtual machine is powered off. - the virtual machine with an agent, and the freeze operation was successful.", + Description: "Create a snapshot of a virtual machine only if it is possible to freeze the machine through the agent.\n\nIf set to `true`, the virtual machine snapshot will be created only in the following cases: - When the virtual machine is powered off. - When the virtual machine has an agent, and the freeze operation was successful.", Default: false, Type: []string{"boolean"}, Format: "", @@ -4778,21 +4778,21 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineSnapshotStatus(ref co }, "consistent": { SchemaProps: spec.SchemaProps{ - Description: "The virtual machine snapshot is consistent.", + Description: "Whether a virtual machine snapshot is consistent.", Type: []string{"boolean"}, Format: "", }, }, "virtualMachineSnapshotSecretName": { SchemaProps: spec.SchemaProps{ - Description: "The name of underlying `Secret`, created for virtual machine snapshotting.", + Description: "Name of the underlying `Secret` created for virtual machine snapshotting.", Type: []string{"string"}, Format: "", }, }, "virtualDiskSnapshotNames": { SchemaProps: spec.SchemaProps{ - Description: "The list of `VirtualDiskSnapshot` names for the snapshots taken from the virtual disks of the associated virtual machine.", + Description: "List of VirtualDiskSnapshot names for the snapshots taken from the virtual disks of the associated virtual machine.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -4821,7 +4821,7 @@ func schema_virtualization_api_core_v1alpha2_VirtualMachineSnapshotStatus(ref co }, "observedGeneration": { SchemaProps: spec.SchemaProps{ - Description: "The generation last processed by the controller.", + Description: "Resource generation last processed by the controller.", Type: []string{"integer"}, Format: "int64", }, @@ -5159,12 +5159,12 @@ func schema_virtualization_api_core_v1alpha2_VolumeSnapshotClassName(ref common. return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "VolumeSnapshotClassName defines `StorageClass` and `VolumeSnapshotClass` binding.", + Description: "VolumeSnapshotClassName defines StorageClass and VolumeSnapshotClass binding.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "storageClassName": { SchemaProps: spec.SchemaProps{ - Description: "The `StorageClass` name associated with `VolumeSnapshotClass`.", + Description: "StorageClass name associated with a VolumeSnapshotClass.", Default: "", Type: []string{"string"}, Format: "", @@ -5172,7 +5172,7 @@ func schema_virtualization_api_core_v1alpha2_VolumeSnapshotClassName(ref common. }, "volumeSnapshotClassName": { SchemaProps: spec.SchemaProps{ - Description: "The name of `VolumeSnapshotClass` to use for virtual disk snapshotting.", + Description: "VolumeSnapshotClass name to use for virtual disk snapshotting.", Default: "", Type: []string{"string"}, Format: "", diff --git a/crds/clustervirtualimages.yaml b/crds/clustervirtualimages.yaml index 72f8c7e3b..aa8b75896 100644 --- a/crds/clustervirtualimages.yaml +++ b/crds/clustervirtualimages.yaml @@ -166,7 +166,7 @@ spec: objectRef: description: Use an existing VirtualImage, ClusterVirtualImage, - or VirtualDisk resource to create an image. + VirtualDisk or VirtualDiskSnapshot resource to create an image. properties: kind: description: @@ -180,28 +180,30 @@ spec: name: description: Name of the existing VirtualImage, ClusterVirtualImage, - or VirtualDisk resource. + VirtualDisk or VirtualDiskSnapshot resource. type: string namespace: description: - Namespace where the VirtualImage or VirtualDisk - resource is located. + Namespace where the VirtualImage, VirtualDisk + or VirtualDiskSnapshot resource is located. type: string required: - kind - name type: object x-kubernetes-validations: - - message: The namespace is required for VirtualDisk and VirtualImage. + - message: + The namespace is required for VirtualDisk, VirtualImage + and VirtualDiskSnapshot rule: "self.kind == 'VirtualImage' || self.kind == 'VirtualDisk' - ? has(self.__namespace__) && size(self.__namespace__) > 0 - : true" + || self.kind == 'VirtualDiskSnapshot' ? has(self.__namespace__) + && size(self.__namespace__) > 0 : true" - message: The namespace must be no longer than 63 characters. rule: - "self.kind == 'VirtualImage' || self.kind == 'VirtualDisk' - ? has(self.__namespace__) && size(self.__namespace__) < 64 - : true" + "self.kind == 'VirtualImage' || self.kind == 'VirtualDisk' + || self.kind == 'VirtualDiskSnapshot' ? has(self.__namespace__) + && size(self.__namespace__) < 64 : true" type: description: |- The following image sources are available for creating an image: @@ -391,7 +393,8 @@ spec: sourceUID: description: UID of the source (VirtualImage, ClusterVirtualImage, - or VirtualDisk) used when creating the cluster virtual image. + VirtualDisk or VirtualDiskSnapshot) used when creating the cluster + virtual image. type: string target: properties: diff --git a/crds/doc-ru-clustervirtualimages.yaml b/crds/doc-ru-clustervirtualimages.yaml index 9ad5ac794..92517f6be 100644 --- a/crds/doc-ru-clustervirtualimages.yaml +++ b/crds/doc-ru-clustervirtualimages.yaml @@ -71,17 +71,17 @@ spec: * xz. objectRef: description: | - Использование существующего ресурса VirtualImage, ClusterVirtualImage или VirtualDisk для создания образа. + Использование существующего ресурса VirtualImage, ClusterVirtualImage, VirtualDisk или VirtualDiskSnapshot для создания образа. properties: kind: description: | - Ссылка на существующий ресурс VirtualImage, ClusterVirtualImage или VirtualDisk. + Ссылка на существующий ресурс VirtualImage, ClusterVirtualImage, VirtualDisk или VirtualDiskSnapshot. name: description: | - Имя существующего ресурса VirtualImage, ClusterVirtualImage или VirtualDisk. + Имя существующего ресурса VirtualImage, ClusterVirtualImage, VirtualDisk или VirtualDiskSnapshot. namespace: description: | - Пространство имён, в котором расположен ресурс VirtualImage или VirtualDisk. + Пространство имён, в котором расположен ресурс VirtualImage, VirtualDisk или VirtualDiskSnapshot. type: description: | Доступные типы источников для создания образа: @@ -182,7 +182,7 @@ spec: Команда для загрузки образа с использованием `Service` внутри кластера. sourceUID: description: | - UID источника (VirtualImage, ClusterVirtualImage или VirtualDisk), использованного при создании кластерного виртуального образа. + UID источника (VirtualImage, ClusterVirtualImage, VirtualDisk или VirtualDiskSnapshot), использованного при создании кластерного виртуального образа. observedGeneration: description: | Поколение ресурса, которое в последний раз обрабатывалось контроллером. diff --git a/images/virtualization-artifact/pkg/controller/cvi/cvi_controller.go b/images/virtualization-artifact/pkg/controller/cvi/cvi_controller.go index 78e3aba8d..ea43329f0 100644 --- a/images/virtualization-artifact/pkg/controller/cvi/cvi_controller.go +++ b/images/virtualization-artifact/pkg/controller/cvi/cvi_controller.go @@ -61,12 +61,13 @@ func NewController( protection := service.NewProtectionService(mgr.GetClient(), virtv2.FinalizerCVIProtection) importer := service.NewImporterService(dvcr, mgr.GetClient(), importerImage, requirements, PodPullPolicy, PodVerbose, ControllerName, protection) uploader := service.NewUploaderService(dvcr, mgr.GetClient(), uploaderImage, requirements, PodPullPolicy, PodVerbose, ControllerName, protection) + disk := service.NewDiskService(mgr.GetClient(), dvcr, protection) recorder := eventrecord.NewEventRecorderLogger(mgr, ControllerName) sources := source.NewSources() sources.Set(virtv2.DataSourceTypeHTTP, source.NewHTTPDataSource(recorder, stat, importer, dvcr, ns)) sources.Set(virtv2.DataSourceTypeContainerImage, source.NewRegistryDataSource(recorder, stat, importer, dvcr, mgr.GetClient(), ns)) - sources.Set(virtv2.DataSourceTypeObjectRef, source.NewObjectRefDataSource(recorder, stat, importer, dvcr, mgr.GetClient(), ns)) + sources.Set(virtv2.DataSourceTypeObjectRef, source.NewObjectRefDataSource(recorder, stat, importer, disk, dvcr, mgr.GetClient(), ns)) sources.Set(virtv2.DataSourceTypeUpload, source.NewUploadDataSource(recorder, stat, uploader, dvcr, ns)) reconciler := NewReconciler( diff --git a/images/virtualization-artifact/pkg/controller/cvi/internal/source/errors.go b/images/virtualization-artifact/pkg/controller/cvi/internal/source/errors.go index f74b39933..d0d9ffac8 100644 --- a/images/virtualization-artifact/pkg/controller/cvi/internal/source/errors.go +++ b/images/virtualization-artifact/pkg/controller/cvi/internal/source/errors.go @@ -78,3 +78,17 @@ func NewVirtualDiskNotAllowedForUseError(name string) error { name: name, } } + +type VirtualDiskSnapshotNotReadyError struct { + name string +} + +func (e VirtualDiskSnapshotNotReadyError) Error() string { + return fmt.Sprintf("VirtualDiskSnapshot %s not ready", e.name) +} + +func NewVirtualDiskSnapshotNotReadyError(name string) error { + return VirtualDiskSnapshotNotReadyError{ + name: name, + } +} diff --git a/images/virtualization-artifact/pkg/controller/cvi/internal/source/object_ref.go b/images/virtualization-artifact/pkg/controller/cvi/internal/source/object_ref.go index 62b41e050..e13624e15 100644 --- a/images/virtualization-artifact/pkg/controller/cvi/internal/source/object_ref.go +++ b/images/virtualization-artifact/pkg/controller/cvi/internal/source/object_ref.go @@ -47,19 +47,22 @@ import ( type ObjectRefDataSource struct { statService Stat importerService Importer + diskService *service.DiskService dvcrSettings *dvcr.Settings client client.Client controllerNamespace string recorder eventrecord.EventRecorderLogger - viOnPvcSyncer *ObjectRefVirtualImageOnPvc - vdSyncer *ObjectRefVirtualDisk + viOnPvcSyncer *ObjectRefVirtualImageOnPvc + vdSyncer *ObjectRefVirtualDisk + vdSnapshotSyncer *ObjectRefVirtualDiskSnapshot } func NewObjectRefDataSource( recorder eventrecord.EventRecorderLogger, statService Stat, importerService Importer, + diskService *service.DiskService, dvcrSettings *dvcr.Settings, client client.Client, controllerNamespace string, @@ -67,13 +70,15 @@ func NewObjectRefDataSource( return &ObjectRefDataSource{ statService: statService, importerService: importerService, + diskService: diskService, dvcrSettings: dvcrSettings, client: client, controllerNamespace: controllerNamespace, recorder: recorder, - viOnPvcSyncer: NewObjectRefVirtualImageOnPvc(recorder, importerService, dvcrSettings, statService), - vdSyncer: NewObjectRefVirtualDisk(recorder, importerService, client, controllerNamespace, dvcrSettings, statService), + viOnPvcSyncer: NewObjectRefVirtualImageOnPvc(recorder, importerService, dvcrSettings, statService), + vdSyncer: NewObjectRefVirtualDisk(recorder, importerService, client, controllerNamespace, dvcrSettings, statService), + vdSnapshotSyncer: NewObjectRefVirtualDiskSnapshot(recorder, importerService, diskService, client, controllerNamespace, dvcrSettings, statService), } } @@ -116,6 +121,19 @@ func (ds ObjectRefDataSource) Sync(ctx context.Context, cvi *virtv2.ClusterVirtu } return ds.vdSyncer.Sync(ctx, cvi, vd, cb) + + case virtv2.VirtualDiskSnapshotKind: + vdSnapshotKey := types.NamespacedName{Name: cvi.Spec.DataSource.ObjectRef.Name, Namespace: cvi.Spec.DataSource.ObjectRef.Namespace} + vdSnapshot, err := object.FetchObject(ctx, vdSnapshotKey, ds.client, &virtv2.VirtualDiskSnapshot{}) + if err != nil { + return reconcile.Result{}, fmt.Errorf("unable to get VDSnapshot %s: %w", vdSnapshotKey, err) + } + + if vdSnapshot == nil { + return reconcile.Result{}, fmt.Errorf("VDSnapshot object ref %s is nil", vdSnapshotKey) + } + + return ds.vdSnapshotSyncer.Sync(ctx, cvi, vdSnapshot, cb) } supgen := supplements.NewGenerator(annotations.CVIShortName, cvi.Name, ds.controllerNamespace, cvi.UID) @@ -336,6 +354,18 @@ func (ds ObjectRefDataSource) Validate(ctx context.Context, cvi *virtv2.ClusterV } return NewClusterImageNotReadyError(cvi.Spec.DataSource.ObjectRef.Name) + case virtv2.VirtualDiskSnapshotKind: + vdSnapshotKey := types.NamespacedName{Name: cvi.Spec.DataSource.ObjectRef.Name, Namespace: cvi.Spec.DataSource.ObjectRef.Namespace} + vdSnapshot, err := object.FetchObject(ctx, vdSnapshotKey, ds.client, &virtv2.VirtualDiskSnapshot{}) + if err != nil { + return fmt.Errorf("unable to get VDSnapshot %s: %w", vdSnapshotKey, err) + } + + if vdSnapshot == nil { + return fmt.Errorf("VDSnapshot object ref %s is nil", vdSnapshotKey) + } + + return ds.vdSnapshotSyncer.Validate(ctx, cvi) case virtv2.ClusterVirtualImageObjectRefKindVirtualDisk: return ds.vdSyncer.Validate(ctx, cvi) default: diff --git a/images/virtualization-artifact/pkg/controller/cvi/internal/source/object_ref_vdsnapshot.go b/images/virtualization-artifact/pkg/controller/cvi/internal/source/object_ref_vdsnapshot.go new file mode 100644 index 000000000..c2c934bde --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/cvi/internal/source/object_ref_vdsnapshot.go @@ -0,0 +1,372 @@ +/* +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 source + +import ( + "context" + "errors" + "fmt" + "strings" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/deckhouse/virtualization-controller/pkg/common" + "github.com/deckhouse/virtualization-controller/pkg/common/annotations" + "github.com/deckhouse/virtualization-controller/pkg/common/datasource" + "github.com/deckhouse/virtualization-controller/pkg/common/object" + podutil "github.com/deckhouse/virtualization-controller/pkg/common/pod" + "github.com/deckhouse/virtualization-controller/pkg/common/pointer" + "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" + "github.com/deckhouse/virtualization-controller/pkg/controller/importer" + "github.com/deckhouse/virtualization-controller/pkg/controller/service" + "github.com/deckhouse/virtualization-controller/pkg/controller/supplements" + "github.com/deckhouse/virtualization-controller/pkg/dvcr" + "github.com/deckhouse/virtualization-controller/pkg/eventrecord" + "github.com/deckhouse/virtualization-controller/pkg/logger" + virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" + "github.com/deckhouse/virtualization/api/core/v1alpha2/vicondition" +) + +type ObjectRefVirtualDiskSnapshot struct { + importerService Importer + diskService *service.DiskService + statService Stat + dvcrSettings *dvcr.Settings + client client.Client + controllerNamespace string + recorder eventrecord.EventRecorderLogger +} + +func NewObjectRefVirtualDiskSnapshot( + recorder eventrecord.EventRecorderLogger, + importerService Importer, + diskService *service.DiskService, + client client.Client, + controllerNamespace string, + dvcrSettings *dvcr.Settings, + statService Stat, +) *ObjectRefVirtualDiskSnapshot { + return &ObjectRefVirtualDiskSnapshot{ + importerService: importerService, + diskService: diskService, + client: client, + recorder: recorder, + controllerNamespace: controllerNamespace, + statService: statService, + dvcrSettings: dvcrSettings, + } +} + +func (ds ObjectRefVirtualDiskSnapshot) Sync(ctx context.Context, cvi *virtv2.ClusterVirtualImage, vdSnapshotRef *virtv2.VirtualDiskSnapshot, cb *conditions.ConditionBuilder) (reconcile.Result, error) { + log, ctx := logger.GetDataSourceContext(ctx, "objectref") + + supgen := supplements.NewGenerator(annotations.VIShortName, cvi.Name, vdSnapshotRef.Namespace, cvi.UID) + pod, err := ds.importerService.GetPod(ctx, supgen) + if err != nil { + return reconcile.Result{}, err + } + + pvc, err := ds.diskService.GetPersistentVolumeClaim(ctx, supgen) + if err != nil { + return reconcile.Result{}, err + } + + vs, err := ds.diskService.GetVolumeSnapshot(ctx, vdSnapshotRef.Status.VolumeSnapshotName, vdSnapshotRef.Namespace) + if err != nil { + return reconcile.Result{}, err + } + + condition, _ := conditions.GetCondition(vicondition.ReadyType, cvi.Status.Conditions) + switch { + case isDiskProvisioningFinished(condition): + log.Info("Virtual image provisioning finished: clean up") + + cb. + Status(metav1.ConditionTrue). + Reason(vicondition.Ready). + Message("") + + cvi.Status.Phase = virtv2.ImageReady + + err = ds.importerService.Unprotect(ctx, pod) + if err != nil { + return reconcile.Result{}, err + } + + return ds.CleanUpSupplements(ctx, cvi) + case object.AnyTerminating(pod, pvc): + cvi.Status.Phase = virtv2.ImagePending + + cb. + Status(metav1.ConditionTrue). + Reason(vicondition.Ready). + Message("") + + log.Info("Cleaning up...") + case pvc == nil: + ds.recorder.Event( + cvi, + corev1.EventTypeNormal, + virtv2.ReasonDataSourceSyncStarted, + "The ObjectRef DataSource import has started", + ) + + namespacedName := supplements.NewGenerator(annotations.VIShortName, cvi.Name, cvi.Spec.DataSource.ObjectRef.Namespace, cvi.UID).PersistentVolumeClaim() + + storageClassName := vs.Annotations["storageClass"] + volumeMode := vs.Annotations["volumeMode"] + accessModesStr := strings.Split(vs.Annotations["accessModes"], ",") + accessModes := make([]corev1.PersistentVolumeAccessMode, 0, len(accessModesStr)) + for _, accessModeStr := range accessModesStr { + accessModes = append(accessModes, corev1.PersistentVolumeAccessMode(accessModeStr)) + } + + spec := corev1.PersistentVolumeClaimSpec{ + AccessModes: accessModes, + DataSource: &corev1.TypedLocalObjectReference{ + APIGroup: ptr.To(vs.GroupVersionKind().Group), + Kind: vs.Kind, + Name: vs.Name, + }, + } + + if storageClassName != "" { + spec.StorageClassName = &storageClassName + } + + if volumeMode != "" { + spec.VolumeMode = ptr.To(corev1.PersistentVolumeMode(volumeMode)) + } + + if vs.Status != nil && vs.Status.RestoreSize != nil { + spec.Resources = corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: *vs.Status.RestoreSize, + }, + } + } + + pvc = &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: namespacedName.Name, + Namespace: namespacedName.Namespace, + OwnerReferences: []metav1.OwnerReference{ + service.MakeOwnerReference(cvi), + }, + }, + Spec: spec, + } + + err = ds.diskService.CreatePersistentVolumeClaim(ctx, pvc) + if err != nil { + setPhaseConditionToFailed(cb, &cvi.Status.Phase, err) + return reconcile.Result{}, err + } + + cvi.Status.Phase = virtv2.ImageProvisioning + cb. + Status(metav1.ConditionFalse). + Reason(vicondition.Provisioning). + Message("PVC has created: waiting to be Bound.") + + cvi.Status.Progress = "0%" + cvi.Status.SourceUID = pointer.GetPointer(vs.UID) + + return reconcile.Result{Requeue: true}, err + case pod == nil: + cvi.Status.Progress = ds.statService.GetProgress(cvi.GetUID(), pod, cvi.Status.Progress) + cvi.Status.Target.RegistryURL = ds.statService.GetDVCRImageName(pod) + + envSettings := ds.getEnvSettings(cvi, supgen) + + ownerRef := metav1.NewControllerRef(cvi, cvi.GroupVersionKind()) + podSettings := ds.importerService.GetPodSettingsWithPVC(ownerRef, supgen, pvc.Name, pvc.Namespace) + err = ds.importerService.StartWithPodSetting(ctx, envSettings, supgen, datasource.NewCABundleForCVMI(cvi.Spec.DataSource), podSettings) + switch { + case err == nil: + // OK. + case common.ErrQuotaExceeded(err): + ds.recorder.Event(cvi, corev1.EventTypeWarning, virtv2.ReasonDataSourceQuotaExceeded, "DataSource quota exceed") + return setQuotaExceededPhaseCondition(cb, &cvi.Status.Phase, err, cvi.CreationTimestamp), nil + default: + setPhaseConditionToFailed(cb, &cvi.Status.Phase, fmt.Errorf("unexpected error: %w", err)) + return reconcile.Result{}, err + } + + cvi.Status.Phase = virtv2.ImageProvisioning + cb. + Status(metav1.ConditionFalse). + Reason(vicondition.Provisioning). + Message("DVCR Provisioner not found: create the new one.") + + log.Info("Create importer pod...", "progress", cvi.Status.Progress, "pod.phase", "nil") + + return reconcile.Result{Requeue: true}, nil + case podutil.IsPodComplete(pod): + err = ds.statService.CheckPod(pod) + if err != nil { + cvi.Status.Phase = virtv2.ImageFailed + + switch { + case errors.Is(err, service.ErrProvisioningFailed): + ds.recorder.Event(cvi, corev1.EventTypeWarning, virtv2.ReasonDataSourceDiskProvisioningFailed, "Disk provisioning failed") + cb. + Status(metav1.ConditionFalse). + Reason(vicondition.ProvisioningFailed). + Message(service.CapitalizeFirstLetter(err.Error() + ".")) + return reconcile.Result{}, nil + default: + return reconcile.Result{}, err + } + } + + cb. + Status(metav1.ConditionTrue). + Reason(vicondition.Ready). + Message("") + + cvi.Status.Phase = virtv2.ImageReady + cvi.Status.Size = ds.statService.GetSize(pod) + cvi.Status.CDROM = ds.statService.GetCDROM(pod) + cvi.Status.Format = ds.statService.GetFormat(pod) + cvi.Status.Progress = "100%" + cvi.Status.Target.RegistryURL = ds.statService.GetDVCRImageName(pod) + + log.Info("Ready", "progress", cvi.Status.Progress, "pod.phase", pod.Status.Phase) + default: + err = ds.statService.CheckPod(pod) + if err != nil { + cvi.Status.Phase = virtv2.ImageFailed + + switch { + case errors.Is(err, service.ErrNotInitialized), errors.Is(err, service.ErrNotScheduled): + cb. + Status(metav1.ConditionFalse). + Reason(vicondition.ProvisioningNotStarted). + Message(service.CapitalizeFirstLetter(err.Error() + ".")) + return reconcile.Result{}, nil + case errors.Is(err, service.ErrProvisioningFailed): + ds.recorder.Event(cvi, corev1.EventTypeWarning, virtv2.ReasonDataSourceDiskProvisioningFailed, "Disk provisioning failed") + cb. + Status(metav1.ConditionFalse). + Reason(vicondition.ProvisioningFailed). + Message(service.CapitalizeFirstLetter(err.Error() + ".")) + return reconcile.Result{}, nil + default: + return reconcile.Result{}, err + } + } + + err = ds.importerService.Protect(ctx, pod) + if err != nil { + return reconcile.Result{}, err + } + + cb. + Status(metav1.ConditionFalse). + Reason(vicondition.Provisioning). + Message("Import is in the process of provisioning to DVCR.") + + cvi.Status.Phase = virtv2.ImageProvisioning + cvi.Status.Progress = ds.statService.GetProgress(cvi.GetUID(), pod, cvi.Status.Progress) + cvi.Status.Target.RegistryURL = ds.statService.GetDVCRImageName(pod) + + log.Info("Provisioning...", "progress", cvi.Status.Progress, "pod.phase", pod.Status.Phase) + } + + return reconcile.Result{Requeue: true}, nil +} + +func (ds ObjectRefVirtualDiskSnapshot) CleanUpSupplements(ctx context.Context, cvi *virtv2.ClusterVirtualImage) (reconcile.Result, error) { + supgen := supplements.NewGenerator(annotations.VIShortName, cvi.Name, cvi.Spec.DataSource.ObjectRef.Namespace, cvi.UID) + + importerRequeue, err := ds.importerService.CleanUpSupplements(ctx, supgen) + if err != nil { + return reconcile.Result{}, err + } + + diskRequeue, err := ds.diskService.CleanUpSupplements(ctx, supgen) + if err != nil { + return reconcile.Result{}, err + } + + pvcCleanupRequeue, err := ds.diskService.CleanUp(ctx, supgen) + if err != nil { + return reconcile.Result{}, err + } + + return reconcile.Result{Requeue: importerRequeue || diskRequeue || pvcCleanupRequeue}, nil +} + +func (ds ObjectRefVirtualDiskSnapshot) CleanUp(ctx context.Context, cvi *virtv2.ClusterVirtualImage) (bool, error) { + supgen := supplements.NewGenerator(annotations.VIShortName, cvi.Name, cvi.Spec.DataSource.ObjectRef.Namespace, cvi.UID) + + importerRequeue, err := ds.importerService.CleanUp(ctx, supgen) + if err != nil { + return false, err + } + + diskRequeue, err := ds.diskService.CleanUp(ctx, supgen) + if err != nil { + return false, err + } + + return importerRequeue || diskRequeue, nil +} + +func (ds ObjectRefVirtualDiskSnapshot) getEnvSettings(cvi *virtv2.ClusterVirtualImage, sup *supplements.Generator) *importer.Settings { + var settings importer.Settings + importer.ApplyBlockDeviceSourceSettings(&settings) + importer.ApplyDVCRDestinationSettings( + &settings, + ds.dvcrSettings, + sup, + ds.dvcrSettings.RegistryImageForVI(cvi), + ) + + return &settings +} + +func (ds ObjectRefVirtualDiskSnapshot) Validate(ctx context.Context, cvi *virtv2.ClusterVirtualImage) error { + if cvi.Spec.DataSource.ObjectRef == nil || cvi.Spec.DataSource.ObjectRef.Kind != virtv2.ClusterVirtualImageObjectRefKindVirtualDiskSnapshot { + return fmt.Errorf("not a %s data source", virtv2.ClusterVirtualImageObjectRefKindVirtualDiskSnapshot) + } + + vdSnapshot, err := ds.diskService.GetVirtualDiskSnapshot(ctx, cvi.Spec.DataSource.ObjectRef.Name, cvi.Spec.DataSource.ObjectRef.Namespace) + if err != nil { + return err + } + + if vdSnapshot == nil || vdSnapshot.Status.Phase != virtv2.VirtualDiskSnapshotPhaseReady { + return NewVirtualDiskSnapshotNotReadyError(cvi.Spec.DataSource.ObjectRef.Name) + } + + volumeSnapshot, err := ds.diskService.GetVolumeSnapshot(ctx, vdSnapshot.Status.VolumeSnapshotName, vdSnapshot.Namespace) + if err != nil { + return err + } + + if volumeSnapshot == nil || !*volumeSnapshot.Status.ReadyToUse { + return NewVirtualDiskSnapshotNotReadyError(cvi.Spec.DataSource.ObjectRef.Name) + } + + return nil +}