Skip to content

Commit

Permalink
Merge pull request #75 from Icinga/config-docs
Browse files Browse the repository at this point in the history
Document `config`
  • Loading branch information
lippserd authored Oct 23, 2024
2 parents b7b454b + 0ab4103 commit ab3632f
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 7 deletions.
85 changes: 85 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,46 @@
// Package config provides utilities for configuration parsing and loading.
// It includes functionality for handling command-line flags and loading configuration from YAML files,
// with additional support for setting default values and validation.
// Additionally, it provides a struct that defines common settings for a TLS client.
//
// Example usage:
//
// type Config struct {
// ServerAddress string `yaml:"server_address" default:"localhost:8080"`
// TLS config.TLS `yaml:",inline"`
// }
//
// // Validate implements the Validator interface.
// func (c *Config) Validate() error {
// if _, _, err := net.SplitHostPort(c.ServerAddress); err != nil {
// return errors.Wrapf(err, "invalid server address: %s", c.ServerAddress)
// }
//
// return nil
// }
//
// type Flags struct {
// Config string `short:"c" long:"config" description:"Path to config file" required:"true"`
// }
//
// func main() {
// var flags Flags
// if err := config.ParseFlags(&flags); err != nil {
// log.Fatalf("error parsing flags: %v", err)
// }
//
// var cfg Config
// if err := config.FromYAMLFile(flags.Config, &cfg); err != nil {
// log.Fatalf("error loading config: %v", err)
// }
//
// tlsCfg, err := cfg.TLS.MakeConfig("icinga.com")
// if err != nil {
// log.Fatalf("error creating TLS config: %v", err)
// }
//
// // ...
// }
package config

import (
Expand All @@ -20,6 +63,33 @@ var ErrInvalidArgument = stderrors.New("invalid argument")
// FromYAMLFile parses the given YAML file and stores the result
// in the value pointed to by v. If v is nil or not a pointer,
// FromYAMLFile returns an [ErrInvalidArgument] error.
// It is possible to define default values via the struct tag `default`.
// The function also validates the configuration using the Validate method
// of the provided [Validator] interface.
//
// Example usage:
//
// type Config struct {
// ServerAddress string `yaml:"server_address" default:"localhost:8080"`
// }
//
// // Validate implements the Validator interface.
// func (c *Config) Validate() error {
// if _, _, err := net.SplitHostPort(c.ServerAddress); err != nil {
// return errors.Wrapf(err, "invalid server address: %s", c.ServerAddress)
// }
//
// return nil
// }
//
// func main() {
// var cfg Config
// if err := config.FromYAMLFile("config.yml", &cfg); err != nil {
// log.Fatalf("error loading config: %v", err)
// }
//
// // ...
// }
func FromYAMLFile(name string, v Validator) error {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Pointer || rv.IsNil() {
Expand Down Expand Up @@ -86,6 +156,21 @@ func FromEnv(v Validator, options EnvOptions) error {
// ParseFlags prints the help message to [os.Stdout] and exits.
// Note that errors are not printed automatically,
// so error handling is the sole responsibility of the caller.
//
// Example usage:
//
// type Flags struct {
// Config string `short:"c" long:"config" description:"Path to config file" required:"true"`
// }
//
// func main() {
// var flags Flags
// if err := config.ParseFlags(&flags); err != nil {
// log.Fatalf("error parsing flags: %v", err)
// }
//
// // ...
// }
func ParseFlags(v any) error {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Pointer || rv.IsNil() {
Expand Down
12 changes: 12 additions & 0 deletions config/contracts.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
package config

// Validator is an interface that must be implemented by any configuration struct used in [FromYAMLFile].
//
// The Validate method checks the configuration values and
// returns an error if any value is invalid or missing when required.
//
// For fields such as file paths, the responsibility of Validate is limited to
// verifying the presence and format of the value,
// not checking external conditions like file existence or readability.
// This principle applies generally to any field where external validation
// (e.g., network availability, resource accessibility) is beyond the scope of basic configuration validation.
type Validator interface {
// Validate checks the configuration values and
// returns an error if any value is invalid or missing when required.
Validate() error
}
49 changes: 42 additions & 7 deletions config/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,51 @@ import (
"os"
)

// TLS provides TLS configuration options.
// TLS represents configuration for a TLS client.
// It provides options to enable TLS, specify certificate and key files,
// CA certificate, and whether to skip verification of the server's certificate chain and host name.
// Use the [TLS.MakeConfig] method to assemble a [*tls.Config] from the TLS struct.
//
// Example usage:
//
// func main() {
// tlsConfig := &config.TLS{
// Enable: true,
// Cert: "path/to/cert.pem",
// Key: "path/to/key.pem",
// Ca: "path/to/ca.pem",
// Insecure: false,
// }
//
// cfg, err := tlsConfig.MakeConfig("example.com")
// if err != nil {
// log.Fatalf("error creating TLS config: %v", err)
// }
//
// // ...
// }
type TLS struct {
Enable bool `yaml:"tls" env:"TLS"`
Cert string `yaml:"cert" env:"CERT"`
Key string `yaml:"key" env:"KEY"`
Ca string `yaml:"ca" env:"CA"`
Insecure bool `yaml:"insecure" env:"INSECURE"`
// Enable indicates whether TLS is enabled.
Enable bool `yaml:"tls" env:"TLS"`

// Cert is the path to the TLS certificate file. If provided, Key must also be specified.
Cert string `yaml:"cert" env:"CERT"`

// Key is the path to the TLS key file. If specified, Cert must also be provided.
Key string `yaml:"key" env:"KEY"`

// Ca is the path to the CA certificate file.
Ca string `yaml:"ca" env:"CA"`

// Insecure indicates whether to skip verification of the server's certificate chain and host name.
// If true, any certificate presented by the server and any host name in that certificate is accepted.
// In this mode, TLS is susceptible to machine-in-the-middle attacks unless custom verification is used.
Insecure bool `yaml:"insecure" env:"INSECURE"`
}

// MakeConfig assembles a tls.Config from t and serverName.
// MakeConfig assembles a [*tls.Config] from the TLS struct and the provided serverName.
// It returns a configured *tls.Config or an error if there are issues with the provided TLS settings.
// If TLS is not enabled (t.Enable is false), it returns nil without an error.
func (t *TLS) MakeConfig(serverName string) (*tls.Config, error) {
if !t.Enable {
return nil, nil
Expand Down

0 comments on commit ab3632f

Please sign in to comment.