summary history files

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
}