Skip to content

Commit

Permalink
Merge pull request #71 from linkernetworks/hwchiu/VX-105
Browse files Browse the repository at this point in the history
Support the create/delete for volume(pvc) via restful API
  • Loading branch information
Hung-Wei Chiu authored Jul 8, 2018
2 parents a656748 + 5545155 commit 006a9e8
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 49 deletions.
6 changes: 3 additions & 3 deletions src/entity/volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ const (
)

/*
Users will create the Volume from the storageProvider and they can use those volumes in their containers
Users will create the Volume from the storage and they can use those volumes in their containers
In the kubernetes implementation, it's PVC
So the Volume will create a PVC type and connect to a known StorageClass
*/
type Volume struct {
ID bson.ObjectId `bson:"_id,omitempty" json:"id"`
Name string `bson:"name" json:"name"`
StorageName string `bson:"storageProviderName" json:"storageProviderName"`
StorageName string `bson:"storageName" json:"storageName"`
AccessMode corev1.PersistentVolumeAccessMode `bson:"accessMode" json:"accessMode"`
Capacity string `bson:"capacity" json:"capacity"`
MetaName string `bson:"metaName" json:"metaName"` //For PVC metaname
Expand All @@ -33,5 +33,5 @@ func (m Volume) GetCollection() string {

//GenerateMetaName - Generate a metaname for kubernetes PVC object
func (m Volume) GenerateMetaName() string {
return "PVC-" + m.ID.Hex()
return "pvc-" + m.ID.Hex()
}
39 changes: 23 additions & 16 deletions src/server/handler_volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/linkernetworks/vortex/src/entity"
response "github.com/linkernetworks/vortex/src/net/http"
"github.com/linkernetworks/vortex/src/net/http/query"
"github.com/linkernetworks/vortex/src/volume"
"github.com/linkernetworks/vortex/src/web"

mgo "gopkg.in/mgo.v2"
Expand All @@ -18,8 +19,8 @@ import (
func createVolume(ctx *web.Context) {
sp, req, resp := ctx.ServiceProvider, ctx.Request, ctx.Response

volume := entity.Volume{}
if err := req.ReadEntity(&volume); err != nil {
v := entity.Volume{}
if err := req.ReadEntity(&v); err != nil {
response.BadRequest(req.Request, resp.ResponseWriter, err)
return
}
Expand All @@ -31,24 +32,18 @@ func createVolume(ctx *web.Context) {
})
defer session.Close()

// Check the storageClass is existed
couunt, err := session.Count(entity.StorageCollectionName, bson.M{"name": volume.StorageName})
if err != nil {
// Check whether this name has been used
v.ID = bson.NewObjectId()
v.CreatedAt = timeutils.Now()
v.MetaName = v.GenerateMetaName()
//Generate the metaName for PVC meta name and we will use it future
if err := volume.CreateVolume(sp, &v); err != nil {
response.InternalServerError(req.Request, resp.ResponseWriter, err)
return
} else if couunt < 1 {
response.BadRequest(req.Request, resp.ResponseWriter, fmt.Errorf("The reference storage provider %s doesn't exist", volume.StorageName))
return
}

// Check whether this name has been used
volume.ID = bson.NewObjectId()
volume.CreatedAt = timeutils.Now()
//Generate the metaName for PVC meta name and we will use it future
volume.MetaName = volume.GenerateMetaName()
if err := session.Insert(entity.VolumeCollectionName, &volume); err != nil {
if err := session.Insert(entity.VolumeCollectionName, &v); err != nil {
if mgo.IsDup(err) {
response.Conflict(req.Request, resp.ResponseWriter, fmt.Errorf("Storage Provider Name: %s already existed", volume.Name))
response.Conflict(req.Request, resp.ResponseWriter, fmt.Errorf("Storage Provider Name: %s already existed", v.Name))
} else {
response.InternalServerError(req.Request, resp.ResponseWriter, err)
}
Expand All @@ -69,6 +64,18 @@ func deleteVolume(ctx *web.Context) {
session := sp.Mongo.NewSession()
defer session.Close()

v := entity.Volume{}
if err := session.FindOne(entity.VolumeCollectionName, bson.M{"_id": bson.ObjectIdHex(id)}, &v); err != nil {
response.BadRequest(req.Request, resp.ResponseWriter, err)
return
}

if err := volume.DeleteVolume(sp, &v); err != nil {
response.InternalServerError(req.Request, resp.ResponseWriter, err)
fmt.Println("QQ-", err)
return
}

if err := session.Remove(entity.VolumeCollectionName, "_id", bson.ObjectIdHex(id)); err != nil {
if mgo.ErrNotFound == err {
response.BadRequest(req.Request, resp.ResponseWriter, err)
Expand Down
20 changes: 17 additions & 3 deletions src/server/handler_volume_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/linkernetworks/vortex/src/config"
"github.com/linkernetworks/vortex/src/entity"
"github.com/linkernetworks/vortex/src/serviceprovider"
v "github.com/linkernetworks/vortex/src/volume"
"github.com/moby/moby/pkg/namesgenerator"
"github.com/stretchr/testify/suite"
"gopkg.in/mgo.v2/bson"
Expand All @@ -26,6 +27,7 @@ func init() {

type VolumeTestSuite struct {
suite.Suite
sp *serviceprovider.Container
wc *restful.Container
session *mongo.Session
storage entity.Storage
Expand All @@ -35,11 +37,12 @@ func (suite *VolumeTestSuite) SetupSuite() {
cf := config.MustRead("../../config/testing.json")
sp := serviceprovider.NewForTesting(cf)

suite.sp = sp
//init session
suite.session = sp.Mongo.NewSession()
//init restful container
suite.wc = restful.NewContainer()
service := newVolumeService(sp)
service := newVolumeService(suite.sp)
suite.wc.Add(service)
//init a Storage
suite.storage = entity.Storage{
Expand Down Expand Up @@ -109,6 +112,7 @@ func (suite *VolumeTestSuite) TestCreateVolume() {
}

func (suite *VolumeTestSuite) TestCreateVolumeWithInvalidParameter() {
//the storageName doesn't exist
tName := namesgenerator.GetRandomName(0)
tAccessMode := corev1.PersistentVolumeAccessMode("ReadOnlyMany")
tCapacity := "500G"
Expand All @@ -129,7 +133,7 @@ func (suite *VolumeTestSuite) TestCreateVolumeWithInvalidParameter() {
httpRequest.Header.Add("Content-Type", "application/json")
httpWriter := httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusBadRequest, httpWriter)
assertResponseCode(suite.T(), http.StatusInternalServerError, httpWriter)
defer suite.session.Remove(entity.VolumeCollectionName, "name", volume.Name)
}

Expand All @@ -143,9 +147,19 @@ func (suite *VolumeTestSuite) TestDeleteVolume() {
StorageName: namesgenerator.GetRandomName(0),
Capacity: tCapacity,
AccessMode: tAccessMode,
MetaName: namesgenerator.GetRandomName(0),
}

err := suite.session.Insert(entity.VolumeCollectionName, &volume)
err := suite.session.Insert(entity.StorageCollectionName, &entity.Storage{
Name: volume.StorageName,
})
defer suite.session.Remove(entity.StorageCollectionName, "name", volume.StorageName)
suite.NoError(err)

err = v.CreateVolume(suite.sp, &volume)
suite.NoError(err)

err = suite.session.Insert(entity.VolumeCollectionName, &volume)
suite.NoError(err)

bodyBytes, err := json.MarshalIndent(volume, "", " ")
Expand Down
27 changes: 0 additions & 27 deletions src/storageprovider/nfs_test.go
Original file line number Diff line number Diff line change
@@ -1,36 +1,16 @@
package storageprovider

import (
"bytes"
"fmt"
"math/rand"
"os/exec"
"testing"
"time"

//"github.com/linkernetworks/mongo"
"github.com/linkernetworks/vortex/src/config"
"github.com/linkernetworks/vortex/src/entity"
kc "github.com/linkernetworks/vortex/src/kubernetes"
"github.com/linkernetworks/vortex/src/serviceprovider"
//"github.com/moby/moby/pkg/namesgenerator"
"github.com/stretchr/testify/suite"
"gopkg.in/mgo.v2/bson"
fakeclientset "k8s.io/client-go/kubernetes/fake"
)

func init() {
rand.Seed(time.Now().UnixNano())
}

func execute(suite *suite.Suite, cmd *exec.Cmd) {
w := bytes.NewBuffer(nil)
cmd.Stderr = w
err := cmd.Run()
suite.NoError(err)
fmt.Printf("Stderr: %s\n", string(w.Bytes()))
}

type StorageTestSuite struct {
suite.Suite
sp *serviceprovider.Container
Expand All @@ -39,13 +19,6 @@ type StorageTestSuite struct {
func (suite *StorageTestSuite) SetupSuite() {
cf := config.MustRead("../../config/testing.json")
suite.sp = serviceprovider.NewForTesting(cf)

//init fakeclient
fakeclient := fakeclientset.NewSimpleClientset()
namespace := "default"
suite.sp.KubeCtl = kc.New(fakeclient, namespace)

//Create a fake clinet
}

func (suite *StorageTestSuite) TearDownSuite() {
Expand Down
56 changes: 56 additions & 0 deletions src/volume/volume.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package volume

import (
"github.com/linkernetworks/mongo"
"github.com/linkernetworks/vortex/src/entity"
"github.com/linkernetworks/vortex/src/serviceprovider"
"gopkg.in/mgo.v2/bson"

"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func getPVCInstance(volume *entity.Volume, storageClassName string) *v1.PersistentVolumeClaim {
capacity, _ := resource.ParseQuantity(volume.Capacity)
return &v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: volume.MetaName,
},
Spec: v1.PersistentVolumeClaimSpec{
AccessModes: []v1.PersistentVolumeAccessMode{volume.AccessMode},
Resources: v1.ResourceRequirements{
Limits: map[v1.ResourceName]resource.Quantity{
"storage": capacity,
},
Requests: map[v1.ResourceName]resource.Quantity{
"storage": capacity,
},
},
StorageClassName: &storageClassName,
},
}
}

func getStorageClassName(session *mongo.Session, storageName string) (string, error) {
storage := entity.Storage{}
err := session.FindOne(entity.StorageCollectionName, bson.M{"name": storageName}, &storage)
return storage.StorageClassName, err
}

func CreateVolume(sp *serviceprovider.Container, volume *entity.Volume) error {
session := sp.Mongo.NewSession()
defer session.Close()
storageName, err := getStorageClassName(session, volume.StorageName)
if err != nil {
return err
}
//fetch the db to get the storageName
pvc := getPVCInstance(volume, storageName)
_, err = sp.KubeCtl.CreatePVC(pvc)
return err
}

func DeleteVolume(sp *serviceprovider.Container, volume *entity.Volume) error {
return sp.KubeCtl.DeletePVC(volume.MetaName)
}
112 changes: 112 additions & 0 deletions src/volume/volume_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package volume

import (
"math/rand"
"testing"
"time"

//"github.com/linkernetworks/mongo"
"github.com/linkernetworks/vortex/src/config"
"github.com/linkernetworks/vortex/src/entity"
"github.com/linkernetworks/vortex/src/serviceprovider"
"github.com/moby/moby/pkg/namesgenerator"
"github.com/stretchr/testify/suite"
"gopkg.in/mgo.v2/bson"
)

func init() {
rand.Seed(time.Now().UnixNano())
}

type VolumeTestSuite struct {
suite.Suite
sp *serviceprovider.Container
}

func (suite *VolumeTestSuite) SetupSuite() {
cf := config.MustRead("../../config/testing.json")
suite.sp = serviceprovider.NewForTesting(cf)
}

func (suite *VolumeTestSuite) TearDownSuite() {
}

func TestVolumeSuite(t *testing.T) {
suite.Run(t, new(VolumeTestSuite))
}

func (suite *VolumeTestSuite) TestGetPVCInstance() {
volume := &entity.Volume{
ID: bson.NewObjectId(),
Name: namesgenerator.GetRandomName(0),
StorageName: namesgenerator.GetRandomName(0),
MetaName: namesgenerator.GetRandomName(0),
}

pvc := getPVCInstance(volume, namesgenerator.GetRandomName(0))
suite.NotNil(pvc)
suite.Equal(pvc.ObjectMeta.Name, volume.MetaName)
}

func (suite *VolumeTestSuite) TestGetStorageClassName() {
session := suite.sp.Mongo.NewSession()
defer session.Close()

storage := entity.Storage{
ID: bson.NewObjectId(),
Name: namesgenerator.GetRandomName(0),
StorageClassName: namesgenerator.GetRandomName(0),
}

session.Insert(entity.StorageCollectionName, &storage)
defer session.Remove(entity.StorageCollectionName, "name", storage.Name)
name, err := getStorageClassName(session, storage.Name)
suite.NoError(err)
suite.Equal(name, storage.StorageClassName)
}

func (suite *VolumeTestSuite) TestCreateVolume() {
session := suite.sp.Mongo.NewSession()
defer session.Close()
storage := entity.Storage{
ID: bson.NewObjectId(),
Name: namesgenerator.GetRandomName(0),
StorageClassName: namesgenerator.GetRandomName(0),
}

session.Insert(entity.StorageCollectionName, &storage)
defer session.Remove(entity.StorageCollectionName, "name", storage.Name)

volume := &entity.Volume{
ID: bson.NewObjectId(),
Name: namesgenerator.GetRandomName(0),
StorageName: storage.Name,
MetaName: namesgenerator.GetRandomName(0),
}

err := CreateVolume(suite.sp, volume)
suite.NoError(err)

v, err := suite.sp.KubeCtl.GetPVC(volume.MetaName)
suite.NoError(err)
suite.NotNil(v)

err = DeleteVolume(suite.sp, volume)
suite.NoError(err)

v, err = suite.sp.KubeCtl.GetPVC(volume.MetaName)
suite.Error(err)
suite.Nil(v)
}

func (suite *VolumeTestSuite) TestCreateVolumeFail() {
volume := &entity.Volume{
ID: bson.NewObjectId(),
Name: namesgenerator.GetRandomName(0),
StorageName: namesgenerator.GetRandomName(0),
MetaName: namesgenerator.GetRandomName(0),
}

err := CreateVolume(suite.sp, volume)
suite.Error(err)
}

0 comments on commit 006a9e8

Please sign in to comment.