mirror of
https://gitee.com/rainbond/Rainbond.git
synced 2024-11-30 02:38:17 +08:00
[ADD] add docker run parser and image handle
This commit is contained in:
parent
2a4b99223e
commit
06f92a78c6
2
Makefile
2
Makefile
@ -88,7 +88,7 @@ push-hub-image:
|
||||
@docker push ${BASE_NAME}/rbd-api:${VERSION}
|
||||
|
||||
run-api:build-api
|
||||
${BIN_PATH}/${BASE_NAME}-api --log-level=debug --mysql="admin:admin@tcp(127.0.0.1:3306)/region" --kube-config="`PWD`/admin.kubeconfig"
|
||||
${BIN_PATH}/${BASE_NAME}-api --log-level=debug --mysql="root:admin@tcp(127.0.0.1:3306)/region" --kube-config="`PWD`/test/admin.kubeconfig"
|
||||
run-mq:build-mq
|
||||
${BIN_PATH}/${BASE_NAME}-mq --log-level=debug
|
||||
run-worker:build-worker
|
||||
|
@ -1258,6 +1258,58 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/servicecheck": {
|
||||
"post": {
|
||||
"description": "应用构建源检测,支持docker run ,docker compose, source code",
|
||||
"consumes": [
|
||||
"application/json",
|
||||
"application/x-protobuf"
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/xml"
|
||||
],
|
||||
"operationId": "serviceCheck",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Body",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"source_type",
|
||||
"source_body"
|
||||
],
|
||||
"properties": {
|
||||
"source_body": {
|
||||
"description": "检测来源定义,\n代码: https://github.com/shurcooL/githubql.git master\ndocker-run: docker run --name xxx nginx:latest nginx\ndocker-compose: compose全文\nin: body",
|
||||
"type": "string",
|
||||
"x-go-name": "SourceBody"
|
||||
},
|
||||
"source_type": {
|
||||
"description": "检测来源类型\nin: body",
|
||||
"type": "string",
|
||||
"x-go-name": "SourceType"
|
||||
},
|
||||
"uuid": {
|
||||
"description": "uuid\nin: body",
|
||||
"type": "string",
|
||||
"x-go-name": "CheckUUID"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"default": {
|
||||
"description": "统一返回格式",
|
||||
"schema": {
|
||||
"$ref": "#/responses/commandResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/show": {
|
||||
"get": {
|
||||
"description": "show api version",
|
||||
|
@ -20,8 +20,6 @@ package doc
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
@ -32,9 +30,10 @@ import (
|
||||
//Routes routes
|
||||
func Routes() chi.Router {
|
||||
r := chi.NewRouter()
|
||||
workDir, _ := os.Getwd()
|
||||
//workDir, _ := os.Getwd()
|
||||
//logrus.Debugf("workdir is %v", workDir)
|
||||
filesDir := filepath.Join(workDir, "html")
|
||||
//filesDir := filepath.Join(workDir, "html")
|
||||
filesDir := "/Users/qingguo/gopath/src/github.com/goodrain/rainbond/hack/contrib/docker/api/html"
|
||||
logrus.Debugf("filesdir is %v", filesDir)
|
||||
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("/docs"))
|
||||
|
@ -49,6 +49,7 @@ func (v2 *V2) tenantRouter() chi.Router {
|
||||
r := chi.NewRouter()
|
||||
r.Post("/", controller.GetManager().Tenant)
|
||||
r.Mount("/{tenant_name}", v2.tenantNameRouter())
|
||||
r.Post("/servicecheck", controller.Check)
|
||||
return r
|
||||
}
|
||||
|
||||
|
68
pkg/api/controller/check.go
Normal file
68
pkg/api/controller/check.go
Normal file
@ -0,0 +1,68 @@
|
||||
// RAINBOND, Application Management Platform
|
||||
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version. For any non-GPL usage of Rainbond,
|
||||
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
|
||||
// must be obtained first.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/goodrain/rainbond/pkg/api/handler"
|
||||
"github.com/goodrain/rainbond/pkg/api/middleware"
|
||||
api_model "github.com/goodrain/rainbond/pkg/api/model"
|
||||
httputil "github.com/goodrain/rainbond/pkg/util/http"
|
||||
)
|
||||
|
||||
//Check service check
|
||||
// swagger:operation POST /v2/servicecheck v2 serviceCheck
|
||||
//
|
||||
// 应用构建源检测,支持docker run ,docker compose, source code
|
||||
//
|
||||
//
|
||||
// ---
|
||||
// consumes:
|
||||
// - application/json
|
||||
// - application/x-protobuf
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
// - application/xml
|
||||
//
|
||||
// responses:
|
||||
// default:
|
||||
// schema:
|
||||
// "$ref": "#/responses/commandResponse"
|
||||
// description: 统一返回格式
|
||||
func Check(w http.ResponseWriter, r *http.Request) {
|
||||
var gt api_model.ServiceCheckStruct
|
||||
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, >.Body, nil); !ok {
|
||||
return
|
||||
}
|
||||
tenantID := r.Context().Value(middleware.ContextKey("tenant_id")).(string)
|
||||
gt.Body.TenantID = tenantID
|
||||
result, err := handler.GetServiceManager().ServiceCheck(>)
|
||||
if err != nil {
|
||||
err.Handle(r, w)
|
||||
return
|
||||
}
|
||||
re := struct {
|
||||
CheckUUID string `json:"check_uuid"`
|
||||
}{
|
||||
CheckUUID: result,
|
||||
}
|
||||
httputil.ReturnSuccess(r, w, re)
|
||||
}
|
@ -68,6 +68,7 @@ type ServiceHandler interface {
|
||||
SetVersionEnv(sve *api_model.SetVersionEnv) *util.APIHandleError
|
||||
UpdateVersionEnv(uve *api_model.SetVersionEnv) *util.APIHandleError
|
||||
DeleteComplexEnvs(tenantID, serviceAlias, pluginID string) *util.APIHandleError
|
||||
ServiceCheck(*api_model.ServiceCheckStruct) (string, *util.APIHandleError)
|
||||
}
|
||||
|
||||
var defaultServieHandler ServiceHandler
|
||||
|
@ -528,7 +528,7 @@ func (s *ServiceAction) ServiceCreate(sc *api_model.ServiceStruct) error {
|
||||
//共享文件存储
|
||||
case dbmodel.ShareFileVolumeType.String():
|
||||
volumn.HostPath = fmt.Sprintf("%s/tenant/%s/service/%s%s", sharePath, sc.TenantID, volumn.ServiceID, volumn.VolumePath)
|
||||
//本地文件存储
|
||||
//本地文件存<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
case dbmodel.LocalVolumeType.String():
|
||||
serviceType, err := db.GetManager().TenantServiceLabelDao().GetTenantServiceTypeLabel(volumn.ServiceID)
|
||||
if err != nil {
|
||||
@ -1967,7 +1967,7 @@ func (s *ServiceAction) upComplexEnvs(uve *api_model.SetVersionEnv) *util.APIHan
|
||||
func TransStatus(eStatus string) string {
|
||||
switch eStatus {
|
||||
case "starting":
|
||||
return "启动中"
|
||||
return "<EFBFBD><EFBFBD><EFBFBD>动中"
|
||||
case "abnormal":
|
||||
return "运行异常"
|
||||
case "upgrade":
|
||||
|
60
pkg/api/handler/serviceCheck.go
Normal file
60
pkg/api/handler/serviceCheck.go
Normal file
@ -0,0 +1,60 @@
|
||||
// RAINBOND, Application Management Platform
|
||||
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version. For any non-GPL usage of Rainbond,
|
||||
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
|
||||
// must be obtained first.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/pquerna/ffjson/ffjson"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
api_db "github.com/goodrain/rainbond/pkg/api/db"
|
||||
api_model "github.com/goodrain/rainbond/pkg/api/model"
|
||||
"github.com/goodrain/rainbond/pkg/api/util"
|
||||
"github.com/twinj/uuid"
|
||||
)
|
||||
|
||||
//ServiceCheck 应用构建源检测
|
||||
func (s *ServiceAction) ServiceCheck(scs *api_model.ServiceCheckStruct) (string, *util.APIHandleError) {
|
||||
checkUUID := uuid.NewV4().String()
|
||||
scs.Body.CheckUUID = checkUUID
|
||||
body, err := ffjson.Marshal(scs.Body)
|
||||
if err != nil {
|
||||
logrus.Errorf("marshal service check request body error.%s", err.Error())
|
||||
return "", util.CreateAPIHandleError(500, err)
|
||||
}
|
||||
bs := &api_db.BuildTaskStruct{
|
||||
TaskType: "service_check",
|
||||
TaskBody: body,
|
||||
User: "define",
|
||||
}
|
||||
eq, errEq := api_db.BuildTaskBuild(bs)
|
||||
if errEq != nil {
|
||||
logrus.Errorf("build equeue code check error, %v", errEq)
|
||||
return "", util.CreateAPIHandleError(500, err)
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
_, err = s.MQClient.Enqueue(ctx, eq)
|
||||
if err != nil {
|
||||
logrus.Errorf("equque mq error, %v", err)
|
||||
return "", util.CreateAPIHandleError(500, err)
|
||||
}
|
||||
return checkUUID, nil
|
||||
}
|
@ -831,6 +831,33 @@ type CheckCodeStruct struct {
|
||||
}
|
||||
}
|
||||
|
||||
//ServiceCheckStruct 应用检测,支持源码检测,镜像检测,dockerrun检测
|
||||
//swagger:parameters serviceCheck
|
||||
type ServiceCheckStruct struct {
|
||||
// in: path
|
||||
// required: true
|
||||
TenantName string `json:"tenant_name"`
|
||||
// in: body
|
||||
Body struct {
|
||||
//uuid
|
||||
// in: body
|
||||
CheckUUID string `json:"uuid"`
|
||||
//检测来源类型
|
||||
// in: body
|
||||
// required: true
|
||||
SourceType string `json:"source_type" validate:"source_type|required|in:docker-run,docker-compose,sourcecode"`
|
||||
|
||||
// 检测来源定义,
|
||||
// 代码: https://github.com/shurcooL/githubql.git master
|
||||
// docker-run: docker run --name xxx nginx:latest nginx
|
||||
// docker-compose: compose全文
|
||||
// in: body
|
||||
// required: true
|
||||
SourceBody string `json:"source_body" validate:"source_body|required"`
|
||||
TenantID string
|
||||
}
|
||||
}
|
||||
|
||||
//CloudShareStruct CloudShareStruct
|
||||
//swagger:parameters sharecloud
|
||||
type CloudShareStruct struct {
|
||||
|
@ -1,19 +1,18 @@
|
||||
|
||||
// RAINBOND, Application Management Platform
|
||||
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
|
||||
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version. For any non-GPL usage of Rainbond,
|
||||
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
|
||||
// must be obtained first.
|
||||
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
@ -23,11 +22,11 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/goodrain/rainbond/pkg/db/config"
|
||||
"github.com/goodrain/rainbond/pkg/event"
|
||||
"github.com/goodrain/rainbond/pkg/mq/api/grpc/pb"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
@ -70,6 +69,8 @@ func (e *exectorManager) AddTask(task *pb.TaskMessage) error {
|
||||
e.imageManual(task.TaskBody)
|
||||
case "code_check":
|
||||
e.codeCheck(task.TaskBody)
|
||||
case "service_check":
|
||||
e.serviceCheck(task.TaskBody)
|
||||
case "app_build":
|
||||
e.appBuild(task.TaskBody)
|
||||
case "plugin_image_build":
|
||||
@ -147,8 +148,6 @@ func (e *exectorManager) appSlug(in []byte) {
|
||||
//}()
|
||||
//updateBuildResult(eventID,"failure",dest)
|
||||
|
||||
|
||||
|
||||
eventID := gjson.GetBytes(in, "event_id").String()
|
||||
logger := event.GetManager().GetLogger(eventID)
|
||||
logger.Info("应用代码包构建任务开始执行", map[string]string{"step": "builder-exector", "status": "starting"})
|
||||
@ -252,6 +251,7 @@ func (e *exectorManager) appBuild(in []byte) {
|
||||
}()
|
||||
//updateBuildResult(eventID,finalStatus,dest)
|
||||
}
|
||||
|
||||
//func updateBuildResult(eventID,finalStatus,dest string) {
|
||||
// if dest == ""||!strings.Contains(dest,"y") {
|
||||
// v,err:=db.GetManager().VersionInfoDao().GetVersionByEventID(eventID)
|
||||
|
116
pkg/builder/exector/service_check.go
Normal file
116
pkg/builder/exector/service_check.go
Normal file
@ -0,0 +1,116 @@
|
||||
// RAINBOND, Application Management Platform
|
||||
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version. For any non-GPL usage of Rainbond,
|
||||
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
|
||||
// must be obtained first.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package exector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/goodrain/rainbond/pkg/builder/parser"
|
||||
"github.com/goodrain/rainbond/pkg/event"
|
||||
"github.com/pquerna/ffjson/ffjson"
|
||||
)
|
||||
|
||||
//ServiceCheckInput 任务输入数据
|
||||
type ServiceCheckInput struct {
|
||||
CheckUUID string `json:"uuid"`
|
||||
//检测来源类型
|
||||
SourceType string `json:"source_type"`
|
||||
|
||||
// 检测来源定义,
|
||||
// 代码: https://github.com/shurcooL/githubql.git master
|
||||
// docker-run: docker run --name xxx nginx:latest nginx
|
||||
// docker-compose: compose全文
|
||||
SourceBody string `json:"source_body"`
|
||||
TenantID string
|
||||
EventID string `json:"event_id"`
|
||||
}
|
||||
|
||||
//ServiceCheckResult 应用检测结果
|
||||
type ServiceCheckResult struct {
|
||||
//检测状态 Success Failure
|
||||
CheckStatus string `json:"check_status"`
|
||||
ErrorInfos parser.ParseErrorList
|
||||
ServiceInfo ServiceInfo `json:"service_info"`
|
||||
}
|
||||
|
||||
//CreateResult 创建检测结果
|
||||
func CreateResult(ErrorInfos parser.ParseErrorList, ServiceInfo ServiceInfo) error {
|
||||
var sr ServiceCheckResult
|
||||
if ErrorInfos != nil && ErrorInfos.IsFatalError() {
|
||||
sr = ServiceCheckResult{
|
||||
CheckStatus: "Failure",
|
||||
ErrorInfos: ErrorInfos,
|
||||
ServiceInfo: ServiceInfo,
|
||||
}
|
||||
}
|
||||
sr = ServiceCheckResult{
|
||||
CheckStatus: "Success",
|
||||
ErrorInfos: ErrorInfos,
|
||||
ServiceInfo: ServiceInfo,
|
||||
}
|
||||
//save result
|
||||
fmt.Println(sr)
|
||||
return nil
|
||||
}
|
||||
|
||||
//ServiceInfo 智能获取的应用信息
|
||||
type ServiceInfo struct {
|
||||
Ports []parser.Port `json:"ports"`
|
||||
Envs []parser.Env `json:"envs"`
|
||||
Volumes []parser.Volume `json:"volumes"`
|
||||
Image parser.Image `json:"image"`
|
||||
Args []string `json:"args"`
|
||||
DependServices []string `json:"depends,omitempty"`
|
||||
ServiceDeployType string `json:"deploy_type,omitempty"`
|
||||
}
|
||||
|
||||
//serviceCheck 应用创建源检测
|
||||
func (e *exectorManager) serviceCheck(in []byte) {
|
||||
//step1 判断应用源类型
|
||||
//step2 获取应用源介质,镜像Or源码
|
||||
//step3 解析判断应用源规范
|
||||
//完成
|
||||
var input ServiceCheckInput
|
||||
if err := ffjson.Unmarshal(in, &input); err != nil {
|
||||
logrus.Error("Unmarshal service check input data error.", err.Error())
|
||||
return
|
||||
}
|
||||
logger := event.GetManager().GetLogger(input.EventID)
|
||||
logger.Info("开始应用构建源检测", map[string]string{"step": "starting"})
|
||||
switch input.SourceType {
|
||||
case "docker-run":
|
||||
parser := parser.CreateDockerRunOrImageParse(input.SourceBody, e.DockerClient, logger)
|
||||
if errList := parser.Parse(); errList != nil {
|
||||
for i, err := range errList {
|
||||
if err.SolveAdvice == "" {
|
||||
errList[i].SolveAdvice = fmt.Sprintf("解析器认为镜像名为:%s,请确认是否正确或镜像是否存在", parser.GetImage())
|
||||
}
|
||||
}
|
||||
if errList.IsFatalError() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
case "docker-compose":
|
||||
|
||||
case "sourcecode":
|
||||
|
||||
}
|
||||
}
|
278
pkg/builder/parser/docker_run.go
Normal file
278
pkg/builder/parser/docker_run.go
Normal file
@ -0,0 +1,278 @@
|
||||
// RAINBOND, Application Management Platform
|
||||
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version. For any non-GPL usage of Rainbond,
|
||||
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
|
||||
// must be obtained first.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package parser
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/goodrain/rainbond/pkg/builder/sources"
|
||||
"github.com/goodrain/rainbond/pkg/db/model"
|
||||
"github.com/goodrain/rainbond/pkg/event"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
)
|
||||
|
||||
//DockerRunOrImageParse docker run 命令解析或直接镜像名解析
|
||||
type DockerRunOrImageParse struct {
|
||||
ports map[int]*Port
|
||||
volumes map[string]*Volume
|
||||
envs map[string]*Env
|
||||
source string
|
||||
memory int
|
||||
image Image
|
||||
args []string
|
||||
errors []ParseError
|
||||
dockerclient *client.Client
|
||||
logger event.Logger
|
||||
}
|
||||
|
||||
//CreateDockerRunOrImageParse create parser
|
||||
func CreateDockerRunOrImageParse(source string, dockerclient *client.Client, logger event.Logger) Parser {
|
||||
source = strings.TrimLeft(source, " ")
|
||||
source = strings.Replace(source, "\n", "", -1)
|
||||
source = strings.Replace(source, "\\", "", -1)
|
||||
source = strings.Replace(source, " ", " ", -1)
|
||||
return &DockerRunOrImageParse{
|
||||
source: source,
|
||||
dockerclient: dockerclient,
|
||||
ports: make(map[int]*Port),
|
||||
volumes: make(map[string]*Volume),
|
||||
envs: make(map[string]*Env),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
//Parse 解码,获取镜像,解析镜像
|
||||
//eg. docker run -it -p 80:80 nginx
|
||||
func (d *DockerRunOrImageParse) Parse() ParseErrorList {
|
||||
if d.source == "" {
|
||||
d.errappend(Errorf(FatalError, "source can not be empty"))
|
||||
return d.errors
|
||||
}
|
||||
//docker run
|
||||
if strings.HasPrefix(d.source, "docker") {
|
||||
d.dockerun(strings.Split(d.source, " "))
|
||||
if _, err := reference.ParseAnyReference(d.image.String()); err != nil {
|
||||
d.errappend(Errorf(FatalError, "Error parsing reference: %q is not a valid repository/tag", d.image))
|
||||
return d.errors
|
||||
}
|
||||
} else {
|
||||
//else image
|
||||
_, err := reference.ParseAnyReference(d.source)
|
||||
if err != nil {
|
||||
d.errappend(Errorf(FatalError, "Error parsing reference: %q is not a valid repository/tag", d.source))
|
||||
return d.errors
|
||||
}
|
||||
d.image = parseImageName(d.source)
|
||||
}
|
||||
//获取镜像,验证是否存在
|
||||
imageInspect, err := sources.ImagePull(d.dockerclient, d.image.String(), types.ImagePullOptions{}, d.logger, 5)
|
||||
if err != nil {
|
||||
d.errappend(Errorf(FatalError, err.Error()))
|
||||
return d.errors
|
||||
}
|
||||
if imageInspect != nil && imageInspect.ContainerConfig != nil {
|
||||
for _, env := range imageInspect.ContainerConfig.Env {
|
||||
envinfo := strings.Split(env, "=")
|
||||
if len(envinfo) == 2 {
|
||||
if _, ok := d.envs[envinfo[0]]; !ok {
|
||||
d.envs[envinfo[0]] = &Env{Name: envinfo[0], Value: envinfo[1]}
|
||||
}
|
||||
}
|
||||
}
|
||||
for k := range imageInspect.ContainerConfig.Volumes {
|
||||
if _, ok := d.volumes[k]; !ok {
|
||||
d.volumes[k] = &Volume{VolumePath: k, VolumeType: model.ShareFileVolumeType.String()}
|
||||
}
|
||||
}
|
||||
for k := range imageInspect.ContainerConfig.ExposedPorts {
|
||||
proto := k.Proto()
|
||||
port := k.Int()
|
||||
if _, ok := d.ports[port]; ok {
|
||||
d.ports[port].Protocol = proto
|
||||
} else {
|
||||
d.ports[port] = &Port{Protocol: proto, ContainerPort: port}
|
||||
}
|
||||
}
|
||||
}
|
||||
return d.errors
|
||||
}
|
||||
|
||||
func (d *DockerRunOrImageParse) dockerun(source []string) {
|
||||
var name string
|
||||
for i, s := range source {
|
||||
if s == "docker" || s == "run" {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(s, "-") {
|
||||
name = strings.TrimLeft(s, "-")
|
||||
index := strings.Index(name, "=")
|
||||
if index > 0 && index < len(name)-1 {
|
||||
s = name[index+1:]
|
||||
name = name[0:index]
|
||||
switch name {
|
||||
case "e", "env":
|
||||
info := strings.Split(s, "=")
|
||||
if len(info) == 2 {
|
||||
d.envs[info[0]] = &Env{Name: info[0], Value: info[1]}
|
||||
}
|
||||
case "p", "public":
|
||||
info := strings.Split(s, ":")
|
||||
if len(info) == 2 {
|
||||
port, _ := strconv.Atoi(info[0])
|
||||
if port != 0 {
|
||||
d.ports[port] = &Port{ContainerPort: port, Protocol: "tcp"}
|
||||
}
|
||||
}
|
||||
case "v", "volume":
|
||||
info := strings.Split(s, ":")
|
||||
if len(info) >= 2 {
|
||||
d.volumes[info[1]] = &Volume{VolumePath: info[1], VolumeType: model.ShareFileVolumeType.String()}
|
||||
}
|
||||
case "memory", "m":
|
||||
d.memory = readmemory(s)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch name {
|
||||
case "e", "env":
|
||||
info := strings.Split(s, "=")
|
||||
if len(info) == 2 {
|
||||
d.envs[info[0]] = &Env{Name: info[0], Value: info[1]}
|
||||
}
|
||||
case "p", "public":
|
||||
info := strings.Split(s, ":")
|
||||
if len(info) == 2 {
|
||||
port, _ := strconv.Atoi(info[0])
|
||||
if port != 0 {
|
||||
d.ports[port] = &Port{ContainerPort: port, Protocol: "tcp"}
|
||||
}
|
||||
}
|
||||
case "v", "volume":
|
||||
info := strings.Split(s, ":")
|
||||
if len(info) >= 2 {
|
||||
d.volumes[info[1]] = &Volume{VolumePath: info[1], VolumeType: model.ShareFileVolumeType.String()}
|
||||
}
|
||||
case "memory", "m":
|
||||
d.memory = readmemory(s)
|
||||
case "", "d", "i", "t", "it":
|
||||
d.image = parseImageName(s)
|
||||
if len(source) > i+1 {
|
||||
d.args = source[i+1:]
|
||||
}
|
||||
return
|
||||
}
|
||||
name = ""
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//readmemory
|
||||
//10m 10
|
||||
//10g 10*1024
|
||||
//10k 128
|
||||
//10b 128
|
||||
func readmemory(s string) int {
|
||||
if strings.HasSuffix(s, "m") {
|
||||
s, err := strconv.Atoi(s[0 : len(s)-1])
|
||||
if err != nil {
|
||||
return 128
|
||||
}
|
||||
return s
|
||||
}
|
||||
if strings.HasSuffix(s, "g") {
|
||||
s, err := strconv.Atoi(s[0 : len(s)-1])
|
||||
if err != nil {
|
||||
return 128
|
||||
}
|
||||
return s * 1024
|
||||
}
|
||||
return 128
|
||||
}
|
||||
|
||||
func parseImageName(s string) Image {
|
||||
index := strings.Index(s, ":")
|
||||
if index > -1 {
|
||||
return Image{
|
||||
Name: s[0:index],
|
||||
Tag: s[index+1:],
|
||||
}
|
||||
}
|
||||
return Image{
|
||||
Name: s,
|
||||
Tag: "latest",
|
||||
}
|
||||
}
|
||||
func (d *DockerRunOrImageParse) errappend(pe ParseError) {
|
||||
d.errors = append(d.errors, pe)
|
||||
}
|
||||
|
||||
//GetBranchs 获取分支列表
|
||||
func (d *DockerRunOrImageParse) GetBranchs() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
//GetPorts 获取端口列表
|
||||
func (d *DockerRunOrImageParse) GetPorts() (ports []Port) {
|
||||
for _, cv := range d.ports {
|
||||
ports = append(ports, *cv)
|
||||
}
|
||||
return ports
|
||||
}
|
||||
|
||||
//GetVolumes 获取存储列表
|
||||
func (d *DockerRunOrImageParse) GetVolumes() (volumes []Volume) {
|
||||
for _, cv := range d.volumes {
|
||||
volumes = append(volumes, *cv)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//GetValid 获取源是否合法
|
||||
func (d *DockerRunOrImageParse) GetValid() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
//GetEnvs 环境变量
|
||||
func (d *DockerRunOrImageParse) GetEnvs() (envs []Env) {
|
||||
for _, cv := range d.envs {
|
||||
envs = append(envs, *cv)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//GetImage 获取镜像
|
||||
func (d *DockerRunOrImageParse) GetImage() Image {
|
||||
return d.image
|
||||
}
|
||||
|
||||
//GetArgs 启动参数
|
||||
func (d *DockerRunOrImageParse) GetArgs() []string {
|
||||
return d.args
|
||||
}
|
||||
|
||||
//GetMemory 获取内存
|
||||
func (d *DockerRunOrImageParse) GetMemory() int {
|
||||
return d.memory
|
||||
}
|
55
pkg/builder/parser/docker_run_test.go
Normal file
55
pkg/builder/parser/docker_run_test.go
Normal file
@ -0,0 +1,55 @@
|
||||
// RAINBOND, Application Management Platform
|
||||
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version. For any non-GPL usage of Rainbond,
|
||||
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
|
||||
// must be obtained first.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/client"
|
||||
)
|
||||
|
||||
var dockerrun = `
|
||||
docker run --name my-custom-nginx-container \
|
||||
-d -it \
|
||||
-v=/host/path/nginx.conf:/etc/nginx/nginx.conf:ro \
|
||||
-e=xxx=xxx \
|
||||
--public 90:90 \
|
||||
-m 10g \
|
||||
mysql
|
||||
|
||||
`
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
dockerclient, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
p := CreateDockerRunOrImageParse(dockerrun, dockerclient)
|
||||
if err := p.Parse(); err != nil {
|
||||
logrus.Errorf(err.Error())
|
||||
}
|
||||
fmt.Printf("Volume:%v \n", p.GetVolumes())
|
||||
fmt.Printf("Ports:%v \n", p.GetPorts())
|
||||
fmt.Printf("Envs:%v \n", p.GetEnvs())
|
||||
fmt.Printf("Image:%v \n", p.GetImage())
|
||||
fmt.Printf("Memory:%v \n", p.GetMemory())
|
||||
fmt.Printf("Args:%v \n", p.GetArgs())
|
||||
}
|
131
pkg/builder/parser/parser.go
Normal file
131
pkg/builder/parser/parser.go
Normal file
@ -0,0 +1,131 @@
|
||||
// RAINBOND, Application Management Platform
|
||||
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version. For any non-GPL usage of Rainbond,
|
||||
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
|
||||
// must be obtained first.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
//Port 端口
|
||||
type Port struct {
|
||||
ContainerPort int
|
||||
Protocol string
|
||||
}
|
||||
|
||||
//Volume 存储地址
|
||||
type Volume struct {
|
||||
VolumePath string
|
||||
VolumeType string
|
||||
}
|
||||
|
||||
//Env env desc
|
||||
type Env struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
//ParseError 错误信息
|
||||
type ParseError struct {
|
||||
ErrorType ParseErrorType
|
||||
ErrorInfo string
|
||||
SolveAdvice string
|
||||
}
|
||||
|
||||
//ParseErrorList 错误列表
|
||||
type ParseErrorList []ParseError
|
||||
|
||||
//ParseErrorType 错误类型
|
||||
type ParseErrorType string
|
||||
|
||||
//FatalError 致命错误
|
||||
var FatalError ParseErrorType = "FatalError"
|
||||
|
||||
//NegligibleError 可忽略错误
|
||||
var NegligibleError ParseErrorType = "NegligibleError"
|
||||
|
||||
//Errorf error create
|
||||
func Errorf(errtype ParseErrorType, format string, a ...interface{}) ParseError {
|
||||
parseError := ParseError{
|
||||
ErrorType: errtype,
|
||||
ErrorInfo: fmt.Sprintf(format, a...),
|
||||
}
|
||||
return parseError
|
||||
}
|
||||
|
||||
//ErrorAndSolve error create
|
||||
func ErrorAndSolve(errtype ParseErrorType, errorInfo, SolveAdvice string) ParseError {
|
||||
parseError := ParseError{
|
||||
ErrorType: errtype,
|
||||
ErrorInfo: errorInfo,
|
||||
SolveAdvice: SolveAdvice,
|
||||
}
|
||||
return parseError
|
||||
}
|
||||
|
||||
func (p ParseError) Error() string {
|
||||
return fmt.Sprintf("Type:%s Message:%s", p.ErrorType, p.ErrorInfo)
|
||||
}
|
||||
func (ps ParseErrorList) Error() string {
|
||||
var re string
|
||||
for _, p := range ps {
|
||||
re += fmt.Sprintf("Type:%s Message:%s\n", p.ErrorType, p.ErrorInfo)
|
||||
}
|
||||
return re
|
||||
}
|
||||
|
||||
//IsFatalError 是否具有致命错误
|
||||
func (ps ParseErrorList) IsFatalError() bool {
|
||||
for _, p := range ps {
|
||||
if p.ErrorType == FatalError {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//Image 镜像
|
||||
type Image struct {
|
||||
Name string
|
||||
Tag string
|
||||
}
|
||||
|
||||
func (i Image) String() string {
|
||||
return fmt.Sprintf("%s:%s", i.Name, i.Tag)
|
||||
}
|
||||
|
||||
//Parser 解析器
|
||||
type Parser interface {
|
||||
Parse() ParseErrorList
|
||||
//获取分支列表
|
||||
GetBranchs() []string
|
||||
//获取端口列表
|
||||
GetPorts() []Port
|
||||
//获取存储列表
|
||||
GetVolumes() []Volume
|
||||
//获取源是否合法
|
||||
GetValid() bool
|
||||
//获取环境变量
|
||||
GetEnvs() []Env
|
||||
//获取镜像名
|
||||
GetImage() Image
|
||||
//获取启动参数
|
||||
GetArgs() []string
|
||||
//获取内存
|
||||
GetMemory() int
|
||||
}
|
76
pkg/builder/sources/image.go
Normal file
76
pkg/builder/sources/image.go
Normal file
@ -0,0 +1,76 @@
|
||||
// RAINBOND, Application Management Platform
|
||||
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version. For any non-GPL usage of Rainbond,
|
||||
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
|
||||
// must be obtained first.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package sources
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/goodrain/rainbond/pkg/event"
|
||||
)
|
||||
|
||||
//ImagePull 拉取镜像
|
||||
//timeout 分钟为单位
|
||||
func ImagePull(dockerCli *client.Client, image string, opts types.ImagePullOptions, logger event.Logger, timeout int) (*types.ImageInspect, error) {
|
||||
if logger != nil {
|
||||
//进度信息
|
||||
logger.Info(fmt.Sprintf("开始获取镜像:%s", image), map[string]string{"step": "pullimage"})
|
||||
}
|
||||
_, err := reference.ParseAnyReference(image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//最少一分钟
|
||||
if timeout < 1 {
|
||||
timeout = 1
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*time.Duration(timeout))
|
||||
defer cancel()
|
||||
readcloser, err := dockerCli.ImagePull(ctx, image, opts)
|
||||
if err != nil {
|
||||
if strings.HasSuffix(err.Error(), "does not exist or no pull access") {
|
||||
return nil, fmt.Errorf("Image(%s) does not exist or no pull access", image)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer readcloser.Close()
|
||||
r := bufio.NewReader(readcloser)
|
||||
for {
|
||||
if line, _, err := r.ReadLine(); err == nil {
|
||||
if logger != nil {
|
||||
//进度信息
|
||||
logger.Debug(string(line), map[string]string{"step": "progress"})
|
||||
}
|
||||
fmt.Println(string(line))
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
ins, _, err := dockerCli.ImageInspectWithRaw(ctx, image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ins, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user