Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 初始化时加载代码,支持不同类型的代码获取方式 #1602

Merged
Merged
72 changes: 72 additions & 0 deletions cnb-builder-shim/cmd/dev-entrypoint/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@ package main

import (
"fmt"
"net/url"
"os"
"path"
"path/filepath"
"strings"

"github.com/go-logr/logr"
"github.com/pelletier/go-toml/v2"
"github.com/pkg/errors"

"github.com/TencentBlueking/bkpaas/cnb-builder-shim/internal/devsandbox/config"
"github.com/TencentBlueking/bkpaas/cnb-builder-shim/pkg/fetcher/fs"
"github.com/TencentBlueking/bkpaas/cnb-builder-shim/pkg/utils"
)

Expand Down Expand Up @@ -134,3 +138,71 @@ func setupBuildpacksOrder(logger logr.Logger, buildpacks string, cnbDir string)
}
return nil
}

// preFetchSourceCode: 根据配置初始化源码
func preFetchSourceCode() error {
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
logger.Info(fmt.Sprintf("Downloading source code to %s...", config.G.SourceCode.SourceFetchMethod))
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
// TODO: 源码初始化不同的方式抽象成一个接口
workspace := config.G.SourceCode.Workspace
fetchSourceCode, err := ensureWorkspace(workspace)
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
}
// 源码已经下载到工作目录,无需初始化
if !fetchSourceCode {
return nil
}
switch config.G.SourceCode.SourceFetchMethod {
case config.BKREPO:
downloadUrl, err := url.Parse(config.G.SourceCode.SourceGetUrl)
if err != nil {
return err
}
// 创建临时文件夹存放源码压缩包
tmpDir, err := os.MkdirTemp("", "source-packages-*")
if err != nil {
return errors.Wrap(err, "create tmp dir")
}
defer os.RemoveAll(tmpDir)

// 下载源码压缩包
if err = fs.NewFetcher(logger).Fetch(downloadUrl.Path, tmpDir); err != nil {
return errors.Wrap(err, "download source code")
}
// 解压源码至工作目录
srcFilePath := path.Join(tmpDir, "tar")
if err = utils.ExtractTarGz(srcFilePath, workspace); err != nil {
return err
}
case config.GIT:
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
return fmt.Errorf("TODO: clone git from revision")
}
return nil
}

// ensureWorkspace 确保 workspace 文件夹存在,如果里面有文件则表示不需要预加载源码
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
func ensureWorkspace(workspace string) (bool, error) {
// 检查文件夹是否存在
if _, err := os.Stat(workspace); os.IsNotExist(err) {
// 文件夹不存在,创建文件夹
logger.Info("creat workspace directory")
if err := os.MkdirAll(workspace, 0750); err != nil {
return false, errors.Wrap(err, "create workspace directory")
}
return true, nil
}

// 文件夹存在,检查文件夹里面是否有文件
files, err := os.ReadDir(workspace)
if err != nil {
return false, errors.Wrap(err, "read workspace directory")
}

if len(files) > 0 {
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
// 文件夹不为空,返回 nil
return false, nil
}

// 文件夹为空
return true, nil
}
5 changes: 5 additions & 0 deletions cnb-builder-shim/cmd/dev-entrypoint/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ func main() {
os.Exit(1)
}

if err := preFetchSourceCode(); err != nil {
logger.Error(err, "PreFetch Source Code Failed")
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
os.Exit(1)
}

runDevContainerServer()
}

Expand Down
1 change: 1 addition & 0 deletions cnb-builder-shim/internal/devsandbox/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package config
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
29 changes: 29 additions & 0 deletions cnb-builder-shim/internal/devsandbox/config/loader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package config

import "github.com/TencentBlueking/bkpaas/cnb-builder-shim/pkg/utils"

var G *Config

