diff --git a/PROJECT b/PROJECT index 908616b..bda3032 100644 --- a/PROJECT +++ b/PROJECT @@ -54,4 +54,13 @@ resources: kind: VirtualMachineSnapshotPolicy path: github.com/alperencelik/kubemox/api/proxmox/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: alperen.cloud + group: proxmox + kind: Container + path: github.com/alperencelik/kubemox/api/proxmox/v1alpha1 + version: v1alpha1 version: "3" diff --git a/README.md b/README.md index 6b27052..f4cf8b2 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ helm install kubemox ./ -f values.yaml -n $NAMESPACE Currently Kubemox brings five different CRDs for only VirtualMachines in Proxmox. These are `VirtualMachine`, `VirtualMachineSet`, `ManagedVirtualMachine`, `VirtualMachineSnapshot` and `VirtualMachineSnapshotPolicy`. You can use these CRDs to create and manage VirtualMachine(s) in Proxmox. +### VirtualMachine + `VirtualMachine` is a way to create new VirtualMachines in Proxmox via operator. You can create `VirtualMachine` resource and Kubemox will create it for you in Proxmox. `VirtualMachine` is also reconciled by the operator which means every change on `VirtualMachine` resource will be reflected to Proxmox as well. `VirtualMachineSet` is a way to create multiple VirtualMachines in Proxmox. The relationship between `VirtualMachineSet` and `VirtualMachine` is similar to the relationship between `Deployment` and `Pod`. `VirtualMachineSet` creates multiple `VirtualMachine` resources and Kubemox will create them for you in Proxmox. You can only use `VirtualMachineSet` with templates. Creating multiple VirtualMachines from scratch is not supported yet. @@ -40,6 +42,11 @@ Currently Kubemox brings five different CRDs for only VirtualMachines in Proxmox `VirtualMachineSnapshot` is helping to create snapshots for `VirtualMachine` object. This object mostly considered for the milestone snapshots. This will create only one snapshot for the `VirtualMachine` object. Also deleting the `VirtualMachineSnapshot` object won't be deleting the snapshot from Proxmox since the current proxmox client the project uses doesn't have an implementation for deleting snapshots. `VirtualMachineSnapshotPolicy` is helping to create snapshots for `VirtualMachine` object periodically. This object mostly considered for the scheduled snapshots. The schedule and the selectors that you specify matches with the `VirtualMachine` objects and according to the schedule it will create snapshots for those `VirtualMachine` objects. `VirtualMachineSnapshotPolicy` will be spawning `VirtualMachineSnapshot` objects for each `VirtualMachine` object that matches with the selectors. Also deleting the `VirtualMachineSnapshotPolicy` object also won't be deleting the snapshots from Proxmox but it will stop creating new `VirtualMachineSnapshot` objects for the `VirtualMachine` objects that matches with the selectors. + +### Containers + +`Container` is a way to create new Linux containers (LXC) in Proxmox via operator. You can create `Container` resource and Kubemox will create it for you in Proxmox. `Container` is also reconciled by the operator which means every change on `Container` resource will be reflected to Proxmox as well. `Container` object should be generated by other container template objects. This is a requirement that comes from Proxmox itself. + ### Create a VirtualMachine To create a VirtualMachine you can use the following `VirtualMachine` resource: @@ -123,7 +130,33 @@ spec: my-label: my-virtualmachine ``` -To learn more about `VirtualMachine`, `VirtualMachineSet` and `VirtualMachineSnapshot` resources you can check `charts/kubemox/samples/` +### Create a Container + +To create a Container you can use the following `Container` resource: + +```yaml +apiVersion: proxmox.alperen.cloud/v1alpha1 +kind: Container +metadata: + name: container-new +spec: + name: container-new + nodeName: lowtower + template: + # This template should be generated by other container template object + name: test-container + cores: 2 + memory: 4096 # As MB + disk: + - storage: nvme + size: 50 # As GB + type: scsi + network: + - model: virtio + bridge: vmbr0 +``` + +To learn more about `VirtualMachine`, `VirtualMachineSet`, `Container` and `VirtualMachineSnapshot` resources you can check `charts/kubemox/samples/` ## Developing diff --git a/api/proxmox/v1alpha1/container_types.go b/api/proxmox/v1alpha1/container_types.go new file mode 100644 index 0000000..5ff3363 --- /dev/null +++ b/api/proxmox/v1alpha1/container_types.go @@ -0,0 +1,103 @@ +/* +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. + +// ContainerSpec defines the desired state of Container +type ContainerSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Name is the name of the Container + Name string `json:"name"` + // NodeName is the name of the target node of Proxmox + NodeName string `json:"nodeName"` + // TemplateSpec of the source Container + Template ContainerTemplate `json:"template,omitempty"` + // This field should be modified further +} + +type ContainerTemplate struct { + // Name of the template + Name string `json:"name,omitempty"` + // Cores is the number of CPU cores + Cores int `json:"cores,omitempty"` + // Memory is the amount of memory in MB + Memory int `json:"memory,omitempty"` + // Disks is the list of disks + Disk []ContainerTemplateDisk `json:"disk,omitempty"` + // Networks is the list of networks + Network []ContainerTemplateNetwork `json:"network,omitempty"` +} + +type ContainerTemplateDisk struct { + // Storage is the name of the storage + Storage string `json:"storage,omitempty"` + // Size is the size of the disk + Size int `json:"size,omitempty"` + // Type is the type of the disk + Type string `json:"type,omitempty"` +} + +type ContainerTemplateNetwork struct { + // Name is the name of the network + Model string `json:"model,omitempty"` + // Bridge is the name of the bridge + Bridge string `json:"bridge,omitempty"` +} + +// ContainerStatus defines the observed state of Container +type ContainerStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + State string `json:"state,omitempty"` + Node string `json:"node,omitempty"` + Name string `json:"name,omitempty"` + ID int `json:"id,omitempty"` + Uptime string `json:"uptime,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// Container is the Schema for the containers API +type Container struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ContainerSpec `json:"spec,omitempty"` + Status ContainerStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// ContainerList contains a list of Container +type ContainerList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Container `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Container{}, &ContainerList{}) +} diff --git a/api/proxmox/v1alpha1/zz_generated.deepcopy.go b/api/proxmox/v1alpha1/zz_generated.deepcopy.go index e0da4c6..ddea066 100644 --- a/api/proxmox/v1alpha1/zz_generated.deepcopy.go +++ b/api/proxmox/v1alpha1/zz_generated.deepcopy.go @@ -26,6 +26,151 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Container) DeepCopyInto(out *Container) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Container. +func (in *Container) DeepCopy() *Container { + if in == nil { + return nil + } + out := new(Container) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Container) 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 *ContainerList) DeepCopyInto(out *ContainerList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Container, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerList. +func (in *ContainerList) DeepCopy() *ContainerList { + if in == nil { + return nil + } + out := new(ContainerList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ContainerList) 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 *ContainerSpec) DeepCopyInto(out *ContainerSpec) { + *out = *in + in.Template.DeepCopyInto(&out.Template) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerSpec. +func (in *ContainerSpec) DeepCopy() *ContainerSpec { + if in == nil { + return nil + } + out := new(ContainerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ContainerStatus) DeepCopyInto(out *ContainerStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerStatus. +func (in *ContainerStatus) DeepCopy() *ContainerStatus { + if in == nil { + return nil + } + out := new(ContainerStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ContainerTemplate) DeepCopyInto(out *ContainerTemplate) { + *out = *in + if in.Disk != nil { + in, out := &in.Disk, &out.Disk + *out = make([]ContainerTemplateDisk, len(*in)) + copy(*out, *in) + } + if in.Network != nil { + in, out := &in.Network, &out.Network + *out = make([]ContainerTemplateNetwork, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerTemplate. +func (in *ContainerTemplate) DeepCopy() *ContainerTemplate { + if in == nil { + return nil + } + out := new(ContainerTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ContainerTemplateDisk) DeepCopyInto(out *ContainerTemplateDisk) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerTemplateDisk. +func (in *ContainerTemplateDisk) DeepCopy() *ContainerTemplateDisk { + if in == nil { + return nil + } + out := new(ContainerTemplateDisk) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ContainerTemplateNetwork) DeepCopyInto(out *ContainerTemplateNetwork) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerTemplateNetwork. +func (in *ContainerTemplateNetwork) DeepCopy() *ContainerTemplateNetwork { + if in == nil { + return nil + } + out := new(ContainerTemplateNetwork) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ManagedVirtualMachine) DeepCopyInto(out *ManagedVirtualMachine) { *out = *in diff --git a/charts/kubemox/samples/container.yaml b/charts/kubemox/samples/container.yaml new file mode 100644 index 0000000..9638801 --- /dev/null +++ b/charts/kubemox/samples/container.yaml @@ -0,0 +1,19 @@ +apiVersion: proxmox.alperen.cloud/v1alpha1 +kind: Container +metadata: + name: container-new +spec: + name: container-new + nodeName: lowtower + template: + # Name of the template to be cloned + name: test-container + cores: 2 + memory: 4096 # As MB + disk: + - storage: nvme + size: 50 # As GB + type: scsi + network: + - model: virtio + bridge: vmbr0 \ No newline at end of file diff --git a/charts/kubemox/templates/role.yaml b/charts/kubemox/templates/role.yaml index 457b14b..5b9ae72 100644 --- a/charts/kubemox/templates/role.yaml +++ b/charts/kubemox/templates/role.yaml @@ -145,4 +145,38 @@ rules: verbs: - get - patch + - update +- apiGroups: + - proxmox.alperen.cloud + resources: + - containers/status + verbs: + - get + - patch + - update +- apiGroups: + - proxmox.alperen.cloud + resources: + - containers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - proxmox.alperen.cloud + resources: + - containers/finalizers + verbs: + - update +- apiGroups: + - proxmox.alperen.cloud + resources: + - containers/status + verbs: + - get + - patch - update \ No newline at end of file diff --git a/cmd/main.go b/cmd/main.go index b11d444..968bcfe 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -126,6 +126,13 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "VirtualMachineSnapshotPolicy") os.Exit(1) } + if err = (&proxmoxcontroller.ContainerReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Container") + os.Exit(1) + } //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { diff --git a/config/crd/bases/proxmox.alperen.cloud_containers.yaml b/config/crd/bases/proxmox.alperen.cloud_containers.yaml new file mode 100644 index 0000000..28f1887 --- /dev/null +++ b/config/crd/bases/proxmox.alperen.cloud_containers.yaml @@ -0,0 +1,108 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.12.0 + name: containers.proxmox.alperen.cloud +spec: + group: proxmox.alperen.cloud + names: + kind: Container + listKind: ContainerList + plural: containers + singular: container + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Container is the Schema for the containers 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: ContainerSpec defines the desired state of Container + properties: + name: + description: Name is the name of the Container + type: string + nodeName: + description: NodeName is the name of the target node of Proxmox + type: string + template: + description: TemplateSpec of the source Container + properties: + cores: + description: Cores is the number of CPU cores + type: integer + disk: + description: Disks is the list of disks + items: + properties: + size: + description: Size is the size of the disk + type: integer + storage: + description: Storage is the name of the storage + type: string + type: + description: Type is the type of the disk + type: string + type: object + type: array + memory: + description: Memory is the amount of memory in MB + type: integer + name: + description: Name of the template + type: string + network: + description: Networks is the list of networks + items: + properties: + bridge: + description: Bridge is the name of the bridge + type: string + model: + description: Name is the name of the network + type: string + type: object + type: array + type: object + required: + - name + - nodeName + type: object + status: + description: ContainerStatus defines the observed state of Container + properties: + id: + type: integer + name: + type: string + node: + type: string + state: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' + type: string + uptime: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index b52f4c1..e6bd192 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -7,6 +7,7 @@ resources: - bases/proxmox.alperen.cloud_virtualmachinesets.yaml - bases/proxmox.alperen.cloud_virtualmachinesnapshots.yaml - bases/proxmox.alperen.cloud_virtualmachinesnapshotpolicies.yaml +- bases/proxmox.alperen.cloud_containers.yaml #+kubebuilder:scaffold:crdkustomizeresource patches: @@ -17,6 +18,7 @@ patches: #- path: patches/webhook_in_virtualmachinesets.yaml #- path: patches/webhook_in_virtualmachinesnapshots.yaml #- path: patches/webhook_in_virtualmachinesnapshotpolicies.yaml +#- path: patches/webhook_in_containers.yaml #+kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. @@ -26,6 +28,7 @@ patches: #- path: patches/cainjection_in_virtualmachinesets.yaml #- path: patches/cainjection_in_virtualmachinesnapshots.yaml #- path: patches/cainjection_in_virtualmachinesnapshotpolicies.yaml +#- path: patches/cainjection_in_containers.yaml #+kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_proxmox_containers.yaml b/config/crd/patches/cainjection_in_proxmox_containers.yaml new file mode 100644 index 0000000..4e339b7 --- /dev/null +++ b/config/crd/patches/cainjection_in_proxmox_containers.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME + name: containers.proxmox.alperen.cloud diff --git a/config/crd/patches/webhook_in_proxmox_containers.yaml b/config/crd/patches/webhook_in_proxmox_containers.yaml new file mode 100644 index 0000000..3d5c172 --- /dev/null +++ b/config/crd/patches/webhook_in_proxmox_containers.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: containers.proxmox.alperen.cloud +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/config/rbac/proxmox_container_editor_role.yaml b/config/rbac/proxmox_container_editor_role.yaml new file mode 100644 index 0000000..7cf0b10 --- /dev/null +++ b/config/rbac/proxmox_container_editor_role.yaml @@ -0,0 +1,31 @@ +# permissions for end users to edit containers. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: container-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: container-editor-role +rules: +- apiGroups: + - proxmox.alperen.cloud + resources: + - containers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - proxmox.alperen.cloud + resources: + - containers/status + verbs: + - get diff --git a/config/rbac/proxmox_container_viewer_role.yaml b/config/rbac/proxmox_container_viewer_role.yaml new file mode 100644 index 0000000..2a087a0 --- /dev/null +++ b/config/rbac/proxmox_container_viewer_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to view containers. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: container-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: container-viewer-role +rules: +- apiGroups: + - proxmox.alperen.cloud + resources: + - containers + verbs: + - get + - list + - watch +- apiGroups: + - proxmox.alperen.cloud + resources: + - containers/status + verbs: + - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index a114b01..fa4b478 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -16,6 +16,32 @@ rules: - patch - update - watch +- apiGroups: + - proxmox.alperen.cloud + resources: + - containers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - proxmox.alperen.cloud + resources: + - containers/finalizers + verbs: + - update +- apiGroups: + - proxmox.alperen.cloud + resources: + - containers/status + verbs: + - get + - patch + - update - apiGroups: - proxmox.alperen.cloud resources: diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 35912a7..78bf305 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -5,4 +5,5 @@ resources: - proxmox_v1alpha1_virtualmachineset.yaml - proxmox_v1alpha1_virtualmachinesnapshot.yaml - proxmox_v1alpha1_virtualmachinesnapshotpolicy.yaml +- proxmox_v1alpha1_container.yaml #+kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/proxmox_v1alpha1_container.yaml b/config/samples/proxmox_v1alpha1_container.yaml new file mode 100644 index 0000000..9c82fb8 --- /dev/null +++ b/config/samples/proxmox_v1alpha1_container.yaml @@ -0,0 +1,24 @@ +apiVersion: proxmox.alperen.cloud/v1alpha1 +kind: Container +metadata: + labels: + app.kubernetes.io/name: container + app.kubernetes.io/instance: container-sample + app.kubernetes.io/part-of: kubemox + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: kubemox + name: container-sample +spec: + name: container-sample + nodeName: lowtower + template: + name: test-container + cores: 2 + memory: 4096 # As MB + disk: + - storage: nvme + size: 50 # As GB + type: scsi + network: + - model: virtio + bridge: vmbr0 diff --git a/config/samples/proxmox_v1alpha1_container2.yaml b/config/samples/proxmox_v1alpha1_container2.yaml new file mode 100644 index 0000000..64c150e --- /dev/null +++ b/config/samples/proxmox_v1alpha1_container2.yaml @@ -0,0 +1,24 @@ +apiVersion: proxmox.alperen.cloud/v1alpha1 +kind: Container +metadata: + labels: + app.kubernetes.io/name: container + app.kubernetes.io/instance: container-new + app.kubernetes.io/part-of: kubemox + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: kubemox + name: container-new +spec: + name: container-new + nodeName: lowtower + template: + name: test-container + cores: 2 + memory: 4096 # As MB + disk: + - storage: nvme + size: 50 # As GB + type: scsi + network: + - model: virtio + bridge: vmbr0 diff --git a/go.mod b/go.mod index a1adfae..672623d 100644 --- a/go.mod +++ b/go.mod @@ -1,15 +1,15 @@ module github.com/alperencelik/kubemox -go 1.20 +go 1.21 require ( - github.com/luthermonson/go-proxmox v0.0.0-alpha8 + github.com/luthermonson/go-proxmox v0.0.0-beta1.0.20231115161518-f6c641c6c80b github.com/onsi/ginkgo/v2 v2.9.5 github.com/onsi/gomega v1.27.7 github.com/robfig/cron/v3 v3.0.1 - k8s.io/api v0.27.2 - k8s.io/apimachinery v0.27.2 - k8s.io/client-go v0.27.2 + k8s.io/api v0.28.3 + k8s.io/apimachinery v0.28.3 + k8s.io/client-go v0.28.3 sigs.k8s.io/controller-runtime v0.15.0 ) @@ -25,15 +25,15 @@ require ( github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/zapr v1.2.4 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect - github.com/go-openapi/jsonreference v0.20.1 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.5.9 // indirect - github.com/google/gofuzz v1.1.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect @@ -56,11 +56,11 @@ require ( go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/net v0.10.0 // indirect - golang.org/x/oauth2 v0.5.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/term v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/oauth2 v0.8.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/term v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.9.1 // indirect gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect @@ -72,9 +72,9 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.27.2 // indirect k8s.io/component-base v0.27.2 // indirect - k8s.io/klog/v2 v2.90.1 // indirect - k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect - k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect + k8s.io/klog/v2 v2.100.1 // indirect + k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect + k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/go.sum b/go.sum index e14eb02..d8f00aa 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,4 @@ 4d63.com/gochecknoinits v0.0.0-20200108094044-eb73b47b9fc4/go.mod h1:4o1i5aXtIF5tJFt3UD1knCVmWOXg7fLYdHVu6jeNcnM= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -7,25 +6,21 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY= github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/diskfs/go-diskfs v1.2.0 h1:Ow4xorEDw1VNYKbC+SA/qQNwi5gWIwdKUxmUcLFST24= github.com/diskfs/go-diskfs v1.2.0/go.mod h1:ZTeTbzixuyfnZW5y5qKMtjV2o+GLLHo1KfMhotJI4Rk= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= @@ -37,43 +32,29 @@ github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= -github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= @@ -84,7 +65,9 @@ github.com/gordonklaus/ineffassign v0.0.0-20190601041439-ed7b1b5ee0f8/go.mod h1: github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE= +github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -99,15 +82,15 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/luthermonson/go-proxmox v0.0.0-alpha8 h1:wlQzpYsnp+UcL8WoHodpse4ZUSb9YsAE2aKLD4Sr3U4= -github.com/luthermonson/go-proxmox v0.0.0-alpha8/go.mod h1:wkD6045y9lKBCP0sJGjNqmlBCo0vwRwnfhmsrPBTu34= +github.com/luthermonson/go-proxmox v0.0.0-beta1.0.20231115161518-f6c641c6c80b h1:3XcE5fIF26Ls8Y1kbYAoJhzUyxAa4qkRjaD0mZCyh7s= +github.com/luthermonson/go-proxmox v0.0.0-beta1.0.20231115161518-f6c641c6c80b/go.mod h1:wkD6045y9lKBCP0sJGjNqmlBCo0vwRwnfhmsrPBTu34= github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo= github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= @@ -135,7 +118,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= @@ -146,22 +128,22 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stripe/safesql v0.2.0/go.mod h1:q7b2n0JmzM1mVGfcYpanfVb2j23cXZeWFxcILPn3JV4= github.com/tsenart/deadcode v0.0.0-20160724212837-210d2dc333e9/go.mod h1:q+QjxYvZ+fpjMXqs+XEriussHjSYqeXVnAdSV1tkMYk= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= @@ -172,6 +154,7 @@ go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= @@ -180,10 +163,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= @@ -191,9 +170,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= @@ -201,19 +178,15 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181021155630-eda9bb28ed51/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -227,23 +200,20 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200102200121-6de373a2766c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -258,32 +228,14 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.3.0 h1:8NFhfS6gzxNqjLIYnZxg319wZ5Qjnx4m/CcX+Klzazc= gomodules.xyz/jsonpatch/v2 v2.3.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/djherbis/times.v1 v1.2.0 h1:UCvDKl1L/fmBygl2Y7hubXCnY7t4Yj46ZrBFNUipFbM= @@ -291,33 +243,29 @@ gopkg.in/djherbis/times.v1 v1.2.0/go.mod h1:AQlg6unIsrsCEdQYhTzERy542dz6SFdQFZFv gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -k8s.io/api v0.27.2 h1:+H17AJpUMvl+clT+BPnKf0E3ksMAzoBBg7CntpSuADo= -k8s.io/api v0.27.2/go.mod h1:ENmbocXfBT2ADujUXcBhHV55RIT31IIEvkntP6vZKS4= +k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM= +k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc= k8s.io/apiextensions-apiserver v0.27.2 h1:iwhyoeS4xj9Y7v8YExhUwbVuBhMr3Q4bd/laClBV6Bo= k8s.io/apiextensions-apiserver v0.27.2/go.mod h1:Oz9UdvGguL3ULgRdY9QMUzL2RZImotgxvGjdWRq6ZXQ= -k8s.io/apimachinery v0.27.2 h1:vBjGaKKieaIreI+oQwELalVG4d8f3YAMNpWLzDXkxeg= -k8s.io/apimachinery v0.27.2/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= -k8s.io/client-go v0.27.2 h1:vDLSeuYvCHKeoQRhCXjxXO45nHVv2Ip4Fe0MfioMrhE= -k8s.io/client-go v0.27.2/go.mod h1:tY0gVmUsHrAmjzHX9zs7eCjxcBsf8IiNe7KQ52biTcQ= +k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A= +k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8= +k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4= +k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo= k8s.io/component-base v0.27.2 h1:neju+7s/r5O4x4/txeUONNTS9r1HsPbyoPBAtHsDCpo= k8s.io/component-base v0.27.2/go.mod h1:5UPk7EjfgrfgRIuDBFtsEFAe4DAvP3U+M8RTzoSJkpo= -k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= -k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= -k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= -k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= +k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= +k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= sigs.k8s.io/controller-runtime v0.15.0 h1:ML+5Adt3qZnMSYxZ7gAverBLNPSMQEibtzAgp0UPojU= diff --git a/internal/controller/proxmox/container_controller.go b/internal/controller/proxmox/container_controller.go new file mode 100644 index 0000000..543c015 --- /dev/null +++ b/internal/controller/proxmox/container_controller.go @@ -0,0 +1,164 @@ +/* +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" + + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + "github.com/alperencelik/kubemox/pkg/proxmox" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + proxmoxv1alpha1 "github.com/alperencelik/kubemox/api/proxmox/v1alpha1" +) + +// ContainerReconciler reconciles a Container object +type ContainerReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +const ( + containerFinalizerName = "container.proxmox.alperen.cloud/finalizer" +) + +//+kubebuilder:rbac:groups=proxmox.alperen.cloud,resources=containers,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=proxmox.alperen.cloud,resources=containers/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=proxmox.alperen.cloud,resources=containers/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 Container 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.15.0/pkg/reconcile +func (r *ContainerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + Log := log.FromContext(ctx) + // Get the Container resource with this namespace/name + container := &proxmoxv1alpha1.Container{} + err := r.Get(ctx, req.NamespacedName, container) + if err != nil { + // Error reading the object - requeue the request. + Log.Error(err, "Failed to get Container") + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + containerName := container.Spec.Name + nodeName := container.Spec.NodeName + + resourceKey := fmt.Sprintf("%s/%s", container.Namespace, container.Name) + + // Check if the Container instance is marked to be deleted, which is indicated by the deletion timestamp being set. + if container.ObjectMeta.DeletionTimestamp.IsZero() { + // The object is not being deleted, so if it does not have our finalizer, then lets add the finalizer and update the object. + if !controllerutil.ContainsFinalizer(container, containerFinalizerName) { + controllerutil.AddFinalizer(container, containerFinalizerName) + if err := r.Update(ctx, container); err != nil { + log.Log.Error(err, "Error updating Container") + return ctrl.Result{}, client.IgnoreNotFound(err) + } + } + } else { + // The object is being deleted + if controllerutil.ContainsFinalizer(container, containerFinalizerName) { + // Delete the Container + deletionKey := fmt.Sprintf("%s/%s-deletion", container.Namespace, container.Name) + if isProcessed(deletionKey) { + } else { + // Delete the Container + proxmox.DeleteContainer(containerName, nodeName) + // Mark it as processed + processedResources[deletionKey] = true + } + // Remove finalizer + controllerutil.RemoveFinalizer(container, containerFinalizerName) + if err := r.Update(ctx, container); err != nil { + Log.Error(err, "Error updating Container") + } + } + // Stop reconciliation as the item is being deleted + return ctrl.Result{}, client.IgnoreNotFound(err) + } + containerExists := proxmox.ContainerExists(containerName, nodeName) + if containerExists { + // Update Container + containerState := proxmox.GetContainerState(containerName, nodeName) + if containerState == "stopped" { + proxmox.StartContainer(containerName, nodeName) + } else { + if isProcessed(resourceKey) { + } else { + Log.Info(fmt.Sprintf("Container %s already exists and running", containerName)) + // Mark it as processed + processedResources[resourceKey] = true + } + // Update Container status + containerStatus := proxmox.UpdateContainerStatus(containerName, nodeName) + container.Status.State = containerStatus.State + container.Status.ID = containerStatus.ID + container.Status.Name = containerStatus.Name + container.Status.Node = containerStatus.Node + container.Status.Uptime = containerStatus.Uptime + // // Update Container + err = r.Status().Update(context.Background(), container) + if err != nil { + Log.Error(err, "Failed to update Container") + return ctrl.Result{}, client.IgnoreNotFound(err) + } + // Update Container + proxmox.UpdateContainer(container) + err = r.Update(context.Background(), container) + if err != nil { + Log.Error(err, "Failed to update Container") + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + } + } else { + // Create Container + err = proxmox.CloneContainer(container) + if err != nil { + Log.Error(err, "Failed to clone Container") + return ctrl.Result{}, client.IgnoreNotFound(err) + } + proxmox.StartContainer(containerName, nodeName) + } + return ctrl.Result{Requeue: true, RequeueAfter: 10 * time.Second}, client.IgnoreNotFound(err) +} + +// SetupWithManager sets up the controller with the Manager. +func (r *ContainerReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&proxmoxv1alpha1.Container{}). + WithEventFilter(predicate.GenerationChangedPredicate{}). + WithOptions(controller.Options{MaxConcurrentReconciles: 1}). + Complete(&ContainerReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }) +} diff --git a/pkg/kubernetes/kubernetes.go b/pkg/kubernetes/kubernetes.go index 3f77797..ce5ca00 100644 --- a/pkg/kubernetes/kubernetes.go +++ b/pkg/kubernetes/kubernetes.go @@ -2,8 +2,10 @@ package kubernetes import ( "context" + "flag" "fmt" "os" + "path/filepath" "time" proxmoxv1alpha1 "github.com/alperencelik/kubemox/api/proxmox/v1alpha1" @@ -12,17 +14,57 @@ import ( "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/util/homedir" ) var ( Clientset, DynamicClient = GetKubeconfig() ) -func GetKubeconfig() (*kubernetes.Clientset, dynamic.Interface) { - config, err := rest.InClusterConfig() +func InsideCluster() bool { + // Check if kubeconfig exists under home directory + homeDir, err := os.UserHomeDir() if err != nil { panic(err.Error()) } + kubeconfig := filepath.Join(homeDir, ".kube", "config") + + if _, err := os.Stat(kubeconfig); os.IsNotExist(err) { + // kubeconfig doesn't exist + return true + } + return false +} + +func ClientConfig() interface{} { + if InsideCluster() { + config, err := rest.InClusterConfig() + if err != nil { + panic(err.Error()) + } + return config + } else { + var kubeconfig *string + flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError) + if home := homedir.HomeDir(); home != "" { + kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file") + } else { + kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file") + } + flag.Parse() + config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig) + if err != nil { + panic(err.Error()) + } + return config + } +} + +func GetKubeconfig() (*kubernetes.Clientset, dynamic.Interface) { + + config := ClientConfig().(*rest.Config) + clientset, err := kubernetes.NewForConfig(config) if err != nil { panic(err.Error()) diff --git a/pkg/proxmox/proxmox.go b/pkg/proxmox/proxmox.go index 6051c6e..19363ea 100644 --- a/pkg/proxmox/proxmox.go +++ b/pkg/proxmox/proxmox.go @@ -10,6 +10,7 @@ import ( "strconv" "strings" "sync" + "time" proxmoxv1alpha1 "github.com/alperencelik/kubemox/api/proxmox/v1alpha1" kubernetes "github.com/alperencelik/kubemox/pkg/kubernetes" @@ -22,6 +23,7 @@ import ( var ( // Create Proxmox client Client = CreateProxmoxClient() + ctx = context.Background() ) type ProxmoxConfig struct { @@ -103,7 +105,7 @@ func CreateProxmoxClient() *proxmox.Client { func GetProxmoxVersion() (*proxmox.Version, error) { // Get the version of the Proxmox server - version, err := Client.Version() + version, err := Client.Version(ctx) if err != nil { return nil, err } @@ -112,7 +114,7 @@ func GetProxmoxVersion() (*proxmox.Version, error) { func GetNodes() ([]string, error) { // Get all nodes - nodes, err := Client.Nodes() + nodes, err := Client.Nodes(ctx) nodeNames := []string{} for _, node := range nodes { nodeNames = append(nodeNames, node.Node) @@ -126,13 +128,13 @@ func GetNodes() ([]string, error) { func CreateVMFromTemplate(vm *proxmoxv1alpha1.VirtualMachine) { nodeName := vm.Spec.NodeName - node, err := Client.Node(nodeName) + node, err := Client.Node(ctx, nodeName) if err != nil { panic(err) } templateVMName := vm.Spec.Template.Name templateVMID := GetVMID(templateVMName, nodeName) - templateVM, err := node.VirtualMachine(templateVMID) + templateVM, err := node.VirtualMachine(ctx, templateVMID) if err != nil { log.Log.Error(err, "Error getting template VM") } @@ -143,7 +145,7 @@ func CreateVMFromTemplate(vm *proxmoxv1alpha1.VirtualMachine) { log.Log.Info(fmt.Sprintf("Creating VM from template: %s", templateVMName)) // Make sure that not two VMs are created at the exact time mutex.Lock() - newID, task, err := templateVM.Clone(&CloneOptions) + newID, task, err := templateVM.Clone(ctx, &CloneOptions) if err != nil { log.Log.Error(err, "Error creating VM") } @@ -154,7 +156,7 @@ func CreateVMFromTemplate(vm *proxmoxv1alpha1.VirtualMachine) { // UPID := task.UPID // log.Log.Info(fmt.Sprintf("VM creation task UPID: %s", UPID)) // TODO: Implement a better way to watch the task - logChan, err := task.Watch(0) + logChan, err := task.Watch(ctx, 0) if err != nil { panic(err) } @@ -177,7 +179,7 @@ func CreateVMFromTemplate(vm *proxmoxv1alpha1.VirtualMachine) { // wg.Add(500) // wg.Wait() mutex.Lock() - _, taskCompleted, taskErr := task.WaitForCompleteStatus(virtualMachineCreateTimesNum, virtualMachineCreateSteps) + _, taskCompleted, taskErr := task.WaitForCompleteStatus(ctx, virtualMachineCreateTimesNum, virtualMachineCreateSteps) if !taskCompleted { log.Log.Error(taskErr, "Error creating VM") } else if taskCompleted { @@ -189,9 +191,9 @@ func CreateVMFromTemplate(vm *proxmoxv1alpha1.VirtualMachine) { } // Add tag to VM - VirtualMachine, err := node.VirtualMachine(newID) - addTagTask, _ := VirtualMachine.AddTag(virtualMachineTag) - _, taskCompleted, taskErr = addTagTask.WaitForCompleteStatus(5, 3) + VirtualMachine, err := node.VirtualMachine(ctx, newID) + addTagTask, _ := VirtualMachine.AddTag(ctx, virtualMachineTag) + _, taskCompleted, taskErr = addTagTask.WaitForCompleteStatus(ctx, 5, 3) if !taskCompleted { log.Log.Error(taskErr, "Error adding tag to VM") } @@ -203,11 +205,11 @@ func CreateVMFromTemplate(vm *proxmoxv1alpha1.VirtualMachine) { } func GetVMID(vmName, nodeName string) int { - node, err := Client.Node(nodeName) + node, err := Client.Node(ctx, nodeName) if err != nil { panic(err) } - vmList, err := node.VirtualMachines() + vmList, err := node.VirtualMachines(ctx) if err != nil { panic(err) } @@ -224,11 +226,11 @@ func GetVMID(vmName, nodeName string) int { } func CheckVM(vmName, nodeName string) bool { - node, err := Client.Node(nodeName) + node, err := Client.Node(ctx, nodeName) if err != nil { panic(err) } - vmList, err := node.VirtualMachines() + vmList, err := node.VirtualMachines(ctx) if err != nil { panic(err) } @@ -242,18 +244,18 @@ func CheckVM(vmName, nodeName string) bool { } func GetVMIPAddress(vmName, nodeName string) string { - node, err := Client.Node(nodeName) + node, err := Client.Node(ctx, nodeName) if err != nil { panic(err) } // Get VMID vmID := GetVMID(vmName, nodeName) - VirtualMachine, err := node.VirtualMachine(vmID) + VirtualMachine, err := node.VirtualMachine(ctx, vmID) if err != nil { log.Log.Error(err, "Error getting VM") } // Get VM IP - VirtualMachineIfaces, err := VirtualMachine.AgentGetNetworkIFaces() + VirtualMachineIfaces, err := VirtualMachine.AgentGetNetworkIFaces(ctx) if err != nil { log.Log.Error(err, "Error getting VM IP") } @@ -266,18 +268,18 @@ func GetVMIPAddress(vmName, nodeName string) string { } func GetOSInfo(vmName, nodeName string) string { - node, err := Client.Node(nodeName) + node, err := Client.Node(ctx, nodeName) if err != nil { panic(err) } // Get VMID vmID := GetVMID(vmName, nodeName) - VirtualMachine, err := node.VirtualMachine(vmID) + VirtualMachine, err := node.VirtualMachine(ctx, vmID) if err != nil { log.Log.Error(err, "Error getting VM") } // Get VM OS - VirtualMachineOS, err := VirtualMachine.AgentOsInfo() + VirtualMachineOS, err := VirtualMachine.AgentOsInfo(ctx) if err != nil { log.Log.Error(err, "Error getting VM OS") } @@ -285,36 +287,32 @@ func GetOSInfo(vmName, nodeName string) string { } func GetVMUptime(vmName, nodeName string) string { - node, err := Client.Node(nodeName) + node, err := Client.Node(ctx, nodeName) if err != nil { panic(err) } // Get VMID vmID := GetVMID(vmName, nodeName) - VirtualMachine, err := node.VirtualMachine(vmID) + VirtualMachine, err := node.VirtualMachine(ctx, vmID) if err != nil { log.Log.Error(err, "Error getting VM") } // Get VM Uptime as seconds VirtualMachineUptime := int(VirtualMachine.Uptime) // Convert seconds to format like 1d 2h 3m 4s - days := VirtualMachineUptime / 86400 - hours := (VirtualMachineUptime - days*86400) / 3600 - minutes := (VirtualMachineUptime - days*86400 - hours*3600) / 60 - seconds := VirtualMachineUptime - days*86400 - hours*3600 - minutes*60 - uptime := fmt.Sprintf("%dd%dh%dm%ds", days, hours, minutes, seconds) + uptime := FormatUptime(VirtualMachineUptime) return uptime } func DeleteVM(vmName, nodeName string) { - node, err := Client.Node(nodeName) + node, err := Client.Node(ctx, nodeName) if err != nil { panic(err) } // Get VMID mutex.Lock() vmID := GetVMID(vmName, nodeName) - VirtualMachine, err := node.VirtualMachine(vmID) + VirtualMachine, err := node.VirtualMachine(ctx, vmID) if err != nil { log.Log.Error(err, "Error getting VM") } @@ -322,11 +320,11 @@ func DeleteVM(vmName, nodeName string) { // Stop VM vmStatus := VirtualMachine.Status if vmStatus == "running" { - task, err := VirtualMachine.Stop() + task, err := VirtualMachine.Stop(ctx) if err != nil { panic(err) } - _, taskCompleted, taskErr := task.WaitForCompleteStatus(virtualMachineStopTimesNum, virtualMachineStopSteps) + _, taskCompleted, taskErr := task.WaitForCompleteStatus(ctx, virtualMachineStopTimesNum, virtualMachineStopSteps) if !taskCompleted { log.Log.Error(taskErr, "Can't stop VM") @@ -337,11 +335,11 @@ func DeleteVM(vmName, nodeName string) { } } // Delete VM - task, err := VirtualMachine.Delete() + task, err := VirtualMachine.Delete(ctx) if err != nil { panic(err) } - _, taskCompleted, taskErr := task.WaitForCompleteStatus(3, 20) + _, taskCompleted, taskErr := task.WaitForCompleteStatus(ctx, 3, 20) if !taskCompleted { log.Log.Error(taskErr, "Can't delete VM") } else if taskCompleted { @@ -352,22 +350,22 @@ func DeleteVM(vmName, nodeName string) { } func StartVM(vmName, nodeName string) { - node, err := Client.Node(nodeName) + node, err := Client.Node(ctx, nodeName) if err != nil { panic(err) } // Get VMID vmID := GetVMID(vmName, nodeName) - VirtualMachine, err := node.VirtualMachine(vmID) + VirtualMachine, err := node.VirtualMachine(ctx, vmID) if err != nil { log.Log.Error(err, "Error getting VM") } // Start VM - task, err := VirtualMachine.Start() + task, err := VirtualMachine.Start(ctx) if err != nil { panic(err) } - _, taskCompleted, taskErr := task.WaitForCompleteStatus(virtualMachineStartTimesNum, virtualMachineStartSteps) + _, taskCompleted, taskErr := task.WaitForCompleteStatus(ctx, virtualMachineStartTimesNum, virtualMachineStartSteps) if !taskCompleted { log.Log.Error(taskErr, "Can't start VM") } else if taskCompleted { @@ -378,18 +376,18 @@ func StartVM(vmName, nodeName string) { } func RestartVM(vmName, nodeName string) *proxmox.Task { - node, err := Client.Node(nodeName) + node, err := Client.Node(ctx, nodeName) if err != nil { panic(err) } // Get VMID vmID := GetVMID(vmName, nodeName) - VirtualMachine, err := node.VirtualMachine(vmID) + VirtualMachine, err := node.VirtualMachine(ctx, vmID) if err != nil { log.Log.Error(err, "Error getting VM") } // Restart VM - task, err := VirtualMachine.Reboot() + task, err := VirtualMachine.Reboot(ctx) if err != nil { panic(err) } @@ -398,12 +396,12 @@ func RestartVM(vmName, nodeName string) *proxmox.Task { func GetVMState(vmName string, nodeName string) string { // Gets the VMstate from Proxmox API - node, err := Client.Node(nodeName) + node, err := Client.Node(ctx, nodeName) if err != nil { log.Log.Error(err, "Error getting node") } vmID := GetVMID(vmName, nodeName) - VirtualMachine, err := node.VirtualMachine(vmID) + VirtualMachine, err := node.VirtualMachine(ctx, vmID) VirtualMachineState := VirtualMachine.Status if err != nil { panic(err) @@ -419,10 +417,10 @@ func GetVMState(vmName string, nodeName string) string { func AgentIsRunning(vmName, nodeName string) bool { // Checks if qemu-agent works on specified VM - node, _ := Client.Node(nodeName) + node, _ := Client.Node(ctx, nodeName) vmID := GetVMID(vmName, nodeName) - VirtualMachine, _ := node.VirtualMachine(vmID) - err := VirtualMachine.WaitForAgent(AgentTimeoutSeconds) + VirtualMachine, _ := node.VirtualMachine(ctx, vmID) + err := VirtualMachine.WaitForAgent(ctx, AgentTimeoutSeconds) if err != nil { return false } else { @@ -433,7 +431,7 @@ func AgentIsRunning(vmName, nodeName string) bool { func CreateVMFromScratch(vm *proxmoxv1alpha1.VirtualMachine) { nodeName := vm.Spec.NodeName - node, err := Client.Node(nodeName) + node, err := Client.Node(ctx, nodeName) if err != nil { panic(err) } @@ -474,20 +472,20 @@ func CreateVMFromScratch(vm *proxmoxv1alpha1.VirtualMachine) { }, } // Get next VMID - cluster, err := Client.Cluster() + cluster, err := Client.Cluster(ctx) if err != nil { panic(err) } - vmID, err := cluster.NextID() + vmID, err := cluster.NextID(ctx) if err != nil { panic(err) } // Create VM - task, err := node.NewVirtualMachine(vmID, VMOptions...) + task, err := node.NewVirtualMachine(ctx, vmID, VMOptions...) if err != nil { panic(err) } - _, taskCompleted, taskErr := task.WaitForCompleteStatus(10, 10) + _, taskCompleted, taskErr := task.WaitForCompleteStatus(ctx, 10, 10) if !taskCompleted { log.Log.Error(taskErr, "Can't create VM") } else if taskCompleted { @@ -495,12 +493,12 @@ func CreateVMFromScratch(vm *proxmoxv1alpha1.VirtualMachine) { } else { log.Log.Info("VM is already created") } - VirtualMachine, err := node.VirtualMachine(vmID) + VirtualMachine, err := node.VirtualMachine(ctx, vmID) if err != nil { panic(err) } - addTagTask, err := VirtualMachine.AddTag(virtualMachineTag) - _, taskCompleted, taskErr = addTagTask.WaitForCompleteStatus(1, 10) + addTagTask, err := VirtualMachine.AddTag(ctx, virtualMachineTag) + _, taskCompleted, taskErr = addTagTask.WaitForCompleteStatus(ctx, 1, 10) if !taskCompleted { log.Log.Error(taskErr, "Can't add tag to VM") } @@ -566,11 +564,11 @@ func GetProxmoxVMs() []string { var VMs []string nodes := GetOnlineNodes() for _, node := range nodes { - node, err := Client.Node(node) + node, err := Client.Node(ctx, node) if err != nil { panic(err) } - VirtualMachines, err := node.VirtualMachines() + VirtualMachines, err := node.VirtualMachines(ctx) if err != nil { panic(err) } @@ -583,7 +581,7 @@ func GetProxmoxVMs() []string { func GetOnlineNodes() []string { - nodes, err := Client.Nodes() + nodes, err := Client.Nodes(ctx) var OnlineNodes []string if err != nil { panic(err) @@ -602,11 +600,11 @@ func GetControllerVMs() []string { nodes := GetOnlineNodes() var ControllerVMs []string for _, node := range nodes { - node, err := Client.Node(node) + node, err := Client.Node(ctx, node) if err != nil { panic(err) } - VirtualMachines, err := node.VirtualMachines() + VirtualMachines, err := node.VirtualMachines(ctx) if err != nil { panic(err) } @@ -635,12 +633,12 @@ func CheckManagedVMExists(ManagedVM string) bool { func GetNodeOfVM(vmName string) string { nodes := GetOnlineNodes() for _, node := range nodes { - node, err := Client.Node(node) + node, err := Client.Node(ctx, node) if err != nil { panic(err) } // List VMs on node - VirtualMachines, err := node.VirtualMachines() + VirtualMachines, err := node.VirtualMachines(ctx) if err != nil { panic(err) } @@ -660,12 +658,12 @@ func GetNodeOfVM(vmName string) string { func GetManagedVMSpec(ManagedVMName, nodeName string) (int, int, int) { // Get spec of VM - node, err := Client.Node(nodeName) + node, err := Client.Node(ctx, nodeName) if err != nil { panic(err) } vmID := GetVMID(ManagedVMName, nodeName) - VirtualMachine, err := node.VirtualMachine(vmID) + VirtualMachine, err := node.VirtualMachine(ctx, vmID) if err != nil { log.Log.Error(err, "Error getting VM") } @@ -678,7 +676,7 @@ func GetManagedVMSpec(ManagedVMName, nodeName string) (int, int, int) { func UpdateVMStatus(vmName, nodeName string) (string, int, string, string, string, string, string) { - node, err := Client.Node(nodeName) + node, err := Client.Node(ctx, nodeName) if err != nil { panic(err) } @@ -686,7 +684,7 @@ func UpdateVMStatus(vmName, nodeName string) (string, int, string, string, strin if CheckVM(vmName, nodeName) { // Get VMID vmID := GetVMID(vmName, nodeName) - VirtualMachine, err := node.VirtualMachine(vmID) + VirtualMachine, err := node.VirtualMachine(ctx, vmID) if err != nil { panic(err) } @@ -711,13 +709,13 @@ func UpdateVMStatus(vmName, nodeName string) (string, int, string, string, strin } func UpdateVM(vmName, nodeName string, vm *proxmoxv1alpha1.VirtualMachine) { - node, err := Client.Node(nodeName) + node, err := Client.Node(ctx, nodeName) if err != nil { panic(err) } // Get VMID vmID := GetVMID(vmName, nodeName) - VirtualMachine, err := node.VirtualMachine(vmID) + VirtualMachine, err := node.VirtualMachine(ctx, vmID) if err != nil { log.Log.Error(err, "Error getting VM") } @@ -754,7 +752,7 @@ func UpdateVM(vmName, nodeName string, vm *proxmoxv1alpha1.VirtualMachine) { //// if current disk is lower than the updated disk size then resize the disk else don't do anything if VirtualMachineMaxDisk <= uint64(DiskSizeInt) { //// Resize Disk - err = VirtualMachine.ResizeDisk(Disk, DiskSize) + err = VirtualMachine.ResizeDisk(ctx, Disk, DiskSize) if err != nil { log.Log.Error(err, "Can't resize disk") } @@ -773,12 +771,12 @@ func UpdateVM(vmName, nodeName string, vm *proxmoxv1alpha1.VirtualMachine) { if VirtualMachine.CPUs != cpuOption.Value || VirtualMachineMem != memoryOption.Value { var task *proxmox.Task - task, err = VirtualMachine.Config(cpuOption, memoryOption) + task, err = VirtualMachine.Config(ctx, cpuOption, memoryOption) if err != nil { panic(err) } - _, taskCompleted, taskErr := task.WaitForCompleteStatus(virtualMachineUpdateTimesNum, virtualMachineUpdateSteps) + _, taskCompleted, taskErr := task.WaitForCompleteStatus(ctx, virtualMachineUpdateTimesNum, virtualMachineUpdateSteps) if !taskCompleted { log.Log.Error(taskErr, "Can't update VM") } else if taskCompleted { @@ -788,7 +786,7 @@ func UpdateVM(vmName, nodeName string, vm *proxmoxv1alpha1.VirtualMachine) { } // After config update, restart VM task = RestartVM(vmName, nodeName) - _, taskCompleted, taskErr = task.WaitForCompleteStatus(virtualMachineRestartTimesNum, virtualMachineRestartSteps) + _, taskCompleted, taskErr = task.WaitForCompleteStatus(ctx, virtualMachineRestartTimesNum, virtualMachineRestartSteps) if !taskCompleted { log.Log.Error(taskErr, "Can't restart VM") } @@ -861,13 +859,13 @@ func UpdateManagedVM(managedVMName, nodeName string, managedVM *proxmoxv1alpha1. return } else { - node, err := Client.Node(nodeName) + node, err := Client.Node(ctx, nodeName) if err != nil { panic(err) } // Get VMID vmID := GetVMID(managedVMName, nodeName) - VirtualMachine, err := node.VirtualMachine(vmID) + VirtualMachine, err := node.VirtualMachine(ctx, vmID) if err != nil { log.Log.Error(err, "Error getting VM") } @@ -886,7 +884,7 @@ func UpdateManagedVM(managedVMName, nodeName string, managedVM *proxmoxv1alpha1. // convert string to uint64 if VirtualMachineMaxDisk <= uint64(diskSize) { // Resize Disk - err = VirtualMachine.ResizeDisk(disk, strconv.Itoa(diskSize)+"G") + err = VirtualMachine.ResizeDisk(ctx, disk, strconv.Itoa(diskSize)+"G") if err != nil { log.Log.Error(err, "Can't resize disk") } @@ -899,11 +897,11 @@ func UpdateManagedVM(managedVMName, nodeName string, managedVM *proxmoxv1alpha1. if VirtualMachine.CPUs != managedVM.Spec.Cores || VirtualMachineMem != uint64(managedVM.Spec.Memory) { // Update VM // log.Log.Info(fmt.Sprintf("The comparison between CR and external resource: CPU: %d, %d || Memory: %d, %d", managedVM.Spec.Cores, VirtualMachine.CPUs, managedVM.Spec.Memory, VirtualMachineMem)) - task, err := VirtualMachine.Config(cpuOption, memoryOption) + task, err := VirtualMachine.Config(ctx, cpuOption, memoryOption) if err != nil { panic(err) } - _, taskCompleted, taskErr := task.WaitForCompleteStatus(virtualMachineUpdateTimesNum, virtualMachineUpdateSteps) + _, taskCompleted, taskErr := task.WaitForCompleteStatus(ctx, virtualMachineUpdateTimesNum, virtualMachineUpdateSteps) if !taskCompleted { log.Log.Error(taskErr, "Can't update VM") } else if taskCompleted { @@ -912,7 +910,7 @@ func UpdateManagedVM(managedVMName, nodeName string, managedVM *proxmoxv1alpha1. log.Log.Info("VM is already updated") } task = RestartVM(managedVMName, nodeName) - _, taskCompleted, taskErr = task.WaitForCompleteStatus(virtualMachineRestartTimesNum, virtualMachineRestartSteps) + _, taskCompleted, taskErr = task.WaitForCompleteStatus(ctx, virtualMachineRestartTimesNum, virtualMachineRestartSteps) if !taskCompleted { log.Log.Error(taskErr, "Can't restart VM") } @@ -956,22 +954,22 @@ func SubstractLowercaseSlices(slice1, slice2 []string) []string { func CreateVMSnapshot(vmName, snapshotName string) (statusCode int) { nodeName := GetNodeOfVM(vmName) - node, err := Client.Node(nodeName) + node, err := Client.Node(ctx, nodeName) if err != nil { panic(err) } // Get VMID vmID := GetVMID(vmName, nodeName) - VirtualMachine, err := node.VirtualMachine(vmID) + VirtualMachine, err := node.VirtualMachine(ctx, vmID) if err != nil { log.Log.Error(err, "Error getting VM") } // Create snapshot - task, err := VirtualMachine.NewSnapshot(snapshotName) + task, err := VirtualMachine.NewSnapshot(ctx, snapshotName) if err != nil { panic(err) } - _, taskCompleted, taskErr := task.WaitForCompleteStatus(3, 10) + _, taskCompleted, taskErr := task.WaitForCompleteStatus(ctx, 3, 10) if !taskCompleted { log.Log.Error(taskErr, "Can't create snapshot for the VirtualMachine %s", vmName) return 1 @@ -983,3 +981,229 @@ func CreateVMSnapshot(vmName, snapshotName string) (statusCode int) { return 2 } } + +func CloneContainer(container *proxmoxv1alpha1.Container) error { + nodeName := container.Spec.NodeName + node, err := Client.Node(ctx, nodeName) + if err != nil { + panic(err) + } + templateContainerName := container.Spec.Template.Name + templateContainerID := 101 + templateContainer, err := node.Container(ctx, templateContainerID) + if err != nil { + panic(err) + } + + var CloneOptions proxmox.ContainerCloneOptions + CloneOptions.Full = 1 + CloneOptions.Hostname = container.Name + CloneOptions.Target = nodeName + log.Log.Info(fmt.Sprintf("Cloning container %s from template %s", container.Name, templateContainerName)) + + _, task, err := templateContainer.Clone(ctx, &CloneOptions) + if err != nil { + log.Log.Error(err, "Can't clone container") + } + if err != nil { + panic(err) + } + _, taskCompleted, taskErr := task.WaitForCompleteStatus(ctx, 5, 10) + if !taskCompleted { + log.Log.Error(taskErr, "Can't clone container") + } + + return taskErr +} + +func GetContainerID(containerName, nodeName string) int { + node, err := Client.Node(ctx, nodeName) + if err != nil { + panic(err) + } + containers, err := node.Containers(ctx) + if err != nil { + panic(err) + } + for _, container := range containers { + if container.Name == containerName { + return int(container.VMID) + } + } + return 0 +} + +func ContainerExists(containerName, nodeName string) bool { + node, err := Client.Node(ctx, nodeName) + if err != nil { + panic(err) + } + containers, err := node.Containers(ctx) + if err != nil { + panic(err) + } + for _, container := range containers { + if container.Name == containerName { + return true + } + } + return false +} + +func GetContainer(containerName, nodeName string) *proxmox.Container { + node, err := Client.Node(ctx, nodeName) + if err != nil { + panic(err) + } + containerID := GetContainerID(containerName, nodeName) + container, err := node.Container(ctx, containerID) + if err != nil { + panic(err) + } + return container +} + +func StopContainer(containerName, nodeName string) (*proxmox.ContainerStatus, error) { + // Get container + log.Log.Info(fmt.Sprintf("Stopping container %s", containerName)) + container := GetContainer(containerName, nodeName) + // Stop container + if container.Status == "running" { + // Stop container called + status, err := container.Stop(ctx) + // Retry method to understand if container is stopped + for i := 0; i < 5; i++ { + contStatus := GetContainerState(containerName, nodeName) + if contStatus == "stopped" { + break + } else { + time.Sleep(5 * time.Second) + } + } + return status, err + } else { + return nil, nil + } +} + +func DeleteContainer(containerName, nodeName string) { + // Get container + mutex.Lock() + container := GetContainer(containerName, nodeName) + mutex.Unlock() + containerStatus := container.Status + if containerStatus == "running" { + // Stop container + _, err := StopContainer(containerName, nodeName) + if err != nil { + panic(err) + } + + } + log.Log.Info(fmt.Sprintf("Deleting container %s", containerName)) + // Delete container + mutex.Lock() + // Delete container + task, err := container.Delete(ctx) + if err != nil { + panic(err) + } + _, taskCompleted, taskErr := task.WaitForCompleteStatus(ctx, 5, 5) + if !taskCompleted { + log.Log.Error(taskErr, "Can't delete container") + } else if taskCompleted { + log.Log.Info(fmt.Sprintf("Container %s has been deleted", containerName)) + } else { + log.Log.Info("Container is already deleted") + } + mutex.Unlock() +} + +func StartContainer(containerName, nodeName string) { + // Get container + container := GetContainer(containerName, nodeName) + // Start container + status, err := container.Start(ctx) + log.Log.Info(fmt.Sprintf("Container %s status: %s", containerName, status)) + if err != nil { + log.Log.Error(err, "Can't start container") + } +} + +func GetContainerState(containerName, nodeName string) string { + // Get container + container := GetContainer(containerName, nodeName) + // Get container state + return container.Status +} + +func UpdateContainerStatus(containerName, nodeName string) proxmoxv1alpha1.ContainerStatus { + var containerStatus proxmoxv1alpha1.ContainerStatus + container := GetContainer(containerName, nodeName) + + containerStatus.State = container.Status + containerStatus.ID = int(container.VMID) + containerStatus.Uptime = FormatUptime(int(container.Uptime)) + containerStatus.Node = container.Node + containerStatus.Name = container.Name + + return containerStatus + +} + +func UpdateContainer(container *proxmoxv1alpha1.Container) { + // Get container from proxmox + containerName := container.Name + nodeName := container.Spec.NodeName + var cpuOption proxmox.ContainerOption + var memoryOption proxmox.ContainerOption + cpuOption.Name = "cores" + memoryOption.Name = "memory" + ProxmoxContainer := GetContainer(containerName, nodeName) + // Check if update is needed + if container.Spec.Template.Cores != ProxmoxContainer.CPUs || container.Spec.Template.Memory != int(ProxmoxContainer.MaxMem/1024/1024) { + cpuOption.Value = container.Spec.Template.Cores + memoryOption.Value = container.Spec.Template.Memory + // Update container + _, err := ProxmoxContainer.Config(ctx, cpuOption, memoryOption) + if err != nil { + panic(err) + } else { + log.Log.Info(fmt.Sprintf("Container %s has been updated", containerName)) + } + // Config of container doesn't require restart + } + +} + +func RestartContainer(containerName, nodeName string) bool { + + // Get container + container := GetContainer(containerName, nodeName) + // Restart container + _, err := container.Reboot(ctx) + if err != nil { + panic(err) + } + // Retry method to understand if container is stopped + for i := 0; i < 5; i++ { + contStatus := GetContainerState(containerName, nodeName) + if contStatus == "running" { + return true + } else { + time.Sleep(5 * time.Second) + } + } + return false + +} + +func FormatUptime(uptime int) string { + // Convert seconds to format like 1d 2h 3m 4s + days := uptime / 86400 + hours := (uptime - days*86400) / 3600 + minutes := (uptime - days*86400 - hours*3600) / 60 + seconds := uptime - days*86400 - hours*3600 - minutes*60 + uptimeString := fmt.Sprintf("%dd%dh%dm%ds", days, hours, minutes, seconds) + return uptimeString +}