-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #37 from linkernetworks/hwchiu/VX-104
Implement a restful to create the volume object. Former-commit-id: f79b7be85c712cbadc137738f36a334bcb84b344 [formerly f79b7be85c712cbadc137738f36a334bcb84b344 [formerly 004f168]] Former-commit-id: 92e9310eacd956ac5b009d0991d46ec7be7ad6a3 Former-commit-id: 5b10d57
- Loading branch information
Showing
4 changed files
with
239 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package entity | ||
|
||
import ( | ||
"time" | ||
|
||
"gopkg.in/mgo.v2/bson" | ||
corev1 "k8s.io/api/core/v1" | ||
) | ||
|
||
const ( | ||
VolumeCollectionName string = "volume" | ||
) | ||
|
||
/* | ||
Users will create the Volume from the storageProvider 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"` | ||
StorageProviderName string `bson:"storageProviderName" json:"storageProviderName"` | ||
AccessMode corev1.PersistentVolumeAccessMode `bson:"accessMode" json:"accessMode"` | ||
Capacity string `bson:"capacity" json:"capacity"` | ||
MetaName string `bson:"metaName" json:"metaName"` //For PVC metaname | ||
CreatedAt *time.Time `bson:"createdAt,omitempty" json:"createdAt,omitempty"` | ||
} | ||
|
||
//GetCollection - get model mongo collection name. | ||
func (m Volume) GetCollection() string { | ||
return VolumeCollectionName | ||
} | ||
|
||
//GenerateMetaName - Generate a metaname for kubernetes PVC object | ||
func (m Volume) GenerateMetaName() string { | ||
return "PVC-" + m.ID.Hex() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package server | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/linkernetworks/utils/timeutils" | ||
"github.com/linkernetworks/vortex/src/entity" | ||
response "github.com/linkernetworks/vortex/src/net/http" | ||
"github.com/linkernetworks/vortex/src/web" | ||
mgo "gopkg.in/mgo.v2" | ||
"gopkg.in/mgo.v2/bson" | ||
) | ||
|
||
func createVolume(ctx *web.Context) { | ||
sp, req, resp := ctx.ServiceProvider, ctx.Request, ctx.Response | ||
|
||
volume := entity.Volume{} | ||
if err := req.ReadEntity(&volume); err != nil { | ||
response.BadRequest(req.Request, resp.ResponseWriter, err) | ||
return | ||
} | ||
|
||
session := sp.Mongo.NewSession() | ||
session.C(entity.VolumeCollectionName).EnsureIndex(mgo.Index{ | ||
Key: []string{"name"}, | ||
Unique: true, | ||
}) | ||
defer session.Close() | ||
|
||
// Check the storageClass is existed | ||
couunt, err := session.Count(entity.StorageProviderCollectionName, bson.M{"displayName": volume.StorageProviderName}) | ||
if 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.StorageProviderName)) | ||
return | ||
} | ||
|
||
// Check whether this displayname 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 mgo.IsDup(err) { | ||
response.Conflict(req.Request, resp.ResponseWriter, fmt.Errorf("Storage Provider Name: %s already existed", volume.Name)) | ||
} else { | ||
response.InternalServerError(req.Request, resp.ResponseWriter, err) | ||
} | ||
return | ||
} | ||
|
||
resp.WriteEntity(ActionResponse{ | ||
Error: false, | ||
Message: "Create success", | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
package server | ||
|
||
import ( | ||
"encoding/json" | ||
"math/rand" | ||
"net/http" | ||
"net/http/httptest" | ||
"strings" | ||
"testing" | ||
"time" | ||
|
||
"github.com/docker/docker/pkg/namesgenerator" | ||
restful "github.com/emicklei/go-restful" | ||
"github.com/linkernetworks/mongo" | ||
"github.com/linkernetworks/vortex/src/config" | ||
"github.com/linkernetworks/vortex/src/entity" | ||
"github.com/linkernetworks/vortex/src/serviceprovider" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/suite" | ||
"gopkg.in/mgo.v2/bson" | ||
corev1 "k8s.io/api/core/v1" | ||
) | ||
|
||
func init() { | ||
rand.Seed(time.Now().UnixNano()) | ||
} | ||
|
||
type VolumeTestSuite struct { | ||
suite.Suite | ||
wc *restful.Container | ||
session *mongo.Session | ||
storageProvider entity.StorageProvider | ||
} | ||
|
||
func (suite *VolumeTestSuite) SetupTest() { | ||
cf := config.MustRead("../../config/testing.json") | ||
sp := serviceprovider.New(cf) | ||
|
||
//init session | ||
suite.session = sp.Mongo.NewSession() | ||
//init restful container | ||
suite.wc = restful.NewContainer() | ||
service := newVolumeService(sp) | ||
suite.wc.Add(service) | ||
//init a StorageProvider | ||
suite.storageProvider = entity.StorageProvider{ | ||
ID: bson.NewObjectId(), | ||
Type: "nfs", | ||
DisplayName: namesgenerator.GetRandomName(0), | ||
} | ||
err := suite.session.Insert(entity.StorageProviderCollectionName, suite.storageProvider) | ||
assert.NoError(suite.T(), err) | ||
} | ||
|
||
func (suite *VolumeTestSuite) TearDownTest() { | ||
suite.session.Remove(entity.StorageProviderCollectionName, "_id", suite.storageProvider.ID) | ||
} | ||
|
||
func TestVolumeSuite(t *testing.T) { | ||
suite.Run(t, new(VolumeTestSuite)) | ||
} | ||
|
||
func (suite *VolumeTestSuite) TestCreateVolume() { | ||
tName := namesgenerator.GetRandomName(0) | ||
tAccessMode := corev1.PersistentVolumeAccessMode("ReadOnlyMany") | ||
tCapacity := "500G" | ||
volume := entity.Volume{ | ||
Name: tName, | ||
StorageProviderName: suite.storageProvider.DisplayName, | ||
Capacity: tCapacity, | ||
AccessMode: tAccessMode, | ||
} | ||
|
||
bodyBytes, err := json.MarshalIndent(volume, "", " ") | ||
assert.NoError(suite.T(), err) | ||
|
||
bodyReader := strings.NewReader(string(bodyBytes)) | ||
httpRequest, err := http.NewRequest("POST", "http://localhost:7890/v1/volume", bodyReader) | ||
assert.NoError(suite.T(), err) | ||
|
||
httpRequest.Header.Add("Content-Type", "application/json") | ||
httpWriter := httptest.NewRecorder() | ||
suite.wc.Dispatch(httpWriter, httpRequest) | ||
assertResponseCode(suite.T(), http.StatusOK, httpWriter) | ||
defer suite.session.Remove(entity.VolumeCollectionName, "name", volume.Name) | ||
|
||
//load data to check | ||
retVolume := entity.Volume{} | ||
err = suite.session.FindOne(entity.VolumeCollectionName, bson.M{"name": volume.Name}, &retVolume) | ||
assert.NoError(suite.T(), err) | ||
assert.NotEqual(suite.T(), "", retVolume.ID) | ||
assert.Equal(suite.T(), volume.Name, retVolume.Name) | ||
assert.Equal(suite.T(), volume.StorageProviderName, retVolume.StorageProviderName) | ||
assert.Equal(suite.T(), volume.AccessMode, retVolume.AccessMode) | ||
assert.Equal(suite.T(), volume.Capacity, retVolume.Capacity) | ||
assert.NotEqual(suite.T(), "", retVolume.MetaName) | ||
|
||
//We use the new write but empty input which will cause the readEntity Error | ||
httpWriter = httptest.NewRecorder() | ||
suite.wc.Dispatch(httpWriter, httpRequest) | ||
assertResponseCode(suite.T(), http.StatusBadRequest, httpWriter) | ||
//Create again and it should fail since the name exist | ||
bodyReader = strings.NewReader(string(bodyBytes)) | ||
httpRequest, err = http.NewRequest("POST", "http://localhost:7890/v1/volume", bodyReader) | ||
assert.NoError(suite.T(), err) | ||
httpRequest.Header.Add("Content-Type", "application/json") | ||
httpWriter = httptest.NewRecorder() | ||
suite.wc.Dispatch(httpWriter, httpRequest) | ||
assertResponseCode(suite.T(), http.StatusConflict, httpWriter) | ||
} | ||
|
||
func (suite *VolumeTestSuite) TestCreateVolumeWithInvalidParameter() { | ||
tName := namesgenerator.GetRandomName(0) | ||
tAccessMode := corev1.PersistentVolumeAccessMode("ReadOnlyMany") | ||
tCapacity := "500G" | ||
volume := entity.Volume{ | ||
Name: tName, | ||
StorageProviderName: namesgenerator.GetRandomName(0), | ||
Capacity: tCapacity, | ||
AccessMode: tAccessMode, | ||
} | ||
|
||
bodyBytes, err := json.MarshalIndent(volume, "", " ") | ||
assert.NoError(suite.T(), err) | ||
|
||
bodyReader := strings.NewReader(string(bodyBytes)) | ||
httpRequest, err := http.NewRequest("POST", "http://localhost:7890/v1/volume", bodyReader) | ||
assert.NoError(suite.T(), err) | ||
|
||
httpRequest.Header.Add("Content-Type", "application/json") | ||
httpWriter := httptest.NewRecorder() | ||
suite.wc.Dispatch(httpWriter, httpRequest) | ||
assertResponseCode(suite.T(), http.StatusBadRequest, httpWriter) | ||
defer suite.session.Remove(entity.VolumeCollectionName, "name", volume.Name) | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters