docs: rework machine config documentation generation

Generate a structured table of contents following the structure of the
config.

Make high-level examples follow the full structure of the config.

Document new multi-doc machine config.

Fixes #8023

Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
This commit is contained in:
Andrey Smirnov 2023-12-05 20:12:16 +04:00
parent e128d3c827
commit 46121c9fec
No known key found for this signature in database
GPG Key ID: FE042E3D4085A811
44 changed files with 3685 additions and 2345 deletions

View File

@ -969,7 +969,7 @@ ARG TARGETOS
ARG TARGETARCH
WORKDIR /src
COPY --from=talosctl-targetarch /talosctl-${TARGETOS}-${TARGETARCH} /bin/talosctl
RUN env HOME=/home/user TAG=latest /bin/talosctl docs --config /tmp \
RUN env HOME=/home/user TAG=latest /bin/talosctl docs --config /tmp/configuration \
&& env HOME=/home/user TAG=latest /bin/talosctl docs --cli /tmp
COPY ./pkg/machinery/config/types/v1alpha1/schemas/ /tmp/schemas/
@ -998,7 +998,7 @@ RUN protoc \
/protos/time/*.proto
FROM scratch AS docs
COPY --from=docs-build /tmp/configuration.md /website/content/v1.6/reference/
COPY --from=docs-build /tmp/configuration/ /website/content/v1.6/reference/configuration/
COPY --from=docs-build /tmp/cli.md /website/content/v1.6/reference/
COPY --from=docs-build /tmp/schemas /website/content/v1.6/schemas/
COPY --from=proto-docs-build /tmp/api.md /website/content/v1.6/reference/

View File

@ -16,19 +16,32 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/cobra/doc"
"sigs.k8s.io/kustomize/kyaml/yaml"
"github.com/siderolabs/talos/pkg/machinery/config/encoder"
"github.com/siderolabs/talos/pkg/machinery/config/types/network"
"github.com/siderolabs/talos/pkg/machinery/config/types/runtime"
"github.com/siderolabs/talos/pkg/machinery/config/types/siderolink"
v1alpha1 "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
)
func frontmatter(title, description string) string {
frontmatter := "---\n"
var buf bytes.Buffer
frontmatter += "title: " + title + "\n"
frontmatter += "desription: " + description + "\n"
buf.WriteString("---\n")
frontmatter += "---\n\n"
if err := yaml.NewEncoder(&buf).Encode(map[string]string{
"title": title,
"description": description,
}); err != nil {
panic(err)
}
return frontmatter + "<!-- markdownlint-disable -->\n\n"
buf.WriteString("---\n")
buf.WriteString("\n")
buf.WriteString("<!-- markdownlint-disable -->\n\n")
return buf.String()
}
func linkHandler(name string) string {
@ -39,10 +52,7 @@ func linkHandler(name string) string {
return "#" + strings.ToLower(base)
}
const (
cliDescription = "Talosctl CLI tool reference."
configurationDescription = "Talos node configuration file reference."
)
const cliDescription = "Talosctl CLI tool reference."
var (
cliDocs bool
@ -90,8 +100,36 @@ var docsCmd = &cobra.Command{
}
if configDocs || all {
if err := v1alpha1.GetConfigurationDoc().Write(dir, frontmatter("Configuration", configurationDescription)); err != nil {
return fmt.Errorf("failed to generate docs: %w", err)
for _, pkg := range []struct {
name string
fileDoc *encoder.FileDoc
}{
{
name: "network",
fileDoc: network.GetFileDoc(),
},
{
name: "runtime",
fileDoc: runtime.GetFileDoc(),
},
{
name: "siderolink",
fileDoc: siderolink.GetFileDoc(),
},
{
name: "v1alpha1",
fileDoc: v1alpha1.GetFileDoc(),
},
} {
path := filepath.Join(dir, pkg.name)
if err := os.MkdirAll(path, 0o777); err != nil {
return fmt.Errorf("failed to create output directory %q", path)
}
if err := pkg.fileDoc.Write(path, frontmatter); err != nil {
return fmt.Errorf("failed to generate docs: %w", err)
}
}
}

View File

@ -24,7 +24,7 @@ func ingressRuleWithinCluster(cidrs []netip.Prefix, gateways []netip.Addr) []net
rules = append(rules,
network.IngressRule{
Subnet: cidrs[i],
Except: netip.PrefixFrom(gateways[i], gateways[i].BitLen()),
Except: network.Prefix{Prefix: netip.PrefixFrom(gateways[i], gateways[i].BitLen())},
},
)
}

View File

@ -12,6 +12,7 @@ import (
"go/parser"
"go/token"
"log"
"maps"
"os"
"path/filepath"
"reflect"
@ -39,7 +40,7 @@ import (
{{ range $struct := .Structs -}}
func ({{ $struct.Name }}) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type : "{{ $struct.Name }}",
Type : "{{ if $struct.Text.Alias }}{{ $struct.Text.Alias}}{{ else }}{{ $struct.Name }}{{ end }}",
Comments: [3]string{ "" /* encoder.HeadComment */, "{{ $struct.Text.Comment }}" /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "{{ $struct.Text.Description }}",
{{ if $struct.AppearsIn -}}
@ -100,8 +101,8 @@ func ({{ $struct.Name }}) Doc() *encoder.Doc {
}
{{ end -}}
// Get{{ .Name }}Doc returns documentation for the file {{ .File }}.
func Get{{ .Name }}Doc() *encoder.FileDoc {
// GetFileDoc returns documentation for the file {{ .File }}.
func GetFileDoc() *encoder.FileDoc {
return &encoder.FileDoc{
Name: "{{ .Name }}",
Description: "{{ .Header }}",
@ -153,6 +154,7 @@ type Text struct {
Comment string `json:"-"`
Description string `json:"description"`
Examples []*Example `json:"examples"`
Alias string `json:"alias"`
Values []string `json:"values"`
Schema *SchemaWrapper `json:"schema"`
}
@ -346,6 +348,8 @@ func collectFields(s *structType, aliases map[string]aliasType) (fields []*Field
for _, f := range s.node.Fields.List {
if f.Names == nil {
// This is an embedded struct.
fields = append(fields, &Field{Type: "unknown"})
continue
}
@ -433,34 +437,52 @@ func render(doc *Doc, dest string) {
}
}
func processFile(inputFile, outputFile, schemaOutputFile, versionTagFile, typeName string) {
abs, err := in(inputFile)
if err != nil {
log.Fatal(err)
func processFile(inputFiles []string, outputFile, schemaOutputFile, versionTagFile string) {
var (
packageName string
packageDoc string
structs []*structType
)
aliases := map[string]aliasType{}
for _, inputFile := range inputFiles {
abs, err := in(inputFile)
if err != nil {
log.Fatal(err)
}
fmt.Printf("creating package file set: %q\n", abs)
fset := token.NewFileSet()
node, err := parser.ParseFile(fset, abs, nil, parser.ParseComments)
if err != nil {
log.Fatal(err)
}
packageName = node.Name.Name
if node.Doc != nil && node.Doc.Text() != "" {
packageDoc = node.Doc.Text()
}
tokenFile := fset.File(node.Pos())
if tokenFile == nil {
log.Fatalf("No token")
}
fmt.Printf("parsing file in package %q: %s\n", packageName, tokenFile.Name())
fileStructs, fileAliases := collectStructs(node)
structs = append(structs, fileStructs...)
maps.Copy(aliases, fileAliases)
}
fmt.Printf("creating package file set: %q\n", abs)
fset := token.NewFileSet()
node, err := parser.ParseFile(fset, abs, nil, parser.ParseComments)
if err != nil {
log.Fatal(err)
}
packageName := node.Name.Name
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, aliases := collectStructs(node)
if len(structs) == 0 {
log.Fatalf("failed to find types that could be documented in %s", abs)
log.Fatalf("failed to find types that could be documented in %v", inputFiles)
}
doc := &Doc{
@ -501,6 +523,11 @@ func processFile(inputFile, outputFile, schemaOutputFile, versionTagFile, typeNa
}
for _, s := range doc.Structs {
if s.Text.Alias != "" {
s.Text.Description = strings.ReplaceAll(s.Text.Description, s.Name, s.Text.Alias)
s.Text.Comment = strings.ReplaceAll(s.Text.Comment, s.Name, s.Text.Alias)
}
if extra, ok := extraExamples[s.Name]; ok {
s.Text.Examples = append(s.Text.Examples, extra...)
}
@ -510,14 +537,9 @@ func processFile(inputFile, outputFile, schemaOutputFile, versionTagFile, typeNa
}
}
if err == nil {
doc.Package = node.Name.Name
doc.Name = typeName
if node.Doc != nil {
doc.Header = escape(node.Doc.Text())
}
}
doc.Package = packageName
doc.Name = doc.Package
doc.Header = escape(packageDoc)
doc.File = outputFile
render(doc, outputFile)
@ -528,17 +550,14 @@ func processFile(inputFile, outputFile, schemaOutputFile, versionTagFile, typeNa
}
func main() {
outputFile := flag.String("output", "doc.go", "output file name")
jsonSchemaOutputFile := flag.String("json-schema-output", "", "output file name for json schema")
versionTagFile := flag.String("version-tag-file", "", "file name for version tag")
flag.Parse()
if flag.NArg() != 3 && flag.NArg() != 5 {
log.Fatalf("unexpected number of args: %d", flag.NArg())
if flag.NArg() == 0 {
log.Fatalf("no input files")
}
inputFile := flag.Arg(0)
outputFile := flag.Arg(1)
typeName := flag.Arg(2)
jsonSchemaOutputFile := flag.Arg(3)
versionTagFile := flag.Arg(4)
processFile(inputFile, outputFile, jsonSchemaOutputFile, versionTagFile, typeName)
processFile(flag.Args(), *outputFile, *jsonSchemaOutputFile, *versionTagFile)
}

View File

@ -15,6 +15,5 @@ func TestProcessFile(t *testing.T) {
outputFile := filepath.Join(t.TempDir(), "out.go")
schemaOutputFile := filepath.Join(t.TempDir(), "out.schema.json")
versionTagFile := filepath.Join("..", "..", "pkg", "machinery", "gendata", "data", "tag")
typeName := "Configuration"
processFile(inputFile, outputFile, schemaOutputFile, versionTagFile, typeName)
processFile([]string{inputFile}, outputFile, schemaOutputFile, versionTagFile)
}

View File

@ -41,7 +41,7 @@ func (suite *NfTablesChainConfigTestSuite) injectConfig(block bool) {
kubeletIngressCfg.Ingress = []networkcfg.IngressRule{
{
Subnet: netip.MustParsePrefix("10.0.0.0/8"),
Except: netip.MustParsePrefix("10.3.0.0/16"),
Except: networkcfg.Prefix{Prefix: netip.MustParsePrefix("10.3.0.0/16")},
},
{
Subnet: netip.MustParsePrefix("192.168.0.0/16"),

View File

@ -6,51 +6,19 @@ package encoder
import (
"bytes"
_ "embed"
"fmt"
"os"
"path/filepath"
"regexp"
"slices"
"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.AppearsIn -}}
Appears in:
{{ range $appearance := $struct.AppearsIn -}}
- <code>{{ encodeType $appearance.TypeName }}.{{ $appearance.FieldName }}</code>
{{ end -}}
{{ end }}
{{ range $example := $struct.Examples }}
{{ yaml $example.GetValue "" }}
{{ end }}
{{ if $struct.Fields -}}
| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
{{ range $field := $struct.Fields -}}
{{ if $field.Name -}}
| {{- $tick }}{{ $field.Name }}{{ $tick }} |
{{- encodeType $field.Type }} |
{{- fmtDesc $field.Description }} {{ with $field.Examples }}<details><summary>Show example(s)</summary>{{ range . }}{{ yaml .GetValue $field.Name }}{{ end }}</details>{{ end }} |
{{- range $value := $field.Values }}{{ $tick }}{{ $value }}{{ $tick }}<br />{{ end }} |
{{ end -}}
{{ end }}
{{ end }}
{{ end }}`
//go:embed "markdown.tmpl"
var markdownTemplate string
// FileDoc represents a single go file documentation.
type FileDoc struct {
@ -60,31 +28,33 @@ type FileDoc struct {
Description string
// Structs structs defined in the file.
Structs []*Doc
Anchors map[string]string
t *template.Template
// Types is map of all non-trivial types defined in the file.
Types map[string]*Doc
}
// 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
fd.t = template.Must(template.New("file_markdown.tpl").
func (fd *FileDoc) Encode(root *Doc, frontmatter func(title, description string) string) ([]byte, error) {
t := template.Must(template.New("markdown.tmpl").
Funcs(template.FuncMap{
"yaml": encodeYaml,
"fmtDesc": formatDescription,
"encodeType": fd.encodeType,
"yaml": encodeYaml,
"fmtDesc": formatDescription,
"dict": tmplDict,
"repeat": strings.Repeat,
"trimPrefix": strings.TrimPrefix,
"add": func(a, b int) int { return a + b },
"frontmatter": frontmatter,
}).
Parse(markdownTemplate))
buf := bytes.Buffer{}
var buf bytes.Buffer
if err := fd.t.Execute(&buf, fd); err != nil {
if err := t.Execute(&buf, struct {
Root *Doc
Types map[string]*Doc
}{
Root: root,
Types: fd.Types,
}); err != nil {
return nil, err
}
@ -92,54 +62,85 @@ func (fd *FileDoc) Encode() ([]byte, error) {
}
// Write dumps documentation string to folder.
func (fd *FileDoc) Write(path, frontmatter string) error {
data, err := fd.Encode()
if err != nil {
return err
}
if stat, e := os.Stat(path); !os.IsNotExist(e) {
//
//nolint:gocyclo
func (fd *FileDoc) Write(path string, frontmatter func(title, description string) string) error {
if stat, err := os.Stat(path); !os.IsNotExist(err) {
if !stat.IsDir() {
return fmt.Errorf("destination path should be a directory")
}
} else {
if e := os.MkdirAll(path, 0o777); e != nil {
return e
if err := os.MkdirAll(path, 0o777); err != nil {
return err
}
}
f, err := os.Create(filepath.Join(path, fmt.Sprintf("%s.%s", strings.ToLower(fd.Name), "md")))
if err != nil {
// generate _index.md
if err := os.WriteFile(filepath.Join(path, "_index.md"), []byte(frontmatter(fd.Name, fd.Description)), 0o666); err != nil {
return err
}
if _, err := f.WriteString(frontmatter); err != nil {
return err
// find map of all types
fd.Types = map[string]*Doc{}
for _, t := range fd.Structs {
if t.Type == "" || strings.ToLower(t.Type) == t.Type {
continue
}
fd.Types[t.Type] = t
}
if _, err := f.Write(data); err != nil {
return err
// find root nodes
var roots []*Doc
for _, t := range fd.Structs {
if len(t.AppearsIn) == 0 {
roots = append(roots, t)
}
}
for _, root := range roots {
contents, err := fd.Encode(root, frontmatter)
if err != nil {
return err
}
if err := os.WriteFile(filepath.Join(path, fmt.Sprintf("%s.%s", strings.ToLower(root.Type), "md")), contents, 0o666); err != nil {
return err
}
}
return nil
}
func (fd *FileDoc) encodeType(t string) string {
re := regexp.MustCompile(`\w+`)
//nolint:gocyclo
func encodeYaml(in any, path string) string {
if path != "" {
parts := strings.Split(path, ".")
for _, s := range re.FindAllString(t, -1) {
if anchor, ok := fd.Anchors[s]; ok {
t = strings.ReplaceAll(t, s, formatLink(s, "#"+anchor))
parts = parts[1:] // strip first segment, it's root element
// if the last element is ""/"-", it means we're at the root of the slice, so we don't need to wrap it once again
if len(parts) > 0 && (parts[len(parts)-1] == "" || parts[len(parts)-1] == "-") {
parts = parts[:len(parts)-1]
}
}
return t
}
slices.Reverse(parts)
func encodeYaml(in interface{}, name string) string {
if name != "" {
in = map[string]interface{}{
name: in,
for _, part := range parts {
switch part {
case "":
in = []any{in}
case "-":
in = map[string]any{
"example.com": in,
}
default:
in = map[string]any{
part: in,
}
}
}
}
@ -161,10 +162,6 @@ func encodeYaml(in interface{}, name string) string {
return fmt.Sprintf("{{< highlight yaml >}}\n%s{{< /highlight >}}", strings.Join(lines, "\n"))
}
func formatLink(text, link string) string {
return fmt.Sprintf(`<a href="%s">%s</a>`, link, text)
}
func formatDescription(description string) string {
lines := strings.Split(description, "\n")
if len(lines) <= 1 {
@ -173,3 +170,22 @@ func formatDescription(description string) string {
return fmt.Sprintf("<details><summary>%s</summary>%s</details>", lines[0], strings.Join(lines[1:], "<br />"))
}
func tmplDict(vals ...any) (map[string]any, error) {
if len(vals)%2 != 0 {
return nil, fmt.Errorf("invalid number of arguments: %d", len(vals))
}
res := map[string]any{}
for i := 0; i < len(vals); i += 2 {
key, ok := vals[i].(string)
if !ok {
return nil, fmt.Errorf("invalid key type: %T", vals[i])
}
res[key] = vals[i+1]
}
return res, nil
}

View File

@ -0,0 +1,59 @@
{{ frontmatter .Root.Type .Root.Description }}
{{ block "struct" dict "Struct" .Root "Level" 1 "Name" .Root.Type "Path" .Root.Type "Types" .Types }}
{{ if gt .Level 1 }}{{ repeat "#" .Level }} {{ .Name }} {#{{ .Path }}}{{ end }}
{{ if and .Struct.Description (gt .Level 1) -}}
{{ .Struct.Description }}
{{ end }}
{{ range $example := .Struct.Examples }}
{{ yaml $example.GetValue $.Path }}
{{ end }}
{{ if .Struct.Fields -}}
| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
{{ range $field := $.Struct.Fields -}}
{{ if $field.Name -}}
|`{{ $field.Name }}` |
{{- $type := index $.Types $field.Type -}}
{{- if $type -}}
<a href="#{{ $.Path }}.{{ $field.Name }}">{{ $field.Type }}</a>
{{- else -}}
{{- $type := index $.Types (trimPrefix $field.Type "[]") -}}
{{- if $type -}}
<a href="#{{ $.Path }}.{{ $field.Name }}.">{{ $field.Type }}</a>
{{- else -}}
{{- $type := index $.Types (trimPrefix $field.Type "map[string]") -}}
{{- if $type -}}
<a href="#{{ $.Path }}.{{ $field.Name }}.-">{{ $field.Type }}</a>
{{- else -}}
{{ $field.Type }}
{{- end -}}
{{- end -}}
{{- end }} |
{{- fmtDesc $field.Description }} {{ with $field.Examples }}<details><summary>Show example(s)</summary>{{ range . }}{{ yaml .GetValue (printf ".%s" $field.Name) }}{{ end }}</details>{{ end }} |
{{- range $value := $field.Values }}`{{ $value }}`<br />{{ end }} |
{{ end -}}
{{ end }}
{{ end }}
{{ range $field := .Struct.Fields }}
{{- $struct := index $.Types $field.Type -}}
{{- if $struct -}}
{{ template "struct" dict "Struct" $struct "Level" (add $.Level 1) "Name" $field.Name "Types" $.Types "Path" (printf "%s.%s" $.Path $field.Name) }}
{{- else -}}
{{- $struct := index $.Types (trimPrefix $field.Type "[]") -}}
{{- if $struct -}}
{{ template "struct" dict "Struct" $struct "Level" (add $.Level 1) "Name" (printf "%s[]" $field.Name) "Types" $.Types "Path" (printf "%s.%s." $.Path $field.Name) }}
{{- else -}}
{{- $struct := index $.Types (trimPrefix $field.Type "map[string]") -}}
{{- if $struct -}}
{{ template "struct" dict "Struct" $struct "Level" (add $.Level 1) "Name" (printf "%s.*" $field.Name) "Types" $.Types "Path" (printf "%s.%s.-" $.Path $field.Name) }}
{{- end -}}
{{- end -}}
{{- end -}}
{{ end }}
{{ end }}

View File

@ -31,10 +31,18 @@ var (
_ config.NetworkRuleConfigSignal = &DefaultActionConfigV1Alpha1{}
)
// DefaultActionConfigV1Alpha1 is a event sink config document.
// DefaultActionConfigV1Alpha1 is a ingress firewall default action configuration document.
//
// examples:
// - value: exampleDefaultActionConfigV1Alpha1()
// alias: NetworkDefaultActionConfig
type DefaultActionConfigV1Alpha1 struct {
meta.Meta `yaml:",inline"`
// description: |
// Default action for all not explicitly configured ingress traffic: accept or block.
// values:
// - "accept"
// - "block"
Ingress nethelpers.DefaultAction `yaml:"ingress"`
}
@ -48,6 +56,13 @@ func NewDefaultActionConfigV1Alpha1() *DefaultActionConfigV1Alpha1 {
}
}
func exampleDefaultActionConfigV1Alpha1() *DefaultActionConfigV1Alpha1 {
cfg := NewDefaultActionConfigV1Alpha1()
cfg.Ingress = nethelpers.DefaultActionAccept
return cfg
}
// Clone implements config.Document interface.
func (s *DefaultActionConfigV1Alpha1) Clone() config.Document {
return s.DeepCopy()

View File

@ -27,7 +27,7 @@ func TestDefaultActionConfigMarshalStability(t *testing.T) {
cfg := network.NewDefaultActionConfigV1Alpha1()
cfg.Ingress = nethelpers.DefaultActionBlock
marshaled, err := encoder.NewEncoder(cfg).Encode()
marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode()
require.NoError(t, err)
t.Log(string(marshaled))

View File

@ -2,7 +2,9 @@
// 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 network provides Talos network config documents.
// Package network provides network machine configuration documents.
package network
//go:generate docgen -output network_doc.go network.go default_action_config.go port_range.go rule_config.go
//go:generate deep-copy -type DefaultActionConfigV1Alpha1 -type RuleConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go .

View File

@ -0,0 +1,165 @@
// 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/.
// Code generated by hack/docgen tool. DO NOT EDIT.
package network
import (
"net/netip"
"github.com/siderolabs/talos/pkg/machinery/config/encoder"
)
func (DefaultActionConfigV1Alpha1) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "NetworkDefaultActionConfig",
Comments: [3]string{"" /* encoder.HeadComment */, "NetworkDefaultActionConfig is a ingress firewall default action configuration document." /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "NetworkDefaultActionConfig is a ingress firewall default action configuration document.",
Fields: []encoder.Doc{
{}, {
Name: "ingress",
Type: "DefaultAction",
Note: "",
Description: "Default action for all not explicitly configured ingress traffic: accept or block.",
Comments: [3]string{"" /* encoder.HeadComment */, "Default action for all not explicitly configured ingress traffic: accept or block." /* encoder.LineComment */, "" /* encoder.FootComment */},
Values: []string{
"accept",
"block",
},
},
},
}
doc.AddExample("", exampleDefaultActionConfigV1Alpha1())
return doc
}
func (RuleConfigV1Alpha1) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "NetworkRuleConfig",
Comments: [3]string{"" /* encoder.HeadComment */, "NetworkRuleConfig is a network firewall rule config document." /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "NetworkRuleConfig is a network firewall rule config document.",
Fields: []encoder.Doc{
{},
{
Name: "name",
Type: "string",
Note: "",
Description: "Name of the config document.",
Comments: [3]string{"" /* encoder.HeadComment */, "Name of the config document." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
{
Name: "portSelector",
Type: "RulePortSelector",
Note: "",
Description: "Port selector defines which ports and protocols on the host are affected by the rule.",
Comments: [3]string{"" /* encoder.HeadComment */, "Port selector defines which ports and protocols on the host are affected by the rule." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
{
Name: "ingress",
Type: "[]IngressRule",
Note: "",
Description: "Ingress defines which source subnets are allowed to access the host ports/protocols defined by the `portSelector`.",
Comments: [3]string{"" /* encoder.HeadComment */, "Ingress defines which source subnets are allowed to access the host ports/protocols defined by the `portSelector`." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
},
}
doc.AddExample("", exampleRuleConfigV1Alpha1())
return doc
}
func (RulePortSelector) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "RulePortSelector",
Comments: [3]string{"" /* encoder.HeadComment */, "RulePortSelector is a port selector for the network rule." /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "RulePortSelector is a port selector for the network rule.",
AppearsIn: []encoder.Appearance{
{
TypeName: "RuleConfigV1Alpha1",
FieldName: "portSelector",
},
},
Fields: []encoder.Doc{
{
Name: "ports",
Type: "PortRanges",
Note: "",
Description: "Ports defines a list of port ranges or single ports.\nThe port ranges are inclusive, and should not overlap.",
Comments: [3]string{"" /* encoder.HeadComment */, "Ports defines a list of port ranges or single ports." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
{
Name: "protocol",
Type: "Protocol",
Note: "",
Description: "Protocol defines traffic protocol (e.g. TCP or UDP).",
Comments: [3]string{"" /* encoder.HeadComment */, "Protocol defines traffic protocol (e.g. TCP or UDP)." /* encoder.LineComment */, "" /* encoder.FootComment */},
Values: []string{
"tcp",
"udp",
"icmp",
"icmpv6",
},
},
},
}
doc.Fields[0].AddExample("", examplePortRanges1())
doc.Fields[0].AddExample("", examplePortRanges2())
return doc
}
func (IngressRule) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "IngressRule",
Comments: [3]string{"" /* encoder.HeadComment */, "IngressRule is a ingress rule." /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "IngressRule is a ingress rule.",
AppearsIn: []encoder.Appearance{
{
TypeName: "RuleConfigV1Alpha1",
FieldName: "ingress",
},
},
Fields: []encoder.Doc{
{
Name: "subnet",
Type: "Prefix",
Note: "",
Description: "Subnet defines a source subnet.",
Comments: [3]string{"" /* encoder.HeadComment */, "Subnet defines a source subnet." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
{
Name: "except",
Type: "Prefix",
Note: "",
Description: "Except defines a source subnet to exclude from the rule, it gets excluded from the `subnet`.",
Comments: [3]string{"" /* encoder.HeadComment */, "Except defines a source subnet to exclude from the rule, it gets excluded from the `subnet`." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
},
}
doc.Fields[0].AddExample("", netip.MustParsePrefix("10.3.4.0/24"))
doc.Fields[0].AddExample("", netip.MustParsePrefix("2001:db8::/32"))
doc.Fields[0].AddExample("", netip.MustParsePrefix("1.3.4.5/32"))
return doc
}
// GetFileDoc returns documentation for the file network_doc.go.
func GetFileDoc() *encoder.FileDoc {
return &encoder.FileDoc{
Name: "network",
Description: "Package network provides network machine configuration documents.\n",
Structs: []*encoder.Doc{
DefaultActionConfigV1Alpha1{}.Doc(),
RuleConfigV1Alpha1{}.Doc(),
RulePortSelector{}.Doc(),
IngressRule{}.Doc(),
},
}
}

View File

@ -2,7 +2,6 @@
// 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 network provides Talos network config documents.
package network
import (
@ -14,6 +13,8 @@ import (
)
// PortRange is a port range.
//
//docgen:nodoc
type PortRange struct {
Lo uint16
Hi uint16
@ -71,6 +72,8 @@ func (pr PortRange) String() string {
}
// PortRanges is a slice of port ranges.
//
//docgen:nodoc
type PortRanges []PortRange
// Validate the port ranges.
@ -100,3 +103,17 @@ func (prs PortRanges) Validate() error {
return nil
}
func examplePortRanges1() PortRanges {
return PortRanges{
{Lo: 80, Hi: 80},
{Lo: 443, Hi: 443},
}
}
func examplePortRanges2() PortRanges {
return PortRanges{
{Lo: 1200, Hi: 1299},
{Lo: 8080, Hi: 8080},
}
}

View File

@ -41,27 +41,78 @@ var (
)
// RuleConfigV1Alpha1 is a network firewall rule config document.
//
// examples:
// - value: exampleRuleConfigV1Alpha1()
// alias: NetworkRuleConfig
type RuleConfigV1Alpha1 struct {
meta.Meta `yaml:",inline"`
MetaName string `yaml:"name"`
// description: |
// Name of the config document.
MetaName string `yaml:"name"`
// description: |
// Port selector defines which ports and protocols on the host are affected by the rule.
PortSelector RulePortSelector `yaml:"portSelector"`
Ingress IngressConfig `yaml:"ingress"`
// description: |
// Ingress defines which source subnets are allowed to access the host ports/protocols defined by the `portSelector`.
Ingress IngressConfig `yaml:"ingress"`
}
// RulePortSelector is a port selector for the network rule.
type RulePortSelector struct {
Ports PortRanges `yaml:"ports"`
// description: |
// Ports defines a list of port ranges or single ports.
// The port ranges are inclusive, and should not overlap.
// examples:
// - value: >
// examplePortRanges1()
// - value: >
// examplePortRanges2()
Ports PortRanges `yaml:"ports"`
// description: |
// Protocol defines traffic protocol (e.g. TCP or UDP).
// values:
// - "tcp"
// - "udp"
// - "icmp"
// - "icmpv6"
Protocol nethelpers.Protocol `yaml:"protocol"`
}
// IngressConfig is a ingress config.
//
//docgen:alias
type IngressConfig []IngressRule
// IngressRule is a ingress rule.
type IngressRule struct {
// description: |
// Subnet defines a source subnet.
// examples:
// - value: >
// netip.MustParsePrefix("10.3.4.0/24")
// - value: >
// netip.MustParsePrefix("2001:db8::/32")
// - value: >
// netip.MustParsePrefix("1.3.4.5/32")
Subnet netip.Prefix `yaml:"subnet"`
Except netip.Prefix `yaml:"except,omitempty"`
// description: |
// Except defines a source subnet to exclude from the rule, it gets excluded from the `subnet`.
Except Prefix `yaml:"except,omitempty"`
}
// Prefix is a wrapper for netip.Prefix.
//
// It implements IsZero() so that yaml.Marshal correctly skips empty values.
//
//docgen:nodoc
type Prefix struct {
netip.Prefix
}
// IsZero implements yaml.IsZeroer interface.
func (n Prefix) IsZero() bool {
return n.Prefix == netip.Prefix{}
}
// NewRuleConfigV1Alpha1 creates a new RuleConfig config document.
@ -74,6 +125,22 @@ func NewRuleConfigV1Alpha1() *RuleConfigV1Alpha1 {
}
}
func exampleRuleConfigV1Alpha1() *RuleConfigV1Alpha1 {
cfg := NewRuleConfigV1Alpha1()
cfg.MetaName = "ingress-apid"
cfg.PortSelector.Protocol = nethelpers.ProtocolTCP
cfg.PortSelector.Ports = PortRanges{
{Lo: 50000, Hi: 50000},
}
cfg.Ingress = IngressConfig{
{
Subnet: netip.MustParsePrefix("192.168.0.0/16"),
},
}
return cfg
}
// Name implements config.NamedDocument interface.
func (s *RuleConfigV1Alpha1) Name() string {
return s.MetaName
@ -148,7 +215,7 @@ func (s *RuleConfigV1Alpha1) ExceptSubnets() []netip.Prefix {
},
),
func(rule IngressRule) netip.Prefix {
return rule.Except
return rule.Except.Prefix
},
)
}

View File

@ -39,14 +39,14 @@ func TestRuleConfigMarshalStability(t *testing.T) {
cfg.Ingress = network.IngressConfig{
{
Subnet: netip.MustParsePrefix("192.168.0.0/16"),
Except: netip.MustParsePrefix("192.168.0.3/32"),
Except: network.Prefix{netip.MustParsePrefix("192.168.0.3/32")},
},
{
Subnet: netip.MustParsePrefix("2001::/16"),
},
}
marshaled, err := encoder.NewEncoder(cfg).Encode()
marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode()
require.NoError(t, err)
t.Log(string(marshaled))
@ -79,7 +79,7 @@ func TestRuleConfigUnmarshal(t *testing.T) {
Ingress: network.IngressConfig{
{
Subnet: netip.MustParsePrefix("192.168.0.0/16"),
Except: netip.MustParsePrefix("192.168.0.3/32"),
Except: network.Prefix{netip.MustParsePrefix("192.168.0.3/32")},
},
{
Subnet: netip.MustParsePrefix("2001::/16"),
@ -159,7 +159,7 @@ func TestRuleConfigValidate(t *testing.T) {
cfg.Ingress = network.IngressConfig{
{
Subnet: netip.MustParsePrefix("192.168.0.0/16"),
Except: netip.MustParsePrefix("192.168.3.0/24"),
Except: network.Prefix{netip.MustParsePrefix("192.168.3.0/24")},
},
{
Subnet: netip.MustParsePrefix("2001::/16"),

View File

@ -38,9 +38,18 @@ var (
)
// EventSinkV1Alpha1 is a event sink config document.
//
// examples:
// - value: exampleEventSinkV1Alpha1()
// alias: EventSinkConfig
type EventSinkV1Alpha1 struct {
meta.Meta `yaml:",inline"`
Endpoint string `yaml:"endpoint"`
// description: |
// The endpoint for the event sink as 'host:port'.
// examples:
// - value: >
// "10.3.7.3:2810"
Endpoint string `yaml:"endpoint"`
}
// NewEventSinkV1Alpha1 creates a new eventsink config document.
@ -53,6 +62,13 @@ func NewEventSinkV1Alpha1() *EventSinkV1Alpha1 {
}
}
func exampleEventSinkV1Alpha1() *EventSinkV1Alpha1 {
cfg := NewEventSinkV1Alpha1()
cfg.Endpoint = "192.168.10.3:3247"
return cfg
}
// Clone implements config.Document interface.
func (s *EventSinkV1Alpha1) Clone() config.Document {
return s.DeepCopy()

View File

@ -22,7 +22,7 @@ func TestEventSinkMarshalStability(t *testing.T) {
cfg := runtime.NewEventSinkV1Alpha1()
cfg.Endpoint = "10.0.0.1:3333"
marshaled, err := encoder.NewEncoder(cfg).Encode()
marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode()
require.NoError(t, err)
t.Log(string(marshaled))

View File

@ -8,6 +8,8 @@ import (
"fmt"
"net/url"
"github.com/siderolabs/gen/ensure"
"github.com/siderolabs/talos/pkg/machinery/config/config"
"github.com/siderolabs/talos/pkg/machinery/config/internal/registry"
"github.com/siderolabs/talos/pkg/machinery/config/types/meta"
@ -36,9 +38,23 @@ var (
)
// KmsgLogV1Alpha1 is a event sink config document.
//
// examples:
// - value: exampleKmsgLogV1Alpha1()
// alias: KmsgLogConfig
type KmsgLogV1Alpha1 struct {
meta.Meta `yaml:",inline"`
MetaName string `yaml:"name"`
meta.Meta `yaml:",inline"`
// description: |
// Name of the config document.
MetaName string `yaml:"name"`
// description: |
// The URL encodes the log destination.
// The scheme must be tcp:// or udp://.
// The path must be empty.
// The port is required.
// examples:
// - value: >
// "udp://10.3.7.3:2810"
KmsgLogURL meta.URL `yaml:"url"`
}
@ -52,6 +68,14 @@ func NewKmsgLogV1Alpha1() *KmsgLogV1Alpha1 {
}
}
func exampleKmsgLogV1Alpha1() *KmsgLogV1Alpha1 {
cfg := NewKmsgLogV1Alpha1()
cfg.MetaName = "remote-log"
cfg.KmsgLogURL.URL = ensure.Value(url.Parse("tcp://192.168.3.7:3478/"))
return cfg
}
// Name implements config.NamedDocument interface.
func (s *KmsgLogV1Alpha1) Name() string {
return s.MetaName

View File

@ -25,7 +25,7 @@ func TestKmsgLogMarshalStability(t *testing.T) {
cfg.MetaName = "apiSink"
cfg.KmsgLogURL.URL = ensure.Value(url.Parse("https://kmsglog.api/logs"))
marshaled, err := encoder.NewEncoder(cfg).Encode()
marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode()
require.NoError(t, err)
t.Log(string(marshaled))

View File

@ -2,7 +2,9 @@
// 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 runtime provides Talos runtime config documents.
// Package runtime provides runtime machine configuration documents.
package runtime
//go:generate docgen -output runtime_doc.go runtime.go kmsg_log.go event_sink.go
//go:generate deep-copy -type EventSinkV1Alpha1 -type KmsgLogV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go .

View File

@ -0,0 +1,77 @@
// 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/.
// Code generated by hack/docgen tool. DO NOT EDIT.
package runtime
import (
"github.com/siderolabs/talos/pkg/machinery/config/encoder"
)
func (KmsgLogV1Alpha1) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "KmsgLogConfig",
Comments: [3]string{"" /* encoder.HeadComment */, "KmsgLogConfig is a event sink config document." /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "KmsgLogConfig is a event sink config document.",
Fields: []encoder.Doc{
{},
{
Name: "name",
Type: "string",
Note: "",
Description: "Name of the config document.",
Comments: [3]string{"" /* encoder.HeadComment */, "Name of the config document." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
{
Name: "url",
Type: "URL",
Note: "",
Description: "The URL encodes the log destination.\nThe scheme must be tcp:// or udp://.\nThe path must be empty.\nThe port is required.",
Comments: [3]string{"" /* encoder.HeadComment */, "The URL encodes the log destination." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
},
}
doc.AddExample("", exampleKmsgLogV1Alpha1())
doc.Fields[2].AddExample("", "udp://10.3.7.3:2810")
return doc
}
func (EventSinkV1Alpha1) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "EventSinkConfig",
Comments: [3]string{"" /* encoder.HeadComment */, "EventSinkConfig is a event sink config document." /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "EventSinkConfig is a event sink config document.",
Fields: []encoder.Doc{
{}, {
Name: "endpoint",
Type: "string",
Note: "",
Description: "The endpoint for the event sink as 'host:port'.",
Comments: [3]string{"" /* encoder.HeadComment */, "The endpoint for the event sink as 'host:port'." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
},
}
doc.AddExample("", exampleEventSinkV1Alpha1())
doc.Fields[1].AddExample("", "10.3.7.3:2810")
return doc
}
// GetFileDoc returns documentation for the file runtime_doc.go.
func GetFileDoc() *encoder.FileDoc {
return &encoder.FileDoc{
Name: "runtime",
Description: "Package runtime provides runtime machine configuration documents.\n",
Structs: []*encoder.Doc{
KmsgLogV1Alpha1{}.Doc(),
EventSinkV1Alpha1{}.Doc(),
},
}
}

View File

@ -2,19 +2,23 @@
// 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 siderolink provides siderolink config documents.
// Package siderolink provides SideroLink machine configuration documents.
package siderolink
import (
"fmt"
"net/url"
"github.com/siderolabs/gen/ensure"
"github.com/siderolabs/talos/pkg/machinery/config/config"
"github.com/siderolabs/talos/pkg/machinery/config/internal/registry"
"github.com/siderolabs/talos/pkg/machinery/config/types/meta"
"github.com/siderolabs/talos/pkg/machinery/config/validation"
)
//go:generate docgen -output ./siderolink_doc.go ./siderolink.go
//go:generate deep-copy -type ConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go .
// Kind is a siderolink config document kind.
@ -38,9 +42,18 @@ var (
_ config.Validator = &ConfigV1Alpha1{}
)
// ConfigV1Alpha1 is a siderolink config document.
// ConfigV1Alpha1 is a SideroLink connection machine configuration document.
//
// examples:
// - value: exampleConfigV1Alpha1()
// alias: SideroLinkConfig
type ConfigV1Alpha1 struct {
meta.Meta `yaml:",inline"`
meta.Meta `yaml:",inline"`
// description: |
// SideroLink API URL to connect to.
// examples:
// - value: >
// "https://siderolink.api/join?token=secret"
APIUrlConfig meta.URL `yaml:"apiUrl"`
}
@ -54,6 +67,13 @@ func NewConfigV1Alpha1() *ConfigV1Alpha1 {
}
}
func exampleConfigV1Alpha1() *ConfigV1Alpha1 {
cfg := NewConfigV1Alpha1()
cfg.APIUrlConfig.URL = ensure.Value(url.Parse("https://siderolink.api/join?token=secret"))
return cfg
}
// Clone implements config.Document interface.
func (s *ConfigV1Alpha1) Clone() config.Document {
return s.DeepCopy()

View File

@ -0,0 +1,45 @@
// 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/.
// Code generated by hack/docgen tool. DO NOT EDIT.
package siderolink
import (
"github.com/siderolabs/talos/pkg/machinery/config/encoder"
)
func (ConfigV1Alpha1) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "SideroLinkConfig",
Comments: [3]string{"" /* encoder.HeadComment */, "SideroLinkConfig is a SideroLink connection machine configuration document." /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "SideroLinkConfig is a SideroLink connection machine configuration document.",
Fields: []encoder.Doc{
{}, {
Name: "apiUrl",
Type: "URL",
Note: "",
Description: "SideroLink API URL to connect to.",
Comments: [3]string{"" /* encoder.HeadComment */, "SideroLink API URL to connect to." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
},
}
doc.AddExample("", exampleConfigV1Alpha1())
doc.Fields[1].AddExample("", "https://siderolink.api/join?token=secret")
return doc
}
// GetFileDoc returns documentation for the file ./siderolink_doc.go.
func GetFileDoc() *encoder.FileDoc {
return &encoder.FileDoc{
Name: "siderolink",
Description: "Package siderolink provides SideroLink machine configuration documents.\n",
Structs: []*encoder.Doc{
ConfigV1Alpha1{}.Doc(),
},
}
}

View File

@ -39,7 +39,7 @@ func TestMarshalStability(t *testing.T) {
cfg := siderolink.NewConfigV1Alpha1()
cfg.APIUrlConfig.URL = ensure.Value(url.Parse("https://siderolink.api/join?jointoken=secret&user=alice"))
marshaled, err := encoder.NewEncoder(cfg).Encode()
marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode()
require.NoError(t, err)
assert.Equal(t, expectedDocument, marshaled)

View File

@ -701,12 +701,6 @@
"markdownDescription": "Enable verbose logging to the console.\nAll system containers logs will flow into serial console.\n\n**Note:** To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput.",
"x-intellij-html-description": "\u003cp\u003eEnable verbose logging to the console.\nAll system containers logs will flow into serial console.\u003c/p\u003e\n\n\u003cp\u003e\u003cstrong\u003eNote:\u003c/strong\u003e To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput.\u003c/p\u003e\n"
},
"persist": {
"type": "boolean",
"title": "persist",
"markdownDescription": "",
"x-intellij-html-description": ""
},
"machine": {
"$ref": "#/$defs/MachineConfig",
"title": "machine",
@ -2395,19 +2389,6 @@
"additionalProperties": false,
"type": "object"
},
"PodCheckpointer": {
"properties": {
"image": {
"type": "string",
"title": "image",
"description": "The image field is an override to the default pod-checkpointer image.\n",
"markdownDescription": "The `image` field is an override to the default pod-checkpointer image.",
"x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eimage\u003c/code\u003e field is an override to the default pod-checkpointer image.\u003c/p\u003e\n"
}
},
"additionalProperties": false,
"type": "object"
},
"ProxyConfig": {
"properties": {
"disabled": {

View File

@ -30,12 +30,10 @@ func mustParseURL(uri string) *url.URL {
func configExample() any {
return struct {
Version string `yaml:"version"`
Persist bool
Machine *yaml.Node
Cluster *yaml.Node
}{
Version: "v1alpha1",
Persist: true,
Machine: &yaml.Node{Kind: yaml.ScalarNode, LineComment: "..."},
Cluster: &yaml.Node{Kind: yaml.ScalarNode, LineComment: "..."},
}

View File

@ -3,17 +3,17 @@
// 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.
Package v1alpha1 contains definition of the `v1alpha1` configuration document.
To generate a set of basic configuration files, run:
Even though the machine configuration in Talos Linux is multi-document, at the moment
this configuration document contains most of the configuration options.
talosctl gen config --version v1alpha1 <cluster name> <cluster endpoint>
This will generate a machine config for each node type, and a talosconfig for the CLI.
It is expected that new configuration options will be added as new documents, and existing ones
migrated to their own documents.
*/
package v1alpha1
//go:generate docgen ./v1alpha1_types.go ./v1alpha1_types_doc.go Configuration ./schemas/v1alpha1_config.schema.json ../../../gendata/data/tag
//go:generate docgen -output ./v1alpha1_types_doc.go -json-schema-output ./schemas/v1alpha1_config.schema.json -version-tag-file ../../../gendata/data/tag ./v1alpha1_types.go
//go:generate deepcopy-gen --input-dirs ../v1alpha1/ --go-header-file ../../../../../hack/boilerplate.txt --bounding-dirs ../v1alpha1 -O zz_generated.deepcopy
@ -42,7 +42,7 @@ func init() {
})
}
// Config defines the v1alpha1 configuration file.
// Config defines the v1alpha1.Config Talos machine configuration document.
//
// examples:
// - value: configExample()
@ -63,7 +63,9 @@ type Config struct {
// - false
// - no
ConfigDebug *bool `yaml:"debug,omitempty"`
//docgen:nodoc
// docgen:nodoc
//
// Deprecated: Not supported anymore.
ConfigPersist *bool `yaml:"persist,omitempty"`
// description: |
// Provides machine specific configuration options.
@ -1002,6 +1004,8 @@ type RegistriesConfig struct {
}
// PodCheckpointer represents the pod-checkpointer config values.
//
//docgen:nodoc
type PodCheckpointer struct {
// description: |
// The `image` field is an override to the default pod-checkpointer image.
@ -2181,6 +2185,8 @@ type VolumeMountConfig struct {
}
// ClusterInlineManifests is a list of ClusterInlineManifest.
//
//docgen:alias
type ClusterInlineManifests []ClusterInlineManifest
// UnmarshalYAML implements yaml.Unmarshaler.

View File

@ -16,8 +16,8 @@ import (
func (Config) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "Config",
Comments: [3]string{"" /* encoder.HeadComment */, "Config defines the v1alpha1 configuration file." /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "Config defines the v1alpha1 configuration file.",
Comments: [3]string{"" /* encoder.HeadComment */, "Config defines the v1alpha1.Config Talos machine configuration document." /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "Config defines the v1alpha1.Config Talos machine configuration document.",
Fields: []encoder.Doc{
{
Name: "version",
@ -42,13 +42,7 @@ func (Config) Doc() *encoder.Doc {
"no",
},
},
{
Name: "persist",
Type: "bool",
Note: "",
Description: "",
Comments: [3]string{"" /* encoder.HeadComment */, "" /* encoder.LineComment */, "" /* encoder.FootComment */},
},
{},
{
Name: "machine",
Type: "MachineConfig",
@ -491,7 +485,7 @@ func (ClusterConfig) Doc() *encoder.Doc {
},
{
Name: "inlineManifests",
Type: "ClusterInlineManifests",
Type: "[]ClusterInlineManifest",
Note: "",
Description: "A list of inline Kubernetes manifests.\nThese will get automatically deployed as part of the bootstrap.",
Comments: [3]string{"" /* encoder.HeadComment */, "A list of inline Kubernetes manifests." /* encoder.LineComment */, "" /* encoder.FootComment */},
@ -1256,25 +1250,6 @@ func (RegistriesConfig) Doc() *encoder.Doc {
return doc
}
func (PodCheckpointer) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "PodCheckpointer",
Comments: [3]string{"" /* encoder.HeadComment */, "PodCheckpointer represents the pod-checkpointer config values." /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "PodCheckpointer represents the pod-checkpointer config values.",
Fields: []encoder.Doc{
{
Name: "image",
Type: "string",
Note: "",
Description: "The `image` field is an override to the default pod-checkpointer image.",
Comments: [3]string{"" /* encoder.HeadComment */, "The `image` field is an override to the default pod-checkpointer image." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
},
}
return doc
}
func (CoreDNS) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "CoreDNS",
@ -1324,7 +1299,9 @@ func (Endpoint) Doc() *encoder.Doc {
FieldName: "endpoint",
},
},
Fields: []encoder.Doc{},
Fields: []encoder.Doc{
{},
},
}
doc.AddExample("", clusterEndpointExample1())
@ -3516,6 +3493,12 @@ func (ClusterInlineManifest) Doc() *encoder.Doc {
Type: "ClusterInlineManifest",
Comments: [3]string{"" /* encoder.HeadComment */, "ClusterInlineManifest struct describes inline bootstrap manifests for the user." /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "ClusterInlineManifest struct describes inline bootstrap manifests for the user.",
AppearsIn: []encoder.Appearance{
{
TypeName: "ClusterConfig",
FieldName: "inlineManifests",
},
},
Fields: []encoder.Doc{
{
Name: "name",
@ -3534,6 +3517,8 @@ func (ClusterInlineManifest) Doc() *encoder.Doc {
},
}
doc.AddExample("", clusterInlineManifestsExample())
doc.Fields[0].AddExample("", "csi")
doc.Fields[1].AddExample("", "/etc/kubernetes/auth")
@ -3954,11 +3939,11 @@ func (KernelModuleConfig) Doc() *encoder.Doc {
return doc
}
// GetConfigurationDoc returns documentation for the file ./v1alpha1_types_doc.go.
func GetConfigurationDoc() *encoder.FileDoc {
// GetFileDoc returns documentation for the file ./v1alpha1_types_doc.go.
func GetFileDoc() *encoder.FileDoc {
return &encoder.FileDoc{
Name: "Configuration",
Description: "Package v1alpha1 configuration file contains all the options available for configuring a machine.\n\nTo generate a set of basic configuration files, run:\n\n talosctl gen config --version v1alpha1 <cluster name> <cluster endpoint>\n\nThis will generate a machine config for each node type, and a talosconfig for the CLI.\n",
Name: "v1alpha1",
Description: "Package v1alpha1 contains definition of the `v1alpha1` configuration document.\n\nEven though the machine configuration in Talos Linux is multi-document, at the moment\nthis configuration document contains most of the configuration options.\n\nIt is expected that new configuration options will be added as new documents, and existing ones\nmigrated to their own documents.\n",
Structs: []*encoder.Doc{
Config{}.Doc(),
MachineConfig{}.Doc(),
@ -3977,7 +3962,6 @@ func GetConfigurationDoc() *encoder.FileDoc {
InstallExtensionConfig{}.Doc(),
TimeConfig{}.Doc(),
RegistriesConfig{}.Doc(),
PodCheckpointer{}.Doc(),
CoreDNS{}.Doc(),
Endpoint{}.Doc(),
ControlPlaneConfig{}.Doc(),

View File

@ -68,6 +68,11 @@ style = "solarized-dark"
# Uncomment if you want your chosen highlight style used for code blocks without a specified language
# guessSyntax = "true"
[markup.tableOfContents]
endLevel = 6
ordered = false
startLevel = 2
# Everything below this are Site Params
# Comment out if you don't want the "print entire section" link enabled.

View File

@ -1,6 +1,6 @@
---
description: Talosctl CLI tool reference.
title: CLI
desription: Talosctl CLI tool reference.
---
<!-- markdownlint-disable -->

View File

@ -0,0 +1,28 @@
---
title: Configuration
description: Talos Linux machine configuration reference.
---
Talos Linux machine is fully configured via a single YAML file called *machine configuration*.
The file might contain one or more configuration documents separated by `---` (three dashes) lines.
At the moment, majority of the configuration options are within the [v1alpha1]({{< relref "./v1alpha1" >}}) document, so
this is the only mandatory document in the configuration file.
Configuration documents might be named (contain a `name:` field) or unnamed.
Unnamed documents can be supplied to the machine configuration file only once, while named documents can be supplied multiple times with unique names.
The `v1alpha1` document has its own (legacy) structure, while every other document has the following set of fields:
```yaml
apiVersion: v1alpha1 # version of the document
kind: NetworkRuleConfig # type of document
name: rule1 # only for named documents
```
This section contains the configuration reference, to learn more about Talos Linux machine configuration management, please see:
* [quick guide to configuration generation]({{< relref "../../introduction/getting-started#configure-talos-linux" >}})
* [configuration management in production]({{< relref "../../introduction/prodnotes#configure-talos" >}})
* [configuration patches]({{< relref "../../talos-guides/configuration/patching" >}})
* [editing live machine configuration]({{< relref "../../talos-guides/configuration/editing-machine-configuration" >}})

View File

@ -0,0 +1,8 @@
---
description: |
Package network provides network machine configuration documents.
title: network
---
<!-- markdownlint-disable -->

View File

@ -0,0 +1,31 @@
---
description: NetworkDefaultActionConfig is a ingress firewall default action configuration document.
title: NetworkDefaultActionConfig
---
<!-- markdownlint-disable -->
{{< highlight yaml >}}
apiVersion: v1alpha1
kind: NetworkDefaultActionConfig
ingress: accept # Default action for all not explicitly configured ingress traffic: accept or block.
{{< /highlight >}}
| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
|`ingress` |DefaultAction |Default action for all not explicitly configured ingress traffic: accept or block. |`accept`<br />`block`<br /> |

View File

@ -0,0 +1,90 @@
---
description: NetworkRuleConfig is a network firewall rule config document.
title: NetworkRuleConfig
---
<!-- markdownlint-disable -->
{{< highlight yaml >}}
apiVersion: v1alpha1
kind: NetworkRuleConfig
name: ingress-apid # Name of the config document.
# Port selector defines which ports and protocols on the host are affected by the rule.
portSelector:
# Ports defines a list of port ranges or single ports.
ports:
- 50000
protocol: tcp # Protocol defines traffic protocol (e.g. TCP or UDP).
# Ingress defines which source subnets are allowed to access the host ports/protocols defined by the `portSelector`.
ingress:
- subnet: 192.168.0.0/16 # Subnet defines a source subnet.
{{< /highlight >}}
| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
|`name` |string |Name of the config document. | |
|`portSelector` |<a href="#NetworkRuleConfig.portSelector">RulePortSelector</a> |Port selector defines which ports and protocols on the host are affected by the rule. | |
|`ingress` |<a href="#NetworkRuleConfig.ingress.">[]IngressRule</a> |Ingress defines which source subnets are allowed to access the host ports/protocols defined by the `portSelector`. | |
## portSelector {#NetworkRuleConfig.portSelector}
RulePortSelector is a port selector for the network rule.
| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
|`ports` |PortRanges |<details><summary>Ports defines a list of port ranges or single ports.</summary>The port ranges are inclusive, and should not overlap.</details> <details><summary>Show example(s)</summary>{{< highlight yaml >}}
ports:
- 80
- 443
{{< /highlight >}}{{< highlight yaml >}}
ports:
- 1200-1299
- 8080
{{< /highlight >}}</details> | |
|`protocol` |Protocol |Protocol defines traffic protocol (e.g. TCP or UDP). |`tcp`<br />`udp`<br />`icmp`<br />`icmpv6`<br /> |
## ingress[] {#NetworkRuleConfig.ingress.}
IngressRule is a ingress rule.
| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
|`subnet` |Prefix |Subnet defines a source subnet. <details><summary>Show example(s)</summary>{{< highlight yaml >}}
subnet: 10.3.4.0/24
{{< /highlight >}}{{< highlight yaml >}}
subnet: 2001:db8::/32
{{< /highlight >}}{{< highlight yaml >}}
subnet: 1.3.4.5/32
{{< /highlight >}}</details> | |
|`except` |Prefix |Except defines a source subnet to exclude from the rule, it gets excluded from the `subnet`. | |

View File

@ -0,0 +1,8 @@
---
description: |
Package runtime provides runtime machine configuration documents.
title: runtime
---
<!-- markdownlint-disable -->

View File

@ -0,0 +1,33 @@
---
description: EventSinkConfig is a event sink config document.
title: EventSinkConfig
---
<!-- markdownlint-disable -->
{{< highlight yaml >}}
apiVersion: v1alpha1
kind: EventSinkConfig
endpoint: 192.168.10.3:3247 # The endpoint for the event sink as 'host:port'.
{{< /highlight >}}
| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
|`endpoint` |string |The endpoint for the event sink as 'host:port'. <details><summary>Show example(s)</summary>{{< highlight yaml >}}
endpoint: 10.3.7.3:2810
{{< /highlight >}}</details> | |

View File

@ -0,0 +1,35 @@
---
description: KmsgLogConfig is a event sink config document.
title: KmsgLogConfig
---
<!-- markdownlint-disable -->
{{< highlight yaml >}}
apiVersion: v1alpha1
kind: KmsgLogConfig
name: remote-log # Name of the config document.
url: tcp://192.168.3.7:3478/ # The URL encodes the log destination.
{{< /highlight >}}
| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
|`name` |string |Name of the config document. | |
|`url` |URL |<details><summary>The URL encodes the log destination.</summary>The scheme must be tcp:// or udp://.<br />The path must be empty.<br />The port is required.</details> <details><summary>Show example(s)</summary>{{< highlight yaml >}}
url: udp://10.3.7.3:2810
{{< /highlight >}}</details> | |

View File

@ -0,0 +1,8 @@
---
description: |
Package siderolink provides SideroLink machine configuration documents.
title: siderolink
---
<!-- markdownlint-disable -->

View File

@ -0,0 +1,33 @@
---
description: SideroLinkConfig is a SideroLink connection machine configuration document.
title: SideroLinkConfig
---
<!-- markdownlint-disable -->
{{< highlight yaml >}}
apiVersion: v1alpha1
kind: SideroLinkConfig
apiUrl: https://siderolink.api/join?token=secret # SideroLink API URL to connect to.
{{< /highlight >}}
| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
|`apiUrl` |URL |SideroLink API URL to connect to. <details><summary>Show example(s)</summary>{{< highlight yaml >}}
apiUrl: https://siderolink.api/join?token=secret
{{< /highlight >}}</details> | |

View File

@ -0,0 +1,14 @@
---
description: |
Package v1alpha1 contains definition of the `v1alpha1` configuration document.
Even though the machine configuration in Talos Linux is multi-document, at the moment
this configuration document contains most of the configuration options.
It is expected that new configuration options will be added as new documents, and existing ones
migrated to their own documents.
title: v1alpha1
---
<!-- markdownlint-disable -->

View File

@ -701,12 +701,6 @@
"markdownDescription": "Enable verbose logging to the console.\nAll system containers logs will flow into serial console.\n\n**Note:** To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput.",
"x-intellij-html-description": "\u003cp\u003eEnable verbose logging to the console.\nAll system containers logs will flow into serial console.\u003c/p\u003e\n\n\u003cp\u003e\u003cstrong\u003eNote:\u003c/strong\u003e To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput.\u003c/p\u003e\n"
},
"persist": {
"type": "boolean",
"title": "persist",
"markdownDescription": "",
"x-intellij-html-description": ""
},
"machine": {
"$ref": "#/$defs/MachineConfig",
"title": "machine",
@ -2395,19 +2389,6 @@
"additionalProperties": false,
"type": "object"
},
"PodCheckpointer": {
"properties": {
"image": {
"type": "string",
"title": "image",
"description": "The image field is an override to the default pod-checkpointer image.\n",
"markdownDescription": "The `image` field is an override to the default pod-checkpointer image.",
"x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eimage\u003c/code\u003e field is an override to the default pod-checkpointer image.\u003c/p\u003e\n"
}
},
"additionalProperties": false,
"type": "object"
},
"ProxyConfig": {
"properties": {
"disabled": {

View File

@ -53,9 +53,11 @@ There are some special rules:
When patching a multi-document machine configuration, following rules apply:
- for each document in the patch, the document is merged with the respective document in the machine configuration (matching by `kind`, `apiVersion` and `name` for named documentes)
- for each document in the patch, the document is merged with the respective document in the machine configuration (matching by `kind`, `apiVersion` and `name` for named documents)
- if the patch document doesn't exist in the machine configuration, it is appended to the machine configuration
The strategic merge patch itself might be a multi-document YAML, and each document will be applied as a patch to the base machine configuration.
### RFC6902 (JSON Patches)
[JSON patches](https://jsonpatch.com/) can be written either in JSON or YAML format.

View File

@ -9,7 +9,8 @@ Talos Linux Ingress Firewall doesn't affect the traffic between the Kubernetes p
## Configuration
Ingress rules are configured as extra documents in the Talos machine configuration:
Ingress rules are configured as extra documents [NetwokDefaultActionConfig]({{< relref "../../reference/configuration/network/networkdefaultactionconfig.md" >}}) and
[NetworkRuleConfig]({{< relref "../../reference/configuration/network/networkruleconfig.md" >}}) in the Talos machine configuration:
```yaml
apiVersion: v1alpha1