mirror of
https://blitiri.com.ar/repos/chasquid
synced 2025-12-23 15:37:01 +00:00
Basic configuration
This patch introduces a basic on disk configuration, comprised of a main configuration file and per-domain directories. It's still not complete, but will be extended in subsequent patches.
This commit is contained in:
60
internal/config/config.go
Normal file
60
internal/config/config.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// Package config implements the chasquid configuration.
|
||||
package config
|
||||
|
||||
// Generate the config protobuf.
|
||||
//go:generate protoc --go_out=. config.proto
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
// Load the config from the given file.
|
||||
func Load(path string) (*Config, error) {
|
||||
c := &Config{}
|
||||
|
||||
buf, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to read config at %q", path)
|
||||
glog.Errorf(" (%v)", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = proto.UnmarshalText(string(buf), c)
|
||||
if err != nil {
|
||||
glog.Errorf("Error parsing config: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Fill in defaults for anything that's missing.
|
||||
|
||||
if c.Hostname == "" {
|
||||
c.Hostname, err = os.Hostname()
|
||||
if err != nil {
|
||||
glog.Errorf("Could not get hostname: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if c.MaxDataSizeMb == 0 {
|
||||
c.MaxDataSizeMb = 50
|
||||
}
|
||||
|
||||
if len(c.Address) == 0 {
|
||||
c.Address = append(c.Address, "systemd")
|
||||
}
|
||||
|
||||
logConfig(c)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func logConfig(c *Config) {
|
||||
glog.Infof("Configuration:")
|
||||
glog.Infof(" Hostname: %q", c.Hostname)
|
||||
glog.Infof(" Max data size (MB): %d", c.MaxDataSizeMb)
|
||||
glog.Infof(" Addresses: %v", c.Address)
|
||||
glog.Infof(" Monitoring address: %s", c.MonitoringAddress)
|
||||
}
|
||||
43
internal/config/config.pb.go
Normal file
43
internal/config/config.pb.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// Code generated by protoc-gen-go.
|
||||
// source: config.proto
|
||||
// DO NOT EDIT!
|
||||
|
||||
/*
|
||||
Package config is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
config.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Config
|
||||
*/
|
||||
package config
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
type Config struct {
|
||||
// Hostname to use when we say hello.
|
||||
// For aesthetic purposes, but may help if our ip address resolves to it.
|
||||
// Default: machine hostname.
|
||||
Hostname string `protobuf:"bytes,1,opt,name=hostname" json:"hostname,omitempty"`
|
||||
// Maximum email size, in megabytes.
|
||||
// Default: 50.
|
||||
MaxDataSizeMb int64 `protobuf:"varint,2,opt,name=max_data_size_mb" json:"max_data_size_mb,omitempty"`
|
||||
// Addresses to listen on.
|
||||
// Default: "systemd", which means systemd passes sockets to us.
|
||||
Address []string `protobuf:"bytes,3,rep,name=address" json:"address,omitempty"`
|
||||
// Address for the monitoring http server.
|
||||
// Default: no monitoring http server.
|
||||
MonitoringAddress string `protobuf:"bytes,4,opt,name=monitoring_address" json:"monitoring_address,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Config) Reset() { *m = Config{} }
|
||||
func (m *Config) String() string { return proto.CompactTextString(m) }
|
||||
func (*Config) ProtoMessage() {}
|
||||
22
internal/config/config.proto
Normal file
22
internal/config/config.proto
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
message Config {
|
||||
// Hostname to use when we say hello.
|
||||
// For aesthetic purposes, but may help if our ip address resolves to it.
|
||||
// Default: machine hostname.
|
||||
string hostname = 1;
|
||||
|
||||
// Maximum email size, in megabytes.
|
||||
// Default: 50.
|
||||
int64 max_data_size_mb = 2;
|
||||
|
||||
// Addresses to listen on.
|
||||
// Default: "systemd", which means systemd passes sockets to us.
|
||||
repeated string address = 3;
|
||||
|
||||
// Address for the monitoring http server.
|
||||
// Default: no monitoring http server.
|
||||
string monitoring_address = 4;
|
||||
}
|
||||
|
||||
104
internal/config/config_test.go
Normal file
104
internal/config/config_test.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func mustCreateConfig(t *testing.T, contents string) (string, string) {
|
||||
tmpDir, err := ioutil.TempDir("", "chasquid_config_test:")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp dir: %v\n", tmpDir)
|
||||
}
|
||||
|
||||
confStr := []byte(contents)
|
||||
err = ioutil.WriteFile(tmpDir+"/chasquid.conf", confStr, 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write tmp config: %v", err)
|
||||
}
|
||||
|
||||
return tmpDir, tmpDir + "/chasquid.conf"
|
||||
}
|
||||
|
||||
func TestEmptyConfig(t *testing.T) {
|
||||
tmpDir, path := mustCreateConfig(t, "")
|
||||
defer os.RemoveAll(tmpDir)
|
||||
c, err := Load(path)
|
||||
if err != nil {
|
||||
t.Fatalf("error loading empty config: %v", err)
|
||||
}
|
||||
|
||||
// Test the default values are set.
|
||||
|
||||
hostname, _ := os.Hostname()
|
||||
if c.Hostname == "" || c.Hostname != hostname {
|
||||
t.Errorf("invalid hostname %q, should be: %q", c.Hostname, hostname)
|
||||
}
|
||||
|
||||
if c.MaxDataSizeMb != 50 {
|
||||
t.Errorf("max data size != 50: %d", c.MaxDataSizeMb)
|
||||
}
|
||||
|
||||
if len(c.Address) != 1 || c.Address[0] != "systemd" {
|
||||
t.Errorf("unexpected address default: %v", c.Address)
|
||||
}
|
||||
|
||||
if c.MonitoringAddress != "" {
|
||||
t.Errorf("monitoring address is set: %v", c.MonitoringAddress)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestFullConfig(t *testing.T) {
|
||||
confStr := `
|
||||
hostname: "joust"
|
||||
address: ":1234"
|
||||
address: ":5678"
|
||||
monitoring_address: ":1111"
|
||||
max_data_size_mb: 26
|
||||
`
|
||||
|
||||
tmpDir, path := mustCreateConfig(t, confStr)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
c, err := Load(path)
|
||||
if err != nil {
|
||||
t.Fatalf("error loading non-existent config: %v", err)
|
||||
}
|
||||
|
||||
if c.Hostname != "joust" {
|
||||
t.Errorf("hostname %q != 'joust'", c.Hostname)
|
||||
}
|
||||
|
||||
if c.MaxDataSizeMb != 26 {
|
||||
t.Errorf("max data size != 26: %d", c.MaxDataSizeMb)
|
||||
}
|
||||
|
||||
if len(c.Address) != 2 ||
|
||||
c.Address[0] != ":1234" || c.Address[1] != ":5678" {
|
||||
t.Errorf("different address: %v", c.Address)
|
||||
}
|
||||
|
||||
if c.MonitoringAddress != ":1111" {
|
||||
t.Errorf("monitoring address %q != ':1111;", c.MonitoringAddress)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorLoading(t *testing.T) {
|
||||
c, err := Load("/does/not/exist")
|
||||
if err == nil {
|
||||
t.Fatalf("loaded a non-existent config: %v", c)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBrokenConfig(t *testing.T) {
|
||||
tmpDir, path := mustCreateConfig(
|
||||
t, "<invalid> this is not a valid protobuf")
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
c, err := Load(path)
|
||||
if err == nil {
|
||||
t.Fatalf("loaded an invalid config: %v", c)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user