Skip to content

Commit

Permalink
Use gotemplates
Browse files Browse the repository at this point in the history
  • Loading branch information
richabanker committed Sep 6, 2024
1 parent 381c8c6 commit 4f8156e
Show file tree
Hide file tree
Showing 6 changed files with 241 additions and 136 deletions.
11 changes: 2 additions & 9 deletions staging/src/k8s.io/apiserver/pkg/server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ import (
"k8s.io/component-base/logs"
"k8s.io/component-base/metrics/features"
"k8s.io/component-base/metrics/prometheus/slis"
"k8s.io/component-base/statusz"
"k8s.io/component-base/tracing"
"k8s.io/component-base/zpages/statusz"
"k8s.io/klog/v2"
openapicommon "k8s.io/kube-openapi/pkg/common"
"k8s.io/kube-openapi/pkg/spec3"
Expand Down Expand Up @@ -1094,8 +1094,7 @@ func installAPI(s *GenericAPIServer, c *Config) {
}

if c.EnableMetrics {
statuzOpts := statuszOptions()
statusz.Statusz{}.Install(s.Handler.NonGoRestfulMux, statuzOpts)
statusz.Statusz{}.Install(s.Handler.NonGoRestfulMux)
if c.EnableProfiling {
routes.MetricsWithReset{}.Install(s.Handler.NonGoRestfulMux)
slis.SLIMetricsWithReset{}.Install(s.Handler.NonGoRestfulMux)
Expand Down Expand Up @@ -1188,9 +1187,3 @@ func SetHostnameFuncForTests(name string) {
return
}
}

func statuszOptions() statusz.Options {
return statusz.Options{
StartTime: time.Now(),
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ var processStartTime = NewGaugeVec(
// a prometheus registry. This metric needs to be included to ensure counter
// data fidelity.
func RegisterProcessStartTime(registrationFunc func(Registerable) error) error {
start, err := getProcessStart()
start, err := GetProcessStart()
if err != nil {
klog.Errorf("Could not get process start time, %v", err)
start = float64(time.Now().Unix())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (
"github.com/prometheus/procfs"
)

func getProcessStart() (float64, error) {
func GetProcessStart() (float64, error) {
pid := os.Getpid()
p, err := procfs.NewProc(pid)
if err != nil {
Expand Down
101 changes: 63 additions & 38 deletions staging/src/k8s.io/component-base/zpages/statusz/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,53 +17,78 @@ limitations under the License.
package statusz

import (
"encoding/json"
"fmt"
"net/http"
"sync"
"time"

"github.com/blang/semver/v4"
"k8s.io/component-base/version"
"k8s.io/klog/v2"

utilversion "k8s.io/apiserver/pkg/util/version"
compbasemetrics "k8s.io/component-base/metrics"
)

type Options struct {
StartTime time.Time
GoVersion string
BinaryVersion string
CompatibilityVersion string
type statuszRegistry interface {
processStartTime() time.Time
goVersion() string
binaryVersion() semver.Version
emulationVersion() semver.Version
minCompatibilityVersion() semver.Version
usefulLinks() map[string]string
}

type registry struct{}

func (*registry) processStartTime() time.Time {
var processStartTime time.Time
start, err := compbasemetrics.GetProcessStart()
if err != nil {
klog.Errorf("Could not get process start time, %v", err)
processStartTime = time.Unix(int64(start), 0)
}

return processStartTime
}

func (*registry) goVersion() string {
return version.Get().GoVersion
}

func (*registry) binaryVersion() semver.Version {
var binaryVersion semver.Version
binaryVersion, err := semver.ParseTolerant(utilversion.DefaultComponentGlobalsRegistry.EffectiveVersionFor(utilversion.DefaultKubeComponent).BinaryVersion().String())
if err != nil {
klog.Errorf("err parsing binary version: %v", err)
}

return binaryVersion
}

type statuszRegistry struct {
lock sync.Mutex
options Options
func (*registry) emulationVersion() semver.Version {
var emulationVersion semver.Version
emulationVersion, err := semver.ParseTolerant(utilversion.DefaultComponentGlobalsRegistry.EffectiveVersionFor(utilversion.DefaultKubeComponent).EmulationVersion().String())
if err != nil {
klog.Infof("err parsing emulationVersion version: %v", err)
}

return emulationVersion
}

func (reg *statuszRegistry) handleStatusz() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
statuszData := reg.populateStatuszData()
jsonData, err := json.MarshalIndent(statuszData, "", " ")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
fmt.Fprint(w, string(jsonData))
func (*registry) minCompatibilityVersion() semver.Version {
var minCompatVersion semver.Version
minCompatVersion, err := semver.ParseTolerant(utilversion.DefaultComponentGlobalsRegistry.EffectiveVersionFor(utilversion.DefaultKubeComponent).MinCompatibilityVersion().String())
if err != nil {
klog.Infof("err parsing min compatibility version: %v", err)
}

return minCompatVersion
}

func (reg *statuszRegistry) populateStatuszData() StatuszResponse {
uptime := int(time.Since(reg.options.StartTime).Seconds())

return StatuszResponse{
StartTime: reg.options.StartTime.String(),
Uptime: fmt.Sprintf("%d hr %02d min %02d sec",
uptime/3600, (uptime/60)%60, uptime%60),
GoVersion: reg.options.GoVersion,
BinaryVersion: reg.options.BinaryVersion,
CompatibilityVersion: reg.options.CompatibilityVersion,
UsefulLinks: map[string]string{
"healthz": "/healthz",
"livez": "/livez",
"readyz": "/readyz",
"metrics": "/metrics",
},
func (*registry) usefulLinks() map[string]string {
return map[string]string{
"healthz": "/healthz",
"livez": "/livez",
"readyz": "/readyz",
"metrics": "/metrics",
"sli metrics": "/metrics/slis",
}
}
133 changes: 102 additions & 31 deletions staging/src/k8s.io/component-base/zpages/statusz/statusz.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,53 +17,124 @@ limitations under the License.
package statusz

import (
"bytes"
"fmt"
"html/template"
"net/http"
"strings"
"sync"
"time"

"k8s.io/apiserver/pkg/endpoints/metrics"
"k8s.io/klog/v2"
)

type Statusz struct {
registry *statuszRegistry
}
var (
installOnce = sync.Once{}
funcMap = template.FuncMap{
"ToLower": strings.ToLower,
}
reg statuszRegistry = &registry{}
)

const (
templ = `
----------------------------
title: Kubernetes Statusz
content_type: reference
auto_generated: true
description: Details of the status data that Kubernetes components report.
----------------------------
## Started: {{.StartTime}}
## Up: {{.Uptime}}
## Build Info
--------------
### Go version: {{.GoVersion}}
### Binary version: {{.BinaryVersion}}
### Emulation version: {{.EmulationVersion}}
### Minimum Compatibility version: {{.MinCompatibilityVersion}}
## List of useful endpoints
--------------
{{- range $name, $link := .UsefulLinks}}
{{$name}}:{{$link -}}
{{- end }}
`
)

type StatuszResponse struct {
StartTime string
Uptime string
GoVersion string
BinaryVersion string
CompatibilityVersion string
UsefulLinks map[string]string
type templateData struct {
StartTime string
Uptime string
GoVersion string
BinaryVersion string
EmulationVersion string
MinCompatibilityVersion string
UsefulLinks map[string]string
}

type Statusz struct{}

type mux interface {
Handle(path string, handler http.Handler)
}

func (f Statusz) Install(m mux, opts Options) {
f.registry = register(opts)
f.registry.installHandler(m)
func (f Statusz) Install(m mux) {
installOnce.Do(func() {
m.Handle("/statusz",
metrics.InstrumentHandlerFunc("GET",
/* group = */ "",
/* version = */ "",
/* resource = */ "",
/* subresource = */ "/statusz",
/* scope = */ "",
/* component = */ "",
/* deprecated */ false,
/* removedRelease */ "",
handleStatusz()))
})
}

func handleStatusz() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
statuszData, err := populateStatuszData()
if err != nil {
klog.Errorf("error while populating statusz data: %v", err)
http.Error(w, "error while populating statusz data", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
fmt.Fprint(w, statuszData)
}
}

func register(opts Options) *statuszRegistry {
registry := &statuszRegistry{
options: opts,
func populateStatuszData() (string, error) {
t := template.New("t").Funcs(funcMap)
t, err := t.Parse(templ)
if err != nil {
return "", err
}

var tpl bytes.Buffer
data := templateData{
StartTime: reg.processStartTime().Format(time.UnixDate),
Uptime: uptime(int64(reg.processStartTime().Second())),
GoVersion: reg.goVersion(),
BinaryVersion: reg.binaryVersion().String(),
EmulationVersion: reg.emulationVersion().String(),
MinCompatibilityVersion: reg.minCompatibilityVersion().String(),
UsefulLinks: reg.usefulLinks(),
}
err = t.Execute(&tpl, data)
if err != nil {
return "", err
}

return registry
return tpl.String(), nil
}

func (reg *statuszRegistry) installHandler(m mux) {
reg.lock.Lock()
defer reg.lock.Unlock()
m.Handle("/statusz",
metrics.InstrumentHandlerFunc("GET",
/* group = */ "",
/* version = */ "",
/* resource = */ "",
/* subresource = */ "/statusz",
/* scope = */ "",
/* component = */ "",
/* deprecated */ false,
/* removedRelease */ "",
reg.handleStatusz()))
func uptime(t int64) string {
return fmt.Sprintf("%d hr %02d min %02d sec",
t/3600, (t/60)%60, t%60)
}
Loading

0 comments on commit 4f8156e

Please sign in to comment.