فهرست منبع

added endpoint for metrics get

Alexander Belanger 5 سال پیش
والد
کامیت
b4efe1a04f
5فایلهای تغییر یافته به همراه110 افزوده شده و 5 حذف شده
  1. 12 0
      internal/forms/metrics.go
  2. 3 3
      internal/kubernetes/prometheus/metrics.go
  3. 67 0
      server/api/k8s_handler.go
  4. 14 2
      server/api/release_handler.go
  5. 14 0
      server/router/router.go

+ 12 - 0
internal/forms/metrics.go

@@ -0,0 +1,12 @@
+package forms
+
+import (
+	"github.com/porter-dev/porter/internal/kubernetes/prometheus"
+)
+
+// MetricsQueryForm is the form for querying pod usage metrics (cpu, memory)
+type MetricsQueryForm struct {
+	*K8sForm
+
+	*prometheus.QueryOpts
+}

+ 3 - 3
internal/kubernetes/prometheus/metrics.go

@@ -1,4 +1,4 @@
-package metrics
+package prometheus
 
 import (
 	"context"
@@ -12,7 +12,7 @@ import (
 )
 
 // returns the prometheus service name
-func GetPrometheusService(clientset *kubernetes.Clientset) (*v1.Service, bool, error) {
+func GetPrometheusService(clientset kubernetes.Interface) (*v1.Service, bool, error) {
 	services, err := clientset.CoreV1().Services("").List(context.TODO(), metav1.ListOptions{
 		LabelSelector: "app=prometheus,component=server,heritage=Helm",
 	})
@@ -39,7 +39,7 @@ type QueryOpts struct {
 }
 
 func QueryPrometheus(
-	clientset *kubernetes.Clientset,
+	clientset kubernetes.Interface,
 	service *v1.Service,
 	opts *QueryOpts,
 ) ([]byte, error) {

+ 67 - 0
server/api/k8s_handler.go

@@ -2,6 +2,7 @@ package api
 
 import (
 	"encoding/json"
+	"fmt"
 	"net/http"
 	"net/url"
 
@@ -9,6 +10,7 @@ import (
 	"github.com/gorilla/websocket"
 	"github.com/porter-dev/porter/internal/forms"
 	"github.com/porter-dev/porter/internal/kubernetes"
+	"github.com/porter-dev/porter/internal/kubernetes/prometheus"
 	v1 "k8s.io/api/core/v1"
 )
 
@@ -321,3 +323,68 @@ func (app *App) HandleStreamControllerStatus(w http.ResponseWriter, r *http.Requ
 		return
 	}
 }
+
+func (app *App) HandleGetPodMetrics(w http.ResponseWriter, r *http.Request) {
+	vals, err := url.ParseQuery(r.URL.RawQuery)
+
+	if err != nil {
+		app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
+		return
+	}
+
+	// get the filter options
+	form := &forms.MetricsQueryForm{
+		K8sForm: &forms.K8sForm{
+			OutOfClusterConfig: &kubernetes.OutOfClusterConfig{
+				Repo:              app.Repo,
+				DigitalOceanOAuth: app.DOConf,
+			},
+		},
+		QueryOpts: &prometheus.QueryOpts{},
+	}
+
+	form.K8sForm.PopulateK8sOptionsFromQueryParams(vals, app.Repo.Cluster)
+
+	// decode from JSON to form value
+	if err := json.NewDecoder(r.Body).Decode(form.QueryOpts); err != nil {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+
+	// validate the form
+	if err := app.validator.Struct(form); err != nil {
+		app.handleErrorFormValidation(err, ErrK8sValidate, w)
+		return
+	}
+
+	// create a new agent
+	var agent *kubernetes.Agent
+
+	if app.ServerConf.IsTesting {
+		agent = app.TestAgents.K8sAgent
+	} else {
+		agent, err = kubernetes.GetAgentOutOfClusterConfig(form.OutOfClusterConfig)
+	}
+
+	// get prometheus service
+	promSvc, found, err := prometheus.GetPrometheusService(agent.Clientset)
+
+	if err != nil {
+		app.handleErrorFormValidation(err, ErrK8sValidate, w)
+		return
+	}
+
+	if !found {
+		app.handleErrorFormValidation(err, ErrK8sValidate, w)
+		return
+	}
+
+	rawQuery, err := prometheus.QueryPrometheus(agent.Clientset, promSvc, form.QueryOpts)
+
+	if err != nil {
+		app.handleErrorFormValidation(err, ErrK8sValidate, w)
+		return
+	}
+
+	fmt.Fprint(w, string(rawQuery))
+}

+ 14 - 2
server/api/release_handler.go

@@ -8,6 +8,7 @@ import (
 	"strconv"
 	"strings"
 
+	"github.com/porter-dev/porter/internal/kubernetes/prometheus"
 	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/internal/templater/parser"
 	"helm.sh/helm/v3/pkg/release"
@@ -70,7 +71,8 @@ func (app *App) HandleListReleases(w http.ResponseWriter, r *http.Request) {
 // PorterRelease is a helm release with a form attached
 type PorterRelease struct {
 	*release.Release
-	Form *models.FormYAML `json:"form"`
+	Form       *models.FormYAML `json:"form"`
+	HasMetrics bool             `json:"has_metrics"`
 }
 
 // HandleGetRelease retrieves a single release based on a name and revision
@@ -149,7 +151,7 @@ func (app *App) HandleGetRelease(w http.ResponseWriter, r *http.Request) {
 		HelmRelease:   release,
 	}
 
-	res := &PorterRelease{release, nil}
+	res := &PorterRelease{release, nil, false}
 
 	for _, file := range release.Chart.Files {
 		if strings.Contains(file.Name, "form.yaml") {
@@ -176,6 +178,16 @@ func (app *App) HandleGetRelease(w http.ResponseWriter, r *http.Request) {
 		}
 	}
 
+	// get prometheus service
+	_, found, err := prometheus.GetPrometheusService(agent.K8sAgent.Clientset)
+
+	if err != nil {
+		app.handleErrorFormValidation(err, ErrK8sValidate, w)
+		return
+	}
+
+	res.HasMetrics = found
+
 	if err := json.NewEncoder(w).Encode(res); err != nil {
 		app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
 		return

+ 14 - 0
server/router/router.go

@@ -1026,6 +1026,20 @@ func New(a *api.App) *chi.Mux {
 			),
 		)
 
+		r.Method(
+			"GET",
+			"/projects/{project_id}/k8s/metrics",
+			auth.DoesUserHaveProjectAccess(
+				auth.DoesUserHaveClusterAccess(
+					requestlog.NewHandler(a.HandleGetPodMetrics, l),
+					mw.URLParam,
+					mw.QueryParam,
+				),
+				mw.URLParam,
+				mw.ReadAccess,
+			),
+		)
+
 		r.Method(
 			"GET",
 			"/projects/{project_id}/k8s/{namespace}/pod/{name}/logs",