Skip to content

Commit

Permalink
Merge pull request #314 from linkernetworks/phstsai/upload-yaml
Browse files Browse the repository at this point in the history
[Task] YAML upload handler for resources
  • Loading branch information
John-Lin authored Sep 18, 2018
2 parents b97b1bd + ed3de51 commit edbd47b
Show file tree
Hide file tree
Showing 11 changed files with 535 additions and 1 deletion.
6 changes: 5 additions & 1 deletion src/server/handler_deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,11 @@ func uploadDeploymentYAMLHandler(ctx *web.Context) {
return
}

deploymentObj := obj.(*v1.Deployment)
deploymentObj, ok := obj.(*v1.Deployment)
if !ok {
response.BadRequest(req.Request, resp.ResponseWriter, fmt.Errorf("The YAML file is not for creating deployment"))
return
}

d := entity.Deployment{
ID: bson.NewObjectId(),
Expand Down
96 changes: 96 additions & 0 deletions src/server/handler_deployment_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package server

import (
"bytes"
"encoding/json"
"fmt"
"io"
"math/rand"
"mime/multipart"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -349,3 +354,94 @@ func (suite *DeploymentTestSuite) TestListDeploymentWithInvalidPage() {
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusInternalServerError, httpWriter)
}

func (suite *DeploymentTestSuite) TestUploadDeploymentYAML() {
filename := "../../testYAMLs/deployment.yaml"

bodyBuf := bytes.NewBufferString("")
bodyWriter := multipart.NewWriter(bodyBuf)

// use the bodyWriter to write the Part headers to the buffer
_, err := bodyWriter.CreateFormFile("file", filename)
suite.NoError(err)

// the file data will be the second part of the body
file, err := os.Open(filename)
suite.NoError(err)

// need to know the boundary to properly close the part myself.
boundary := bodyWriter.Boundary()
//close_string := fmt.Sprintf("\r\n--%s--\r\n", boundary)
closeBuf := bytes.NewBufferString(fmt.Sprintf("\r\n--%s--\r\n", boundary))

// use multi-reader to defer the reading of the file data until
// writing to the socket buffer.
requestReader := io.MultiReader(bodyBuf, file, closeBuf)
fileStat, err := file.Stat()
suite.NoError(err)

httpRequest, err := http.NewRequest("POST", "http://localhost:7890/v1/deployments/upload/yaml", requestReader)
suite.NoError(err)

httpRequest.Header.Add("Content-Type", "multipart/form-data; boundary="+boundary)
httpRequest.Header.Add("Authorization", suite.JWTBearer)
httpRequest.ContentLength = fileStat.Size() + int64(bodyBuf.Len()) + int64(closeBuf.Len())
httpWriter := httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
defer suite.session.Remove(entity.DeploymentCollectionName, "name", "upload-deployment")

assertResponseCode(suite.T(), http.StatusCreated, httpWriter)

//load data to check
retDeployment := entity.Deployment{}
err = suite.session.FindOne(entity.DeploymentCollectionName, bson.M{"name": "upload-deployment"}, &retDeployment)
suite.NoError(err)
suite.NotEqual("", retDeployment.ID)
suite.Equal("upload-deployment", retDeployment.Name)
suite.Equal("default", retDeployment.Namespace)

//Create again and it should fail since the name exist
httpWriter = httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusConflict, httpWriter)

err = p.DeleteDeployment(suite.sp, &retDeployment)
suite.NoError(err)
}

func (suite *DeploymentTestSuite) TestUploadDeploymentYAMLFail() {
filename := "../../testYAMLs/namespace.yaml"

bodyBuf := bytes.NewBufferString("")
bodyWriter := multipart.NewWriter(bodyBuf)

// use the bodyWriter to write the Part headers to the buffer
_, err := bodyWriter.CreateFormFile("file", filename)
suite.NoError(err)

// the file data will be the second part of the body
file, err := os.Open(filename)
suite.NoError(err)

// need to know the boundary to properly close the part myself.
boundary := bodyWriter.Boundary()
//close_string := fmt.Sprintf("\r\n--%s--\r\n", boundary)
closeBuf := bytes.NewBufferString(fmt.Sprintf("\r\n--%s--\r\n", boundary))

// use multi-reader to defer the reading of the file data until
// writing to the socket buffer.
requestReader := io.MultiReader(bodyBuf, file, closeBuf)
fileStat, err := file.Stat()
suite.NoError(err)

httpRequest, err := http.NewRequest("POST", "http://localhost:7890/v1/deployments/upload/yaml", requestReader)
suite.NoError(err)

httpRequest.Header.Add("Content-Type", "multipart/form-data; boundary="+boundary)
httpRequest.Header.Add("Authorization", suite.JWTBearer)
httpRequest.ContentLength = fileStat.Size() + int64(bodyBuf.Len()) + int64(closeBuf.Len())
httpWriter := httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)

