Skip to content

Commit 6442703

Browse files
committed
swarm
1 parent fae3a1a commit 6442703

File tree

11 files changed

+352
-198
lines changed

11 files changed

+352
-198
lines changed

app/application/http/controller/compose.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ func (self Compose) GetTask(http *gin.Context) {
306306

307307
tasker, err := logic.Compose{}.GetTasker(yamlRow)
308308
if err != nil {
309+
slog.Debug("compose get task", "err", err)
309310
// 如果获取任务失败,可能是没有文件或是Yaml文件错误,直接返回内容待用户修改
310311
yaml, err := yamlRow.Setting.GetYaml()
311312
if err != nil {
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
package controller
2+
3+
import (
4+
"fmt"
5+
"github.com/docker/docker/api/types"
6+
"github.com/docker/docker/api/types/network"
7+
"github.com/docker/docker/api/types/swarm"
8+
"github.com/donknap/dpanel/app/common/logic"
9+
"github.com/donknap/dpanel/common/function"
10+
"github.com/donknap/dpanel/common/service/docker"
11+
"github.com/gin-gonic/gin"
12+
"log/slog"
13+
"net"
14+
"time"
15+
)
16+
17+
func (self Swarm) NodeList(http *gin.Context) {
18+
list, err := docker.Sdk.Client.NodeList(docker.Sdk.Ctx, types.NodeListOptions{})
19+
if err != nil {
20+
self.JsonResponseWithError(http, err, 500)
21+
return
22+
}
23+
self.JsonResponseWithoutError(http, gin.H{
24+
"list": list,
25+
})
26+
return
27+
}
28+
29+
func (self Swarm) NodeUpdate(http *gin.Context) {
30+
type ParamsValidate struct {
31+
NodeId string `json:"nodeId" binding:"required"`
32+
Availability swarm.NodeAvailability `json:"availability" binding:"omitempty,oneof=active pause drain"`
33+
Role swarm.NodeRole `json:"role" binding:"omitempty,oneof=worker manager"`
34+
Labels []docker.ValueItem `json:"labels"`
35+
}
36+
params := ParamsValidate{}
37+
if !self.Validate(http, &params) {
38+
return
39+
}
40+
node, _, err := docker.Sdk.Client.NodeInspectWithRaw(docker.Sdk.Ctx, params.NodeId)
41+
if err != nil {
42+
self.JsonResponseWithError(http, err, 500)
43+
return
44+
}
45+
if params.Availability != "" {
46+
node.Spec.Availability = params.Availability
47+
}
48+
if params.Role != "" {
49+
node.Spec.Role = params.Role
50+
}
51+
if !function.IsEmptyArray(params.Labels) {
52+
node.Spec.Labels = function.PluckArrayMapWalk(params.Labels, func(item docker.ValueItem) (string, string, bool) {
53+
return item.Name, item.Value, true
54+
})
55+
}
56+
err = docker.Sdk.Client.NodeUpdate(docker.Sdk.Ctx, params.NodeId, node.Version, node.Spec)
57+
if err != nil {
58+
self.JsonResponseWithError(http, err, 500)
59+
return
60+
}
61+
self.JsonResponseWithoutError(http, gin.H{
62+
"detail": node,
63+
})
64+
return
65+
}
66+
67+
func (self Swarm) NodeJoin(http *gin.Context) {
68+
type ParamsValidate struct {
69+
DockerEnvName string `json:"dockerEnvName" binding:"required"`
70+
Type string `json:"type" binding:"required,oneof=add join"`
71+
Role swarm.NodeRole `json:"role" binding:"omitempty,oneof=worker manager"`
72+
ListenAddr string `json:"listenAddr" binding:"required"`
73+
}
74+
params := ParamsValidate{}
75+
if !self.Validate(http, &params) {
76+
return
77+
}
78+
dockerEnv, err := logic.DockerEnv{}.GetEnvByName(params.DockerEnvName)
79+
if err != nil {
80+
self.JsonResponseWithError(http, err, 500)
81+
return
82+
}
83+
dockerClient, err := docker.NewBuilderWithDockerEnv(dockerEnv)
84+
if err != nil {
85+
self.JsonResponseWithError(http, err, 500)
86+
return
87+
}
88+
defer func() {
89+
dockerClient.Close()
90+
}()
91+
92+
var swarmDockerClient *docker.Builder
93+
var clientDockerClient *docker.Builder
94+
95+
if params.Type == "join" {
96+
// join 是将当前环境添加到目标集群节点
97+
swarmDockerClient = dockerClient
98+
clientDockerClient = docker.Sdk
99+
} else {
100+
swarmDockerClient = docker.Sdk
101+
clientDockerClient = dockerClient
102+
}
103+
swarmDockerInfo, err := swarmDockerClient.Client.Info(swarmDockerClient.Ctx)
104+
if err != nil {
105+
self.JsonResponseWithError(http, err, 500)
106+
return
107+
}
108+
if swarmDockerInfo.Swarm.LocalNodeState == swarm.LocalNodeStateInactive {
109+
self.JsonResponseWithError(http, function.ErrorMessage(".swarmNotInit"), 500)
110+
return
111+
}
112+
if !swarmDockerInfo.Swarm.ControlAvailable {
113+
self.JsonResponseWithError(http, function.ErrorMessage(".swarmNotManager"), 500)
114+
return
115+
}
116+
swarmInfo, err := swarmDockerClient.Client.SwarmInspect(swarmDockerClient.Ctx)
117+
if err != nil {
118+
self.JsonResponseWithError(http, err, 500)
119+
return
120+
}
121+
swarmManageNode, _, err := swarmDockerClient.Client.NodeInspectWithRaw(swarmDockerClient.Ctx, swarmDockerInfo.Swarm.NodeID)
122+
if err != nil {
123+
self.JsonResponseWithError(http, err, 500)
124+
return
125+
}
126+
127+
joinRequest := swarm.JoinRequest{
128+
ListenAddr: params.ListenAddr,
129+
AdvertiseAddr: swarmManageNode.ManagerStatus.Addr,
130+
Availability: swarm.NodeAvailabilityActive,
131+
RemoteAddrs: function.PluckArrayWalk(swarmDockerInfo.Swarm.RemoteManagers, func(item swarm.Peer) (string, bool) {
132+
return item.Addr, true
133+
}),
134+
}
135+
if ip, _, err := net.SplitHostPort(joinRequest.AdvertiseAddr); err == nil {
136+
joinRequest.DataPathAddr = ip
137+
}
138+
139+
if params.Role == swarm.NodeRoleManager {
140+
joinRequest.JoinToken = swarmInfo.JoinTokens.Manager
141+
} else {
142+
joinRequest.JoinToken = swarmInfo.JoinTokens.Worker
143+
}
144+
145+
err = clientDockerClient.Client.SwarmJoin(clientDockerClient.Ctx, joinRequest)
146+
if err != nil {
147+
self.JsonResponseWithError(http, err, 500)
148+
return
149+
}
150+
151+
leave := func(nodeId string) {
152+
_ = clientDockerClient.Client.SwarmLeave(clientDockerClient.Ctx, true)
153+
if nodeId != "" {
154+
_ = swarmDockerClient.Client.NodeRemove(swarmDockerClient.Ctx, nodeId, types.NodeRemoveOptions{})
155+
}
156+
}
157+
158+
clientDockerInfo, err := clientDockerClient.Client.Info(clientDockerClient.Ctx)
159+
if err != nil {
160+
leave("")
161+
self.JsonResponseWithError(http, err, 500)
162+
return
163+
}
164+
node, _, err := docker.Sdk.Client.NodeInspectWithRaw(docker.Sdk.Ctx, clientDockerInfo.Swarm.NodeID)
165+
if err != nil {
166+
self.JsonResponseWithError(http, err, 500)
167+
return
168+
}
169+
for i := 0; i < 5; i++ {
170+
if _, err := clientDockerClient.Client.NetworkInspect(clientDockerClient.Ctx, "ingress", network.InspectOptions{
171+
Scope: "swarm",
172+
}); err == nil {
173+
// 增加一条 envname label
174+
if node.Spec.Labels == nil {
175+
node.Spec.Labels = make(map[string]string)
176+
}
177+
node.Spec.Labels["com.dpanel.swarm.nodeEnvName"] = params.DockerEnvName
178+
err = swarmDockerClient.Client.NodeUpdate(swarmDockerClient.Ctx, node.ID, node.Version, node.Spec)
179+
if err != nil {
180+
slog.Debug("swarm node update label", "type", params.Type, "err", err)
181+
}
182+
self.JsonResponseWithoutError(http, gin.H{
183+
"id": clientDockerInfo.Swarm.NodeID,
184+
})
185+
return
186+
}
187+
time.Sleep(time.Second)
188+
}
189+
190+
leave(clientDockerInfo.Swarm.NodeID)
191+
192+
self.JsonResponseWithoutError(http, gin.H{
193+
"id": "",
194+
"cmd": fmt.Sprintf("docker swarm join --token %s %s", joinRequest.JoinToken, joinRequest.AdvertiseAddr),
195+
})
196+
return
197+
}
198+
199+
func (self Swarm) NodeRemove(http *gin.Context) {
200+
type ParamsValidate struct {
201+
NodeId string `json:"nodeId"`
202+
Force bool `json:"force"`
203+
}
204+
params := ParamsValidate{}
205+
if !self.Validate(http, &params) {
206+
return
207+
}
208+
info, err := docker.Sdk.Client.Info(docker.Sdk.Ctx)
209+
if err != nil {
210+
self.JsonResponseWithError(http, err, 500)
211+
return
212+
}
213+
if info.Swarm.ControlAvailable && params.NodeId != "" {
214+
err := docker.Sdk.Client.NodeRemove(docker.Sdk.Ctx, params.NodeId, types.NodeRemoveOptions{
215+
Force: params.Force,
216+
})
217+
if err != nil {
218+
self.JsonResponseWithError(http, err, 500)
219+
return
220+
}
221+
} else {
222+
err := docker.Sdk.Client.SwarmLeave(docker.Sdk.Ctx, params.Force)
223+
if err != nil {
224+
self.JsonResponseWithError(http, err, 500)
225+
return
226+
}
227+
}
228+
self.JsonSuccessResponse(http)
229+
return
230+
}
231+
232+
func (self Swarm) NodePrune(http *gin.Context) {
233+
if nodeList, err := docker.Sdk.Client.NodeList(docker.Sdk.Ctx, types.NodeListOptions{}); err == nil {
234+
for _, node := range nodeList {
235+
if node.Status.State == swarm.NodeStateDown {
236+
_ = docker.Sdk.Client.NodeRemove(docker.Sdk.Ctx, node.ID, types.NodeRemoveOptions{})
237+
}
238+
}
239+
}
240+
self.JsonSuccessResponse(http)
241+
return
242+
}

app/application/http/controller/swarm-service.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,41 @@ package controller
22

33
import (
44
"github.com/docker/docker/api/types"
5+
"github.com/docker/docker/api/types/filters"
56
"github.com/donknap/dpanel/common/service/docker"
67
"github.com/gin-gonic/gin"
8+
"sort"
79
)
810

911
func (self Swarm) ServiceList(http *gin.Context) {
1012
type ParamsValidate struct {
11-
NodeName string `json:"nodeName"`
13+
Name string `json:"name"`
1214
}
13-
list, err := docker.Sdk.Client.ServiceList(docker.Sdk.Ctx, types.ServiceListOptions{
14-
Status: true,
15+
params := ParamsValidate{}
16+
if !self.Validate(http, &params) {
17+
return
18+
}
19+
filter := filters.NewArgs()
20+
if params.Name != "" {
21+
filter.Add("name", params.Name)
22+
}
23+
serviceList, err := docker.Sdk.Client.ServiceList(docker.Sdk.Ctx, types.ServiceListOptions{
24+
Status: true,
25+
Filters: filter,
1526
})
1627
if err != nil {
1728
self.JsonResponseWithError(http, err, 500)
1829
return
1930
}
31+
sort.Slice(serviceList, func(i, j int) bool {
32+
return serviceList[i].Spec.Name < serviceList[j].Spec.Name
33+
})
2034
self.JsonResponseWithoutError(http, gin.H{
21-
"list": list,
35+
"list": serviceList,
2236
})
2337
return
2438
}
39+
40+
func (self Swarm) ServiceUpdate(gin *gin.Context) {
41+
42+
}

app/application/http/controller/swarm-task.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,24 @@ import (
77
"github.com/donknap/dpanel/common/function"
88
"github.com/donknap/dpanel/common/service/docker"
99
"github.com/gin-gonic/gin"
10+
"sort"
1011
)
1112

1213
func (self Swarm) TaskList(http *gin.Context) {
1314
type ParamsValidate struct {
14-
NodeName string `json:"nodeName"`
15+
ServiceName string `json:"serviceName"`
16+
Status string `json:"status"`
1517
}
1618
params := ParamsValidate{}
1719
if !self.Validate(http, &params) {
1820
return
1921
}
2022
filter := filters.NewArgs()
21-
if params.NodeName != "" {
22-
filter.Add("node", params.NodeName)
23+
if params.ServiceName != "" {
24+
filter.Add("service", params.ServiceName)
25+
}
26+
if params.Status != "" {
27+
filter.Add("desired-state", params.Status)
2328
}
2429
list, err := docker.Sdk.Client.TaskList(docker.Sdk.Ctx, types.TaskListOptions{
2530
Filters: filter,
@@ -28,6 +33,9 @@ func (self Swarm) TaskList(http *gin.Context) {
2833
self.JsonResponseWithError(http, err, 500)
2934
return
3035
}
36+
sort.Slice(list, func(i, j int) bool {
37+
return list[i].CreatedAt.After(list[j].CreatedAt)
38+
})
3139
self.JsonResponseWithoutError(http, gin.H{
3240
"list": list,
3341
})
@@ -44,7 +52,7 @@ func (self Swarm) TaskListInNode(http *gin.Context) {
4452
}
4553
type resultItem struct {
4654
swarm.Service
47-
Children []swarm.Task `json:"Children"`
55+
Task []swarm.Task `json:"Task"`
4856
}
4957
result := make([]resultItem, 0)
5058

@@ -70,17 +78,23 @@ func (self Swarm) TaskListInNode(http *gin.Context) {
7078
self.JsonResponseWithError(http, err, 500)
7179
return
7280
}
81+
sort.Slice(serviceList, func(i, j int) bool {
82+
return serviceList[i].Spec.Name < serviceList[j].Spec.Name
83+
})
7384
for _, service := range serviceList {
7485
item := resultItem{
7586
Service: service,
76-
Children: function.PluckArrayWalk(taskList, func(item swarm.Task) (swarm.Task, bool) {
87+
Task: function.PluckArrayWalk(taskList, func(item swarm.Task) (swarm.Task, bool) {
7788
if item.ServiceID == service.ID {
7889
return item, true
7990
} else {
8091
return swarm.Task{}, false
8192
}
8293
}),
8394
}
95+
sort.Slice(item.Task, func(i, j int) bool {
96+
return item.Task[i].CreatedAt.After(item.Task[j].CreatedAt)
97+
})
8498
result = append(result, item)
8599
}
86100
self.JsonResponseWithoutError(http, gin.H{

0 commit comments

Comments
 (0)