package handler
import (
"bytes"
"context"
"fmt"
corev1 "k8s.io/api/core/v1"
storagev1 "k8s.io/api/storage/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/yaml"
"github.com/goodrain/rainbond/pkg/component/k8s"
)
type StorageClassInfo struct {
Name string `json:"name"`
Provisioner string `json:"provisioner"`
IsDefault bool `json:"is_default"`
ReclaimPolicy string `json:"reclaim_policy"`
VolumeBindingMode string `json:"volume_binding_mode"`
AllowVolumeExpansion bool `json:"allow_volume_expansion"`
PVCount int `json:"pv_count"`
}
type StorageHandler struct{}
func (h *StorageHandler) ListStorageClasses() ([]StorageClassInfo, error) {
client := k8s.Default().Clientset
scList, err := client.StorageV1().StorageClasses().List(context.Background(), metav1.ListOptions{})
if err != nil {
return nil, err
}
pvList, err := client.CoreV1().PersistentVolumes().List(context.Background(), metav1.ListOptions{})
if err != nil {
return nil, err
}
pvCount := make(map[string]int)
for _, pv := range pvList.Items {
if pv.Spec.StorageClassName != "" {
pvCount[pv.Spec.StorageClassName]++
}
}
var result []StorageClassInfo
for _, sc := range scList.Items {
isDefault := sc.Annotations["storageclass.kubernetes.io/is-default-class"] == "true"
reclaimPolicy := ""
if sc.ReclaimPolicy != nil {
reclaimPolicy = string(*sc.ReclaimPolicy)
}
volumeBindingMode := ""
if sc.VolumeBindingMode != nil {
volumeBindingMode = string(*sc.VolumeBindingMode)
}
allowExpansion := sc.AllowVolumeExpansion != nil && *sc.AllowVolumeExpansion
result = append(result, StorageClassInfo{
Name: sc.Name,
Provisioner: sc.Provisioner,
IsDefault: isDefault,
ReclaimPolicy: reclaimPolicy,
VolumeBindingMode: volumeBindingMode,
AllowVolumeExpansion: allowExpansion,
PVCount: pvCount[sc.Name],
})
}
return result, nil
}
func (h *StorageHandler) CreateStorageClass(yamlBody []byte) (*storagev1.StorageClass, error) {
sc := &storagev1.StorageClass{}
decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(yamlBody), 4096)
if err := decoder.Decode(sc); err != nil {
return nil, fmt.Errorf("invalid StorageClass YAML: %v", err)
}
return k8s.Default().Clientset.StorageV1().StorageClasses().Create(context.Background(), sc, metav1.CreateOptions{})
}
func (h *StorageHandler) DeleteStorageClass(name string) error {
err := k8s.Default().Clientset.StorageV1().StorageClasses().Delete(context.Background(), name, metav1.DeleteOptions{})
if errors.IsNotFound(err) {
return nil
}
return err
}
type PVInfo struct {
Name string `json:"name"`
Capacity string `json:"capacity"`
AccessModes []string `json:"access_modes"`
StorageClass string `json:"storage_class"`
Status string `json:"status"`
ReclaimPolicy string `json:"reclaim_policy"`
Claim string `json:"claim"`
}
func (h *StorageHandler) ListPersistentVolumes() ([]PVInfo, error) {
list, err := k8s.Default().Clientset.CoreV1().PersistentVolumes().List(context.Background(), metav1.ListOptions{})
if err != nil {
return nil, err
}
result := make([]PVInfo, 0, len(list.Items))
for _, pv := range list.Items {
capacity := ""
if q, ok := pv.Spec.Capacity[corev1.ResourceStorage]; ok {
capacity = q.String()
}
modes := make([]string, 0, len(pv.Spec.AccessModes))
for _, m := range pv.Spec.AccessModes {
modes = append(modes, string(m))
}
claim := ""
if pv.Spec.ClaimRef != nil {
claim = pv.Spec.ClaimRef.Namespace + "/" + pv.Spec.ClaimRef.Name
}
reclaimPolicy := ""
if pv.Spec.PersistentVolumeReclaimPolicy != "" {
reclaimPolicy = string(pv.Spec.PersistentVolumeReclaimPolicy)
}
result = append(result, PVInfo{
Name: pv.Name,
Capacity: capacity,
AccessModes: modes,
StorageClass: pv.Spec.StorageClassName,
Status: string(pv.Status.Phase),
ReclaimPolicy: reclaimPolicy,
Claim: claim,
})
}
return result, nil
}
func (h *StorageHandler) CreatePersistentVolume(yamlBody []byte) (*corev1.PersistentVolume, error) {
pv := &corev1.PersistentVolume{}
decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(yamlBody), 4096)
if err := decoder.Decode(pv); err != nil {
return nil, fmt.Errorf("invalid PersistentVolume YAML: %v", err)
}
return k8s.Default().Clientset.CoreV1().PersistentVolumes().Create(context.Background(), pv, metav1.CreateOptions{})
}
func (h *StorageHandler) DeletePersistentVolume(name string) error {
err := k8s.Default().Clientset.CoreV1().PersistentVolumes().Delete(context.Background(), name, metav1.DeleteOptions{})
if errors.IsNotFound(err) {
return nil
}
return err
}
var storageHandler *StorageHandler
func GetStorageHandler() *StorageHandler {
if storageHandler == nil {
storageHandler = &StorageHandler{}
}
return storageHandler
}