func init() {
cfg, _ := loadConfigFromEnv()
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved

// 设置全局变量
G = cfg
}
func loadSourceConfigFromEnv() (SourceCodeConfig, error) {
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
sourceFetchMethod := utils.EnvOrDefault("SOURCE_FETCH_METHOD", HTTP)
sourceGetUrl := utils.EnvOrDefault("SOURCE_GET_URL", "")
gitRevision := utils.EnvOrDefault("GIT_REVISION", "")
workspace := utils.EnvOrDefault("WORKSPACE", "/cnb/devsandbox/src")

return SourceCodeConfig{SourceFetchMethod: sourceFetchMethod, SourceGetUrl: sourceGetUrl, GitRevision: gitRevision, Workspace: workspace}, nil
}

func loadConfigFromEnv() (*Config, error) {
sourceCodeConfig, err := loadSourceConfigFromEnv()
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, err
}

return &Config{SourceCode: sourceCodeConfig}, nil
}
27 changes: 27 additions & 0 deletions cnb-builder-shim/internal/devsandbox/config/type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package config
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved

// 源码获取方式
const (
HTTP = "HTTP"
BKREPO = "BKREPO"
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
GIT = "GIT"
)

// SourceCodeConfig 源码配置
type SourceCodeConfig struct {
// 源码获取方式
SourceFetchMethod string
// 源码地址
SourceGetUrl string
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
// Git 仓库版本
GitRevision string
// 工作目录
Workspace string
}

// Config 全局配置
type Config struct {
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
// 源码配置
SourceCode SourceCodeConfig
// ...
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
}
52 changes: 51 additions & 1 deletion cnb-builder-shim/internal/devsandbox/webserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ package webserver

import (
"fmt"
"github.com/TencentBlueking/bkpaas/cnb-builder-shim/internal/devsandbox/config"
"github.com/TencentBlueking/bkpaas/cnb-builder-shim/pkg/utils"
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
"net/http"
"os"
"path"
"path/filepath"
"strconv"
Expand Down Expand Up @@ -70,6 +73,7 @@ func New(lg *logr.Logger) (*WebServer, error) {

mgr := service.NewDeployManager()
r.POST("/deploys", DeployHandler(s, mgr))
r.POST("/deploys_without_file", DeployWithoutFileHandler(s, mgr))
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
r.GET("/deploys/:deployID/results", ResultHandler(mgr))

return s, nil
Expand Down Expand Up @@ -110,6 +114,20 @@ func tokenAuthMiddleware(token string) gin.HandlerFunc {
// DeployHandler handles the deployment of a file to the web server.
func DeployHandler(s *WebServer, svc service.DeployServiceHandler) gin.HandlerFunc {
return func(c *gin.Context) {
// 在其他模式下,不允许使用该接口
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
if config.G.SourceCode.SourceFetchMethod != config.HTTP {
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("unsupported source fetch method: %s", config.G.SourceCode.SourceFetchMethod)})
return
}
var tmpAppDir string
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
// 创建临时文件夹
tmpDir, err := os.MkdirTemp("", "source-*")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("create tmp dir err: %s", err.Error())})
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
return
}
defer os.RemoveAll(tmpDir)

file, err := c.FormFile("file")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("get form err: %s", err.Error())})
Expand All @@ -122,8 +140,40 @@ func DeployHandler(s *WebServer, svc service.DeployServiceHandler) gin.HandlerFu
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("upload file err: %s", err.Error())})
return
}
// 解压文件到临时目录
if err = utils.Unzip(srcFilePath, tmpDir); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("unzip file err: %s", err.Error())})
return
}
tmpAppDir = path.Join(tmpDir, strings.Split(fileName, ".")[0])
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved

status, err := svc.Deploy(tmpAppDir)
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("deploy error: %s", err.Error())})
return
}

select {
case s.ch <- dc.AppReloadEvent{ID: status.DeployID, Rebuild: status.StepOpts.Rebuild, Relaunch: status.StepOpts.Relaunch}:
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
c.JSON(http.StatusOK, gin.H{"deployID": status.DeployID})
default:
c.JSON(
http.StatusTooManyRequests,
gin.H{"message": "app is deploying, please wait for a while and try again."},
)
}
}
}

