Skip to content

Commit

Permalink
Add support of checking local dashboard files (#759)
Browse files Browse the repository at this point in the history
Signed-off-by: Arnob kumar saha <[email protected]>
  • Loading branch information
ArnobKumarSaha authored Feb 27, 2024
1 parent 8da97b4 commit 6980c71
Show file tree
Hide file tree
Showing 6 changed files with 304 additions and 16 deletions.
20 changes: 13 additions & 7 deletions pkg/cmds/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ var alertLong = templates.LongDesc(`

var alertExample = templates.Examples(`
kubectl dba monitor get-alerts [DATABASE] [DATABASE_NAME] -n [NAMESPACE] \
--prom-svc=[PROM_SVC_NAME] --prom-svc-namespace=[PROM_SVC_NS] --prom-svc-port=[PROM_SVC_PORT]
--prom-svc-name=[PROM_SVC_NAME] --prom-svc-namespace=[PROM_SVC_NS] --prom-svc-port=[PROM_SVC_PORT]
# Get triggered alert for a specific mongodb
kubectl dba monitor get-alerts mongodb sample-mongodb -n demo \
Expand Down Expand Up @@ -121,11 +121,13 @@ var dashboardLong = templates.LongDesc(`
`)

var dashboardExample = templates.Examples(`
kubectl dba monitor dashboard [DATABASE] [DASHBOARD_NAME] \
--prom-svc=[PROM_SVC_NAME] --prom-svc-namespace=[PROM_SVC_NS] --prom-svc-port=[PROM_SVC_PORT]
kubectl dba monitor dashboard [DATABASE] [DATABASE_NAME] -n [NAMESPACE] \
[DASHBOARD_NAME] --file=[FILE_CONTAINING_DASHBOARD_JSON] \
--prom-svc-name=[PROM_SVC_NAME] --prom-svc-namespace=[PROM_SVC_NS] --prom-svc-port=[PROM_SVC_PORT]
# Check availability of a postgres grafana dashboard
kubectl-dba monitor dashboard postgres postgres_databases_dashboard \
kubectl-dba monitor dashboard postgres pg15 -n demo \
--file=/home/arnob/yamls/summary.json \
--prom-svc-name=prometheus-kube-prometheus-prometheus --prom-svc-namespace=monitoring --prom-svc-port=9090
Valid dashboards include:
Expand All @@ -141,20 +143,24 @@ var dashboardExample = templates.Examples(`
`)

func DashboardCMD(f cmdutil.Factory) *cobra.Command {
var branch string
var (
branch string
file string
)
cmd := &cobra.Command{
Use: "dashboard",
Short: i18n.T("Check availability of a grafana dashboard"),
Long: dashboardLong,

Run: func(cmd *cobra.Command, args []string) {
dashboard.Run(f, args, branch, prom)
dashboard.Run(f, args, branch, file, prom)
},
Example: dashboardExample,
DisableFlagsInUseLine: true,
DisableAutoGenTag: true,
}
cmd.Flags().StringVarP(&branch, "branch", "b", "master", "branch name of the github repo")
cmd.Flags().StringVarP(&file, "file", "f", "", "absolute or relative path of the file containing dashboard")
return cmd
}

Expand All @@ -165,7 +171,7 @@ var connectionLong = templates.LongDesc(`

var connectionExample = templates.Examples(`
kubectl dba monitor check-connection [DATABASE] [DATABASE_NAME] -n [NAMESPACE] \
--prom-svc=[PROM_SVC_NAME] --prom-svc-namespace=[PROM_SVC_NS] --prom-svc-port=[PROM_SVC_PORT]
--prom-svc-name=[PROM_SVC_NAME] --prom-svc-namespace=[PROM_SVC_NS] --prom-svc-port=[PROM_SVC_PORT]
# Check connection status for different targets with prometheus server for a specific postgres database
kubectl dba monitor check-connection mongodb sample_mg -n demo \
Expand Down
2 changes: 1 addition & 1 deletion pkg/monitor/connection/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (

const (
cAdvisorMetric = "container_cpu_usage_seconds_total"
kubeletMetric = "kubelet_active_pods"
kubeletMetric = "kubelet_running_pods"
ksmMetric = "kube_pod_status_phase"
nodeExporterMetric = "node_cpu_seconds_total"
)
Expand Down
34 changes: 27 additions & 7 deletions pkg/monitor/dashboard/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,36 @@ type missingOpts struct {
panelTitle []string
}

func Run(f cmdutil.Factory, args []string, branch string, prom monitor.PromSvc) {
func Run(f cmdutil.Factory, args []string, branch, file string, prom monitor.PromSvc) {
if len(args) < 2 {
log.Fatal("Enter database and grafana dashboard name as argument")
log.Fatal("Enter db object's name as an argument")
}
database := monitor.ConvertedResourceToPlural(args[0])
dbName := args[1]
namespace, _, err := f.ToRawKubeConfigLoader().Namespace()
if err != nil {
_ = fmt.Errorf("failed to get current namespace")
return
}

database := monitor.ConvertedResourceToSingular(args[0])
dashboard := args[1]

url := getURL(branch, database, dashboard)
db, err := getDB(f, database, namespace, dbName)
if err != nil {
fmt.Printf("failed to get %s database %s/%s. error %s \n", database, namespace, dbName, err.Error())
return
}

dashboardData := getDashboard(url)
database = monitor.ConvertedResourceToSingular(database)
var dashboardData map[string]interface{}
if file == "" {
if len(args) < 3 {
log.Fatal("Enter dashboard name as third argument")
}
dashboard := args[2]
url := getURL(branch, database, dashboard)
dashboardData = getDashboardFromURL(url)
} else {
dashboardData = getDashboardFromFile(file)
}

queries := parseAllExpressions(dashboardData)

Expand Down Expand Up @@ -105,6 +124,7 @@ func Run(f cmdutil.Factory, args []string, branch string, prom monitor.PromSvc)
unknown[metricName].panelTitle = uniqueAppend(unknown[metricName].panelTitle, query.panelTitle)
}
}
unknown = ignoreModeSpecificExpressions(unknown, database, db)
if len(unknown) > 0 {
fmt.Println("Missing Information:")
for metric, opts := range unknown {
Expand Down
145 changes: 145 additions & 0 deletions pkg/monitor/dashboard/elasticsearch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
Copyright AppsCode Inc. and Contributors
Licensed under the AppsCode Community License 1.0.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://github.com/appscode/licenses/raw/1.0.0/AppsCode-Community-1.0.0.md
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 dashboard

import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

func ignoreElasticsearchModeSpecificExpressions(unknown map[string]*missingOpts, db *unstructured.Unstructured) map[string]*missingOpts {
master := []string{
"kubedb_com_elasticsearch_master_node_replicas",
"kubedb_com_elasticsearch_master_node_storage_class_info",
"kubedb_com_elasticsearch_master_node_max_unavailable",
}
data := []string{
"kubedb_com_elasticsearch_data_node_replicas",
"kubedb_com_elasticsearch_data_node_storage_class_info",
"kubedb_com_elasticsearch_data_node_max_unavailable",
}
datacontent := []string{
"kubedb_com_elasticsearch_datacontent_node_replicas",
"kubedb_com_elasticsearch_datacontent_node_storage_class_info",
"kubedb_com_elasticsearch_datacontent_node_max_unavailable",
}
datahot := []string{
"kubedb_com_elasticsearch_datahot_node_replicas",
"kubedb_com_elasticsearch_datahot_node_storage_class_info",
"kubedb_com_elasticsearch_datahot_node_max_unavailable",
}
datawarm := []string{
"kubedb_com_elasticsearch_datawarm_node_replicas",
"kubedb_com_elasticsearch_datawarm_node_storage_class_info",
"kubedb_com_elasticsearch_datawarm_node_max_unavailable",
}
datacold := []string{
"kubedb_com_elasticsearch_datacold_node_replicas",
"kubedb_com_elasticsearch_datacold_node_storage_class_info",
"kubedb_com_elasticsearch_datacold_node_max_unavailable",
}
datafrozen := []string{
"kubedb_com_elasticsearch_datafrozen_node_replicas",
"kubedb_com_elasticsearch_datafrozen_node_storage_class_info",
"kubedb_com_elasticsearch_datafrozen_node_max_unavailable",
}
ingest := []string{
"kubedb_com_elasticsearch_ingest_node_replicas",
"kubedb_com_elasticsearch_ingest_node_storage_class_info",
"kubedb_com_elasticsearch_ingest_node_max_unavailable",
}
ml := []string{
"kubedb_com_elasticsearch_ml_node_replicas",
"kubedb_com_elasticsearch_ml_node_storage_class_info",
"kubedb_com_elasticsearch_ml_node_max_unavailable",
}
transform := []string{
"kubedb_com_elasticsearch_transform_node_replicas",
"kubedb_com_elasticsearch_transform_node_storage_class_info",
"kubedb_com_elasticsearch_transform_node_max_unavailable",
}
coordinating := []string{
"kubedb_com_elasticsearch_coordinating_node_replicas",
"kubedb_com_elasticsearch_coordinating_node_storage_class_info",
"kubedb_com_elasticsearch_coordinating_node_max_unavailable",
}

has := func(shared []string, expr string) bool {
for _, s := range shared {
if expr == s {
return true
}
}
return false
}

isSet := func(db *unstructured.Unstructured, typ string) bool {
spec, found, err := unstructured.NestedMap(db.Object, "spec")
if err != nil || !found {
return false
}

topo, found, err := unstructured.NestedMap(spec, "topology")
if err != nil || !found {
return false
}

typed, found, err := unstructured.NestedMap(topo, typ)
if err != nil || !found {
return false
}
return len(typed) > 0
}

ret := make(map[string]*missingOpts)
for s, o := range unknown {
if has(master, s) && !isSet(db, "master") {
continue
}
if has(data, s) && !isSet(db, "data") {
continue
}
if has(datacontent, s) && !isSet(db, "dataContent") {
continue
}
if has(datahot, s) && !isSet(db, "dataHot") {
continue
}
if has(datawarm, s) && !isSet(db, "dataWarm") {
continue
}
if has(datacold, s) && !isSet(db, "dataCold") {
continue
}
if has(datafrozen, s) && !isSet(db, "dataFrozen") {
continue
}
if has(ingest, s) && !isSet(db, "ingest") {
continue
}
if has(ml, s) && !isSet(db, "ml") {
continue
}
if has(transform, s) && !isSet(db, "transform") {
continue
}
if has(coordinating, s) && !isSet(db, "coordinating") {
continue
}
ret[s] = o
}
return ret
}
51 changes: 50 additions & 1 deletion pkg/monitor/dashboard/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,44 @@ limitations under the License.
package dashboard

import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"

api "kubedb.dev/apimachinery/apis/kubedb/v1alpha2"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
)

func getDB(f cmdutil.Factory, resource, ns, name string) (*unstructured.Unstructured, error) {
config, err := f.ToRESTConfig()
if err != nil {
return nil, err
}

dc, err := dynamic.NewForConfig(config)
if err != nil {
return nil, err
}

gvk := api.SchemeGroupVersion
dbRes := schema.GroupVersionResource{Group: gvk.Group, Version: gvk.Version, Resource: resource}
return dc.Resource(dbRes).Namespace(ns).Get(context.TODO(), name, metav1.GetOptions{})
}

func getURL(branch, database, dashboard string) string {
return fmt.Sprintf("https://raw.githubusercontent.com/appscode/grafana-dashboards/%s/%s/%s.json", branch, database, dashboard)
}

func getDashboard(url string) map[string]interface{} {
func getDashboardFromURL(url string) map[string]interface{} {
var dashboardData map[string]interface{}
response, err := http.Get(url)
if err != nil {
Expand All @@ -50,6 +76,19 @@ func getDashboard(url string) map[string]interface{} {
return dashboardData
}

func getDashboardFromFile(file string) map[string]interface{} {
body, err := os.ReadFile(file)
if err != nil {
log.Fatal("Error on ReadFile:", err)
}
var dashboardData map[string]interface{}
err = json.Unmarshal(body, &dashboardData)
if err != nil {
log.Fatal("Error unmarshalling JSON data:", err)
}
return dashboardData
}

func uniqueAppend(slice []string, valueToAdd string) []string {
for _, existingValue := range slice {
if existingValue == valueToAdd {
Expand All @@ -58,3 +97,13 @@ func uniqueAppend(slice []string, valueToAdd string) []string {
}
return append(slice, valueToAdd)
}

func ignoreModeSpecificExpressions(unknown map[string]*missingOpts, database string, db *unstructured.Unstructured) map[string]*missingOpts {
if database == api.ResourceSingularMongoDB {
return ignoreMongoDBModeSpecificExpressions(unknown, db)
}
if database == api.ResourceSingularElasticsearch {
return ignoreElasticsearchModeSpecificExpressions(unknown, db)
}
return unknown
}
Loading

0 comments on commit 6980c71

Please sign in to comment.