Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[csi] Add field ActualLVNameOnTheNode. Add SDSLocalVolumeCSIFinalizer. Add delete LVMLogicalVolume after error in CreateVolume. Bump go builder to 1.21 #18

Merged
merged 4 commits into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions images/sds-local-volume-csi/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ARG BASE_ALPINE=registry.deckhouse.io/base_images/alpine:3.16.3@sha256:5548e9172c24a1b0ca9afdd2bf534e265c94b12b36b3e0c0302f5853eaf00abb
ARG BASE_GOLANG_20_ALPINE_BUILDER=registry.deckhouse.io/base_images/golang:1.20.5-alpine3.18@sha256:51a47fb0851397db2f506c15c426735bc23de31177cbdd962880c0879d1906a4
ARG BASE_GOLANG_21_ALPINE_BUILDER=registry.deckhouse.io/base_images/golang:1.21.4-alpine3.18@sha256:cf84f3d6882c49ea04b6478ac514a2582c8922d7e5848b43d2918fff8329f6e6

FROM $BASE_GOLANG_20_ALPINE_BUILDER as builder
FROM $BASE_GOLANG_21_ALPINE_BUILDER as builder

WORKDIR /go/src

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ type LVMLogicalVolume struct {
}

type LVMLogicalVolumeSpec struct {
Type string `json:"type"`
Size resource.Quantity `json:"size"`
LvmVolumeGroup string `json:"lvmVolumeGroup"`
Thin *ThinLogicalVolumeSpec `json:"thin"`
ActualLVNameOnTheNode string `json:"actualLVNameOnTheNode"`
Type string `json:"type"`
Size resource.Quantity `json:"size"`
LvmVolumeGroupName string `json:"lvmVolumeGroupName"`
Thin *ThinLogicalVolumeSpec `json:"thin"`
}

