mirror of
https://github.com/0x1d/rcond.git
synced 2025-12-14 18:25:21 +01:00
feat: introduce YAML configuration
This commit is contained in:
8
Makefile
8
Makefile
@@ -1,5 +1,5 @@
|
||||
SHELL := bash
|
||||
ARCH ?= arm64
|
||||
ARCH ?= amd64
|
||||
ADDR ?= 0.0.0.0:8080
|
||||
|
||||
generate:
|
||||
@@ -11,10 +11,12 @@ build:
|
||||
env GOOS=linux GOARCH=${ARCH} go build -o bin/rcond-${ARCH} ./cmd/rcond/main.go
|
||||
|
||||
run:
|
||||
source .env && bin/rcond-${ARCH} ${ADDR}
|
||||
bin/rcond-${ARCH} -config config.yaml
|
||||
|
||||
dev:
|
||||
RCOND_API_TOKEN=1234567890 go run cmd/rcond/main.go
|
||||
RCOND_ADDR=127.0.0.1:8080 \
|
||||
RCOND_API_TOKEN=1234567890 \
|
||||
go run cmd/rcond/main.go
|
||||
|
||||
upload:
|
||||
scp bin/rcond-${ARCH} pi@rpi-test:/home/pi/rcond
|
||||
|
||||
15
README.md
15
README.md
@@ -6,6 +6,7 @@ A simple daemon and REST API to manage:
|
||||
- authorized SSH keys through the user's authorized_keys file
|
||||
|
||||
## Requirements
|
||||
|
||||
- Make
|
||||
- Go 1.19 or later
|
||||
- NetworkManager
|
||||
@@ -50,6 +51,20 @@ All endpoints except `/health` require authentication via an API token passed in
|
||||
### Request/Response Format
|
||||
All endpoints use JSON for request and response payloads.
|
||||
|
||||
## Configuration
|
||||
|
||||
### File
|
||||
|
||||
The default config file location is `/etc/rcond/config.yaml`.
|
||||
It can be overwritten by environment variables and flags.
|
||||
|
||||
Example configuration:
|
||||
```yaml
|
||||
rcond:
|
||||
addr: 0.0.0.0:8080
|
||||
api_token: 1234567890
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Environment Variable | Description | Default |
|
||||
|
||||
@@ -3,41 +3,105 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/0x1d/rcond/pkg/config"
|
||||
http "github.com/0x1d/rcond/pkg/http"
|
||||
)
|
||||
|
||||
const (
|
||||
NETWORK_CONNECTION_UUID = "7d706027-727c-4d4c-a816-f0e1b99db8ab"
|
||||
)
|
||||
|
||||
func usage() {
|
||||
fmt.Printf("Usage: %s <address>\n", os.Args[0])
|
||||
os.Exit(0)
|
||||
fmt.Println("Usage: rcond <flags>")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
addr := os.Getenv("RCOND_ADDR")
|
||||
if addr == "" {
|
||||
addr = "0.0.0.0:8080"
|
||||
}
|
||||
if len(os.Args) > 1 {
|
||||
addr = os.Args[1]
|
||||
}
|
||||
apiToken := os.Getenv("RCOND_API_TOKEN")
|
||||
if apiToken == "" {
|
||||
log.Fatal("RCOND_API_TOKEN environment variable not set")
|
||||
appConfig, err := loadConfig()
|
||||
if err != nil {
|
||||
usage()
|
||||
fmt.Printf("\nFailed to load config: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
srv := http.NewServer(addr, apiToken)
|
||||
srv := http.NewServer(appConfig)
|
||||
srv.RegisterRoutes()
|
||||
|
||||
log.Printf("Starting server on %s", addr)
|
||||
log.Printf("Starting server on %s", appConfig.Rcond.Addr)
|
||||
if err := srv.Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func loadConfig() (*config.Config, error) {
|
||||
configPath := "/etc/rcond/config.yaml"
|
||||
appConfig := &config.Config{}
|
||||
help := false
|
||||
|
||||
flag.StringVar(&configPath, "config", configPath, "Path to the configuration file")
|
||||
flag.StringVar(&appConfig.Rcond.Addr, "addr", "", "Address to bind the HTTP server to")
|
||||
flag.StringVar(&appConfig.Rcond.ApiToken, "token", "", "API token to use for authentication")
|
||||
flag.BoolVar(&help, "help", false, "Show help")
|
||||
flag.Parse()
|
||||
|
||||
if help {
|
||||
usage()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Load config from file
|
||||
if _, err := os.Stat(configPath); !os.IsNotExist(err) {
|
||||
configFile, err := config.LoadConfig(configPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
appConfig = configFile
|
||||
}
|
||||
|
||||
// Override config values from environment variables and flags
|
||||
overrideConfigValuesFromEnv(map[string]*string{
|
||||
"RCOND_ADDR": &appConfig.Rcond.Addr,
|
||||
"RCOND_API_TOKEN": &appConfig.Rcond.ApiToken,
|
||||
})
|
||||
|
||||
overrideConfigValuesFromFlag(map[string]*string{
|
||||
"addr": &appConfig.Rcond.Addr,
|
||||
"token": &appConfig.Rcond.ApiToken,
|
||||
})
|
||||
|
||||
// Validate required fields
|
||||
if err := validateRequiredFields(map[string]*string{
|
||||
"addr": &appConfig.Rcond.Addr,
|
||||
"token": &appConfig.Rcond.ApiToken,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return appConfig, nil
|
||||
}
|
||||
|
||||
func overrideConfigValuesFromEnv(envMap map[string]*string) {
|
||||
for varName, configValue := range envMap {
|
||||
if envValue, ok := os.LookupEnv(varName); ok {
|
||||
*configValue = envValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func overrideConfigValuesFromFlag(flagMap map[string]*string) {
|
||||
for flagName, configValue := range flagMap {
|
||||
if flagValue := flag.Lookup(flagName).Value.String(); flagValue != "" {
|
||||
*configValue = flagValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validateRequiredFields(fields map[string]*string) error {
|
||||
for name, value := range fields {
|
||||
if *value == "" {
|
||||
return fmt.Errorf("%s is required", name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
3
config.yaml
Normal file
3
config.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
rcond:
|
||||
addr: 0.0.0.0:8080
|
||||
api_token: 1234567890
|
||||
1
go.mod
1
go.mod
@@ -11,6 +11,7 @@ require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/mux v1.8.1
|
||||
golang.org/x/crypto v0.37.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require golang.org/x/sys v0.32.0 // indirect
|
||||
|
||||
4
go.sum
4
go.sum
@@ -10,3 +10,7 @@ golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
13
install/rcond.service
Normal file
13
install/rcond.service
Normal file
@@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=rcond service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/path/to/your/rcond
|
||||
ExecStart=/path/to/your/rcond/bin/rcond-${ARCH} ${ADDR}
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
38
pkg/config/config.go
Normal file
38
pkg/config/config.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Rcond RcondConfig `yaml:"rcond"`
|
||||
}
|
||||
|
||||
type RcondConfig struct {
|
||||
Addr string `yaml:"addr"`
|
||||
ApiToken string `yaml:"api_token"`
|
||||
}
|
||||
|
||||
func LoadConfig(path string) (*Config, error) {
|
||||
yamlFile, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var config Config
|
||||
err = yaml.Unmarshal(yamlFile, &config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
func SaveConfig(path string, config *Config) error {
|
||||
yamlFile, err := yaml.Marshal(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, yamlFile, 0644)
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/0x1d/rcond/pkg/config"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
@@ -15,11 +16,15 @@ type Server struct {
|
||||
apiToken string
|
||||
}
|
||||
|
||||
func NewServer(addr string, apiToken string) *Server {
|
||||
func NewServer(cfg *config.Config) *Server {
|
||||
if cfg.Rcond.Addr == "" || cfg.Rcond.ApiToken == "" {
|
||||
panic("addr or api_token is not set")
|
||||
}
|
||||
|
||||
router := mux.NewRouter()
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: addr,
|
||||
Addr: cfg.Rcond.Addr,
|
||||
Handler: router,
|
||||
ReadTimeout: 15 * time.Second,
|
||||
WriteTimeout: 15 * time.Second,
|
||||
@@ -28,7 +33,7 @@ func NewServer(addr string, apiToken string) *Server {
|
||||
return &Server{
|
||||
router: router,
|
||||
srv: srv,
|
||||
apiToken: apiToken,
|
||||
apiToken: cfg.Rcond.ApiToken,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user