Skip to content

Commit

Permalink
Merge pull request #148 from axif0/Backend
Browse files Browse the repository at this point in the history
Karmada Metrics Backend
  • Loading branch information
karmada-bot authored Dec 4, 2024
2 parents 732d022 + fdac128 commit 042ba69
Show file tree
Hide file tree
Showing 16 changed files with 1,426 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@ charts/*/charts
cmd/ops
.turbo
.dockerconfigjson

#local db files
*.db
*.db-journal
11 changes: 11 additions & 0 deletions cmd/metrics-scraper/app/db/consts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package db

const (
Namespace = "karmada-system"
KarmadaAgent = "karmada-agent"
KarmadaScheduler = "karmada-scheduler"
KarmadaSchedulerEstimator = "karmada-scheduler-estimator"
KarmadaControllerManager = "karmada-controller-manager"
SchedulerPort = "10351"
ControllerManagerPort = "8080"
)
23 changes: 23 additions & 0 deletions cmd/metrics-scraper/app/db/models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package db

type PodInfo struct {
Name string `json:"name"`
}

type Metric struct {
Name string `json:"name"`
Help string `json:"help"`
Type string `json:"type"`
Values []MetricValue `json:"values,omitempty"`
}

type MetricValue struct {
Labels map[string]string `json:"labels,omitempty"`
Value string `json:"value"`
Measure string `json:"measure"`
}

type ParsedData struct {
CurrentTime string `json:"currentTime"`
Metrics map[string]*Metric `json:"metrics"`
}
124 changes: 124 additions & 0 deletions cmd/metrics-scraper/app/metricsscraper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package app

import (
"context"
"fmt"
"github.com/spf13/cobra"
cliflag "k8s.io/component-base/cli/flag"
"k8s.io/klog/v2"
"os"

"github.com/karmada-io/dashboard/pkg/client"
"github.com/karmada-io/dashboard/pkg/config"
"github.com/karmada-io/dashboard/pkg/environment"
"github.com/karmada-io/karmada/pkg/sharedcli/klogflag"
"github.com/karmada-io/dashboard/cmd/metrics-scraper/app/router"
"github.com/karmada-io/dashboard/cmd/metrics-scraper/app/scrape"
"github.com/karmada-io/dashboard/cmd/metrics-scraper/app/routes/metrics"
"github.com/karmada-io/dashboard/cmd/metrics-scraper/app/options"
)

// NewMetricsScraperCommand creates a *cobra.Command object with default parameters
func NewMetricsScraperCommand(ctx context.Context) *cobra.Command {
opts := options.NewOptions()
cmd := &cobra.Command{
Use: "karmada-dashboard-metrics-scraper",
Long: `The karmada-dashboard-metrics-scraper responsible for scraping and visualizing the metrics of karmada components. `,
RunE: func(cmd *cobra.Command, args []string) error {
if err := run(ctx, opts); err != nil {
return err
}
return nil
},
Args: func(cmd *cobra.Command, args []string) error {
for _, arg := range args {
if len(arg) > 0 {
return fmt.Errorf("%q does not take any arguments, got %q", cmd.CommandPath(), args)
}
}
return nil
},
}
fss := cliflag.NamedFlagSets{}

genericFlagSet := fss.FlagSet("generic")
opts.AddFlags(genericFlagSet)

// Set klog flags
logsFlagSet := fss.FlagSet("logs")
klogflag.Add(logsFlagSet)

cmd.Flags().AddFlagSet(genericFlagSet)
cmd.Flags().AddFlagSet(logsFlagSet)
return cmd
}

func run(ctx context.Context, opts *options.Options) error {
klog.InfoS("Starting Karmada Dashboard API", "version", environment.Version)

client.InitKarmadaConfig(
client.WithUserAgent(environment.UserAgent()),
client.WithKubeconfig(opts.KarmadaKubeConfig),
client.WithKubeContext(opts.KarmadaContext),
client.WithInsecureTLSSkipVerify(opts.SkipKarmadaApiserverTLSVerify),
)

client.InitKubeConfig(
client.WithUserAgent(environment.UserAgent()),
client.WithKubeconfig(opts.KubeConfig),
client.WithKubeContext(opts.KubeContext),
client.WithInsecureTLSSkipVerify(opts.SkipKubeApiserverTLSVerify),
)
ensureAPIServerConnectionOrDie()
serve(opts)
go scrape.InitDatabase()

config.InitDashboardConfig(client.InClusterClient(), ctx.Done())
select {
case <-ctx.Done():
os.Exit(0)
}
return nil
}

func serve(opts *options.Options) {
insecureAddress := fmt.Sprintf("%s:%d", opts.InsecureBindAddress, opts.InsecurePort)
klog.V(1).InfoS("Listening and serving on", "address", insecureAddress)
go func() {
klog.Fatal(router.Router().Run(insecureAddress))
}()
}


func ensureAPIServerConnectionOrDie() {
versionInfo, err := client.InClusterClient().Discovery().ServerVersion()
if err != nil {
klog.Fatalf("Error while initializing connection to Kubernetes apiserver. "+
"This most likely means that the cluster is misconfigured. Reason: %s\n", err)
os.Exit(1)
}
klog.InfoS("Successful initial request to the Kubernetes apiserver", "version", versionInfo.String())

karmadaVersionInfo, err := client.InClusterKarmadaClient().Discovery().ServerVersion()
if err != nil {
klog.Fatalf("Error while initializing connection to Karmada apiserver. "+
"This most likely means that the cluster is misconfigured. Reason: %s\n", err)
os.Exit(1)
}
klog.InfoS("Successful initial request to the Karmada apiserver", "version", karmadaVersionInfo.String())
}

func init() {
r := router.V1()
r.GET("/metrics", metrics.GetMetrics)
r.GET("/metrics/:app_name", metrics.GetMetrics)
r.GET("/metrics/:app_name/:pod_name", metrics.QueryMetrics)
}

// http://localhost:8000/api/v1/metrics/karmada-scheduler?type=metricsdetails //from sqlite details bar

// http://localhost:8000/api/v1/metrics/karmada-scheduler/karmada-scheduler-7bd4659f9f-hh44f?type=details&mname=workqueue_queue_duration_seconds

// http://localhost:8000/api/v1/metrics?type=sync_off // to skip all metrics

// http://localhost:8000/api/v1/metrics/karmada-scheduler?type=sync_off // to skip specific metrics
47 changes: 47 additions & 0 deletions cmd/metrics-scraper/app/options/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package options

import (
"github.com/spf13/pflag"
"net"
)

// Options contains everything necessary to create and run api.
type Options struct {
BindAddress net.IP
Port int
InsecureBindAddress net.IP
InsecurePort int
KubeConfig string
KubeContext string
SkipKubeApiserverTLSVerify bool
KarmadaKubeConfig string
KarmadaContext string
SkipKarmadaApiserverTLSVerify bool
Namespace string
DisableCSRFProtection bool
OpenAPIEnabled bool
}

func NewOptions() *Options {
return &Options{}
}

// AddFlags adds flags of api to the specified FlagSet
func (o *Options) AddFlags(fs *pflag.FlagSet) {
if o == nil {
return
}
fs.IPVar(&o.BindAddress, "bind-address", net.IPv4(127, 0, 0, 1), "IP address on which to serve the --port, set to 0.0.0.0 for all interfaces")
fs.IntVar(&o.Port, "port", 8001, "secure port to listen to for incoming HTTPS requests")
fs.IPVar(&o.InsecureBindAddress, "insecure-bind-address", net.IPv4(127, 0, 0, 1), "IP address on which to serve the --insecure-port, set to 0.0.0.0 for all interfaces")
fs.IntVar(&o.InsecurePort, "insecure-port", 8000, "port to listen to for incoming HTTP requests")
fs.StringVar(&o.KubeConfig, "kubeconfig", "", "Path to the host cluster kubeconfig file.")
fs.StringVar(&o.KubeContext, "context", "", "The name of the kubeconfig context to use.")
fs.BoolVar(&o.SkipKubeApiserverTLSVerify, "skip-kube-apiserver-tls-verify", false, "enable if connection with remote Kubernetes API server should skip TLS verify")
fs.StringVar(&o.KarmadaKubeConfig, "karmada-kubeconfig", "", "Path to the karmada control plane kubeconfig file.")
fs.StringVar(&o.KarmadaContext, "karmada-context", "", "The name of the karmada-kubeconfig context to use.")
fs.BoolVar(&o.SkipKarmadaApiserverTLSVerify, "skip-karmada-apiserver-tls-verify", false, "enable if connection with remote Karmada API server should skip TLS verify")
fs.StringVar(&o.Namespace, "namespace", "karmada-dashboard", "Namespace to use when accessing Dashboard specific resources, i.e. configmap")
fs.BoolVar(&o.DisableCSRFProtection, "disable-csrf-protection", false, "allows disabling CSRF protection")
fs.BoolVar(&o.OpenAPIEnabled, "openapi-enabled", false, "enables OpenAPI v2 endpoint under '/apidocs.json'")
}
36 changes: 36 additions & 0 deletions cmd/metrics-scraper/app/router/setup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package router

import (
"github.com/gin-gonic/gin"
"github.com/karmada-io/dashboard/pkg/environment"
)

var (
router *gin.Engine
v1 *gin.RouterGroup
)

func init() {
if !environment.IsDev() {
gin.SetMode(gin.ReleaseMode)
}

router = gin.Default()
_ = router.SetTrustedProxies(nil)
v1 = router.Group("/api/v1")

router.GET("/livez", func(c *gin.Context) {
c.String(200, "livez")
})
router.GET("/readyz", func(c *gin.Context) {
c.String(200, "readyz")
})
}

func V1() *gin.RouterGroup {
return v1
}

func Router() *gin.Engine {
return router
}
44 changes: 44 additions & 0 deletions cmd/metrics-scraper/app/routes/metrics/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package metrics

import (
"net/http"
"github.com/karmada-io/dashboard/cmd/metrics-scraper/app/scrape"
"github.com/gin-gonic/gin"
)

var requests = make(chan scrape.SaveRequest)

func GetMetrics(c *gin.Context) {
appName := c.Param("app_name")
queryType := c.Query("type")

if queryType == "sync_on" || queryType == "sync_off" {
syncValue := 0
if queryType == "sync_on" {
syncValue = 1
}
scrape.HandleSyncOperation(c, appName, syncValue, queryType)
return
}

if queryType == "metricsdetails" {
QueryMetrics(c)
return
}

if queryType == "sync_status" {
scrape.CheckAppStatus(c)
return
}

allMetrics, errors, err := scrape.FetchMetrics(c.Request.Context(), appName, requests)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"errors": errors, "error": err.Error()})
return
}
if len(allMetrics) > 0 {
c.JSON(http.StatusOK, allMetrics)
} else {
c.JSON(http.StatusInternalServerError, gin.H{"error": "No metrics data found", "errors": errors})
}
}
Loading

0 comments on commit 042ba69

Please sign in to comment.