feat: encode comments as part of talosctl generated configs
Comments encoding works, defaults encoding works. Docgen was revamped: now it generates go files. While markdown files are all handled by `pkg/machinery/config/encoder/markdown.go`. Changed scheme for docs. Now it no longer relies on a single `doc.go` in the root of a package. Instead it can generate separate `*_doc.go` files for each file in the package. `docgen` now expects to get 3 params instead of 2. 3rd parameter is used to define a unique method name for getting the list of structs in the file. Backward compatibility is supported if we define package name as the 3rd parameter. 1st parameter no longer scans whole current directory, instead it points to the particular file that should be processed by docgen. `talosctl docs` command now supports two flags: `--config` and `--cli`. They allow generating only docs for v1alpha1 configs or for talosctl. If no flags are defined, all docs are generated. Additionally made field types clickable in the output markdown file. Signed-off-by: Artem Chernyshev <artem.0xD2@gmail.com>
This commit is contained in:
parent
e7f6344d97
commit
d0ed6d7cc6
15
Dockerfile
15
Dockerfile
@ -90,6 +90,12 @@ RUN protoc -I/api --go_out=plugins=grpc,paths=source_relative:/api cluster/clust
|
||||
# Gofumports generated files to adjust import order
|
||||
RUN gofumports -w -local github.com/talos-systems/talos /api/
|
||||
|
||||
# run docgen for machinery config
|
||||
COPY ./pkg/machinery /pkg/machinery
|
||||
WORKDIR /pkg/machinery
|
||||
RUN go generate /pkg/machinery/config/types/v1alpha1/
|
||||
WORKDIR /
|
||||
|
||||
FROM scratch AS generate
|
||||
COPY --from=generate-build /api/common/common.pb.go /pkg/machinery/api/common/
|
||||
COPY --from=generate-build /api/health/health.pb.go /pkg/machinery/api/health/
|
||||
@ -99,6 +105,7 @@ COPY --from=generate-build /api/machine/machine.pb.go /pkg/machinery/api/machine
|
||||
COPY --from=generate-build /api/time/time.pb.go /pkg/machinery/api/time/
|
||||
COPY --from=generate-build /api/network/network.pb.go /pkg/machinery/api/network/
|
||||
COPY --from=generate-build /api/cluster/cluster.pb.go /pkg/machinery/api/cluster/
|
||||
COPY --from=generate-build /pkg/machinery/config/types/v1alpha1/*_doc.go /pkg/machinery/config/types/v1alpha1/
|
||||
|
||||
# The base target provides a container that can be used to build all Talos
|
||||
# assets.
|
||||
@ -112,6 +119,7 @@ COPY ./cmd ./cmd
|
||||
COPY ./pkg ./pkg
|
||||
COPY ./internal ./internal
|
||||
COPY --from=generate /pkg/machinery/api ./pkg/machinery/api
|
||||
COPY --from=generate /pkg/machinery/config ./pkg/machinery/config
|
||||
RUN go list -mod=readonly all >/dev/null
|
||||
RUN ! go mod tidy -v 2>&1 | grep .
|
||||
|
||||
@ -592,13 +600,12 @@ RUN find . -name '*.md' -not -path '*/node_modules/*' -not -path '*/docs/talosct
|
||||
# The docs target generates documentation.
|
||||
|
||||
FROM base AS docs-build
|
||||
WORKDIR /src/pkg/machinery/config
|
||||
RUN go generate ./types/v1alpha1
|
||||
WORKDIR /src
|
||||
COPY --from=talosctl-linux /talosctl-linux-amd64 /bin/talosctl
|
||||
RUN mkdir -p /docs/talosctl \
|
||||
&& env HOME=/home/user TAG=latest /bin/talosctl docs /docs/talosctl
|
||||
&& env HOME=/home/user TAG=latest /bin/talosctl docs --config /docs/configuration \
|
||||
&& env HOME=/home/user TAG=latest /bin/talosctl docs --cli /docs/talosctl
|
||||
|
||||
FROM scratch AS docs
|
||||
COPY --from=docs-build /tmp/v1alpha1.md /docs/website/content/v0.7/en/configuration/v1alpha1.md
|
||||
COPY --from=docs-build /docs/configuration/* /docs/website/content/v0.7/en/configuration/
|
||||
COPY --from=docs-build /docs/talosctl/* /docs/talosctl/
|
||||
|
1
Makefile
1
Makefile
@ -130,6 +130,7 @@ generate: ## Generates source code from protobuf definitions.
|
||||
|
||||
.PHONY: docs
|
||||
docs: ## Generates the documentation for machine config, and talosctl.
|
||||
@rm -rf docs/configuration/*
|
||||
@rm -rf docs/talosctl/*
|
||||
@$(MAKE) local-$@ DEST=./ PLATFORM=linux/amd64
|
||||
|
||||
|
@ -10,6 +10,8 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/cobra/doc"
|
||||
|
||||
v1alpha1 "github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1"
|
||||
)
|
||||
|
||||
func filePrepender(filename string) string {
|
||||
@ -18,10 +20,15 @@ func filePrepender(filename string) string {
|
||||
|
||||
func linkHandler(s string) string { return s }
|
||||
|
||||
var (
|
||||
cliDocs bool
|
||||
configDocs bool
|
||||
)
|
||||
|
||||
// docsCmd represents the docs command.
|
||||
var docsCmd = &cobra.Command{
|
||||
Use: "docs <output>",
|
||||
Short: "Generate documentation for the CLI",
|
||||
Use: "docs <output> [flags]",
|
||||
Short: "Generate documentation for the CLI or config",
|
||||
Long: ``,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Hidden: true,
|
||||
@ -32,8 +39,18 @@ var docsCmd = &cobra.Command{
|
||||
return fmt.Errorf("failed to create output directory %q", out)
|
||||
}
|
||||
|
||||
if err := doc.GenMarkdownTreeCustom(rootCmd, out, filePrepender, linkHandler); err != nil {
|
||||
return fmt.Errorf("failed to generate docs: %w", err)
|
||||
all := !cliDocs && !configDocs
|
||||
|
||||
if cliDocs || all {
|
||||
if err := doc.GenMarkdownTreeCustom(rootCmd, out, filePrepender, linkHandler); err != nil {
|
||||
return fmt.Errorf("failed to generate docs: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if configDocs || all {
|
||||
if err := v1alpha1.GetDoc().Write(out); err != nil {
|
||||
return fmt.Errorf("failed to generate docs: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -41,5 +58,7 @@ var docsCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func init() {
|
||||
docsCmd.Flags().BoolVarP(&configDocs, "config", "c", false, "generate docs for v1alpha1 configs")
|
||||
docsCmd.Flags().BoolVarP(&cliDocs, "cli", "C", false, "generate docs for CLI commands")
|
||||
rootCmd.AddCommand(docsCmd)
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,4 +2,7 @@ module github.com/talos-systems/docgen
|
||||
|
||||
go 1.13
|
||||
|
||||
require gopkg.in/yaml.v2 v2.2.5
|
||||
require (
|
||||
gopkg.in/yaml.v2 v2.2.5
|
||||
mvdan.cc/gofumpt v0.0.0-20200927160801-5bfeb2e70dd6
|
||||
)
|
||||
|
@ -1,4 +1,40 @@
|
||||
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6 h1:qKpj8TpV+LEhel7H/fR788J+KvhWZ3o3V6N2fU/iuLU=
|
||||
golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
mvdan.cc/gofumpt v0.0.0-20200927160801-5bfeb2e70dd6 h1:z+/YqapuV7VZPvBb3GYmuEJbA88M3PFUxaHilHYVCpQ=
|
||||
mvdan.cc/gofumpt v0.0.0-20200927160801-5bfeb2e70dd6/go.mod h1:bzrjFmaD6+xqohD3KYP0H2FEuxknnBmyyOxdhLdaIws=
|
||||
|
@ -5,6 +5,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
@ -16,62 +17,116 @@ import (
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
"mvdan.cc/gofumpt/format"
|
||||
)
|
||||
|
||||
var tpl = `// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// DO NOT EDIT: this file is automatically generated by docgen
|
||||
|
||||
package {{ .Package }}
|
||||
|
||||
import (
|
||||
"github.com/talos-systems/talos/pkg/machinery/config/encoder"
|
||||
)
|
||||
|
||||
var tpl = `
|
||||
{{ $tick := "` + "`" + `" -}}
|
||||
### {{ .Name }}
|
||||
var (
|
||||
{{ range $struct := .Structs -}}
|
||||
{{ $struct.Name }}Doc encoder.Doc
|
||||
{{ end -}}
|
||||
)
|
||||
|
||||
{{ range $entry := .Entries -}}
|
||||
#### {{ $entry.Name }}
|
||||
func init() {
|
||||
{{ range $struct := .Structs -}}
|
||||
{{ $docVar := printf "%v%v" $struct.Name "Doc" }}
|
||||
{{ $docVar }}.Type = "{{ $struct.Name }}"
|
||||
{{ $docVar }}.Comments[encoder.HeadComment] = "{{ $struct.Text.Comment }}"
|
||||
{{ $docVar }}.Description = "{{ $struct.Text.Description }}"
|
||||
{{ range $example := $struct.Text.Examples }}
|
||||
{{ if $example.Value }}
|
||||
{{ $docVar }}.AddExample("{{ $example.Name }}", {{ $example.Value }})
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ $docVar }}.Fields = make([]encoder.Doc,{{ len $struct.Fields }})
|
||||
{{ range $index, $field := $struct.Fields -}}
|
||||
{{ $docVar }}.Fields[{{ $index }}].Name = "{{ $field.Tag }}"
|
||||
{{ $docVar }}.Fields[{{ $index }}].Type = "{{ $field.Type }}"
|
||||
{{ $docVar }}.Fields[{{ $index }}].Note = "{{ $field.Note }}"
|
||||
{{ $docVar }}.Fields[{{ $index }}].Description = "{{ $field.Text.Description }}"
|
||||
{{ $docVar }}.Fields[{{ $index }}].Comments[encoder.LineComment] = "{{ $field.Text.Comment }}"
|
||||
{{ range $example := $field.Text.Examples }}
|
||||
{{ if $example.Value }}
|
||||
{{ $docVar }}.Fields[{{ $index }}].AddExample("{{ $example.Name }}", {{ $example.Value }})
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ if $field.Text.Values -}}
|
||||
{{ $docVar }}.Fields[{{ $index }}].Values = []string{
|
||||
{{ range $value := $field.Text.Values -}}
|
||||
"{{ $value }}",
|
||||
{{ end -}}
|
||||
}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ end }}
|
||||
}
|
||||
|
||||
{{ $entry.Text.Description }}
|
||||
Type: {{ $tick }}{{ $entry.Type }}{{ $tick }}
|
||||
|
||||
{{ if $entry.Text.Values -}}
|
||||
Valid Values:
|
||||
|
||||
{{ range $value := $entry.Text.Values -}}
|
||||
- {{ $tick }}{{ $value }}{{ $tick }}
|
||||
{{ end }}
|
||||
{{ range $struct := .Structs -}}
|
||||
func (_ {{ $struct.Name }}) Doc() *encoder.Doc {
|
||||
return &{{ $struct.Name }}Doc
|
||||
}
|
||||
{{ end -}}
|
||||
{{ if $entry.Text.Examples -}}
|
||||
Examples:
|
||||
{{ range $example := $entry.Text.Examples }}
|
||||
{{ $tick }}{{ $tick }}{{ $tick }}yaml
|
||||
{{ $example }}
|
||||
{{ $tick }}{{ $tick }}{{ $tick }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{- if $entry.Note -}}
|
||||
> {{ $entry.Note }}
|
||||
{{ end }}
|
||||
{{- end -}}
|
||||
---
|
||||
|
||||
// Get{{ .Name }}Doc returns documentation for the file {{ .File }}.
|
||||
func Get{{ .Name }}Doc() *encoder.FileDoc {
|
||||
return &encoder.FileDoc{
|
||||
Name: "{{ .Package }}{{ if .Name }}.{{ .Name }}{{ end }}",
|
||||
Description: "---\ntitle: {{ .Package }}{{ if .Name }}.{{ .Name }}{{ end }}\n---\n<!-- markdownlint-disable MD024 -->\n\n{{ .Header }}",
|
||||
Structs: []*encoder.Doc{
|
||||
{{ range $struct := .Structs -}}
|
||||
&{{ $struct.Name }}Doc,
|
||||
{{ end -}}
|
||||
},
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
type Doc struct {
|
||||
Title string
|
||||
Sections []*Section
|
||||
}
|
||||
|
||||
type Section struct {
|
||||
Name string
|
||||
Entries []*Entry
|
||||
Package string
|
||||
Title string
|
||||
Header string
|
||||
File string
|
||||
Structs []*Struct
|
||||
}
|
||||
|
||||
type Entry struct {
|
||||
type Struct struct {
|
||||
Name string
|
||||
Text *Text
|
||||
Fields []*Field
|
||||
}
|
||||
|
||||
type Example struct {
|
||||
Name string `yaml:"name"`
|
||||
Value string `yaml:"value"`
|
||||
}
|
||||
|
||||
type Field struct {
|
||||
Name string
|
||||
Type string
|
||||
Text *Text
|
||||
Tag string
|
||||
Note string
|
||||
}
|
||||
|
||||
type Text struct {
|
||||
Description string `json:"description"`
|
||||
Values []string `json:"values"`
|
||||
Examples []string `json:"examples"`
|
||||
Comment string `json:"-"`
|
||||
Description string `json:"description"`
|
||||
Examples []*Example `json:"examples"`
|
||||
Values []string `json:"values"`
|
||||
}
|
||||
|
||||
func in(p string) (string, error) {
|
||||
@ -89,6 +144,7 @@ func out(p string) (*os.File, error) {
|
||||
|
||||
type structType struct {
|
||||
name string
|
||||
text *Text
|
||||
pos token.Pos
|
||||
node *ast.StructType
|
||||
}
|
||||
@ -127,8 +183,15 @@ func collectStructs(node ast.Node) []*structType {
|
||||
|
||||
structName := t.Name.Name
|
||||
|
||||
text := &Text{}
|
||||
|
||||
if t.Doc != nil {
|
||||
text = parseComment([]byte(t.Doc.Text()))
|
||||
}
|
||||
|
||||
s := &structType{
|
||||
name: structName,
|
||||
text: text,
|
||||
node: x,
|
||||
pos: x.Pos(),
|
||||
}
|
||||
@ -144,17 +207,40 @@ func collectStructs(node ast.Node) []*structType {
|
||||
return structs
|
||||
}
|
||||
|
||||
type field struct {
|
||||
func parseComment(comment []byte) *Text {
|
||||
text := &Text{}
|
||||
if err := yaml.Unmarshal(comment, text); err != nil {
|
||||
// not yaml, fallback
|
||||
text.Description = string(comment)
|
||||
// take only the first line from the Description for the comment
|
||||
text.Comment = strings.Split(text.Description, "\n")[0]
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
text.Description = strings.TrimSpace(text.Description)
|
||||
// take only the first line from the Description for the comment
|
||||
text.Comment = strings.Split(text.Description, "\n")[0]
|
||||
|
||||
text.Description = escape(text.Description)
|
||||
for _, example := range text.Examples {
|
||||
example.Name = escape(example.Name)
|
||||
example.Value = strings.TrimSpace(example.Value)
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
func parseFieldType(p interface{}) string {
|
||||
if m, ok := p.(*ast.MapType); ok {
|
||||
return fmt.Sprintf("map[%s]%s", parseFieldType(m.Key), parseFieldType(m.Value))
|
||||
}
|
||||
|
||||
switch t := p.(type) {
|
||||
case *ast.Ident:
|
||||
return t.Name
|
||||
case *ast.ArrayType:
|
||||
return "array"
|
||||
case *ast.MapType:
|
||||
return "map"
|
||||
return "[]" + parseFieldType(p.(*ast.ArrayType).Elt)
|
||||
case *ast.StructType:
|
||||
return "struct"
|
||||
case *ast.StarExpr:
|
||||
@ -163,56 +249,84 @@ func parseFieldType(p interface{}) string {
|
||||
return parseFieldType(t.Sel)
|
||||
default:
|
||||
log.Printf("unknown: %#v", t)
|
||||
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func collectFields(s *structType) (entries []*Entry) {
|
||||
entries = []*Entry{}
|
||||
func escape(value string) string {
|
||||
return strings.TrimSpace(strings.ReplaceAll(
|
||||
strings.ReplaceAll(value, "\"", "\\\""),
|
||||
"\n",
|
||||
"\\n",
|
||||
))
|
||||
}
|
||||
|
||||
for _, field := range s.node.Fields.List {
|
||||
if field.Tag == nil {
|
||||
if field.Names == nil {
|
||||
func collectFields(s *structType) (fields []*Field) {
|
||||
fields = []*Field{}
|
||||
|
||||
for _, f := range s.node.Fields.List {
|
||||
if f.Tag == nil {
|
||||
if f.Names == nil {
|
||||
// This is an embedded struct.
|
||||
continue
|
||||
}
|
||||
log.Fatalf("field %q is missing a yaml tag", field.Names[0].Name)
|
||||
}
|
||||
|
||||
if field.Doc == nil {
|
||||
log.Fatalf("field %q is missing a documentation", field.Names[0].Name)
|
||||
if f.Doc == nil {
|
||||
log.Fatalf("field %q is missing a documentation", f.Names[0].Name)
|
||||
}
|
||||
|
||||
tag := reflect.StructTag(strings.Trim(field.Tag.Value, "`"))
|
||||
name := tag.Get("yaml")
|
||||
name = strings.Split(name, ",")[0]
|
||||
name := f.Names[0].Name
|
||||
|
||||
fieldType := parseFieldType(field.Type)
|
||||
fieldType := parseFieldType(f.Type)
|
||||
|
||||
text := &Text{}
|
||||
if err := yaml.Unmarshal([]byte(field.Doc.Text()), text); err != nil {
|
||||
log.Fatal(err)
|
||||
tag := reflect.StructTag(strings.Trim(f.Tag.Value, "`"))
|
||||
yamlTag := tag.Get("yaml")
|
||||
yamlTag = strings.Split(yamlTag, ",")[0]
|
||||
|
||||
if yamlTag == "" {
|
||||
yamlTag = strings.ToLower(yamlTag)
|
||||
}
|
||||
|
||||
entry := &Entry{
|
||||
text := parseComment([]byte(f.Doc.Text()))
|
||||
|
||||
field := &Field{
|
||||
Name: name,
|
||||
Tag: yamlTag,
|
||||
Type: fieldType,
|
||||
Text: text,
|
||||
}
|
||||
|
||||
if field.Comment != nil {
|
||||
entry.Note = field.Comment.Text()
|
||||
if f.Comment != nil {
|
||||
field.Note = escape(f.Comment.Text())
|
||||
}
|
||||
|
||||
entries = append(entries, entry)
|
||||
fields = append(fields, field)
|
||||
}
|
||||
|
||||
return entries
|
||||
return fields
|
||||
}
|
||||
|
||||
func render(section *Section, f *os.File) {
|
||||
t := template.Must(template.New("section.tpl").Parse(tpl))
|
||||
err := t.Execute(f, section)
|
||||
func render(doc *Doc, dest string) {
|
||||
t := template.Must(template.New("docfile.tpl").Parse(tpl))
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
err := t.Execute(&buf, doc)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
formatted, err := format.Source(buf.Bytes(), format.Options{})
|
||||
if err != nil {
|
||||
log.Printf("data: %s", buf.Bytes())
|
||||
panic(err)
|
||||
}
|
||||
|
||||
out, err := out(dest)
|
||||
defer out.Close()
|
||||
_, err = out.Write(formatted)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -228,64 +342,58 @@ func main() {
|
||||
|
||||
fset := token.NewFileSet()
|
||||
|
||||
pkgs, err := parser.ParseDir(fset, abs, nil, parser.ParseComments)
|
||||
node, err := parser.ParseFile(fset, abs, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var structs []*structType
|
||||
|
||||
for _, pkg := range pkgs {
|
||||
for _, astFile := range pkg.Files {
|
||||
tokenFile := fset.File(astFile.Pos())
|
||||
if tokenFile == nil {
|
||||
continue
|
||||
}
|
||||
packageName := node.Name.Name
|
||||
|
||||
fmt.Printf("parsing file in package %q: %s\n", pkg.Name, tokenFile.Name())
|
||||
structs = append(structs, collectStructs(astFile)...)
|
||||
}
|
||||
tokenFile := fset.File(node.Pos())
|
||||
if tokenFile == nil {
|
||||
log.Fatalf("No token")
|
||||
}
|
||||
|
||||
fmt.Printf("parsing file in package %q: %s\n", packageName, tokenFile.Name())
|
||||
|
||||
structs = append(structs, collectStructs(node)...)
|
||||
|
||||
if len(structs) == 0 {
|
||||
log.Fatalf("failed to find types that could be documented in %s", abs)
|
||||
}
|
||||
|
||||
doc := &Doc{
|
||||
Sections: []*Section{},
|
||||
Package: packageName,
|
||||
Structs: []*Struct{},
|
||||
}
|
||||
|
||||
for _, s := range structs {
|
||||
fmt.Printf("generating docs for type: %q\n", s.name)
|
||||
|
||||
entries := collectFields(s)
|
||||
fields := collectFields(s)
|
||||
|
||||
section := &Section{
|
||||
Name: s.name,
|
||||
Entries: entries,
|
||||
s := &Struct{
|
||||
Name: s.name,
|
||||
Text: s.text,
|
||||
Fields: fields,
|
||||
}
|
||||
|
||||
doc.Sections = append(doc.Sections, section)
|
||||
doc.Structs = append(doc.Structs, s)
|
||||
}
|
||||
|
||||
node, err := parser.ParseFile(fset, filepath.Join(abs, "doc.go"), nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
if err == nil && len(os.Args) > 3 {
|
||||
doc.Package = node.Name.Name
|
||||
if os.Args[3] != doc.Package {
|
||||
doc.Name = os.Args[3]
|
||||
}
|
||||
|
||||
if node.Doc != nil {
|
||||
doc.Header = escape(node.Doc.Text())
|
||||
}
|
||||
}
|
||||
|
||||
out, err := out(os.Args[2])
|
||||
defer out.Close()
|
||||
|
||||
out.WriteString("---\n")
|
||||
out.WriteString("title: " + node.Name.Name + "\n")
|
||||
out.WriteString("---\n")
|
||||
out.WriteString("\n")
|
||||
out.WriteString("<!-- markdownlint-disable MD024 -->")
|
||||
out.WriteString("\n")
|
||||
out.WriteString("\n")
|
||||
out.WriteString(node.Doc.Text())
|
||||
|
||||
for _, section := range doc.Sections {
|
||||
render(section, out)
|
||||
}
|
||||
doc.File = os.Args[2]
|
||||
render(doc, os.Args[2])
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ type Mock struct {
|
||||
}
|
||||
|
||||
func init() {
|
||||
config.Register("mock", func(verion string) interface{} {
|
||||
config.Register("mock", func(string) interface{} {
|
||||
return &Mock{}
|
||||
})
|
||||
}
|
||||
|
264
pkg/machinery/config/encoder/documentation.go
Normal file
264
pkg/machinery/config/encoder/documentation.go
Normal file
@ -0,0 +1,264 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
// HeadComment populates `yaml.Node` `HeadComment`.
|
||||
HeadComment = iota
|
||||
// LineComment populates `yaml.Node` `LineComment`.
|
||||
LineComment
|
||||
// FootComment populates `yaml.Node` `FootComment`.
|
||||
FootComment
|
||||
)
|
||||
|
||||
// Doc represents a struct documentation rendered from comments by docgen.
|
||||
type Doc struct {
|
||||
// Comments stores foot, line and head comments.
|
||||
Comments [3]string
|
||||
// Fields contains fields documentation if related item is a struct.
|
||||
Fields []Doc
|
||||
// Examples list of example values for the item.
|
||||
Examples []*Example
|
||||
// Values is only used to render valid values list in the documentation.
|
||||
Values []string
|
||||
// Description represents the full description for the item.
|
||||
Description string
|
||||
// Name represents struct name or field name.
|
||||
Name string
|
||||
// Type represents struct name or field type.
|
||||
Type string
|
||||
// Note is rendered as a note for the example in markdown file.
|
||||
Note string
|
||||
}
|
||||
|
||||
// AddExample adds a new example snippet to the doc.
|
||||
func (d *Doc) AddExample(name string, value interface{}) {
|
||||
if d.Examples == nil {
|
||||
d.Examples = []*Example{}
|
||||
}
|
||||
|
||||
d.Examples = append(d.Examples, &Example{
|
||||
Name: name,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
|
||||
// Example represents one example snippet for a type.
|
||||
type Example struct {
|
||||
Name string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// Field gets field from the list of fields.
|
||||
func (d *Doc) Field(i int) *Doc {
|
||||
if i < len(d.Fields) {
|
||||
return &d.Fields[i]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Documented is used to check if struct has any documentation defined for it.
|
||||
type Documented interface {
|
||||
// Doc requests documentation object.
|
||||
Doc() *Doc
|
||||
}
|
||||
|
||||
func mergeDoc(a, b *Doc) *Doc {
|
||||
var res Doc
|
||||
if a != nil {
|
||||
res = *a
|
||||
}
|
||||
|
||||
if b == nil {
|
||||
return &res
|
||||
}
|
||||
|
||||
for i, comment := range b.Comments {
|
||||
if comment != "" {
|
||||
res.Comments[i] = comment
|
||||
}
|
||||
}
|
||||
|
||||
if len(res.Examples) == 0 {
|
||||
res.Examples = b.Examples
|
||||
}
|
||||
|
||||
return &res
|
||||
}
|
||||
|
||||
func getDoc(in interface{}) *Doc {
|
||||
v := reflect.ValueOf(in)
|
||||
if v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
in = reflect.New(v.Type().Elem()).Interface()
|
||||
}
|
||||
|
||||
if d, ok := in.(Documented); ok {
|
||||
return d.Doc()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func addComments(node *yaml.Node, doc *Doc, comments ...int) {
|
||||
if doc != nil {
|
||||
dest := []*string{
|
||||
&node.HeadComment,
|
||||
&node.LineComment,
|
||||
&node.FootComment,
|
||||
}
|
||||
|
||||
if len(comments) == 0 {
|
||||
comments = []int{
|
||||
HeadComment,
|
||||
LineComment,
|
||||
FootComment,
|
||||
}
|
||||
}
|
||||
|
||||
for _, i := range comments {
|
||||
if doc.Comments[i] != "" {
|
||||
*dest[i] = doc.Comments[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nolint:gocyclo
|
||||
func renderExample(key string, doc *Doc) string {
|
||||
if doc == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
examples := []string{}
|
||||
|
||||
for i, e := range doc.Examples {
|
||||
v := reflect.ValueOf(e.Value)
|
||||
|
||||
if !isSet(v) {
|
||||
continue
|
||||
}
|
||||
|
||||
if v.Kind() != reflect.Ptr {
|
||||
v = reflect.Indirect(v)
|
||||
}
|
||||
|
||||
defaultValue := v.Interface()
|
||||
populateExamples(defaultValue, i)
|
||||
|
||||
node, err := toYamlNode(defaultValue)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
node, err = toYamlNode(map[string]*yaml.Node{
|
||||
key: node,
|
||||
})
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
addComments(node, doc, HeadComment, LineComment)
|
||||
}
|
||||
|
||||
// replace head comment with line comment
|
||||
if node.HeadComment == "" {
|
||||
node.HeadComment = node.LineComment
|
||||
}
|
||||
|
||||
node.LineComment = ""
|
||||
if e.Name != "" {
|
||||
if node.HeadComment != "" {
|
||||
node.HeadComment += "\n\n"
|
||||
}
|
||||
|
||||
node.HeadComment = node.HeadComment + e.Name + "\n"
|
||||
}
|
||||
|
||||
data, err := yaml.Marshal(node)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var example string
|
||||
|
||||
// don't collapse comment
|
||||
re := regexp.MustCompile(`(?m)^#`)
|
||||
data = re.ReplaceAll(data, []byte("# #"))
|
||||
|
||||
example += string(data)
|
||||
examples = append(examples, example)
|
||||
}
|
||||
|
||||
return strings.Join(examples, "")
|
||||
}
|
||||
|
||||
func readExample(v reflect.Value, doc *Doc, index int) {
|
||||
if doc == nil || len(doc.Examples) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
numExamples := len(doc.Examples)
|
||||
if index >= numExamples {
|
||||
index = numExamples - 1
|
||||
}
|
||||
|
||||
defaultValue := reflect.ValueOf(doc.Examples[index].Value)
|
||||
if isSet(defaultValue) {
|
||||
if v.Kind() != reflect.Ptr && defaultValue.Kind() == reflect.Ptr {
|
||||
defaultValue = defaultValue.Elem()
|
||||
}
|
||||
|
||||
v.Set(defaultValue.Convert(v.Type()))
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func populateExamples(in interface{}, index int) {
|
||||
doc := getDoc(in)
|
||||
|
||||
if reflect.TypeOf(in).Kind() != reflect.Ptr {
|
||||
return
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(in).Elem()
|
||||
|
||||
readExample(v, doc, index)
|
||||
|
||||
//nolint:exhaustive
|
||||
switch v.Kind() {
|
||||
case reflect.Struct:
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
field := v.Field(i)
|
||||
if !field.CanInterface() {
|
||||
continue
|
||||
}
|
||||
|
||||
if doc != nil && i < len(doc.Fields) {
|
||||
readExample(field, doc.Field(i), index)
|
||||
}
|
||||
|
||||
value := field.Interface()
|
||||
populateExamples(value, index)
|
||||
}
|
||||
case reflect.Map:
|
||||
for _, key := range v.MapKeys() {
|
||||
populateExamples(v.MapIndex(key), index)
|
||||
}
|
||||
case reflect.Slice:
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
populateExamples(v.Index(i), index)
|
||||
}
|
||||
}
|
||||
}
|
292
pkg/machinery/config/encoder/encoder.go
Normal file
292
pkg/machinery/config/encoder/encoder.go
Normal file
@ -0,0 +1,292 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Encoder implements config encoder.
|
||||
type Encoder struct {
|
||||
value interface{}
|
||||
}
|
||||
|
||||
// NewEncoder initializes and returns an `Encoder`.
|
||||
func NewEncoder(value interface{}) *Encoder {
|
||||
return &Encoder{
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// Encode convert value to yaml.
|
||||
func (e *Encoder) Encode() ([]byte, error) {
|
||||
node, err := toYamlNode(e.value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addComments(node, getDoc(e.value), HeadComment, LineComment)
|
||||
|
||||
// special handling for case when we get an empty output
|
||||
if node.Kind == yaml.MappingNode && len(node.Content) == 0 && node.FootComment != "" {
|
||||
res := ""
|
||||
|
||||
if node.HeadComment != "" {
|
||||
res += node.HeadComment + "\n"
|
||||
}
|
||||
|
||||
lines := strings.Split(res+node.FootComment, "\n")
|
||||
for i, line := range lines {
|
||||
if strings.HasPrefix(line, "#") || strings.TrimSpace(line) == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
lines[i] = "# " + line
|
||||
}
|
||||
|
||||
return []byte(strings.Join(lines, "\n")), nil
|
||||
}
|
||||
|
||||
return yaml.Marshal(node)
|
||||
}
|
||||
|
||||
func isSet(value reflect.Value) bool {
|
||||
if !value.IsValid() {
|
||||
return false
|
||||
}
|
||||
|
||||
//nolint:exhaustive
|
||||
switch value.Kind() {
|
||||
case reflect.Ptr:
|
||||
return !value.IsNil() && !value.Elem().IsZero()
|
||||
case reflect.Map:
|
||||
return len(value.MapKeys()) != 0
|
||||
case reflect.Slice:
|
||||
return value.Len() > 0
|
||||
default:
|
||||
return !value.IsZero()
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func toYamlNode(in interface{}) (*yaml.Node, error) {
|
||||
node := &yaml.Node{}
|
||||
|
||||
// do not wrap yaml.Node into yaml.Node
|
||||
if n, ok := in.(*yaml.Node); ok {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// if input implements yaml.Marshaler we should use that marshaller instead
|
||||
// same way as regular yaml marshal does
|
||||
if m, ok := in.(yaml.Marshaler); ok {
|
||||
res, err := m.MarshalYAML()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if n, ok := res.(*yaml.Node); ok {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
in = res
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(in)
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
doc := getDoc(in)
|
||||
|
||||
//nolint:exhaustive
|
||||
switch v.Kind() {
|
||||
case reflect.Struct:
|
||||
node.Kind = yaml.MappingNode
|
||||
|
||||
t := v.Type()
|
||||
|
||||
examples := []string{}
|
||||
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
// skip unexported fields
|
||||
if !v.Field(i).CanInterface() {
|
||||
continue
|
||||
}
|
||||
|
||||
tag := t.Field(i).Tag.Get("yaml")
|
||||
parts := strings.Split(tag, ",")
|
||||
fieldName := parts[0]
|
||||
|
||||
if fieldName == "" {
|
||||
fieldName = strings.ToLower(t.Field(i).Name)
|
||||
}
|
||||
|
||||
if fieldName == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
var (
|
||||
defined bool = isSet(v.Field(i))
|
||||
|
||||
skip bool
|
||||
inline bool
|
||||
flow bool
|
||||
)
|
||||
|
||||
for i, part := range parts {
|
||||
// always skip the first argument
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if part == "omitempty" && !defined {
|
||||
skip = true
|
||||
}
|
||||
|
||||
if part == "inline" {
|
||||
inline = true
|
||||
}
|
||||
|
||||
if part == "flow" {
|
||||
flow = true
|
||||
}
|
||||
}
|
||||
|
||||
var value interface{}
|
||||
if v.Field(i).CanInterface() {
|
||||
value = v.Field(i).Interface()
|
||||
}
|
||||
|
||||
// get documentation data either from field, or from type
|
||||
var fieldDoc *Doc
|
||||
|
||||
if doc != nil {
|
||||
fieldDoc = mergeDoc(getDoc(value), doc.Field(i))
|
||||
} else {
|
||||
fieldDoc = getDoc(value)
|
||||
}
|
||||
|
||||
if !defined {
|
||||
example := renderExample(fieldName, fieldDoc)
|
||||
|
||||
if example != "" {
|
||||
examples = append(examples, example)
|
||||
skip = true
|
||||
}
|
||||
}
|
||||
|
||||
var style yaml.Style
|
||||
if flow {
|
||||
style |= yaml.FlowStyle
|
||||
}
|
||||
|
||||
if !skip {
|
||||
if inline {
|
||||
child, err := toYamlNode(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if child.Kind == yaml.MappingNode || child.Kind == yaml.SequenceNode {
|
||||
appendNodes(node, child.Content...)
|
||||
}
|
||||
} else if err := addToMap(node, fieldDoc, fieldName, value, style); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(examples) > 0 {
|
||||
comment := strings.Join(examples, "\n")
|
||||
// add rendered example to the foot comment of the last node
|
||||
// or to the foot comment of parent node
|
||||
if len(node.Content) > 0 {
|
||||
node.Content[len(node.Content)-2].FootComment += "\n" + comment
|
||||
} else {
|
||||
node.FootComment += comment
|
||||
}
|
||||
}
|
||||
case reflect.Map:
|
||||
node.Kind = yaml.MappingNode
|
||||
keys := v.MapKeys()
|
||||
// always interate keys in alphabetical order to preserve the same output for maps
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
return keys[i].String() < keys[j].String()
|
||||
})
|
||||
|
||||
for _, k := range keys {
|
||||
element := v.MapIndex(k)
|
||||
value := element.Interface()
|
||||
|
||||
if err := addToMap(node, getDoc(value), k.Interface(), value, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
case reflect.Slice:
|
||||
node.Kind = yaml.SequenceNode
|
||||
nodes := make([]*yaml.Node, v.Len())
|
||||
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
element := v.Index(i)
|
||||
|
||||
var err error
|
||||
|
||||
nodes[i], err = toYamlNode(element.Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
appendNodes(node, nodes...)
|
||||
default:
|
||||
if err := node.Encode(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return node, nil
|
||||
}
|
||||
|
||||
func appendNodes(dest *yaml.Node, nodes ...*yaml.Node) {
|
||||
if dest.Content == nil {
|
||||
dest.Content = []*yaml.Node{}
|
||||
}
|
||||
|
||||
dest.Content = append(dest.Content, nodes...)
|
||||
}
|
||||
|
||||
func addToMap(dest *yaml.Node, doc *Doc, fieldName, in interface{}, style yaml.Style) error {
|
||||
key, err := toYamlNode(fieldName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
value, err := toYamlNode(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
value.Style = style
|
||||
|
||||
addComments(key, doc, HeadComment, FootComment)
|
||||
addComments(value, doc, LineComment)
|
||||
|
||||
// override head comment with line comment for non-scalar nodes
|
||||
if value.Kind != yaml.ScalarNode {
|
||||
if key.HeadComment == "" {
|
||||
key.HeadComment = value.LineComment
|
||||
}
|
||||
|
||||
value.LineComment = ""
|
||||
}
|
||||
|
||||
appendNodes(dest, key, value)
|
||||
|
||||
return nil
|
||||
}
|
384
pkg/machinery/config/encoder/encoder_test.go
Normal file
384
pkg/machinery/config/encoder/encoder_test.go
Normal file
@ -0,0 +1,384 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package encoder_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/talos-systems/talos/pkg/machinery/config/encoder"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Integer int `yaml:"integer"`
|
||||
Slice []string `yaml:"slice"`
|
||||
ComplexSlice []*Endpoint `yaml:"complex_slice"`
|
||||
Map map[string]*Endpoint `yaml:"map"`
|
||||
|
||||
Skip string `yaml:"-"`
|
||||
Omit int `yaml:",omitempty"`
|
||||
Inline *Mixin `yaml:",inline"`
|
||||
|
||||
CustomMarshaller *WithCustomMarshaller `yaml:",omitempty"`
|
||||
Bytes []byte `yaml:"bytes,flow,omitempty"`
|
||||
|
||||
unexported int
|
||||
}
|
||||
|
||||
type FakeConfig struct {
|
||||
Machine Machine
|
||||
}
|
||||
|
||||
type Mixin struct {
|
||||
MixedIn string `yaml:"mixed_in"`
|
||||
}
|
||||
|
||||
type Endpoint struct {
|
||||
Host string
|
||||
Port int `yaml:",omitempty"`
|
||||
}
|
||||
|
||||
type Machine struct {
|
||||
State int
|
||||
Config *MachineConfig
|
||||
}
|
||||
|
||||
type MachineConfig struct {
|
||||
Version string
|
||||
Capabilities []string
|
||||
}
|
||||
|
||||
type WithCustomMarshaller struct {
|
||||
value string
|
||||
}
|
||||
|
||||
// MarshalYAML implements custom marshaller.
|
||||
func (cm *WithCustomMarshaller) MarshalYAML() (interface{}, error) {
|
||||
node := &yaml.Node{}
|
||||
|
||||
if err := node.Encode(map[string]string{"value": cm.value}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
node.HeadComment = "completely custom"
|
||||
|
||||
return node, nil
|
||||
}
|
||||
|
||||
// This is manually defined documentation data for Config.
|
||||
// It is intended to be generated by `docgen` command.
|
||||
var (
|
||||
configDoc encoder.Doc
|
||||
endpointDoc encoder.Doc
|
||||
mixinDoc encoder.Doc
|
||||
machineDoc encoder.Doc
|
||||
machineConfigDoc encoder.Doc
|
||||
)
|
||||
|
||||
func init() {
|
||||
configDoc.Comments[encoder.HeadComment] = "test configuration"
|
||||
configDoc.Fields = make([]encoder.Doc, 8)
|
||||
configDoc.Fields[1].Comments[encoder.LineComment] = "<<<"
|
||||
configDoc.Fields[2].Comments[encoder.HeadComment] = "complex slice"
|
||||
configDoc.Fields[3].Comments[encoder.FootComment] = "some text example for map"
|
||||
|
||||
endpointDoc.Comments[encoder.HeadComment] = "endpoint settings"
|
||||
endpointDoc.Fields = make([]encoder.Doc, 2)
|
||||
endpointDoc.Fields[0].Comments[encoder.LineComment] = "endpoint host"
|
||||
endpointDoc.Fields[1].Comments[encoder.LineComment] = "custom port"
|
||||
|
||||
mixinDoc.Fields = make([]encoder.Doc, 1)
|
||||
mixinDoc.Fields[0].Comments[encoder.LineComment] = "was inlined"
|
||||
|
||||
machineDoc.Examples = []*encoder.Example{
|
||||
{
|
||||
Name: "uncomment me",
|
||||
Value: &Machine{
|
||||
State: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "second example",
|
||||
Value: &Machine{
|
||||
State: -1,
|
||||
},
|
||||
},
|
||||
}
|
||||
machineDoc.Fields = make([]encoder.Doc, 2)
|
||||
machineDoc.Fields[1].Examples = []*encoder.Example{
|
||||
{
|
||||
Name: "",
|
||||
Value: &MachineConfig{
|
||||
Version: "0.0.2",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
machineConfigDoc.Fields = make([]encoder.Doc, 2)
|
||||
machineConfigDoc.Fields[0].Comments[encoder.HeadComment] = "this is some version"
|
||||
machineConfigDoc.Fields[1].Examples = []*encoder.Example{
|
||||
{
|
||||
Name: "",
|
||||
Value: []string{
|
||||
"reboot", "upgrade",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c Config) Doc() *encoder.Doc {
|
||||
return &configDoc
|
||||
}
|
||||
|
||||
func (c Endpoint) Doc() *encoder.Doc {
|
||||
return &endpointDoc
|
||||
}
|
||||
|
||||
func (c Mixin) Doc() *encoder.Doc {
|
||||
return &mixinDoc
|
||||
}
|
||||
|
||||
func (c Machine) Doc() *encoder.Doc {
|
||||
return &machineDoc
|
||||
}
|
||||
|
||||
func (c MachineConfig) Doc() *encoder.Doc {
|
||||
return &machineConfigDoc
|
||||
}
|
||||
|
||||
// tests
|
||||
|
||||
type EncoderSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func (suite *EncoderSuite) TestRun() {
|
||||
e := &Endpoint{
|
||||
Port: 8080,
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
value interface{}
|
||||
expectedYAML string
|
||||
incompatible bool
|
||||
}{
|
||||
{
|
||||
name: "default struct",
|
||||
value: &Config{},
|
||||
expectedYAML: `# test configuration
|
||||
integer: 0
|
||||
# <<<
|
||||
slice: []
|
||||
# complex slice
|
||||
complex_slice: []
|
||||
map: {}
|
||||
# some text example for map
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "struct with custom marshaller",
|
||||
value: &Config{
|
||||
CustomMarshaller: &WithCustomMarshaller{
|
||||
value: "abcd",
|
||||
},
|
||||
},
|
||||
expectedYAML: `# test configuration
|
||||
integer: 0
|
||||
# <<<
|
||||
slice: []
|
||||
# complex slice
|
||||
complex_slice: []
|
||||
map: {}
|
||||
# some text example for map
|
||||
|
||||
custommarshaller:
|
||||
# completely custom
|
||||
value: abcd
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "bytes flow",
|
||||
value: &Config{
|
||||
Bytes: []byte("..."),
|
||||
},
|
||||
expectedYAML: `# test configuration
|
||||
integer: 0
|
||||
# <<<
|
||||
slice: []
|
||||
# complex slice
|
||||
complex_slice: []
|
||||
map: {}
|
||||
# some text example for map
|
||||
|
||||
bytes: [46, 46, 46]
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "map check",
|
||||
value: &Config{
|
||||
Map: map[string]*Endpoint{
|
||||
"endpoint": new(Endpoint),
|
||||
},
|
||||
unexported: -1,
|
||||
},
|
||||
expectedYAML: `# test configuration
|
||||
integer: 0
|
||||
# <<<
|
||||
slice: []
|
||||
# complex slice
|
||||
complex_slice: []
|
||||
map:
|
||||
# endpoint settings
|
||||
endpoint:
|
||||
host: "" # endpoint host
|
||||
# some text example for map
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "nil map element",
|
||||
value: &Config{
|
||||
Map: map[string]*Endpoint{
|
||||
"endpoint": nil,
|
||||
},
|
||||
},
|
||||
expectedYAML: `# test configuration
|
||||
integer: 0
|
||||
# <<<
|
||||
slice: []
|
||||
# complex slice
|
||||
complex_slice: []
|
||||
map:
|
||||
# endpoint settings
|
||||
endpoint: null
|
||||
# some text example for map
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "nil map element",
|
||||
value: &Config{
|
||||
Map: map[string]*Endpoint{
|
||||
"endpoint": new(Endpoint),
|
||||
},
|
||||
ComplexSlice: []*Endpoint{e},
|
||||
},
|
||||
expectedYAML: `# test configuration
|
||||
integer: 0
|
||||
# <<<
|
||||
slice: []
|
||||
# complex slice
|
||||
complex_slice:
|
||||
- host: "" # endpoint host
|
||||
port: 8080 # custom port
|
||||
map:
|
||||
# endpoint settings
|
||||
endpoint:
|
||||
host: "" # endpoint host
|
||||
# some text example for map
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "inline",
|
||||
value: &Config{
|
||||
Inline: &Mixin{
|
||||
MixedIn: "a",
|
||||
},
|
||||
},
|
||||
expectedYAML: `# test configuration
|
||||
integer: 0
|
||||
# <<<
|
||||
slice: []
|
||||
# complex slice
|
||||
complex_slice: []
|
||||
map: {}
|
||||
# some text example for map
|
||||
|
||||
mixed_in: a # was inlined
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "comment example if zero",
|
||||
value: &FakeConfig{},
|
||||
expectedYAML: `# # uncomment me
|
||||
# machine:
|
||||
# state: 100
|
||||
# config:
|
||||
# # this is some version
|
||||
# version: 0.0.2
|
||||
# capabilities:
|
||||
# - reboot
|
||||
# - upgrade
|
||||
# # second example
|
||||
# machine:
|
||||
# state: -1
|
||||
# config:
|
||||
# # this is some version
|
||||
# version: 0.0.2
|
||||
# capabilities:
|
||||
# - reboot
|
||||
# - upgrade
|
||||
`,
|
||||
incompatible: true,
|
||||
},
|
||||
{
|
||||
name: "comment example if partially set",
|
||||
value: &FakeConfig{
|
||||
Machine{
|
||||
State: 1000,
|
||||
},
|
||||
},
|
||||
expectedYAML: `machine:
|
||||
state: 1000
|
||||
|
||||
# config:
|
||||
# # this is some version
|
||||
# version: 0.0.2
|
||||
# capabilities:
|
||||
# - reboot
|
||||
# - upgrade
|
||||
`,
|
||||
incompatible: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
encoder := encoder.NewEncoder(test.value)
|
||||
data, err := encoder.Encode()
|
||||
suite.Assert().NoError(err)
|
||||
|
||||
// compare with expected string output
|
||||
suite.Assert().EqualValues(test.expectedYAML, string(data), test.name)
|
||||
|
||||
// decode into raw map to strip all comments
|
||||
actualMap, err := decodeToMap(data)
|
||||
suite.Assert().NoError(err)
|
||||
|
||||
// skip if marshaller output is not the same for our encoder and vanilla one
|
||||
// note: it is only incompatible if config contains nested structs stored as value
|
||||
// and if these nested structs are documented and you try to load generated yaml into map[interface{}]interface{}
|
||||
if !test.incompatible {
|
||||
// compare with regular yaml.Marshal call
|
||||
expected, err := yaml.Marshal(test.value)
|
||||
suite.Assert().NoError(err)
|
||||
|
||||
expectedMap, err := decodeToMap(expected)
|
||||
suite.Assert().NoError(err)
|
||||
suite.Assert().EqualValues(expectedMap, actualMap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeToMap(data []byte) (map[interface{}]interface{}, error) {
|
||||
raw := map[interface{}]interface{}{}
|
||||
err := yaml.Unmarshal(data, &raw)
|
||||
|
||||
return raw, err
|
||||
}
|
||||
|
||||
func TestEncoderSuite(t *testing.T) {
|
||||
suite.Run(t, &EncoderSuite{})
|
||||
}
|
171
pkg/machinery/config/encoder/markdown.go
Normal file
171
pkg/machinery/config/encoder/markdown.go
Normal file
@ -0,0 +1,171 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var markdownTemplate = `{{ .Description }}
|
||||
{{- $anchors := .Anchors -}}
|
||||
{{- $tick := "` + "`" + `" -}}
|
||||
{{ range $struct := .Structs }}
|
||||
### {{ $struct.Type }}
|
||||
{{ if $struct.Description -}}
|
||||
{{ $struct.Description }}
|
||||
{{ end -}}
|
||||
{{ if $struct.Examples -}}
|
||||
|
||||
Examples:
|
||||
{{ range $example := $struct.Examples }}
|
||||
{{ yaml $example.Value "" }}
|
||||
{{ end -}}
|
||||
{{ end }}
|
||||
{{ if $struct.Fields -}}
|
||||
{{ range $field := $struct.Fields -}}
|
||||
|
||||
#### {{ $field.Name }}
|
||||
|
||||
Type: <code>{{ encodeType $field.Type }}</code>
|
||||
|
||||
{{ $field.Description }}
|
||||
{{ if $field.Values }}
|
||||
Valid Values:
|
||||
|
||||
{{ range $value := $field.Values -}}
|
||||
- {{ $tick }}{{ $value }}{{ $tick }}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ if $field.Examples }}
|
||||
Examples:
|
||||
{{ range $example := $field.Examples }}
|
||||
{{ yaml $example.Value $field.Name }}
|
||||
{{ end -}}
|
||||
{{ end }}
|
||||
{{ if $field.Note -}}
|
||||
> {{ $field.Note }}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ if $struct.Values -}}
|
||||
|
||||
{{ $struct.Type }} Valid Values:
|
||||
|
||||
{{ range $value := $struct.Values -}}
|
||||
- {{ $tick }}{{ $value }}{{ $tick }}
|
||||
{{ end -}}
|
||||
{{- end -}}
|
||||
---
|
||||
{{ end -}}`
|
||||
|
||||
// FileDoc represents a single go file documentation.
|
||||
type FileDoc struct {
|
||||
// Name will be used in md file name pattern.
|
||||
Name string
|
||||
// Description file description, supports markdown.
|
||||
Description string
|
||||
// Structs structs defined in the file.
|
||||
Structs []*Doc
|
||||
Anchors map[string]string
|
||||
}
|
||||
|
||||
// Encode encodes file documentation as MD file.
|
||||
func (fd *FileDoc) Encode() ([]byte, error) {
|
||||
anchors := map[string]string{}
|
||||
for _, t := range fd.Structs {
|
||||
anchors[t.Type] = strings.ToLower(t.Type)
|
||||
}
|
||||
|
||||
fd.Anchors = anchors
|
||||
|
||||
t := template.Must(template.New("file_markdown.tpl").
|
||||
Funcs(template.FuncMap{
|
||||
"yaml": encodeYaml,
|
||||
"encodeType": fd.encodeType,
|
||||
}).
|
||||
Parse(markdownTemplate))
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
if err := t.Execute(&buf, fd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// Write dumps documentation string to folder.
|
||||
func (fd *FileDoc) Write(path string) error {
|
||||
data, err := fd.Encode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if stat, e := os.Stat(path); !os.IsNotExist(e) {
|
||||
if !stat.IsDir() {
|
||||
return fmt.Errorf("destination path should be a directory")
|
||||
}
|
||||
} else {
|
||||
if e := os.MkdirAll(path, 0o777); e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
f, err := os.Create(filepath.Join(path, fmt.Sprintf("%s.%s", fd.Name, "md")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := f.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fd *FileDoc) encodeType(t string) string {
|
||||
re := regexp.MustCompile(`\w+`)
|
||||
|
||||
for _, s := range re.FindAllString(t, -1) {
|
||||
if anchor, ok := fd.Anchors[s]; ok {
|
||||
t = strings.ReplaceAll(t, s, fmt.Sprintf("[%s](#%s)", s, anchor))
|
||||
}
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func encodeYaml(in interface{}, name string) string {
|
||||
if name != "" {
|
||||
in = map[string]interface{}{
|
||||
name: in,
|
||||
}
|
||||
}
|
||||
|
||||
node, err := toYamlNode(in)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("yaml encoding failed %s", err)
|
||||
}
|
||||
|
||||
data, err := yaml.Marshal(node)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("yaml encoding failed %s", err)
|
||||
}
|
||||
|
||||
lines := strings.Split(string(data), "\n")
|
||||
for i, line := range lines {
|
||||
lines[i] = strings.TrimRight(line, " ")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("```yaml\n%s```", strings.Join(lines, "\n"))
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
/*
|
||||
Package v1alpha1 configuration file contains all the options available for configuring a machine.
|
||||
|
||||
We can generate the files using `talosctl`.
|
||||
This configuration is enough to get started in most cases, however it can be customized as needed.
|
||||
|
||||
```bash
|
||||
talosctl config generate --version v1alpha1 <cluster name> <cluster endpoint>
|
||||
````
|
||||
|
||||
This will generate a machine config for each node type, and a talosconfig.
|
||||
The following is an example of an `init.yaml`:
|
||||
|
||||
```yaml
|
||||
version: v1alpha1
|
||||
machine:
|
||||
type: init
|
||||
token: 5dt69c.npg6duv71zwqhzbg
|
||||
ca:
|
||||
crt: <base64 encoded Ed25519 certificate>
|
||||
key: <base64 encoded Ed25519 key>
|
||||
certSANs: []
|
||||
kubelet: {}
|
||||
network: {}
|
||||
install:
|
||||
disk: /dev/sda
|
||||
image: ghcr.io/talos-systems/installer:latest
|
||||
bootloader: true
|
||||
wipe: false
|
||||
force: false
|
||||
cluster:
|
||||
controlPlane:
|
||||
endpoint: https://1.2.3.4
|
||||
clusterName: example
|
||||
network:
|
||||
cni: ""
|
||||
dnsDomain: cluster.local
|
||||
podSubnets:
|
||||
- 10.244.0.0/16
|
||||
serviceSubnets:
|
||||
- 10.96.0.0/12
|
||||
token: wlzjyw.bei2zfylhs2by0wd
|
||||
certificateKey: 20d9aafb46d6db4c0958db5b3fc481c8c14fc9b1abd8ac43194f4246b77131be
|
||||
aescbcEncryptionSecret: z01mye6j16bspJYtTB/5SFX8j7Ph4JXxM2Xuu4vsBPM=
|
||||
ca:
|
||||
crt: <base64 encoded RSA certificate>
|
||||
key: <base64 encoded RSA key>
|
||||
apiServer: {}
|
||||
controllerManager: {}
|
||||
scheduler: {}
|
||||
etcd:
|
||||
ca:
|
||||
crt: <base64 encoded RSA certificate>
|
||||
key: <base64 encoded RSA key>
|
||||
```
|
||||
*/
|
||||
package v1alpha1
|
@ -14,8 +14,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
|
||||
"github.com/talos-systems/bootkube-plugin/pkg/asset"
|
||||
@ -23,6 +21,7 @@ import (
|
||||
"github.com/talos-systems/crypto/x509"
|
||||
|
||||
"github.com/talos-systems/talos/pkg/machinery/config"
|
||||
"github.com/talos-systems/talos/pkg/machinery/config/encoder"
|
||||
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1/machine"
|
||||
"github.com/talos-systems/talos/pkg/machinery/constants"
|
||||
)
|
||||
@ -68,13 +67,10 @@ func (c *Config) String() (string, error) {
|
||||
}
|
||||
|
||||
// Bytes implements the config.Provider interface.
|
||||
func (c *Config) Bytes() ([]byte, error) {
|
||||
b, err := yaml.Marshal(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func (c *Config) Bytes() (res []byte, err error) {
|
||||
res, err = encoder.NewEncoder(c).Encode()
|
||||
|
||||
return b, nil
|
||||
return
|
||||
}
|
||||
|
||||
// Install implements the config.Provider interface.
|
||||
|
@ -2,9 +2,65 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
/*
|
||||
Package v1alpha1 configuration file contains all the options available for configuring a machine.
|
||||
|
||||
We can generate the files using `talosctl`.
|
||||
This configuration is enough to get started in most cases, however it can be customized as needed.
|
||||
|
||||
```bash
|
||||
talosctl config generate --version v1alpha1 <cluster name> <cluster endpoint>
|
||||
````
|
||||
|
||||
This will generate a machine config for each node type, and a talosconfig.
|
||||
The following is an example of an `init.yaml`:
|
||||
|
||||
```yaml
|
||||
version: v1alpha1
|
||||
machine:
|
||||
type: init
|
||||
token: 5dt69c.npg6duv71zwqhzbg
|
||||
ca:
|
||||
crt: <base64 encoded Ed25519 certificate>
|
||||
key: <base64 encoded Ed25519 key>
|
||||
certSANs: []
|
||||
kubelet: {}
|
||||
network: {}
|
||||
install:
|
||||
disk: /dev/sda
|
||||
image: ghcr.io/talos-systems/installer:latest
|
||||
bootloader: true
|
||||
wipe: false
|
||||
force: false
|
||||
cluster:
|
||||
controlPlane:
|
||||
endpoint: https://1.2.3.4
|
||||
clusterName: example
|
||||
network:
|
||||
cni: ""
|
||||
dnsDomain: cluster.local
|
||||
podSubnets:
|
||||
- 10.244.0.0/16
|
||||
serviceSubnets:
|
||||
- 10.96.0.0/12
|
||||
token: wlzjyw.bei2zfylhs2by0wd
|
||||
certificateKey: 20d9aafb46d6db4c0958db5b3fc481c8c14fc9b1abd8ac43194f4246b77131be
|
||||
aescbcEncryptionSecret: z01mye6j16bspJYtTB/5SFX8j7Ph4JXxM2Xuu4vsBPM=
|
||||
ca:
|
||||
crt: <base64 encoded RSA certificate>
|
||||
key: <base64 encoded RSA key>
|
||||
apiServer: {}
|
||||
controllerManager: {}
|
||||
scheduler: {}
|
||||
etcd:
|
||||
ca:
|
||||
crt: <base64 encoded RSA certificate>
|
||||
key: <base64 encoded RSA key>
|
||||
```
|
||||
*/
|
||||
package v1alpha1
|
||||
|
||||
//go:generate docgen . /tmp/v1alpha1.md
|
||||
//go:generate docgen ./v1alpha1_types.go ./v1alpha1_types_doc.go v1alpha1
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
@ -25,6 +81,209 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
// Examples section.
|
||||
|
||||
machineConfigRegistriesExample = &RegistriesConfig{
|
||||
RegistryMirrors: map[string]*RegistryMirrorConfig{
|
||||
"docker.io": {
|
||||
MirrorEndpoints: []string{"https://registry-1.docker.io"},
|
||||
},
|
||||
},
|
||||
RegistryConfig: map[string]*RegistryConfig{
|
||||
"some.host:123": {
|
||||
RegistryTLS: &RegistryTLSConfig{
|
||||
TLSClientIdentity: &x509.PEMEncodedCertificateAndKey{
|
||||
Crt: []byte("..."),
|
||||
Key: []byte("..."),
|
||||
},
|
||||
},
|
||||
RegistryAuth: &RegistryAuthConfig{
|
||||
RegistryUsername: "...",
|
||||
RegistryPassword: "...",
|
||||
RegistryAuth: "...",
|
||||
RegistryIdentityToken: "...",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
pemEncodedCertificateExample *x509.PEMEncodedCertificateAndKey = &x509.PEMEncodedCertificateAndKey{
|
||||
Crt: []byte("LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJIekNCMHF..."),
|
||||
Key: []byte("LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM..."),
|
||||
}
|
||||
|
||||
machineKubeletExample = &KubeletConfig{
|
||||
KubeletImage: "docker.io/autonomy/kubelet:v1.19.3",
|
||||
KubeletExtraArgs: map[string]string{
|
||||
"key": "value",
|
||||
},
|
||||
}
|
||||
|
||||
machineNetworkConfigExample = &NetworkConfig{
|
||||
NetworkHostname: "worker-1",
|
||||
NetworkInterfaces: []*Device{
|
||||
{},
|
||||
},
|
||||
NameServers: []string{"9.8.7.6", "8.7.6.5"},
|
||||
}
|
||||
|
||||
machineDisksExample []*MachineDisk = []*MachineDisk{
|
||||
{
|
||||
DeviceName: "/dev/sdb",
|
||||
DiskPartitions: []*DiskPartition{
|
||||
{
|
||||
DiskMountPoint: "//lib/extra",
|
||||
DiskSize: 100000000,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
machineInstallExample = &InstallConfig{
|
||||
InstallDisk: "/dev/sda",
|
||||
InstallExtraKernelArgs: []string{"option=value"},
|
||||
InstallImage: "ghcr.io/talos-systems/installer:latest",
|
||||
InstallBootloader: true,
|
||||
InstallWipe: false,
|
||||
}
|
||||
|
||||
machineFilesExample = []*MachineFile{
|
||||
{
|
||||
FileContent: "...",
|
||||
FilePermissions: 0o666,
|
||||
FilePath: "/tmp/file.txt",
|
||||
FileOp: "append",
|
||||
},
|
||||
}
|
||||
|
||||
machineEnvExamples = []Env{
|
||||
{
|
||||
"GRPC_GO_LOG_VERBOSITY_LEVEL": "99",
|
||||
"GRPC_GO_LOG_SEVERITY_LEVEL": "info",
|
||||
"https_proxy": "http://SERVER:PORT/",
|
||||
},
|
||||
{
|
||||
"GRPC_GO_LOG_SEVERITY_LEVEL": "error",
|
||||
"https_proxy": "https://USERNAME:PASSWORD@SERVER:PORT/",
|
||||
},
|
||||
{
|
||||
"https_proxy": "http://DOMAIN\\USERNAME:PASSWORD@SERVER:PORT/",
|
||||
},
|
||||
}
|
||||
|
||||
machineTimeExample = &TimeConfig{
|
||||
TimeServers: []string{"time.cloudflare.com"},
|
||||
}
|
||||
|
||||
machineSysctlsExample map[string]string = map[string]string{
|
||||
"kernel.domainname": "talos.dev",
|
||||
"net.ipv4.ip_forward": "0",
|
||||
}
|
||||
|
||||
clusterControlPlaneExample = &ControlPlaneConfig{
|
||||
Endpoint: &Endpoint{
|
||||
&url.URL{
|
||||
Host: "1.2.3.4",
|
||||
Scheme: "https",
|
||||
},
|
||||
},
|
||||
LocalAPIServerPort: 443,
|
||||
}
|
||||
|
||||
clusterNetworkExample = &ClusterNetworkConfig{
|
||||
CNI: &CNIConfig{
|
||||
CNIName: "flannel",
|
||||
},
|
||||
DNSDomain: "cluster.local",
|
||||
PodSubnet: []string{"10.244.0.0/16"},
|
||||
ServiceSubnet: []string{"10.96.0.0/12"},
|
||||
}
|
||||
|
||||
clusterAPIServerExample = &APIServerConfig{
|
||||
ContainerImage: "...", // TODO: actual image name
|
||||
ExtraArgsConfig: map[string]string{
|
||||
"key": "value", // TODO: add more real live examples
|
||||
},
|
||||
CertSANs: []string{
|
||||
"1.2.3.4",
|
||||
"4.5.6.7",
|
||||
},
|
||||
}
|
||||
|
||||
clusterControllerManagerExample = &ControllerManagerConfig{
|
||||
ContainerImage: "...", // TODO: actual image name
|
||||
ExtraArgsConfig: map[string]string{
|
||||
"key": "value", // TODO: add more real live examples
|
||||
},
|
||||
}
|
||||
|
||||
clusterProxyExample = &ProxyConfig{
|
||||
ContainerImage: "...", // TODO: actual image name
|
||||
ExtraArgsConfig: map[string]string{
|
||||
"key": "value", // TODO: add more real live examples
|
||||
},
|
||||
ModeConfig: "ipvs",
|
||||
}
|
||||
|
||||
clusterSchedulerConfig = &SchedulerConfig{
|
||||
ContainerImage: "...", // TODO: actual image name
|
||||
ExtraArgsConfig: map[string]string{
|
||||
"key": "value", // TODO: add more real live examples
|
||||
},
|
||||
}
|
||||
|
||||
clusterEtcdConfig = &EtcdConfig{
|
||||
ContainerImage: "...", // TODO: actual image name
|
||||
EtcdExtraArgs: map[string]string{
|
||||
"key": "value", // TODO: add more real live examples
|
||||
},
|
||||
RootCA: pemEncodedCertificateExample,
|
||||
}
|
||||
|
||||
clusterPodCheckpointerExample = &PodCheckpointer{
|
||||
PodCheckpointerImage: "...", // TODO: actual image name
|
||||
}
|
||||
|
||||
clusterCoreDNSExample = &CoreDNS{
|
||||
CoreDNSImage: "...", // TODO: actual image name
|
||||
}
|
||||
|
||||
clusterAdminKubeconfigExample = AdminKubeconfigConfig{
|
||||
AdminKubeconfigCertLifetime: time.Hour,
|
||||
}
|
||||
|
||||
kubeletExtraMountsExample = []specs.Mount{
|
||||
{
|
||||
Source: "/var/lib/example",
|
||||
Destination: "/var/lib/example",
|
||||
Type: "bind",
|
||||
Options: []string{
|
||||
"rshared",
|
||||
"ro",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
networkConfigExtraHostsExample = []*ExtraHost{
|
||||
{
|
||||
HostIP: "192.168.1.100",
|
||||
HostAliases: []string{
|
||||
"test",
|
||||
"test.domain.tld",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
clusterCustomCNIExample = &CNIConfig{
|
||||
CNIName: "custom",
|
||||
CNIUrls: []string{
|
||||
"https://www.mysweethttpserver.com/supersecretcni.yaml",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// Config defines the v1alpha1 configuration file.
|
||||
type Config struct {
|
||||
// description: |
|
||||
@ -85,46 +344,34 @@ type MachineConfig struct {
|
||||
// The `token` is used by a machine to join the PKI of the cluster.
|
||||
// Using this token, a machine will create a certificate signing request (CSR), and request a certificate that will be used as its' identity.
|
||||
// examples:
|
||||
// - "token: 328hom.uqjzh6jnn2eie9oi"
|
||||
// - name: example token
|
||||
// value: "\"328hom.uqjzh6jnn2eie9oi\""
|
||||
MachineToken string `yaml:"token"` // Warning: It is important to ensure that this token is correct since a machine's certificate has a short TTL by default
|
||||
// description: |
|
||||
// The root certificate authority of the PKI.
|
||||
// It is composed of a base64 encoded `crt` and `key`.
|
||||
// examples:
|
||||
// - |
|
||||
// ca:
|
||||
// crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJIekNCMHF...
|
||||
// key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM...
|
||||
// - value: pemEncodedCertificateExample
|
||||
// name: machine CA example
|
||||
MachineCA *x509.PEMEncodedCertificateAndKey `yaml:"ca,omitempty"`
|
||||
// description: |
|
||||
// Extra certificate subject alternative names for the machine's certificate.
|
||||
// By default, all non-loopback interface IPs are automatically added to the certificate's SANs.
|
||||
// examples:
|
||||
// - |
|
||||
// certSANs:
|
||||
// - 10.0.0.10
|
||||
// - 172.16.0.10
|
||||
// - 192.168.0.10
|
||||
// - name: Uncomment this to enable SANs.
|
||||
// value: '[]string{"10.0.0.10", "172.16.0.10", "192.168.0.10"}'
|
||||
MachineCertSANs []string `yaml:"certSANs"`
|
||||
// description: |
|
||||
// Used to provide additional options to the kubelet.
|
||||
// examples:
|
||||
// - |
|
||||
// kubelet:
|
||||
// image:
|
||||
// extraArgs:
|
||||
// key: value
|
||||
// - name: Kubelet definition example.
|
||||
// value: machineKubeletExample
|
||||
MachineKubelet *KubeletConfig `yaml:"kubelet,omitempty"`
|
||||
// description: |
|
||||
// Used to configure the machine's network.
|
||||
// examples:
|
||||
// - |
|
||||
// network:
|
||||
// hostname: worker-1
|
||||
// interfaces:
|
||||
// nameservers:
|
||||
// - 9.8.7.6
|
||||
// - 8.7.6.5
|
||||
// - name: Network definition example.
|
||||
// value: machineNetworkConfigExample
|
||||
MachineNetwork *NetworkConfig `yaml:"network,omitempty"`
|
||||
// description: |
|
||||
// Used to partition, format and mount additional disks.
|
||||
@ -132,26 +379,14 @@ type MachineConfig struct {
|
||||
// Note that the partitioning and formating is done only once, if and only if no existing partitions are found.
|
||||
// If `size:` is omitted, the partition is sized to occupy full disk.
|
||||
// examples:
|
||||
// - |
|
||||
// disks:
|
||||
// - device: /dev/sdb
|
||||
// partitions:
|
||||
// - mountpoint: /var/lib/extra
|
||||
// size: 10000000000
|
||||
//
|
||||
// - name: MachineDisks list example.
|
||||
// value: machineDisksExample
|
||||
MachineDisks []*MachineDisk `yaml:"disks,omitempty"` // Note: `size` is in units of bytes.
|
||||
// description: |
|
||||
// Used to provide instructions for bare-metal installations.
|
||||
// examples:
|
||||
// - |
|
||||
// install:
|
||||
// disk: /dev/sda
|
||||
// extraKernelArgs:
|
||||
// - option=value
|
||||
// image: ghcr.io/talos-systems/installer:latest
|
||||
// bootloader: true
|
||||
// wipe: false
|
||||
// force: false
|
||||
// - name: MachineInstall config usage example.
|
||||
// value: machineInstallExample
|
||||
MachineInstall *InstallConfig `yaml:"install,omitempty"`
|
||||
// description: |
|
||||
// Allows the addition of user specified files.
|
||||
@ -161,13 +396,8 @@ type MachineConfig struct {
|
||||
// If an `op` value of `append` is used, the existing file will be appended.
|
||||
// Note that the file contents are not required to be base64 encoded.
|
||||
// examples:
|
||||
// - |
|
||||
// files:
|
||||
// - content: |
|
||||
// ...
|
||||
// permissions: 0666
|
||||
// path: /tmp/file.txt
|
||||
// op: append
|
||||
// - name: MachineFiles usage example.
|
||||
// value: machineFilesExample
|
||||
MachineFiles []*MachineFile `yaml:"files,omitempty"` // Note: The specified `path` is relative to `/var`.
|
||||
// description: |
|
||||
// The `env` field allows for the addition of environment variables to a machine.
|
||||
@ -179,34 +409,22 @@ type MachineConfig struct {
|
||||
// - "`https_proxy`"
|
||||
// - "`no_proxy`"
|
||||
// examples:
|
||||
// - |
|
||||
// env:
|
||||
// GRPC_GO_LOG_VERBOSITY_LEVEL: "99"
|
||||
// GRPC_GO_LOG_SEVERITY_LEVEL: info
|
||||
// https_proxy: http://SERVER:PORT/
|
||||
// - |
|
||||
// env:
|
||||
// GRPC_GO_LOG_SEVERITY_LEVEL: error
|
||||
// https_proxy: https://USERNAME:PASSWORD@SERVER:PORT/
|
||||
// - |
|
||||
// env:
|
||||
// https_proxy: http://DOMAIN\\USERNAME:PASSWORD@SERVER:PORT/
|
||||
// - name: Environment variables definition examples.
|
||||
// value: machineEnvExamples[0]
|
||||
// - value: machineEnvExamples[1]
|
||||
// - value: machineEnvExamples[2]
|
||||
MachineEnv Env `yaml:"env,omitempty"`
|
||||
// description: |
|
||||
// Used to configure the machine's time settings.
|
||||
// examples:
|
||||
// - |
|
||||
// time:
|
||||
// servers:
|
||||
// - time.cloudflare.com
|
||||
// - name: Example configuration for cloudflare ntp server.
|
||||
// value: machineTimeExample
|
||||
MachineTime *TimeConfig `yaml:"time,omitempty"`
|
||||
// description: |
|
||||
// Used to configure the machine's sysctls.
|
||||
// examples:
|
||||
// - |
|
||||
// sysctls:
|
||||
// kernel.domainname: talos.dev
|
||||
// net.ipv4.ip_forward: "0"
|
||||
// - name: MachineSysctls usage example.
|
||||
// value: machineSysctlsExample
|
||||
MachineSysctls map[string]string `yaml:"sysctls,omitempty"`
|
||||
// description: |
|
||||
// Used to configure the machine's container image registry mirrors.
|
||||
@ -222,27 +440,7 @@ type MachineConfig struct {
|
||||
//
|
||||
// See also matching configuration for [CRI containerd plugin](https://github.com/containerd/cri/blob/master/docs/registry.md).
|
||||
// examples:
|
||||
// - |
|
||||
// registries:
|
||||
// mirrors:
|
||||
// docker.io:
|
||||
// endpoints:
|
||||
// - https://registry-1.docker.io
|
||||
// '*':
|
||||
// endpoints:
|
||||
// - http://some.host:123/
|
||||
// config:
|
||||
// "some.host:123":
|
||||
// tls:
|
||||
// ca: ... # base64-encoded CA certificate in PEM format
|
||||
// clientIdentity:
|
||||
// cert: ... # base64-encoded client certificate in PEM format
|
||||
// key: ... # base64-encoded client key in PEM format
|
||||
// auth:
|
||||
// username: ...
|
||||
// password: ...
|
||||
// auth: ...
|
||||
// identityToken: ...
|
||||
// - value: machineConfigRegistriesExample
|
||||
MachineRegistries RegistriesConfig `yaml:"registries,omitempty"`
|
||||
}
|
||||
|
||||
@ -251,10 +449,8 @@ type ClusterConfig struct {
|
||||
// description: |
|
||||
// Provides control plane specific configuration options.
|
||||
// examples:
|
||||
// - |
|
||||
// controlPlane:
|
||||
// endpoint: https://1.2.3.4
|
||||
// localAPIServerPort: 443
|
||||
// - name: Setting controlplain endpoint address to 1.2.3.4 and port to 443 example.
|
||||
// value: clusterControlPlaneExample
|
||||
ControlPlane *ControlPlaneConfig `yaml:"controlPlane"`
|
||||
// description: |
|
||||
// Configures the cluster's name.
|
||||
@ -262,121 +458,86 @@ type ClusterConfig struct {
|
||||
// description: |
|
||||
// Provides cluster network configuration.
|
||||
// examples:
|
||||
// - |
|
||||
// network:
|
||||
// cni:
|
||||
// name: flannel
|
||||
// dnsDomain: cluster.local
|
||||
// podSubnets:
|
||||
// - 10.244.0.0/16
|
||||
// serviceSubnets:
|
||||
// - 10.96.0.0/12
|
||||
// - name: Configuring with flannel cni and setting up subnets.
|
||||
// value: clusterNetworkExample
|
||||
ClusterNetwork *ClusterNetworkConfig `yaml:"network,omitempty"`
|
||||
// description: |
|
||||
// The [bootstrap token](https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/).
|
||||
// examples:
|
||||
// - wlzjyw.bei2zfylhs2by0wd
|
||||
// - name: Bootstrap token example (do not use in production!).
|
||||
// value: '"wlzjyw.bei2zfylhs2by0wd"'
|
||||
BootstrapToken string `yaml:"token,omitempty"`
|
||||
// description: |
|
||||
// The key used for the [encryption of secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/).
|
||||
// examples:
|
||||
// - z01mye6j16bspJYtTB/5SFX8j7Ph4JXxM2Xuu4vsBPM=
|
||||
// - name: Decryption secret example (do not use in production!).
|
||||
// value: '"z01mye6j16bspJYtTB/5SFX8j7Ph4JXxM2Xuu4vsBPM="'
|
||||
ClusterAESCBCEncryptionSecret string `yaml:"aescbcEncryptionSecret"`
|
||||
// description: |
|
||||
// The base64 encoded root certificate authority used by Kubernetes.
|
||||
// examples:
|
||||
// - |
|
||||
// ca:
|
||||
// crt: LS0tLS1CRUdJTiBDRV...
|
||||
// key: LS0tLS1CRUdJTiBSU0...
|
||||
// - name: ClusterCA example.
|
||||
// value: pemEncodedCertificateExample
|
||||
ClusterCA *x509.PEMEncodedCertificateAndKey `yaml:"ca,omitempty"`
|
||||
// description: |
|
||||
// API server specific configuration options.
|
||||
// examples:
|
||||
// - |
|
||||
// apiServer:
|
||||
// image: ...
|
||||
// extraArgs:
|
||||
// key: value
|
||||
// certSANs:
|
||||
// - 1.2.3.4
|
||||
// - 5.6.7.8
|
||||
// - value: clusterAPIServerExample
|
||||
APIServerConfig *APIServerConfig `yaml:"apiServer,omitempty"`
|
||||
// description: |
|
||||
// Controller manager server specific configuration options.
|
||||
// examples:
|
||||
// - |
|
||||
// controllerManager:
|
||||
// image: ...
|
||||
// extraArgs:
|
||||
// key: value
|
||||
// - value: clusterControllerManagerExample
|
||||
ControllerManagerConfig *ControllerManagerConfig `yaml:"controllerManager,omitempty"`
|
||||
// description: |
|
||||
// Kube-proxy server-specific configuration options
|
||||
// examples:
|
||||
// - |
|
||||
// proxy:
|
||||
// mode: ipvs
|
||||
// extraArgs:
|
||||
// key: value
|
||||
// - value: clusterProxyExample
|
||||
ProxyConfig *ProxyConfig `yaml:"proxy,omitempty"`
|
||||
// description: |
|
||||
// Scheduler server specific configuration options.
|
||||
// examples:
|
||||
// - |
|
||||
// scheduler:
|
||||
// image: ...
|
||||
// extraArgs:
|
||||
// key: value
|
||||
// - value: clusterSchedulerConfig
|
||||
SchedulerConfig *SchedulerConfig `yaml:"scheduler,omitempty"`
|
||||
// description: |
|
||||
// Etcd specific configuration options.
|
||||
// examples:
|
||||
// - |
|
||||
// etcd:
|
||||
// ca:
|
||||
// crt: LS0tLS1CRUdJTiBDRV...
|
||||
// key: LS0tLS1CRUdJTiBSU0...
|
||||
// image: ...
|
||||
// - value: clusterEtcdConfig
|
||||
EtcdConfig *EtcdConfig `yaml:"etcd,omitempty"`
|
||||
// description: |
|
||||
// Pod Checkpointer specific configuration options.
|
||||
// examples:
|
||||
// - |
|
||||
// podCheckpointer:
|
||||
// image: ...
|
||||
// - value: clusterPodCheckpointerExample
|
||||
PodCheckpointerConfig *PodCheckpointer `yaml:"podCheckpointer,omitempty"`
|
||||
// description: |
|
||||
// Core DNS specific configuration options.
|
||||
// examples:
|
||||
// - |
|
||||
// coreDNS:
|
||||
// image: ...
|
||||
// - value: clusterCoreDNSExample
|
||||
CoreDNSConfig *CoreDNS `yaml:"coreDNS,omitempty"`
|
||||
// description: |
|
||||
// A list of urls that point to additional manifests.
|
||||
// These will get automatically deployed by bootkube.
|
||||
// examples:
|
||||
// - |
|
||||
// extraManifests:
|
||||
// - "https://www.mysweethttpserver.com/manifest1.yaml"
|
||||
// - "https://www.mysweethttpserver.com/manifest2.yaml"
|
||||
// - value: >
|
||||
// []string{
|
||||
// "https://www.mysweethttpserver.com/manifest1.yaml",
|
||||
// "https://www.mysweethttpserver.com/manifest2.yaml",
|
||||
// }
|
||||
ExtraManifests []string `yaml:"extraManifests,omitempty"`
|
||||
// description: |
|
||||
// A map of key value pairs that will be added while fetching the ExtraManifests.
|
||||
// examples:
|
||||
// - |
|
||||
// extraManifestHeaders:
|
||||
// Token: "1234567"
|
||||
// X-ExtraInfo: info
|
||||
// - value: >
|
||||
// map[string]string{
|
||||
// "Token": "1234567",
|
||||
// "X-ExtraInfo": "info",
|
||||
// }
|
||||
ExtraManifestHeaders map[string]string `yaml:"extraManifestHeaders,omitempty"`
|
||||
// description: |
|
||||
// Settings for admin kubeconfig generation.
|
||||
// Certificate lifetime can be configured.
|
||||
// examples:
|
||||
// - |
|
||||
// adminKubeconfig:
|
||||
// certLifetime: 1h
|
||||
// - value: clusterAdminKubeconfigExample
|
||||
AdminKubeconfigConfig AdminKubeconfigConfig `yaml:"adminKubeconfig,omitempty"`
|
||||
// description: |
|
||||
// Indicates if master nodes are schedulable.
|
||||
@ -393,26 +554,20 @@ type KubeletConfig struct {
|
||||
// description: |
|
||||
// The `image` field is an optional reference to an alternative kubelet image.
|
||||
// examples:
|
||||
// - "image: docker.io/<org>/kubelet:latest"
|
||||
// - value: '"docker.io/<org>/kubelet:latest"'
|
||||
KubeletImage string `yaml:"image,omitempty"`
|
||||
// description: |
|
||||
// The `extraArgs` field is used to provide additional flags to the kubelet.
|
||||
// examples:
|
||||
// - |
|
||||
// extraArgs:
|
||||
// key: value
|
||||
// - value: >
|
||||
// map[string]string{
|
||||
// "key": "value",
|
||||
// }
|
||||
KubeletExtraArgs map[string]string `yaml:"extraArgs,omitempty"`
|
||||
// description: |
|
||||
// The `extraMounts` field is used to add additional mounts to the kubelet container.
|
||||
// examples:
|
||||
// - |
|
||||
// extraMounts:
|
||||
// - source: /var/lib/example
|
||||
// destination: /var/lib/example
|
||||
// type: bind
|
||||
// options:
|
||||
// - rshared
|
||||
// - ro
|
||||
// - value: kubeletExtraMountsExample
|
||||
KubeletExtraMounts []specs.Mount `yaml:"extraMounts,omitempty"`
|
||||
}
|
||||
|
||||
@ -478,12 +633,7 @@ type NetworkConfig struct {
|
||||
// description: |
|
||||
// Allows for extra entries to be added to /etc/hosts file
|
||||
// examples:
|
||||
// - |
|
||||
// extraHostEntries:
|
||||
// - ip: 192.168.1.100
|
||||
// aliases:
|
||||
// - test
|
||||
// - test.domain.tld
|
||||
// - value: networkConfigExtraHostsExample
|
||||
ExtraHostEntries []*ExtraHost `yaml:"extraHostEntries,omitempty"`
|
||||
}
|
||||
|
||||
@ -492,21 +642,18 @@ type InstallConfig struct {
|
||||
// description: |
|
||||
// The disk used to install the bootloader, and ephemeral partitions.
|
||||
// examples:
|
||||
// - /dev/sda
|
||||
// - /dev/nvme0
|
||||
// - value: '"/dev/sda"'
|
||||
// - value: '"/dev/nvme0"'
|
||||
InstallDisk string `yaml:"disk,omitempty"`
|
||||
// description: |
|
||||
// Allows for supplying extra kernel args to the bootloader config.
|
||||
// examples:
|
||||
// - |
|
||||
// extraKernelArgs:
|
||||
// - a=b
|
||||
// - value: '[]string{"a=b"}'
|
||||
InstallExtraKernelArgs []string `yaml:"extraKernelArgs,omitempty"`
|
||||
// description: |
|
||||
// Allows for supplying the image used to perform the installation.
|
||||
// examples:
|
||||
// - |
|
||||
// image: docker.io/<org>/installer:latest
|
||||
// - value: '"docker.io/<org>/installer:latest"'
|
||||
InstallImage string `yaml:"image,omitempty"`
|
||||
// description: |
|
||||
// Indicates if a bootloader should be installed.
|
||||
@ -609,7 +756,7 @@ type ControlPlaneConfig struct {
|
||||
// Endpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname.
|
||||
// It is single-valued, and may optionally include a port number.
|
||||
// examples:
|
||||
// - https://1.2.3.4:443
|
||||
// - value: '"https://1.2.3.4:443"'
|
||||
Endpoint *Endpoint `yaml:"endpoint"`
|
||||
// description: |
|
||||
// The port that the API server listens on internally.
|
||||
@ -674,10 +821,7 @@ type EtcdConfig struct {
|
||||
// The `ca` is the root certificate authority of the PKI.
|
||||
// It is composed of a base64 encoded `crt` and `key`.
|
||||
// examples:
|
||||
// - |
|
||||
// ca:
|
||||
// crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJIekNCMHF...
|
||||
// key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM...
|
||||
// - value: pemEncodedCertificateExample
|
||||
RootCA *x509.PEMEncodedCertificateAndKey `yaml:"ca"`
|
||||
// description: |
|
||||
// Extra arguments to supply to etcd.
|
||||
@ -696,10 +840,11 @@ type EtcdConfig struct {
|
||||
// - `peer-trusted-ca-file`
|
||||
// - `peer-key-file`
|
||||
// examples:
|
||||
// - |
|
||||
// extraArgs:
|
||||
// initial-cluster: https://1.2.3.4:2380
|
||||
// advertise-client-urls: https://1.2.3.4:2379
|
||||
// - values: >
|
||||
// map[string]string{
|
||||
// "initial-cluster": "https://1.2.3.4:2380",
|
||||
// "advertise-client-urls": "https://1.2.3.4:2379",
|
||||
// }
|
||||
EtcdExtraArgs map[string]string `yaml:"extraArgs,omitempty"`
|
||||
}
|
||||
|
||||
@ -713,31 +858,26 @@ type ClusterNetworkConfig struct {
|
||||
// URLs should point to a single yaml file that will get deployed.
|
||||
// Empty struct or any other name will default to bootkube's flannel.
|
||||
// examples:
|
||||
// - |
|
||||
// cni:
|
||||
// name: "custom"
|
||||
// urls:
|
||||
// - "https://www.mysweethttpserver.com/supersecretcni.yaml"
|
||||
// - value: clusterCustomCNIExample
|
||||
CNI *CNIConfig `yaml:"cni,omitempty"`
|
||||
// description: |
|
||||
// The domain used by Kubernetes DNS.
|
||||
// The default is `cluster.local`
|
||||
// examples:
|
||||
// - cluser.local
|
||||
// - value: '"cluser.local"'
|
||||
DNSDomain string `yaml:"dnsDomain"`
|
||||
// description: |
|
||||
// The pod subnet CIDR.
|
||||
// examples:
|
||||
// - |
|
||||
// podSubnets:
|
||||
// - 10.244.0.0/16
|
||||
// - value: >
|
||||
// []string{"10.244.0.0/16"}
|
||||
PodSubnet []string `yaml:"podSubnets"`
|
||||
// description: |
|
||||
// The service subnet CIDR.
|
||||
// examples:
|
||||
// - |
|
||||
// serviceSubnets:
|
||||
// - 10.96.0.0/12
|
||||
// examples:
|
||||
// - value: >
|
||||
// []string{"10.96.0.0/12"}
|
||||
ServiceSubnet []string `yaml:"serviceSubnets"`
|
||||
}
|
||||
|
||||
@ -1014,10 +1154,7 @@ type RegistryTLSConfig struct {
|
||||
// Enable mutual TLS authentication with the registry.
|
||||
// Client certificate and key should be base64-encoded.
|
||||
// examples:
|
||||
// - |
|
||||
// clientIdentity:
|
||||
// crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJIekNCMHF...
|
||||
// key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM...
|
||||
// - value: pemEncodedCertificateExample
|
||||
TLSClientIdentity *x509.PEMEncodedCertificateAndKey `yaml:"clientIdentity,omitempty"`
|
||||
// description: |
|
||||
// CA registry certificate to add the list of trusted certificates.
|
||||
|
1229
pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go
Normal file
1229
pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user