Skip to content

Commit ab3632f

Browse files
authored
Merge pull request #75 from Icinga/config-docs
Document `config`
2 parents b7b454b + 0ab4103 commit ab3632f

File tree

3 files changed

+139
-7
lines changed

3 files changed

+139
-7
lines changed

config/config.go

+85
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,46 @@
1+
// Package config provides utilities for configuration parsing and loading.
2+
// It includes functionality for handling command-line flags and loading configuration from YAML files,
3+
// with additional support for setting default values and validation.
4+
// Additionally, it provides a struct that defines common settings for a TLS client.
5+
//
6+
// Example usage:
7+
//
8+
// type Config struct {
9+
// ServerAddress string `yaml:"server_address" default:"localhost:8080"`
10+
// TLS config.TLS `yaml:",inline"`
11+
// }
12+
//
13+
// // Validate implements the Validator interface.
14+
// func (c *Config) Validate() error {
15+
// if _, _, err := net.SplitHostPort(c.ServerAddress); err != nil {
16+
// return errors.Wrapf(err, "invalid server address: %s", c.ServerAddress)
17+
// }
18+
//
19+
// return nil
20+
// }
21+
//
22+
// type Flags struct {
23+
// Config string `short:"c" long:"config" description:"Path to config file" required:"true"`
24+
// }
25+
//
26+
// func main() {
27+
// var flags Flags
28+
// if err := config.ParseFlags(&flags); err != nil {
29+
// log.Fatalf("error parsing flags: %v", err)
30+
// }
31+
//
32+
// var cfg Config
33+
// if err := config.FromYAMLFile(flags.Config, &cfg); err != nil {
34+
// log.Fatalf("error loading config: %v", err)
35+
// }
36+
//
37+
// tlsCfg, err := cfg.TLS.MakeConfig("icinga.com")
38+
// if err != nil {
39+
// log.Fatalf("error creating TLS config: %v", err)
40+
// }
41+
//
42+
// // ...
43+
// }
144
package config
245

346
import (
@@ -20,6 +63,33 @@ var ErrInvalidArgument = stderrors.New("invalid argument")
2063
// FromYAMLFile parses the given YAML file and stores the result
2164
// in the value pointed to by v. If v is nil or not a pointer,
2265
// FromYAMLFile returns an [ErrInvalidArgument] error.
66+
// It is possible to define default values via the struct tag `default`.
67+
// The function also validates the configuration using the Validate method
68+
// of the provided [Validator] interface.
69+
//
70+
// Example usage:
71+
//
72+
// type Config struct {
73+
// ServerAddress string `yaml:"server_address" default:"localhost:8080"`
74+
// }
75+
//
76+
// // Validate implements the Validator interface.
77+
// func (c *Config) Validate() error {
78+
// if _, _, err := net.SplitHostPort(c.ServerAddress); err != nil {
79+
// return errors.Wrapf(err, "invalid server address: %s", c.ServerAddress)
80+
// }
81+
//
82+
// return nil
83+
// }
84+
//
85+
// func main() {
86+
// var cfg Config
87+
// if err := config.FromYAMLFile("config.yml", &cfg); err != nil {
88+
// log.Fatalf("error loading config: %v", err)
89+
// }
90+
//
91+
// // ...
92+
// }
2393
func FromYAMLFile(name string, v Validator) error {
2494
rv := reflect.ValueOf(v)
2595
if rv.Kind() != reflect.Pointer || rv.IsNil() {
@@ -86,6 +156,21 @@ func FromEnv(v Validator, options EnvOptions) error {
86156
// ParseFlags prints the help message to [os.Stdout] and exits.
87157
// Note that errors are not printed automatically,
88158
// so error handling is the sole responsibility of the caller.
159+
//
160+
// Example usage:
161+
//
162+
// type Flags struct {
163+
// Config string `short:"c" long:"config" description:"Path to config file" required:"true"`
164+
// }
165+
//
166+
// func main() {
167+
// var flags Flags
168+
// if err := config.ParseFlags(&flags); err != nil {
169+
// log.Fatalf("error parsing flags: %v", err)
170+
// }
171+
//
172+
// // ...
173+
// }
89174
func ParseFlags(v any) error {
90175
rv := reflect.ValueOf(v)
91176
if rv.Kind() != reflect.Pointer || rv.IsNil() {

config/contracts.go

+12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
package config
22

3+
// Validator is an interface that must be implemented by any configuration struct used in [FromYAMLFile].
4+
//
5+
// The Validate method checks the configuration values and
6+
// returns an error if any value is invalid or missing when required.
7+
//
8+
// For fields such as file paths, the responsibility of Validate is limited to
9+
// verifying the presence and format of the value,
10+
// not checking external conditions like file existence or readability.
11+
// This principle applies generally to any field where external validation
12+
// (e.g., network availability, resource accessibility) is beyond the scope of basic configuration validation.
313
type Validator interface {
14+
// Validate checks the configuration values and
15+
// returns an error if any value is invalid or missing when required.
416
Validate() error
517
}

config/tls.go

+42-7
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,51 @@ import (
77
"os"
88
)
99

10-
// TLS provides TLS configuration options.
10+
// TLS represents configuration for a TLS client.
11+
// It provides options to enable TLS, specify certificate and key files,
12+
// CA certificate, and whether to skip verification of the server's certificate chain and host name.
13+
// Use the [TLS.MakeConfig] method to assemble a [*tls.Config] from the TLS struct.
14+
//
15+
// Example usage:
16+
//
17+
// func main() {
18+
// tlsConfig := &config.TLS{
19+
// Enable: true,
20+
// Cert: "path/to/cert.pem",
21+
// Key: "path/to/key.pem",
22+
// Ca: "path/to/ca.pem",
23+
// Insecure: false,
24+
// }
25+
//
26+
// cfg, err := tlsConfig.MakeConfig("example.com")
27+
// if err != nil {
28+
// log.Fatalf("error creating TLS config: %v", err)
29+
// }
30+
//
31+
// // ...
32+
// }
1133
type TLS struct {
12-
Enable bool `yaml:"tls" env:"TLS"`
13-
Cert string `yaml:"cert" env:"CERT"`
14-
Key string `yaml:"key" env:"KEY"`
15-
Ca string `yaml:"ca" env:"CA"`
16-
Insecure bool `yaml:"insecure" env:"INSECURE"`
34+
// Enable indicates whether TLS is enabled.
35+
Enable bool `yaml:"tls" env:"TLS"`
36+
37+
// Cert is the path to the TLS certificate file. If provided, Key must also be specified.
38+
Cert string `yaml:"cert" env:"CERT"`
39+
40+
// Key is the path to the TLS key file. If specified, Cert must also be provided.
41+
Key string `yaml:"key" env:"KEY"`
42+
43+
// Ca is the path to the CA certificate file.
44+
Ca string `yaml:"ca" env:"CA"`
45+
46+
// Insecure indicates whether to skip verification of the server's certificate chain and host name.
47+
// If true, any certificate presented by the server and any host name in that certificate is accepted.
48+
// In this mode, TLS is susceptible to machine-in-the-middle attacks unless custom verification is used.
49+
Insecure bool `yaml:"insecure" env:"INSECURE"`
1750
}
1851

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

0 commit comments

Comments
 (0)