type ThinLogicalVolumeSpec struct {
Expand Down
40 changes: 26 additions & 14 deletions images/sds-local-volume-csi/driver/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,19 @@ func (d *Driver) CreateVolume(ctx context.Context, request *csi.CreateVolumeRequ
return nil, status.Errorf(codes.InvalidArgument, err.Error())
}

storageClassLVGs, storageClassLVGParametersMap, err := utils.GetStorageClassLVGsAndParameters(ctx, d.cl, *d.log, request.GetParameters()[internal.LvmVolumeGroupKey])
storageClassLVGs, storageClassLVGParametersMap, err := utils.GetStorageClassLVGsAndParameters(ctx, d.cl, d.log, request.GetParameters()[internal.LvmVolumeGroupKey])
if err != nil {
d.log.Error(err, "error GetStorageClassLVGs")
return nil, status.Errorf(codes.Internal, err.Error())
}

// TODO: Consider refactoring the naming strategy for llvName and lvName.
// Currently, we use the same name for llvName (the name of the LVMLogicalVolume resource in Kubernetes)
// and lvName (the name of the LV in LVM on the node) because the PV name is unique within the cluster,
// preventing name collisions. This approach simplifies matching between nodes and Kubernetes by maintaining
// the same name in both contexts. Future consideration should be given to optimizing this logic to enhance
// code readability and maintainability.
llvName := request.Name
lvName := request.Name
d.log.Info(fmt.Sprintf("llv name: %s ", llvName))

llvSize := resource.NewQuantity(request.CapacityRange.GetRequiredBytes(), resource.BinarySI)
Expand All @@ -77,7 +83,7 @@ func (d *Driver) CreateVolume(ctx context.Context, request *csi.CreateVolumeRequ
switch BindingMode {
case internal.BindingModeI:
d.log.Info(fmt.Sprintf("BindingMode is %s. Start selecting node", internal.BindingModeI))
selectedNodeName, freeSpace, err := utils.GetNodeWithMaxFreeSpace(*d.log, storageClassLVGs, storageClassLVGParametersMap, LvmType)
selectedNodeName, freeSpace, err := utils.GetNodeWithMaxFreeSpace(d.log, storageClassLVGs, storageClassLVGParametersMap, LvmType)
if err != nil {
d.log.Error(err, "error GetNodeMaxVGSize")
}
Expand All @@ -103,8 +109,13 @@ func (d *Driver) CreateVolume(ctx context.Context, request *csi.CreateVolumeRequ
return nil, status.Errorf(codes.Internal, err.Error())
}

llvSpec := utils.GetLLVSpec(*d.log, selectedLVG, storageClassLVGParametersMap, preferredNode, LvmType, *llvSize)
llvSpec := utils.GetLLVSpec(d.log, lvName, selectedLVG, storageClassLVGParametersMap, preferredNode, LvmType, *llvSize)
d.log.Info(fmt.Sprintf("LVMLogicalVolumeSpec : %+v", llvSpec))
resizeDelta, err := resource.ParseQuantity(internal.ResizeDelta)
if err != nil {
d.log.Error(err, "error ParseQuantity for ResizeDelta")
return nil, err
}

d.log.Trace("------------ CreateLVMLogicalVolume start ------------")
_, err = utils.CreateLVMLogicalVolume(ctx, d.cl, llvName, llvSpec)
Expand All @@ -119,14 +130,15 @@ func (d *Driver) CreateVolume(ctx context.Context, request *csi.CreateVolumeRequ
d.log.Trace("------------ CreateLVMLogicalVolume end ------------")

d.log.Trace("start wait CreateLVMLogicalVolume ")
resizeDelta, err := resource.ParseQuantity(internal.ResizeDelta)
if err != nil {
d.log.Error(err, "error ParseQuantity for ResizeDelta")
return nil, err
}
attemptCounter, err := utils.WaitForStatusUpdate(ctx, d.cl, *d.log, request.Name, "", *llvSize, resizeDelta)

attemptCounter, err := utils.WaitForStatusUpdate(ctx, d.cl, d.log, request.Name, "", *llvSize, resizeDelta)
if err != nil {
d.log.Error(err, "error WaitForStatusUpdate")
deleteErr := utils.DeleteLVMLogicalVolume(ctx, d.cl, d.log, request.Name)

d.log.Error(err, fmt.Sprintf("error WaitForStatusUpdate. Delete LVMLogicalVolume %s", request.Name))
if deleteErr != nil {
d.log.Error(deleteErr, "error DeleteLVMLogicalVolume")
}
return nil, err
}
d.log.Trace(fmt.Sprintf("stop waiting CreateLVMLogicalVolume, attempt counter = %d ", attemptCounter))
Expand Down Expand Up @@ -156,7 +168,7 @@ func (d *Driver) CreateVolume(ctx context.Context, request *csi.CreateVolumeRequ

func (d *Driver) DeleteVolume(ctx context.Context, request *csi.DeleteVolumeRequest) (*csi.DeleteVolumeResponse, error) {
d.log.Info("method DeleteVolume")
err := utils.DeleteLVMLogicalVolume(ctx, d.cl, request.VolumeId)
err := utils.DeleteLVMLogicalVolume(ctx, d.cl, d.log, request.VolumeId)
if err != nil {
d.log.Error(err, "error DeleteLVMLogicalVolume")
}
Expand Down Expand Up @@ -284,7 +296,7 @@ func (d *Driver) ControllerExpandVolume(ctx context.Context, request *csi.Contro
}, nil
}

lvg, err := utils.GetLVMVolumeGroup(ctx, d.cl, llv.Spec.LvmVolumeGroup, llv.Namespace)
lvg, err := utils.GetLVMVolumeGroup(ctx, d.cl, llv.Spec.LvmVolumeGroupName, llv.Namespace)
if err != nil {
return nil, status.Errorf(codes.Internal, "error getting LVMVolumeGroup: %v", err)
}
Expand All @@ -308,7 +320,7 @@ func (d *Driver) ControllerExpandVolume(ctx context.Context, request *csi.Contro
return nil, status.Errorf(codes.Internal, "error updating LVMLogicalVolume: %v", err)
}

attemptCounter, err := utils.WaitForStatusUpdate(ctx, d.cl, *d.log, llv.Name, llv.Namespace, *requestCapacity, resizeDelta)
attemptCounter, err := utils.WaitForStatusUpdate(ctx, d.cl, d.log, llv.Name, llv.Namespace, *requestCapacity, resizeDelta)
if err != nil {
d.log.Error(err, "error WaitForStatusUpdate")
return nil, err
Expand Down
67 changes: 49 additions & 18 deletions images/sds-local-volume-csi/pkg/utils/func.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"sds-local-volume-csi/api/v1alpha1"
"sds-local-volume-csi/internal"
"sds-local-volume-csi/pkg/logger"
"slices"
"time"

"gopkg.in/yaml.v2"
Expand All @@ -40,6 +41,7 @@ const (
LLVTypeThin = "Thin"
KubernetesApiRequestLimit = 3
KubernetesApiRequestTimeout = 1
SDSLocalVolumeCSIFinalizer = "storage.deckhouse.io/sds-local-volume-csi"
)

func CreateLVMLogicalVolume(ctx context.Context, kc client.Client, name string, LVMLogicalVolumeSpec v1alpha1.LVMLogicalVolumeSpec) (*v1alpha1.LVMLogicalVolume, error) {
Expand All @@ -52,6 +54,7 @@ func CreateLVMLogicalVolume(ctx context.Context, kc client.Client, name string,
ObjectMeta: metav1.ObjectMeta{
Name: name,
OwnerReferences: []metav1.OwnerReference{},
Finalizers: []string{SDSLocalVolumeCSIFinalizer},
},
Spec: LVMLogicalVolumeSpec,
}
Expand All @@ -75,20 +78,26 @@ func CreateLVMLogicalVolume(ctx context.Context, kc client.Client, name string,
return llv, nil
}

func DeleteLVMLogicalVolume(ctx context.Context, kc client.Client, LVMLogicalVolumeName string) error {
func DeleteLVMLogicalVolume(ctx context.Context, kc client.Client, log *logger.Logger, LVMLogicalVolumeName string) error {
var err error
llv := &v1alpha1.LVMLogicalVolume{
ObjectMeta: metav1.ObjectMeta{
Name: LVMLogicalVolumeName,
},
TypeMeta: metav1.TypeMeta{
Kind: v1alpha1.LVMLogicalVolumeKind,
APIVersion: v1alpha1.TypeMediaAPIVersion,
},

llv, err := GetLVMLogicalVolume(ctx, kc, LVMLogicalVolumeName, "")
if err != nil {
return fmt.Errorf("get LVMLogicalVolume %s: %w", LVMLogicalVolumeName, err)
}

removed, err := removeLLVFinalizerIfExist(ctx, kc, llv, SDSLocalVolumeCSIFinalizer)
if err != nil {
return fmt.Errorf("remove finalizers from LVMLogicalVolume %s: %w", LVMLogicalVolumeName, err)
}
if removed {
log.Trace(fmt.Sprintf("[DeleteLVMLogicalVolume] finalizer %s removed from LVMLogicalVolume %s", SDSLocalVolumeCSIFinalizer, LVMLogicalVolumeName))
} else {
log.Warning(fmt.Sprintf("[DeleteLVMLogicalVolume] finalizer %s not found in LVMLogicalVolume %s", SDSLocalVolumeCSIFinalizer, LVMLogicalVolumeName))
}

for attempt := 0; attempt < KubernetesApiRequestLimit; attempt++ {
err := kc.Delete(ctx, llv)
err = kc.Delete(ctx, llv)
if err == nil {
return nil
}
Expand All @@ -101,7 +110,7 @@ func DeleteLVMLogicalVolume(ctx context.Context, kc client.Client, LVMLogicalVol
return nil
}

func WaitForStatusUpdate(ctx context.Context, kc client.Client, log logger.Logger, LVMLogicalVolumeName, namespace string, llvSize, delta resource.Quantity) (int, error) {
func WaitForStatusUpdate(ctx context.Context, kc client.Client, log *logger.Logger, LVMLogicalVolumeName, namespace string, llvSize, delta resource.Quantity) (int, error) {
var attemptCounter int
sizeEquals := false
for {
Expand Down Expand Up @@ -162,7 +171,7 @@ func AreSizesEqualWithinDelta(leftSize, rightSize, allowedDelta resource.Quantit
return math.Abs(leftSizeFloat-rightSizeFloat) < float64(allowedDelta.Value())
}

func GetNodeWithMaxFreeSpace(log logger.Logger, lvgs []v1alpha1.LvmVolumeGroup, storageClassLVGParametersMap map[string]string, lvmType string) (nodeName string, freeSpace resource.Quantity, err error) {
func GetNodeWithMaxFreeSpace(log *logger.Logger, lvgs []v1alpha1.LvmVolumeGroup, storageClassLVGParametersMap map[string]string, lvmType string) (nodeName string, freeSpace resource.Quantity, err error) {

var maxFreeSpace int64
for _, lvg := range lvgs {
Expand Down Expand Up @@ -316,7 +325,7 @@ func UpdateLVMLogicalVolume(ctx context.Context, kc client.Client, llv *v1alpha1
return nil
}

func GetStorageClassLVGsAndParameters(ctx context.Context, kc client.Client, log logger.Logger, storageClassLVGParametersString string) (storageClassLVGs []v1alpha1.LvmVolumeGroup, storageClassLVGParametersMap map[string]string, err error) {
func GetStorageClassLVGsAndParameters(ctx context.Context, kc client.Client, log *logger.Logger, storageClassLVGParametersString string) (storageClassLVGs []v1alpha1.LvmVolumeGroup, storageClassLVGParametersMap map[string]string, err error) {
var storageClassLVGParametersList LVMVolumeGroups
err = yaml.Unmarshal([]byte(storageClassLVGParametersString), &storageClassLVGParametersList)
if err != nil {
Expand Down Expand Up @@ -373,18 +382,19 @@ func GetLVGList(ctx context.Context, kc client.Client) (*v1alpha1.LvmVolumeGroup
return nil, fmt.Errorf("after %d attempts of getting LvmVolumeGroupList, last error: %w", KubernetesApiRequestLimit, err)
}

func GetLLVSpec(log logger.Logger, selectedLVG v1alpha1.LvmVolumeGroup, storageClassLVGParametersMap map[string]string, nodeName, lvmType string, llvSize resource.Quantity) v1alpha1.LVMLogicalVolumeSpec {
func GetLLVSpec(log *logger.Logger, lvName string, selectedLVG v1alpha1.LvmVolumeGroup, storageClassLVGParametersMap map[string]string, nodeName, lvmType string, llvSize resource.Quantity) v1alpha1.LVMLogicalVolumeSpec {
var llvThin *v1alpha1.ThinLogicalVolumeSpec
if lvmType == internal.LLMTypeThin {
llvThin = &v1alpha1.ThinLogicalVolumeSpec{}
llvThin.PoolName = storageClassLVGParametersMap[selectedLVG.Name]
log.Info(fmt.Sprintf("[GetLLVSpec] Thin pool name: %s", llvThin.PoolName))
}
return v1alpha1.LVMLogicalVolumeSpec{
Type: lvmType,
Size: llvSize,
LvmVolumeGroup: selectedLVG.Name,
Thin: llvThin,
ActualLVNameOnTheNode: lvName,
Type: lvmType,
Size: llvSize,
LvmVolumeGroupName: selectedLVG.Name,
Thin: llvThin,
}
}

Expand All @@ -396,3 +406,24 @@ func SelectLVG(storageClassLVGs []v1alpha1.LvmVolumeGroup, storageClassLVGParame
}
return v1alpha1.LvmVolumeGroup{}, fmt.Errorf("[SelectLVG] no LVMVolumeGroup found for node %s", nodeName)
}

func removeLLVFinalizerIfExist(ctx context.Context, kc client.Client, llv *v1alpha1.LVMLogicalVolume, finalizer string) (bool, error) {
removed := false

for i, val := range llv.Finalizers {
if val == finalizer {
llv.Finalizers = slices.Delete(llv.Finalizers, i, i+1)
removed = true
break
}
}

if removed {
err := UpdateLVMLogicalVolume(ctx, kc, llv)
if err != nil {
return false, err
}
return true, nil
}
return false, nil
}