package
0.0.0-20230614132413-d125d8407c3b
Repository: https://github.com/rschone/corefile2struct.git
Documentation: pkg.go.dev

# README

corefile

Description

Corefile package provides parsing of Corefile plugin configuration into a specified structure for custom CoreDNS plugins. Parsing is driven by field tags in a structure provided to the parser as a target. That means that the only thing you have to do is to properly define a structure of your configuration, default values and validations.

Usage

The configuration might be parsed into a structure just by providing a specific structure instance as follows.

func setup(c *caddy.Controller) error {
	var cfg RedisConfig
	if err := corefile.Parse(c, &cfg); err != nil {
		return err
	}
	...
}

Basic use case

The structure definition prescribes the structure of the plugin configuration. For each exported field in the structure a cf tag binds the structure field to a corresponding field in the configuration. The cf tag value is case-sensitive and represents the property name in the configuration.

You can provide default value for each field by using default tag. The value has to be compatible with the field type, otherwise an error will be returned. The defaults are applied to structure fields when the structure is about to be parsed. They can be later overridden by parsed values.

After the structure is parsed, validations are to be performed. For each field a several checks might be specified by using check tag. The syntax and supported checks will described later.

type RedisConfig struct {
    Host string        `cf:"host" check:"nonempty"`
    Port string        `cf:"port" default:"1943" check:"nonempty,lt(5000)"`
    TTL  time.Duration `cf:"TTL" default:"30s"`
}

Here is how a configuration corresponding to the previously defined structure could look like.

redis {
    host redis.company.com
    port 1944
    TTL 10s
}

Details

Supported field types

Currently, the following field types are supported:

  • string
  • bool
  • numeric types
    • int, int8, int16, int32, int64
    • float32, float64
  • slices
    • []string
    • []int
  • time.Duration
  • net.IP
  • structs
  • pointer to structs

Plugin specific structure configuration

If a plugin configuration structure contains field name Arguments defined as []string, it will be filled with plugin arguments provided in the configuration. If such field is not present in the plugin configuration structure, the arguments will be ignored.

With the following configuration:

plugin arg1 arg2

the field Arguments will be filled by ["arg1", "arg2"]:

type pluginCfg struct {
    Arguments []string
}

Initialization

There are two possible ways to initialize fields in the structure before it is parsed: by default values, or by a structure initialization function.

Default values

Specified by using default tag - the value has the same format as if the it would be provided in the configuration file

Example values:

    10s
    15.4
    "string value"
    true
    127.0.0.1

Specific structure initializer

If the structure implements corefile.Initializer interface, the method Init() error will be called immediately after the default values application.

This method of initialization is meant to be used for complex value initialization that is not possible via default values - like an initialization conditioned by some external factors.

In case of nested structures (or structure pointers), their initializer will be called when the parser enters the corresponding configuration block. If nested structure is not present in the config, the initialization of the corresponding nested structure won't be done.

Be aware, that the initialization function has to be implemented with pointer receiver.

type pluginCfg struct {
    ...
}

func (v *pluginCfg) Init() error {
    ...
    return nil
}

Validations

As same as the initialization, also validation can be done on a field level by providing a list a checkers, or on the structure level, by a structure specific checker function.

Field level checkers

There are several supported checkers, that can be applied on fields:

  • nonempty - field must not have "default" value (for given golang type)
  • oneOf(arg1|...|argN) - field must be one of specified values; it is applicable only on string fields
  • lt(arg) - field value must be less than provided argument
  • lte(arg) - field value must be less than or equal to provided argument
  • gt(arg) - field value must be great than provided argument
  • gte(arg) - field value must be great than or equal to provided argument

Checker's names are case-insensitive. You can specify several checkers in the check tag:

    age  `cf:"age" check:"gte(18),lt(100)"`
    city `cf:"city" check:"oneOf(Brno|Praha)"`

Custom structure validation

In case of complex validations the structure can implement CustomChecker interface. The Check() error function is called right after the field validations.

It can help in the situation where there are some relations between fields and simple field checkers are not sufficient.

type pluginCfg struct {
    ...
}

func (v *pluginCfg) Check() error {
    ...
    return nil
}

Check() function might be implemented on top of value as same as pointer receiver.

Notes to structures

A configuration may refer another structures directly or by a pointer.

When the configuration refers to another structure by pointer and such field is not initialized before the parsing, the memory for such field will be automatically allocated if the field is present in the configuration when the parser enters into the structure during the parsing.

Such field might be easily checked with nonempty checker to force its presence in the configuration, or left as is in case it is optional. In the following example, Regatta config is referred via pointer and its presence forced by usage of nonempty checker and the cache configuration directly via structure. The CacheCfg prescribes defaults where needed, so the presence of the cache settings in the configuration is not necessary.

type CacheCfg struct {
  Name            string        `cf:"name"`
  MaxSize         int           `cf:"maxSize" default:"1000"`
  TTL             time.Duration `cf:"ttl" default:"1m"`
  CleanupInterval time.Duration `cf:"cleanupInterval" default:"1m"`
  EvictType       string        `cf:"evictType" default:"arc" check:"oneOf(simple|lru|lfu|arc)"`
}

type Config struct {
  TLSCaCertFile  string       `cf:"tls-ca-cert-file" check:"nonempty"`
  Regatta        *grpc.Config `cf:"regatta" check:"nonempty"`
  Cache          CacheCfg     `cf:"customer-settings-cache"`
}

# Functions

Parse parses the input provided by caddy and fills the configuration into provided pointer to a custom structure.

# Interfaces

CustomChecker represents a custom validation function call.
Initializer is implemented by a structure when custom structure initialization is required.