Skip to content

Commit a0a9ac8

Browse files
tanmoysrtUbunturoot
authored
feat: introduce swiftwave cli (#133)
* feat: move to yml based config * feat: replaced env vars with system config * chore: rename serviceconfig to systemconfig * chore: remopve http support from haproxy image * chore: haproxy no need to expose 5555 anymore * chore: update config variable names * chore: configuration example added * chore: configuration example added * chore: tidy dependecies * feat: seperating swiftwave server entrypoint from root main.go * feat: seperated server function call from main and boilerplate cli added * feat: init and config command added * feat: update configuration * feat: added checks so that cli run as root user * feat: some info added for info command * feat: swiftwave info command completed * feat: update docs * feat: boilerplate added * chore: remove duplicate code * feat: postgres commands added * chore: cleanup * chore: cleanup * chore: cleanup and boilerplate for haproxy added * chore: cmd rename * chore: init command fixed * chore: setup and init command added and fixed * chore: create folder before binding * chore: setup command modified * chore: haproxy commands added * chore: doctor command not required * chore: remove auto sort of flag and commands * chore: start command added * feat: ssl_mode param added * feat: boilerplate added * chore: typo fix * feat: generate-tls cli has been completed * feat: ssl server support added * feat: db migrate command added * chore: cleanup * feat: swiftwave service command added * feat: service command added --------- Co-authored-by: Ubuntu <[email protected]> Co-authored-by: root <[email protected]>
1 parent 057f693 commit a0a9ac8

31 files changed

+1587
-161
lines changed

cmd/config.cluster.yml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
version: 1.0
2+
mode: cluster # standalone or cluster
3+
service:
4+
use_tls: true # true or false
5+
ssl_certificate_dir: /var/lib/swiftwave/certs
6+
address_of_current_node: will_be_overridden # provide a domain name associated with the current node
7+
bind_address: 0.0.0.0
8+
bind_port: 3333 # choose any other ports except 80 and 443
9+
network_name: swiftwave_network # docker swarm overflow network name
10+
data_dir: /var/lib/swiftwave/data
11+
docker_unix_socket_path: /var/run/docker.sock
12+
restricted_ports: # ports that can't be exposed and bound to haproxy
13+
- 2377 # docker swarm port
14+
- 7946 # docker swarm port
15+
- 4789 # docker swarm port
16+
- 3333 # swiftwave port
17+
auto_migrate_database: true # true or false
18+
lets_encrypt:
19+
staging_environment: false # true or false
20+
email_id: will_be_overridden
21+
account_private_key_path: /etc/swiftwave/letsencrypt/account.key
22+
haproxy:
23+
service_name: haproxy
24+
image: ghcr.io/swiftwave-org/haproxy:2.9
25+
unix_socket_path: /etc/swiftwave/haproxy/dataplaneapi.sock # should end with dataplaneapi.sock
26+
user: will_be_overridden
27+
password: will_be_overridden
28+
data_dir: /var/lib/swiftwave/haproxy
29+
postgresql:
30+
host: 127.0.0.1
31+
port: 5432
32+
user: postgres
33+
password: postgres
34+
database: swiftwave
35+
time_zone: Asia/Kolkata
36+
ssl_mode: disable # disable or require
37+
pubsub:
38+
mode: remote # local or remote
39+
buffer_length: 1000
40+
redis: # all the info should be filled if mode is remote
41+
host: localhost
42+
port: 6379
43+
password: ""
44+
database_id: 0
45+
task_queue:
46+
mode: remote # local or remote
47+
max_outstanding_messages_per_queue: 1000
48+
amqp: # all the info should be filled if mode is remote
49+
protocol: amqp # amqp or amqps
50+
host: localhost
51+
user: guest
52+
password: guest
53+
vhost: vhost
54+
client_name: system_hostname # this client name will be shown in rabbitmq management console

cmd/config.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package cmd
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
"os"
6+
)
7+
8+
func init() {
9+
configCmd.Flags().StringP("editor", "e", "", "Editor to use (vi, vim, nano, gedit, etc.)")
10+
}
11+
12+
var configCmd = &cobra.Command{
13+
Use: "config",
14+
Short: "Open SwiftWave configuration file in editor",
15+
Run: func(cmd *cobra.Command, args []string) {
16+
if checkIfFileExists(configFilePath) {
17+
if editor, _ := cmd.Flags().GetString("editor"); editor != "" {
18+
// set env variable
19+
err := os.Setenv("EDITOR", editor)
20+
if err != nil {
21+
printError("Failed to set EDITOR environment variable")
22+
}
23+
}
24+
openFileInEditor(configFilePath)
25+
} else {
26+
printError("Config file not found at " + configFilePath)
27+
printInfo("Run `swiftwave init` to create a new config file")
28+
}
29+
},
30+
}
Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,39 @@
11
version: 1.0
22
mode: standalone # standalone or cluster
33
service:
4-
auto_tls: true # true or false
5-
tls_cache_dir: /var/www/.cache
6-
whitelisted_domains: # domains that can be used to access the service and generate certificates
7-
- *
4+
use_tls: true # true or false
5+
ssl_certificate_dir: /var/lib/swiftwave/certs
6+
address_of_current_node: will_be_overridden # provide a domain name associated with the current node
87
bind_address: 0.0.0.0
98
bind_port: 3333 # choose any other ports except 80 and 443
109
network_name: swiftwave_network # docker swarm overflow network name
11-
data_dir: ~/swiftwave/data
10+
data_dir: /var/lib/swiftwave/data
1211
docker_unix_socket_path: /var/run/docker.sock
1312
restricted_ports: # ports that can't be exposed and bound to haproxy
1413
- 2377 # docker swarm port
1514
- 7946 # docker swarm port
1615
- 4789 # docker swarm port
1716
- 3333 # swiftwave port
17+
auto_migrate_database: true # true or false
18+
lets_encrypt:
19+
staging_environment: false # true or false
20+
email_id: will_be_overridden
21+
account_private_key_path: /etc/swiftwave/letsencrypt/account.key
1822
haproxy:
1923
service_name: haproxy
2024
image: ghcr.io/swiftwave-org/haproxy:2.9
21-
unix_socket_path: ~/swiftwave/dataplaneapi.sock
22-
user: admin
23-
password: admin
25+
unix_socket_path: /etc/swiftwave/haproxy/dataplaneapi.sock
26+
user: will_be_overridden
27+
password: will_be_overridden
28+
data_dir: /var/lib/swiftwave/haproxy
2429
postgresql:
25-
host: localhost
30+
host: 127.0.0.1
2631
port: 5432
2732
user: postgres
2833
password: postgres
2934
database: swiftwave
3035
time_zone: Asia/Kolkata
31-
lets_encrypt:
32-
staging_environment: false # true or false
33-
34-
account_private_key_path: ~/swiftwave/account.key
36+
ssl_mode: disable # disable or require
3537
pubsub:
3638
mode: local # local or remote
3739
buffer_length: 1000
@@ -48,5 +50,5 @@ task_queue:
4850
host: localhost
4951
user: guest
5052
password: guest
51-
vhost:
52-
client_name: swiftwave
53+
vhost: vhost
54+
client_name: system_hostname # this client name will be shown in rabbitmq management console

