Skip to content

Commit

Permalink
配置模板压缩包导入以及批量新增配置模板
Browse files Browse the repository at this point in the history
  • Loading branch information
Ambition9186 committed Oct 26, 2023
1 parent 6571bcf commit 8b0e922
Show file tree
Hide file tree
Showing 21 changed files with 7,781 additions and 5,345 deletions.
206 changes: 206 additions & 0 deletions bcs-services/bcs-bscp/cmd/api-server/service/config_import.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/*
* Tencent is pleased to support the open source community by making Blueking Container Service available.
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/

package service

import (
"bytes"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"io"
"io/fs"
"net/http"
"os"
"path/filepath"
"strings"
"unicode/utf8"

"github.com/go-chi/render"

"bscp.io/pkg/cc"
"bscp.io/pkg/criteria/constant"
"bscp.io/pkg/dal/repository"
"bscp.io/pkg/iam/auth"
"bscp.io/pkg/kit"
pbcs "bscp.io/pkg/protocol/config-server"
"bscp.io/pkg/rest"
"bscp.io/pkg/runtime/archive"
"bscp.io/pkg/types"
)

type configImport struct {
authorizer auth.Authorizer
provider repository.Provider
cfgClient pbcs.ConfigClient
}

// FileImport Import config file
func (c *configImport) FileImport(w http.ResponseWriter, r *http.Request) {
kt := kit.MustGetKit(r.Context())

// Validation size

if r.ContentLength > constant.ContentLength {
_ = render.Render(w, r, rest.BadRequest(errors.New("request body size exceeds 100MB")))
return
}

// Unzip file
unpackTempDir, err := archive.Unpack(r.Body)
if err != nil {
_ = render.Render(w, r, rest.BadRequest(err))
return
}
// 删除临时文件夹
defer func() { _ = os.RemoveAll(unpackTempDir) }()

folder, err := c.scanFolder(kt, unpackTempDir, unpackTempDir)
if err != nil {
_ = render.Render(w, r, rest.BadRequest(err))
return
}
exist := make([]*types.TemplateItem, 0)

nonExist := make([]*types.TemplateItem, 0)
for _, item := range folder {
// 验证模板配置文件是否存在
template, _ := c.cfgClient.GetTemplateDetailByUniqueKey(kt.RpcCtx(), &pbcs.GetTemplateDetailByUniqueKeyReq{
TemplateSpaceId: kt.TmplSpaceID,
BizId: kt.BizID,
Name: item.Name,
Path: item.Path,
})
if template.GetTemplate().GetId() != 0 {
exist = append(exist, &types.TemplateItem{
Id: template.GetTemplate().GetId(),
Name: item.Name,
Path: item.Path,
FileType: item.FileType,
FileMode: template.GetTemplateRevision().GetFileMode(),
Memo: template.GetTemplate().GetSpec().GetMemo(),
Privilege: template.GetTemplateRevision().GetPermission().GetPrivilege(),
User: template.GetTemplateRevision().GetPermission().GetUser(),
UserGroup: template.GetTemplateRevision().GetPermission().GetUserGroup(),
Sign: item.Sign,
ByteSize: item.ByteSize,
})
} else {
nonExist = append(nonExist, &types.TemplateItem{
Name: item.Name,
Path: item.Path,
FileType: item.FileType,
FileMode: constant.FileMode,
Sign: item.Sign,
ByteSize: item.ByteSize,
})
}
}
_ = render.Render(w, r, rest.OKRender(&types.TemplatesImportResp{
Exist: exist,
NonExist: nonExist,
}))
}

// 扫描文件夹
// fileDir 文件目录
// rootDir 根目录
func (c *configImport) scanFolder(kt *kit.Kit, fileDir, rootDir string) ([]*types.FileInfo, error) {
fileData := make([]*types.FileInfo, 0)
err := filepath.WalkDir(fileDir, func(path string, d fs.DirEntry, err error) error {
if !d.IsDir() {
info, err := c.uploadFile(kt, path, rootDir)
if err != nil {
return err
}
fileData = append(fileData, info)
}
return nil
})
if err != nil {
return nil, err
}
return fileData, nil
}

// 上传文件
func (c *configImport) uploadFile(kt *kit.Kit, filePath, rootDir string) (*types.FileInfo, error) {
var (
content bytes.Buffer
fileType = "text"
fileDir = ""
)
fileContent, err := os.Open(filePath)
if err != nil {
return nil, fmt.Errorf("open file fail %s: %v\n", filePath, err)
}

defer func(fileContent *os.File) {
_ = fileContent.Close()
}(fileContent)

_, err = io.Copy(&content, fileContent)
if err != nil {
return nil, err
}

// 计算文件的SHA-256散列值
hash := sha256.New()
hash.Write(content.Bytes())
hashInBytes := hash.Sum(nil)
hashString := hex.EncodeToString(hashInBytes)
// 处理路径、名称、文件类型
fileInfo, err := fileContent.Stat()
if err != nil {
return nil, err
}
// Check if the file size is greater than 5MB (5 * 1024 * 1024 bytes)
if fileInfo.Size() > constant.MaxFileSize {
fileType = "binary"
} else {
if !utf8.Valid(content.Bytes()) {
fileType = "binary"
}
}
fileDir = strings.Replace(filePath, rootDir, "", 1)
fileDir = strings.Replace(fileDir, "\\", "/", -1)
lastSlashIndex := strings.LastIndex(fileDir, "/")
if lastSlashIndex >= 0 {
fileDir = fileDir[:lastSlashIndex]
}

upload, err := c.provider.Upload(kt, hashString, &content)
if err != nil {
return nil, fmt.Errorf("file upload fail %s: %v\n", filePath, err)
}
return &types.FileInfo{
Name: fileInfo.Name(),
Path: fileDir,
FileType: fileType,
Sign: upload.Sha256,
ByteSize: uint64(upload.ByteSize),
}, nil
}

func newConfigImportService(settings cc.Repository, authorizer auth.Authorizer, cfgClient pbcs.ConfigClient) (*configImport, error) {
provider, err := repository.NewProvider(settings)
if err != nil {
return nil, err
}
config := &configImport{
authorizer: authorizer,
provider: provider,
cfgClient: cfgClient,
}
return config, nil
}
31 changes: 19 additions & 12 deletions bcs-services/bcs-bscp/cmd/api-server/service/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,13 @@ import (

// proxy all server's mux proxy.
type proxy struct {
cfgSvrMux *runtime.ServeMux
authSvrMux http.Handler
repo *repoService
state serviced.State
authorizer auth.Authorizer
cfgClient pbcs.ConfigClient
cfgSvrMux *runtime.ServeMux
authSvrMux http.Handler
repo *repoService
state serviced.State
authorizer auth.Authorizer
cfgClient pbcs.ConfigClient
configImportService *configImport
}

// newProxy create new mux proxy.
Expand Down Expand Up @@ -75,13 +76,19 @@ func newProxy(dis serviced.Discover) (*proxy, error) {
return nil, err
}

configImportService, err := newConfigImportService(cc.ApiServer().Repo, authorizer, cfgClient)
if err != nil {
return nil, err
}

p := &proxy{
cfgSvrMux: cfgSvrMux,
repo: repo,
state: state,
authorizer: authorizer,
authSvrMux: authSvrMux,
cfgClient: cfgClient,
cfgSvrMux: cfgSvrMux,
repo: repo,
configImportService: configImportService,
state: state,
authorizer: authorizer,
authSvrMux: authSvrMux,
cfgClient: cfgClient,
}

p.initBizsOfTmplSpaces()
Expand Down
8 changes: 8 additions & 0 deletions bcs-services/bcs-bscp/cmd/api-server/service/routers.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,13 @@ func (p *proxy) routers() http.Handler {
r.Get("/metadata", p.repo.FileMetadata)
})

// 导入模板压缩包
r.Route("/api/v1/config/biz/{biz_id}/template_config_items", func(r chi.Router) {
r.Use(p.authorizer.UnifiedAuthentication)
r.Use(p.authorizer.BizVerified)
r.Use(p.authorizer.ContentVerified)
r.Post("/import", p.configImportService.FileImport)
})

return r
}
93 changes: 93 additions & 0 deletions bcs-services/bcs-bscp/cmd/config-server/service/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,3 +405,96 @@ func (s *Service) ListTmplsOfTmplSet(ctx context.Context, req *pbcs.ListTmplsOfT
}
return resp, nil
}

// GetTemplateDetailByUniqueKey get template detail by unique key
func (s *Service) GetTemplateDetailByUniqueKey(ctx context.Context, req *pbcs.GetTemplateDetailByUniqueKeyReq) (*pbcs.GetTemplateDetailByUniqueKeyResp, error) {
grpcKit := kit.FromGrpcContext(ctx)

res := []*meta.ResourceAttribute{
{Basic: meta.Basic{Type: meta.Biz, Action: meta.FindBusinessResource}, BizID: req.BizId},
}
if err := s.authorizer.Authorize(grpcKit, res...); err != nil {
return nil, err
}

r := &pbds.GetTemplateDetailByUniqueKeyReq{
BizId: req.BizId,
TemplateSpaceId: req.TemplateSpaceId,
Name: req.Name,
Path: req.Path,
}
templateDetail, err := s.client.DS.GetTemplateDetailByUniqueKey(grpcKit.RpcCtx(), r)
if err != nil {
return nil, err
}
resp := &pbcs.GetTemplateDetailByUniqueKeyResp{
TemplateRevision: templateDetail.TemplateRevision,
Template: templateDetail.Template,
}
return resp, nil
}

// BatchUpsertTemplates batch upsert templates.
func (s *Service) BatchUpsertTemplates(ctx context.Context, req *pbcs.BatchUpsertTemplatesReq) (*pbcs.BatchUpsertTemplatesResp, error) {
grpcKit := kit.FromGrpcContext(ctx)
res := []*meta.ResourceAttribute{
{Basic: meta.Basic{Type: meta.Biz, Action: meta.FindBusinessResource}, BizID: req.BizId},
}

if err := s.authorizer.Authorize(grpcKit, res...); err != nil {
return nil, err
}

items := make([]*pbds.BatchUpsertTemplatesReq_Item, 0, len(req.Items))
for _, item := range req.Items {
// validate if file content uploaded.
if err := s.validateContentExist(grpcKit, req.BizId, item.Sign); err != nil {
logs.Errorf("validate file content uploaded failed, err: %v, rid: %s", err, grpcKit.Rid)
return nil, err
}

items = append(items, &pbds.BatchUpsertTemplatesReq_Item{
Templates: &pbtemplate.Template{
Id: item.Id,
Spec: &pbtemplate.TemplateSpec{
Name: item.Name,
Path: item.Path,
Memo: item.Memo,
},
Attachment: &pbtemplate.TemplateAttachment{
BizId: req.BizId,
TemplateSpaceId: req.TemplateSpaceId,
},
},
TemplateRevisions: &pbtr.TemplateRevision{
Spec: &pbtr.TemplateRevisionSpec{
Name: item.Name,
Path: item.Path,
FileType: item.FileType,
FileMode: item.FileMode,
Permission: &pbci.FilePermission{
User: item.User,
UserGroup: item.UserGroup,
Privilege: item.Privilege,
},
ContentSpec: &pbcontent.ContentSpec{
Signature: item.Sign,
ByteSize: item.ByteSize,
},
},
Attachment: &pbtr.TemplateRevisionAttachment{
BizId: req.BizId,
TemplateSpaceId: req.TemplateSpaceId,
},
},
})
}

in := &pbds.BatchUpsertTemplatesReq{Items: items}
data, err := s.client.DS.BatchUpsertTemplates(grpcKit.RpcCtx(), in)
if err != nil {
return nil, err
}
resp := &pbcs.BatchUpsertTemplatesResp{Ids: data.Ids}
return resp, nil
}
Loading

0 comments on commit 8b0e922

Please sign in to comment.