Update to using docker compose for configuration
ci/woodpecker/push/build Pipeline failed Details

This commit is contained in:
Melon 2023-01-09 00:04:01 +00:00
parent 0441ff46a9
commit 27ca66c01b
Signed by: melon
GPG Key ID: 6C9D970C50D26A25
42 changed files with 564 additions and 319 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
.data/
dist/

View File

@ -2,7 +2,11 @@
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true">
<buildTags>
<option name="goVersion" value="go1.19" />
<option name="customFlags">
<array>
<option value="DEBUG" />
</array>
</option>
</buildTags>
</component>
<component name="NewModuleRootManager">

View File

@ -1,4 +1,4 @@
.PHONY: all test setup-docker restart-docker run-cli
.PHONY: all build test dev dev-down run-cli
SHELL := bash
VERSION := $(shell git describe --dirty --always)
@ -25,11 +25,12 @@ build:
test:
$(CC) test ./... -tags TEST
setup-docker: build
"$(BUILD_DIR)/scripts/setup-docker.sh" "$(BUILD_DIR)" "$(PROGRAMS)"
dev:
docker compose -f docker-compose.development.yml build --no-cache
docker compose -f docker-compose.development.yml up
restart-docker: build
"$(BUILD_DIR)/scripts/restart-docker.sh" "$(PROGRAMS)"
dev-down:
docker compose -f docker-compose.development.yml down
run-cli:
docker run --rm -ti --hostname=cli.test --network=summer ubuntu bash
docker exec -ti summer-cli-1 /bin/bash

9
cmd/azalea/Dockerfile Normal file
View File

@ -0,0 +1,9 @@
FROM golang:1.19
WORKDIR /go/src/app
COPY ../.. .
RUN go get -d -v ./...
RUN ./scripts/build.sh "/bin/azalea" "./cmd/azalea/"
CMD ["azalea"]

View File

@ -0,0 +1,9 @@
FROM golang:1.19
WORKDIR /go/src/app
COPY ../.. .
RUN go get -d -v ./...
RUN ./scripts/build.sh "/bin/azalea" "./cmd/azalea/" "DEBUG"
CMD ["azalea"]

View File

