CasaOS/route/v1/docker.go

1263 lines
42 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package v1
import (
"bytes"
json2 "encoding/json"
"net/http"
"os/exec"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/model/notify"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/docker"
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
port2 "github.com/IceWhaleTech/CasaOS/pkg/utils/port"
"github.com/IceWhaleTech/CasaOS/pkg/utils/random"
"github.com/IceWhaleTech/CasaOS/service"
"github.com/IceWhaleTech/CasaOS/service/docker_base"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"github.com/IceWhaleTech/CasaOS/types"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"github.com/jinzhu/copier"
uuid "github.com/satori/go.uuid"
"go.uber.org/zap"
"golang.org/x/crypto/ssh"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool { return true },
HandshakeTimeout: time.Duration(time.Second * 5),
}
// 打开docker的terminal
func DockerTerminal(c *gin.Context) {
col := c.DefaultQuery("cols", "100")
row := c.DefaultQuery("rows", "30")
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
return
}
defer conn.Close()
container := c.Param("id")
hr, err := service.Exec(container, row, col)
if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
return
}
// 关闭I/O流
defer hr.Close()
// 退出进程
defer func() {
hr.Conn.Write([]byte("exit\r"))
}()
go func() {
docker.WsWriterCopy(hr.Conn, conn)
}()
docker.WsReaderCopy(conn, hr.Conn)
}
func PostSshLogin(c *gin.Context) {
j := make(map[string]string)
c.ShouldBind(&j)
userName := j["username"]
password := j["password"]
port := j["port"]
if userName == "" || password == "" || port == "" {
c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS), Data: "Username or password or port is empty"})
return
}
_, err := docker.NewSshClient(userName, password, port)
if err != nil {
c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.CLIENT_ERROR), Data: "Please check if the username and port are correct, and make sure that ssh server is installed."})
loger.Error("connect ssh error", zap.Any("error", err))
return
}
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
}
func WsSsh(c *gin.Context) {
_, e := exec.LookPath("ssh")
if e != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: "ssh server not found"})
return
}
userName := c.Query("username")
password := c.Query("password")
port := c.Query("port")
wsConn, _ := upgrader.Upgrade(c.Writer, c.Request, nil)
var logBuff = new(bytes.Buffer)
quitChan := make(chan bool, 3)
// user := ""
// password := ""
var login int = 1
cols, _ := strconv.Atoi(c.DefaultQuery("cols", "200"))
rows, _ := strconv.Atoi(c.DefaultQuery("rows", "32"))
var client *ssh.Client
for login != 0 {
var err error
if userName == "" || password == "" || port == "" {
wsConn.WriteMessage(websocket.TextMessage, []byte("username or password or port is empty"))
}
// wsConn.WriteMessage(websocket.TextMessage, []byte("login:"))
// user = docker.ReceiveWsMsgUser(wsConn, logBuff)
// wsConn.WriteMessage(websocket.TextMessage, []byte("\r\n\x1b[0m"))
// wsConn.WriteMessage(websocket.TextMessage, []byte("password:"))
// password = docker.ReceiveWsMsgPassword(wsConn, logBuff)
// wsConn.WriteMessage(websocket.TextMessage, []byte("\r\n\x1b[0m"))
client, err = docker.NewSshClient(userName, password, port)
if err != nil && client == nil {
wsConn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
wsConn.WriteMessage(websocket.TextMessage, []byte("\r\n\x1b[0m"))
} else {
login = 0
}
}
if client != nil {
defer client.Close()
}
ssConn, _ := docker.NewSshConn(cols, rows, client)
defer ssConn.Close()
go ssConn.ReceiveWsMsg(wsConn, logBuff, quitChan)
go ssConn.SendComboOutput(wsConn, quitChan)
go ssConn.SessionWait(quitChan)
<-quitChan
}
// @Summary 安装app(该接口需要post json数据)
// @Produce application/json
// @Accept application/json
// @Tags app
// @Param id path int true "id"
// @Param port formData int true "主端口"
// @Param tcp formData string false "tcp端口"
// @Param udp formData string false "udp端口"
// @Param env formData string false "环境变量"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/install [post]
func InstallApp(c *gin.Context) {
var appInfo model.ServerAppList
m := model.CustomizationPostData{}
c.ShouldBind(&m)
const CUSTOM = "custom"
var dockerImage string
var dockerImageVersion string
//check app name is exist
if len(m.Protocol) == 0 {
m.Protocol = "http"
}
m.ContainerName = strings.Replace(m.Label, " ", "_", -1)
if m.Origin != CUSTOM {
oldName := m.ContainerName
oldLabel := m.Label
for i := 0; true; i++ {
if i != 0 {
m.ContainerName = oldName + "-" + strconv.Itoa(i)
m.Label = oldLabel + "-" + strconv.Itoa(i)
}
if _, err := service.MyService.Docker().DockerListByName(m.ContainerName); err != nil {
break
}
}
} else {
if _, err := service.MyService.Docker().DockerListByName(m.ContainerName); err == nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.ERROR_APP_NAME_EXIST, Message: common_err.GetMsg(common_err.ERROR_APP_NAME_EXIST)})
return
}
}
//check port
if len(m.PortMap) > 0 && m.PortMap != "0" {
//c.JSON(http.StatusOK, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
portMap, _ := strconv.Atoi(m.PortMap)
if !port2.IsPortAvailable(portMap, "tcp") {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: "Duplicate port:" + m.PortMap})
return
}
}
//if len(m.Port) == 0 || m.Port == "0" {
// m.Port = m.PortMap
//}
imageArr := strings.Split(m.Image, ":")
if len(imageArr) == 2 {
dockerImage = imageArr[0]
dockerImageVersion = imageArr[1]
} else {
dockerImage = m.Image
dockerImageVersion = "latest"
}
m.Image = dockerImage + ":" + dockerImageVersion
for _, u := range m.Ports {
if u.Protocol == "udp" {
t, _ := strconv.Atoi(u.CommendPort)
if !port2.IsPortAvailable(t, "udp") {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: "Duplicate port:" + u.CommendPort})
return
}
} else if u.Protocol == "tcp" {
te, _ := strconv.Atoi(u.CommendPort)
if !port2.IsPortAvailable(te, "tcp") {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: "Duplicate port:" + u.CommendPort})
return
}
} else if u.Protocol == "both" {
t, _ := strconv.Atoi(u.CommendPort)
if !port2.IsPortAvailable(t, "udp") {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: "Duplicate port:" + u.CommendPort})
return
}
te, _ := strconv.Atoi(u.CommendPort)
if !port2.IsPortAvailable(te, "tcp") {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: "Duplicate port:" + u.CommendPort})
return
}
}
}
if m.Origin == CUSTOM {
for _, device := range m.Devices {
if file.CheckNotExist(device.Path) {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.DEVICE_NOT_EXIST, Message: device.Path + "," + common_err.GetMsg(common_err.DEVICE_NOT_EXIST)})
return
}
}
} else {
dev := []model.PathMap{}
for _, device := range dev {
if !file.CheckNotExist(device.Path) {
dev = append(dev, device)
}
}
m.Devices = dev
}
//restart := c.PostForm("restart") //always 总是重启, unless-stopped 除非用户手动停止容器,否则总是重新启动, on-failure:仅当容器退出代码非零时重新启动
//if len(restart) > 0 {
//
//}
//
//privileged := c.PostForm("privileged") //是否处于特权模式
//if len(privileged) > 0 {
//
//}
id := uuid.NewV4().String()
m.CustomId = id
var relyMap = make(map[string]string)
go func() {
// installLog := model2.AppNotify{}
// installLog.State = 0
// installLog.CustomId = m.Label
// installLog.Message = "installing rely"
// installLog.Class = types.NOTIFY_APP
// installLog.Type = types.NOTIFY_TYPE_UNIMPORTANT
// installLog.CreatedAt = strconv.FormatInt(time.Now().Unix(), 10)
// installLog.UpdatedAt = strconv.FormatInt(time.Now().Unix(), 10)
// installLog.Id = uuid.NewV4().String()
// service.MyService.Notify().AddLog(installLog)
if m.Origin != CUSTOM {
for _, plugin := range appInfo.Plugins {
if plugin == "mysql" {
mid := uuid.NewV4().String()
mc := docker_base.MysqlConfig{}
mc.DataBasePassword = random.RandomString(6, false)
mc.DataBaseDB = appInfo.Title
mc.DataBaseUser = "root"
mc.DataBasePort = "3306"
mysqlContainerId, err := docker_base.MysqlCreate(mc, mid, m.CpuShares, m.Memory)
if len(mysqlContainerId) > 0 && err == nil {
mc.DataBaseHost = mid
m.Envs = docker_base.MysqlFilter(mc, m.Envs)
rely := model2.RelyDBModel{}
rely.Type = types.RELY_TYPE_MYSQL
rely.ContainerId = mysqlContainerId
rely.CustomId = mid
rely.ContainerCustomId = m.Label
var mysqlConfig model2.MysqlConfigs
//结构体转换
copier.Copy(&mysqlConfig, &mc)
rely.Config = mysqlConfig
service.MyService.Rely().Create(rely)
relyMap["mysql"] = mid
} else {
docker_base.MysqlDelete(mysqlContainerId)
// installLog.State = 0
// installLog.Message = err.Error()
// service.MyService.Notify().UpdateLog(installLog)
}
}
}
}
// step下载镜像
err := service.MyService.Docker().DockerPullImage(dockerImage+":"+dockerImageVersion, m.Icon, m.Label)
if err != nil {
notify := notify.Application{}
notify.Icon = m.Icon
notify.Name = m.Label
notify.State = "PULLING"
notify.Type = "INSTALL"
notify.Success = false
notify.Finished = false
notify.Message = err.Error()
service.MyService.Notify().SendInstallAppBySocket(notify)
return
}
for !service.MyService.Docker().IsExistImage(m.Image) {
time.Sleep(time.Second)
}
_, err = service.MyService.Docker().DockerContainerCreate(m, "")
if err != nil {
//service.MyService.Redis().Set(id, "{\"id\"\""+id+"\",\"state\":false,\"message\":\""+err.Error()+"\",\"speed\":80}", 100)
notify := notify.Application{}
notify.Icon = m.Icon
notify.Name = m.Label
notify.State = "STARTING"
notify.Type = "INSTALL"
notify.Success = false
notify.Finished = false
notify.Message = err.Error()
service.MyService.Notify().SendInstallAppBySocket(notify)
return
} else {
notify := notify.Application{}
notify.Icon = m.Icon
notify.Name = m.Label
notify.State = "STARTING"
notify.Type = "INSTALL"
notify.Success = true
notify.Finished = false
service.MyService.Notify().SendInstallAppBySocket(notify)
}
// echo -e "hellow\nworld" >>
//step启动容器
err = service.MyService.Docker().DockerContainerStart(m.ContainerName)
if err != nil {
//service.MyService.Redis().Set(id, "{\"id\"\""+id+"\",\"state\":false,\"message\":\""+err.Error()+"\",\"speed\":90}", 100)
notify := notify.Application{}
notify.Icon = m.Icon
notify.Name = m.Label
notify.State = "STARTING"
notify.Type = "INSTALL"
notify.Success = false
notify.Finished = false
notify.Message = err.Error()
service.MyService.Notify().SendInstallAppBySocket(notify)
return
} else {
// if m.Origin != CUSTOM {
// installLog.Message = "setting upnp"
// } else {
// installLog.Message = "nearing completion"
// }
// service.MyService.Notify().UpdateLog(installLog)
}
//step: 启动成功 检查容器状态确认启动成功
container, err := service.MyService.Docker().DockerContainerInfo(m.ContainerName)
if err != nil && container.ContainerJSONBase.State.Running {
notify := notify.Application{}
notify.Icon = m.Icon
notify.Name = m.Label
notify.State = "INSTALLED"
notify.Type = "INSTALL"
notify.Success = false
notify.Finished = true
notify.Message = err.Error()
service.MyService.Notify().SendInstallAppBySocket(notify)
return
} else {
notify := notify.Application{}
notify.Icon = m.Icon
notify.Name = m.Label
notify.State = "INSTALLED"
notify.Type = "INSTALL"
notify.Success = true
notify.Finished = true
service.MyService.Notify().SendInstallAppBySocket(notify)
}
// if m.Origin != "custom" {
// for i := 0; i < len(m.Volumes); i++ {
// m.Volumes[i].Path = docker.GetDir(id, m.Volumes[i].Path)
// }
// }
//service.MyService.App().SaveContainer(md)
config.CasaOSGlobalVariables.AppChange = true
}()
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: m.Label})
}
//// @Summary 自定义安装app(该接口需要post json数据)
//// @Produce application/json
//// @Accept application/json
//// @Tags app
//// @Param id path int true "id"
//// @Param port formData int true "主端口"
//// @Param tcp formData string false "tcp端口"
//// @Param udp formData string false "udp端口"
//// @Param env formData string false "环境变量"
//// @Security ApiKeyAuth
//// @Success 200 {string} string "ok"
//// @Router /app/install/{id} [post]
//func CustomInstallApp(c *gin.Context) {
// //appId := c.Param("id")
// // appInfo := service.MyService.App().GetServerAppInfo(appId)
//
// m := model.CustomizationPostData{}
// c.ShouldBind(&m)
// //检查端口
// if len(m.PortMap) == 0 || m.PortMap == "0" {
// c.JSON(http.StatusOK, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
// return
// }
// if len(m.Port) == 0 || m.Port == "0" {
// m.Port = m.PortMap
// }
//
// portMap, _ := strconv.Atoi(m.PortMap)
// if !port2.IsPortAvailable(portMap, "tcp") {
// c.JSON(http.StatusOK, model.Result{Success: common_err.ERROR, Message: "Duplicate port:" + m.PortMap})
// return
// }
//
// for _, u := range m.Udp {
// t, _ := strconv.Atoi(u.CommendPort)
// if !port2.IsPortAvailable(t, "udp") {
// c.JSON(http.StatusOK, model.Result{Success: common_err.ERROR, Message: "Duplicate port:" + u.CommendPort})
// return
// }
// }
//
// for _, t := range m.Tcp {
// te, _ := strconv.Atoi(t.CommendPort)
// if !port2.IsPortAvailable(te, "tcp") {
// c.JSON(http.StatusOK, model.Result{Success: common_err.ERROR, Message: "Duplicate port:" + t.CommendPort})
// return
// }
// }
//
// //restart := c.PostForm("restart") //always 总是重启, unless-stopped 除非用户手动停止容器,否则总是重新启动, on-failure:仅当容器退出代码非零时重新启动
// //if len(restart) > 0 {
// //
// //}
// //
// //privileged := c.PostForm("privileged") //是否处于特权模式
// //if len(privileged) > 0 {
// //
// //}
//
// err := service.MyService.Docker().DockerPullImage(m.Image)
// if err != nil {
// c.JSON(http.StatusOK, model.Result{Success: common_err.PULL_IMAGE_ERROR, Message: common_err.GetMsg(common_err.PULL_IMAGE_ERROR)})
// }
//
// id := uuid.NewV4().String()
//
// var relyMap = make(map[string]string)
// go func() {
// installLog := model2.AppNotify{}
// installLog.CustomId = id
// installLog.State = 0
// installLog.Message = "installing rely"
// installLog.Speed = 30
// installLog.CreatedAt = time.Now()
// installLog.UpdatedAt = time.Now()
// service.MyService.Notify().AddLog(installLog)
//
// for !service.MyService.Docker().IsExistImage(m.Image) {
// time.Sleep(time.Second)
// }
//
// installLog.Speed = 50
// installLog.Message = "pulling"
// service.MyService.Notify().UpdateLog(installLog)
// // step下载镜像
//
// var cpd model.PostData
// copier.Copy(&cpd, &m)
// //step创建容器
// containerId, err := service.MyService.Docker().DockerContainerCreate(m.Image, id, cpd, m.NetworkModel, m.Image, "custom")
// installLog.ContainerId = containerId
// if err != nil {
// //service.MyService.Redis().Set(id, "{\"id\"\""+id+"\",\"state\":false,\"message\":\""+err.Error()+"\",\"speed\":80}", 100)
// installLog.State = 0
// installLog.Speed = 80
// installLog.Message = err.Error()
// service.MyService.Notify().UpdateLog(installLog)
// return
// } else {
// //service.MyService.Redis().Set(id, "{\"id\":\""+id+"\",\"state\":true,\"message\":\"starting\",\"speed\":80}", 100)
// installLog.Speed = 80
// installLog.Message = "starting"
// service.MyService.Notify().UpdateLog(installLog)
// }
//
// //step启动容器
// err = service.MyService.Docker().DockerContainerStart(id)
// if err != nil {
// //service.MyService.Redis().Set(id, "{\"id\"\""+id+"\",\"state\":false,\"message\":\""+err.Error()+"\",\"speed\":90}", 100)
// installLog.State = 0
// installLog.Speed = 90
// installLog.Message = err.Error()
// service.MyService.Notify().UpdateLog(installLog)
// return
// } else {
// //service.MyService.Redis().Set(id, "{\"id\":\""+id+"\",\"state\":true,\"message\":\"setting upnp\",\"speed\":90}", 100)
// installLog.Speed = 90
// installLog.Message = "setting upnp"
// service.MyService.Notify().UpdateLog(installLog)
// }
//
// //step: 启动成功 检查容器状态确认启动成功
// containerStatus, err := service.MyService.Docker().DockerContainerInfo(id)
// if err != nil && containerStatus.ContainerJSONBase.State.Running {
// //service.MyService.Redis().Set(id, "{\"id\"\""+id+"\",\"state\":false,\"message\":\""+err.Error()+"\",\"speed\":100}", 100)
// installLog.State = 0
// installLog.Speed = 100
// installLog.Message = err.Error()
// service.MyService.Notify().UpdateLog(installLog)
// return
// } else {
// //service.MyService.Redis().Set(id, "{\"id\":\""+id+"\",\"state\":true,\"message\":\"installed\",\"speed\":100}", 100)
// installLog.Speed = 100
// installLog.Message = "installed"
// service.MyService.Notify().UpdateLog(installLog)
// }
//
// rely := model.MapStrings{}
//
// copier.Copy(&rely, &relyMap)
//
// //step: 保存数据到数据库
// md := model2.AppListDBModel{
// CustomId: id,
// Title: m.Label,
// // ScreenshotLink: []string,
// Slogan: "",
// Description: m.Description,
// // Tags: ,
// Icon: m.Icon,
// Version: m.Image,
// ContainerId: containerId,
// Image: m.Image,
// Index: "",
// Port: m.Port,
// PortMap: m.PortMap,
// Label: m.Label,
// EnableUPNP: m.EnableUPNP,
// UdpPorts: m.Udp,
// TcpPorts: m.Tcp,
// Envs: m.Envs,
// Volumes: m.Volumes,
// Position: m.Position,
// NetModel: m.NetworkModel,
// Restart: m.Restart,
// CpuShares: m.CpuShares,
// Memory: m.Memory,
// Devices: m.Devices,
// Rely: rely,
// Origin: "custom",
// }
// if m.NetworkModel == "host" {
// m.PortMap = m.Port
// }
// service.MyService.App().SaveContainer(md)
//
// }()
//
// c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: id})
//
//}
// @Summary 卸载app
// @Produce application/json
// @Accept multipart/form-data
// @Tags app
// @Param id path string true "容器id"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/uninstall/{id} [delete]
func UnInstallApp(c *gin.Context) {
appId := c.Param("id")
if len(appId) == 0 {
c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
return
}
//info := service.MyService.App().GetUninstallInfo(appId)
info, err := service.MyService.Docker().DockerContainerInfo(appId)
if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
return
}
//step停止容器
err = service.MyService.Docker().DockerContainerStop(appId)
if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.UNINSTALL_APP_ERROR, Message: common_err.GetMsg(common_err.UNINSTALL_APP_ERROR), Data: err.Error()})
return
}
err = service.MyService.Docker().DockerContainerRemove(appId, false)
if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.UNINSTALL_APP_ERROR, Message: common_err.GetMsg(common_err.UNINSTALL_APP_ERROR), Data: err.Error()})
return
}
// stepremove image
service.MyService.Docker().DockerImageRemove(info.Config.Image)
if info.Config.Labels["origin"] != "custom" {
//step: 删除文件夹
for _, v := range info.Mounts {
if strings.Contains(v.Source, info.Name) {
path := filepath.Join(strings.Split(v.Source, info.Name)[0], info.Name)
service.MyService.App().DelAppConfigDir(path)
}
}
//step: 删除install log
//service.MyService.Notify().DelLog(appId)
// for k, v := range info.Rely {
//
// if k == "mysql" {
// docker_base.MysqlDelete(v)
// service.MyService.Rely().Delete(v)
// }
// }
//if info.EnableUPNP {
// upnp, err := upnp2.Gateway()
// if err == nil {
// for _, p := range info.Ports {
// if p.Protocol == "udp" {
// upnp.CtrlUrl = upnp2.GetCtrlUrl(upnp.GatewayHost, upnp.DeviceDescUrl)
// upnp.LocalHost = ip_helper2.GetLoclIp()
// tComment, _ := strconv.Atoi(p.CommendPort)
// upnp.DelPortMapping(tComment, "UDP")
// time.Sleep(time.Millisecond * 200)
// } else if p.Protocol == "tcp" {
// upnp.CtrlUrl = upnp2.GetCtrlUrl(upnp.GatewayHost, upnp.DeviceDescUrl)
// upnp.LocalHost = ip_helper2.GetLoclIp()
// tComment, _ := strconv.Atoi(p.CommendPort)
// upnp.DelPortMapping(tComment, "TCP")
// time.Sleep(time.Millisecond * 200)
// } else if p.Protocol == "both" {
// upnp.CtrlUrl = upnp2.GetCtrlUrl(upnp.GatewayHost, upnp.DeviceDescUrl)
// upnp.LocalHost = ip_helper2.GetLoclIp()
// tComment, _ := strconv.Atoi(p.CommendPort)
// upnp.DelPortMapping(tComment, "UDP")
//
// upnp.DelPortMapping(tComment, "TCP")
// time.Sleep(time.Millisecond * 200)
// }
// }
// }
//}
}
config.CasaOSGlobalVariables.AppChange = true
notify := notify.Application{}
notify.Icon = info.Config.Labels["icon"]
notify.Name = strings.ReplaceAll(info.Name, "/", "")
notify.State = "FINISHED"
notify.Type = "UNINSTALL"
notify.Success = true
notify.Finished = true
service.MyService.Notify().SendUninstallAppBySocket(notify)
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
}
// @Summary 修改app状态
// @Produce application/json
// @Accept multipart/form-data
// @Tags app
// @Param id path string true "appid"
// @Param state query string false "是否停止 start stop restart"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/state/{id} [put]
func ChangAppState(c *gin.Context) {
appId := c.Param("id")
js := make(map[string]string)
c.ShouldBind(&js)
state := js["state"]
if len(appId) == 0 || len(state) == 0 {
c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
return
}
var err error
if state == "start" {
err = service.MyService.Docker().DockerContainerStart(appId)
} else if state == "restart" {
service.MyService.Docker().DockerContainerStop(appId)
err = service.MyService.Docker().DockerContainerStart(appId)
} else {
err = service.MyService.Docker().DockerContainerStop(appId)
}
if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
return
}
info, err := service.MyService.App().GetContainerInfo(appId)
if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
return
}
// @tiger - 用 {'state': ...} 来体现出参上下文
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: info.State})
}
// @Summary 查看容器日志
// @Produce application/json
// @Accept application/json
// @Tags app
// @Param id path string true "appid"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/logs/{id} [get]
func ContainerLog(c *gin.Context) {
appId := c.Param("id")
log, _ := service.MyService.Docker().DockerContainerLog(appId)
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: string(log)})
}
// @Summary 获取容器状态
// @Produce application/json
// @Accept application/json
// @Tags app
// @Param id path string true "容器id"
// @Param type query string false "type=1"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/state/{id} [get]
func GetContainerState(c *gin.Context) {
id := c.Param("id")
//t := c.DefaultQuery("type", "0")
containerInfo, e := service.MyService.App().GetSimpleContainerInfo(id)
if e != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: e.Error()})
return
}
var data = make(map[string]interface{})
data["state"] = containerInfo.State
// if t == "1" {
// appInfo := service.MyService.App().GetAppDBInfo(id)
// data["app"] = appInfo
// }
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data})
}
// @Summary 更新设置
// @Produce application/json
// @Accept multipart/form-data
// @Tags app
// @Param id path string true "容器id"
// @Param shares formData string false "cpu权重"
// @Param mem formData string false "内存大小MB"
// @Param restart formData string false "重启策略"
// @Param label formData string false "应用名称"
// @Param position formData bool true "是否放到首页"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/update/{id}/setting [put]
func UpdateSetting(c *gin.Context) {
id := c.Param("id")
const CUSTOM = "custom"
m := model.CustomizationPostData{}
c.ShouldBind(&m)
if len(id) == 0 {
c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
return
}
//var cpd model.CustomizationPostData
//copier.Copy(&cpd, &m)
//appInfo := service.MyService.App().GetAppDBInfo(id)
//info, err := service.MyService.Docker().DockerContainerInfo(id)
// //check app name is exist
// if _, err := service.MyService.Docker().DockerListByName(m.Label); err == nil {
// c.JSON(http.StatusOK, model.Result{Success: common_err.ERROR_APP_NAME_EXIST, Message: common_err.GetMsg(common_err.ERROR_APP_NAME_EXIST)})
// return
// }
service.MyService.Docker().DockerContainerStop(id)
portMap, _ := strconv.Atoi(m.PortMap)
if !port2.IsPortAvailable(portMap, "tcp") {
service.MyService.Docker().DockerContainerStart(id)
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: "Duplicate port:" + m.PortMap})
return
}
for _, u := range m.Ports {
if u.Protocol == "udp" {
t, _ := strconv.Atoi(u.CommendPort)
if !port2.IsPortAvailable(t, "udp") {
service.MyService.Docker().DockerContainerStart(id)
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: "Duplicate port:" + u.CommendPort})
return
}
} else if u.Protocol == "tcp" {
te, _ := strconv.Atoi(u.CommendPort)
if !port2.IsPortAvailable(te, "tcp") {
service.MyService.Docker().DockerContainerStart(id)
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: "Duplicate port:" + u.CommendPort})
return
}
} else if u.Protocol == "both" {
t, _ := strconv.Atoi(u.CommendPort)
if !port2.IsPortAvailable(t, "udp") {
service.MyService.Docker().DockerContainerStart(id)
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: "Duplicate port:" + u.CommendPort})
return
}
te, _ := strconv.Atoi(u.CommendPort)
if !port2.IsPortAvailable(te, "tcp") {
service.MyService.Docker().DockerContainerStart(id)
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: "Duplicate port:" + u.CommendPort})
return
}
}
}
service.MyService.Docker().DockerContainerUpdateName(id, id)
//service.MyService.Docker().DockerContainerRemove(id, true)
containerId, err := service.MyService.Docker().DockerContainerCreate(m, id)
if err != nil {
service.MyService.Docker().DockerContainerUpdateName(m.ContainerName, id)
service.MyService.Docker().DockerContainerStart(id)
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
return
}
// echo -e "hellow\nworld" >>
//step启动容器
err = service.MyService.Docker().DockerContainerStart(containerId)
if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR)})
return
}
service.MyService.Docker().DockerContainerRemove(id, true)
//更新upnp
if m.Origin != CUSTOM {
//if appInfo.EnableUPNP != appInfo.EnableUPNP {
// if appInfo.EnableUPNP {
// upnp, err := upnp2.Gateway()
// if err == nil {
//
// for _, p := range appInfo.Ports {
// if p.Protocol == "udp" {
// upnp.CtrlUrl = upnp2.GetCtrlUrl(upnp.GatewayHost, upnp.DeviceDescUrl)
// upnp.LocalHost = ip_helper2.GetLoclIp()
// tComment, _ := strconv.Atoi(p.CommendPort)
// upnp.AddPortMapping(tComment, tComment, "UDP")
// time.Sleep(time.Millisecond * 200)
// } else if p.Protocol == "tcp" {
// upnp.CtrlUrl = upnp2.GetCtrlUrl(upnp.GatewayHost, upnp.DeviceDescUrl)
// upnp.LocalHost = ip_helper2.GetLoclIp()
// tComment, _ := strconv.Atoi(p.CommendPort)
// upnp.AddPortMapping(tComment, tComment, "TCP")
// time.Sleep(time.Millisecond * 200)
// } else if p.Protocol == "both" {
// upnp.CtrlUrl = upnp2.GetCtrlUrl(upnp.GatewayHost, upnp.DeviceDescUrl)
// upnp.LocalHost = ip_helper2.GetLoclIp()
// tComment, _ := strconv.Atoi(p.CommendPort)
// upnp.AddPortMapping(tComment, tComment, "UDP")
// time.Sleep(time.Millisecond * 200)
//
// upnp.AddPortMapping(tComment, tComment, "TCP")
// time.Sleep(time.Millisecond * 200)
// }
// }
// }
// } else {
// upnp, err := upnp2.Gateway()
// if err == nil {
// for _, p := range appInfo.Ports {
// if p.Protocol == "udp" {
//
// upnp.CtrlUrl = upnp2.GetCtrlUrl(upnp.GatewayHost, upnp.DeviceDescUrl)
// upnp.LocalHost = ip_helper2.GetLoclIp()
// tComment, _ := strconv.Atoi(p.CommendPort)
// upnp.DelPortMapping(tComment, "UDP")
// time.Sleep(time.Millisecond * 200)
// } else if p.Protocol == "tcp" {
// upnp.CtrlUrl = upnp2.GetCtrlUrl(upnp.GatewayHost, upnp.DeviceDescUrl)
// upnp.LocalHost = ip_helper2.GetLoclIp()
// tComment, _ := strconv.Atoi(p.CommendPort)
// upnp.DelPortMapping(tComment, "TCP")
// time.Sleep(time.Millisecond * 200)
// } else if p.Protocol == "both" {
// upnp.CtrlUrl = upnp2.GetCtrlUrl(upnp.GatewayHost, upnp.DeviceDescUrl)
// upnp.LocalHost = ip_helper2.GetLoclIp()
// tComment, _ := strconv.Atoi(p.CommendPort)
// upnp.DelPortMapping(tComment, "UDP")
// time.Sleep(time.Millisecond * 200)
//
// upnp.DelPortMapping(tComment, "TCP")
// time.Sleep(time.Millisecond * 200)
// }
// }
// }
// }
//}
}
//service.MyService.App().UpdateApp(appInfo)
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
}
// @Summary update app version
// @Produce application/json
// @Accept multipart/form-data
// @Tags app
// @Param id path string true "容器id"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/update/{id} [put]
func PutAppUpdate(c *gin.Context) {
id := c.Param("id")
if len(id) == 0 {
c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
return
}
inspect, err := service.MyService.Docker().DockerContainerInfo(id)
if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
return
}
imageLatest := strings.Split(inspect.Config.Image, ":")[0] + ":latest"
err = service.MyService.Docker().DockerPullImage(imageLatest, "", "")
if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
return
}
service.MyService.Docker().DockerContainerStop(id)
service.MyService.Docker().DockerContainerUpdateName(id, id)
//service.MyService.Docker().DockerContainerRemove(id, true)
inspect.Image = imageLatest
inspect.Config.Image = imageLatest
containerId, err := service.MyService.Docker().DockerContainerCopyCreate(inspect)
if err != nil {
service.MyService.Docker().DockerContainerUpdateName(inspect.Name, id)
service.MyService.Docker().DockerContainerStart(id)
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR)})
return
}
//step启动容器
err = service.MyService.Docker().DockerContainerStart(containerId)
if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR)})
return
}
service.MyService.Docker().DockerContainerRemove(id, true)
delete(service.NewVersionApp, id)
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
}
// @Summary 获取容器详情
// @Produce application/json
// @Accept application/json
// @Tags app
// @Param id path string true "appid"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/info/{id} [get]
func ContainerInfo(c *gin.Context) {
appId := c.Param("id")
// @tiger - 作为最佳实践,不应该直接把数据库的信息返回,来避免未来数据库结构上的迭代带来的新字段
appInfo := service.MyService.App().GetAppDBInfo(appId)
containerInfo, _ := service.MyService.Docker().DockerContainerStats(appId)
var cpuModel = "arm"
if cpu := service.MyService.System().GetCpuInfo(); len(cpu) > 0 {
if strings.Count(strings.ToLower(strings.TrimSpace(cpu[0].ModelName)), "intel") > 0 {
cpuModel = "intel"
} else if strings.Count(strings.ToLower(strings.TrimSpace(cpu[0].ModelName)), "amd") > 0 {
cpuModel = "amd"
}
}
info, err := service.MyService.Docker().DockerContainerInfo(appId)
if err != nil {
//todo 需要自定义错误
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
return
}
con := struct {
Status string `json:"status"`
StartedAt string `json:"started_at"`
CPUShares int64 `json:"cpu_shares"`
Memory int64 `json:"total_memory"` // @tiger - 改成 total_memory方便以后增加 free_memory 之类的字段
Restart string `json:"restart_policy"` // @tiger - 改成 restart_policy?
}{Status: info.State.Status, StartedAt: info.State.StartedAt, CPUShares: info.HostConfig.CPUShares, Memory: info.HostConfig.Memory >> 20, Restart: info.HostConfig.RestartPolicy.Name}
data := make(map[string]interface{}, 5)
data["app"] = appInfo // @tiget - 最佳实践是,返回 appid然后具体的 app 信息由前端另行获取
data["cpu"] = cpuModel // @tiger - 改成 arch
data["memory"] = service.MyService.System().GetMemInfo()["total"] // @tiger - 改成 total_memory方便以后增加 free_memory 之类的字段
data["container"] = json2.RawMessage(containerInfo)
data["info"] = con
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data})
}
func GetDockerNetworks(c *gin.Context) {
networks := service.MyService.Docker().DockerNetworkModelList()
list := []map[string]string{}
for _, network := range networks {
if network.Driver != "null" {
list = append(list, map[string]string{"name": network.Name, "driver": network.Driver, "id": network.ID})
}
}
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: list})
}
// @Summary 获取依赖数据
// @Produce application/json
// @Accept application/json
// @Tags app
// @Param id path string true "rely id"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/rely/{id}/info [get]
func ContainerRelyInfo(c *gin.Context) {
id := c.Param("id")
appInfo := service.MyService.Rely().GetInfo(id)
c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: appInfo})
}
// @Produce application/json
// @Accept application/json
// @Tags app
// @Param id path string true "appid"
// @Security ApiKeyAuth
// @Success 200 {string} string "ok"
// @Router /app/update/{id}/info [get]
func ContainerUpdateInfo(c *gin.Context) {
appId := c.Param("id")
//appInfo := service.MyService.App().GetAppDBInfo(appId)
info, err := service.MyService.Docker().DockerContainerInfo(appId)
if err != nil {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: err.Error()})
return
}
var port model.PortArray
// json2.Unmarshal([]byte(appInfo.Ports), &port)
for k, v := range info.HostConfig.PortBindings {
temp := model.PortMap{
CommendPort: v[0].HostPort,
ContainerPort: k.Port(),
Protocol: k.Proto(),
}
port = append(port, temp)
}
var envs model.EnvArray
// json2.Unmarshal([]byte(appInfo.Envs), &envs)
showENV := info.Config.Labels["show_env"]
showENVList := strings.Split(showENV, ",")
showENVMap := make(map[string]string)
if len(showENVList) > 0 && showENVList[0] != "" {
for _, name := range showENVList {
showENVMap[name] = "1"
}
}
for _, v := range info.Config.Env {
if len(showENVList) > 0 && info.Config.Labels["origin"] != "local" {
if _, ok := showENVMap[strings.Split(v, "=")[0]]; ok {
temp := model.Env{
Name: strings.Split(v, "=")[0],
Value: strings.Split(v, "=")[1],
}
envs = append(envs, temp)
}
} else {
temp := model.Env{
Name: strings.Split(v, "=")[0],
Value: strings.Split(v, "=")[1],
}
envs = append(envs, temp)
}
}
var vol model.PathArray
// json2.Unmarshal([]byte(appInfo.Volumes), &vol)
for i := 0; i < len(info.Mounts); i++ {
temp := model.PathMap{
Path: strings.ReplaceAll(info.Mounts[i].Source, "$AppID", info.Name),
ContainerPath: info.Mounts[i].Destination,
}
vol = append(vol, temp)
}
var driver model.PathArray
//volumesStr, _ := json2.Marshal(m.Volumes)
//devicesStr, _ := json2.Marshal(m.Devices)
for _, v := range info.HostConfig.Resources.Devices {
temp := model.PathMap{
Path: v.PathOnHost,
ContainerPath: v.PathInContainer,
}
driver = append(driver, temp)
}
m := model.CustomizationPostData{}
m.Icon = info.Config.Labels["icon"]
m.Ports = port
m.Image = info.Config.Image
m.Origin = info.Config.Labels["origin"]
if len(m.Origin) == 0 {
m.Origin = "local"
}
m.NetworkModel = string(info.HostConfig.NetworkMode)
m.Description = info.Config.Labels["desc"]
m.ContainerName = strings.ReplaceAll(info.Name, "/", "")
m.PortMap = info.Config.Labels["web"]
m.Devices = driver
m.Envs = envs
m.Memory = info.HostConfig.Memory >> 20
m.CpuShares = info.HostConfig.CPUShares
m.Volumes = vol //appInfo.Volumes
m.Restart = info.HostConfig.RestartPolicy.Name
m.EnableUPNP = false
m.Index = info.Config.Labels["index"]
m.Position = false
m.CustomId = info.Config.Labels["custom_id"]
m.Host = info.Config.Labels["host"]
if len(m.CustomId) == 0 {
m.CustomId = uuid.NewV4().String()
}
m.CapAdd = info.HostConfig.CapAdd
m.Cmd = info.Config.Cmd
m.HostName = info.Config.Hostname
m.Privileged = info.HostConfig.Privileged
name := info.Config.Labels["name"]
if len(name) == 0 {
name = strings.ReplaceAll(info.Name, "/", "")
}
m.Label = name
m.Protocol = info.Config.Labels["protocol"]
if m.Protocol == "" {
m.Protocol = "http"
}
c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: m})
}
////准备安装(暂时不需要)
//func ReadyInstall(c *gin.Context) {
// _, tcp, udp := service.MyService.GetManifestJsonByRepo()
// data := make(map[string]interface{}, 2)
// if t := gjson.Parse(tcp).Array(); len(t) > 0 {
// //tcpList := []model.TcpPorts{}
// //e := json2.Unmarshal([]byte(tcp), tcpList)
// //if e!=nil {
// // return
// //}
// //for _, port := range tcpList {
// // if port.ContainerPort>0&&port.ExtranetPort {
// //
// // }
// //}
// var inarr []interface{}
// for _, result := range t {
//
// var p int
// ok := true
// for ok {
// p, _ = port.GetAvailablePort()
// ok = !port.IsPortAvailable(p)
// }
// pm := model.PortMap{gjson.Get(result.Raw, "container_port").Int(), p}
// inarr = append(inarr, pm)
// }
// data["tcp"] = inarr
// }
// if u := gjson.Parse(udp).Array(); len(u) > 0 {
// //udpList := []model.UdpPorts{}
// //e := json2.Unmarshal([]byte(udp), udpList)
// //if e != nil {
// // return
// //}
// var inarr []model.PortMap
// for _, result := range u {
// var p int
// ok := true
// for ok {
// p, _ = port.GetAvailablePort()
// ok = !port.IsPortAvailable(p)
// }
// pm := model.PortMap{gjson.Get(result.Raw, "container_port").Int(), p}
// inarr = append(inarr, pm)
// }
// data["udp"] = inarr
// }
// c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data})
//}