Skip to content

Commit

Permalink
feat: 新增记录变更文件时差异化的日志
Browse files Browse the repository at this point in the history
  • Loading branch information
Ambition9186 committed Nov 28, 2024
1 parent a1fb5cf commit 4e68b5a
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 4 deletions.
90 changes: 86 additions & 4 deletions client/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"errors"
"fmt"
"os"
"path"
"path/filepath"
"runtime"
"strings"
Expand Down Expand Up @@ -65,6 +66,7 @@ type Release struct {
BizID uint32
ClientMode sfs.ClientMode
AppMate *sfs.SideAppMeta
ChangeLog *eventmeta.ChangeLog
}

// ConfigItemFile defines config item file
Expand Down Expand Up @@ -196,6 +198,12 @@ func (p *PostScriptStrategy) executeScript(r *Release) error {
if r.PostHook == nil {
return nil
}

// 记录变更的文件的差异
if err := r.recordChangeLog(); err != nil {
logger.Error("record changeLog file failed", logger.ErrAttr(err))
}

err := util.ExecuteHook(r.PostHook, table.PostHook, r.TempDir, r.BizID, r.AppMate.App, r.ReleaseName)
if err != nil {
logger.Error("execute post hook", logger.ErrAttr(err))
Expand Down Expand Up @@ -226,10 +234,20 @@ func (r *Release) UpdateFiles() Function {
return func() error {
filesDir := filepath.Join(r.AppDir, "files")
if err := updateFiles(filesDir, r.FileItems, &r.AppMate.DownloadFileNum, &r.AppMate.DownloadFileSize,
r.SemaphoreCh); err != nil {
r.SemaphoreCh, r.ChangeLog); err != nil {
logger.Error("update file failed", logger.ErrAttr(err))
return err
}

// 查找被删除的文件
delFiles, err := findDeletedFiles(util.ConvertBackslashes(filesDir), r.FileItems)
if err != nil {
return sfs.WrapPrimaryError(sfs.DownloadFailed,
sfs.SecondaryError{SpecificFailedReason: sfs.UnknownSpecificFailed,
Err: fmt.Errorf("search for deleted files failed, %s", err.Error())})
}
r.ChangeLog.Delete = delFiles

if r.ClientMode == sfs.Pull {
return nil
}
Expand Down Expand Up @@ -274,12 +292,16 @@ func (r *Release) UpdateMetadata() Function {
}

// checkFileExists checks the file exists and the SHA256 is match.
func checkFileExists(absPath string, ci *sfs.ConfigItemMetaV1) (bool, error) {
func checkFileExists(absPath string, ci *sfs.ConfigItemMetaV1, changeLog *eventmeta.ChangeLog) (bool, error) {
filePath := filepath.Join(absPath, ci.ConfigItemSpec.Name)
_, err := os.Stat(filePath)
if err != nil {
if os.IsNotExist(err) {
// content is not exist
// 记录需要新增的文件
changeLog.Add = append(changeLog.Add, path.Join(
util.ConvertBackslashes(ci.ConfigItemSpec.Path),
ci.ConfigItemSpec.Name))
return false, nil
}

Expand All @@ -292,6 +314,10 @@ func checkFileExists(absPath string, ci *sfs.ConfigItemMetaV1) (bool, error) {
}

if sha != ci.ContentSpec.Signature {
// 记录需要更新的文件
changeLog.Modif = append(changeLog.Modif, path.Join(
util.ConvertBackslashes(ci.ConfigItemSpec.Path),
ci.ConfigItemSpec.Name))
logger.Debug("configuration item's SHA256 is not match, need to update",
slog.String("localHash", sha), slog.String("remoteHash", ci.ContentSpec.Signature))
return false, nil
Expand Down Expand Up @@ -353,6 +379,12 @@ func (r *Release) Execute(steps ...Function) error {
err error
skip bool
)
// 初始化变更文件日志
r.ChangeLog = &eventmeta.ChangeLog{
Add: make([]string, 0),
Modif: make([]string, 0),
Delete: make([]string, 0),
}
// 填充appMate数据
r.AppMate.CursorID = r.CursorID
r.AppMate.StartTime = time.Now().UTC()
Expand Down Expand Up @@ -569,7 +601,7 @@ func (r *Release) sendHeartbeatMessaging(vas *kit.Vas, msgType sfs.MessagingType

// updateFiles updates the files to the target directory.
func updateFiles(filesDir string, files []*ConfigItemFile, successDownloads *int32, successFileSize *uint64,
semaphoreCh chan struct{}) error {
semaphoreCh chan struct{}, changeLog *eventmeta.ChangeLog) error {
start := time.Now()
// Initialize the successDownloads and successFileSize to zero at the beginning of the function.
atomic.StoreInt32(successDownloads, 0)
Expand All @@ -591,7 +623,7 @@ func updateFiles(filesDir string, files []*ConfigItemFile, successDownloads *int
Err: fmt.Errorf("create dir %s failed, err: %s", fileDir, err.Error())})
}
// 2. check and download file
exists, err := checkFileExists(fileDir, file.FileMeta)
exists, err := checkFileExists(fileDir, file.FileMeta, changeLog)
if err != nil {
atomic.AddInt32(&failed, 1)
return sfs.WrapPrimaryError(sfs.DownloadFailed,
Expand Down Expand Up @@ -698,3 +730,53 @@ func (r *FileStreamReader) Read(p []byte) (int, error) {
func (r *FileStreamReader) Close() error {
return r.stream.CloseSend()
}

// recordChangeLog 记录变更日志
func (r *Release) recordChangeLog() error {
r.ChangeLog.CurrentReleaseID = r.AppMate.CurrentReleaseID
r.ChangeLog.TargetReleaseID = r.AppMate.TargetReleaseID
err := eventmeta.RecordChangeLog(r.AppDir, r.ChangeLog)
if err != nil {
logger.Error("record change log failed", logger.ErrAttr(err))
return err
}
return nil
}

// findDeletedFiles 查找删除的文件
func findDeletedFiles(folder string, files []*ConfigItemFile) ([]string, error) {
deletedFiles := []string{}

// 将路径数组转换为一个集合以方便查找
pathSet := make(map[string]struct{})
for _, file := range files {
// 1. prapare file path
fileDir := filepath.Join(folder, file.Path)
filePath := filepath.Join(fileDir, file.Name)
pathSet[filePath] = struct{}{}
}

// 遍历文件夹中的文件
err := filepath.Walk(folder, func(file string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// 跳过目录
if info.IsDir() {
return nil
}

// 检查文件路径是否在给定路径数组中
if _, exists := pathSet[file]; !exists {
fileDir, _ := filepath.Rel(folder, file)
deletedFiles = append(deletedFiles, "/"+util.ConvertBackslashes(fileDir))
}
return nil
})

if err != nil {
return nil, err
}

return deletedFiles, nil
}
7 changes: 7 additions & 0 deletions internal/util/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

package util

import "strings"

// TruncateString It accepts a string s and an integer maxLength as parameters,
// If the length of string s is greater than maxLength, the first maxLength characters are
// truncated and '...' is appended to the end.
Expand All @@ -22,3 +24,8 @@ func TruncateString(s string, maxLength int) string {
}
return s
}

// ConvertBackslashes 用于将字符串中的反斜杠转换为正斜杠
func ConvertBackslashes(input string) string {
return strings.ReplaceAll(input, `\`, "/")
}
61 changes: 61 additions & 0 deletions internal/util/eventmeta/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,3 +238,64 @@ func GetLatestChangeEventFromFile(tempDir string) (*ChangeEvent, error) {

return metadata, nil
}

// ChangeLog 记录变更后的文件
type ChangeLog struct {
// CurrentReleaseID is sidecar's current effected release id.
CurrentReleaseID uint32 `json:"currentReleaseID"`
// TargetReleaseID is sidecar's target release id
TargetReleaseID uint32 `json:"targetReleaseID"`
// Add newly added files
Add []string `json:"add"`
// Delete deletion files
Delete []string `json:"delete"`
// Modif modified file
Modif []string `json:"modif"`
}

// RecordChangeLog 记录变更的日志
func RecordChangeLog(tempDir string, log *ChangeLog) error {
if tempDir == "" {
return sfs.WrapPrimaryError(sfs.UnknownFailed,
sfs.SecondaryError{SpecificFailedReason: sfs.FilePathNotFound,
Err: errors.New("the file path for record change log is empty")})
}

// prepare temp dir, make sure it exists
if err := os.MkdirAll(tempDir, os.ModePerm); err != nil {
return sfs.WrapPrimaryError(sfs.UnknownFailed,
sfs.SecondaryError{SpecificFailedReason: sfs.NewFolderFailed, Err: err})
}

metaFilePath := filepath.Join(tempDir, "changelog.json")

metaFile, err := os.OpenFile(metaFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
return sfs.WrapPrimaryError(sfs.UnknownFailed,
sfs.SecondaryError{SpecificFailedReason: sfs.OpenFileFailed,
Err: fmt.Errorf("open record change log file failed, err: %s", err.Error())})
}
defer metaFile.Close()

b, err := json.Marshal(log)
if err != nil {
return sfs.WrapPrimaryError(sfs.UpdateMetadataFailed,
sfs.SecondaryError{SpecificFailedReason: sfs.SerializationFailed,
Err: fmt.Errorf("marshal metadata failed, err: %s", err.Error())})
}
compress := bytes.NewBuffer([]byte{})
if err := json.Compact(compress, b); err != nil {
return sfs.WrapPrimaryError(sfs.UpdateMetadataFailed,
sfs.SecondaryError{SpecificFailedReason: sfs.FormattingFailed,
Err: fmt.Errorf("compress metadata failed, err: %s", err.Error())})
}
if _, err := metaFile.WriteString(compress.String() + "\n"); err != nil {
return sfs.WrapPrimaryError(sfs.UpdateMetadataFailed,
sfs.SecondaryError{SpecificFailedReason: sfs.WriteFileFailed,
Err: fmt.Errorf("record change log failed, err: %s", err.Error())})
}

logger.Info("record change log success", slog.String("event", compress.String()))

return nil
}

0 comments on commit 4e68b5a

Please sign in to comment.