assertResponseCode(suite.T(), http.StatusBadRequest, httpWriter)
}
86 changes: 86 additions & 0 deletions src/server/handler_namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@ package server

import (
"fmt"
"io/ioutil"
"math"
"net/http"
"strconv"

"github.com/linkernetworks/utils/timeutils"
"github.com/linkernetworks/vortex/src/entity"
"github.com/linkernetworks/vortex/src/kubernetes"
"github.com/linkernetworks/vortex/src/namespace"
response "github.com/linkernetworks/vortex/src/net/http"
"github.com/linkernetworks/vortex/src/net/http/query"
"github.com/linkernetworks/vortex/src/server/backend"
"github.com/linkernetworks/vortex/src/web"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"

mgo "gopkg.in/mgo.v2"
Expand Down Expand Up @@ -189,3 +192,86 @@ func getNamespaceHandler(ctx *web.Context) {
namespace.CreatedBy, _ = backend.FindUserByID(session, namespace.OwnerID)
resp.WriteEntity(namespace)
}

func uploadNamespaceYAMLHandler(ctx *web.Context) {
sp, req, resp := ctx.ServiceProvider, ctx.Request, ctx.Response
userID, ok := req.Attribute("UserID").(string)
if !ok {
response.Unauthorized(req.Request, resp.ResponseWriter, fmt.Errorf("Unauthorized: User ID is empty"))
return
}

if err := req.Request.ParseMultipartForm(_24K); nil != err {
response.InternalServerError(req.Request, resp.ResponseWriter, fmt.Errorf("Failed to read multipart form: %s", err.Error()))
return
}

infile, _, err := req.Request.FormFile("file")
if err != nil {
response.BadRequest(req.Request, resp.ResponseWriter, fmt.Errorf("Error parsing uploaded file %v", err))
return
}

content, err := ioutil.ReadAll(infile)
if err != nil {
response.InternalServerError(req.Request, resp.ResponseWriter, fmt.Errorf("Failed to read data: %s", err.Error()))
return
}

if len(content) == 0 {
response.BadRequest(req.Request, resp.ResponseWriter, fmt.Errorf("Empty content"))
return
}

obj, err := kubernetes.ParseK8SYAML(content)
if err != nil {
response.BadRequest(req.Request, resp.ResponseWriter, err)
return
}

namespaceObj, ok := obj.(*v1.Namespace)
if !ok {
response.BadRequest(req.Request, resp.ResponseWriter, fmt.Errorf("The YAML file is not for creating namespace"))
return
}

d := entity.Namespace{
ID: bson.NewObjectId(),
OwnerID: bson.ObjectIdHex(userID),
Name: namespaceObj.ObjectMeta.Name,
}

session := sp.Mongo.NewSession()
session.C(entity.NamespaceCollectionName).EnsureIndex(mgo.Index{
Key: []string{"name"},
Unique: true,
})
defer session.Close()

d.CreatedAt = timeutils.Now()
_, err = sp.KubeCtl.CreateNamespace(namespaceObj)
if err != nil {
if errors.IsAlreadyExists(err) {
response.Conflict(req.Request, resp.ResponseWriter, fmt.Errorf("Namespace Name: %s already existed", d.Name))
} else if errors.IsConflict(err) {
response.Conflict(req.Request, resp.ResponseWriter, fmt.Errorf("Create setting has conflict: %v", err))
} else if errors.IsInvalid(err) {
response.BadRequest(req.Request, resp.ResponseWriter, fmt.Errorf("Create setting is invalid: %v", err))
} else {
response.InternalServerError(req.Request, resp.ResponseWriter, err)
}
return
}

if err := session.Insert(entity.NamespaceCollectionName, &d); err != nil {
if mgo.IsDup(err) {
response.Conflict(req.Request, resp.ResponseWriter, fmt.Errorf("Namespace Name: %s already existed", d.Name))
} else {
response.InternalServerError(req.Request, resp.ResponseWriter, err)
}
return
}
// find owner in user entity
d.CreatedBy, _ = backend.FindUserByID(session, d.OwnerID)
resp.WriteHeaderAndEntity(http.StatusCreated, d)
}
95 changes: 95 additions & 0 deletions src/server/handler_namespace_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package server

import (
"bytes"
"encoding/json"
"fmt"
"io"
"math/rand"
"mime/multipart"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -265,3 +270,93 @@ func (suite *NamespaceTestSuite) TestListNamespaceWithInvalidPage() {
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusInternalServerError, httpWriter)
}

func (suite *NamespaceTestSuite) TestUploadNamespaceYAML() {
filename := "../../testYAMLs/namespace.yaml"

bodyBuf := bytes.NewBufferString("")
bodyWriter := multipart.NewWriter(bodyBuf)

// use the bodyWriter to write the Part headers to the buffer
_, err := bodyWriter.CreateFormFile("file", filename)
suite.NoError(err)

// the file data will be the second part of the body
file, err := os.Open(filename)
suite.NoError(err)

// need to know the boundary to properly close the part myself.
boundary := bodyWriter.Boundary()
//close_string := fmt.Sprintf("\r\n--%s--\r\n", boundary)
closeBuf := bytes.NewBufferString(fmt.Sprintf("\r\n--%s--\r\n", boundary))

// use multi-reader to defer the reading of the file data until
// writing to the socket buffer.
requestReader := io.MultiReader(bodyBuf, file, closeBuf)
fileStat, err := file.Stat()
suite.NoError(err)

httpRequest, err := http.NewRequest("POST", "http://localhost:7890/v1/namespaces/upload/yaml", requestReader)
suite.NoError(err)

httpRequest.Header.Add("Content-Type", "multipart/form-data; boundary="+boundary)
httpRequest.Header.Add("Authorization", suite.JWTBearer)
httpRequest.ContentLength = fileStat.Size() + int64(bodyBuf.Len()) + int64(closeBuf.Len())
httpWriter := httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
defer suite.session.Remove(entity.NamespaceCollectionName, "name", "uploadnamespace")

assertResponseCode(suite.T(), http.StatusCreated, httpWriter)

//load data to check
retNamespace := entity.Namespace{}
err = suite.session.FindOne(entity.NamespaceCollectionName, bson.M{"name": "uploadnamespace"}, &retNamespace)
suite.NoError(err)
suite.NotEqual("", retNamespace.ID)
suite.Equal("uploadnamespace", retNamespace.Name)

//Create again and it should fail since the name exist
httpWriter = httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusConflict, httpWriter)

err = ns.DeleteNamespace(suite.sp, &retNamespace)
suite.NoError(err)
}

