Files
houston/server/http_api.go

510 lines
12 KiB
Go
Raw Normal View History

2023-05-22 00:54:49 +09:00
package server
import (
"encoding/json"
2023-06-26 19:19:58 +09:00
"errors"
"fmt"
2023-05-22 00:54:49 +09:00
"io"
2023-06-14 14:16:47 +09:00
"io/fs"
2023-05-22 00:54:49 +09:00
"net/http"
"os"
"path"
2023-06-27 09:44:56 +09:00
"regexp"
2023-05-22 15:40:01 +09:00
"strconv"
2023-06-26 11:26:57 +09:00
"strings"
2023-05-22 00:54:49 +09:00
"time"
2023-05-22 02:13:03 +09:00
2023-06-14 00:13:51 +09:00
"repositories.action2quare.com/ayo/gocommon/logger"
2023-05-22 02:13:03 +09:00
"repositories.action2quare.com/ayo/houston/shared"
2023-05-22 00:54:49 +09:00
)
2023-05-24 15:14:04 +09:00
/*
*
현재 접속 중인 Agent 목록을 보여줍니다.
- http method : GET
*/
2023-06-22 17:15:56 +09:00
const (
sub_folder_name_deploys = string("_deploys")
sub_folder_name_downloads = string("_downloads")
)
2023-05-22 00:54:49 +09:00
func (h *houstonHandler) GetAgents(w http.ResponseWriter, r *http.Request) {
enc := json.NewEncoder(w)
2023-06-22 17:15:56 +09:00
allHosts := h.Operation().Hosts()
enc.Encode(allHosts)
2023-05-22 00:54:49 +09:00
}
func (h *houstonHandler) GetDeploySources(w http.ResponseWriter, r *http.Request) {
2023-06-13 11:04:30 +09:00
files, err := os.ReadDir(h.deployPath)
2023-05-22 00:54:49 +09:00
if err != nil {
2023-06-14 00:13:51 +09:00
logger.Println(err)
2023-05-22 00:54:49 +09:00
w.WriteHeader(http.StatusInternalServerError)
return
}
getVersions := func(name string) []string {
var out []string
vers, _ := os.ReadDir(path.Join(h.deployPath, name))
mytags, _ := os.ReadFile(path.Join(h.deployPath, name, "@tags"))
for _, fd := range vers {
2023-05-22 00:54:49 +09:00
if fd.IsDir() {
ver := fd.Name()
files, _ := os.ReadDir(path.Join(h.deployPath, name, ver))
2023-07-03 14:08:28 +09:00
vertags, _ := os.ReadFile(path.Join(h.deployPath, name, ver, "@tags"))
ver = ver + string(mytags) + string(vertags)
2023-07-03 14:08:28 +09:00
if len(files) > 0 {
for _, file := range files {
if strings.HasPrefix(file.Name(), "@") {
continue
}
downloadpath := path.Join(sub_folder_name_deploys, name, ver, file.Name())
ver = fmt.Sprintf("%s:%s", ver, downloadpath)
break
}
}
2023-07-03 14:08:28 +09:00
out = append(out, ver)
2023-05-22 00:54:49 +09:00
}
}
2023-05-22 00:54:49 +09:00
return out
}
out := make(map[string][]string)
for _, fd := range files {
if fd.IsDir() {
out[fd.Name()] = getVersions(fd.Name())
}
}
enc := json.NewEncoder(w)
enc.Encode(out)
}
2023-05-23 13:54:25 +09:00
func (h *houstonHandler) UploadDeploySource(w http.ResponseWriter, r *http.Request) {
2023-05-22 00:54:49 +09:00
// <form action="/houston" method="post" enctype="multipart/form-data">
// <input type="file" name="file">
// <input type="text" name="name">
// <input type="text" name="version">
// <input type="submit" value="업로드">
// </form>
file, header, err := r.FormFile("file")
if err != nil {
2023-06-14 00:13:51 +09:00
logger.Println(err)
2023-05-22 00:54:49 +09:00
w.WriteHeader(http.StatusBadRequest)
return
}
defer file.Close()
contents, err := io.ReadAll(file)
if err != nil {
2023-06-14 00:13:51 +09:00
logger.Println(err)
2023-05-22 00:54:49 +09:00
w.WriteHeader(http.StatusInternalServerError)
return
}
version := r.FormValue("version")
name := r.FormValue("name")
ext := path.Ext(header.Filename)
2023-06-12 11:43:29 +09:00
var filename string
if version == "config" {
2023-06-22 17:15:56 +09:00
filename = path.Join(h.deployPath, name, version, "config"+ext)
tags, _ := os.ReadFile(path.Join(h.deployPath, name, version, "@tags"))
if !strings.Contains(string(tags), "#hidden") {
tags = []byte(string(tags) + "#hidden")
os.WriteFile(path.Join(h.deployPath, name, version, "@tags"), tags, 0644)
}
2023-06-12 11:43:29 +09:00
} else {
2023-06-13 11:04:30 +09:00
filename = path.Join(h.deployPath, name, version, name+ext)
2023-06-12 11:43:29 +09:00
}
if err = os.MkdirAll(path.Dir(filename), 0775); err != nil {
2023-06-14 00:13:51 +09:00
logger.Println(err)
2023-05-22 00:54:49 +09:00
w.WriteHeader(http.StatusInternalServerError)
return
}
// 파일 저장
err = os.WriteFile(filename, contents, 0644)
if err != nil {
2023-06-14 00:13:51 +09:00
logger.Println(err)
2023-05-22 00:54:49 +09:00
w.WriteHeader(http.StatusInternalServerError)
return
}
}
2023-05-30 12:12:23 +09:00
func (h *houstonHandler) DeleteDeploySource(w http.ResponseWriter, r *http.Request) {
// <form action="/houston" method="post" enctype="multipart/form-data">
// <input type="text" name="name">
// <input type="text" name="version">
// </form>
version := r.FormValue("version")
name := r.FormValue("name")
if len(version) == 0 || len(name) == 0 {
w.WriteHeader(http.StatusBadRequest)
return
}
// deploys 폴더는 파일시스템 서비스이므로 다운로드 가능
2023-06-13 11:04:30 +09:00
targetpath := path.Join(h.deployPath, name, version)
2023-05-30 12:12:23 +09:00
if err := os.RemoveAll(targetpath); err != nil {
2023-06-14 00:13:51 +09:00
logger.Println("deleteDeploySource failed :", err)
2023-05-30 12:12:23 +09:00
w.WriteHeader(http.StatusInternalServerError)
return
}
}
2023-06-26 11:26:57 +09:00
func (h *houstonHandler) findLastestConfigFile(name string) (string, error) {
logger.Println("findLastestConfigFile :", name)
configFiles, err := os.ReadDir(path.Join(h.deployPath, name, "config"))
if err != nil {
2023-06-26 19:19:58 +09:00
if errors.Is(err, fs.ErrNotExist) {
return "", nil
}
2023-06-26 11:26:57 +09:00
logger.Println("findLastestConfigFile failed :", err)
return "", err
}
var cf fs.FileInfo
for _, file := range configFiles {
if file.IsDir() {
continue
}
if strings.HasPrefix(file.Name(), "config.") {
test, err := file.Info()
if err != nil {
return "", err
}
if cf == nil {
cf = test
} else if test.ModTime().After(cf.ModTime()) {
cf = test
}
}
}
if cf != nil {
logger.Println("findLastestConfigFile cf found :", cf.Name())
return path.Join(sub_folder_name_deploys, name, "config", cf.Name()), nil
}
logger.Println("findLastestConfigFile cf NOT found")
return "", nil
}
2023-05-22 00:54:49 +09:00
func (h *houstonHandler) Deploy(w http.ResponseWriter, r *http.Request) {
// <form action="/houston" method="post" enctype="multipart/form-data">
// <input type="text" name="name">
// <input type="text" name="version">
// <input type="text" name="targets">
// <input type="submit" value="업로드">
// </form>
name := r.FormValue("name")
version := r.FormValue("version")
traws := r.FormValue("targets")
var targets []string
2023-05-23 13:54:25 +09:00
if len(traws) > 0 {
if err := json.Unmarshal([]byte(traws), &targets); err != nil {
2023-06-14 00:13:51 +09:00
logger.Println(err)
2023-05-23 13:54:25 +09:00
w.WriteHeader(http.StatusBadRequest)
return
}
2023-05-22 00:54:49 +09:00
}
if len(name) == 0 || len(version) == 0 || len(targets) == 0 {
w.WriteHeader(http.StatusBadRequest)
return
}
2023-06-13 11:04:30 +09:00
relPath := path.Join(h.deployPath, name, version)
2023-05-22 00:54:49 +09:00
files, err := os.ReadDir(relPath)
if err != nil {
2023-06-14 00:13:51 +09:00
logger.Println(err)
2023-06-11 16:04:04 +09:00
w.WriteHeader(http.StatusBadRequest)
2023-05-22 00:54:49 +09:00
return
}
latestTime := time.Time{}
var latestFilename string
for _, fd := range files {
if fd.IsDir() {
continue
}
if strings.HasPrefix(fd.Name(), "@") {
continue
}
2023-05-22 00:54:49 +09:00
fi, _ := fd.Info()
if fi.ModTime().After(latestTime) {
latestFilename = fi.Name()
latestTime = fi.ModTime()
}
}
if len(latestFilename) == 0 {
w.WriteHeader(http.StatusBadRequest)
return
}
2023-06-26 11:26:57 +09:00
configPath, err := h.findLastestConfigFile(name)
if err != nil {
logger.Println(err)
w.WriteHeader(http.StatusBadRequest)
return
2023-06-14 14:16:47 +09:00
}
2023-05-22 00:54:49 +09:00
h.Operation().Deploy(MakeDeployRequest(
shared.DeployRequest{
Name: name,
Version: version,
2023-06-22 17:15:56 +09:00
Url: path.Join(sub_folder_name_deploys, name, version, latestFilename),
2023-06-14 14:16:47 +09:00
Config: configPath,
2023-05-22 00:54:49 +09:00
},
targets,
))
}
2023-05-30 12:12:23 +09:00
func (h *houstonHandler) Undeploy(w http.ResponseWriter, r *http.Request) {
// <form action="/houston" method="post" enctype="multipart/form-data">
// <input type="text" name="name">
// <input type="text" name="version">
// <input type="text" name="targets">
// </form>
name := r.FormValue("name")
version := r.FormValue("version")
traws := r.FormValue("targets")
var targets []string
if len(traws) > 0 {
if err := json.Unmarshal([]byte(traws), &targets); err != nil {
2023-06-14 00:13:51 +09:00
logger.Println(err)
2023-05-30 12:12:23 +09:00
w.WriteHeader(http.StatusBadRequest)
return
}
}
if len(name) == 0 || len(version) == 0 || len(targets) == 0 {
w.WriteHeader(http.StatusBadRequest)
return
}
h.Operation().Withdraw(MakeWithdrawRequest(
shared.WithdrawRequest{
Name: name,
Version: version,
},
targets,
))
}
2023-05-22 00:54:49 +09:00
func (h *houstonHandler) StartProcess(w http.ResponseWriter, r *http.Request) {
// <form action="/houston" method="post" enctype="multipart/form-data">
// <input type="text" name="name">
// <input type="text" name="version">
// <input type="text" name="args">
// <input type="text" name="targets">
// <input type="submit" value="업로드">
// </form>
name := r.FormValue("name")
version := r.FormValue("version")
2023-06-27 09:44:56 +09:00
argsline := r.FormValue("args")
2023-05-22 00:54:49 +09:00
traws := r.FormValue("targets")
var targets []string
2023-05-23 13:54:25 +09:00
if len(traws) > 0 {
if err := json.Unmarshal([]byte(traws), &targets); err != nil {
2023-06-14 00:13:51 +09:00
logger.Println(err)
2023-05-23 13:54:25 +09:00
w.WriteHeader(http.StatusBadRequest)
return
}
2023-05-22 00:54:49 +09:00
}
if len(name) == 0 || len(version) == 0 || len(targets) == 0 {
w.WriteHeader(http.StatusBadRequest)
return
}
2023-06-27 09:44:56 +09:00
re := regexp.MustCompile(`[^\s"']+|"([^"]*)"|'([^']*)`)
argsTemp := re.FindAllString(argsline, -1)
var args []string
for _, arg := range argsTemp {
if strings.HasPrefix(arg, `"`) && len(args) > 0 {
lastarg := args[len(args)-1]
if strings.HasSuffix(lastarg, "=") {
args[len(args)-1] = lastarg + arg
} else {
args = append(args, arg)
}
} else {
args = append(args, arg)
}
}
if len(args) == 0 {
w.WriteHeader(http.StatusBadRequest)
return
}
2023-05-22 00:54:49 +09:00
h.Operation().StartProcess(MakeStartProcessRequest(shared.StartProcessRequest{
Name: name,
Version: version,
Args: args, // 실행 파일 포함 e.g. "biglocal.exe -port=8090 -dev",
}, targets))
2023-05-22 15:40:01 +09:00
}
func (h *houstonHandler) StopProcess(w http.ResponseWriter, r *http.Request) {
// <form action="/houston" method="post" enctype="multipart/form-data">
// <input type="text" name="name">
// <input type="text" name="version">
// <input type="text" name="args">
// <input type="text" name="targets">
// <input type="submit" value="업로드">
// </form>
name := r.FormValue("name")
if len(name) == 0 {
w.WriteHeader(http.StatusBadRequest)
return
}
2023-05-22 00:54:49 +09:00
2023-05-22 15:40:01 +09:00
version := r.FormValue("version") // option
pidstr := r.FormValue("pid") // option
traws := r.FormValue("targets")
var targets []string
2023-05-23 13:54:25 +09:00
if len(traws) > 0 {
if err := json.Unmarshal([]byte(traws), &targets); err != nil {
2023-06-14 00:13:51 +09:00
logger.Println(err)
2023-05-23 13:54:25 +09:00
w.WriteHeader(http.StatusBadRequest)
return
}
2023-05-22 15:40:01 +09:00
}
pid, _ := strconv.Atoi(pidstr)
h.Operation().StopProcess(MakeStopProcessRequest(shared.StopProcessRequest{
Name: name,
Version: version,
Pid: int32(pid),
}, targets))
2023-05-22 00:54:49 +09:00
}
2023-05-23 13:54:25 +09:00
2023-06-26 11:26:57 +09:00
func (h *houstonHandler) RestartProcess(w http.ResponseWriter, r *http.Request) {
// <form action="/houston" method="post" enctype="multipart/form-data">
// <input type="text" name="name">
// <input type="text" name="target">
// <input type="text" name="pid">
// <input type="text" name="config">
// </form>
pidstr := r.FormValue("pid")
target := r.FormValue("target")
name := r.FormValue("name")
if len(target) == 0 || len(pidstr) == 0 || len(name) == 0 {
w.WriteHeader(http.StatusBadRequest)
return
}
deployConfig := false
configstr := r.FormValue("config")
if len(configstr) > 0 {
deployConfig, _ = strconv.ParseBool(configstr)
}
pid, err := strconv.ParseInt(pidstr, 10, 0)
if err != nil {
logger.Println(err)
w.WriteHeader(http.StatusBadRequest)
return
}
var configPath string
if deployConfig {
configPath, err = h.findLastestConfigFile(name)
if err != nil {
logger.Println(err)
w.WriteHeader(http.StatusBadRequest)
return
}
}
h.Operation().RestartProcess(MakeRestartRequest(shared.RestartProcessRequest{
Name: name,
Pid: int32(pid),
Config: configPath,
}, []string{target}))
}
2023-05-23 13:54:25 +09:00
func (h *houstonHandler) UploadLogs(w http.ResponseWriter, r *http.Request) {
// <form action="/houston" method="post" enctype="multipart/form-data">
// <input type="text" name="name">
// <input type="text" name="version">
// <input type="text" name="filter">
// <input type="text" name="targets">
// <input type="submit" value="업로드">
// </form>
name := r.FormValue("name")
if len(name) == 0 {
w.WriteHeader(http.StatusBadRequest)
return
}
version := r.FormValue("version") // option
traws := r.FormValue("targets")
filter := r.FormValue("filter")
if len(filter) == 0 {
filter = "logs/*"
}
var targets []string
if err := json.Unmarshal([]byte(traws), &targets); err != nil {
2023-06-14 00:13:51 +09:00
logger.Println(err)
2023-05-23 13:54:25 +09:00
w.WriteHeader(http.StatusBadRequest)
return
}
h.Operation().Upload(MakeUploadRequest(shared.UploadRequest{
Name: name,
Version: version,
Url: "upload",
Filter: filter,
}, targets))
}
func (h *houstonHandler) GetLogFileLinks(w http.ResponseWriter, r *http.Request) {
// <form action="/houston" method="post" enctype="multipart/form-data">
// <input type="text" name="name">
// <input type="text" name="version">
// </form>
name := r.FormValue("name")
version := r.FormValue("version")
if len(name) == 0 || len(version) == 0 {
w.WriteHeader(http.StatusBadRequest)
return
}
if version == "latest" {
2023-06-14 14:16:47 +09:00
version, _ = shared.FindLastestVersion(h.downloadPath, name)
2023-05-23 13:54:25 +09:00
}
2023-06-13 11:04:30 +09:00
root := path.Join(h.downloadPath, name, version)
2023-05-23 13:54:25 +09:00
logfiles, err := os.ReadDir(root)
if err != nil {
2023-06-11 16:04:04 +09:00
w.WriteHeader(http.StatusBadRequest)
2023-05-23 13:54:25 +09:00
return
}
var out []string
for _, lf := range logfiles {
2023-06-22 17:15:56 +09:00
out = append(out, path.Join(sub_folder_name_downloads, name, version, lf.Name()))
2023-05-23 13:54:25 +09:00
}
enc := json.NewEncoder(w)
enc.Encode(out)
}