mirror of
https://gitee.com/rainbond/Rainbond.git
synced 2024-12-02 03:37:46 +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}
|
@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
|
||||||
|
@ -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",
|
||||||
|
@ -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"))
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
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
|
||||||
|
@ -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":
|
||||||
|
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
|
//CloudShareStruct CloudShareStruct
|
||||||
//swagger:parameters sharecloud
|
//swagger:parameters sharecloud
|
||||||
type CloudShareStruct struct {
|
type CloudShareStruct struct {
|
||||||
|
@ -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)
|
||||||
|
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