GitHub

@@ -4,6 +4,8 @@ import (

44

"fmt"

55

"log/syslog"

66

"os"

7+

pathpkg "path"

8+

"path/filepath"

79

"time"

810911

"github.com/pkg/errors"

@@ -22,8 +24,9 @@ const (

2224

)

23252426

type Config struct {

25-

Jobs []JobEnum `yaml:"jobs,optional"`

26-

Global *Global `yaml:"global,optional,fromdefaults"`

27+

Jobs []JobEnum `yaml:"jobs,optional"`

28+

Global *Global `yaml:"global,optional,fromdefaults"`

29+

Include []string `yaml:"include,optional"`

2730

}

28312932

func (c *Config) Job(name string) (*JobEnum, error) {

@@ -655,8 +658,9 @@ var ConfigFileDefaultLocations = []string{

655658

"/usr/local/etc/zrepl/zrepl.yml",

656659

}

657660658-

func ParseConfig(path string) (i *Config, err error) {

661+

func ParseConfig(path string) (rootConfig *Config, err error) {

659662663+

// Parse main configuration file

660664

if path == "" {

661665

// Try default locations

662666

for _, l := range ConfigFileDefaultLocations {

@@ -679,7 +683,67 @@ func ParseConfig(path string) (i *Config, err error) {

679683

return

680684

}

681685682-

return ParseConfigBytes(bytes)

686+

rootConfig, err = ParseConfigBytes(bytes)

687+

if err != nil {

688+

return nil, err

689+

}

690+691+

err = expandConfigInclude(path, rootConfig)

692+

if err != nil {

693+

return nil, err

694+

}

695+696+

return rootConfig, err

697+

}

698+699+

func expandConfigInclude(configPath string, config *Config) (err error) {

700+

var includeConfigPaths []string

701+

for _, path := range config.Include {

702+

if !pathpkg.IsAbs(configPath) {

703+

path = pathpkg.Join(pathpkg.Dir(configPath), path)

704+

}

705+706+

stat, statErr := os.Stat(path)

707+

if statErr != nil {

708+

return errors.Wrapf(statErr, "stat path %q", path)

709+

}

710+711+

if stat.Mode().IsDir() {

712+

directoryPaths, err := filepath.Glob(path + "/*.yml")

713+

if err != nil {

714+

return err

715+

}

716+717+

includeConfigPaths = append(includeConfigPaths, directoryPaths...)

718+

} else if stat.Mode().IsRegular() {

719+

if extention := filepath.Ext(path); extention != ".yml" {

720+

return fmt.Errorf("include config files must end with `.yml`: %s", path)

721+

}

722+

includeConfigPaths = append(includeConfigPaths, path)

723+

} else {

724+

return fmt.Errorf("not a file or directory: %s", path)

725+

}

726+

}

727+728+

for _, path := range includeConfigPaths {

729+

var bytes []byte

730+

if bytes, err = os.ReadFile(path); err != nil {

731+

return errors.Wrapf(err, "read file: %q", path)

732+

}

733+734+

includedConfig, err := ParseConfigBytes(bytes)

735+

if err != nil {

736+

return err

737+

}

738+739+

if len(includedConfig.Include) > 0 {

740+

return errors.Errorf("included configuration files must not include other files: %s", path)

741+

}

742+743+

config.Jobs = append(config.Jobs, includedConfig.Jobs...)

744+

}

745+746+

return nil

683747

}

684748685749

func ParseConfigBytes(bytes []byte) (*Config, error) {

Read the original on github.com ↗