feat: publish standalone worker

Separate worker packaging and service lifecycle from the control plane.
Этот коммит содержится в:
Gleb Tv
2026-07-13 17:55:14 +03:00
Коммит 2c7a0236da
309 изменённых файлов: 44004 добавлений и 0 удалений

46
internal/netaddr/cidr.go Обычный файл
Просмотреть файл

@@ -0,0 +1,46 @@
// Package netaddr provides network address utilities for RSMon.
package netaddr
import (
"database/sql/driver"
"errors"
"net"
)
// Cidr is a wrapper for transferring CIDR values back and forth easily.
type Cidr struct {
Cidr net.IPNet
Valid bool
}
// Scan implements the Scanner interface.
func (c *Cidr) Scan(value interface{}) error {
c.Cidr.IP = nil
c.Cidr.Mask = nil
c.Valid = false
if value == nil {
c.Valid = false
return nil
}
cidrAsBytes, ok := value.([]byte)
if !ok {
return errors.New("could not convert scanned value to bytes")
}
_, parsedIPNet, parseErr := net.ParseCIDR(string(cidrAsBytes))
if parseErr != nil {
return parseErr
}
c.Valid = true
c.Cidr.IP = parsedIPNet.IP
c.Cidr.Mask = parsedIPNet.Mask
return nil
}
// Value implements the driver Valuer interface. Note if c.Valid is false
// or c.Cidr.IP is nil the database column value will be set to NULL.
func (c Cidr) Value() (driver.Value, error) {
if !c.Valid || c.Cidr.IP == nil {
return nil, nil
}
return []byte(c.Cidr.String()), nil
}

116
internal/netaddr/cidr_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,116 @@
package netaddr
import (
"bytes"
"net"
"testing"
_ "github.com/lib/pq"
)
func TestCidr(t *testing.T) {
db := openTestConn(t)
defer db.Close()
cidr := Cidr{}
// Test scanning NULL values
err := db.QueryRow("SELECT NULL::cidr").Scan(&cidr)
if err != nil {
t.Fatal(err)
}
if cidr.Valid {
t.Fatalf("expected null result")
}
// Test setting NULL values
err = db.QueryRow("SELECT $1::cidr", cidr).Scan(&cidr)
if err != nil {
t.Fatalf("re-query null value failed: %s", err.Error())
}
if cidr.Valid {
t.Fatalf("expected null result")
}
// test encoding in query params, then decoding during Scan
testBidirectional := func(c Cidr, label string) {
err = db.QueryRow("SELECT $1::cidr", c).Scan(&cidr)
if err != nil {
t.Fatalf("re-query %s cidr failed: %s", label, err.Error())
}
if !cidr.Valid {
t.Fatalf("expected non-null value, got null for %s", label)
}
if !net.IP.Equal(c.Cidr.IP, cidr.Cidr.IP) {
t.Fatalf("expected IP addresses to match, but did not for %s - %s %s", label, c.Cidr.IP.String(), cidr.Cidr.IP.String())
}
if !bytes.Equal(c.Cidr.Mask, cidr.Cidr.Mask) {
t.Fatalf("expected net masks to match, but did not for %s", label)
}
}
// a few example CIDRs to test out
_, exampleCidr, err := net.ParseCIDR("135.104.0.0/32")
if err != nil {
t.Fatalf("Fatal error while building simple IP example - %s", err.Error())
}
simpleIP4 := Cidr{Cidr: *exampleCidr, Valid: true}
testBidirectional(simpleIP4, "Simple IPv4")
_, exampleCidr, err = net.ParseCIDR("0.0.0.0/24")
if err != nil {
t.Fatalf("Fatal error while building Zero IP example - %s", err.Error())
}
zeroIP4Subnet := Cidr{Cidr: *exampleCidr, Valid: true}
testBidirectional(zeroIP4Subnet, "Zero IPv4 Subnet")
_, exampleCidr, err = net.ParseCIDR("135.104.0.0/24")
if err != nil {
t.Fatalf("Fatal error while building simple IPv4 subnet example - %s", err.Error())
}
simpleIP4Subnet := Cidr{Cidr: *exampleCidr, Valid: true}
testBidirectional(simpleIP4Subnet, "Simple IPv4 Subnet")
_, exampleCidr, err = net.ParseCIDR("::1/128")
if err != nil {
t.Fatalf("Fatal error while building simple IPv6 loopback example - %s", err.Error())
}
ip6Loopback := Cidr{Cidr: *exampleCidr, Valid: true}
testBidirectional(ip6Loopback, "IPv6 Loopback")
_, exampleCidr, err = net.ParseCIDR("abcd:2345::/65")
if err != nil {
t.Fatalf("Fatal error while building simple IPv6 subnet example - %s", err.Error())
}
ip6Subnet := Cidr{Cidr: *exampleCidr, Valid: true}
testBidirectional(ip6Subnet, "IPv6 Subnet #1")
_, exampleCidr, err = net.ParseCIDR("abcd:2300::/24")
if err != nil {
t.Fatalf("Fatal error while building simple IPv6 subnet #2 example - %s", err.Error())
}
ip6Subnet2 := Cidr{Cidr: *exampleCidr, Valid: true}
testBidirectional(ip6Subnet2, "IPv6 Subnet #2")
_, exampleCidr, err = net.ParseCIDR("2001:DB8::1/48")
if err != nil {
t.Fatalf("Fatal error while building simple IPv6 subnet #3 example - %s", err.Error())
}
ip6Subnet3 := Cidr{Cidr: *exampleCidr, Valid: true}
testBidirectional(ip6Subnet3, "IPv6 Subnet #3")
// Error handling
// Bad argument
cidr = Cidr{}
err = cidr.Scan(456)
if err == nil {
t.Fatal("Expected error for non-byte[] argument to Scan")
}
cidr = Cidr{}
err = cidr.Scan([]byte(""))
if err == nil {
t.Fatalf("Expected error for invalid CIDR")
}
}

