mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 02:41:25 +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
|
```console
|
||||||
./test/instrumentation/test-update.sh
|
./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)
|
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) {
|
func (f *stableMetricFinder) Visit(node ast.Node) (w ast.Visitor) {
|
||||||
switch opts := node.(type) {
|
switch opts := node.(type) {
|
||||||
case *ast.CallExpr:
|
case *ast.CallExpr:
|
||||||
@ -76,15 +85,19 @@ func (f *stableMetricFinder) Visit(node ast.Node) (w ast.Visitor) {
|
|||||||
f.errors = append(f.errors, err)
|
f.errors = append(f.errors, err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
switch *stabilityLevel {
|
classes := []metrics.StabilityLevel{metrics.STABLE, metrics.BETA}
|
||||||
case metrics.STABLE, metrics.BETA:
|
if ALL_STABILITY_CLASSES {
|
||||||
|
classes = append(classes, metrics.ALPHA)
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case contains(*stabilityLevel, classes):
|
||||||
if f.currentFunctionCall == nil {
|
if f.currentFunctionCall == nil {
|
||||||
f.errors = append(f.errors, newDecodeErrorf(opts, errNotDirectCall))
|
f.errors = append(f.errors, newDecodeErrorf(opts, errNotDirectCall))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
f.stableMetricsFunctionCalls = append(f.stableMetricsFunctionCalls, f.currentFunctionCall)
|
f.stableMetricsFunctionCalls = append(f.stableMetricsFunctionCalls, f.currentFunctionCall)
|
||||||
f.currentFunctionCall = nil
|
f.currentFunctionCall = nil
|
||||||
case metrics.INTERNAL, metrics.ALPHA:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -44,9 +44,12 @@ var (
|
|||||||
GOROOT string = os.Getenv("GOROOT")
|
GOROOT string = os.Getenv("GOROOT")
|
||||||
GOOS string = os.Getenv("GOOS")
|
GOOS string = os.Getenv("GOOS")
|
||||||
KUBE_ROOT string = os.Getenv("KUBE_ROOT")
|
KUBE_ROOT string = os.Getenv("KUBE_ROOT")
|
||||||
|
ALL_STABILITY_CLASSES bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
|
flag.BoolVar(&ALL_STABILITY_CLASSES, "allstabilityclasses", false, "use this flag to enable all stability classes")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if len(flag.Args()) < 1 {
|
if len(flag.Args()) < 1 {
|
||||||
fmt.Fprintf(os.Stderr, "USAGE: %s <DIR or FILE or '-'> [...]\n", os.Args[0])
|
fmt.Fprintf(os.Stderr, "USAGE: %s <DIR or FILE or '-'> [...]\n", os.Args[0])
|
||||||
@ -86,6 +89,12 @@ func main() {
|
|||||||
if len(stableMetrics) == 0 {
|
if len(stableMetrics) == 0 {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
for i, m := range stableMetrics {
|
||||||
|
if m.StabilityLevel == "" {
|
||||||
|
m.StabilityLevel = "ALPHA"
|
||||||
|
}
|
||||||
|
stableMetrics[i] = m
|
||||||
|
}
|
||||||
sort.Sort(byFQName(stableMetrics))
|
sort.Sort(byFQName(stableMetrics))
|
||||||
data, err := yaml.Marshal(stableMetrics)
|
data, err := yaml.Marshal(stableMetrics)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -29,20 +29,20 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type metric struct {
|
type metric struct {
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name" json:"name"`
|
||||||
Subsystem string `yaml:"subsystem,omitempty"`
|
Subsystem string `yaml:"subsystem,omitempty" json:"subsystem,omitempty"`
|
||||||
Namespace string `yaml:"namespace,omitempty"`
|
Namespace string `yaml:"namespace,omitempty" json:"namespace,omitempty"`
|
||||||
Help string `yaml:"help,omitempty"`
|
Help string `yaml:"help,omitempty" json:"help,omitempty"`
|
||||||
Type string `yaml:"type,omitempty"`
|
Type string `yaml:"type,omitempty" json:"type,omitempty"`
|
||||||
DeprecatedVersion string `yaml:"deprecatedVersion,omitempty"`
|
DeprecatedVersion string `yaml:"deprecatedVersion,omitempty" json:"deprecatedVersion,omitempty"`
|
||||||
StabilityLevel string `yaml:"stabilityLevel,omitempty"`
|
StabilityLevel string `yaml:"stabilityLevel,omitempty" json:"stabilityLevel,omitempty"`
|
||||||
Labels []string `yaml:"labels,omitempty"`
|
Labels []string `yaml:"labels,omitempty" json:"labels,omitempty"`
|
||||||
Buckets []float64 `yaml:"buckets,omitempty"`
|
Buckets []float64 `yaml:"buckets,omitempty" json:"buckets,omitempty"`
|
||||||
Objectives map[float64]float64 `yaml:"objectives,omitempty"`
|
Objectives map[float64]float64 `yaml:"objectives,omitempty" json:"objectives,omitempty"`
|
||||||
AgeBuckets uint32 `yaml:"ageBuckets,omitempty"`
|
AgeBuckets uint32 `yaml:"ageBuckets,omitempty" json:"ageBuckets,omitempty"`
|
||||||
BufCap uint32 `yaml:"bufCap,omitempty"`
|
BufCap uint32 `yaml:"bufCap,omitempty" json:"bufCap,omitempty"`
|
||||||
MaxAge int64 `yaml:"maxAge,omitempty"`
|
MaxAge int64 `yaml:"maxAge,omitempty" json:"maxAge,omitempty"`
|
||||||
ConstLabels map[string]string `yaml:"constLabels,omitempty"`
|
ConstLabels map[string]string `yaml:"constLabels,omitempty" json:"constLabels,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m metric) buildFQName() string {
|
func (m metric) buildFQName() string {
|
||||||
|
@ -105,6 +105,31 @@ kube::update::stablemetrics() {
|
|||||||
echo "${green}Updated golden list of stable metrics.${reset}"
|
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() {
|
kube::update::test::stablemetrics() {
|
||||||
stability_check_setup
|
stability_check_setup
|
||||||
temp_file=$(mktemp)
|
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