Skip to content

Commit 162db38

Browse files
authored
feat: support udp ingress + container capabilities (#244) (#245)
1 parent 77f716a commit 162db38

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1096
-93
lines changed

cmd/config.cluster.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,17 @@ lets_encrypt:
2424
email_id: will_be_overridden
2525
account_private_key_path: /etc/swiftwave/letsencrypt/account.key
2626
haproxy:
27-
service_name: haproxy
27+
service_name: swiftwave_haproxy
2828
image: ghcr.io/swiftwave-org/haproxy:2.9
2929
unix_socket_path: /etc/swiftwave/haproxy/dataplaneapi.sock # should end with dataplaneapi.sock
3030
user: will_be_overridden
3131
password: will_be_overridden
3232
data_dir: /var/lib/swiftwave/haproxy
33+
udp_proxy:
34+
service_name: swiftwave_udp_proxy
35+
image: ghcr.io/swiftwave-org/udpproxy:latest
36+
unix_socket_path: /etc/swiftwave/udpproxy/api.sock
37+
data_dir: /var/lib/swiftwave/udpproxy
3338
postgresql:
3439
auto_start_local_postgres: false # true or false
3540
host: 127.0.0.1

cmd/config.standalone.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,17 @@ lets_encrypt:
2424
email_id: will_be_overridden
2525
account_private_key_path: /etc/swiftwave/letsencrypt/account.key
2626
haproxy:
27-
service_name: haproxy
27+
service_name: swiftwave_haproxy
2828
image: ghcr.io/swiftwave-org/haproxy:2.9
2929
unix_socket_path: /etc/swiftwave/haproxy/dataplaneapi.sock
3030
user: will_be_overridden
3131
password: will_be_overridden
3232
data_dir: /var/lib/swiftwave/haproxy
33+
udp_proxy:
34+
service_name: swiftwave_udp_proxy
35+
image: ghcr.io/swiftwave-org/udpproxy:latest
36+
unix_socket_path: /etc/swiftwave/udpproxy/api.sock
37+
data_dir: /var/lib/swiftwave/udpproxy
3338
postgresql:
3439
auto_start_local_postgres: true # true or false
3540
host: 127.0.0.1

cmd/haproxy.go

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cmd
33
import (
44
"errors"
55
"fmt"
6+
"github.com/swiftwave-org/swiftwave/swiftwave_service/core"
67
"github.com/swiftwave-org/swiftwave/system_config"
78
"io"
89
"net/http"
@@ -81,19 +82,49 @@ var haproxyStartCmd = &cobra.Command{
8182
printError("Error : " + err.Error())
8283
return
8384
}
85+
// Fetch hostname
86+
hostname, err := os.Hostname()
87+
if err != nil {
88+
printError("failed to fetch hostname")
89+
return
90+
}
91+
// Find out required ports
92+
ports := []uint{80, 443}
93+
dbClient, err := getDBClient()
94+
if err == nil {
95+
var ingressRules []core.IngressRule
96+
tx := dbClient.Select("port").Where("port IS NOT NULL").Where("protocol != udp").Find(&ingressRules)
97+
if tx.Error == nil {
98+
if ingressRules != nil {
99+
for _, ingressRule := range ingressRules {
100+
ports = append(ports, ingressRule.Port)
101+
}
102+
}
103+
}
104+
}
84105
// Start HAProxy service
85-
dockerCmd := exec.Command("docker", "service", "create",
106+
args1 := []string{
107+
"service", "create",
86108
"--name", systemConfig.HAProxyConfig.ServiceName,
87-
"--mode", "global",
109+
"--mode", "replicated",
110+
"--replicas", "1",
88111
"--network", systemConfig.ServiceConfig.NetworkName,
89-
"--mount", "type=bind,source="+systemConfig.HAProxyConfig.DataDir+",destination=/etc/haproxy",
90-
"--mount", "type=bind,source="+unixSocketMountDir+",destination=/home/",
91-
"--publish", "mode=host,target=80,published=80",
92-
"--publish", "mode=host,target=443,published=443",
93-
"--env", "ADMIN_USER="+systemConfig.HAProxyConfig.User,
94-
"--env", "ADMIN_PASSWORD="+systemConfig.HAProxyConfig.Password,
95-
"--env", "SWIFTWAVE_SERVICE_ENDPOINT="+systemConfig.ServiceConfig.AddressOfCurrentNode+":"+strconv.Itoa(systemConfig.ServiceConfig.BindPort),
96-
dockerImage)
112+
"--constraint", "node.hostname==" + hostname,
113+
"--mount", "type=bind,source=" + systemConfig.HAProxyConfig.DataDir + ",destination=/etc/haproxy",
114+
"--mount", "type=bind,source=" + unixSocketMountDir + ",destination=/home/",
115+
}
116+
args2 := make([]string, 0, len(ports))
117+
for _, port := range ports {
118+
args2 = append(args2, "--publish", "mode=host,protocol=tcp,target="+strconv.Itoa(int(port))+",published="+strconv.Itoa(int(port)))
119+
}
120+
args3 := []string{
121+
"--env", "ADMIN_USER=" + systemConfig.HAProxyConfig.User,
122+
"--env", "ADMIN_PASSWORD=" + systemConfig.HAProxyConfig.Password,
123+
"--env", "SWIFTWAVE_SERVICE_ENDPOINT=" + systemConfig.ServiceConfig.AddressOfCurrentNode + ":" + strconv.Itoa(systemConfig.ServiceConfig.BindPort),
124+
dockerImage,
125+
}
126+
finalArgs := append(append(args1, args2...), args3...)
127+
dockerCmd := exec.Command("docker", finalArgs...)
97128
dockerCmd.Stdout = os.Stdout
98129
dockerCmd.Stderr = os.Stderr
99130
dockerCmd.Stdin = os.Stdin

cmd/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ func init() {
2525
rootCmd.AddCommand(deleteUserCmd)
2626
rootCmd.AddCommand(startCmd)
2727
rootCmd.AddCommand(haproxyCmd)
28+
rootCmd.AddCommand(udpProxyCmd)
2829
rootCmd.AddCommand(postgresCmd)
2930
rootCmd.AddCommand(generateTLSCommand)
3031
rootCmd.AddCommand(dbMigrateCmd)

cmd/setup.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,19 @@ var setupCmd = &cobra.Command{
5151
printSuccess("Created HAProxy unix socket directory [" + dir + "]")
5252
}
5353
}
54+
// Create udp_proxy.unix_socket_path base directory if it doesn't exist
55+
dir = systemConfig.UDPProxyConfig.UnixSocketPath
56+
dir = filepath.Dir(dir)
57+
if checkIfFolderExists(dir) {
58+
printSuccess("UDP Proxy unix socket directory [" + dir + "] already exists")
59+
} else {
60+
err := createFolder(dir)
61+
if err != nil {
62+
printError("Failed to create UDP Proxy unix socket directory [" + dir + "]")
63+
} else {
64+
printSuccess("Created UDP Proxy unix socket directory [" + dir + "]")
65+
}
66+
}
5467
// Create service.data_dir if it doesn't exist
5568
dir = systemConfig.ServiceConfig.DataDir
5669
if checkIfFolderExists(dir) {
@@ -87,6 +100,18 @@ var setupCmd = &cobra.Command{
87100
printSuccess("Created HAProxy SSL directory [" + dir + "]")
88101
}
89102
}
103+
// Create udp_proxy.data_dir if it doesn't exist
104+
dir = systemConfig.UDPProxyConfig.DataDir
105+
if checkIfFolderExists(dir) {
106+
printSuccess("UDP Proxy data directory [" + dir + "] already exists")
107+
} else {
108+
err := createFolder(dir)
109+
if err != nil {
110+
printError("Failed to create UDP Proxy data directory [" + dir + "]")
111+
} else {
112+
printSuccess("Created UDP Proxy data directory [" + dir + "]")
113+
}
114+
}
90115
// Create the swarm network if it doesn't exist
91116
dockerManager, err := containermanger.NewDockerManager(systemConfig.ServiceConfig.DockerUnixSocketPath)
92117
if err != nil {

cmd/start.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ package cmd
22

33
import (
44
"github.com/spf13/cobra"
5+
containermanger "github.com/swiftwave-org/swiftwave/container_manager"
56
swiftwave "github.com/swiftwave-org/swiftwave/swiftwave_service"
7+
"os"
8+
"os/exec"
69
)
710

811
var startCmd = &cobra.Command{
@@ -11,6 +14,51 @@ var startCmd = &cobra.Command{
1114
Long: `Start swiftwave service`,
1215
Run: func(cmd *cobra.Command, args []string) {
1316
systemConfig.IsDevelopmentMode = isDevelopmentMode(cmd)
17+
binaryPath, err := os.Executable()
18+
if err != nil {
19+
printError("Failed to get swiftwave binary path")
20+
return
21+
}
22+
if !isHaproxyRunning() {
23+
printInfo("Starting HAProxy service")
24+
c := exec.Command(binaryPath, "haproxy", "start")
25+
c.Stdout = os.Stdout
26+
c.Stderr = os.Stderr
27+
if err := c.Run(); err != nil {
28+
printError("Failed to start HAProxy service")
29+
return
30+
}
31+
}
32+
if !isUDPProxyRunning() {
33+
printInfo("Starting UDPProxy service")
34+
c := exec.Command(binaryPath, "udpproxy", "start")
35+
c.Stdout = os.Stdout
36+
c.Stderr = os.Stderr
37+
if err := c.Run(); err != nil {
38+
printError("Failed to start UDPProxy service")
39+
return
40+
}
41+
}
1442
swiftwave.Start(systemConfig)
1543
},
1644
}
45+
46+
func isHaproxyRunning() bool {
47+
dockerManager, err := containermanger.NewDockerManager(systemConfig.ServiceConfig.DockerUnixSocketPath)
48+
if err != nil {
49+
printError("Failed to connect to docker daemon")
50+
return false
51+
}
52+
_, err = dockerManager.GetService(systemConfig.HAProxyConfig.ServiceName)
53+
return err == nil
54+
}
55+
56+
func isUDPProxyRunning() bool {
57+
dockerManager, err := containermanger.NewDockerManager(systemConfig.ServiceConfig.DockerUnixSocketPath)
58+
if err != nil {
59+
printError("Failed to connect to docker daemon")
60+
return false
61+
}
62+
_, err = dockerManager.GetService(systemConfig.UDPProxyConfig.ServiceName)
63+
return err == nil
64+
}

cmd/udp_proxy.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package cmd
2+
3+
import (
4+
"github.com/fatih/color"
5+
"github.com/spf13/cobra"
6+
containermanger "github.com/swiftwave-org/swiftwave/container_manager"
7+
"github.com/swiftwave-org/swiftwave/swiftwave_service/core"
8+
"os"
9+
"os/exec"
10+
"path/filepath"
11+
"strconv"
12+
)
13+
14+
func init() {
15+
udpProxyCmd.AddCommand(udpProxyStatusCmd)
16+
udpProxyCmd.AddCommand(udpProxyStartCmd)
17+
udpProxyCmd.AddCommand(udpProxyStopCmd)
18+
}
19+
20+
var udpProxyCmd = &cobra.Command{
21+
Use: "udpproxy",
22+
Short: "Manage UDP Proxy service",
23+
Long: "Manage UDP Proxy service",
24+
}
25+
26+
// Start command
27+
var udpProxyStartCmd = &cobra.Command{
28+
Use: "start",
29+
Short: "Start UDP Proxy service",
30+
Long: "Start UDP Proxy service",
31+
Run: func(cmd *cobra.Command, args []string) {
32+
// Delete socket file if it already exists
33+
if checkIfFileExists(systemConfig.UDPProxyConfig.UnixSocketPath) {
34+
err := os.Remove(systemConfig.UDPProxyConfig.UnixSocketPath)
35+
if err != nil {
36+
printError("Failed to remove socket file > " + systemConfig.UDPProxyConfig.UnixSocketPath)
37+
return
38+
}
39+
}
40+
dockerImage := systemConfig.UDPProxyConfig.DockerImage
41+
// base directory for socket file
42+
unixSocketMountDir := filepath.Dir(systemConfig.UDPProxyConfig.UnixSocketPath)
43+
// Fetch hostname
44+
hostname, err := os.Hostname()
45+
if err != nil {
46+
printError("failed to fetch hostname")
47+
return
48+
}
49+
// Find out required ports
50+
ports := []uint{}
51+
dbClient, err := getDBClient()
52+
if err == nil {
53+
var ingressRules []core.IngressRule
54+
tx := dbClient.Select("port").Where("port IS NOT NULL").Where("protocol = ?", "udp").Find(&ingressRules)
55+
if tx.Error == nil {
56+
if ingressRules != nil {
57+
for _, ingressRule := range ingressRules {
58+
ports = append(ports, ingressRule.Port)
59+
}
60+
}
61+
}
62+
}
63+
// Start HAProxy service
64+
args1 := []string{
65+
"service", "create",
66+
"--name", systemConfig.UDPProxyConfig.ServiceName,
67+
"--mode", "replicated",
68+
"--replicas", "1",
69+
"--network", systemConfig.ServiceConfig.NetworkName,
70+
"--constraint", "node.hostname==" + hostname,
71+
"--mount", "type=bind,source=" + unixSocketMountDir + ",destination=/etc/udpproxy",
72+
"--mount", "type=bind,source=" + systemConfig.UDPProxyConfig.DataDir + ",destination=/var/lib/udpproxy",
73+
}
74+
args2 := make([]string, 0, len(ports))
75+
for _, port := range ports {
76+
args2 = append(args2, "--publish", "mode=host,protocol=udp,target="+strconv.Itoa(int(port))+",published="+strconv.Itoa(int(port)))
77+
}
78+
args3 := []string{
79+
"--env", "SWIFTWAVE_SERVICE_ENDPOINT=" + systemConfig.ServiceConfig.AddressOfCurrentNode + ":" + strconv.Itoa(systemConfig.ServiceConfig.BindPort),
80+
dockerImage,
81+
}
82+
finalArgs := append(append(args1, args2...), args3...)
83+
dockerCmd := exec.Command("docker", finalArgs...)
84+
dockerCmd.Stdout = os.Stdout
85+
dockerCmd.Stderr = os.Stderr
86+
dockerCmd.Stdin = os.Stdin
87+
err = dockerCmd.Run()
88+
if err != nil {
89+
printError("Failed to start UDP Proxy service")
90+
return
91+
}
92+
printSuccess("Started UDP Proxy service")
93+
},
94+
}
95+
96+
// Stop command
97+
var udpProxyStopCmd = &cobra.Command{
98+
Use: "stop",
99+
Short: "Stop UDP Proxy service",
100+
Long: "Stop UDP Proxy service",
101+
Run: func(cmd *cobra.Command, args []string) {
102+
// Stop HAProxy service
103+
dockerCmd := exec.Command("docker", "service", "rm", systemConfig.UDPProxyConfig.ServiceName)
104+
err := dockerCmd.Run()
105+
if err != nil {
106+
printError("Failed to stop UDP Proxy service")
107+
return
108+
}
109+
printSuccess("Stopped UDP Proxy service")
110+
},
111+
}
112+
113+
// Status command
114+
var udpProxyStatusCmd = &cobra.Command{
115+
Use: "status",
116+
Short: "Show UDP Proxy service status",
117+
Long: "Show UDP Proxy service status",
118+
Run: func(cmd *cobra.Command, args []string) {
119+
// Show HAProxy service status
120+
dockerManager, err := containermanger.NewDockerManager(systemConfig.ServiceConfig.DockerUnixSocketPath)
121+
if err != nil {
122+
printError("Failed to connect to docker daemon")
123+
return
124+
}
125+
serviceDetails, err := dockerManager.GetService(systemConfig.UDPProxyConfig.ServiceName)
126+
if err != nil {
127+
printError("UDP Proxy service is not running")
128+
return
129+
}
130+
// Check realtime status of HAProxy service
131+
info, err := dockerManager.RealtimeInfoService(systemConfig.UDPProxyConfig.ServiceName, false)
132+
if err != nil {
133+
printError("Failed to get realtime info of UDP Proxy service")
134+
return
135+
}
136+
// Print service status
137+
printSuccess("UDP Proxy service is running")
138+
printInfo("Service : " + systemConfig.UDPProxyConfig.ServiceName)
139+
printInfo("Image : " + removeHashFromDockerImageName(serviceDetails.Image))
140+
printInfo("Running replicas : " + strconv.Itoa(info.RunningReplicas))
141+
color.Green("\n--------------Node Names-------------")
142+
for _, placementInfo := range info.PlacementInfos {
143+
printInfo(placementInfo.NodeName + " (" + placementInfo.NodeID + ")")
144+
}
145+
color.Green("------------------------------------")
146+
},
147+
}

container_manager/ports.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
func (m Manager) FetchPublishedHostPorts(service_name string) ([]int, error) {
1111
serviceData, _, err := m.client.ServiceInspectWithRaw(m.ctx, service_name, types.ServiceInspectOptions{})
1212
if err != nil {
13-
return nil, errors.New("error getting swarm server version")
13+
return nil, errors.New("error getting service details > " + service_name)
1414
}
1515
ports := []int{}
1616
for _, port := range serviceData.Endpoint.Ports {

container_manager/service.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,8 @@ func (m Manager) serviceToServiceSpec(service Service) swarm.ServiceSpec {
347347
Mode: swarm.SeccompModeDefault,
348348
},
349349
},
350+
CapabilityAdd: service.Capabilities,
351+
Sysctls: service.Sysctls,
350352
},
351353
// Set network name
352354
Networks: networkAttachmentConfigs,

container_manager/types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ type Service struct {
2323
Image string `json:"image"`
2424
Command []string `json:"command,omitempty"`
2525
Env map[string]string `json:"env,omitempty"`
26+
Capabilities []string `json:"capabilities,omitempty"`
27+
Sysctls map[string]string `json:"sysctl,omitempty"`
2628
VolumeMounts []VolumeMount `json:"volumemounts,omitempty"`
2729
Networks []string `json:"networks,omitempty"`
2830
DeploymentMode DeploymentMode `json:"deploymentmode"`

0 commit comments

Comments
 (0)