Merge pull request #4446 from zhu733756/master

Support importing Grafana templates to the workspace level.
This commit is contained in:
KubeSphere CI Bot
2022-01-17 15:59:36 +08:00
committed by GitHub
10 changed files with 112 additions and 58 deletions

View File

@@ -216,8 +216,8 @@ func (s *APIServer) installKubeSphereAPIs() {
urlruntime.Must(configv1alpha2.AddToContainer(s.container, s.Config))
urlruntime.Must(resourcev1alpha3.AddToContainer(s.container, s.InformerFactory, s.RuntimeCache))
urlruntime.Must(monitoringv1alpha3.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.MonitoringClient, s.MetricsClient, s.InformerFactory, s.KubernetesClient.KubeSphere(), s.Config.OpenPitrixOptions))
urlruntime.Must(meteringv1alpha1.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.MonitoringClient, s.InformerFactory, s.KubernetesClient.KubeSphere(), s.RuntimeCache, s.Config.MeteringOptions, nil))
urlruntime.Must(monitoringv1alpha3.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.MonitoringClient, s.MetricsClient, s.InformerFactory, s.KubernetesClient.KubeSphere(), s.Config.OpenPitrixOptions, s.RuntimeClient))
urlruntime.Must(meteringv1alpha1.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.MonitoringClient, s.InformerFactory, s.KubernetesClient.KubeSphere(), s.RuntimeCache, s.Config.MeteringOptions, nil, s.RuntimeClient))
urlruntime.Must(openpitrixv1.AddToContainer(s.container, s.InformerFactory, s.KubernetesClient.KubeSphere(), s.Config.OpenPitrixOptions))
urlruntime.Must(openpitrixv2alpha1.AddToContainer(s.container, s.InformerFactory, s.KubernetesClient.KubeSphere(), s.Config.OpenPitrixOptions))
urlruntime.Must(operationsv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes()))

View File