func (suite *NamespaceTestSuite) TestUploadNamespaceYAMLFail() {
filename := "../../testYAMLs/deployment.yaml"

bodyBuf := bytes.NewBufferString("")
bodyWriter := multipart.NewWriter(bodyBuf)

// use the bodyWriter to write the Part headers to the buffer
_, err := bodyWriter.CreateFormFile("file", filename)
suite.NoError(err)

// the file data will be the second part of the body
file, err := os.Open(filename)
suite.NoError(err)

// need to know the boundary to properly close the part myself.
boundary := bodyWriter.Boundary()
//close_string := fmt.Sprintf("\r\n--%s--\r\n", boundary)
closeBuf := bytes.NewBufferString(fmt.Sprintf("\r\n--%s--\r\n", boundary))

// use multi-reader to defer the reading of the file data until
// writing to the socket buffer.
requestReader := io.MultiReader(bodyBuf, file, closeBuf)
fileStat, err := file.Stat()
suite.NoError(err)

httpRequest, err := http.NewRequest("POST", "http://localhost:7890/v1/namespaces/upload/yaml", requestReader)
suite.NoError(err)

httpRequest.Header.Add("Content-Type", "multipart/form-data; boundary="+boundary)
httpRequest.Header.Add("Authorization", suite.JWTBearer)
httpRequest.ContentLength = fileStat.Size() + int64(bodyBuf.Len()) + int64(closeBuf.Len())
httpWriter := httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)

assertResponseCode(suite.T(), http.StatusBadRequest, httpWriter)
}
Loading

0 comments on commit edbd47b

Please sign in to comment.