@ -1,7 +1,7 @@
package main
import (
quickCert "code.mrmelon54.com/melon/summer/cmd/azalea/quick-cert"
quickDb "code.mrmelon54.com/melon/summer/cmd/azalea/quick-db"
"code.mrmelon54.com/melon/summer/cmd/azalea/routing"
"code.mrmelon54.com/melon/summer/cmd/azalea/servers"
"code.mrmelon54.com/melon/summer/pkg/cli"
@ -40,13 +40,12 @@ var usedTables = []any{
}
func main() {
conf := cli.DecodeConfig[AzaleaConfig]("azalea")
cli.Run("azalea", conf.Database, &Azalea{conf: conf})
cli.RunWithConfig[AzaleaConfig]("azalea", func(t AzaleaConfig) string { return t.Database }, new(Azalea))
}
type Azalea struct {
conf AzaleaConfig
runner *cli.Runner
runner *cli.Runner[AzaleaConfig]
reverseProxy *httputil.ReverseProxy
httpServer *http.Server
httpsServer *http.Server
@ -54,10 +53,11 @@ type Azalea struct {
verify mjwt.Provider
}
func (azalea *Azalea) Init(runner *cli.Runner) {
func (azalea *Azalea) Init(runner *cli.Runner[AzaleaConfig]) {
azalea.runner = runner
azalea.conf = runner.Config
utils.Check("[Azalea.Init()] Failed to sync tables", runner.Database.Sync(usedTables...))
quickCert.QuickCert(runner.Database)
quickDb.QuickDb(runner.Database)
addrPort, err := splitPortCombo(azalea.conf.Listen.Https)
utils.Check("[Azalea.Init()] Failed to parse HTTPs port", err)

View File

@ -1,71 +0,0 @@
//go:build DEBUG || TEST
package quick_cert
import (
"code.mrmelon54.com/melon/certgen"
"code.mrmelon54.com/melon/summer/pkg/tables/certificate"
"code.mrmelon54.com/melon/summer/pkg/utils"
"crypto/x509/pkix"
"log"
"math/big"
"math/rand"
"net"
"time"
"xorm.io/xorm"
)
func QuickCert(db *xorm.Engine) {
log.Println("[QuickCert()] Generating development certificate for testing azalea")
count, err := db.Count(&certificate.Certificate{})
utils.Check("[QuickCert()] Failed to count certificates:", err)
if count != 0 {
return
}
n := time.Now()
caTls, err := certgen.MakeCaTls(pkix.Name{CommonName: "ca.summer.test"}, new(big.Int).SetUint64(rand.Uint64()))
utils.Check("[QuickCert()] Failed to make CA TLS:", err)
serverTls, err := certgen.MakeServerTls(caTls, pkix.Name{CommonName: "summer.test"}, new(big.Int).SetUint64(rand.Uint64()), []string{"summer.test", "*.summer.test"}, []net.IP{})
utils.Check("[QuickCert()] Failed to make server TLS:", err)
cert := serverTls.GetTlsLeaf()
leaf := certgen.TlsLeaf(&cert)
c := certificate.Certificate{
Owner: 1,
LetsEncrypt: 1,
Namesilo: 1,
AutoRenew: utils.PBool(false),
Active: utils.PBool(true),
Renewing: utils.PBool(false),
RenewFailed: utils.PBool(false),
}
_, err = db.Insert(&c)
utils.Check("[QuickCert()] Failed to insert certificate:", err)
_, err = db.Insert(&certificate.CertificateData{
MetaId: c.Id,
PreviousCert: 0,
CertificateBytes: serverTls.GetCertPem(),
KeyBytes: serverTls.GetKeyPem(),
NotAfter: leaf.NotAfter,
CreatedAt: n,
UpdatedAt: n,
Ready: utils.PBool(true),
})
utils.Check("[QuickCert()] Failed to insert certificate data:", err)
_, err = db.Insert(&certificate.CertificateDomain{
DomainId: 0,
CertId: c.Id,
Domain: "summer.test",
Wildcard: utils.PBool(false),
}, &certificate.CertificateDomain{
DomainId: 0,
CertId: c.Id,
Domain: "summer.test",
Wildcard: utils.PBool(true),
})
utils.Check("[QuickCert()] Failed to insert certificate domains:", err)
log.Println("[QuickCert()] Finished generating development certificate")
}

View File

@ -0,0 +1,154 @@
//go:build DEBUG || TEST
package quick_db
import (
"code.mrmelon54.com/melon/certgen"
"code.mrmelon54.com/melon/summer/pkg/tables/certificate"
"code.mrmelon54.com/melon/summer/pkg/tables/web"
"code.mrmelon54.com/melon/summer/pkg/utils"
"crypto/x509/pkix"
"fmt"
"log"
"math/big"
"math/rand"
"net"
"time"
"xorm.io/xorm"
)
func QuickDb(db *xorm.Engine) {
quickCert(db)
quickDomain(db)
quickApi(db)
}
func quickCert(db *xorm.Engine) {
log.Println("[quickCert()] Generating development certificate for testing azalea")
count, err := db.Count(&certificate.Certificate{})
utils.Check("[quickCert()] Failed to count certificates:", err)
if count != 0 {
return
}
n := time.Now()
caTls, err := certgen.MakeCaTls(pkix.Name{CommonName: "ca.summer.test"}, new(big.Int).SetUint64(rand.Uint64()))
utils.Check("[quickCert()] Failed to make CA TLS:", err)
serverTls, err := certgen.MakeServerTls(caTls, pkix.Name{CommonName: "summer.test"}, new(big.Int).SetUint64(rand.Uint64()), []string{"summer.test", "*.summer.test"}, []net.IP{})
utils.Check("[quickCert()] Failed to make server TLS:", err)
cert := serverTls.GetTlsLeaf()
leaf := certgen.TlsLeaf(&cert)
c := certificate.Certificate{
Owner: 1,
LetsEncrypt: 1,
Namesilo: 1,
AutoRenew: utils.PBool(false),
Active: utils.PBool(true),
Renewing: utils.PBool(false),
RenewFailed: utils.PBool(false),
}
_, err = db.Insert(&c)
utils.Check("[quickCert()] Failed to insert certificate:", err)
_, err = db.Insert(&certificate.CertificateData{
MetaId: c.Id,
PreviousCert: 0,
CertificateBytes: serverTls.GetCertPem(),
KeyBytes: serverTls.GetKeyPem(),
NotAfter: leaf.NotAfter,
CreatedAt: n,
UpdatedAt: n,
Ready: utils.PBool(true),
})
utils.Check("[quickCert()] Failed to insert certificate data:", err)
_, err = db.Insert(&certificate.CertificateDomain{
DomainId: 0,
CertId: c.Id,
Domain: "summer.test",
Wildcard: utils.PBool(false),
}, &certificate.CertificateDomain{
DomainId: 0,
CertId: c.Id,
Domain: "summer.test",
Wildcard: utils.PBool(true),
})
utils.Check("[quickCert()] Failed to insert certificate domains:", err)
log.Println("[quickCert()] Finished generating development certificate")
}
func quickDomain(db *xorm.Engine) {
log.Println("[quickDomain()] Generating development domain for testing azalea")
count, err := db.Count(&web.Domain{})
utils.Check("[quickDomain()] Failed to count domains:", err)
if count != 0 {
return
}
_, err = db.Insert(&web.Domain{Domain: "summer.test"})
utils.Check("[quickDomain()] Failed to insert domain:", err)
}
func quickApi(db *xorm.Engine) {
log.Println("[quickApi()] Generating development API for testing azalea")
count, err := db.Count(&web.ApiDomain{})
utils.Check("[quickApi()] Failed to count API domains:", err)
if count == 0 {
_, err = db.Insert(&web.ApiDomain{
Domain: "api.summer.test",
Enabled: utils.PBool(true),
})
utils.Check("[quickApi()] Failed to insert API domain:", err)
}
apiDomain := web.ApiDomain{Domain: "api.summer.test"}
get, err := db.Get(&apiDomain)
utils.Check("[quickApi()] Failed to get API domain:", err)
if !get {
utils.Check("[quickApi()] Failed to get API domain:", fmt.Errorf("no such record"))
}
count, err = db.Count(&web.ApiRoute{})
utils.Check("[quickApi()] Failed to count API routes:", err)
if count == 0 {
_, err = db.Insert(&web.ApiRoute{
DomainId: apiDomain.Id,
Version: 1,
Route: "azalea",
Dst: "azalea:7070",
SecureMode: utils.PBool(false),
IgnoreCert: utils.PBool(false),
Enabled: utils.PBool(true),
})
_, err = db.Insert(&web.ApiRoute{
DomainId: apiDomain.Id,
Version: 1,
Route: "buttercup",
Dst: "buttercup:7070",
SecureMode: utils.PBool(false),
IgnoreCert: utils.PBool(false),
Enabled: utils.PBool(true),
})
_, err = db.Insert(&web.ApiRoute{
DomainId: apiDomain.Id,
Version: 1,
Route: "marigold",
Dst: "marigold:7070",
SecureMode: utils.PBool(false),
IgnoreCert: utils.PBool(false),
Enabled: utils.PBool(true),
})
_, err = db.Insert(&web.ApiRoute{
DomainId: apiDomain.Id,
Version: 1,
Route: "rose",
Dst: "rose:7070",
SecureMode: utils.PBool(false),
IgnoreCert: utils.PBool(false),
Enabled: utils.PBool(true),
})
}
}

View File

@ -1,4 +1,4 @@
package quick_cert
package quick_db
import (
"code.mrmelon54.com/melon/summer/pkg/tables/certificate"

View File

@ -1,6 +1,6 @@
//go:build !DEBUG && !TEST
package quick_cert
package quick_db
import "xorm.io/xorm"

View File

@ -1,10 +1,12 @@
package routing
import (
"bytes"
"code.mrmelon54.com/melon/summer/pkg/proxy"
"code.mrmelon54.com/melon/summer/pkg/utils"
"fmt"
"github.com/gorilla/handlers"
"io"
"net/http"
"net/http/httputil"
"net/url"
@ -142,13 +144,13 @@ func (s ServeApiRoute) internalServeHTTP(rw http.ResponseWriter, req *http.Reque
s.Port = 80
}
}
fmt.Println(req.URL.Path)
if !strings.HasPrefix(req.URL.Path, s.PathPre) {
utils.RespondHttpStatus(rw, http.StatusExpectationFailed)
return
}
p1 := req.URL.Path[len(s.PathPre):]
fmt.Println(p1)
buf := new(bytes.Buffer)
_, _ = io.Copy(buf, req.Body)
u := url.URL{
Scheme: scheme,
@ -156,7 +158,7 @@ func (s ServeApiRoute) internalServeHTTP(rw http.ResponseWriter, req *http.Reque
Path: p1,
RawQuery: req.URL.RawQuery,
}
req2, err := http.NewRequest(req.Method, u.String(), req.Body)
req2, err := http.NewRequest(req.Method, u.String(), buf)
if err != nil {
utils.RespondHttpStatus(rw, http.StatusBadGateway)
return

9
cmd/buttercup/Dockerfile Normal file
View File

@ -0,0 +1,9 @@
FROM golang:1.19
WORKDIR /go/src/app
COPY ../.. .
RUN go get -d -v ./...
RUN ./scripts/build.sh "/bin/buttercup" "./cmd/buttercup/"
CMD ["buttercup"]

View File

@ -0,0 +1,9 @@
FROM golang:1.19
WORKDIR /go/src/app
COPY ../.. .
RUN go get -d -v ./...
RUN ./scripts/build.sh "/bin/buttercup" "./cmd/buttercup/" "DEBUG"
CMD ["buttercup"]

View File

@ -24,21 +24,21 @@ var usedTables = []any{
}
func main() {
conf := cli.DecodeConfig[ButtercupConfig]("buttercup")
cli.Run("buttercup", conf.Database, &Buttercup{conf: conf})
cli.RunWithConfig[ButtercupConfig]("buttercup", func(t ButtercupConfig) string { return t.Database }, new(Buttercup))
}
type Buttercup struct {
conf ButtercupConfig
runner *cli.Runner
runner *cli.Runner[ButtercupConfig]
renewal *renewal.Renewal
renewalWg *sync.WaitGroup
apiServer *http.Server
verify mjwt.Provider
}
func (buttercup *Buttercup) Init(runner *cli.Runner) {
func (buttercup *Buttercup) Init(runner *cli.Runner[ButtercupConfig]) {
buttercup.runner = runner
buttercup.conf = runner.Config
utils.Check("[Buttercup.Init()] Failed to sync tables", runner.Database.Sync(usedTables...))
var err error
buttercup.verify, err = mjwt.NewMJwtVerifierFromFile(buttercup.conf.Auth.Public)

9
cmd/marigold/Dockerfile Normal file
View File

@ -0,0 +1,9 @@
FROM golang:1.19
WORKDIR /go/src/app
COPY ../.. .
RUN go get -d -v ./...
RUN ./scripts/build.sh "/bin/marigold" "./cmd/marigold/"
CMD ["marigold"]

View File

@ -0,0 +1,9 @@
FROM golang:1.19
WORKDIR /go/src/app
COPY ../.. .
RUN go get -d -v ./...
RUN ./scripts/build.sh "/bin/marigold" "./cmd/marigold/" "DEBUG"
CMD ["marigold"]

View File

@ -38,13 +38,12 @@ var usedTables = []any{
}
func main() {
conf := cli.DecodeConfig[MarigoldConfig]("marigold")
cli.Run("marigold", conf.Database, &Marigold{conf: conf})
cli.RunWithConfig[MarigoldConfig]("marigold", func(t MarigoldConfig) string { return t.Database }, new(Marigold))
}
type Marigold struct {
conf MarigoldConfig
runner *cli.Runner
runner *cli.Runner[MarigoldConfig]
server *http.Server
signer mjwt.Provider
oauthManager *manage.Manager
@ -55,8 +54,9 @@ type Marigold struct {
mailer *mailer.Mailer
}
func (marigold *Marigold) Init(runner *cli.Runner) {
func (marigold *Marigold) Init(runner *cli.Runner[MarigoldConfig]) {
marigold.runner = runner
marigold.conf = runner.Config
utils.Check("[Marigold.Init()] Failed to sync tables", runner.Database.Sync(usedTables...))
quickUser.QuickUser(runner.Database)

9
cmd/rose/Dockerfile Normal file
View File

@ -0,0 +1,9 @@
FROM golang:1.19
WORKDIR /go/src/app
COPY ../.. .
RUN go get -d -v ./...
RUN ./scripts/build.sh "/bin/rose" "./cmd/rose/"
CMD ["rose"]

View File

@ -0,0 +1,9 @@
FROM golang:1.19
WORKDIR /go/src/app
COPY ../.. .
RUN go get -d -v ./...
RUN ./scripts/build.sh "/bin/rose" "./cmd/rose/" "DEBUG"
CMD ["rose"]

View File

@ -19,21 +19,21 @@ var usedTables = []any{
}
func main() {
conf := cli.DecodeConfig[RoseConfig]("rose")
cli.Run("rose", conf.Database, &Rose{conf: conf})
cli.RunWithConfig[RoseConfig]("rose", func(t RoseConfig) string { return t.Database }, new(Rose))
}
type Rose struct {
conf RoseConfig
runner *cli.Runner
runner *cli.Runner[RoseConfig]
server *http.Server
tcpApi *tcp.Manager
udpApi *udp.Manager
verify mjwt.Provider
}
func (rose *Rose) Init(runner *cli.Runner) {
func (rose *Rose) Init(runner *cli.Runner[RoseConfig]) {
rose.runner = runner
rose.conf = runner.Config
utils.Check("[Rose.Init()] Failed to sync tables", runner.Database.Sync(usedTables...))
var err error
rose.verify, err = mjwt.NewMJwtVerifierFromFile(rose.conf.Auth.Public)
@ -44,6 +44,10 @@ func (rose *Rose) Init(runner *cli.Runner) {
rose.udpApi = udp.NewUdpManager(runner.Database)
crud.NewCrudHandler[web.TcpRedirect](router, rose.verify, crud.NewSinglePermProvider[web.TcpRedirect]("manager:tcp", crud.NewDatabaseProvider[web.TcpRedirect](runner.Database, "id")), "/tcp", "/tcp/{id}")
crud.NewCrudHandler[web.UdpRedirect](router, rose.verify, crud.NewSinglePermProvider[web.UdpRedirect]("manager:udp", crud.NewDatabaseProvider[web.UdpRedirect](runner.Database, "id")), "/udp", "/udp/{id}")
router.HandleFunc("/compile", func(rw http.ResponseWriter, req *http.Request) {
rose.tcpApi.Compile()
rose.udpApi.Compile()
})
rose.server = api.RunApiServer(rose.conf.Listen, router)
}

9
config/azalea.config.yml Normal file
View File

@ -0,0 +1,9 @@
database: summer:summer@tcp(database:3306)/summer?charset=utf8&parseTime=True&loc=Local
listen:
http: :80
https: :443
api: :7070
auth:
public: /etc/melon-summer/keys/public.key.pem
apiDomain: api.summer.test
rateLimit: 300

View File

@ -0,0 +1,8 @@
database: summer:summer@tcp(database:3306)/summer?charset=utf8&parseTime=True&loc=Local
listen: :7070
renewal:
letsEncrypt:
directory: https://localhost:14000/dir
certificate: pebble
auth:
public: /etc/melon-summer/keys/public.key.pem

View File

@ -0,0 +1,22 @@
database: summer:summer@tcp(database:3306)/summer?charset=utf8&parseTime=True&loc=Local
listen: :7070
auth:
issuer: http://localhost:7070
authClient: https://id.summer.test:5443/oauth/authorize
key: /etc/melon-summer/keys/private.key.pem
public: /etc/melon-summer/keys/public.key.pem
smtp:
startTls: false
tls: false
server: mailhog:1025
from: No-Reply <noreply@example.com>
username: noreply@example.com
password: no
user:
default-icons:
- https://cdn.mrmelon54.com/assets/default-icons/0.svg
- https://cdn.mrmelon54.com/assets/default-icons/1.svg
- https://cdn.mrmelon54.com/assets/default-icons/2.svg
- https://cdn.mrmelon54.com/assets/default-icons/3.svg
- https://cdn.mrmelon54.com/assets/default-icons/4.svg
- https://cdn.mrmelon54.com/assets/default-icons/5.svg

4
config/rose.config.yml Normal file
View File

@ -0,0 +1,4 @@
database: summer:summer@tcp(database:3306)/summer?charset=utf8&parseTime=True&loc=Local
listen: :7070
auth:
public: /etc/melon-summer/keys/public.key.pem

View File

@ -0,0 +1,97 @@
version: '3.8'
x-common:
database:
&db-environment
MARIADB_PASSWORD: "summer"
MARIADB_ROOT_PASSWORD: "summer"
services:
pebble:
image: letsencrypt/pebble:latest
environment:
PEBBLE_VA_NOSLEEP: 1
mailhog:
image: mailhog/mailhog:latest
ports:
- "8025:8025"
database:
image: mariadb:10
restart: always
volumes:
- ".data/mariadb-test:/var/lib/mysql"
environment:
<<: *db-environment
MARIADB_USER: summer
MARIADB_DATABASE: summer
ports:
- "4406:3306"
azalea:
restart: on-failure
image: mrmelon54/summer-azalea
build:
context: .
dockerfile: cmd/azalea/development.Dockerfile
volumes:
- ".data/keys:/etc/melon-summer/keys"
- "./config/azalea.config.yml:/etc/melon-summer/azalea.config.yml:ro"
ports:
- "8080:80"
- "8443:443"
links:
- database
- cli
depends_on:
- database
buttercup:
restart: on-failure
image: mrmelon54/summer-buttercup
build:
context: .
dockerfile: cmd/buttercup/development.Dockerfile
volumes:
- ".data/keys:/etc/melon-summer/keys"
- "./config/buttercup.config.yml:/etc/melon-summer/buttercup.config.yml:ro"
links:
- database
- pebble
- cli
depends_on:
- database
- pebble
marigold:
restart: on-failure
image: mrmelon54/summer-marigold
build:
context: .
dockerfile: cmd/marigold/development.Dockerfile
volumes:
- "./.data/keys:/etc/melon-summer/keys"
- "./config/marigold.config.yml:/etc/melon-summer/marigold.config.yml:ro"
links:
- database
- cli
depends_on:
- database
rose:
restart: on-failure
image: mrmelon54/summer-rose
build:
context: .
dockerfile: cmd/rose/development.Dockerfile
volumes:
- "./.data/keys:/etc/melon-summer/keys"
- "./config/rose.config.yml:/etc/melon-summer/rose.config.yml:ro"
ports:
- "1234:1234"
- "4321:4321"
- "1234:1234/udp"
- "4321:4321/udp"
links:
- database
- cli
depends_on:
- database
cli:
restart: on-failure
image: ubuntu
entrypoint: sleep infinity

View File

@ -0,0 +1,83 @@
version: '3.8'
x-common:
database:
&db-environment
MYSQL_PASSWORD: "CHANGE_ME"
MYSQL_ROOT_PASSWORD: "CHANGE_ME_TOO"
services:
database:
image: mariadb:10
restart: always
volumes:
- ".data/mariadb-test:/var/lib/mysql"
environment:
<<: *db-environment
MARIADB_USER: summer
MARIADB_DATABASE: summer
azalea:
restart: on-failure
image: mrmelon54/summer-azalea
build:
context: .
dockerfile: cmd/azalea/Dockerfile
volumes:
- ".data/keys:/etc/melon-summer/keys"
- "./config/azalea.config.yml:/etc/melon-summer/azalea.config.yml:ro"
ports:
- "8080:80"
- "8443:443"
links:
- database
- cli
depends_on:
- database
buttercup:
restart: on-failure
image: mrmelon54/summer-buttercup
build:
context: .
dockerfile: cmd/buttercup/Dockerfile
volumes:
- ".data/keys:/etc/melon-summer/keys"
- "./config/buttercup.config.yml:/etc/melon-summer/buttercup.config.yml:ro"
links:
- database
- pebble
- cli
depends_on:
- database
- pebble
marigold:
restart: on-failure
image: mrmelon54/summer-marigold
build:
context: .
dockerfile: cmd/marigold/Dockerfile
volumes:
- "./.data/keys:/etc/melon-summer/keys"
- "./config/marigold.config.yml:/etc/melon-summer/marigold.config.yml:ro"
links:
- database
- cli
depends_on:
- database
rose:
restart: on-failure
image: mrmelon54/summer-rose
build:
context: .
dockerfile: cmd/rose/Dockerfile
volumes:
- "./.data/keys:/etc/melon-summer/keys"
- "./config/rose.config.yml:/etc/melon-summer/rose.config.yml:ro"
ports:
- "1234:1234"
- "4321:4321"
- "1234:1234/udp"
- "4321:4321/udp"
links:
- database
- cli
depends_on:
- database

1
go.mod
View File

@ -4,6 +4,7 @@ go 1.19
require (
code.mrmelon54.com/melon/certgen v0.0.0-20220830133534-0fb4cb7e67d1
github.com/MrMelon54/inactive v0.0.0-20230108181516-4e441eef21a8
github.com/emersion/go-message v0.16.0
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
github.com/emersion/go-smtp v0.15.0

2
go.sum
View File

@ -21,6 +21,8 @@ github.com/ByteArena/poly2tri-go v0.0.0-20170716161910-d102ad91854f/go.mod h1:vI
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/MrMelon54/inactive v0.0.0-20230108181516-4e441eef21a8 h1:6ydh2VCGQy/n/LfiEeSbwkZdH9+HrB1uP5w+sRoy+Ms=
github.com/MrMelon54/inactive v0.0.0-20230108181516-4e441eef21a8/go.mod h1:CMWdZcEOd6UuzghGGxME9SWCfeIs8i6YOhw7vOMkNfM=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=

View File

@ -1,19 +0,0 @@
//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")
}

View File

@ -1,11 +0,0 @@
//go:build !DEBUG
package cli
import (
"fmt"
)
func getConfigPath(software string) string {
return fmt.Sprintf("%s.conf.yml", software)
}

View File

@ -15,16 +15,39 @@ import (
"xorm.io/xorm"
)
func Run(software, dbString string, executor Executor) {
func Run(software, dbString string, executor Executor[any]) {
preSetup(software, nil)
postSetup[any](software, dbString, nil, executor)
}
func RunWithConfig[T any](software string, dbFunc func(t T) string, executor Executor[T]) {
var configPath string
preSetup(software, &configPath)
open, err := os.Open(configPath)
utils.Check("Failed to open config file", err)
decoder := yaml.NewDecoder(open)
t := *new(T)
utils.Check("Failed to decode config file", decoder.Decode(&t))
postSetup(software, dbFunc(t), t, executor)
}
func preSetup(software string, configPath *string) {
rand.Seed(time.Now().UnixMilli())
var version bool
flag.BoolVar(&version, "version", false, "Show program version")
if configPath != nil {
flag.StringVar(configPath, "config", fmt.Sprintf("/etc/melon-summer/%s.config.yml", software), fmt.Sprintf("Path to config file for %s", software))
}
flag.Parse()
if version {
fmt.Printf("%s %s\n", software, utils.BuildVersion)
return
}
}
func postSetup[T any](software string, dbString string, t T, executor Executor[T]) {
log.Printf("[Main] Starting up %s (%s from %s)\n", software, utils.BuildVersion, utils.BuildDate)
// Load database
@ -32,7 +55,7 @@ func Run(software, dbString string, executor Executor) {
utils.Check("Failed to connect to database", err)
// Start runner
runner := &Runner{software: software, executor: executor, Database: engine}
runner := &Runner[T]{software: software, executor: executor, Config: t, Database: engine}
executor.Init(runner)
// Wait for exit signal
@ -49,22 +72,14 @@ func Run(software, dbString string, executor Executor) {
log.Println("[Main] Goodbye")
}
func DecodeConfig[T any](software string) (t T) {
f := getConfigPath(software)
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))
return
}
type Executor interface {
Init(*Runner)
type Executor[T any] interface {
Init(*Runner[T])
Destroy()
}
type Runner struct {
type Runner[T any] struct {
software string
executor Executor
executor Executor[T]
Config T
Database *xorm.Engine
}

View File

@ -23,8 +23,14 @@ func NewTcpManager(db *xorm.Engine) *Manager {
servers: make(map[uint64]*Proxy),
db: db,
}
m.Compile()
return m
}
func (m *Manager) Compile() {
m.empty()
var a []web.TcpRedirect
err := db.Where("enabled = ?", true).Find(&a)
err := m.db.Where("enabled = ?", true).Find(&a)
if err != nil {
log.Println("[TcpManager] Failed to open startup TCP ports")
a = []web.TcpRedirect{}
@ -32,7 +38,6 @@ func NewTcpManager(db *xorm.Engine) *Manager {
for _, i := range a {
m.Add(i)
}
return m
}
func (m *Manager) Open(n uint64, lAddr, rAddr *net.TCPAddr) error {
@ -57,8 +62,7 @@ func (m *Manager) Close(n uint64) {
m.mutex.Unlock()
}
func (m *Manager) Destroy() {
log.Println("[TcpManager] Destroying TCP Manager")
func (m *Manager) empty() {
wg := &sync.WaitGroup{}
m.mutex.Lock()
for _, p := range m.servers {
@ -73,6 +77,11 @@ func (m *Manager) Destroy() {
m.mutex.Unlock()
}
func (m *Manager) Destroy() {
log.Println("[TcpManager] Destroying TCP Manager")
m.empty()
}
func (m *Manager) Add(item web.TcpRedirect) {
lAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf(":%d", item.Port))
if err != nil {
@ -84,17 +93,3 @@ func (m *Manager) Add(item web.TcpRedirect) {
}
_ = m.Open(item.Id, lAddr, rAddr)
}
func (m *Manager) Put(id uint64, item web.TcpRedirect) {
m.Close(id)
m.Add(item)
}
func (m *Manager) Patch(id uint64, item web.TcpRedirect) {
m.Close(id)
m.Add(item)
}
func (m *Manager) Delete(id uint64) {
m.Close(id)
}

View File

@ -23,8 +23,14 @@ func NewUdpManager(db *xorm.Engine) *Manager {
servers: make(map[uint64]*Proxy),
db: db,
}
m.Compile()
return m
}
func (m *Manager) Compile() {
m.empty()
var a []web.UdpRedirect
err := db.Where("enabled = ?", true).Find(&a)
err := m.db.Where("enabled = ?", true).Find(&a)
if err != nil {
log.Println("[UdpManager] Failed to open startup UDP ports")
a = []web.UdpRedirect{}
@ -32,7 +38,6 @@ func NewUdpManager(db *xorm.Engine) *Manager {
for _, i := range a {
m.Add(i)
}
return m
}
func (m *Manager) Open(n uint64, lAddr, rAddr *net.UDPAddr) error {
@ -57,8 +62,7 @@ func (m *Manager) Close(n uint64) {
m.mutex.Unlock()
}
func (m *Manager) Destroy() {
log.Println("[UdpManager] Destroying UDP Manager")
func (m *Manager) empty() {
wg := &sync.WaitGroup{}
m.mutex.Lock()
for _, p := range m.servers {
@ -73,6 +77,11 @@ func (m *Manager) Destroy() {
m.mutex.Unlock()
}
func (m *Manager) Destroy() {
log.Println("[UdpManager] Destroying UDP Manager")
m.empty()
}
func (m *Manager) Add(item web.UdpRedirect) {
lAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", item.Port))
if err != nil {
@ -84,18 +93,3 @@ func (m *Manager) Add(item web.UdpRedirect) {
}
_ = m.Open(item.Id, lAddr, rAddr)
}
func (m *Manager) Put(id uint64, item web.UdpRedirect) {
//TODO implement me
panic("implement me")
}
func (m *Manager) Patch(id uint64, item web.UdpRedirect) {
//TODO implement me
panic("implement me")
}
func (m *Manager) Delete(id uint64) {
//TODO implement me
panic("implement me")
}

View File

@ -14,7 +14,7 @@ type Proxy struct {
l *net.UDPConn
n uint64
cMux *sync.RWMutex
cMap map[*net.UDPAddr]*SingleConn
cMap map[string]*SingleConn
}
type SingleConn struct {
@ -28,7 +28,7 @@ func NewUdpProxy(lAddr, rAddr *net.UDPAddr) (*Proxy, error) {
rAddr: rAddr,
done: utils.NewDoneChan(),
cMux: &sync.RWMutex{},
cMap: make(map[*net.UDPAddr]*SingleConn),
cMap: make(map[string]*SingleConn),
}
l, err := net.ListenUDP("udp", p.lAddr)
if err != nil {
@ -57,11 +57,11 @@ outer:
data := buf[:i]
p.cMux.RLock()
c, ok := p.cMap[addr]
c, ok := p.cMap[addr.String()]
p.cMux.RUnlock()
if !ok {
conn := p.setupConn(addr)
go p.runConn(conn)
c = p.setupConn(addr)
go p.runConn(c)
}
// relay to server
@ -73,7 +73,7 @@ outer:
}
func (p *Proxy) setupConn(addr *net.UDPAddr) *SingleConn {
r, err := net.DialUDP("", nil, p.rAddr)
r, err := net.DialUDP("udp", nil, p.rAddr)
if err != nil {
log.Printf("[WARN %s @ %s] Dial UDP failed '%s'\n", addr, p.lAddr, err)
return nil
@ -83,7 +83,7 @@ func (p *Proxy) setupConn(addr *net.UDPAddr) *SingleConn {
r: r,
}
p.cMux.Lock()
p.cMap[addr] = c
p.cMap[addr.String()] = c
p.cMux.Unlock()
return c
}
@ -107,6 +107,6 @@ func (p *Proxy) runConn(conn *SingleConn) {
}
}
p.cMux.Lock()
delete(p.cMap, conn.l)
delete(p.cMap, conn.l.String())
p.cMux.Unlock()
}

View File

@ -1,12 +1,6 @@
#!/bin/bash
BUILD_DIR="$1"
CC="$2"
LD_FLAGS="$3"
PROGRAMS="$4"
TAGS="$5"
for PROG in ${PROGRAMS}
do
echo -e "\e[32m=== Building ${PROG} ===\e[0m"
${CC} build -o "${BUILD_DIR}/dist/${PROG}" -ldflags="${LD_FLAGS}" -tags="${TAGS}" "${BUILD_DIR}/cmd/${PROG}"
done
VERSION=$(git describe --dirty --always)
BUILD_DATE=$(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}.BuildDate=${BUILD_DATE}'"
go build -o "$1" -ldflags="$LD_FLAGS" -tags "$3" -v "$2"

View File

@ -1,27 +0,0 @@
#!/bin/bash
function countdown() {
for i in $(seq "$1" -1 1); do
echo -ne "\r$i "
sleep 1
done
}
PROGRAMS="$1"
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
done
exit 0

View File

@ -1,90 +0,0 @@
#!/bin/bash
BUILD_DIR="$1"
PROGRAMS="$2"
mkdir -p .data/keys/
function countdown() {
for i in $(seq "$1" -1 1); do
echo -ne "\r$i "
sleep 1
done
}
if [ "$(docker network inspect summer | jq '.[0].Name' -r)" != "summer" ]; then
docker network create -d bridge summer
fi
if [ "$(docker inspect summer-pebble 2>/dev/null)" = "[]" ]; then
echo -e "\e[31m=== Container missing, creating pebble ===\e[0m"
docker run -d \
--network summer \
--hostname pebble \
--name summer-pebble \
-e "PEBBLE_VA_NOSLEEP=1" \
-p 14000:14000 \
-p 15000:15000 \
letsencrypt/pebble:latest
echo -e "\e[33m=== Sleeping for 30 seconds to allow pebble setup === \e[0m"
countdown 30
echo -e "\rFinished"
fi
echo -e "\e[32m=== Setting up Docker container for summer database ===\e[0m"
if [ "$(docker inspect summer-mariadb 2>/dev/null)" = "[]" ]; then
echo -e "\e[31m=== Container missing, creating database ===\e[0m"
docker run -d \
-v "${BUILD_DIR}/.data/mariadb-test:/var/lib/mysql" \
-e MARIADB_ROOT_PASSWORD=my-secret-pw \
-e MARIADB_USER=summer \
-e MARIADB_PASSWORD=summer \
-e MARIADB_DATABASE=summer \
--network summer \
--name summer-mariadb \
--hostname summer-mariadb \
-p 4406:3306 \
mariadb:latest
echo -e "\e[33m=== Sleeping for 1 minute to allow mariadb setup ===\e[0m"
countdown 60
echo -e "\rFinished"
fi
for PROG in ${PROGRAMS}; do
N="${PROG}-test"
echo -e "\e[32m=== Setting up Docker container for ${PROG} ===\e[0m"
if [ "$(docker inspect "${N}" 2>/dev/null)" != "[]" ]; then
if [ "$(docker inspect "${N}" | jq '.[0].State.Status' -r)" == "running" ]; then
docker stop "${N}" >/dev/null
fi
if [ "$(docker inspect "${N}" | jq '.[0].State.Status' -r)" == "exited" ]; then
docker rm "${N}" >/dev/null
fi
fi
A=""
if [ "${PROG}" = "azalea" ]; then
A="-p 8080:80 -p 8443:443 --net-alias summer.test --net-alias api.summer.test"
fi
# shellcheck disable=SC2086
docker run -d \
-v "${BUILD_DIR}/dist/${PROG}:/bin/${PROG}" \
-v "${BUILD_DIR}/cmd/${PROG}/config.yml:/etc/melon-summer/conf/${PROG}.yml" \
-v "${BUILD_DIR}/.data/keys:/etc/melon-summer/keys" \
--name "${N}" \
--hostname "${N}" \
--network summer \
--entrypoint "/bin/${PROG}" \
$A ubuntu:latest
done
echo -e "\e[33m=== Sleeping for 20 seconds to allow program setup ===\e[0m"
countdown 20
echo -e "\rFinished"
echo -e "\e[33m=== Setup is done, restarting containers ===\e[0m"
"${BUILD_DIR}/scripts/restart-docker.sh" "${PROGRAMS}"
exit 0