@@ -24,6 +24,8 @@ import (
openpitrixoptions "kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned"
"kubesphere.io/kubesphere/pkg/informers"
monitorhle "kubesphere.io/kubesphere/pkg/kapis/monitoring/v1alpha3"
@@ -45,6 +47,6 @@ type meterHandler interface {
HandlePVCMeterQuery(req *restful.Request, resp *restful.Response)
}
func newHandler(k kubernetes.Interface, m monitoring.Interface, f informers.InformerFactory, ksClient versioned.Interface, resourceGetter *resourcev1alpha3.ResourceGetter, meteringOptions *meteringclient.Options, opOptions *openpitrixoptions.Options) meterHandler {
return monitorhle.NewHandler(k, m, nil, f, ksClient, resourceGetter, meteringOptions, opOptions)
func newHandler(k kubernetes.Interface, m monitoring.Interface, f informers.InformerFactory, ksClient versioned.Interface, resourceGetter *resourcev1alpha3.ResourceGetter, meteringOptions *meteringclient.Options, opOptions *openpitrixoptions.Options, rtClient runtimeclient.Client) meterHandler {
return monitorhle.NewHandler(k, m, nil, f, ksClient, resourceGetter, meteringOptions, opOptions, rtClient)
}

View File

@@ -30,6 +30,8 @@ import (
"k8s.io/client-go/kubernetes"
"sigs.k8s.io/controller-runtime/pkg/cache"
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
@@ -47,10 +49,10 @@ const (
var GroupVersion = schema.GroupVersion{Group: groupName, Version: "v1alpha1"}
func AddToContainer(c *restful.Container, k8sClient kubernetes.Interface, meteringClient monitoring.Interface, factory informers.InformerFactory, ksClient versioned.Interface, cache cache.Cache, meteringOptions *meteringclient.Options, opOptions *openpitrixoptions.Options) error {
func AddToContainer(c *restful.Container, k8sClient kubernetes.Interface, meteringClient monitoring.Interface, factory informers.InformerFactory, ksClient versioned.Interface, cache cache.Cache, meteringOptions *meteringclient.Options, opOptions *openpitrixoptions.Options, rtClient runtimeclient.Client) error {
ws := runtime.NewWebService(GroupVersion)
h := newHandler(k8sClient, meteringClient, factory, ksClient, resourcev1alpha3.NewResourceGetter(factory, cache), meteringOptions, opOptions)
h := newHandler(k8sClient, meteringClient, factory, ksClient, resourcev1alpha3.NewResourceGetter(factory, cache), meteringOptions, opOptions, rtClient)
ws.Route(ws.GET("/cluster").
To(h.HandleClusterMeterQuery).

View File

@@ -1,3 +0,0 @@
#! /bin/bash
curl -d '{"grafanaDashboardUrl":"https://grafana.com/api/dashboards/7362/revisions/5/download", "description":"this is a test dashboard."}' -H "Content-Type: application/json" localhost:9090/kapis/monitoring.kubesphere.io/v1alpha3/clusterdashboards/test1/template

View File

@@ -20,7 +20,6 @@ package v1alpha3
import (
"context"
"encoding/json"
"errors"
"io/ioutil"
"net/http"
@@ -41,6 +40,7 @@ import (
"github.com/emicklei/go-restful"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
monitoringdashboardv1alpha2 "kubesphere.io/monitoring-dashboard/api/v1alpha2"
@@ -57,9 +57,10 @@ type handler struct {
mo model.MonitoringOperator
opRelease openpitrix.ReleaseInterface
meteringOptions *meteringclient.Options
rtClient runtimeclient.Client
}
func NewHandler(k kubernetes.Interface, monitoringClient monitoring.Interface, metricsClient monitoring.Interface, f informers.InformerFactory, ksClient versioned.Interface, resourceGetter *resourcev1alpha3.ResourceGetter, meteringOptions *meteringclient.Options, opOptions *openpitrixoptions.Options) *handler {
func NewHandler(k kubernetes.Interface, monitoringClient monitoring.Interface, metricsClient monitoring.Interface, f informers.InformerFactory, ksClient versioned.Interface, resourceGetter *resourcev1alpha3.ResourceGetter, meteringOptions *meteringclient.Options, opOptions *openpitrixoptions.Options, rtClient runtimeclient.Client) *handler {
var opRelease openpitrix.Interface
var s3Client s3.Interface
if opOptions != nil && opOptions.S3Options != nil && len(opOptions.S3Options.Endpoint) != 0 {
@@ -81,6 +82,7 @@ func NewHandler(k kubernetes.Interface, monitoringClient monitoring.Interface, m
mo: model.NewMonitoringOperator(monitoringClient, metricsClient, k, f, resourceGetter, opRelease),
opRelease: opRelease,
meteringOptions: meteringOptions,
rtClient: rtClient,
}
}
@@ -345,6 +347,7 @@ func (h handler) handleGrafanaDashboardImport(req *restful.Request, resp *restfu
}
grafanaDashboardName := req.PathParameter("grafanaDashboardName")
namespace := req.PathParameter("namespace")
if grafanaDashboardName == "" {
err := errors.New("the requested parameter grafanaDashboardName cannot be empty")
@@ -357,6 +360,7 @@ func (h handler) handleGrafanaDashboardImport(req *restful.Request, resp *restfu
return
}
// download the Grafana dashboard
grafanaDashboardContent := []byte(entity.GrafanaDashboardContent)
if entity.GrafanaDashboardUrl != "" {
c, err := func(u string) ([]byte, error) {
@@ -392,57 +396,89 @@ func (h handler) handleGrafanaDashboardImport(req *restful.Request, resp *restfu
grafanaDashboardContent = []byte(c)
}
isClusterCrd := namespace == ""
c := converter.NewConverter()
convertedDashboard, err := c.ConvertToDashboard(grafanaDashboardContent, true, "", grafanaDashboardName)
convertedDashboard, err := c.ConvertToDashboard(grafanaDashboardContent, isClusterCrd, namespace, grafanaDashboardName)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
ctx := context.TODO()
annotation := map[string]string{"kubesphere.io/description": entity.Description}
dashboard := monitoringdashboardv1alpha2.ClusterDashboard{
TypeMeta: v1.TypeMeta{
APIVersion: convertedDashboard.APIVersion,
Kind: convertedDashboard.Kind,
},
ObjectMeta: v1.ObjectMeta{
Name: convertedDashboard.Metadata["name"],
Annotations: annotation,
},
Spec: *convertedDashboard.Spec,
// a cluster scope dashboard or a namespaced dashboard with the same name cannot post.
if isClusterCrd {
clusterdashboard := monitoringdashboardv1alpha2.ClusterDashboard{
TypeMeta: v1.TypeMeta{
APIVersion: convertedDashboard.APIVersion,
Kind: convertedDashboard.Kind,
},
ObjectMeta: v1.ObjectMeta{
Name: convertedDashboard.Metadata["name"],
Annotations: annotation,
},
Spec: *convertedDashboard.Spec,
}
objKey := runtimeclient.ObjectKey{
Namespace: "",
Name: clusterdashboard.Name,
}
err = h.rtClient.Get(ctx, objKey, &clusterdashboard)
if err == nil {
api.HandleBadRequest(resp, nil, errors.New("dashboards with the same name already exists."))
return
}
// create this dashboard
err = h.rtClient.Create(ctx, &clusterdashboard)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
resp.WriteAsJson(clusterdashboard)
} else {
dashboard := monitoringdashboardv1alpha2.Dashboard{
TypeMeta: v1.TypeMeta{
APIVersion: convertedDashboard.APIVersion,
Kind: convertedDashboard.Kind,
},
ObjectMeta: v1.ObjectMeta{
Name: convertedDashboard.Metadata["name"],
Namespace: namespace,
Annotations: annotation,
},
Spec: *convertedDashboard.Spec,
}
objKey := runtimeclient.ObjectKey{
Namespace: namespace,
Name: dashboard.Name,
}
err = h.rtClient.Get(ctx, objKey, &dashboard)
if err == nil {
api.HandleBadRequest(resp, nil, errors.New("dashboards with the same name already exists."))
return
}
// create this dashboard
err = h.rtClient.Create(ctx, &dashboard)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
resp.WriteAsJson(dashboard)
}
jsonDashbaord, err := json.Marshal(dashboard)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
// a dashboard with the same name cannot post.
ctx := context.TODO()
_, err = h.k.Discovery().RESTClient().
Get().
AbsPath("/apis/monitoring.kubesphere.io/v1alpha2/clusterdashboards/" + dashboard.Name).
DoRaw(ctx)
if err == nil {
api.HandleBadRequest(resp, nil, errors.New("a dashboard with the same name already exists."))
return
}
// create this dashboard
_, err = h.k.Discovery().RESTClient().
Post().
AbsPath("/apis/monitoring.kubesphere.io/v1alpha2/clusterdashboards").
Body(jsonDashbaord).
DoRaw(ctx)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
resp.WriteAsJson(dashboard)
}

View File

@@ -373,7 +373,7 @@ func TestParseRequestParams(t *testing.T) {
fakeInformerFactory.KubeSphereSharedInformerFactory()
handler := NewHandler(client, nil, nil, fakeInformerFactory, ksClient, nil, nil, nil)
handler := NewHandler(client, nil, nil, fakeInformerFactory, ksClient, nil, nil, nil, nil)
result, err := handler.makeQueryOptions(tt.params, tt.lvl)
if err != nil {

View File

@@ -36,6 +36,8 @@ import (
"kubesphere.io/kubesphere/pkg/informers"
model "kubesphere.io/kubesphere/pkg/models/monitoring"
"kubesphere.io/kubesphere/pkg/simple/client/monitoring"
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
)
const (
@@ -45,10 +47,10 @@ const (
var GroupVersion = schema.GroupVersion{Group: groupName, Version: "v1alpha3"}
func AddToContainer(c *restful.Container, k8sClient kubernetes.Interface, monitoringClient monitoring.Interface, metricsClient monitoring.Interface, factory informers.InformerFactory, ksClient versioned.Interface, opOptions *openpitrixoptions.Options) error {
func AddToContainer(c *restful.Container, k8sClient kubernetes.Interface, monitoringClient monitoring.Interface, metricsClient monitoring.Interface, factory informers.InformerFactory, ksClient versioned.Interface, opOptions *openpitrixoptions.Options, rtClient runtimeclient.Client) error {
ws := runtime.NewWebService(GroupVersion)
h := NewHandler(k8sClient, monitoringClient, metricsClient, factory, ksClient, nil, nil, opOptions)
h := NewHandler(k8sClient, monitoringClient, metricsClient, factory, ksClient, nil, nil, opOptions, rtClient)
ws.Route(ws.GET("/kubesphere").
To(h.handleKubeSphereMetricsQuery).
@@ -555,6 +557,16 @@ func AddToContainer(c *restful.Container, k8sClient kubernetes.Interface, monito
Returns(http.StatusOK, respOK, monitoringdashboardv1alpha2.ClusterDashboard{})).
Produces(restful.MIME_JSON)
ws.Route(ws.POST("/namespaces/{namespace}/dashboards/{grafanaDashboardName}/template").
To(h.handleGrafanaDashboardImport).
Doc("Convert Grafana templates to KubeSphere dashboards.").
Param(ws.PathParameter("grafanaDashboardName", "The name of the Grafana template to be converted").DataType("string").Required(true)).
Param(ws.PathParameter("namespace", "The name of the project").DataType("string").Required(true)).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.DashboardTag}).
Writes(monitoringdashboardv1alpha2.Dashboard{}).
Returns(http.StatusOK, respOK, monitoringdashboardv1alpha2.Dashboard{})).
Produces(restful.MIME_JSON)
c.Add(ws)
return nil
}

View File

@@ -0,0 +1,4 @@
#! /bin/bash
curl -d '{"grafanaDashboardUrl":"https://grafana.com/api/dashboards/7362/revisions/5/download", "description":"this is a test clusterdashboard."}' -H "Content-Type: application/json" localhost:9090/kapis/monitoring.kubesphere.io/v1alpha3/clusterdashboards/test1/template
curl -d '{"grafanaDashboardUrl":"https://grafana.com/api/dashboards/7362/revisions/5/download", "description":"this is a test dashboard."}' -H "Content-Type: application/json" localhost:9090/kapis/monitoring.kubesphere.io/v1alpha3/namespaces/default/dashboards/test2/template

View File

@@ -53,6 +53,7 @@ type DashboardEntity struct {
GrafanaDashboardUrl string `json:"grafanaDashboardUrl,omitempty"`
GrafanaDashboardContent string `json:"grafanaDashboardContent,omitempty"`
Description string `json:"description,omitempty"`
Namespace string `json:"namespace,omitempty"`
}
// The first element is the timestamp, the second is the metric value.

View File

@@ -123,7 +123,7 @@ func generateSwaggerJson() []byte {
urlruntime.Must(devopsv1alpha2.AddToContainer(container, ""))
urlruntime.Must(devopsv1alpha3.AddToContainer(container, ""))
urlruntime.Must(iamv1alpha2.AddToContainer(container, nil, nil, group.New(informerFactory, clientsets.KubeSphere(), clientsets.Kubernetes()), nil))
urlruntime.Must(monitoringv1alpha3.AddToContainer(container, clientsets.Kubernetes(), nil, nil, informerFactory, nil, nil))
urlruntime.Must(monitoringv1alpha3.AddToContainer(container, clientsets.Kubernetes(), nil, nil, informerFactory, nil, nil, nil))
urlruntime.Must(openpitrixv1.AddToContainer(container, informerFactory, fake.NewSimpleClientset(), nil))
urlruntime.Must(openpitrixv2.AddToContainer(container, informerFactory, fake.NewSimpleClientset(), nil))
urlruntime.Must(operationsv1alpha2.AddToContainer(container, clientsets.Kubernetes()))