Skip to content

Commit

Permalink
metrics: add /metrics endpoint and console_helm_install_count metric
Browse files Browse the repository at this point in the history
Works with openshift/console-operator#601 to add
a console_helm_install_count counter vector metric. This change will
increment the counter each time a user installs a Helm chart in the
console.

Closes: https://issues.redhat.com/browse/HELM-235
Signed-off-by: Allen Bai <abai@redhat.com>
  • Loading branch information
Allen Bai committed Oct 6, 2021
1 parent 92c113f commit 1806dba
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 3 deletions.
4 changes: 4 additions & 0 deletions pkg/helm/actions/install_chart.go
@@ -1,6 +1,7 @@
package actions

import (
"github.com/openshift/console/pkg/helm/metrics"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chart/loader"
Expand Down Expand Up @@ -40,5 +41,8 @@ func InstallChart(ns, name, url string, vals map[string]interface{}, conf *actio
if err != nil {
return nil, err
}

metrics.HandleConsoleHelmInstallCount(ch.Metadata.Name, ch.Metadata.Version)

return release, nil
}
34 changes: 34 additions & 0 deletions pkg/helm/metrics/metrics.go
@@ -0,0 +1,34 @@
package metrics

import (
"github.com/prometheus/client_golang/prometheus"
"k8s.io/klog/v2"
)

var (
consoleHelmInstallCount = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "console_helm_install_count",
Help: "Number of Helm installation from console.",
},
[]string{"console_helm_chart_name", "console_helm_chart_version"},
)
)

func init() {
prometheus.MustRegister(consoleHelmInstallCount)
}

func HandleConsoleHelmInstallCount(chartName, chartVersion string) {
defer recoverMetricPanic()

klog.Infof("metric console_helm_install_count: %s %s", chartName, chartVersion)
consoleHelmInstallCount.WithLabelValues(chartName, chartVersion).Add(1)
}

// Reference: https://github.com/openshift/console-operator/blob/master/pkg/console/metrics/metrics.go#L80
func recoverMetricPanic() {
if r := recover(); r != nil {
klog.Errorf("Recovering from metric function - %v", r)
}
}
98 changes: 98 additions & 0 deletions pkg/helm/metrics/metrics_test.go
@@ -0,0 +1,98 @@
package metrics

import (
"bufio"
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
"testing"

"github.com/prometheus/client_golang/prometheus/promhttp"
)

const (
consoleHelmChartName = "console_helm_chart_name"
)

func TestHandleConsoleHelmInstallCountNoRelease(t *testing.T) {
ts := httptest.NewServer(promhttp.Handler())

defer ts.Close()

res, err := http.Get(ts.URL + "/metrics")
if err != nil {
t.Errorf("http error: %s", err)
}

if !httpOK(res) {
t.Errorf("http error: %d %s", res.StatusCode, http.StatusText(res.StatusCode))
}

defer res.Body.Close()

bytes, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Fatalf("read error: %s", err)
}

found := findLineInResponse(t, string(bytes), consoleHelmChartName)
if found {
t.Error("console_helm_chart_name should not be available")
}
}

func TestHandleConsoleHelmInstallCountSingleRelease(t *testing.T) {
ts := httptest.NewServer(promhttp.Handler())

defer ts.Close()

chartName, chartVersion := "test-chart", "0.0.1"
HandleConsoleHelmInstallCount(chartName, chartVersion)

res, err := http.Get(ts.URL + "/metrics")
if err != nil {
t.Errorf("http error: %s", err)
}

if !httpOK(res) {
t.Errorf("http error: %d %s", res.StatusCode, http.StatusText(res.StatusCode))
}

defer res.Body.Close()

bytes, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Errorf("read error: %s", err)
}

found := findLineInResponse(t, string(bytes), consoleHelmChartName)
if !found {
t.Error("console_helm_chart_name should be available")
}
}

// Reference: https://github.com/openshift/console-operator/blob/master/test/e2e/metrics_test.go#L71
func findLineInResponse(t *testing.T, haystack, needle string) (found bool) {
scanner := bufio.NewScanner(strings.NewReader(haystack))
for scanner.Scan() {
text := scanner.Text()
// skip comments
if strings.HasPrefix(text, "#") {
continue
}
found := strings.Contains(text, needle)
if found {
t.Logf("found %s\n", scanner.Text())
return true
}
}
return false
}

func httpOK(resp *http.Response) bool {
if resp.StatusCode >= 200 && resp.StatusCode <= 299 {
return true
}
return false
}
6 changes: 3 additions & 3 deletions pkg/proxy/proxy.go
Expand Up @@ -229,17 +229,17 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
CheckOrigin: func(r *http.Request) bool {
origin := r.Header["Origin"]
if p.config.Origin == "" {
log.Printf("CheckOrigin: Proxy has no configured Origin. Allowing origin %v to %v", origin, r.URL)
// log.Printf("CheckOrigin: Proxy has no configured Origin. Allowing origin %v to %v", origin, r.URL)
return true
}
if len(origin) == 0 {
log.Printf("CheckOrigin: No origin header. Denying request to %v", r.URL)
// log.Printf("CheckOrigin: No origin header. Denying request to %v", r.URL)
return false
}
if p.config.Origin == origin[0] {
return true
}
log.Printf("CheckOrigin '%v' != '%v'", p.config.Origin, origin[0])
// log.Printf("CheckOrigin '%v' != '%v'", p.config.Origin, origin[0])
return false
},
}
Expand Down
7 changes: 7 additions & 0 deletions pkg/server/middleware.go
Expand Up @@ -23,6 +23,13 @@ func authMiddleware(a *auth.Authenticator, hdlr http.HandlerFunc) http.Handler {

func authMiddlewareWithUser(a *auth.Authenticator, handlerFunc func(user *auth.User, w http.ResponseWriter, r *http.Request)) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Requests from prometheus-k8s have the access token in headers instead of cookies.
// This allows metric requests with proper tokens in either headers or cookies.
if r.URL.Path == "/metrics" {
openshiftSessionCookieName := "openshift-session-token"
openshiftSessionCookieValue := r.Header.Get("Authorization")
r.AddCookie(&http.Cookie{Name: openshiftSessionCookieName, Value: openshiftSessionCookieValue})
}
user, err := a.Authenticate(r)
if err != nil {
klog.V(4).Infof("authentication failed: %v", err)
Expand Down
5 changes: 5 additions & 0 deletions pkg/server/server.go
Expand Up @@ -17,6 +17,7 @@ import (
"time"

"github.com/coreos/pkg/health"
"github.com/prometheus/client_golang/prometheus/promhttp"

"github.com/openshift/console/pkg/auth"
"github.com/openshift/console/pkg/graphql/resolver"
Expand Down Expand Up @@ -470,6 +471,10 @@ func (s *Server) HTTPHandler() http.Handler {
})

// Helm Endpoints
handle("/metrics", authHandler(func(w http.ResponseWriter, r *http.Request) {
promhttp.Handler().ServeHTTP(w, r)
}))

handle("/api/helm/template", authHandlerWithUser(helmHandlers.HandleHelmRenderManifests))
handle("/api/helm/releases", authHandlerWithUser(helmHandlers.HandleHelmList))
handle("/api/helm/chart", authHandlerWithUser(helmHandlers.HandleChartGet))
Expand Down

0 comments on commit 1806dba

Please sign in to comment.