~emersion/kimchi

01449f287527be65f34a9d4686e3a7b111cda8dd — Simon Ser 11 months ago 86eca56
Add import directive
3 files changed, 79 insertions(+), 8 deletions(-)

M directives.go
M kimchi.1.scd
M main.go
M directives.go => directives.go +68 -0
@@ 7,11 7,26 @@ import (
	"net/http"
	"net/http/httputil"
	"net/url"
	"path/filepath"
	"strings"

	"git.sr.ht/~emersion/go-scfg"
)

func loadConfig(srv *Server, filename string) error {
	cfg, err := scfg.Load(filename)
	if err != nil {
		return err
	}

	cfg, err = resolveImports(cfg, filename)
	if err != nil {
		return err
	}

	return parseConfig(srv, cfg)
}

func parseConfig(srv *Server, cfg scfg.Block) error {
	for _, dir := range cfg {
		switch dir.Name {


@@ 170,3 185,56 @@ func parseMiddleware(dir *scfg.Directive, next http.Handler) (http.Handler, erro
		return nil, fmt.Errorf("unknown directive")
	}
}

func resolveImports(input scfg.Block, filename string) (scfg.Block, error) {
	dirname := filepath.Dir(filename)

	output := make(scfg.Block, 0, len(input))
	for _, dir := range input {
		switch dir.Name {
		case "import":
			var pattern string
			if err := dir.ParseParams(&pattern); err != nil {
				return nil, err
			}
			if !filepath.IsAbs(pattern) {
				pattern = filepath.Join(dirname, pattern)
			}

			matches, err := filepath.Glob(pattern)
			if err != nil {
				return nil, fmt.Errorf("failed to import %q: %v", pattern, err)
			}

			for _, filename := range matches {
				block, err := scfg.Load(filename)
				if err != nil {
					return nil, err
				}

				block, err = resolveImports(block, filename)
				if err != nil {
					return nil, err
				}

				output = append(output, block...)
			}
		default:
			if len(dir.Children) > 0 {
				children, err := resolveImports(dir.Children, filename)
				if err != nil {
					return nil, err
				}

				dirCopy := *dir
				dirCopy.Children = children
				dir = &dirCopy
			}

			output = append(output, dir)
			continue
		}
	}

	return output, nil
}

M kimchi.1.scd => kimchi.1.scd +10 -0
@@ 80,6 80,16 @@ The following directives are supported:
	*basic_auth* <username> <password>
		Sets up HTTP basic authentication.

*import* <pattern>
	Include external files.

	_pattern_ can be a filename or a glob pattern. Its contents will replace
	this directive. A glob that doesn't match any file is not an error. Paths
	are resolved relative to the file the *import* directive appears in.

	This directive is a special case: it is evaluated before the configuration
	is parsed, and it can appear anywhere.

# FILES

_/etc/kimchi/config_

M main.go => main.go +1 -8
@@ 3,8 3,6 @@ package main
import (
	"flag"
	"log"

	"git.sr.ht/~emersion/go-scfg"
)

var configPath = "/etc/kimchi/config"


@@ 13,13 11,8 @@ func main() {
	flag.StringVar(&configPath, "config", configPath, "configuration file")
	flag.Parse()

	cfg, err := scfg.Load(configPath)
	if err != nil {
		log.Fatal(err)
	}

	srv := NewServer()
	if err := parseConfig(srv, cfg); err != nil {
	if err := loadConfig(srv, configPath); err != nil {
		log.Fatal(err)
	}