From ad78ecef51a25f57c71eb7f1538016f5d59ffea1 Mon Sep 17 00:00:00 2001 From: John-Lin Date: Mon, 3 Sep 2018 15:03:16 +0800 Subject: [PATCH] upload deployment yaml --- src/deployment/deployment.go | 2 +- src/kubernetes/yaml.go | 17 ++++++ src/server/handler_deployment.go | 91 ++++++++++++++++++++++++++++++++ src/server/route.go | 1 + 4 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 src/kubernetes/yaml.go diff --git a/src/deployment/deployment.go b/src/deployment/deployment.go index 3d63e8e9..fa524922 100644 --- a/src/deployment/deployment.go +++ b/src/deployment/deployment.go @@ -334,7 +334,7 @@ func CreateDeployment(sp *serviceprovider.Container, deploy *entity.Deployment) return err } -// DeleteDeployment will delete deploy +// DeleteDeployment will delete a deployment func DeleteDeployment(sp *serviceprovider.Container, deploy *entity.Deployment) error { return sp.KubeCtl.DeleteDeployment(deploy.Name, deploy.Namespace) } diff --git a/src/kubernetes/yaml.go b/src/kubernetes/yaml.go new file mode 100644 index 00000000..4bb28e61 --- /dev/null +++ b/src/kubernetes/yaml.go @@ -0,0 +1,17 @@ +package kubernetes + +import ( + "fmt" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/scheme" +) + +func ParseK8SYAML(data []byte) (runtime.Object, error) { + decode := scheme.Codecs.UniversalDeserializer().Decode + obj, _, err := decode(data, nil, nil) + if err != nil { + return obj, fmt.Errorf("Error while decoding YAML object. Err was: %v", err) + } + return obj, nil +} diff --git a/src/server/handler_deployment.go b/src/server/handler_deployment.go index b6fdf467..5934bd9f 100644 --- a/src/server/handler_deployment.go +++ b/src/server/handler_deployment.go @@ -2,6 +2,7 @@ package server import ( "fmt" + "io/ioutil" "math" "net/http" "strconv" @@ -9,16 +10,20 @@ import ( "github.com/linkernetworks/utils/timeutils" "github.com/linkernetworks/vortex/src/deployment" "github.com/linkernetworks/vortex/src/entity" + "github.com/linkernetworks/vortex/src/kubernetes" 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/apps/v1" "k8s.io/apimachinery/pkg/api/errors" mgo "gopkg.in/mgo.v2" "gopkg.in/mgo.v2/bson" ) +const _24K = (1 << 10) * 24 + func createDeploymentHandler(ctx *web.Context) { sp, req, resp := ctx.ServiceProvider, ctx.Request, ctx.Response userID, ok := req.Attribute("UserID").(string) @@ -196,3 +201,89 @@ func getDeploymentHandler(ctx *web.Context) { deployment.CreatedBy, _ = backend.FindUserByID(session, deployment.OwnerID) resp.WriteEntity(deployment) } + +func uploadDeploymentYAMLHandler(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 + } + + deploymentObj := obj.(*v1.Deployment) + + d := entity.Deployment{ + ID: bson.NewObjectId(), + OwnerID: bson.ObjectIdHex(userID), + Name: deploymentObj.ObjectMeta.Name, + Namespace: deploymentObj.ObjectMeta.Namespace, + NetworkType: entity.DeploymentClusterNetwork, + Replicas: *deploymentObj.Spec.Replicas, + } + + if d.Namespace == "" { + d.Namespace = "default" + } + + session := sp.Mongo.NewSession() + session.C(entity.DeploymentCollectionName).EnsureIndex(mgo.Index{ + Key: []string{"name"}, + Unique: true, + }) + defer session.Close() + + d.CreatedAt = timeutils.Now() + _, err = sp.KubeCtl.CreateDeployment(deploymentObj, d.Namespace) + if err != nil { + if errors.IsAlreadyExists(err) { + response.Conflict(req.Request, resp.ResponseWriter, fmt.Errorf("Deployment 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.DeploymentCollectionName, &d); err != nil { + if mgo.IsDup(err) { + response.Conflict(req.Request, resp.ResponseWriter, fmt.Errorf("Deployment 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) +} diff --git a/src/server/route.go b/src/server/route.go index 2fdec48b..27a28962 100644 --- a/src/server/route.go +++ b/src/server/route.go @@ -125,6 +125,7 @@ func newDeploymentService(sp *serviceprovider.Container) *restful.WebService { webService.Route(webService.DELETE("/{id}").To(handler.RESTfulServiceHandler(sp, deleteDeploymentHandler))) webService.Route(webService.GET("/").To(handler.RESTfulServiceHandler(sp, listDeploymentHandler))) webService.Route(webService.GET("/{id}").To(handler.RESTfulServiceHandler(sp, getDeploymentHandler))) + webService.Route(webService.POST("/upload/yaml").Consumes("multipart/form-data").To(handler.RESTfulServiceHandler(sp, uploadDeploymentYAMLHandler))) return webService }