diff --git a/PROJECT b/PROJECT index bc95993..107ab20 100644 --- a/PROJECT +++ b/PROJECT @@ -74,4 +74,13 @@ resources: kind: CustomCertificate path: github.com/alperencelik/kubemox/api/proxmox/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: alperen.cloud + group: proxmox + kind: StorageDownloadURL + path: github.com/alperencelik/kubemox/api/proxmox/v1alpha1 + version: v1alpha1 version: "3" diff --git a/api/proxmox/v1alpha1/storagedownloadurl_types.go b/api/proxmox/v1alpha1/storagedownloadurl_types.go new file mode 100644 index 0000000..17e5668 --- /dev/null +++ b/api/proxmox/v1alpha1/storagedownloadurl_types.go @@ -0,0 +1,72 @@ +/* +Copyright 2023. + +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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// StorageDownloadURLSpec defines the desired state of StorageDownloadURL +type StorageDownloadURLSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + Content string `json:"content"` + Filename string `json:"filename"` + Node string `json:"node"` + Storage string `json:"storage"` + URL string `json:"url"` + // Optional fields + Checksum string `json:"checksum,omitempty"` + ChecksumAlgorithm string `json:"checksumAlgorithm,omitempty"` + Compression string `json:"compression,omitempty"` + VerifyCertificate bool `json:"verifyCertificate,omitempty"` +} + +// StorageDownloadURLStatus defines the observed state of StorageDownloadURL +type StorageDownloadURLStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// StorageDownloadURL is the Schema for the storagedownloadurls API +type StorageDownloadURL struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec StorageDownloadURLSpec `json:"spec,omitempty"` + Status StorageDownloadURLStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// StorageDownloadURLList contains a list of StorageDownloadURL +type StorageDownloadURLList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []StorageDownloadURL `json:"items"` +} + +func init() { + SchemeBuilder.Register(&StorageDownloadURL{}, &StorageDownloadURLList{}) +} diff --git a/api/proxmox/v1alpha1/zz_generated.deepcopy.go b/api/proxmox/v1alpha1/zz_generated.deepcopy.go index 5203739..b116e5d 100644 --- a/api/proxmox/v1alpha1/zz_generated.deepcopy.go +++ b/api/proxmox/v1alpha1/zz_generated.deepcopy.go @@ -461,6 +461,94 @@ func (in *NewVMSpecOSImage) DeepCopy() *NewVMSpecOSImage { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StorageDownloadURL) DeepCopyInto(out *StorageDownloadURL) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageDownloadURL. +func (in *StorageDownloadURL) DeepCopy() *StorageDownloadURL { + if in == nil { + return nil + } + out := new(StorageDownloadURL) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *StorageDownloadURL) 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 *StorageDownloadURLList) DeepCopyInto(out *StorageDownloadURLList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]StorageDownloadURL, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageDownloadURLList. +func (in *StorageDownloadURLList) DeepCopy() *StorageDownloadURLList { + if in == nil { + return nil + } + out := new(StorageDownloadURLList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *StorageDownloadURLList) 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 *StorageDownloadURLSpec) DeepCopyInto(out *StorageDownloadURLSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageDownloadURLSpec. +func (in *StorageDownloadURLSpec) DeepCopy() *StorageDownloadURLSpec { + if in == nil { + return nil + } + out := new(StorageDownloadURLSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StorageDownloadURLStatus) DeepCopyInto(out *StorageDownloadURLStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageDownloadURLStatus. +func (in *StorageDownloadURLStatus) DeepCopy() *StorageDownloadURLStatus { + if in == nil { + return nil + } + out := new(StorageDownloadURLStatus) + in.DeepCopyInto(out) + return out +} + func (in *ProxmoxCertSpec) DeepCopyInto(out *ProxmoxCertSpec) { *out = *in } diff --git a/cmd/main.go b/cmd/main.go index 506eca2..e0a2810 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -133,27 +133,33 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "Container") os.Exit(1) } - if err = (&proxmoxcontroller.CustomCertificateReconciler{ + if err = (&proxmoxcontroller.StorageDownloadURLReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "CustomCertificate") - os.Exit(1) - } - //+kubebuilder:scaffold:builder - - if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { - setupLog.Error(err, "unable to set up health check") - os.Exit(1) - } - if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { - setupLog.Error(err, "unable to set up ready check") - os.Exit(1) - } - - setupLog.Info("starting manager") - if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { - setupLog.Error(err, "problem running manager") - os.Exit(1) + setupLog.Error(err, "unable to create controller", "controller", "StorageDownloadURL") + if err = (&proxmoxcontroller.CustomCertificateReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "CustomCertificate") + os.Exit(1) + } + //+kubebuilder:scaffold:builder + + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up health check") + os.Exit(1) + } + if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up ready check") + os.Exit(1) + } + + setupLog.Info("starting manager") + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + setupLog.Error(err, "problem running manager") + os.Exit(1) + } } } diff --git a/config/crd/bases/proxmox.alperen.cloud_storagedownloadurls.yaml b/config/crd/bases/proxmox.alperen.cloud_storagedownloadurls.yaml new file mode 100644 index 0000000..84c6aef --- /dev/null +++ b/config/crd/bases/proxmox.alperen.cloud_storagedownloadurls.yaml @@ -0,0 +1,71 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.12.0 + name: storagedownloadurls.proxmox.alperen.cloud +spec: + group: proxmox.alperen.cloud + names: + kind: StorageDownloadURL + listKind: StorageDownloadURLList + plural: storagedownloadurls + singular: storagedownloadurl + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: StorageDownloadURL is the Schema for the storagedownloadurls + API + 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: + description: StorageDownloadURLSpec defines the desired state of StorageDownloadURL + properties: + checksum: + description: Optional fields + type: string + checksumAlgorithm: + type: string + compression: + type: string + content: + type: string + filename: + type: string + node: + type: string + storage: + type: string + url: + type: string + verifyCertificate: + type: boolean + required: + - content + - filename + - node + - storage + - url + type: object + status: + description: StorageDownloadURLStatus defines the observed state of StorageDownloadURL + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 57025be..2080fea 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -8,6 +8,7 @@ resources: - bases/proxmox.alperen.cloud_virtualmachinesnapshots.yaml - bases/proxmox.alperen.cloud_virtualmachinesnapshotpolicies.yaml - bases/proxmox.alperen.cloud_containers.yaml +- bases/proxmox.alperen.cloud_storagedownloadurls.yaml - bases/proxmox.alperen.cloud_customcertificates.yaml #+kubebuilder:scaffold:crdkustomizeresource @@ -20,6 +21,7 @@ patches: #- path: patches/webhook_in_virtualmachinesnapshots.yaml #- path: patches/webhook_in_virtualmachinesnapshotpolicies.yaml #- path: patches/webhook_in_containers.yaml +#- path: patches/webhook_in_proxmox_storagedownloadurls.yaml #- path: patches/webhook_in_proxmox_customcertificates.yaml #+kubebuilder:scaffold:crdkustomizewebhookpatch @@ -31,6 +33,7 @@ patches: #- path: patches/cainjection_in_virtualmachinesnapshots.yaml #- path: patches/cainjection_in_virtualmachinesnapshotpolicies.yaml #- path: patches/cainjection_in_containers.yaml +#- path: patches/cainjection_in_proxmox_storagedownloadurls.yaml #- path: patches/cainjection_in_proxmox_customcertificates.yaml #+kubebuilder:scaffold:crdkustomizecainjectionpatch diff --git a/config/rbac/proxmox_storagedownloadurl_editor_role.yaml b/config/rbac/proxmox_storagedownloadurl_editor_role.yaml new file mode 100644 index 0000000..712a4b1 --- /dev/null +++ b/config/rbac/proxmox_storagedownloadurl_editor_role.yaml @@ -0,0 +1,31 @@ +# permissions for end users to edit storagedownloadurls. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: storagedownloadurl-editor-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: kubemox + app.kubernetes.io/part-of: kubemox + app.kubernetes.io/managed-by: kustomize + name: storagedownloadurl-editor-role +rules: +- apiGroups: + - proxmox.alperen.cloud + resources: + - storagedownloadurls + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - proxmox.alperen.cloud + resources: + - storagedownloadurls/status + verbs: + - get diff --git a/config/rbac/proxmox_storagedownloadurl_viewer_role.yaml b/config/rbac/proxmox_storagedownloadurl_viewer_role.yaml new file mode 100644 index 0000000..93cbc0f --- /dev/null +++ b/config/rbac/proxmox_storagedownloadurl_viewer_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to view storagedownloadurls. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: storagedownloadurl-viewer-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: kubemox + app.kubernetes.io/part-of: kubemox + app.kubernetes.io/managed-by: kustomize + name: storagedownloadurl-viewer-role +rules: +- apiGroups: + - proxmox.alperen.cloud + resources: + - storagedownloadurls + verbs: + - get + - list + - watch +- apiGroups: + - proxmox.alperen.cloud + resources: + - storagedownloadurls/status + verbs: + - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index baa618a..c9cdd46 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -102,6 +102,32 @@ rules: - get - patch - update +- apiGroups: + - proxmox.alperen.cloud + resources: + - storagedownloadurls + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - proxmox.alperen.cloud + resources: + - storagedownloadurls/finalizers + verbs: + - update +- apiGroups: + - proxmox.alperen.cloud + resources: + - storagedownloadurls/status + verbs: + - get + - patch + - update - apiGroups: - proxmox.alperen.cloud resources: diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 6b6d840..f9aa991 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -6,5 +6,6 @@ resources: - proxmox_v1alpha1_virtualmachinesnapshot.yaml - proxmox_v1alpha1_virtualmachinesnapshotpolicy.yaml - proxmox_v1alpha1_container.yaml +- proxmox_v1alpha1_storagedownloadurl.yaml - proxmox_v1alpha1_customcertificate.yaml #+kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/proxmox_v1alpha1_storagedownloadurl.yaml b/config/samples/proxmox_v1alpha1_storagedownloadurl.yaml new file mode 100644 index 0000000..51a0d64 --- /dev/null +++ b/config/samples/proxmox_v1alpha1_storagedownloadurl.yaml @@ -0,0 +1,16 @@ +apiVersion: proxmox.alperen.cloud/v1alpha1 +kind: StorageDownloadURL +metadata: + labels: + app.kubernetes.io/name: storagedownloadurl + app.kubernetes.io/instance: storagedownloadurl-sample + app.kubernetes.io/part-of: kubemox + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: kubemox + name: storagedownloadurl-sample +spec: + content: "iso" + filename: "ubuntu-20.04-server-cloudimg-amd64.img" + node: "lowtower" + storage: "local" + url: "https://cloud-images.ubuntu.com/releases/focal/release/ubuntu-20.04-server-cloudimg-amd64.img" \ No newline at end of file diff --git a/internal/controller/proxmox/storagedownloadurl_controller.go b/internal/controller/proxmox/storagedownloadurl_controller.go new file mode 100644 index 0000000..3a5dfd9 --- /dev/null +++ b/internal/controller/proxmox/storagedownloadurl_controller.go @@ -0,0 +1,169 @@ +/* +Copyright 2023. + +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 proxmox + +import ( + "context" + "fmt" + "time" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + proxmoxv1alpha1 "github.com/alperencelik/kubemox/api/proxmox/v1alpha1" + "github.com/alperencelik/kubemox/pkg/proxmox" +) + +// StorageDownloadURLReconciler reconciles a StorageDownloadURL object +type StorageDownloadURLReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +const ( + storageDownloadURLFinalizerName = "storagedownloadurl.proxmox.alperen.cloud/finalizer" + + // Controller settings + SDUreconcilationPeriod = 10 + SDUmaxConcurrentReconciles = 3 + storageDownloadURLTimesNum = 5 + storageDownloadURLSteps = 12 +) + +//+kubebuilder:rbac:groups=proxmox.alperen.cloud,resources=storagedownloadurls,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=proxmox.alperen.cloud,resources=storagedownloadurls/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=proxmox.alperen.cloud,resources=storagedownloadurls/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the StorageDownloadURL object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.16.3/pkg/reconcile +func (r *StorageDownloadURLReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + Log := log.FromContext(ctx) + + // Fetch the StorageDownloadURL resource + storageDownloadURL := &proxmoxv1alpha1.StorageDownloadURL{} + err := r.Get(ctx, req.NamespacedName, storageDownloadURL) + if err != nil { + // Error reading the object - requeue the request. + return ctrl.Result{}, err + } + // Get fields from the spec + content := storageDownloadURL.Spec.Content + node := storageDownloadURL.Spec.Node + storage := storageDownloadURL.Spec.Storage + // Check if the content is only iso or vztmpl + if content != "iso" && content != "vztmpl" { + Log.Info("Content should be either iso or vztmpl") + return ctrl.Result{}, nil + } + if storageDownloadURL.ObjectMeta.DeletionTimestamp.IsZero() { + if !controllerutil.ContainsFinalizer(storageDownloadURL, storageDownloadURLFinalizerName) { + controllerutil.AddFinalizer(storageDownloadURL, storageDownloadURLFinalizerName) + if err = r.Update(ctx, storageDownloadURL); err != nil { + log.Log.Error(err, "unable to update StorageDownloadURL") + return ctrl.Result{}, client.IgnoreNotFound(err) + } + } + } else { + if controllerutil.ContainsFinalizer(storageDownloadURL, storageDownloadURLFinalizerName) { + deletionKey := fmt.Sprintf("%s/%s-deletion", storageDownloadURL.Namespace, storageDownloadURL.Name) + if isProcessed(deletionKey) { + } else { + // Delete the file from the storage + // TODO implement the deletion + processedResources[deletionKey] = true + } + controllerutil.RemoveFinalizer(storageDownloadURL, storageDownloadURLFinalizerName) + if err = r.Update(ctx, storageDownloadURL); err != nil { + log.Log.Error(err, "unable to update StorageDownloadURL") + return ctrl.Result{}, client.IgnoreNotFound(err) + } + } + // Stop reconciliation as the object is being deleted + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + // Check if the filename exists in the storage + storageContent, err := proxmox.GetStorageContent(node, storage) + if err != nil { + Log.Error(err, "unable to get storage content") + return ctrl.Result{}, err + } + // Check if the filename exists in the storage + if !proxmox.HasFile(storageContent, &storageDownloadURL.Spec) { + resourceKey := fmt.Sprintf("%s:%s/%s", storage, content, storageDownloadURL.Spec.Filename) + if isProcessed(resourceKey) { + } else { + Log.Info("File does not exist in the storage, so downloading it") + // Download the file + taskUPID, taskErr := proxmox.StorageDownloadURL(node, &storageDownloadURL.Spec) + if taskErr != nil { + Log.Error(taskErr, "unable to download the file") + return ctrl.Result{}, taskErr + } + // Get the task + task := proxmox.GetTask(taskUPID) + var logChannel <-chan string + logChannel, err = task.Watch(ctx, 5) + if err != nil { + Log.Error(err, "unable to watch the task") + return ctrl.Result{}, err + } + for logEntry := range logChannel { + log.Log.Info(fmt.Sprintf("Download task for %s: %s", storageDownloadURL.Spec.Filename, logEntry)) + } + _, taskCompleted, taskErr := task.WaitForCompleteStatus(ctx, storageDownloadURLTimesNum, storageDownloadURLSteps) + if taskErr != nil { + Log.Error(taskErr, "unable to get the task status") + return ctrl.Result{}, taskErr + } + switch { + case !taskCompleted: + log.Log.Error(taskErr, "Download task did not complete") + case taskCompleted: + Log.Info("Download task completed") + processedResources[resourceKey] = true + default: + Log.Info("Download task did not complete yet") + } + } + } + return ctrl.Result{Requeue: true, RequeueAfter: SDUreconcilationPeriod * time.Second}, client.IgnoreNotFound(err) +} + +// SetupWithManager sets up the controller with the Manager. +func (r *StorageDownloadURLReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&proxmoxv1alpha1.StorageDownloadURL{}). + WithEventFilter(predicate.GenerationChangedPredicate{}). + WithOptions(controller.Options{MaxConcurrentReconciles: SDUmaxConcurrentReconciles}). + Complete(&StorageDownloadURLReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }) +} diff --git a/pkg/proxmox/storage.go b/pkg/proxmox/storage.go new file mode 100644 index 0000000..798f22f --- /dev/null +++ b/pkg/proxmox/storage.go @@ -0,0 +1,59 @@ +package proxmox + +import ( + "fmt" + + proxmoxv1alpha1 "github.com/alperencelik/kubemox/api/proxmox/v1alpha1" + proxmox "github.com/luthermonson/go-proxmox" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +func StorageDownloadURL(node string, storageDownloadURLSpec *proxmoxv1alpha1.StorageDownloadURLSpec) (string, error) { + // Get node + Node, err := Client.Node(ctx, node) + if err != nil { + log.Log.Error(err, "unable to get node") + } + storageDownloadURLOptions := proxmox.StorageDownloadURLOptions{ + Content: storageDownloadURLSpec.Content, + Filename: storageDownloadURLSpec.Filename, + Node: storageDownloadURLSpec.Node, + Storage: storageDownloadURLSpec.Storage, + URL: storageDownloadURLSpec.URL, + // Optional parameters + Checksum: storageDownloadURLSpec.Checksum, + ChecksumAlgorithm: storageDownloadURLSpec.ChecksumAlgorithm, + Compression: storageDownloadURLSpec.Compression, + } + // Post request to get download URL + response, err := Node.StorageDownloadURL(ctx, &storageDownloadURLOptions) + if err != nil { + log.Log.Error(err, "unable to start download operation") + } + return response, err +} + +func GetStorageContent(node, storageName string) ([]*proxmox.StorageContent, error) { + // Get node + Node, err := Client.Node(ctx, node) + if err != nil { + log.Log.Error(err, "unable to get node") + } + storage, _ := Node.Storage(ctx, storageName) + // Get storage content + content, err := storage.GetContent(ctx) + if err != nil { + log.Log.Error(err, "unable to get storage content") + } + return content, err +} + +func HasFile(storageContent []*proxmox.StorageContent, storageDownloadSpec *proxmoxv1alpha1.StorageDownloadURLSpec) bool { + targetFile := fmt.Sprintf("%s:%s/%s", storageDownloadSpec.Storage, storageDownloadSpec.Content, storageDownloadSpec.Filename) + for _, item := range storageContent { + if item.Volid == targetFile { + return true + } + } + return false +} diff --git a/pkg/proxmox/tasks.go b/pkg/proxmox/tasks.go new file mode 100644 index 0000000..a2971e2 --- /dev/null +++ b/pkg/proxmox/tasks.go @@ -0,0 +1,12 @@ +package proxmox + +import ( + "github.com/luthermonson/go-proxmox" +) + +func GetTask(taskUPID string) *proxmox.Task { + taskID := proxmox.UPID( + taskUPID, + ) + return proxmox.NewTask(taskID, Client) +}