mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-20 02:11:09 +00:00
Merge pull request #113343 from logicalhan/doc
Add support to metrics framework to auto-generate documentation for metrics
This commit is contained in:
commit
9acd38e07d
@ -25,3 +25,16 @@ To update the golden test list, you can run:
|
||||
```console
|
||||
./test/instrumentation/test-update.sh
|
||||
```
|
||||
|
||||
To update the list of documented metrics (which you need to run first before
|
||||
upgrading the documentation markdown file).
|
||||
|
||||
```console
|
||||
./test/instrumentation/update-documentation-metrics.sh
|
||||
```
|
||||
|
||||
To update the documented list of metrics for k8s/website, please run:
|
||||
|
||||
```console
|
||||
./test/instrumentation/update-documentation.sh
|
||||
```
|
||||
|
156
test/instrumentation/documentation/main.go
Executable file
156
test/instrumentation/documentation/main.go
Executable file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"k8s.io/component-base/metrics"
|
||||
)
|
||||
|
||||
var (
|
||||
GOROOT string = os.Getenv("GOROOT")
|
||||
GOOS string = os.Getenv("GOOS")
|
||||
KUBE_ROOT string = os.Getenv("KUBE_ROOT")
|
||||
funcMap = template.FuncMap{
|
||||
"ToLower": strings.ToLower,
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
templ = `---
|
||||
title: Kubernetes Metrics Reference
|
||||
content_type: reference
|
||||
description: >-
|
||||
Details of the metric data that Kubernetes components export.
|
||||
---
|
||||
|
||||
|
||||
## Metrics (auto-generated {{.GeneratedDate.Format "2006 Jan 02"}})
|
||||
|
||||
This page details the metrics that different Kubernetes components export. You can query the metrics endpoint for these
|
||||
components using an HTTP scrape, and fetch the current metrics data in Prometheus format.
|
||||
|
||||
### List of Kubernetes Metrics
|
||||
|
||||
<table class="table" caption="This is the list of metrics emitted from core Kubernetes components">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="metric_name">Name</th>
|
||||
<th class="metric_stability_level">Stability Level</th>
|
||||
<th class="metric_type">Type</th>
|
||||
<th class="metric_help">Help</th>
|
||||
<th class="metric_labels">Labels</th>
|
||||
<th class="metric_const_labels">Const Labels</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range $index, $metric := .Metrics}}
|
||||
<tr class="metric"><td class="metric_name">{{with $metric}}{{.BuildFQName}}{{end}}</td>
|
||||
<td class="metric_stability_level" data-stability="{{$metric.StabilityLevel | ToLower}}">{{$metric.StabilityLevel}}</td>
|
||||
<td class="metric_type" data-type="{{$metric.Type | ToLower}}">{{$metric.Type}}</td>
|
||||
<td class="metric_description">{{$metric.Help}}</td>
|
||||
{{if not $metric.Labels }}<td class="metric_labels_varying">None</td>{{else }}<td class="metric_labels_varying">{{range $label := $metric.Labels}}<div class="metric_label">{{$label}}</div>{{end}}</td>{{end}}
|
||||
{{if not $metric.ConstLabels }}<td class="metric_labels_constant">None</td>{{else }}<td class="metric_labels_constant">{{$metric.ConstLabels}}</td>{{end}}</tr>{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
`
|
||||
)
|
||||
|
||||
type templateData struct {
|
||||
Metrics []metric
|
||||
GeneratedDate time.Time
|
||||
}
|
||||
|
||||
func main() {
|
||||
dat, err := os.ReadFile("test/instrumentation/testdata/documentation-list.yaml")
|
||||
if err == nil {
|
||||
metrics := []metric{}
|
||||
err = yaml.Unmarshal(dat, &metrics)
|
||||
if err != nil {
|
||||
println("err", err)
|
||||
}
|
||||
sort.Sort(byFQName(metrics))
|
||||
t := template.New("t").Funcs(funcMap)
|
||||
t, err := t.Parse(templ)
|
||||
if err != nil {
|
||||
println("err", err)
|
||||
}
|
||||
var tpl bytes.Buffer
|
||||
for i, m := range metrics {
|
||||
m.Help = strings.Join(strings.Split(m.Help, "\n"), ", ")
|
||||
_ = m.BuildFQName() // ignore golint error
|
||||
metrics[i] = m
|
||||
}
|
||||
data := templateData{
|
||||
Metrics: metrics,
|
||||
GeneratedDate: time.Now(),
|
||||
}
|
||||
err = t.Execute(&tpl, data)
|
||||
if err != nil {
|
||||
println("err", err)
|
||||
}
|
||||
fmt.Print(tpl.String())
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type metric struct {
|
||||
Name string `yaml:"name" json:"name"`
|
||||
Subsystem string `yaml:"subsystem,omitempty" json:"subsystem,omitempty"`
|
||||
Namespace string `yaml:"namespace,omitempty" json:"namespace,omitempty"`
|
||||
Help string `yaml:"help,omitempty" json:"help,omitempty"`
|
||||
Type string `yaml:"type,omitempty" json:"type,omitempty"`
|
||||
DeprecatedVersion string `yaml:"deprecatedVersion,omitempty" json:"deprecatedVersion,omitempty"`
|
||||
StabilityLevel string `yaml:"stabilityLevel,omitempty" json:"stabilityLevel,omitempty"`
|
||||
Labels []string `yaml:"labels,omitempty" json:"labels,omitempty"`
|
||||
Buckets []float64 `yaml:"buckets,omitempty" json:"buckets,omitempty"`
|
||||
Objectives map[float64]float64 `yaml:"objectives,omitempty" json:"objectives,omitempty"`
|
||||
AgeBuckets uint32 `yaml:"ageBuckets,omitempty" json:"ageBuckets,omitempty"`
|
||||
BufCap uint32 `yaml:"bufCap,omitempty" json:"bufCap,omitempty"`
|
||||
MaxAge int64 `yaml:"maxAge,omitempty" json:"maxAge,omitempty"`
|
||||
ConstLabels map[string]string `yaml:"constLabels,omitempty" json:"constLabels,omitempty"`
|
||||
}
|
||||
|
||||
func (m metric) BuildFQName() string {
|
||||
return metrics.BuildFQName(m.Namespace, m.Subsystem, m.Name)
|
||||
}
|
||||
|
||||
type byFQName []metric
|
||||
|
||||
func (ms byFQName) Len() int { return len(ms) }
|
||||
func (ms byFQName) Less(i, j int) bool {
|
||||
if ms[i].StabilityLevel < ms[j].StabilityLevel {
|
||||
return true
|
||||
} else if ms[i].StabilityLevel > ms[j].StabilityLevel {
|
||||
return false
|
||||
}
|
||||
return ms[i].BuildFQName() < ms[j].BuildFQName()
|
||||
}
|
||||
func (ms byFQName) Swap(i, j int) {
|
||||
ms[i], ms[j] = ms[j], ms[i]
|
||||
}
|
@ -52,6 +52,15 @@ type stableMetricFinder struct {
|
||||
|
||||
var _ ast.Visitor = (*stableMetricFinder)(nil)
|
||||
|
||||
func contains(v metrics.StabilityLevel, a []metrics.StabilityLevel) bool {
|
||||
for _, i := range a {
|
||||
if i == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *stableMetricFinder) Visit(node ast.Node) (w ast.Visitor) {
|
||||
switch opts := node.(type) {
|
||||
case *ast.CallExpr:
|
||||
@ -76,15 +85,19 @@ func (f *stableMetricFinder) Visit(node ast.Node) (w ast.Visitor) {
|
||||
f.errors = append(f.errors, err)
|
||||
return nil
|
||||
}
|
||||
switch *stabilityLevel {
|
||||
case metrics.STABLE, metrics.BETA:
|
||||
classes := []metrics.StabilityLevel{metrics.STABLE, metrics.BETA}
|
||||
if ALL_STABILITY_CLASSES {
|
||||
classes = append(classes, metrics.ALPHA)
|
||||
}
|
||||
switch {
|
||||
case contains(*stabilityLevel, classes):
|
||||
if f.currentFunctionCall == nil {
|
||||
f.errors = append(f.errors, newDecodeErrorf(opts, errNotDirectCall))
|
||||
return nil
|
||||
}
|
||||
f.stableMetricsFunctionCalls = append(f.stableMetricsFunctionCalls, f.currentFunctionCall)
|
||||
f.currentFunctionCall = nil
|
||||
case metrics.INTERNAL, metrics.ALPHA:
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
default:
|
||||
|
@ -41,12 +41,15 @@ const (
|
||||
|
||||
var (
|
||||
// env configs
|
||||
GOROOT string = os.Getenv("GOROOT")
|
||||
GOOS string = os.Getenv("GOOS")
|
||||
KUBE_ROOT string = os.Getenv("KUBE_ROOT")
|
||||
GOROOT string = os.Getenv("GOROOT")
|
||||
GOOS string = os.Getenv("GOOS")
|
||||
KUBE_ROOT string = os.Getenv("KUBE_ROOT")
|
||||
ALL_STABILITY_CLASSES bool
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
flag.BoolVar(&ALL_STABILITY_CLASSES, "allstabilityclasses", false, "use this flag to enable all stability classes")
|
||||
flag.Parse()
|
||||
if len(flag.Args()) < 1 {
|
||||
fmt.Fprintf(os.Stderr, "USAGE: %s <DIR or FILE or '-'> [...]\n", os.Args[0])
|
||||
@ -86,6 +89,12 @@ func main() {
|
||||
if len(stableMetrics) == 0 {
|
||||
os.Exit(0)
|
||||
}
|
||||
for i, m := range stableMetrics {
|
||||
if m.StabilityLevel == "" {
|
||||
m.StabilityLevel = "ALPHA"
|
||||
}
|
||||
stableMetrics[i] = m
|
||||
}
|
||||
sort.Sort(byFQName(stableMetrics))
|
||||
data, err := yaml.Marshal(stableMetrics)
|
||||
if err != nil {
|
||||
|
@ -29,20 +29,20 @@ const (
|
||||
)
|
||||
|
||||
type metric struct {
|
||||
Name string `yaml:"name"`
|
||||
Subsystem string `yaml:"subsystem,omitempty"`
|
||||
Namespace string `yaml:"namespace,omitempty"`
|
||||
Help string `yaml:"help,omitempty"`
|
||||
Type string `yaml:"type,omitempty"`
|
||||
DeprecatedVersion string `yaml:"deprecatedVersion,omitempty"`
|
||||
StabilityLevel string `yaml:"stabilityLevel,omitempty"`
|
||||
Labels []string `yaml:"labels,omitempty"`
|
||||
Buckets []float64 `yaml:"buckets,omitempty"`
|
||||
Objectives map[float64]float64 `yaml:"objectives,omitempty"`
|
||||
AgeBuckets uint32 `yaml:"ageBuckets,omitempty"`
|
||||
BufCap uint32 `yaml:"bufCap,omitempty"`
|
||||
MaxAge int64 `yaml:"maxAge,omitempty"`
|
||||
ConstLabels map[string]string `yaml:"constLabels,omitempty"`
|
||||
Name string `yaml:"name" json:"name"`
|
||||
Subsystem string `yaml:"subsystem,omitempty" json:"subsystem,omitempty"`
|
||||
Namespace string `yaml:"namespace,omitempty" json:"namespace,omitempty"`
|
||||
Help string `yaml:"help,omitempty" json:"help,omitempty"`
|
||||
Type string `yaml:"type,omitempty" json:"type,omitempty"`
|
||||
DeprecatedVersion string `yaml:"deprecatedVersion,omitempty" json:"deprecatedVersion,omitempty"`
|
||||
StabilityLevel string `yaml:"stabilityLevel,omitempty" json:"stabilityLevel,omitempty"`
|
||||
Labels []string `yaml:"labels,omitempty" json:"labels,omitempty"`
|
||||
Buckets []float64 `yaml:"buckets,omitempty" json:"buckets,omitempty"`
|
||||
Objectives map[float64]float64 `yaml:"objectives,omitempty" json:"objectives,omitempty"`
|
||||
AgeBuckets uint32 `yaml:"ageBuckets,omitempty" json:"ageBuckets,omitempty"`
|
||||
BufCap uint32 `yaml:"bufCap,omitempty" json:"bufCap,omitempty"`
|
||||
MaxAge int64 `yaml:"maxAge,omitempty" json:"maxAge,omitempty"`
|
||||
ConstLabels map[string]string `yaml:"constLabels,omitempty" json:"constLabels,omitempty"`
|
||||
}
|
||||
|
||||
func (m metric) buildFQName() string {
|
||||
|
@ -105,6 +105,31 @@ kube::update::stablemetrics() {
|
||||
echo "${green}Updated golden list of stable metrics.${reset}"
|
||||
}
|
||||
|
||||
kube::update::documentation::list() {
|
||||
stability_check_setup
|
||||
temp_file=$(mktemp)
|
||||
doCheckStability=$(find_files_to_check | grep -E ".*.go" | grep -v ".*_test.go" | sort | KUBE_ROOT=${KUBE_ROOT} xargs -L 200 go run "test/instrumentation/main.go" "test/instrumentation/decode_metric.go" "test/instrumentation/find_stable_metric.go" "test/instrumentation/error.go" "test/instrumentation/metric.go" --allstabilityclasses -- 1>"${temp_file}")
|
||||
|
||||
if ! $doCheckStability; then
|
||||
echo "${red}!!! updating golden list of metrics has failed! ${reset}" >&2
|
||||
exit 1
|
||||
fi
|
||||
mv -f "$temp_file" "${KUBE_ROOT}/test/instrumentation/testdata/documentation-list.yaml"
|
||||
echo "${green}Updated list of metrics for documentation ${reset}"
|
||||
}
|
||||
|
||||
kube::update::documentation() {
|
||||
stability_check_setup
|
||||
temp_file=$(mktemp)
|
||||
doUpdateDocs=$(go run "test/instrumentation/documentation/main.go" -- 1>"${temp_file}")
|
||||
if ! $doUpdateDocs; then
|
||||
echo "${red}!!! updating documentation has failed! ${reset}" >&2
|
||||
exit 1
|
||||
fi
|
||||
mv -f "$temp_file" "${KUBE_ROOT}/test/instrumentation/testdata/documentation.md"
|
||||
echo "${green}Updated documentation of metrics.${reset}"
|
||||
}
|
||||
|
||||
kube::update::test::stablemetrics() {
|
||||
stability_check_setup
|
||||
temp_file=$(mktemp)
|
||||
|
3458
test/instrumentation/testdata/documentation-list.yaml
vendored
Normal file
3458
test/instrumentation/testdata/documentation-list.yaml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1806
test/instrumentation/testdata/documentation.md
vendored
Normal file
1806
test/instrumentation/testdata/documentation.md
vendored
Normal file
File diff suppressed because it is too large
Load Diff
28
test/instrumentation/update-documentation-metrics.sh
Executable file
28
test/instrumentation/update-documentation-metrics.sh
Executable file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2022 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This script runs to ensure that we do not violate metric stability
|
||||
# policies.
|
||||
# Usage: `test/instrumentation/test-verify.sh`.
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/../..
|
||||
source "${KUBE_ROOT}/test/instrumentation/stability-utils.sh"
|
||||
|
||||
kube::update::documentation::list
|
28
test/instrumentation/update-documentation.sh
Executable file
28
test/instrumentation/update-documentation.sh
Executable file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2022 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This script runs to ensure that we do not violate metric stability
|
||||
# policies.
|
||||
# Usage: `test/instrumentation/test-update.sh`.
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/../..
|
||||
source "${KUBE_ROOT}/test/instrumentation/stability-utils.sh"
|
||||
|
||||
kube::update::documentation
|
Loading…
Reference in New Issue
Block a user