[ADD] add docker run parser and image handle

This commit is contained in:
goodrain 2018-01-10 13:35:05 +08:00
parent 2a4b99223e
commit 06f92a78c6
15 changed files with 879 additions and 15 deletions

View File

@ -88,7 +88,7 @@ push-hub-image:
@docker push ${BASE_NAME}/rbd-api:${VERSION} @docker push ${BASE_NAME}/rbd-api:${VERSION}
run-api:build-api 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 run-mq:build-mq
${BIN_PATH}/${BASE_NAME}-mq --log-level=debug ${BIN_PATH}/${BASE_NAME}-mq --log-level=debug
run-worker:build-worker run-worker:build-worker

View File

@ -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": { "/v2/show": {
"get": { "get": {
"description": "show api version", "description": "show api version",

View File

@ -20,8 +20,6 @@ package doc
import ( import (
"net/http" "net/http"
"os"
"path/filepath"
"strings" "strings"
"github.com/go-chi/chi" "github.com/go-chi/chi"
@ -32,9 +30,10 @@ import (
//Routes routes //Routes routes
func Routes() chi.Router { func Routes() chi.Router {
r := chi.NewRouter() r := chi.NewRouter()
workDir, _ := os.Getwd() //workDir, _ := os.Getwd()
//logrus.Debugf("workdir is %v", workDir) //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) logrus.Debugf("filesdir is %v", filesDir)
r.Get("/", func(w http.ResponseWriter, r *http.Request) { r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("/docs")) w.Write([]byte("/docs"))

View File

@ -49,6 +49,7 @@ func (v2 *V2) tenantRouter() chi.Router {
r := chi.NewRouter() r := chi.NewRouter()
r.Post("/", controller.GetManager().Tenant) r.Post("/", controller.GetManager().Tenant)
r.Mount("/{tenant_name}", v2.tenantNameRouter()) r.Mount("/{tenant_name}", v2.tenantNameRouter())
r.Post("/servicecheck", controller.Check)
return r return r
} }

View 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, &gt.Body, nil); !ok {
return
}
tenantID := r.Context().Value(middleware.ContextKey("tenant_id")).(string)
gt.Body.TenantID = tenantID
result, err := handler.GetServiceManager().ServiceCheck(&gt)
if err != nil {
err.Handle(r, w)
return
}
re := struct {
CheckUUID string `json:"check_uuid"`
}{
CheckUUID: result,
}
httputil.ReturnSuccess(r, w, re)
}

View File

@ -68,6 +68,7 @@ type ServiceHandler interface {
SetVersionEnv(sve *api_model.SetVersionEnv) *util.APIHandleError SetVersionEnv(sve *api_model.SetVersionEnv) *util.APIHandleError
UpdateVersionEnv(uve *api_model.SetVersionEnv) *util.APIHandleError UpdateVersionEnv(uve *api_model.SetVersionEnv) *util.APIHandleError
DeleteComplexEnvs(tenantID, serviceAlias, pluginID string) *util.APIHandleError DeleteComplexEnvs(tenantID, serviceAlias, pluginID string) *util.APIHandleError
ServiceCheck(*api_model.ServiceCheckStruct) (string, *util.APIHandleError)
} }
var defaultServieHandler ServiceHandler var defaultServieHandler ServiceHandler

View File

@ -528,7 +528,7 @@ func (s *ServiceAction) ServiceCreate(sc *api_model.ServiceStruct) error {
//共享文件存储 //共享文件存储
case dbmodel.ShareFileVolumeType.String(): case dbmodel.ShareFileVolumeType.String():
volumn.HostPath = fmt.Sprintf("%s/tenant/%s/service/%s%s", sharePath, sc.TenantID, volumn.ServiceID, volumn.VolumePath) 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(): case dbmodel.LocalVolumeType.String():
serviceType, err := db.GetManager().TenantServiceLabelDao().GetTenantServiceTypeLabel(volumn.ServiceID) serviceType, err := db.GetManager().TenantServiceLabelDao().GetTenantServiceTypeLabel(volumn.ServiceID)
if err != nil { if err != nil {
@ -1967,7 +1967,7 @@ func (s *ServiceAction) upComplexEnvs(uve *api_model.SetVersionEnv) *util.APIHan
func TransStatus(eStatus string) string { func TransStatus(eStatus string) string {
switch eStatus { switch eStatus {
case "starting": case "starting":
return "动中" return "<EFBFBD><EFBFBD><EFBFBD>动中"
case "abnormal": case "abnormal":
return "运行异常" return "运行异常"
case "upgrade": case "upgrade":

View 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
}

View File

@ -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 //CloudShareStruct CloudShareStruct
//swagger:parameters sharecloud //swagger:parameters sharecloud
type CloudShareStruct struct { type CloudShareStruct struct {

View File

@ -1,4 +1,3 @@
// RAINBOND, Application Management Platform // RAINBOND, Application Management Platform
// Copyright (C) 2014-2017 Goodrain Co., Ltd. // Copyright (C) 2014-2017 Goodrain Co., Ltd.
@ -23,11 +22,11 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/client"
"github.com/goodrain/rainbond/pkg/db/config" "github.com/goodrain/rainbond/pkg/db/config"
"github.com/goodrain/rainbond/pkg/event" "github.com/goodrain/rainbond/pkg/event"
"github.com/goodrain/rainbond/pkg/mq/api/grpc/pb" "github.com/goodrain/rainbond/pkg/mq/api/grpc/pb"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/client"
"github.com/tidwall/gjson" "github.com/tidwall/gjson"
) )
@ -70,6 +69,8 @@ func (e *exectorManager) AddTask(task *pb.TaskMessage) error {
e.imageManual(task.TaskBody) e.imageManual(task.TaskBody)
case "code_check": case "code_check":
e.codeCheck(task.TaskBody) e.codeCheck(task.TaskBody)
case "service_check":
e.serviceCheck(task.TaskBody)
case "app_build": case "app_build":
e.appBuild(task.TaskBody) e.appBuild(task.TaskBody)
case "plugin_image_build": case "plugin_image_build":
@ -147,8 +148,6 @@ func (e *exectorManager) appSlug(in []byte) {
//}() //}()
//updateBuildResult(eventID,"failure",dest) //updateBuildResult(eventID,"failure",dest)
eventID := gjson.GetBytes(in, "event_id").String() eventID := gjson.GetBytes(in, "event_id").String()
logger := event.GetManager().GetLogger(eventID) logger := event.GetManager().GetLogger(eventID)
logger.Info("应用代码包构建任务开始执行", map[string]string{"step": "builder-exector", "status": "starting"}) logger.Info("应用代码包构建任务开始执行", map[string]string{"step": "builder-exector", "status": "starting"})
@ -252,6 +251,7 @@ func (e *exectorManager) appBuild(in []byte) {
}() }()
//updateBuildResult(eventID,finalStatus,dest) //updateBuildResult(eventID,finalStatus,dest)
} }
//func updateBuildResult(eventID,finalStatus,dest string) { //func updateBuildResult(eventID,finalStatus,dest string) {
// if dest == ""||!strings.Contains(dest,"y") { // if dest == ""||!strings.Contains(dest,"y") {
// v,err:=db.GetManager().VersionInfoDao().GetVersionByEventID(eventID) // v,err:=db.GetManager().VersionInfoDao().GetVersionByEventID(eventID)

View 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":
}
}

View 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
}

View 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())
}

View 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
}

View 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
}