backend/services/persistent_volumes_service.go
package services
import (
"context"
"encoding/json"
"errors"
"fmt"
"kd/backend/config"
"kd/backend/logwrap"
"kd/backend/types"
"sync"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/kubectl/pkg/describe"
describecmd "k8s.io/kubectl/pkg/describe"
"k8s.io/kubectl/pkg/scheme"
)
type persistentVolumesService struct {
ctx context.Context
conf config.Config
logger *logwrap.LogWrap
}
var persistentVolumes *persistentVolumesService
var oncePersistentVolumes sync.Once
func PersistentVolumes() *persistentVolumesService {
if persistentVolumes == nil {
oncePersistentVolumes.Do(func() {
persistentVolumes = &persistentVolumesService{}
})
}
return persistentVolumes
}
func (p *persistentVolumesService) Start(ctx context.Context, conf config.Config, logger *logwrap.LogWrap) {
p.ctx = ctx
p.conf = conf
p.logger = logger
}
func (p *persistentVolumesService) List(context string) types.PersistentVolumesResponse {
r := types.NewPersistentVolumesResponse()
cs, err := p.conf.GetClientSet(context)
if err != nil {
r.Msg = "Failed to find context name"
return r
}
persistentVolumesList, err := cs.CoreV1().PersistentVolumes().List(p.ctx, metav1.ListOptions{})
if err != nil {
r.Msg = "Failed to list Persistent Volumes"
return r
}
r.Data = persistentVolumesList.Items
r.Success = true
return r
}
func (p *persistentVolumesService) Describe(context, namespace, name string) types.DescribeResponse {
r := types.NewDescribeResponse()
rc, err := p.conf.GetRestConfig(context)
if err != nil {
r.Msg = fmt.Sprintf("Failed to find rest config name: %s", err.Error())
return r
}
gk := schema.GroupKind{Group: "", Kind: "PersistentVolume"}
d, ok := describecmd.DescriberFor(gk, rc)
if !ok {
r.Msg = fmt.Sprintf("Failed to find describer: %s", err.Error())
return r
}
out, err := d.Describe(namespace, name, describe.DescriberSettings{})
if err != nil {
r.Msg = fmt.Sprintf("Failed to describe resource: %s", err.Error())
return r
}
r.Data = out
r.Success = true
return r
}
func (p *persistentVolumesService) Create(context string, definition any) types.Response {
r := types.NewResponse()
cs, err := p.conf.GetClientSet(context)
if err != nil {
r.Msg = fmt.Sprintf("Failed to find context name: %s", err.Error())
return r
}
persistentVolume, err := p.getPersistentVolumeFromDefinition(definition)
if err != nil {
r.Msg = fmt.Sprintf("Failed to get volume from definition: %s", err.Error())
return r
}
if _, err := cs.CoreV1().PersistentVolumes().Create(p.ctx, persistentVolume, metav1.CreateOptions{}); err != nil {
r.Msg = fmt.Sprintf("Failed to create resource: %s", err.Error())
return r
}
r.Msg = fmt.Sprintf("Persistent Volume created: %s", persistentVolume.GetName())
r.Success = true
return r
}
func (p *persistentVolumesService) getPersistentVolumeFromDefinition(definition any) (*corev1.PersistentVolume, error) {
var data []byte
var err error
switch v := definition.(type) {
case string:
data = []byte(v)
case []byte:
data = v
default:
data, err = json.Marshal(definition)
if err != nil {
return nil, fmt.Errorf("failed to marshal definition: %w", err)
}
}
decode := serializer.NewCodecFactory(scheme.Scheme).UniversalDeserializer().Decode
obj, gvk, err := decode(data, nil, nil)
if err != nil {
return nil, err
}
if gvk.Kind != "PersistentVolume" {
return nil, errors.New("definition does not define a PersistentVolume resource")
}
persistentVolume, ok := obj.(*corev1.PersistentVolume)
if !ok {
return nil, errors.New("failed to convert object to PersistentVolume")
}
return persistentVolume, nil
}
func (p *persistentVolumesService) Delete(context, name string) types.Response {
r := types.NewResponse()
cs, err := p.conf.GetClientSet(context)
if err != nil {
r.Msg = "Failed to find context name"
return r
}
client := cs.CoreV1().PersistentVolumes()
if err := client.Delete(p.ctx, name, kubernetesDeleteOptions); err != nil {
r.Msg = fmt.Sprintf("Failed to delete Persistent Volume: %s", err.Error())
return r
}
r.Msg = fmt.Sprintf("Persistent Volume deleted: %s", name)
r.Success = true
return r
}