package main import ( "bytes" "fmt" "io" "os" "reflect" "sort" "strings" "github.com/BurntSushi/toml" "github.com/rs/zerolog/log" "github.com/traefik/paerser/flag" "github.com/traefik/paerser/generator" "github.com/traefik/traefik/v3/cmd" "github.com/traefik/traefik/v3/pkg/collector/hydratation" "github.com/traefik/traefik/v3/pkg/config/dynamic" "gopkg.in/yaml.v3" ) var commentGenerated = `## CODE GENERATED AUTOMATICALLY ## THIS FILE MUST NOT BE EDITED BY HAND ` func main() { genRoutingConfDoc() genInstallConfDoc() } // Generate the Routing Configuration YAML and TOML files. func genRoutingConfDoc() { logger := log.With().Logger() dynConf := &dynamic.Configuration{} err := hydratation.Hydrate(dynConf) if err != nil { logger.Fatal().Err(err).Send() } dynConf.HTTP.Models = map[string]*dynamic.Model{} clean(dynConf.HTTP.Middlewares) clean(dynConf.TCP.Middlewares) clean(dynConf.HTTP.Services) clean(dynConf.TCP.Services) clean(dynConf.UDP.Services) err = tomlWrite("./docs/content/reference/routing-configuration/other-providers/file.toml", dynConf) if err != nil { logger.Fatal().Err(err).Send() } err = yamlWrite("./docs/content/reference/routing-configuration/other-providers/file.yaml", dynConf) if err != nil { logger.Fatal().Err(err).Send() } } func yamlWrite(outputFile string, element any) error { file, err := os.OpenFile(outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o666) if err != nil { return err } defer file.Close() // Write the comment at the beginning of the file. if _, err := file.WriteString(commentGenerated); err != nil { return err } buf := new(bytes.Buffer) encoder := yaml.NewEncoder(buf) encoder.SetIndent(2) err = encoder.Encode(element) if err != nil { return err } _, err = file.Write(buf.Bytes()) return err } func tomlWrite(outputFile string, element any) error { file, err := os.OpenFile(outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o666) if err != nil { return err } defer file.Close() // Write the comment at the beginning of the file. if _, err := file.WriteString(commentGenerated); err != nil { return err } return toml.NewEncoder(file).Encode(element) } func clean(element any) { valSvcs := reflect.ValueOf(element) key := valSvcs.MapKeys()[0] valueSvcRoot := valSvcs.MapIndex(key).Elem() var svcFieldNames []string for i := range valueSvcRoot.NumField() { field := valueSvcRoot.Type().Field(i) // do not create empty node for hidden config. if field.Tag.Get("file") == "-" && field.Tag.Get("kv") == "-" && field.Tag.Get("label") == "-" { continue } svcFieldNames = append(svcFieldNames, field.Name) } sort.Strings(svcFieldNames) for i, fieldName := range svcFieldNames { v := reflect.New(valueSvcRoot.Type()) v.Elem().FieldByName(fieldName).Set(valueSvcRoot.FieldByName(fieldName)) valSvcs.SetMapIndex(reflect.ValueOf(fmt.Sprintf("%s%.2d", valueSvcRoot.Type().Name(), i+1)), v) } valSvcs.SetMapIndex(reflect.ValueOf(fmt.Sprintf("%s0", valueSvcRoot.Type().Name())), reflect.Value{}) valSvcs.SetMapIndex(reflect.ValueOf(fmt.Sprintf("%s1", valueSvcRoot.Type().Name())), reflect.Value{}) } // Generate the Install Configuration in a table. func genInstallConfDoc() { outputFile := "./docs/content/reference/install-configuration/configuration-options.md" logger := log.With().Str("file", outputFile).Logger() element := &cmd.NewTraefikConfiguration().Configuration generator.Generate(element) flats, err := flag.Encode(element) if err != nil { logger.Fatal().Err(err).Send() } err = os.RemoveAll(outputFile) if err != nil { logger.Fatal().Err(err).Send() } file, err := os.OpenFile(outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o666) if err != nil { logger.Fatal().Err(err).Send() } defer file.Close() w := errWriter{w: file} w.writeln(``) w.writeln(`# Install Configuration Options`) w.writeln(`## Configuration Options`) w.writeln(` | Field | Description | Default | |:-------|:------------|:-------|`) for _, flat := range flats { // TODO must be move into the flats creation. if flat.Name == "experimental.plugins." || flat.Name == "TRAEFIK_EXPERIMENTAL_PLUGINS_" { continue } if strings.HasPrefix(flat.Name, "pilot.") || strings.HasPrefix(flat.Name, "TRAEFIK_PILOT_") { continue } line := "| " + strings.ReplaceAll(strings.ReplaceAll(flat.Name, "<", "_"), ">", "_") + " | " + flat.Description + " | " if flat.Default == "" { line += "|" } else { line += flat.Default + " |" } w.writeln(line) } if w.err != nil { logger.Fatal().Err(err).Send() } } type errWriter struct { w io.Writer err error } func (ew *errWriter) writeln(a ...interface{}) { if ew.err != nil { return } _, ew.err = fmt.Fprintln(ew.w, a...) }