Merge pull request #113343 from logicalhan/doc

Add support to metrics framework to auto-generate documentation for metrics
This commit is contained in:
Kubernetes Prow Robot 2022-10-26 07:34:51 -07:00 committed by GitHub
commit 9acd38e07d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 5556 additions and 20 deletions

View File

@ -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
```

View 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]
}

View File

@ -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:

View File

@ -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 {

View File

@ -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 {

View File

@ -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)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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

View 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