39
internal/netaddr/inet.go Обычный файл
Просмотреть файл

@@ -0,0 +1,39 @@
package netaddr
import (
"database/sql/driver"
"errors"
"net"
)
// Inet is a wrapper for transferring Inet values back and forth easily.
type Inet struct {
Inet net.IP
}
// Scan implements the Scanner interface.
func (i *Inet) Scan(value interface{}) error {
i.Inet = nil
if value == nil {
return nil
}
ipAsBytes, ok := value.([]byte)
if !ok {
return errors.New("could not convert scanned value to bytes")
}
parsedIP := net.ParseIP(string(ipAsBytes))
if parsedIP == nil {
return nil
}
i.Inet = parsedIP
return nil
}
// Value implements the driver Valuer interface. Note if
// i.IP is nil the database column value will be set to NULL.
func (i Inet) Value() (driver.Value, error) {
if i.Inet == nil {
return nil, nil
}
return []byte(i.Inet.String()), nil
}

67
internal/netaddr/inet_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,67 @@
package netaddr
import (
"net"
"testing"
_ "github.com/lib/pq"
)
func TestInet(t *testing.T) {
db := openTestConn(t)
defer db.Close()
inet := Inet{}
// Test scanning NULL values
err := db.QueryRow("SELECT NULL::inet").Scan(&inet)
if err != nil {
t.Fatal(err)
}
if inet.Inet != nil {
t.Fatalf("expected null result")
}
// Test setting NULL values
err = db.QueryRow("SELECT $1::inet", inet).Scan(&inet)
if err != nil {
t.Fatalf("re-query null value failed: %s", err.Error())
}
if inet.Inet != nil {
t.Fatalf("expected null result")
}
// test encoding in query params, then decoding during Scan
testBidirectional := func(i Inet, label string) {
err = db.QueryRow("SELECT $1::inet", i).Scan(&inet)
if err != nil {
t.Fatalf("re-query %s inet failed: %s", label, err.Error())
}
if inet.Inet == nil {
t.Fatalf("expected non-null value, got null for %s", label)
}
if !net.IP.Equal(i.Inet, inet.Inet) {
t.Fatalf("expected IP addresses to match, but did not for %s - %s %s", label, i.Inet.String(), inet.Inet.String())
}
}
testBidirectional(Inet{Inet: net.ParseIP("192.168.0.1")}, "Simple IPv4")
testBidirectional(Inet{Inet: net.ParseIP("::1")}, "Loopback IPv6")
testBidirectional(Inet{Inet: net.ParseIP("abcd:2345::")}, "Loopback IPv6")
// Bad argument
inet = Inet{}
err = inet.Scan(456)
if err == nil {
t.Fatal("Expected error for non-byte[] argument to Scan")
}
inet = Inet{}
err = inet.Scan([]byte(""))
if err != nil {
t.Fatalf("Unexpected error for empty string - %s", err.Error())
}
if inet.Inet != nil {
t.Fatalf("Unexpected not null for empty/non-IP string string")
}
}

43
internal/netaddr/macaddr.go Обычный файл
Просмотреть файл

