2018-03-14 14:12:26 +08:00
// Copyright (C) 2014-2018 Goodrain Co., Ltd.
2018-01-29 11:25:49 +08:00
// RAINBOND, Application Management Platform
2018-03-14 14:33:31 +08:00
2018-01-29 11:25:49 +08:00
// 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.
2018-03-14 14:33:31 +08:00
2018-01-29 11:25:49 +08:00
// 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.
2018-03-14 14:33:31 +08:00
2018-01-29 11:25:49 +08:00
// 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 (
2019-12-02 15:56:46 +08:00
"context"
2018-01-29 11:25:49 +08:00
"fmt"
"os"
2018-02-28 16:12:47 +08:00
"path"
2018-10-18 12:19:06 +08:00
"strings"
2018-02-28 16:12:47 +08:00
"time"
2020-03-16 12:55:51 +08:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2018-12-07 14:24:14 +08:00
"github.com/docker/docker/client"
2019-08-16 06:53:16 +08:00
"github.com/goodrain/rainbond/builder"
2018-07-11 11:49:55 +08:00
"github.com/goodrain/rainbond/builder/build"
2019-08-16 06:53:16 +08:00
"github.com/goodrain/rainbond/builder/parser"
"github.com/goodrain/rainbond/builder/parser/code"
2018-04-24 16:44:59 +08:00
"github.com/goodrain/rainbond/builder/sources"
"github.com/goodrain/rainbond/db"
dbmodel "github.com/goodrain/rainbond/db/model"
2019-08-16 06:53:16 +08:00
"github.com/goodrain/rainbond/event"
"github.com/goodrain/rainbond/util"
"github.com/pquerna/ffjson/ffjson"
2020-11-25 16:39:38 +08:00
"github.com/sirupsen/logrus"
2019-08-16 06:53:16 +08:00
"github.com/tidwall/gjson" //"github.com/docker/docker/api/types"
2020-01-06 20:26:56 +08:00
"k8s.io/client-go/kubernetes"
2019-08-16 06:53:16 +08:00
//"github.com/docker/docker/client"
2018-01-29 11:25:49 +08:00
)
2018-01-30 15:10:12 +08:00
//SourceCodeBuildItem SouceCodeBuildItem
type SourceCodeBuildItem struct {
2020-03-04 17:24:54 +08:00
Namespace string ` json:"namespace" `
TenantName string ` json:"tenant_name" `
GRDataPVCName string ` json:"gr_data_pvc_name" `
CachePVCName string ` json:"cache_pvc_name" `
2020-06-11 09:36:50 +08:00
CacheMode string ` json:"cache_mode" `
CachePath string ` json:"cache_path" `
2020-03-04 17:24:54 +08:00
ServiceAlias string ` json:"service_alias" `
Action string ` json:"action" `
DestImage string ` json:"dest_image" `
Logger event . Logger ` json:"logger" `
EventID string ` json:"event_id" `
CacheDir string ` json:"cache_dir" `
2018-03-02 10:11:57 +08:00
//SourceDir string `json:"source_dir"`
TGZDir string ` json:"tgz_dir" `
2018-02-28 16:12:47 +08:00
DockerClient * client . Client
2020-01-06 20:26:56 +08:00
KubeClient kubernetes . Interface
RbdNamespace string
RbdRepoName string
2018-02-28 16:12:47 +08:00
TenantID string
ServiceID string
DeployVersion string
Lang string
Runtime string
BuildEnvs map [ string ] string
CodeSouceInfo sources . CodeSourceInfo
2018-03-02 10:11:57 +08:00
RepoInfo * sources . RepostoryBuildInfo
2018-07-23 17:39:01 +08:00
commit Commit
2019-03-08 19:22:22 +08:00
Configs map [ string ] gjson . Result ` json:"configs" `
2020-02-02 21:29:18 +08:00
Ctx context . Context
2018-07-23 17:39:01 +08:00
}
//Commit code Commit
type Commit struct {
Hash string
Author string
Message string
2018-01-29 11:25:49 +08:00
}
2018-04-13 11:14:32 +08:00
//NewSouceCodeBuildItem create
2018-01-30 15:10:12 +08:00
func NewSouceCodeBuildItem ( in [ ] byte ) * SourceCodeBuildItem {
2018-01-29 11:25:49 +08:00
eventID := gjson . GetBytes ( in , "event_id" ) . String ( )
logger := event . GetManager ( ) . GetLogger ( eventID )
csi := sources . CodeSourceInfo {
2018-10-18 12:19:06 +08:00
ServerType : strings . Replace ( gjson . GetBytes ( in , "server_type" ) . String ( ) , " " , "" , - 1 ) ,
2018-02-02 15:10:49 +08:00
RepositoryURL : gjson . GetBytes ( in , "repo_url" ) . String ( ) ,
2018-02-28 16:12:47 +08:00
Branch : gjson . GetBytes ( in , "branch" ) . String ( ) ,
2018-04-13 11:14:32 +08:00
User : gjson . GetBytes ( in , "user" ) . String ( ) ,
Password : gjson . GetBytes ( in , "password" ) . String ( ) ,
TenantID : gjson . GetBytes ( in , "tenant_id" ) . String ( ) ,
2018-07-05 15:33:58 +08:00
ServiceID : gjson . GetBytes ( in , "service_id" ) . String ( ) ,
2018-01-29 11:25:49 +08:00
}
2018-01-30 15:10:12 +08:00
envs := gjson . GetBytes ( in , "envs" ) . String ( )
be := make ( map [ string ] string )
if err := ffjson . Unmarshal ( [ ] byte ( envs ) , & be ) ; err != nil {
logrus . Errorf ( "unmarshal build envs error: %s" , err . Error ( ) )
}
2018-02-28 16:12:47 +08:00
scb := & SourceCodeBuildItem {
Namespace : gjson . GetBytes ( in , "tenant_id" ) . String ( ) ,
TenantName : gjson . GetBytes ( in , "tenant_name" ) . String ( ) ,
ServiceAlias : gjson . GetBytes ( in , "service_alias" ) . String ( ) ,
TenantID : gjson . GetBytes ( in , "tenant_id" ) . String ( ) ,
ServiceID : gjson . GetBytes ( in , "service_id" ) . String ( ) ,
Action : gjson . GetBytes ( in , "action" ) . String ( ) ,
2018-01-29 11:25:49 +08:00
DeployVersion : gjson . GetBytes ( in , "deploy_version" ) . String ( ) ,
2018-02-28 16:12:47 +08:00
Logger : logger ,
EventID : eventID ,
2018-01-29 11:25:49 +08:00
CodeSouceInfo : csi ,
2018-02-28 16:12:47 +08:00
Lang : gjson . GetBytes ( in , "lang" ) . String ( ) ,
Runtime : gjson . GetBytes ( in , "runtime" ) . String ( ) ,
2019-03-08 19:22:22 +08:00
Configs : gjson . GetBytes ( in , "configs" ) . Map ( ) ,
2018-02-28 16:12:47 +08:00
BuildEnvs : be ,
2018-01-29 11:25:49 +08:00
}
2018-02-28 16:12:47 +08:00
scb . CacheDir = fmt . Sprintf ( "/cache/build/%s/cache/%s" , scb . TenantID , scb . ServiceID )
2018-03-02 10:11:57 +08:00
//scb.SourceDir = scb.CodeSouceInfo.GetCodeSourceDir()
2018-02-28 16:29:37 +08:00
scb . TGZDir = fmt . Sprintf ( "/grdata/build/tenant/%s/slug/%s" , scb . TenantID , scb . ServiceID )
2018-02-28 16:12:47 +08:00
return scb
2018-01-29 11:25:49 +08:00
}
//Run Run
2018-01-30 15:10:12 +08:00
func ( i * SourceCodeBuildItem ) Run ( timeout time . Duration ) error {
2018-01-29 11:25:49 +08:00
// 1.clone
// 2.check dockerfile/ source_code
// 3.build
// 4.upload image /upload slug
2018-07-23 17:39:01 +08:00
rbi , err := sources . CreateRepostoryBuildInfo ( i . CodeSouceInfo . RepositoryURL , i . CodeSouceInfo . ServerType , i . CodeSouceInfo . Branch , i . TenantID , i . ServiceID )
2018-03-02 10:11:57 +08:00
if err != nil {
i . Logger . Error ( "Git项目仓库地址格式错误" , map [ string ] string { "step" : "parse" } )
return err
}
i . RepoInfo = rbi
2018-02-28 16:29:37 +08:00
if err := i . prepare ( ) ; err != nil {
logrus . Errorf ( "prepare build code error: %s" , err . Error ( ) )
i . Logger . Error ( fmt . Sprintf ( "准备源码构建失败" ) , map [ string ] string { "step" : "builder-exector" , "status" : "failure" } )
return err
}
2018-03-02 10:11:57 +08:00
i . CodeSouceInfo . RepositoryURL = rbi . RepostoryURL
2018-07-23 17:39:01 +08:00
switch i . CodeSouceInfo . ServerType {
case "svn" :
csi := i . CodeSouceInfo
2018-10-26 14:22:59 +08:00
svnclient := sources . NewClient ( csi , rbi . GetCodeHome ( ) , i . Logger )
2018-10-25 11:41:02 +08:00
rs , err := svnclient . UpdateOrCheckout ( rbi . BuildPath )
2018-07-23 17:39:01 +08:00
if err != nil {
logrus . Errorf ( "checkout svn code error: %s" , err . Error ( ) )
2018-10-25 11:41:02 +08:00
i . Logger . Error ( fmt . Sprintf ( "Checkout svn code failed, please make sure the code can be downloaded properly" ) , map [ string ] string { "step" : "builder-exector" , "status" : "failure" } )
2018-07-23 17:39:01 +08:00
return err
}
2020-06-03 16:32:30 +08:00
if rs . Logs == nil || len ( rs . Logs . CommitEntrys ) < 1 {
2018-07-23 17:39:01 +08:00
logrus . Errorf ( "get code commit info error: %s" , err . Error ( ) )
i . Logger . Error ( fmt . Sprintf ( "读取代码版本信息失败" ) , map [ string ] string { "step" : "builder-exector" , "status" : "failure" } )
return err
}
i . commit = Commit {
Hash : rs . Logs . CommitEntrys [ 0 ] . Revision ,
Message : rs . Logs . CommitEntrys [ 0 ] . Msg ,
Author : rs . Logs . CommitEntrys [ 0 ] . Author ,
}
default :
//default git
rs , err := sources . GitCloneOrPull ( i . CodeSouceInfo , rbi . GetCodeHome ( ) , i . Logger , 5 )
if err != nil {
logrus . Errorf ( "pull git code error: %s" , err . Error ( ) )
i . Logger . Error ( fmt . Sprintf ( "拉取代码失败,请确保代码可以被正常下载" ) , map [ string ] string { "step" : "builder-exector" , "status" : "failure" } )
return err
}
//get last commit
commit , err := sources . GetLastCommit ( rs )
if err != nil || commit == nil {
logrus . Errorf ( "get code commit info error: %s" , err . Error ( ) )
i . Logger . Error ( fmt . Sprintf ( "读取代码版本信息失败" ) , map [ string ] string { "step" : "builder-exector" , "status" : "failure" } )
return err
}
i . commit = Commit {
Hash : commit . Hash . String ( ) ,
Author : commit . Author . Name ,
Message : commit . Message ,
}
2018-03-01 11:56:09 +08:00
}
2020-03-16 12:55:51 +08:00
// clean cache code
2019-10-30 10:23:28 +08:00
defer func ( ) {
if err := os . RemoveAll ( rbi . GetCodeHome ( ) ) ; err != nil {
logrus . Warningf ( "remove source code: %v" , err )
}
} ( )
2018-07-27 12:53:38 +08:00
hash := i . commit . Hash
if len ( hash ) >= 8 {
hash = i . commit . Hash [ 0 : 7 ]
}
2018-08-27 18:44:29 +08:00
info := fmt . Sprintf ( "CodeVersion:%s Author:%s Commit:%s " , hash , i . commit . Author , i . commit . Message )
2018-03-01 11:56:09 +08:00
i . Logger . Info ( info , map [ string ] string { "step" : "code-version" } )
2018-07-24 23:20:27 +08:00
if _ , ok := i . BuildEnvs [ "REPARSE" ] ; ok {
2018-07-23 17:39:01 +08:00
_ , lang , err := parser . ReadRbdConfigAndLang ( rbi )
if err != nil {
logrus . Errorf ( "reparse code lange error %s" , err . Error ( ) )
i . Logger . Error ( fmt . Sprintf ( "重新解析代码语言错误" ) , map [ string ] string { "step" : "builder-exector" , "status" : "failure" } )
return err
}
i . Lang = string ( lang )
}
2018-09-02 22:44:38 +08:00
2020-03-16 12:55:51 +08:00
i . Logger . Info ( "pull or clone code successfully, start code build" , map [ string ] string { "step" : "codee-version" } )
2018-09-02 22:44:38 +08:00
res , err := i . codeBuild ( )
if err != nil {
2019-12-02 15:56:46 +08:00
if err . Error ( ) == context . DeadlineExceeded . Error ( ) {
2020-08-26 19:15:41 +08:00
i . Logger . Error ( "Build app version from source code timeout, the maximum time is 60 minutes" , map [ string ] string { "step" : "builder-exector" , "status" : "failure" } )
2019-12-02 15:56:46 +08:00
} else {
i . Logger . Error ( "Build app version from source code failure," + err . Error ( ) , map [ string ] string { "step" : "builder-exector" , "status" : "failure" } )
}
2018-09-02 22:44:38 +08:00
return err
}
if err := i . UpdateBuildVersionInfo ( res ) ; err != nil {
return err
2018-01-29 11:25:49 +08:00
}
return nil
}
2018-12-04 13:43:15 +08:00
2018-07-11 11:49:55 +08:00
func ( i * SourceCodeBuildItem ) codeBuild ( ) ( * build . Response , error ) {
codeBuild , err := build . GetBuild ( code . Lang ( i . Lang ) )
if err != nil {
2018-08-24 11:51:15 +08:00
logrus . Errorf ( "get code build error: %s lang %s" , err . Error ( ) , i . Lang )
i . Logger . Error ( util . Translation ( "No way of compiling to support this source type was found" ) , map [ string ] string { "step" : "builder-exector" , "status" : "failure" } )
2018-07-11 11:49:55 +08:00
return nil , err
}
2020-01-16 21:13:45 +08:00
hostAlias , err := i . getHostAlias ( )
2020-01-06 20:26:56 +08:00
if err != nil {
i . Logger . Error ( util . Translation ( "get rbd-repo ip failure" ) , map [ string ] string { "step" : "builder-exector" , "status" : "failure" } )
return nil , err
}
2018-07-11 11:49:55 +08:00
buildReq := & build . Request {
2020-05-15 20:31:34 +08:00
RbdNamespace : i . RbdNamespace ,
2020-02-24 16:04:10 +08:00
SourceDir : i . RepoInfo . GetCodeBuildAbsPath ( ) ,
2018-07-11 11:49:55 +08:00
CacheDir : i . CacheDir ,
2018-08-27 17:32:56 +08:00
TGZDir : i . TGZDir ,
2018-07-11 11:49:55 +08:00
RepositoryURL : i . RepoInfo . RepostoryURL ,
ServiceAlias : i . ServiceAlias ,
2018-08-23 14:06:21 +08:00
ServiceID : i . ServiceID ,
TenantID : i . TenantID ,
ServerType : i . CodeSouceInfo . ServerType ,
Runtime : i . Runtime ,
2018-08-23 14:36:29 +08:00
Branch : i . CodeSouceInfo . Branch ,
2018-07-11 11:49:55 +08:00
DeployVersion : i . DeployVersion ,
2018-07-23 17:39:01 +08:00
Commit : build . Commit { User : i . commit . Author , Message : i . commit . Message , Hash : i . commit . Hash } ,
2018-07-11 11:49:55 +08:00
Lang : code . Lang ( i . Lang ) ,
BuildEnvs : i . BuildEnvs ,
Logger : i . Logger ,
DockerClient : i . DockerClient ,
2020-01-16 21:13:45 +08:00
KubeClient : i . KubeClient ,
HostAlias : hostAlias ,
2020-02-02 21:29:18 +08:00
Ctx : i . Ctx ,
2020-03-04 17:24:54 +08:00
GRDataPVCName : i . GRDataPVCName ,
CachePVCName : i . CachePVCName ,
2020-06-11 09:36:50 +08:00
CacheMode : i . CacheMode ,
CachePath : i . CachePath ,
2018-07-11 11:49:55 +08:00
}
res , err := codeBuild . Build ( buildReq )
return res , err
}
2018-01-29 11:25:49 +08:00
2020-01-06 20:26:56 +08:00
func ( i * SourceCodeBuildItem ) getExtraHosts ( ) ( extraHosts [ ] string , err error ) {
2021-04-01 19:08:57 +08:00
endpoints , err := i . KubeClient . CoreV1 ( ) . Endpoints ( i . RbdNamespace ) . Get ( context . Background ( ) , i . RbdRepoName , metav1 . GetOptions { } )
2020-01-06 20:26:56 +08:00
if err != nil {
2020-01-16 21:13:45 +08:00
logrus . Errorf ( "do not found ep by name: %s in namespace: %s" , i . RbdRepoName , i . Namespace )
2020-01-06 20:26:56 +08:00
return nil , err
}
for _ , subset := range endpoints . Subsets {
for _ , addr := range subset . Addresses {
extraHosts = append ( extraHosts , fmt . Sprintf ( "maven.goodrain.me:%s" , addr . IP ) )
extraHosts = append ( extraHosts , fmt . Sprintf ( "lang.goodrain.me:%s" , addr . IP ) )
}
}
return
}
2020-01-16 21:13:45 +08:00
func ( i * SourceCodeBuildItem ) getHostAlias ( ) ( hostAliasList [ ] build . HostAlias , err error ) {
2021-04-01 19:08:57 +08:00
endpoints , err := i . KubeClient . CoreV1 ( ) . Endpoints ( i . RbdNamespace ) . Get ( context . Background ( ) , i . RbdRepoName , metav1 . GetOptions { } )
2020-01-16 21:13:45 +08:00
if err != nil {
logrus . Errorf ( "do not found ep by name: %s in namespace: %s" , i . RbdRepoName , i . Namespace )
return nil , err
}
hostNames := [ ] string { "maven.goodrain.me" , "lang.goodrain.me" }
for _ , subset := range endpoints . Subsets {
for _ , addr := range subset . Addresses {
hostAliasList = append ( hostAliasList , build . HostAlias { IP : addr . IP , Hostnames : hostNames } )
}
}
return
}
2018-01-29 11:25:49 +08:00
//IsDockerfile CheckDockerfile
2018-01-30 15:10:12 +08:00
func ( i * SourceCodeBuildItem ) IsDockerfile ( ) bool {
2018-03-02 10:11:57 +08:00
filepath := path . Join ( i . RepoInfo . GetCodeBuildAbsPath ( ) , "Dockerfile" )
2018-01-29 11:25:49 +08:00
_ , err := os . Stat ( filepath )
if err != nil {
return false
}
return true
}
2018-02-28 16:29:37 +08:00
func ( i * SourceCodeBuildItem ) prepare ( ) error {
if err := util . CheckAndCreateDir ( i . CacheDir ) ; err != nil {
return err
}
if err := util . CheckAndCreateDir ( i . TGZDir ) ; err != nil {
return err
}
2018-03-02 10:11:57 +08:00
if err := util . CheckAndCreateDir ( i . RepoInfo . GetCodeHome ( ) ) ; err != nil {
2018-03-01 11:30:54 +08:00
return err
}
2019-03-20 18:52:32 +08:00
if _ , ok := i . BuildEnvs [ "NO_CACHE" ] ; ok {
2018-03-27 12:48:42 +08:00
if ! util . DirIsEmpty ( i . RepoInfo . GetCodeHome ( ) ) {
os . RemoveAll ( i . RepoInfo . GetCodeHome ( ) )
}
2018-03-16 10:42:57 +08:00
if err := os . RemoveAll ( i . CacheDir ) ; err != nil {
logrus . Error ( "remove cache dir error" , err . Error ( ) )
}
2018-03-16 11:10:06 +08:00
if err := os . MkdirAll ( i . CacheDir , 0755 ) ; err != nil {
2018-03-16 10:42:57 +08:00
logrus . Error ( "make cache dir error" , err . Error ( ) )
}
}
2018-03-01 10:06:01 +08:00
os . Chown ( i . CacheDir , 200 , 200 )
os . Chown ( i . TGZDir , 200 , 200 )
2018-02-28 16:29:37 +08:00
return nil
}
2018-03-01 11:30:54 +08:00
2018-09-02 22:44:38 +08:00
//UpdateVersionInfo Update build application service version info
2018-01-30 15:10:12 +08:00
func ( i * SourceCodeBuildItem ) UpdateVersionInfo ( vi * dbmodel . VersionInfo ) error {
2018-03-07 15:11:20 +08:00
version , err := db . GetManager ( ) . VersionInfoDao ( ) . GetVersionByDeployVersion ( i . DeployVersion , i . ServiceID )
2018-01-30 15:10:12 +08:00
if err != nil {
return err
}
if vi . DeliveredType != "" {
version . DeliveredType = vi . DeliveredType
}
if vi . DeliveredPath != "" {
version . DeliveredPath = vi . DeliveredPath
2018-05-23 12:17:34 +08:00
if vi . DeliveredType == "image" {
version . ImageName = vi . DeliveredPath
}
2018-01-30 15:10:12 +08:00
}
if vi . FinalStatus != "" {
version . FinalStatus = vi . FinalStatus
}
2018-03-07 12:59:25 +08:00
version . CommitMsg = vi . CommitMsg
version . Author = vi . Author
version . CodeVersion = vi . CodeVersion
2019-08-27 11:29:23 +08:00
version . CodeBranch = vi . CodeBranch
2021-03-15 21:24:48 +08:00
version . FinishTime = time . Now ( )
2018-01-30 15:10:12 +08:00
if err := db . GetManager ( ) . VersionInfoDao ( ) . UpdateModel ( version ) ; err != nil {
return err
}
return nil
2018-02-28 16:12:47 +08:00
}
2018-01-30 15:10:12 +08:00
2018-07-11 11:49:55 +08:00
//UpdateBuildVersionInfo update service build version info to db
func ( i * SourceCodeBuildItem ) UpdateBuildVersionInfo ( res * build . Response ) error {
vi := & dbmodel . VersionInfo {
DeliveredType : string ( res . MediumType ) ,
DeliveredPath : res . MediumPath ,
EventID : i . EventID ,
2018-11-16 18:16:55 +08:00
ImageName : builder . RUNNERIMAGENAME ,
2018-07-11 11:49:55 +08:00
FinalStatus : "success" ,
2019-08-27 11:29:23 +08:00
CodeBranch : i . CodeSouceInfo . Branch ,
2018-07-23 17:39:01 +08:00
CodeVersion : i . commit . Hash ,
2018-07-11 11:49:55 +08:00
CommitMsg : i . commit . Message ,
2018-07-23 17:39:01 +08:00
Author : i . commit . Author ,
2019-08-27 11:29:23 +08:00
FinishTime : time . Now ( ) ,
2018-07-11 11:49:55 +08:00
}
if err := i . UpdateVersionInfo ( vi ) ; err != nil {
logrus . Errorf ( "update version info error: %s" , err . Error ( ) )
2018-09-02 22:44:38 +08:00
i . Logger . Error ( "Update application service version information failed" , map [ string ] string { "step" : "build-code" , "status" : "failure" } )
2018-07-11 11:49:55 +08:00
return err
}
return nil
}
2018-01-30 15:10:12 +08:00
//UpdateCheckResult UpdateCheckResult
2018-02-28 16:12:47 +08:00
func ( i * SourceCodeBuildItem ) UpdateCheckResult ( result * dbmodel . CodeCheckResult ) error {
2018-01-30 15:10:12 +08:00
return nil
2018-02-28 16:12:47 +08:00
}