conf: add pretty printing,raw conf, conf tests
All checks were successful
continuous-integration/drone/push Build is passing

* also, set debug logger directly in config if devel mode is set
* add new flag to indicate whether the passed config is path or raw
  config
This commit is contained in:
leo 2023-05-04 23:49:25 +02:00
parent 2a56ba3456
commit dfdcc77737
Signed by: wanderer
SSH Key Fingerprint: SHA256:Dp8+iwKHSlrMEHzE3bJnPng70I7LEsa3IJXRH/U+idQ
5 changed files with 208 additions and 12 deletions

View File

@ -1,6 +1,12 @@
package config
import (
"context"
"fmt"
"os"
"os/exec"
"time"
"git.dotya.ml/mirre-mt/pcmt/slogging"
"github.com/philandstuff/dhall-golang/v6"
)
@ -15,15 +21,72 @@ type Config struct {
SessionCookieSecret string
}
func LoadConfig(path string) (*Config, error) {
func LoadConfig(conf string, isPath bool) (*Config, error) {
var config Config
err := dhall.UnmarshalFile(path, &config)
var err error
if isPath {
slogging.GetLogger().Debug("config from file")
err = dhall.UnmarshalFile(conf, &config)
} else {
slogging.GetLogger().Debug("config raw from cmdline")
err = dhall.Unmarshal([]byte(conf), &config)
}
if err != nil {
return nil, err
}
slogging.GetLogger().Debugf("parsed config: %+v", config)
if config.DevelMode {
_ = slogging.SetLevel(slogging.LevelDebug)
slogging.GetLogger().Debugf("parsed config: %+v", config)
if dhallCmdExists() {
_ = prettyPrintConfig(conf, isPath)
}
}
return &config, nil
}
func prettyPrintConfig(conf string, isPath bool) error {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
var cmd *exec.Cmd
if isPath {
cmd = exec.CommandContext(ctx, "/bin/sh", "-c", "dhall --file "+conf) //nolint:gosec
} else {
cmd = exec.CommandContext(ctx, "/bin/sh", "-c", "dhall <<< \""+conf+"\"") //nolint:gosec
}
output, err := cmd.CombinedOutput()
if err != nil {
slogging.GetLogger().Debug("could not pretty-print config", "error", err)
return err
}
if isPath {
fmt.Fprintln(os.Stderr, "\n"+conf+":\n"+string(output))
} else {
fmt.Fprintln(os.Stderr, "\nconfig:\n"+string(output))
}
return nil
}
func dhallCmdExists() bool {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
if err := exec.CommandContext(ctx, "/bin/sh", "-c", "command -v dhall").Run(); err != nil {
slogging.GetLogger().Debug("no command dhall")
return false
}
return true
}

114
config/config_test.go Normal file
View File

@ -0,0 +1,114 @@
package config
import (
"os"
"testing"
"git.dotya.ml/mirre-mt/pcmt/slogging"
)
func TestConfig(t *testing.T) {
if os.Getenv("CI") == "true" {
t.Skip("we're running in CI apparently, skipping these tests")
}
confPath := "./testconfigs/"
ts := []struct {
name string
fails bool
isPath bool
conf string
}{
{
// incorrect type of the `Host` field.
name: "1.dhall",
fails: true,
isPath: true,
conf: "1.dhall",
},
{
name: "2.dhall",
fails: false,
isPath: true,
conf: "2.dhall",
},
{
name: "3.dhall",
fails: false,
isPath: false,
conf: `
let ConfigSchema =
https://git.dotya.ml/mirre-mt/pcmt/raw/branch/development/config/schema/package.dhall
sha256:ad7ba86d5d388a99b7543faa0e4c81ba1d9b78fa6c32fdaf4ac4477089d177be
? https://git.dotya.ml/mirre-mt/pcmt/raw/branch/development/config/schema/package.dhall
let Config = ConfigSchema.Schema
let c = Config::{ DevelMode = True }
in c
`,
},
{
// misses final `in`.
name: "4.dhall",
fails: true,
isPath: false,
conf: `
let ConfigSchema =
https://git.dotya.ml/mirre-mt/pcmt/raw/branch/development/config/schema/package.dhall
sha256:ad7ba86d5d388a99b7543faa0e4c81ba1d9b78fa6c32fdaf4ac4477089d177be
? https://git.dotya.ml/mirre-mt/pcmt/raw/branch/development/config/schema/package.dhall
let Config = ConfigSchema.Schema
let c = Config::{ DevelMode = True }
`,
},
{
name: "5.dhall",
fails: false,
isPath: false,
conf: `
let ConfigSchema =
https://git.dotya.ml/mirre-mt/pcmt/raw/branch/development/config/schema/package.dhall
in ConfigSchema.Schema::{ DevelMode = True, Port = 5555 }
`,
},
{
name: "6.dhall",
fails: false,
isPath: false,
conf: `
let ConfigSchema =
https://git.dotya.ml/mirre-mt/pcmt/raw/branch/development/config/schema/package.dhall
in ConfigSchema.Schema.default // { DevelMode = True }
`,
},
}
_ = slogging.Init(false)
for _, tc := range ts {
t.Logf("running test case %s", tc.name)
var err error
shouldFail := tc.fails
if tc.isPath {
_, err = LoadConfig(confPath+tc.conf, tc.isPath)
} else {
_, err = LoadConfig(tc.conf, tc.isPath)
}
switch {
case err == nil && shouldFail:
t.Errorf("test case '%s' should have failed", tc.name)
case err != nil && !shouldFail:
t.Log(err)
t.Errorf("test case '%s' should not have failed", tc.name)
}
}
}

View File

@ -0,0 +1,9 @@
let ConfigSchema =
https://git.dotya.ml/mirre-mt/pcmt/raw/branch/development/config/schema/package.dhall
sha256:ad7ba86d5d388a99b7543faa0e4c81ba1d9b78fa6c32fdaf4ac4477089d177be
let Config = ConfigSchema.Schema
let c = Config::{ Host = False }
in c

View File

@ -0,0 +1,9 @@
let ConfigSchema =
https://git.dotya.ml/mirre-mt/pcmt/raw/branch/development/config/schema/package.dhall
sha256:ad7ba86d5d388a99b7543faa0e4c81ba1d9b78fa6c32fdaf4ac4477089d177be
let Config = ConfigSchema.Schema
let c = Config::{ Host = "localhost" }
in c

19
run.go
View File

@ -51,13 +51,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.`
)
var (
host = flag.String("host", "unset", "host address to listen on")
port = flag.Int("port", -1, "TCP port to listen on")
configFlag = flag.String("config", "config.dhall", "Default path of the config file")
devel = flag.Bool("devel", false, "Run the application in dev mode, connect to a local browser-sync instance for hot-reloading")
license = flag.Bool("license", false, "Print licensing information and exit")
version = "dev"
log *slogging.Logger
host = flag.String("host", "unset", "host address to listen on")
port = flag.Int("port", -1, "TCP port to listen on")
configFlag = flag.String("config", "config.dhall", "Default path of the config file")
configIsPathFlag = flag.Bool("configIsPath", true, "Whether the provided config is path or raw config")
devel = flag.Bool("devel", false, "Run the application in dev mode, connect to a local browser-sync instance for hot-reloading")
license = flag.Bool("license", false, "Print licensing information and exit")
version = "dev"
log *slogging.Logger
)
func run() error {
@ -81,9 +82,9 @@ func run() error {
// TODO: SBOM: https://actuated.dev/blog/sbom-in-github-actions
// TODO: SBOM: https://www.docker.com/blog/generate-sboms-with-buildkit/
conf, err := config.LoadConfig(*configFlag)
conf, err := config.LoadConfig(*configFlag, *configIsPathFlag)
if err != nil {
log.Errorf("error loading config file at '%s', bailing", *configFlag)
log.Errorf("error loading config file '%s', bailing", *configFlag)
return err
}