@@ -0,0 +1,43 @@
package netaddr
import (
"database/sql/driver"
"errors"
"net"
)
// Macaddr is a wrapper for transferring Macaddr values back and forth easily.
type Macaddr struct {
Macaddr net.HardwareAddr
Valid bool
}
// Scan implements the Scanner interface.
func (m *Macaddr) Scan(value interface{}) error {
m.Macaddr = nil
m.Valid = false
if value == nil {
m.Valid = false
return nil
}
macaddrAsBytes, ok := value.([]byte)
if !ok {
return errors.New("could not convert scanned value to bytes")
}
parsedMacaddr, parseErr := net.ParseMAC(string(macaddrAsBytes))
if parseErr != nil {
return parseErr
}
m.Valid = true
m.Macaddr = parsedMacaddr
return nil
}
// Value implements the driver Valuer interface. Note if m.Valid is false
// or m.Macaddr is nil the database column value will be set to NULL.
func (m Macaddr) Value() (driver.Value, error) {
if !m.Valid || m.Macaddr == nil {
return nil, nil
}
return []byte(m.Macaddr.String()), nil
}

64
internal/netaddr/macaddr_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,64 @@
package netaddr
import (
"bytes"
"net"
"testing"
_ "github.com/lib/pq"
)
func TestMacaddr(t *testing.T) {
db := openTestConn(t)
defer db.Close()
macaddr := Macaddr{}
// Test scanning NULL values
err := db.QueryRow("SELECT NULL::macaddr").Scan(&macaddr)
if err != nil {
t.Fatal(err)
}
if macaddr.Valid {
t.Fatalf("expected null result")
}
// Test setting NULL values
err = db.QueryRow("SELECT $1::macaddr", macaddr).Scan(&macaddr)
if err != nil {
t.Fatalf("re-query null value failed: %s", err.Error())
}
if macaddr.Valid {
t.Fatalf("expected null result")
}
// test encoding in query params, then decoding during Scan
testBidirectional := func(m Macaddr, label string) {
err = db.QueryRow("SELECT $1::macaddr", m).Scan(&macaddr)
if err != nil {
t.Fatalf("re-query %s macaddr failed: %s", label, err.Error())
}
if !macaddr.Valid {
t.Fatalf("expected non-null value, got null for %s", label)
}
if !bytes.Equal(m.Macaddr, macaddr.Macaddr) {
t.Fatalf("expected MAC addresses to match, but did not for %s", label)
}
}
simpleMac := Macaddr{Macaddr: net.HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab}, Valid: true}
testBidirectional(simpleMac, "Simple MAC Address")
// Bad argument
macaddr = Macaddr{}
err = macaddr.Scan(456)
if err == nil {
t.Fatal("Expected error for non-byte[] argument to Scan")
}
macaddr = Macaddr{}
err = macaddr.Scan([]byte(""))
if err == nil {
t.Fatalf("Expected error for invalid Macaddr")
}
}

35
internal/netaddr/main_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,35 @@
package netaddr
import (
"os"
"path/filepath"
"testing"
"github.com/joho/godotenv"
)
// TestMain loads .env.test before any test runs so DATABASE_* env vars
// are populated in this test binary. The netaddr package is intentionally
// low-level and does not import config/env; this TestMain gives it the
// same environment the rest of the test suite sees without pulling in
// the app-wide env init.
func TestMain(m *testing.M) {
loadEnvIfPresent(".env.test")
os.Exit(m.Run())
}
func loadEnvIfPresent(name string) {
candidates := []string{name}
if cwd := os.Getenv("CWD"); cwd != "" {
candidates = append(candidates, filepath.Join(cwd, name))
}
if dir, err := os.Getwd(); err == nil {
candidates = append(candidates, filepath.Join(dir, name))
}
for _, c := range candidates {
if _, err := os.Stat(c); err == nil {
_ = godotenv.Load(c)
return
}
}
}

39
internal/netaddr/testutil.go Обычный файл
Просмотреть файл

@@ -0,0 +1,39 @@
package netaddr
import (
"database/sql"
"os"
_ "github.com/lib/pq" // postgres driver
)
type Fatalistic interface {
Fatal(args ...interface{})
}
func openTestConn(t Fatalistic) *sql.DB {
host := getEnv("DATABASE_HOST", getEnv("DB_HOST", "localhost"))
if os.Getenv("CI") != "" && os.Getenv("DATABASE_HOST") == "" && os.Getenv("DB_HOST") == "" {
host = "postgres"
}
port := getEnv("DATABASE_PORT", getEnv("DB_PORT", "35432"))
user := getEnv("DATABASE_USER", getEnv("DB_USER", "rsmon"))
password := getEnv("DATABASE_PASSWORD", getEnv("DB_PASSWORD", "rsmon"))
dbname := getEnv("DATABASE_NAME", getEnv("DB_NAME", "rsmon_test"))
conn, err := sql.Open(
"postgres",
"host="+host+" port="+port+" user="+user+" password="+password+" dbname="+dbname+" sslmode=disable",
)
if err != nil {
t.Fatal(err)
}
return conn
}
func getEnv(key, fallback string) string {
if value := os.Getenv(key); value != "" {
return value
}
return fallback
}