agent/taskengine/sendfile.go (230 lines of code) (raw):
package taskengine
import (
"encoding/base64"
"fmt"
"io/ioutil"
"os"
"os/user"
"path"
"runtime"
"strconv"
"strings"
"github.com/aliyun/aliyun_assist_client/agent/log"
"github.com/aliyun/aliyun_assist_client/agent/metrics"
"github.com/aliyun/aliyun_assist_client/agent/taskengine/models"
"github.com/aliyun/aliyun_assist_client/agent/util"
"github.com/aliyun/aliyun_assist_client/common/pathutil"
)
const (
ESuccess = 0
EFileCreateFail = 1
EChownError = 2
EChmodError = 3
ECreateDirFailed = 4
EInvalidFilePath = 10
EFileAlreadyExist = 11
EEmptyContent = 12
EInvalidContent = 13
EInvalidContentType = 14
EInvalidFileType = 15
EInvalidSignature = 16
EInalidFileMode = 17
EInalidGID = 18
EInalidUID = 19
)
var G_IsWindows bool = false
var G_IsFreebsd bool = false
var G_IsLinux bool = false
func init() {
if runtime.GOOS == "windows" {
G_IsWindows = true
} else if runtime.GOOS == "linux" {
G_IsLinux = true
} else if runtime.GOOS == "freebsd" {
G_IsFreebsd = true
} else {
}
}
func SendFiles(sendFileTasks []models.SendFileTaskInfo) {
for _, s := range sendFileTasks {
doSendFile(s)
}
}
func SendFileFinished(sendFile models.SendFileTaskInfo, status int) {
url := util.GetFinishOutputService()
reportStatus := "Success"
if status != ESuccess {
reportStatus = "Failed"
}
param := fmt.Sprintf("?taskId=%s&status=%s&taskType=%s&errorcode=%d",
sendFile.TaskID, reportStatus, "sendfile", status)
url += param
log.GetLogger().Printf("post = %s", url)
if status != ESuccess {
metrics.GetTaskFailedEvent(
"taskid", sendFile.TaskID,
"errormsg", param,
).ReportEvent()
}
_, err := util.HttpPost(url, "", "text")
if err != nil {
log.GetLogger().Printf("HttpPost url %s error:%s ", url, err.Error())
}
}
func SendFileInvalid(sendFile models.SendFileTaskInfo, status int) {
url := util.GetInvalidTaskService()
key := ""
value := ""
if status == EInvalidFilePath {
key = "FileNameInvalid"
value = sendFile.Name
} else if status == EFileAlreadyExist {
key = "FileExist"
value = sendFile.Name
} else if status == EEmptyContent {
key = "EmptyFile"
} else if status == EInvalidContent {
key = "InvalidFileContent"
} else if status == EInvalidSignature {
key = "InvalidSignature"
value = sendFile.Signature
} else if status == EInalidFileMode {
key = "InvalidFileMode"
value = sendFile.Mode
} else if status == EInalidGID {
key = "FileGroupNotExist"
value = sendFile.Group
} else if status == EInalidUID {
key = "FileOwnerNotExist"
value = sendFile.Owner
}
metrics.GetTaskFailedEvent(
"taskid", sendFile.TaskID,
"errormsg", fmt.Sprintf("%s : %s", key, value),
).ReportEvent()
url = url + "?" + "taskId=" + sendFile.TaskID + "&taskType=sendfile¶m=" + key + "&value=" + value
log.GetLogger().Printf("post = %s", url)
_, err := util.HttpPost(url, "", "text")
if err != nil {
log.GetLogger().Printf("HttpPost url %s error:%s ", url, err.Error())
}
}
func doSendFile(task models.SendFileTaskInfo) {
ret := sendFile(task)
log.GetLogger().Println("sendFile ret: ", ret)
if ret <= ECreateDirFailed {
SendFileFinished(task, ret)
} else {
SendFileInvalid(task, ret)
}
}
func sendFile(sendFile models.SendFileTaskInfo) int {
if sendFile.Name == "" {
return EInvalidFilePath
}
if sendFile.Content == "" {
return EEmptyContent
}
fileDir := ""
if sendFile.Destination == "" {
if G_IsWindows {
fileDir, _ = pathutil.GetExecutableDir()
} else {
fileDir = "/root"
}
} else {
fileDir = sendFile.Destination
}
if sendFile.Destination != "" {
err := os.MkdirAll(sendFile.Destination, os.ModePerm)
if err != nil {
log.GetLogger().Errorln("MkdirAll error: ", err)
return ECreateDirFailed
}
}
if G_IsLinux || G_IsFreebsd {
//文件下发时,如果root目录有一个test的文件,又创建了一个/root/test下的文件,则会报错。报错应通过invalid接口上报
if util.IsFile(sendFile.Destination) {
return EInvalidFilePath
}
}
file_path := path.Join(fileDir, sendFile.Name)
fileContent, err := base64.StdEncoding.DecodeString(sendFile.Content)
if err != nil {
log.GetLogger().Errorln("base64 decode error: ", err)
return EInvalidContent
}
contentMd5 := util.ComputeStrMd5(sendFile.Content)
if strings.ToLower(contentMd5) != strings.ToLower(sendFile.Signature) {
return EInvalidSignature
}
fileMode := sendFile.Mode
if len(fileMode) != 3 && len(fileMode) != 4 && len(fileMode) != 0 {
return EInalidFileMode
}
if len(fileMode) == 0 {
fileMode = "0644"
}
fMode, err := strconv.ParseInt(fileMode, 8, 32)
if err != nil {
return EInalidFileMode
}
ret := writeFile(file_path, fileContent, sendFile.Overwrite, os.FileMode(fMode))
if ret != ESuccess {
return ret
}
return changeFileOwner(file_path, sendFile.Owner, sendFile.Group)
}
func changeFileOwner(filePath string, User string, Group string) int {
if G_IsWindows {
return ESuccess
}
if User == "" && Group == "" {
return ESuccess
}
if User == "root" && Group == "root" {
return ESuccess
}
if User == "" {
User = "root"
}
lu, err := user.Lookup(User)
if err != nil {
log.GetLogger().Printf("Lookup uid %s error:%s ", User, err.Error())
return EInalidUID
}
uid, _ := strconv.Atoi(lu.Uid)
if Group == "" {
Group = "root"
}
lg, err := user.LookupGroup(Group)
if err != nil {
log.GetLogger().Printf("Lookup gid %s error:%s ", Group, err.Error())
return EInalidGID
}
gid, _ := strconv.Atoi(lg.Gid)
err = os.Chown(filePath, uid, gid)
if err != nil {
log.GetLogger().Printf("Chown file %s error:%s ", filePath, err.Error())
return EChownError
}
return ESuccess
}
func writeFile(filePath string, data []byte, overWrite bool, fileMode os.FileMode) int {
fileExist := util.FileExist(filePath)
if fileExist && !overWrite {
return EFileAlreadyExist
}
err := ioutil.WriteFile(filePath, data, 0644)
if err != nil {
log.GetLogger().Errorln("WriteFile: ", err)
return EFileCreateFail
}
if G_IsLinux {
err = os.Chmod(filePath, fileMode)
if err != nil {
log.GetLogger().Errorln(" Chmod faild", err)
return EChmodError
}
}
return ESuccess
}