// DeployWithoutFileHandler handles the deployment without file.
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
func DeployWithoutFileHandler(s *WebServer, svc service.DeployServiceHandler) gin.HandlerFunc {
return func(c *gin.Context) {
if config.G.SourceCode.SourceFetchMethod == config.HTTP {
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("unsupported source fetch method: %s", config.G.SourceCode.SourceFetchMethod)})
return
}

status, err := svc.Deploy(srcFilePath)
status, err := svc.Deploy(config.G.SourceCode.Workspace)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("deploy error: %s", err.Error())})
return
Expand Down
30 changes: 8 additions & 22 deletions cnb-builder-shim/internal/devsandbox/webserver/service/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,16 @@ type DeployManager struct {
// It analyzes the source code and copies it to the application directory.
//
// Parameters:
// - srcFilePath: The path of the source file to be deployed.
// - tmpAppDir: The tmp path of the source file to be deployed.
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
//
// Returns:
// - *DeployResult: The deployment result.
// - error: Any error that occurred during the deployment process.
func (m *DeployManager) Deploy(srcFilePath string) (*DeployResult, error) {
func (m *DeployManager) Deploy(tmpAppDir string) (*DeployResult, error) {
deployID := m.newDeployID()

// 分析源码并拷贝到应用目录
if err := m.analyzeAndSyncToAppDir(srcFilePath, dc.DefaultAppDir); err != nil {
if err := m.analyzeAndSyncToAppDir(tmpAppDir, dc.DefaultAppDir); err != nil {
return nil, err
}

Expand Down Expand Up @@ -134,26 +134,12 @@ func (m DeployManager) newDeployID() string {
return strings.Replace(uuidString, "-", "", -1)
}

func (m *DeployManager) analyzeAndSyncToAppDir(srcFilePath, appDir string) error {
tmpDir, err := os.MkdirTemp("", "source-*")
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)

// 1. 解压文件到临时目录
if err = utils.Unzip(srcFilePath, tmpDir); err != nil {
return err
}

fileName := filepath.Base(srcFilePath)
tmpAppDir := path.Join(tmpDir, strings.Split(fileName, ".")[0])

// 2. 通过对比新旧文件的变化, 确定哪些步骤需要执行
func (m *DeployManager) analyzeAndSyncToAppDir(tmpAppDir, appDir string) error {
// 1. 通过对比新旧文件的变化, 确定哪些步骤需要执行
m.stepOpts = parseDeployStepOpts(appDir, tmpAppDir)

// 3. 将 tmpAppDir 中的文件拷贝到 appDir
if err = m.syncFiles(tmpAppDir, appDir); err != nil {
// 2. 将 tmpAppDir 中的文件拷贝到 appDir
if err := m.syncFiles(tmpAppDir, appDir); err != nil {
return err
}

Expand Down Expand Up @@ -233,7 +219,7 @@ var _ DeployServiceHandler = (*DeployManager)(nil)
type FakeDeployManger struct{}

// Deploy fake deploy for unit test
func (m *FakeDeployManger) Deploy(srcFilePath string) (*DeployResult, error) {
func (m *FakeDeployManger) Deploy(tmpAppDir string) (*DeployResult, error) {
return &DeployResult{
DeployID: uuid.NewString(),
Status: dc.ReloadProcessing,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ var _ = Describe("Test DeployManager", func() {
var m *DeployManager
var tmpAppDir string

testSrcFilePath := filepath.Join("testdata", "django-helloworld.zip")
testSrcFilePath := filepath.Join("testdata", "tmpDir", "django-helloworld")
oldAppDir := dc.DefaultAppDir

BeforeEach(func() {
Expand Down
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
web1: python manage.py runserver 0.0.0.0:8080
SheepSheepChen marked this conversation as resolved.
Show resolved Hide resolved
web: python manage.py runserver 0.0.0.0:8000
Loading