Add testing to CI, add more utils and modify config paths
ci/woodpecker/push/build Pipeline was successful
Details
ci/woodpecker/push/build Pipeline was successful
Details
This commit is contained in:
parent
f9ab4323e6
commit
dbe8c9d959
|
@ -0,0 +1,46 @@
|
|||
platform: linux/amd64
|
||||
|
||||
pipeline:
|
||||
format:
|
||||
image: golang
|
||||
commands:
|
||||
- files=$(gofmt -l .) && echo "$files" && [ -z "$files" ]
|
||||
|
||||
test:
|
||||
image: golang
|
||||
commands:
|
||||
- make test
|
||||
|
||||
build:
|
||||
image: golang
|
||||
commands:
|
||||
- make build
|
||||
|
||||
prepare:
|
||||
image: alpine
|
||||
commands:
|
||||
- mkdir release-out
|
||||
- mkdir summer
|
||||
- mv dist/* summer
|
||||
|
||||
archive:
|
||||
image: joseluisq/drone-archive
|
||||
settings:
|
||||
format: tar
|
||||
src_base_path: .
|
||||
src: ./summer
|
||||
dest: ./release-out/summer.tar.gz
|
||||
checksum: true
|
||||
checksum_algo: sha256
|
||||
checksum_dest: release-out/summer.CHECKSUM.tar.gz.txt
|
||||
|
||||
publish:
|
||||
image: plugins/gitea-release
|
||||
settings:
|
||||
api_key:
|
||||
from_secret: release-token
|
||||
base_url: https://code.mrmelon54.com
|
||||
files: release-out/*
|
||||
when:
|
||||
event:
|
||||
- tag
|
14
Makefile
14
Makefile
|
@ -1,10 +1,14 @@
|
|||
.PHONY: all test setup-docker restart-docker run-cli
|
||||
SHELL := bash
|
||||
VERSION = 0.0.1
|
||||
HASH = $(shell git rev-parse --short HEAD)
|
||||
COMMIT_DATE = $(shell date '+%Y-%m-%d %H:%M:%S')
|
||||
VERSION := $(shell git describe --tags --dirty --always)
|
||||
|
||||
ifdef CI_BUILD_NUMBER
|
||||
VERSION := "build.$(CI_BUILD_NUMBER)-$(VERSION)"
|
||||
endif
|
||||
|
||||
BUILD_DATE = $(shell date '+%Y-%m-%d %H:%M:%S')
|
||||
UTILS_PKG = code.mrmelon54.com/melon/summer/pkg/utils
|
||||
LD_FLAGS = -s -w -X '${UTILS_PKG}.BuildVersion=${VERSION}' -X '${UTILS_PKG}.BuildCommit=${HASH}' -X '${UTILS_PKG}.BuildDate=${COMMIT_DATE}'
|
||||
LD_FLAGS = -s -w -X '${UTILS_PKG}.BuildVersion=${VERSION}' -X '${UTILS_PKG}.BuildDate=${BUILD_DATE}'
|
||||
CC = go
|
||||
BUILD_DIR = $(shell pwd)
|
||||
PROGRAMS = azalea buttercup marigold rose
|
||||
|
@ -19,7 +23,7 @@ build:
|
|||
"$(BUILD_DIR)/scripts/build.sh" "$(BUILD_DIR)" "$(CC)" "$(LD_FLAGS)" "$(PROGRAMS)" "$(TAGS)"
|
||||
|
||||
test:
|
||||
$(CC) test ./...
|
||||
$(CC) test ./... -tags TEST
|
||||
|
||||
setup-docker: build
|
||||
"$(BUILD_DIR)/scripts/setup-docker.sh" "$(BUILD_DIR)" "$(PROGRAMS)"
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# Summer
|
||||
|
||||
[![status-badge](https://ci.mrmelon54.com/api/badges/melon/summer/status.svg)](https://ci.mrmelon54.com/melon/summer)
|
||||
|
||||
APIs and server side code for connecting multiple backend services.
|
||||
|
||||
| Program | Description |
|
||||
|-----------|--------------------------------------------------------------------------------|
|
||||
| Azalea | HTTP server with subdomain based internal proxying and API endpoint management |
|
||||
| Buttercup | SSL certificate management and renewal |
|
||||
| Lily | Process management and logging |
|
||||
| Marigold | User authentication and management with OAuth application support |
|
||||
| Rose | TCP and UDP forwarding server |
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
make build
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
# first time docker setup
|
||||
debug=1 make setup-docker
|
||||
|
||||
# rebuild and restart docker containers
|
||||
debug=1 make restart-docker
|
||||
```
|
|
@ -5,4 +5,4 @@ listen:
|
|||
api: :7071
|
||||
auth:
|
||||
public: public.key.pem
|
||||
apiDomain: api.summer-test
|
||||
apiDomain: api.summer.test
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"code.mrmelon54.com/melon/summer/cmd/azalea/servers"
|
||||
"code.mrmelon54.com/melon/summer/pkg/api"
|
||||
_ "embed"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
@ -22,13 +22,7 @@ func TestConfig(t *testing.T) {
|
|||
Https: ":443",
|
||||
Api: ":7071",
|
||||
},
|
||||
Auth: servers.ApiAuthConfig{
|
||||
Public: "public.key.pem",
|
||||
Perm: map[string]uint32{
|
||||
"httpService": 3,
|
||||
"httpRedirect": 4,
|
||||
},
|
||||
SudoPerm: []uint32{1, 2},
|
||||
},
|
||||
Auth: api.AuthConfig{Public: "public.key.pem"},
|
||||
ApiDomain: "api.summer.test",
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build DEBUG
|
||||
//go:build DEBUG || TEST
|
||||
|
||||
package quick_cert
|
||||
|
||||
|
|
|
@ -3,14 +3,16 @@ package quick_cert
|
|||
import (
|
||||
"code.mrmelon54.com/melon/summer/pkg/tables/certificate"
|
||||
"code.mrmelon54.com/melon/summer/pkg/utils"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func TestQuickCertDebug(t *testing.T) {
|
||||
db, err := xorm.NewEngine("sqlite3", ":memory")
|
||||
db, err := xorm.NewEngine("sqlite3", ":memory:")
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, db.CreateTables(&certificate.Certificate{}, &certificate.CertificateData{}, &certificate.CertificateDomain{}))
|
||||
QuickCert(db)
|
||||
|
||||
var c []certificate.Certificate
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build !DEBUG
|
||||
//go:build !DEBUG && !TEST
|
||||
|
||||
package quick_cert
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build DEBUG
|
||||
//go:build DEBUG || TEST
|
||||
|
||||
package pebble_dev
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build !DEBUG
|
||||
//go:build !DEBUG && !TEST
|
||||
|
||||
package pebble_dev
|
||||
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"code.mrmelon54.com/melon/summer/pkg/utils"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type LilyConn struct {
|
||||
daemon *LilyDaemon
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
func HandleLilyConn(daemon *LilyDaemon, conn net.Conn) *LilyConn {
|
||||
defer func(conn net.Conn) {
|
||||
_ = conn.Close()
|
||||
}(conn)
|
||||
l := &LilyConn{daemon, conn}
|
||||
go l.background()
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *LilyConn) background() {
|
||||
a := bufio.NewReader(l.conn)
|
||||
for {
|
||||
// read string until newline
|
||||
line, err := a.ReadString('\n')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// get first space to split off command
|
||||
i := strings.IndexByte(line, ' ')
|
||||
cmd := line[:i]
|
||||
hasArgs := i > 0
|
||||
|
||||
if hasArgs {
|
||||
args := utils.QuotedStringToArray(line[i+1:])
|
||||
pairs := utils.ParseKeyValueFromStringArray(args)
|
||||
l.daemon.RunCmd(cmd, pairs)
|
||||
} else {
|
||||
l.daemon.RunCmd(cmd, []utils.KeyValuePair{})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LilyConn) sendError(msg string) {
|
||||
_, _ = fmt.Fprintln(l.conn, "ERROR", msg)
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"code.mrmelon54.com/melon/summer/pkg/utils"
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
type LilyDaemon struct {
|
||||
sock net.Listener
|
||||
}
|
||||
|
||||
func NewLilyDaemon(sock net.Listener) *LilyDaemon {
|
||||
return &LilyDaemon{sock: sock}
|
||||
}
|
||||
|
||||
func (l *LilyDaemon) Start() {
|
||||
log.Println("[LilyDaemon] Starting watchers")
|
||||
for {
|
||||
conn, err := l.sock.Accept()
|
||||
if err != nil {
|
||||
log.Println("[LilyDaemon] Accept error:", err)
|
||||
}
|
||||
go HandleLilyConn(l, conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LilyDaemon) Destroy() {
|
||||
_ = l.sock.Close()
|
||||
}
|
||||
|
||||
func (l *LilyDaemon) RunCmd(cmd string, pairs []utils.KeyValuePair) {
|
||||
switch cmd {
|
||||
case "ADD":
|
||||
log.Printf("ADD %#v\n", pairs)
|
||||
case "REMOVE":
|
||||
log.Printf("REMOVE %#v\n", pairs)
|
||||
case "START":
|
||||
log.Printf("START %#v\n", pairs)
|
||||
case "STOP":
|
||||
log.Printf("STOP %#v\n", pairs)
|
||||
case "RESTART":
|
||||
log.Printf("RESTART %#v\n", pairs)
|
||||
case "LOG":
|
||||
log.Printf("LOG %#v\n", pairs)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package daemon
|
||||
|
||||
// TODO: make a watcher, run and log the subprocess
|
|
@ -0,0 +1,73 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"code.mrmelon54.com/melon/summer/cmd/lily/daemon"
|
||||
utils2 "code.mrmelon54.com/melon/summer/cmd/lily/utils"
|
||||
"code.mrmelon54.com/melon/summer/pkg/utils"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/juju/fslock"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var isVersion, isDaemon bool
|
||||
flag.BoolVar(&isVersion, "version", false, "Show program version")
|
||||
flag.BoolVar(&isDaemon, "daemon", false, "Run daemon process")
|
||||
flag.Parse()
|
||||
if isVersion {
|
||||
fmt.Printf("lily %s\n", utils.BuildVersion)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[Main] Starting up lily (%s from %s)\n", utils.BuildVersion, utils.BuildDate)
|
||||
|
||||
sharedPaths, err := utils2.NewSharedPaths()
|
||||
if err != nil {
|
||||
log.Fatalf("[Main] Failed to generate shared paths")
|
||||
return
|
||||
}
|
||||
|
||||
if isDaemon {
|
||||
confLock := fslock.New(sharedPaths.GetConfLockPath())
|
||||
err := confLock.TryLock()
|
||||
if err != nil {
|
||||
log.Fatal("[Main] Failed to acquire lock, is another daemon process running:", err)
|
||||
}
|
||||
if err := os.RemoveAll(sharedPaths.GetSockAddr()); err != nil {
|
||||
log.Fatal("[Main] Failed to remove old unix socket:", err)
|
||||
}
|
||||
sock, err := net.Listen("unix", sharedPaths.GetSockAddr())
|
||||
if err != nil {
|
||||
log.Fatal("[Main] Failed to listen on unix socket:", err)
|
||||
}
|
||||
defer func(sock net.Listener) {
|
||||
_ = sock.Close()
|
||||
}(sock)
|
||||
|
||||
l := daemon.NewLilyDaemon(sock)
|
||||
l.Start()
|
||||
|
||||
// Wait for exit signal
|
||||
sc := make(chan os.Signal, 1)
|
||||
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
|
||||
<-sc
|
||||
fmt.Println()
|
||||
|
||||
// Stop runner
|
||||
log.Printf("[Main] Stopping lily")
|
||||
n := time.Now()
|
||||
l.Destroy()
|
||||
log.Printf("[Main] Took '%s' to shutdown\n", time.Now().Sub(n))
|
||||
log.Println("[Main] Goodbye")
|
||||
return
|
||||
}
|
||||
|
||||
// Lily client
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
type SharedPaths struct {
|
||||
config, cache string
|
||||
}
|
||||
|
||||
func NewSharedPaths() (s SharedPaths, err error) {
|
||||
s.config, err = os.UserConfigDir()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.cache, err = os.UserCacheDir()
|
||||
return
|
||||
}
|
||||
|
||||
func (s SharedPaths) getConfPath() string {
|
||||
return path.Join(s.config, "summer-lily")
|
||||
}
|
||||
|
||||
func (s SharedPaths) GetConfLockPath() string {
|
||||
return path.Join(s.getConfPath(), "config.lock")
|
||||
}
|
||||
|
||||
func (s SharedPaths) getConfFilePath() string {
|
||||
return path.Join(s.getConfPath(), "config.json")
|
||||
}
|
||||
|
||||
func (s SharedPaths) getConfOldFilePath() string {
|
||||
return path.Join(s.getConfPath(), "config.old.json")
|
||||
}
|
||||
|
||||
func (s SharedPaths) GetSockAddr() string {
|
||||
return path.Join(s.cache, "summer-lily.sock")
|
||||
}
|
8
go.mod
8
go.mod
|
@ -1,6 +1,6 @@
|
|||
module code.mrmelon54.com/melon/summer
|
||||
|
||||
go 1.18
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
code.mrmelon54.com/melon/certgen v0.0.0-20220830133534-0fb4cb7e67d1
|
||||
|
@ -11,7 +11,9 @@ require (
|
|||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/handlers v1.5.1
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/mrmelon54/favicon v0.0.0-20220830075604-72b3eafe69b9
|
||||
github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b
|
||||
github.com/mattn/go-sqlite3 v1.14.9
|
||||
github.com/mrmelon54/favicon v1.0.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sec51/twofactor v1.0.0
|
||||
github.com/sethvargo/go-limiter v0.7.2
|
||||
|
@ -24,7 +26,6 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
code.mrmelon54.xyz/sean/png2ico v0.0.0-20220321230631-311127b42237 // indirect
|
||||
github.com/ByteArena/poly2tri-go v0.0.0-20170716161910-d102ad91854f // indirect
|
||||
github.com/adrg/strutil v0.2.2 // indirect
|
||||
github.com/adrg/sysfont v0.1.2 // indirect
|
||||
|
@ -44,6 +45,7 @@ require (
|
|||
github.com/miekg/dns v1.1.47 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mrmelon54/png2ico v1.0.0 // indirect
|
||||
github.com/nrdcg/namesilo v0.2.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/sec51/convert v1.0.2 // indirect
|
||||
|
|
10
go.sum
10
go.sum
|
@ -2,8 +2,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
|||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
code.mrmelon54.com/melon/certgen v0.0.0-20220830133534-0fb4cb7e67d1 h1:tll8DwvO1CL+xXJIMLyDmQYoYr/gA4BkcUFtNHB1BFo=
|
||||
code.mrmelon54.com/melon/certgen v0.0.0-20220830133534-0fb4cb7e67d1/go.mod h1:Liyhe1bkNyeVfw6LicCgrQ+4oUT/w/qONLjvejkUim0=
|
||||
code.mrmelon54.xyz/sean/png2ico v0.0.0-20220321230631-311127b42237 h1:CIOWl5Xe64MAxdiqHy6kG52M2q3sYjN0qZWw5Z62600=
|
||||
code.mrmelon54.xyz/sean/png2ico v0.0.0-20220321230631-311127b42237/go.mod h1:Dr9YQ0FwIMNC+GGRCahsp5IhB3BZkvyZbrtFni+3GDk=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
fyne.io/fyne/v2 v2.1.2/go.mod h1:p+E/Dh+wPW8JwR2DVcsZ9iXgR9ZKde80+Y+40Is54AQ=
|
||||
|
@ -315,6 +313,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
|
|||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b h1:FQ7+9fxhyp82ks9vAuyPzG0/vVbWwMwLJ+P6yJI5FN8=
|
||||
github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b/go.mod h1:HMcgvsgd0Fjj4XXDkbjdmlbI505rUPBs6WBMYg2pXks=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||
|
@ -383,8 +383,10 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
|||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
|
||||
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
|
||||
github.com/mrmelon54/favicon v0.0.0-20220830075604-72b3eafe69b9 h1:37A6XlY1rNqbnD2ksPtHSHIUobNaFsBI1zDbuH/saFw=
|
||||
github.com/mrmelon54/favicon v0.0.0-20220830075604-72b3eafe69b9/go.mod h1:11VXnD2+bF1vuLcw4K/2vCTz3atY0kf0kD5iA3Hop9s=
|
||||
github.com/mrmelon54/favicon v1.0.0 h1:01e9jgulx1oLoR2SEA4vI0gti0w3Nhf10MukdJLLioI=
|
||||
github.com/mrmelon54/favicon v1.0.0/go.mod h1:YrUKmXdn2dCmR21btYPJqRlkm1cUyoPE45s3nzDlUIE=
|
||||
github.com/mrmelon54/png2ico v1.0.0 h1:YE20i0xao8rkuYaCq3Xj2hUkVkJ6xp412aGDMrGqufA=
|
||||
github.com/mrmelon54/png2ico v1.0.0/go.mod h1:vp8Be9y5cz102ANon+BnsIzTUdet3VQRvOuWJTH9h0M=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
||||
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
//go:build DEBUG
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
func getConfigPath(software string) string {
|
||||
// path running locally
|
||||
p := path.Join("cmd", software, "config.yml")
|
||||
_, err := os.Stat(p)
|
||||
if err == nil {
|
||||
return p
|
||||
}
|
||||
// path inside docker container
|
||||
return path.Join("/etc/melon-summer/conf", software+".yml")
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
//go:build !DEBUG
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func getConfigPath(software string) string {
|
||||
return fmt.Sprintf("%s.conf.yml", software)
|
||||
}
|
|
@ -9,7 +9,6 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
"xorm.io/xorm"
|
||||
|
@ -20,10 +19,12 @@ func Run(software, dbString string, executor Executor) {
|
|||
flag.BoolVar(&version, "version", false, "Show program version")
|
||||
flag.Parse()
|
||||
if version {
|
||||
fmt.Println(utils.FullVersionString())
|
||||
fmt.Printf("%s %s\n", software, utils.BuildVersion)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[Main] Starting up %s (%s from %s)\n", software, utils.BuildVersion, utils.BuildDate)
|
||||
|
||||
// Load database
|
||||
engine, err := xorm.NewEngine("mysql", dbString)
|
||||
utils.Check("Failed to connect to database", err)
|
||||
|
@ -31,7 +32,6 @@ func Run(software, dbString string, executor Executor) {
|
|||
// Start runner
|
||||
runner := &Runner{software: software, executor: executor, Database: engine}
|
||||
executor.Init(runner)
|
||||
log.Printf("[Main] Starting up %s v%s #%s (%s)\n", software, utils.BuildVersion, utils.BuildCommit, utils.BuildDate)
|
||||
|
||||
// Wait for exit signal
|
||||
sc := make(chan os.Signal, 1)
|
||||
|
@ -48,12 +48,8 @@ func Run(software, dbString string, executor Executor) {
|
|||
}
|
||||
|
||||
func DecodeConfig[T any](software string) (t T) {
|
||||
f := filepath.Join("cmd", software, "config.yml")
|
||||
f := getConfigPath(software)
|
||||
open, err := os.Open(f)
|
||||
if os.IsNotExist(err) {
|
||||
f = filepath.Join("/etc/melon-summer/conf", software+".yml")
|
||||
open, err = os.Open(f)
|
||||
}
|
||||
utils.Check("Failed to open config file", err)
|
||||
decoder := yaml.NewDecoder(open)
|
||||
utils.Check("Failed to decode config file", decoder.Decode(&t))
|
||||
|
|
|
@ -3,27 +3,4 @@ package utils
|
|||
var (
|
||||
BuildVersion = ""
|
||||
BuildDate = ""
|
||||
BuildCommit = ""
|
||||
BuildDirty = false
|
||||
)
|
||||
|
||||
//goland:noinspection GoBoolExpressions
|
||||
func IsDebug() bool {
|
||||
return BuildVersion == "" || BuildCommit == "" || BuildDate == ""
|
||||
}
|
||||
|
||||
func FullVersionString() string {
|
||||
return BuildVersion + "+" + getVersionMetadata()
|
||||
}
|
||||
|
||||
//goland:noinspection GoBoolExpressions
|
||||
func getVersionMetadata() string {
|
||||
if BuildCommit != "" {
|
||||
a := "rev." + BuildCommit
|
||||
if BuildDirty {
|
||||
a += "-dirty"
|
||||
}
|
||||
return a
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package utils
|
||||
|
||||
import "strings"
|
||||
|
||||
type KeyValuePair struct{ key, value string }
|
||||
|
||||
func ParseKeyValueString(text string) *KeyValuePair {
|
||||
v := strings.SplitN(text, "=", 2)
|
||||
if len(v) != 2 {
|
||||
return nil
|
||||
}
|
||||
return &KeyValuePair{v[0], v[1]}
|
||||
}
|
||||
|
||||
func ParseKeyValueFromStringArray(text []string) []KeyValuePair {
|
||||
out := make([]KeyValuePair, 0)
|
||||
for _, a := range text {
|
||||
b := ParseKeyValueString(a)
|
||||
if b != nil {
|
||||
out = append(out, *b)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseKeyValueString(t *testing.T) {
|
||||
assert.Nil(t, ParseKeyValueString("hello world"))
|
||||
assert.Equal(t, &KeyValuePair{"hello", "world"}, ParseKeyValueString("hello=world"))
|
||||
}
|
||||
|
||||
func TestParseKeyValueFromStringArray(t *testing.T) {
|
||||
assert.Equal(t, []KeyValuePair{}, ParseKeyValueFromStringArray([]string{}))
|
||||
assert.Equal(t, []KeyValuePair{}, ParseKeyValueFromStringArray([]string{"hello world"}))
|
||||
assert.Equal(t, []KeyValuePair{{"hello", "world"}}, ParseKeyValueFromStringArray([]string{"hello=world"}))
|
||||
assert.Equal(t, []KeyValuePair{{"hello", "world"}}, ParseKeyValueFromStringArray([]string{"hello=world", "test"}))
|
||||
assert.Equal(t, []KeyValuePair{{"hello", "world"}, {"test", "1"}}, ParseKeyValueFromStringArray([]string{"hello=world", "test=1"}))
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package utils
|
||||
|
||||
func QuotedStringToArray(text string) []string {
|
||||
var escape, quoted bool
|
||||
out := make([]string, 0)
|
||||
var a string
|
||||
|
||||
runeArr := []rune(text)
|
||||
for _, char := range runeArr {
|
||||
if escape {
|
||||
a += string(char)
|
||||
escape = false
|
||||
continue
|
||||
}
|
||||
switch char {
|
||||
case ' ':
|
||||
if quoted {
|
||||
a += " "
|
||||
} else if a != "" {
|
||||
out = append(out, a)
|
||||
a = ""
|
||||
}
|
||||
case '\\':
|
||||
escape = true
|
||||
case '"':
|
||||
quoted = !quoted
|
||||
default:
|
||||
a += string(char)
|
||||
}
|
||||
}
|
||||
if a != "" {
|
||||
out = append(out, a)
|
||||
a = ""
|
||||
}
|
||||
return out
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestQuotedStringToArray(t *testing.T) {
|
||||
assert.Equal(t, []string{"hello", "world"}, QuotedStringToArray("hello world"))
|
||||
assert.Equal(t, []string{"hello world"}, QuotedStringToArray("\"hello world\""))
|
||||
assert.Equal(t, []string{"test", "hello world", "message"}, QuotedStringToArray("test \"hello world\" message"))
|
||||
assert.Equal(t, []string{"test", "hello world \"with extra data\" inside", "message"}, QuotedStringToArray("test \"hello world \\\"with extra data\\\" inside\" message"))
|
||||
assert.Equal(t, []string{"hello world"}, QuotedStringToArray("hello\\ world"))
|
||||
assert.Equal(t, []string{"test", "hello world", "message"}, QuotedStringToArray("test hello\\ world message"))
|
||||
}
|
|
@ -1,9 +1,24 @@
|
|||
#!/bin/bash
|
||||
function countdown() {
|
||||
for i in $(seq "$1" -1 1); do
|
||||
echo -ne "\r$i "
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
|
||||
PROGRAMS="$1"
|
||||
echo "Restarting summer-mariadb..."
|
||||
docker restart "summer-mariadb" >/dev/null
|
||||
echo "Restarting summer-pebble..."
|
||||
docker restart "summer-pebble" >/dev/null
|
||||
if [ "$(docker inspect summer-mariadb | jq '.[0].State.Status' -r)" != "running" ]; then
|
||||
echo "Restarting summer-mariadb..."
|
||||
docker restart "summer-mariadb" >/dev/null
|
||||
countdown 10
|
||||
echo -e "\rFinished"
|
||||
fi
|
||||
if [ "$(docker inspect summer-pebble | jq '.[0].State.Status' -r)" != "running" ]; then
|
||||
echo "Restarting summer-pebble..."
|
||||
docker restart "summer-pebble" >/dev/null
|
||||
countdown 10
|
||||
echo -e "\rFinished"
|
||||
fi
|
||||
for PROG in ${PROGRAMS}; do
|
||||
echo "Restarting ${PROG}..."
|
||||
docker restart "${PROG}-test" >/dev/null
|
||||
|
|
Reference in New Issue