cmd/db-migrate.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package cmd
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
"github.com/swiftwave-org/swiftwave/swiftwave_service/core"
6+
"gorm.io/driver/postgres"
7+
"gorm.io/gorm"
8+
)
9+
10+
var dbMigrateCmd = &cobra.Command{
11+
Use: "db-migrate",
12+
Short: "Migrate the database",
13+
Long: `Migrate the database`,
14+
Run: func(cmd *cobra.Command, args []string) {
15+
// Initiating database client
16+
dbDialect := postgres.Open(systemConfig.PostgresqlConfig.DSN())
17+
client, err := gorm.Open(dbDialect, &gorm.Config{})
18+
if err != nil {
19+
printError("Failed to create database client")
20+
}
21+
// Migrate the database
22+
err = core.MigrateDatabase(client)
23+
if err != nil {
24+
printError("Failed to migrate the database")
25+
} else {
26+
printSuccess("Successfully migrated the database")
27+
}
28+
},
29+
}

cmd/generate-tls.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"crypto/rand"
6+
"crypto/rsa"
7+
"crypto/x509"
8+
"encoding/pem"
9+
"errors"
10+
"net/http"
11+
"os"
12+
"strings"
13+
14+
"github.com/labstack/echo/v4"
15+
"github.com/labstack/echo/v4/middleware"
16+
"github.com/spf13/cobra"
17+
SSL "github.com/swiftwave-org/swiftwave/ssl_manager"
18+
"gorm.io/driver/postgres"
19+
"gorm.io/gorm"
20+
)
21+
22+
func init() {
23+
generateTLSCommand.Flags().String("domain", "", "Domain name for which to generate the certificate")
24+
}
25+
26+
var generateTLSCommand = &cobra.Command{
27+
Use: "generate-tls",
28+
Short: "Generate TLS certificates for swiftwave endpoints",
29+
Long: `This command generates TLS certificates for swiftwave endpoints.
30+
It's not for generating certificates for domain of hosted applications`,
31+
Run: func(cmd *cobra.Command, args []string) {
32+
// If domain is not provided, use the domain from config
33+
domain := cmd.Flag("domain").Value.String()
34+
if strings.TrimSpace(domain) == "" {
35+
domain = systemConfig.ServiceConfig.AddressOfCurrentNode
36+
}
37+
// Check if there is already someone listening on port 80
38+
if checkIfPortIsInUse("80") {
39+
printError("Port 80 is already in use, please stop the process and try again")
40+
return
41+
}
42+
//// Start http-01 challenge server
43+
echoServer := echo.New()
44+
echoServer.HideBanner = true
45+
echoServer.Pre(middleware.RemoveTrailingSlash())
46+
// Initiating database client
47+
dbDialect := postgres.Open(systemConfig.PostgresqlConfig.DSN())
48+
dbClient, err := gorm.Open(dbDialect, &gorm.Config{})
49+
if err != nil {
50+
printError("Failed to connect to database")
51+
return
52+
}
53+
// Initiating SSL Manager
54+
options := SSL.ManagerOptions{
55+
IsStaging: systemConfig.LetsEncryptConfig.StagingEnvironment,
56+
Email: systemConfig.LetsEncryptConfig.EmailID,
57+
AccountPrivateKeyFilePath: systemConfig.LetsEncryptConfig.AccountPrivateKeyPath,
58+
}
59+
sslManager := SSL.Manager{}
60+
err = sslManager.Init(context.Background(), *dbClient, options)
61+
if err != nil {
62+
printError("Failed to initiate SSL Manager")
63+
return
64+
}
65+
// Start the server
66+
go func(sslManager *SSL.Manager) {
67+
sslManager.InitHttpHandlers(echoServer)
68+
err := echoServer.Start(":80")
69+
if err != nil {
70+
if errors.Is(err, http.ErrServerClosed) {
71+
printSuccess("http-01 challenge server has been stopped")
72+
} else {
73+
printError("Failed to start http-01 challenge server")
74+
os.Exit(1)
75+
}
76+
}
77+
}(&sslManager)
78+
// Generate private key
79+
privateKey, err := generatePrivateKey()
80+
if err != nil {
81+
printError("Failed to generate private key")
82+
return
83+
}
84+
// Generate the certificate
85+
certificate, err := sslManager.ObtainCertificate(domain, privateKey)
86+
if err != nil {
87+
println(err.Error())
88+
printError("Failed to generate certificate")
89+
return
90+
}
91+
// Stop the http-01 challenge server
92+
echoServer.Server.Shutdown(context.Background())
93+
// Store private key and certificate in the service.ssl_certificate_dir/<domain> folder
94+
dir := systemConfig.ServiceConfig.SSLCertificateDir + "/" + domain
95+
if !checkIfFolderExists(dir) {
96+
err = createFolder(dir)
97+
if err != nil {
98+
printError("Failed to create folder " + dir)
99+
return
100+
}
101+
}
102+
// Store private key
103+
err = os.WriteFile(dir+"/private.key", []byte(privateKey), 0644)
104+
if err != nil {
105+
printError("Failed to store private key")
106+
return
107+
}
108+
// Store certificate
109+
err = os.WriteFile(dir+"/certificate.crt", []byte(certificate), 0644)
110+
if err != nil {
111+
printError("Failed to store certificate")
112+
return
113+
}
114+
// Print success message
115+
printSuccess("Successfully generated TLS certificate for " + domain)
116+
},
117+
}
118+
119+
func generatePrivateKey() (string, error) {
120+
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
121+
if err != nil {
122+
return "", errors.New("unable to generate private key")
123+
}
124+
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
125+
pemKey := pem.Block{
126+
Type: "RSA PRIVATE KEY",
127+
Bytes: privateKeyBytes,
128+
}
129+
privateKeyBytes = pem.EncodeToMemory(&pemKey)
130+
return string(privateKeyBytes), nil
131+
}

0 commit comments

Comments
 (0)