Skip to content

Commit

Permalink
storage: add deletes and unit tests to pv
Browse files Browse the repository at this point in the history
This PR adds Delete, DeleteAndWait, and WaitUntilDeleted methods to the storage PVBuilder struct. Unit tests for the file, including for these new methods, have been added.
  • Loading branch information
klaskosk committed Jun 5, 2024
1 parent f93417f commit 86c6e9c
Show file tree
Hide file tree
Showing 2 changed files with 285 additions and 0 deletions.
74 changes: 74 additions & 0 deletions pkg/storage/pv.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ package storage
import (
"context"
"fmt"
"time"

"github.com/golang/glog"
"github.com/openshift-kni/eco-goinfra/pkg/clients"
"github.com/openshift-kni/eco-goinfra/pkg/msg"
corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
)

// PVBuilder provides struct for persistentvolume object containing connection
Expand Down Expand Up @@ -60,6 +62,78 @@ func (builder *PVBuilder) Exists() bool {
return err == nil || !k8serrors.IsNotFound(err)
}

// Delete removes a PersistentVolume from the apiClient if it exists.
func (builder *PVBuilder) Delete() error {
if valid, err := builder.validate(); !valid {
return err
}

glog.V(100).Infof("Deleting the PersistentVolume %s", builder.Definition.Name)

if !builder.Exists() {
glog.V(100).Infof("PersistentVolume %s cannot be deleted because it does not exist", builder.Definition.Name)

builder.Object = nil

return nil
}

err := builder.apiClient.PersistentVolumes().Delete(context.TODO(), builder.Definition.Name, metav1.DeleteOptions{})
if err != nil {
return err
}

builder.Object = nil

return nil
}

// DeleteAndWait deletes the PersistentVolume and waits up to timeout until it has been removed.
func (builder *PVBuilder) DeleteAndWait(timeout time.Duration) error {
if valid, err := builder.validate(); !valid {
return err
}

glog.V(100).Infof(
"Deleting PersistentVolume %s and waiting up to %s until it is removed", timeout, builder.Definition.Name)

err := builder.Delete()
if err != nil {
return err
}

return builder.WaitUntilDeleted(timeout)
}

// WaitUntilDeleted waits for the duration of timeout or until the PersistentVolume has been deleted.
func (builder *PVBuilder) WaitUntilDeleted(timeout time.Duration) error {
if valid, err := builder.validate(); !valid {
return err
}

glog.V(100).Infof("Waiting up to %s until PersistentVolume %s is deleted", timeout, builder.Definition.Name)

return wait.PollUntilContextTimeout(
context.TODO(), time.Second, timeout, true, func(ctx context.Context) (bool, error) {
_, err := builder.apiClient.PersistentVolumes().Get(context.TODO(), builder.Definition.Name, metav1.GetOptions{})
if err == nil {
glog.V(100).Infof("PersistentVolume %s still present", builder.Definition.Name)

return false, nil
}

if k8serrors.IsNotFound(err) {
glog.V(100).Infof("PersistentVolume %s is gone", builder.Definition.Name)

return true, nil
}

glog.V(100).Infof("failed to get PersistentVolume %s", builder.Definition.Name)

return false, err
})
}

// validate will check that the builder and builder definition are properly initialized before
// accessing any member fields.
func (builder *PVBuilder) validate() (bool, error) {
Expand Down
211 changes: 211 additions & 0 deletions pkg/storage/pv_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package storage

import (
"context"
"fmt"
"testing"
"time"

"github.com/openshift-kni/eco-goinfra/pkg/clients"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)

const defaultPersistentVolumeName = "persistentvolume-test"

func TestPullPersistentVolume(t *testing.T) {
testCases := []struct {
persistentVolumeName string
addToRuntimeObjects bool
client bool
expectedErrorText string
}{
{
persistentVolumeName: defaultPersistentVolumeName,
addToRuntimeObjects: true,
client: false,
expectedErrorText: fmt.Sprintf("PersistentVolume object %s does not exist", defaultPersistentVolumeName),
},
{
persistentVolumeName: defaultPersistentVolumeName,
addToRuntimeObjects: true,
client: true,
expectedErrorText: "",
},
{
persistentVolumeName: defaultPersistentVolumeName,
addToRuntimeObjects: false,
client: true,
expectedErrorText: fmt.Sprintf("PersistentVolume object %s does not exist", defaultPersistentVolumeName),
},
{
persistentVolumeName: "",
addToRuntimeObjects: true,
client: true,
expectedErrorText: "",
},
}

for _, testCase := range testCases {
var (
runtimeObjects []runtime.Object
testSettings *clients.Settings
)

testPersistentVolume := buildDummyPersistentVolume(testCase.persistentVolumeName)

if testCase.addToRuntimeObjects {
runtimeObjects = append(runtimeObjects, testPersistentVolume)
}

if testCase.client {
testSettings = clients.GetTestClients(clients.TestClientParams{
K8sMockObjects: runtimeObjects,
})
}

persistentVolumeBuilder, err := PullPersistentVolume(testSettings, testCase.persistentVolumeName)

if testCase.expectedErrorText == "" {
assert.Nil(t, err)
assert.Equal(t, testPersistentVolume.Name, persistentVolumeBuilder.Definition.Name)
} else {
assert.EqualError(t, err, testCase.expectedErrorText)
}
}
}

func TestPersistentVolumeExists(t *testing.T) {
testCases := []struct {
testBuilder *PVBuilder
exists bool
}{
{
testBuilder: buildValidPersistentVolumeTestBuilder(buildTestClientWithDummyPersistentVolume()),
exists: true,
},
{
testBuilder: buildInvalidPersistentVolumeTestBuilder(buildTestClientWithDummyPersistentVolume()),
exists: false,
},
{
testBuilder: buildValidPersistentVolumeTestBuilder(clients.GetTestClients(clients.TestClientParams{})),
exists: false,
},
}

for _, testCase := range testCases {
exists := testCase.testBuilder.Exists()
assert.Equal(t, testCase.exists, exists)
}
}

func TestPersistentVolumeDelete(t *testing.T) {
testCases := []struct {
testBuilder *PVBuilder
expectedErrorText string
}{
{
testBuilder: buildValidPersistentVolumeTestBuilder(buildTestClientWithDummyPersistentVolume()),
expectedErrorText: "",
},
{
testBuilder: buildValidPersistentVolumeTestBuilder(clients.GetTestClients(clients.TestClientParams{})),
expectedErrorText: "",
},
}

for _, testCase := range testCases {
err := testCase.testBuilder.Delete()

if testCase.expectedErrorText == "" {
assert.Nil(t, err)
assert.Nil(t, testCase.testBuilder.Object)
}
}
}

func TestPersistentVolumeDeleteAndWait(t *testing.T) {
testCases := []struct {
testBuilder *PVBuilder
expectedErrorText string
}{
{
testBuilder: buildValidPersistentVolumeTestBuilder(buildTestClientWithDummyPersistentVolume()),
expectedErrorText: "",
},
{
testBuilder: buildValidPersistentVolumeTestBuilder(clients.GetTestClients(clients.TestClientParams{})),
expectedErrorText: "",
},
}

for _, testCase := range testCases {
err := testCase.testBuilder.DeleteAndWait(time.Second)

if testCase.expectedErrorText == "" {
assert.Nil(t, err)
assert.Nil(t, testCase.testBuilder.Object)
}
}
}

func TestPersistentVolumeWaitUntilDeleted(t *testing.T) {
testCases := []struct {
testBuilder *PVBuilder
expectedError error
}{
{
testBuilder: buildValidPersistentVolumeTestBuilder(buildTestClientWithDummyPersistentVolume()),
expectedError: context.DeadlineExceeded,
},
{
testBuilder: buildValidPersistentVolumeTestBuilder(clients.GetTestClients(clients.TestClientParams{})),
expectedError: nil,
},
}

for _, testCase := range testCases {
err := testCase.testBuilder.WaitUntilDeleted(time.Second)
assert.Equal(t, testCase.expectedError, err)

if testCase.expectedError == nil {
assert.Nil(t, testCase.testBuilder.Object)
}
}
}

// buildDummyPersistentVolume returns a PersistenVolume with the specified name.
func buildDummyPersistentVolume(name string) *corev1.PersistentVolume {
return &corev1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
}
}

// buildTestClientWithDummyPersistentVolume returns a client with a mock PersistentVolume with the default name.
func buildTestClientWithDummyPersistentVolume() *clients.Settings {
return clients.GetTestClients(clients.TestClientParams{
K8sMockObjects: []runtime.Object{
buildDummyPersistentVolume(defaultPersistentVolumeName),
},
})
}

// buildValidPersistentVolumeTestBuilder returns a valid PVBuilder with the default name and specified client.
func buildValidPersistentVolumeTestBuilder(apiClient *clients.Settings) *PVBuilder {
return &PVBuilder{
apiClient: apiClient,
Definition: buildDummyPersistentVolume(defaultPersistentVolumeName),
}
}

// buildInvalidPersistentVolumeTestBuilder returns an invalid PVBuilder with no definition and the specified client.
func buildInvalidPersistentVolumeTestBuilder(apiClient *clients.Settings) *PVBuilder {
return &PVBuilder{
apiClient: apiClient,
}
}

0 comments on commit 86c6e9c

Please sign in to comment.