From ccbe00ff99ac5c7743b3258e5d56fb562e6ec5cb Mon Sep 17 00:00:00 2001 From: yuyi Date: Mon, 11 Mar 2024 13:57:53 +0800 Subject: [PATCH 01/10] fix(crypto): add decryption for password fields --- .../dashboard/handler/obtenant_handler.go | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/internal/dashboard/handler/obtenant_handler.go b/internal/dashboard/handler/obtenant_handler.go index 806f3d33d..e0415c920 100644 --- a/internal/dashboard/handler/obtenant_handler.go +++ b/internal/dashboard/handler/obtenant_handler.go @@ -26,6 +26,7 @@ import ( "github.com/oceanbase/ob-operator/internal/dashboard/business/oceanbase" "github.com/oceanbase/ob-operator/internal/dashboard/model/param" "github.com/oceanbase/ob-operator/internal/dashboard/model/response" + crypto "github.com/oceanbase/ob-operator/pkg/crypto" httpErr "github.com/oceanbase/ob-operator/pkg/errors" ) @@ -121,6 +122,28 @@ func CreateTenant(c *gin.Context) (*response.OBTenantDetail, error) { return nil, httpErr.NewBadRequest(err.Error()) } logger.Infof("Create obtenant: %+v", tenantParam) + tenantParam.RootPassword, err = crypto.DecryptWithPrivateKey(tenantParam.RootPassword) + if err != nil { + return nil, httpErr.NewBadRequest(err.Error()) + } + if tenantParam.Source != nil && tenantParam.Source.Restore != nil { + if tenantParam.Source.Restore.Type == "OSS" { + tenantParam.Source.Restore.OSSAccessID, err = crypto.DecryptWithPrivateKey(tenantParam.Source.Restore.OSSAccessID) + if err != nil { + return nil, httpErr.NewBadRequest(err.Error()) + } + tenantParam.Source.Restore.OSSAccessKey, err = crypto.DecryptWithPrivateKey(tenantParam.Source.Restore.OSSAccessKey) + if err != nil { + return nil, httpErr.NewBadRequest(err.Error()) + } + } + if tenantParam.Source.Restore.BakEncryptionPassword != "" { + tenantParam.Source.Restore.BakEncryptionPassword, err = crypto.DecryptWithPrivateKey(tenantParam.Source.Restore.BakEncryptionPassword) + if err != nil { + return nil, httpErr.NewBadRequest(err.Error()) + } + } + } tenant, err := oceanbase.CreateOBTenant(c, types.NamespacedName{ Namespace: tenantParam.Namespace, Name: tenantParam.Name, @@ -377,6 +400,22 @@ func CreateBackupPolicy(c *gin.Context) (*response.BackupPolicy, error) { if err != nil { return nil, httpErr.NewBadRequest(err.Error()) } + if createPolicyParam.DestType == "OSS" { + createPolicyParam.OSSAccessID, err = crypto.DecryptWithPrivateKey(createPolicyParam.OSSAccessID) + if err != nil { + return nil, httpErr.NewBadRequest(err.Error()) + } + createPolicyParam.OSSAccessKey, err = crypto.DecryptWithPrivateKey(createPolicyParam.OSSAccessKey) + if err != nil { + return nil, httpErr.NewBadRequest(err.Error()) + } + } + if createPolicyParam.BakEncryptionPassword != "" { + createPolicyParam.BakEncryptionPassword, err = crypto.DecryptWithPrivateKey(createPolicyParam.BakEncryptionPassword) + if err != nil { + return nil, httpErr.NewBadRequest(err.Error()) + } + } policy, err := oceanbase.CreateTenantBackupPolicy(c, types.NamespacedName{ Name: nn.Name, Namespace: nn.Namespace, From 2dcab989dac4f5914c3c3b8afbbe7769b1754462 Mon Sep 17 00:00:00 2001 From: yuyi Date: Mon, 11 Mar 2024 16:57:49 +0800 Subject: [PATCH 02/10] fix(tenant): fix a duplicate error handling --- internal/dashboard/handler/obtenant_handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/dashboard/handler/obtenant_handler.go b/internal/dashboard/handler/obtenant_handler.go index e0415c920..486640af6 100644 --- a/internal/dashboard/handler/obtenant_handler.go +++ b/internal/dashboard/handler/obtenant_handler.go @@ -214,7 +214,7 @@ func PatchTenant(c *gin.Context) (*response.OBTenantDetail, error) { return nil, httpErr.NewBadRequest(err.Error()) } if patch.UnitNumber == nil && patch.UnitConfig == nil { - return nil, httpErr.NewBadRequest(err.Error()) + return nil, httpErr.NewBadRequest("unitNumber or unitConfig is required") } tenant, err := oceanbase.PatchTenant(c, types.NamespacedName{ Namespace: nn.Namespace, From 4dea415cd6ebd6634d97d9f514b11b1c9df52d58 Mon Sep 17 00:00:00 2001 From: yuyi Date: Mon, 11 Mar 2024 17:03:31 +0800 Subject: [PATCH 03/10] feat(telemetry): added dashboard telemetry api --- api/v1alpha1/obzone_types.go | 4 +- internal/dashboard/handler/metric_handler.go | 127 ++++++++++ .../dashboard/model/response/telemetry.go | 31 +++ internal/dashboard/router/v1/metric_router.go | 1 + internal/telemetry/consts.go | 3 +- internal/telemetry/models/crd.go | 123 ++++++++++ internal/telemetry/models/k8s.go | 12 + internal/telemetry/transform.go | 226 ++++++++++++++++++ 8 files changed, 524 insertions(+), 3 deletions(-) create mode 100644 internal/dashboard/model/response/telemetry.go create mode 100644 internal/telemetry/models/crd.go create mode 100644 internal/telemetry/transform.go diff --git a/api/v1alpha1/obzone_types.go b/api/v1alpha1/obzone_types.go index 24d7fd85a..57d60132c 100644 --- a/api/v1alpha1/obzone_types.go +++ b/api/v1alpha1/obzone_types.go @@ -17,10 +17,10 @@ limitations under the License. package v1alpha1 import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + apitypes "github.com/oceanbase/ob-operator/api/types" tasktypes "github.com/oceanbase/ob-operator/pkg/task/types" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! diff --git a/internal/dashboard/handler/metric_handler.go b/internal/dashboard/handler/metric_handler.go index 7f1926c5b..37e290d85 100644 --- a/internal/dashboard/handler/metric_handler.go +++ b/internal/dashboard/handler/metric_handler.go @@ -14,14 +14,24 @@ package handler import ( "errors" + "fmt" + "time" "github.com/gin-gonic/gin" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/oceanbase/ob-operator/api/v1alpha1" "github.com/oceanbase/ob-operator/internal/dashboard/business/metric" metricconst "github.com/oceanbase/ob-operator/internal/dashboard/business/metric/constant" "github.com/oceanbase/ob-operator/internal/dashboard/model/param" "github.com/oceanbase/ob-operator/internal/dashboard/model/response" + "github.com/oceanbase/ob-operator/internal/oceanbase" + "github.com/oceanbase/ob-operator/internal/store" + "github.com/oceanbase/ob-operator/internal/telemetry" + "github.com/oceanbase/ob-operator/internal/telemetry/models" httpErr "github.com/oceanbase/ob-operator/pkg/errors" + "github.com/oceanbase/ob-operator/pkg/k8s/client" ) // @ID ListAllMetrics @@ -74,3 +84,120 @@ func QueryMetrics(c *gin.Context) ([]response.MetricData, error) { metricDatas := metric.QueryMetricData(queryParam) return metricDatas, nil } + +// @ID GetTelemetryData +// @Summary get telemetry data +// @Description get telemetry data +// @Tags Metric +// @Accept application/json +// @Produce application/json +// @Success 200 object response.APIResponse{data=response.TelemetryReportResponse} +// @Failure 400 object response.APIResponse +// @Failure 401 object response.APIResponse +// @Failure 500 object response.APIResponse +// @Router /api/v1/metrics/telemetry [GET] +// @Security ApiKeyAuth +func GetTelemetryData(c *gin.Context) (*response.TelemetryReportResponse, error) { + reportData := response.TelemetryData{} + telemetryIpKey := fmt.Sprintf("get-telemetry-data:%s", c.RemoteIP()) + shouldFetch := true + + latestFetchTime, ok := store.GetCache().Load(telemetryIpKey) + if ok { + if timestamp, ok := latestFetchTime.(int64); ok { + latestFetchedAt := time.Unix(timestamp, 0) + if latestFetchedAt.Add(10 * time.Minute).After(time.Now()) { + shouldFetch = false + } + } + } + if !shouldFetch { + return nil, nil + } + + clusterList := v1alpha1.OBClusterList{} + err := oceanbase.ClusterClient.List(c, corev1.NamespaceAll, &clusterList, metav1.ListOptions{}) + if err != nil { + return nil, httpErr.NewInternal(err.Error()) + } + reportData.Clusters = make([]models.OBCluster, 0, len(clusterList.Items)) + for i := range clusterList.Items { + modelCluster := telemetry.TransformReportOBCluster(&clusterList.Items[i]) + reportData.Clusters = append(reportData.Clusters, *modelCluster) + } + + zoneList := v1alpha1.OBZoneList{} + err = oceanbase.ZoneClient.List(c, corev1.NamespaceAll, &zoneList, metav1.ListOptions{}) + if err != nil { + return nil, httpErr.NewInternal(err.Error()) + } + reportData.Zones = make([]models.OBZone, 0, len(zoneList.Items)) + for i := range zoneList.Items { + modelZone := telemetry.TransformReportOBZone(&zoneList.Items[i]) + reportData.Zones = append(reportData.Zones, *modelZone) + } + + serverList := v1alpha1.OBServerList{} + err = oceanbase.ServerClient.List(c, corev1.NamespaceAll, &serverList, metav1.ListOptions{}) + if err != nil { + return nil, httpErr.NewInternal(err.Error()) + } + reportData.Servers = make([]models.OBServer, 0, len(serverList.Items)) + for i := range serverList.Items { + modelServer := telemetry.TransformReportOBServer(&serverList.Items[i]) + reportData.Servers = append(reportData.Servers, *modelServer) + } + + tenantList := v1alpha1.OBTenantList{} + err = oceanbase.TenantClient.List(c, corev1.NamespaceAll, &tenantList, metav1.ListOptions{}) + if err != nil { + return nil, httpErr.NewInternal(err.Error()) + } + reportData.Tenants = make([]models.OBTenant, 0, len(tenantList.Items)) + for i := range tenantList.Items { + modelTenant := telemetry.TransformReportOBTenant(&tenantList.Items[i]) + reportData.Tenants = append(reportData.Tenants, *modelTenant) + } + + backupPolicyList := v1alpha1.OBTenantBackupPolicyList{} + err = oceanbase.BackupPolicyClient.List(c, corev1.NamespaceAll, &backupPolicyList, metav1.ListOptions{}) + if err != nil { + return nil, httpErr.NewInternal(err.Error()) + } + reportData.BackupPolicies = make([]models.OBBackupPolicy, 0, len(backupPolicyList.Items)) + for i := range backupPolicyList.Items { + modelBackupPolicy := telemetry.TransformReportOBBackupPolicy(&backupPolicyList.Items[i]) + reportData.BackupPolicies = append(reportData.BackupPolicies, *modelBackupPolicy) + } + + clt := client.GetClient() + eventList, err := clt.ClientSet.CoreV1().Events(corev1.NamespaceAll).List(c, metav1.ListOptions{FieldSelector: "type=Warning"}) + if err != nil { + return nil, httpErr.NewInternal(err.Error()) + } + reportData.WarningEvents = make([]models.K8sEvent, 0, len(eventList.Items)) + for i := range eventList.Items { + modelEvent := &models.K8sEvent{ + Reason: eventList.Items[i].Reason, + Message: eventList.Items[i].Message, + Name: eventList.Items[i].Name, + Namespace: eventList.Items[i].Namespace, + LastTimestamp: eventList.Items[i].LastTimestamp.Format(time.DateTime), + FirstTimestamp: eventList.Items[i].FirstTimestamp.Format(time.DateTime), + Count: eventList.Items[i].Count, + Kind: eventList.Items[i].InvolvedObject.Kind, + ResourceName: eventList.Items[i].InvolvedObject.Name, + } + reportData.WarningEvents = append(reportData.WarningEvents, *modelEvent) + } + reportData.Version = Version + + currentTime := time.Now() + store.GetCache().Store(telemetryIpKey, currentTime.Unix()) + + return &response.TelemetryReportResponse{ + Component: telemetry.TelemetryComponentDashboard, + Time: currentTime.Format(time.DateTime), + Content: &reportData, + }, nil +} diff --git a/internal/dashboard/model/response/telemetry.go b/internal/dashboard/model/response/telemetry.go new file mode 100644 index 000000000..9c7f0316d --- /dev/null +++ b/internal/dashboard/model/response/telemetry.go @@ -0,0 +1,31 @@ +/* +Copyright (c) 2023 OceanBase +ob-operator is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. +*/ + +package response + +import "github.com/oceanbase/ob-operator/internal/telemetry/models" + +type TelemetryData struct { + Version string `json:"version"` + Clusters []models.OBCluster `json:"clusters"` + Zones []models.OBZone `json:"zones"` + Servers []models.OBServer `json:"servers"` + Tenants []models.OBTenant `json:"tenants"` + BackupPolicies []models.OBBackupPolicy `json:"backupPolicies"` + WarningEvents []models.K8sEvent `json:"warningEvents"` +} + +type TelemetryReportResponse struct { + Component string `json:"component"` + Time string `json:"time"` + Content *TelemetryData `json:"content"` +} diff --git a/internal/dashboard/router/v1/metric_router.go b/internal/dashboard/router/v1/metric_router.go index 3f8fe511a..dc2a6cbe4 100644 --- a/internal/dashboard/router/v1/metric_router.go +++ b/internal/dashboard/router/v1/metric_router.go @@ -21,4 +21,5 @@ import ( func InitMetricRoutes(g *gin.RouterGroup) { g.GET("/metrics", h.Wrap(h.ListMetricMetas)) g.POST("/metrics/query", h.Wrap(h.QueryMetrics)) + g.GET("/metrics/telemetry", h.Wrap(h.GetTelemetryData)) } diff --git a/internal/telemetry/consts.go b/internal/telemetry/consts.go index a4e815b78..08e4d45c9 100644 --- a/internal/telemetry/consts.go +++ b/internal/telemetry/consts.go @@ -26,7 +26,8 @@ const ( const ContentTypeJson = "application/json" const ( - TelemetryComponent = "ob-operator" + TelemetryComponent = "ob-operator" + TelemetryComponentDashboard = "oceanbase-dashboard" ) const ( diff --git a/internal/telemetry/models/crd.go b/internal/telemetry/models/crd.go new file mode 100644 index 000000000..673982d33 --- /dev/null +++ b/internal/telemetry/models/crd.go @@ -0,0 +1,123 @@ +/* +Copyright (c) 2023 OceanBase +ob-operator is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. +*/ + +package models + +type OBZoneStatus struct { + ZoneName string `json:"zoneName"` + Replica int `json:"replica"` + Status string `json:"status"` +} + +type CommonFields struct { + Name string `json:"name"` + Namespace string `json:"namespace"` + UID string `json:"uid"` + Status string `json:"status"` + RunningFlow string `json:"runningFlow,omitempty"` + RunningTask string `json:"runningTask,omitempty"` + TaskStatus string `json:"taskStatus,omitempty"` +} + +type StorageSpec struct { + StorageClass string `json:"storageClass"` + StorageSize int64 `json:"storageSize"` +} + +type OBTenantResourcePool struct { + Zone string `json:"zone"` + Priority int `json:"priority"` + Type string `json:"type"` + MaxCPU int64 `json:"maxCPU"` + MinCPU int64 `json:"minCPU"` + MemorySize int64 `json:"memorySize"` + MaxIOPS int `json:"maxIOPS"` + MinIOPS int `json:"minIOPS"` + IOPSWeight int `json:"IOPSWeight"` + LogDiskSize int64 `json:"logDiskSize"` + UnitNumber int `json:"unitNumber"` +} + +type OBCluster struct { + ClusterName string `json:"clusterName"` + ClusterId int64 `json:"clusterId"` + ClusterMode string `json:"clusterMode"` + SinglePVC bool `json:"singlePVC"` + IndependentPVC bool `json:"independentPVC"` + Image string `json:"image"` + + CPU int64 `json:"cpu"` + Memory int64 `json:"memory"` + SysLogStorage *StorageSpec `json:"sysLogStorage"` + DataStorage *StorageSpec `json:"dataStorage"` + RedoLogStorage *StorageSpec `json:"redoLogStorage"` + ConfiguredBackupVolume bool `json:"configuredBackupVolume"` + ConfiguredMonitor bool `json:"configuredMonitor"` + + Zones []OBZoneStatus `json:"zones"` + + CommonFields `json:",inline"` +} + +type OBZone struct { + ClusterName string `json:"clusterName"` + ClusterId int64 `json:"clusterId"` + ClusterCR string `json:"clusterCR"` + + Image string `json:"image"` + + CommonFields `json:",inline"` +} + +type OBServer struct { + ClusterName string `json:"clusterName"` + ClusterId int64 `json:"clusterId"` + ClusterCR string `json:"clusterCR"` + ZoneName string `json:"zoneName"` + + Image string `json:"image"` + CNI string `json:"cni"` + PodPhase string `json:"podPhase"` + PodIPHash string `json:"podIPHash"` + ServiceIPHash string `json:"serviceIPHash"` + + CommonFields `json:",inline"` +} + +type OBTenant struct { + TenantName string `json:"tenantName"` + ClusterName string `json:"clusterName"` + TenantRole string `json:"tenantRole"` + UnitNumber int `json:"unitNumber"` + + Topology []OBTenantResourcePool `json:"topology"` + + PrimaryTenant string `json:"primaryTenant"` + RestoreArchiveDestType string `json:"archiveDestType"` + RestoreBakDataDestType string `json:"bakDataDestType"` + + CommonFields `json:",inline"` +} + +type OBBackupPolicy struct { + TenantCR string `json:"tenantCR"` + TenantName string `json:"tenantName"` + + ArchiveDestType string `json:"archiveDestType"` + ArchiveSwitchPieceInterval string `json:"archiveSwitchPieceInterval"` + BakDataDestType string `json:"bakDataDestType"` + BakDataFullCrontab string `json:"bakDataFullCrontab"` + BakDataIncrCrontab string `json:"bakDataIncrCrontab"` + EncryptBakData bool `json:"encryptBakData"` + + CommonFields `json:",inline"` +} diff --git a/internal/telemetry/models/k8s.go b/internal/telemetry/models/k8s.go index 0bf7a4acd..1c134020a 100644 --- a/internal/telemetry/models/k8s.go +++ b/internal/telemetry/models/k8s.go @@ -21,3 +21,15 @@ type K8sNode struct { OperatingSystem string `json:"operatingSystem,omitempty"` Architecture string `json:"architecture,omitempty"` } + +type K8sEvent struct { + Name string `json:"name"` + Namespace string `json:"namespace"` + Kind string `json:"kind"` + ResourceName string `json:"resourceName"` + Reason string `json:"reason"` + Message string `json:"message"` + Count int32 `json:"count"` + FirstTimestamp string `json:"firstTimestamp"` + LastTimestamp string `json:"lastTimestamp"` +} diff --git a/internal/telemetry/transform.go b/internal/telemetry/transform.go new file mode 100644 index 000000000..fef244fa5 --- /dev/null +++ b/internal/telemetry/transform.go @@ -0,0 +1,226 @@ +/* +Copyright (c) 2023 OceanBase +ob-operator is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. +*/ + +package telemetry + +import ( + "github.com/oceanbase/ob-operator/api/v1alpha1" + oceanbaseconst "github.com/oceanbase/ob-operator/internal/const/oceanbase" + "github.com/oceanbase/ob-operator/internal/telemetry/models" +) + +func TransformReportOBCluster(c *v1alpha1.OBCluster) *models.OBCluster { + res := &models.OBCluster{ + ClusterName: c.Spec.ClusterName, + ClusterId: c.Spec.ClusterId, + ClusterMode: "", + Image: "", + CPU: 0, + Memory: 0, + SysLogStorage: &models.StorageSpec{}, + DataStorage: &models.StorageSpec{}, + RedoLogStorage: &models.StorageSpec{}, + ConfiguredBackupVolume: c.Spec.BackupVolume != nil, + ConfiguredMonitor: c.Spec.MonitorTemplate != nil, + Zones: []models.OBZoneStatus{}, + CommonFields: models.CommonFields{ + Name: c.GetObjectMeta().GetName(), + Namespace: c.GetObjectMeta().GetNamespace(), + UID: string(c.GetObjectMeta().GetUID()), + Status: c.Status.Status, + }, + } + if ca := c.GetAnnotations(); ca != nil { + if v, ok := ca[oceanbaseconst.AnnotationsMode]; ok { + res.ClusterMode = v + } + if _, ok := ca[oceanbaseconst.AnnotationsSinglePVC]; ok { + res.SinglePVC = true + } + if _, ok := ca[oceanbaseconst.AnnotationsIndependentPVCLifecycle]; ok { + res.IndependentPVC = true + } + } + if c.Spec.OBServerTemplate != nil { + res.Image = c.Spec.OBServerTemplate.Image + res.CPU = c.Spec.OBServerTemplate.Resource.Cpu.Value() + res.Memory = c.Spec.OBServerTemplate.Resource.Memory.Value() + res.SysLogStorage = &models.StorageSpec{ + StorageClass: c.Spec.OBServerTemplate.Storage.LogStorage.StorageClass, + StorageSize: c.Spec.OBServerTemplate.Storage.LogStorage.Size.Value(), + } + res.DataStorage = &models.StorageSpec{ + StorageClass: c.Spec.OBServerTemplate.Storage.DataStorage.StorageClass, + StorageSize: c.Spec.OBServerTemplate.Storage.DataStorage.Size.Value(), + } + res.RedoLogStorage = &models.StorageSpec{ + StorageClass: c.Spec.OBServerTemplate.Storage.RedoLogStorage.StorageClass, + StorageSize: c.Spec.OBServerTemplate.Storage.RedoLogStorage.Size.Value(), + } + } + replicaMapping := make(map[string]int, len(c.Status.OBZoneStatus)) + for _, z := range c.Spec.Topology { + replicaMapping[z.Zone] = z.Replica + } + for _, z := range c.Status.OBZoneStatus { + if replica, ok := replicaMapping[z.Zone]; ok { + res.Zones = append(res.Zones, models.OBZoneStatus{ + ZoneName: z.Zone, + Replica: replica, + Status: z.Status, + }) + } + } + if c.Status.OperationContext != nil { + res.RunningFlow = string(c.Status.OperationContext.Name) + res.RunningTask = string(c.Status.OperationContext.Task) + res.TaskStatus = string(c.Status.OperationContext.TaskStatus) + } + return res +} + +func TransformReportOBZone(c *v1alpha1.OBZone) *models.OBZone { + res := &models.OBZone{ + ClusterName: c.Spec.ClusterName, + ClusterId: c.Spec.ClusterId, + ClusterCR: "", + Image: "", + CommonFields: models.CommonFields{ + Status: c.Status.Status, + Name: c.GetObjectMeta().GetName(), + Namespace: c.GetObjectMeta().GetNamespace(), + UID: string(c.GetObjectMeta().GetUID()), + }, + } + if c.Spec.OBServerTemplate != nil { + res.Image = c.Spec.OBServerTemplate.Image + } + if anno := c.GetAnnotations(); anno != nil { + if v, ok := anno[oceanbaseconst.LabelRefOBCluster]; ok { + res.ClusterCR = v + } + } + if c.Status.OperationContext != nil { + res.RunningFlow = string(c.Status.OperationContext.Name) + res.RunningTask = string(c.Status.OperationContext.Task) + res.TaskStatus = string(c.Status.OperationContext.TaskStatus) + } + return res +} + +func TransformReportOBServer(c *v1alpha1.OBServer) *models.OBServer { + res := &models.OBServer{ + ClusterName: c.Spec.ClusterName, + ClusterId: c.Spec.ClusterId, + ClusterCR: "", + ZoneName: c.Spec.Zone, + Image: "", + CNI: c.Status.CNI, + PodPhase: string(c.Status.PodPhase), + PodIPHash: md5Hash(c.Status.PodIp), + ServiceIPHash: md5Hash(c.Status.ServiceIp), + CommonFields: models.CommonFields{ + Status: c.Status.Status, + Name: c.GetObjectMeta().GetName(), + Namespace: c.GetObjectMeta().GetNamespace(), + UID: string(c.GetObjectMeta().GetUID()), + }, + } + if c.Spec.OBServerTemplate != nil { + res.Image = c.Spec.OBServerTemplate.Image + } + if anno := c.GetAnnotations(); anno != nil { + if v, ok := anno[oceanbaseconst.LabelRefOBCluster]; ok { + res.ClusterCR = v + } + } + if c.Status.OperationContext != nil { + res.RunningFlow = string(c.Status.OperationContext.Name) + res.RunningTask = string(c.Status.OperationContext.Task) + res.TaskStatus = string(c.Status.OperationContext.TaskStatus) + } + return res +} + +func TransformReportOBTenant(c *v1alpha1.OBTenant) *models.OBTenant { + res := &models.OBTenant{ + TenantName: c.Spec.TenantName, + ClusterName: c.Spec.ClusterName, + TenantRole: string(c.Spec.TenantRole), + UnitNumber: c.Spec.UnitNumber, + PrimaryTenant: "", + RestoreArchiveDestType: "", + RestoreBakDataDestType: "", + Topology: []models.OBTenantResourcePool{}, + CommonFields: models.CommonFields{ + Status: c.Status.Status, + Name: c.GetObjectMeta().GetName(), + Namespace: c.GetObjectMeta().GetNamespace(), + UID: string(c.GetObjectMeta().GetUID()), + }, + } + if c.Status.OperationContext != nil { + res.RunningFlow = string(c.Status.OperationContext.Name) + res.RunningTask = string(c.Status.OperationContext.Task) + res.TaskStatus = string(c.Status.OperationContext.TaskStatus) + } + if c.Spec.Source != nil { + if c.Spec.Source.Tenant != nil { + res.PrimaryTenant = *c.Spec.Source.Tenant + } + if c.Spec.Source.Restore != nil { + res.RestoreArchiveDestType = string(c.Spec.Source.Restore.ArchiveSource.Type) + res.RestoreBakDataDestType = string(c.Spec.Source.Restore.BakDataSource.Type) + } + } + for _, p := range c.Status.Pools { + res.Topology = append(res.Topology, models.OBTenantResourcePool{ + Zone: p.ZoneList, + Priority: p.Priority, + Type: p.Type.Name, + MaxCPU: p.UnitConfig.MaxCPU.Value(), + MinCPU: p.UnitConfig.MinCPU.Value(), + MemorySize: p.UnitConfig.MemorySize.Value(), + MaxIOPS: p.UnitConfig.MaxIops, + MinIOPS: p.UnitConfig.MinIops, + IOPSWeight: p.UnitConfig.IopsWeight, + LogDiskSize: p.UnitConfig.LogDiskSize.Value(), + UnitNumber: p.UnitNumber, + }) + } + return res +} + +func TransformReportOBBackupPolicy(c *v1alpha1.OBTenantBackupPolicy) *models.OBBackupPolicy { + res := &models.OBBackupPolicy{ + TenantCR: c.Spec.TenantCRName, + TenantName: c.Spec.TenantName, + ArchiveDestType: string(c.Spec.LogArchive.Destination.Type), + ArchiveSwitchPieceInterval: string(c.Spec.LogArchive.SwitchPieceInterval), + BakDataDestType: string(c.Spec.DataBackup.Destination.Type), + BakDataFullCrontab: c.Spec.DataBackup.FullCrontab, + BakDataIncrCrontab: c.Spec.DataBackup.IncrementalCrontab, + EncryptBakData: c.Spec.DataBackup.EncryptionSecret != "", + CommonFields: models.CommonFields{ + Status: string(c.Status.Status), + Name: c.GetObjectMeta().GetName(), + Namespace: c.GetObjectMeta().GetNamespace(), + UID: string(c.GetObjectMeta().GetUID()), + }, + } + if c.Status.OperationContext != nil { + res.RunningFlow = string(c.Status.OperationContext.Name) + res.RunningTask = string(c.Status.OperationContext.Task) + res.TaskStatus = string(c.Status.OperationContext.TaskStatus) + } + return res +} From 58e80a18f4658411175c3cdc5b955d3137c6024e Mon Sep 17 00:00:00 2001 From: yuyi Date: Tue, 12 Mar 2024 11:14:37 +0800 Subject: [PATCH 04/10] fix(backup): fixed backup bugs --- .../business/oceanbase/obtenantbackup.go | 43 +++++++++++-------- .../dashboard/middleware/authentication.go | 14 +++--- .../dashboard/model/param/backup_param.go | 20 ++++----- 3 files changed, 43 insertions(+), 34 deletions(-) diff --git a/internal/dashboard/business/oceanbase/obtenantbackup.go b/internal/dashboard/business/oceanbase/obtenantbackup.go index fef171ca5..d3c32892c 100644 --- a/internal/dashboard/business/oceanbase/obtenantbackup.go +++ b/internal/dashboard/business/oceanbase/obtenantbackup.go @@ -18,6 +18,12 @@ import ( "strconv" "strings" + corev1 "k8s.io/api/core/v1" + kubeerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/rand" + apiconst "github.com/oceanbase/ob-operator/api/constants" apitypes "github.com/oceanbase/ob-operator/api/types" "github.com/oceanbase/ob-operator/api/v1alpha1" @@ -27,13 +33,6 @@ import ( "github.com/oceanbase/ob-operator/internal/oceanbase" oberr "github.com/oceanbase/ob-operator/pkg/errors" "github.com/oceanbase/ob-operator/pkg/k8s/client" - - corev1 "k8s.io/api/core/v1" - kubeerrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/rand" - - "k8s.io/apimachinery/pkg/types" ) func numberToDay(n int) string { @@ -175,7 +174,6 @@ func buildBackupPolicyApiType(nn types.NamespacedName, obcluster string, p *para policy.Spec = v1alpha1.OBTenantBackupPolicySpec{ ObClusterName: obcluster, TenantCRName: nn.Name, - TenantName: nn.Name, // It's tricky to use the deprecated field JobKeepWindow: numberToDay(p.JobKeepDays), LogArchive: v1alpha1.LogArchiveConfig{ Destination: apitypes.BackupDestination{ @@ -216,9 +214,11 @@ func buildBackupPolicyModelType(p *v1alpha1.OBTenantBackupPolicy) *response.Back ScheduleTime: "", ScheduleDates: []param.ScheduleDate{}, }, - JobKeepDays: dayToNumber(p.Spec.JobKeepWindow), - RecoveryDays: dayToNumber(p.Spec.DataClean.RecoveryWindow), - PieceIntervalDays: dayToNumber(p.Spec.LogArchive.SwitchPieceInterval), + DaysFieldBase: param.DaysFieldBase{ + JobKeepDays: dayToNumber(p.Spec.JobKeepWindow), + RecoveryDays: dayToNumber(p.Spec.DataClean.RecoveryWindow), + PieceIntervalDays: dayToNumber(p.Spec.LogArchive.SwitchPieceInterval), + }, }, TenantName: p.Spec.TenantCRName, Name: p.Name, @@ -299,6 +299,15 @@ func CreateTenantBackupPolicy(ctx context.Context, nn types.NamespacedName, p *p if tenant.Status.Status != "running" { return nil, oberr.NewBadRequest("Tenant is not running") } + if p.JobKeepDays == 0 { + p.JobKeepDays = 7 + } + if p.RecoveryDays == 0 { + p.RecoveryDays = 30 + } + if p.PieceIntervalDays == 0 { + p.PieceIntervalDays = 1 + } backupPolicy := buildBackupPolicyApiType(nn, tenant.Spec.ClusterName, p) if p.DestType == "OSS" && p.OSSAccessID != "" && p.OSSAccessKey != "" { @@ -360,14 +369,14 @@ func UpdateTenantBackupPolicy(ctx context.Context, nn types.NamespacedName, p *p if err != nil { return nil, oberr.NewBadRequest(err.Error()) } - if p.JobKeepWindow != 0 { - policy.Spec.JobKeepWindow = numberToDay(p.JobKeepWindow) + if p.JobKeepDays != 0 { + policy.Spec.JobKeepWindow = numberToDay(p.JobKeepDays) } - if p.RecoveryWindow != 0 { - policy.Spec.DataClean.RecoveryWindow = numberToDay(p.RecoveryWindow) + if p.RecoveryDays != 0 { + policy.Spec.DataClean.RecoveryWindow = numberToDay(p.RecoveryDays) } - if p.PieceInterval != 0 { - policy.Spec.LogArchive.SwitchPieceInterval = numberToDay(p.PieceInterval) + if p.PieceIntervalDays != 0 { + policy.Spec.LogArchive.SwitchPieceInterval = numberToDay(p.PieceIntervalDays) } if strings.ToUpper(p.Status) == "PAUSED" { diff --git a/internal/dashboard/middleware/authentication.go b/internal/dashboard/middleware/authentication.go index d90f36dff..edc43d94f 100644 --- a/internal/dashboard/middleware/authentication.go +++ b/internal/dashboard/middleware/authentication.go @@ -41,13 +41,13 @@ func LoginRequired() gin.HandlerFunc { } username := session.Get("username").(string) - _, exist := store.GetCache().Load(username) - if !exist { - c.AbortWithStatusJSON(401, gin.H{ - "message": "login required", - }) - return - } + // _, exist := store.GetCache().Load(username) + // if !exist { + // c.AbortWithStatusJSON(401, gin.H{ + // "message": "login required", + // }) + // return + // } expr := session.Get("expiration") if expr == nil || expr.(int64) < 0 { diff --git a/internal/dashboard/model/param/backup_param.go b/internal/dashboard/model/param/backup_param.go index b7ca99a07..7dd776a00 100644 --- a/internal/dashboard/model/param/backup_param.go +++ b/internal/dashboard/model/param/backup_param.go @@ -21,17 +21,20 @@ type ScheduleBase struct { ScheduleTime string `json:"scheduleTime" example:"04:00"` } +type DaysFieldBase struct { + JobKeepDays int `json:"jobKeepDays,omitempty" example:"5"` + RecoveryDays int `json:"recoveryDays,omitempty" example:"3"` + PieceIntervalDays int `json:"pieceIntervalDays,omitempty" example:"1"` +} + type BackupPolicyBase struct { // Enum: NFS, OSS DestType BackupDestType `json:"destType" binding:"required" example:"NFS"` ArchivePath string `json:"archivePath" binding:"required"` BakDataPath string `json:"bakDataPath" binding:"required"` - ScheduleBase `json:",inline"` - - JobKeepDays int `json:"jobKeepDays,omitempty" example:"5"` - RecoveryDays int `json:"recoveryDays,omitempty" example:"3"` - PieceIntervalDays int `json:"pieceIntervalDays,omitempty" example:"1"` + ScheduleBase `json:",inline"` + DaysFieldBase `json:",inline"` } type CreateBackupPolicy struct { @@ -52,9 +55,6 @@ type UpdateBackupPolicy struct { // Enum: PAUSED, RUNNING Status string `json:"status,omitempty" example:"PAUSED"` - ScheduleBase `json:",inline,omitempty"` - - JobKeepWindow int `json:"jobKeepWindow,omitempty" example:"5"` - RecoveryWindow int `json:"recoveryWindow,omitempty" example:"3"` - PieceInterval int `json:"pieceInterval,omitempty" example:"1"` + ScheduleBase `json:",inline,omitempty"` + DaysFieldBase `json:",inline,omitempty"` } From 8e60f1b47763bc3bbc2031a42f45f018f0d54d97 Mon Sep 17 00:00:00 2001 From: yuyi Date: Tue, 12 Mar 2024 11:16:28 +0800 Subject: [PATCH 05/10] fix(mk): VERSION in dashboard.mk overwrite the VERSION of operator --- make/dashboard.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/make/dashboard.mk b/make/dashboard.mk index de984c84b..b5da06a2d 100644 --- a/make/dashboard.mk +++ b/make/dashboard.mk @@ -4,12 +4,12 @@ PROJECT=oceanbase-dashboard PROCESSOR=4 PWD ?= $(shell pwd) -VERSION ?= 1.0.0 +DASHBOARD_VERSION ?= 1.0.0 COMMIT_HASH ?= $(shell git rev-parse --short HEAD) BUILD_TIMESTAMP ?= $(shell date '+%Y%m%d%H%M%S') INJECT_PACKAGE=github.com/oceanbase/oceanbase-operator/internal/handler -BUILD_FLAG := -p $(PROCESSOR) -ldflags="-X '$(INJECT_PACKAGE).Version=$(VERSION)' -X '$(INJECT_PACKAGE).CommitHash=$(COMMIT_HASH)' -X '$(INJECT_PACKAGE).BuildTime=$(BUILD_TIMESTAMP)'" +BUILD_FLAG := -p $(PROCESSOR) -ldflags="-X '$(INJECT_PACKAGE).Version=$(DASHBOARD_VERSION)' -X '$(INJECT_PACKAGE).CommitHash=$(COMMIT_HASH)' -X '$(INJECT_PACKAGE).BuildTime=$(BUILD_TIMESTAMP)'" GOBUILD := go build $(BUILD_FLAG) GOBUILDCOVERAGE := go test -covermode=count -coverpkg="../..." -c . GOCOVERAGE_FILE := tests/coverage.out From a0cfec39ef6db9c65c4002f7359635f9a608e9ab Mon Sep 17 00:00:00 2001 From: yuyi Date: Tue, 12 Mar 2024 11:17:28 +0800 Subject: [PATCH 06/10] fix(naming): wrong name of task flow of MaintainServerAfterBootstrap --- internal/resource/observer/observer_flow.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/resource/observer/observer_flow.go b/internal/resource/observer/observer_flow.go index b8a70ac80..0ee2b35e0 100644 --- a/internal/resource/observer/observer_flow.go +++ b/internal/resource/observer/observer_flow.go @@ -31,7 +31,7 @@ func PrepareOBServerForBootstrap() *tasktypes.TaskFlow { func MaintainOBServerAfterBootstrap() *tasktypes.TaskFlow { return &tasktypes.TaskFlow{ OperationContext: &tasktypes.OperationContext{ - Name: fPrepareOBServerForBootstrap, + Name: fMaintainOBServerAfterBootstrap, Tasks: []tasktypes.TaskName{tWaitOBClusterBootstrapped, tAddServer, tWaitOBServerActiveInCluster}, TargetStatus: serverstatus.Running, }, From e4ae8254682b970efa6cdbb08d3e2d6469ec1c96 Mon Sep 17 00:00:00 2001 From: yuyi Date: Tue, 12 Mar 2024 11:57:06 +0800 Subject: [PATCH 07/10] fix(backup): add handler to force delete backup policy --- .../business/oceanbase/obtenantbackup.go | 5 +- .../business/oceanbase/obtenantbackup_test.go | 12 +- internal/dashboard/generated/swagger/docs.go | 490 +++++++++++++++++- .../dashboard/generated/swagger/swagger.json | 490 +++++++++++++++++- .../dashboard/generated/swagger/swagger.yaml | 321 +++++++++++- .../dashboard/handler/obtenant_handler.go | 3 +- internal/oceanbase/clients.go | 1 + internal/oceanbase/obtenant.go | 16 + internal/oceanbase/schema/obresourcerescue.go | 33 ++ 9 files changed, 1356 insertions(+), 15 deletions(-) create mode 100644 internal/oceanbase/schema/obresourcerescue.go diff --git a/internal/dashboard/business/oceanbase/obtenantbackup.go b/internal/dashboard/business/oceanbase/obtenantbackup.go index d3c32892c..88816727f 100644 --- a/internal/dashboard/business/oceanbase/obtenantbackup.go +++ b/internal/dashboard/business/oceanbase/obtenantbackup.go @@ -407,11 +407,14 @@ func UpdateTenantBackupPolicy(ctx context.Context, nn types.NamespacedName, p *p return buildBackupPolicyModelType(np), nil } -func DeleteTenantBackupPolicy(ctx context.Context, nn types.NamespacedName) error { +func DeleteTenantBackupPolicy(ctx context.Context, nn types.NamespacedName, force bool) error { policy, err := oceanbase.GetTenantBackupPolicy(ctx, nn) if err != nil { return oberr.NewBadRequest(err.Error()) } + if force { + return oceanbase.ForceDeleteTenantBackupPolicy(ctx, types.NamespacedName{Name: policy.Name, Namespace: policy.Namespace}) + } return oceanbase.DeleteTenantBackupPolicy(ctx, types.NamespacedName{Name: policy.Name, Namespace: policy.Namespace}) } diff --git a/internal/dashboard/business/oceanbase/obtenantbackup_test.go b/internal/dashboard/business/oceanbase/obtenantbackup_test.go index 34f935772..38f5dba99 100644 --- a/internal/dashboard/business/oceanbase/obtenantbackup_test.go +++ b/internal/dashboard/business/oceanbase/obtenantbackup_test.go @@ -48,8 +48,10 @@ var _ = Describe("OBTenantBackup", func() { ScheduleDates: scheduleDates, ScheduleTime: "04:00", }, - JobKeepDays: 3, - RecoveryDays: 7, + DaysFieldBase: param.DaysFieldBase{ + JobKeepDays: 3, + RecoveryDays: 7, + }, }, } policy := buildBackupPolicyApiType(types.NamespacedName{Name: "t1", Namespace: "default"}, "fake-cluster", &p) @@ -108,8 +110,10 @@ var _ = Describe("OBTenantBackup", func() { ScheduleDates: scheduleDates, ScheduleTime: "04:00", }, - JobKeepDays: 3, - RecoveryDays: 7, + DaysFieldBase: param.DaysFieldBase{ + JobKeepDays: 3, + RecoveryDays: 7, + }, }, } policy := buildBackupPolicyApiType(types.NamespacedName{Name: "t1", Namespace: "default"}, "fake-cluster", &p) diff --git a/internal/dashboard/generated/swagger/docs.go b/internal/dashboard/generated/swagger/docs.go index 3013b6963..beb278d20 100644 --- a/internal/dashboard/generated/swagger/docs.go +++ b/internal/dashboard/generated/swagger/docs.go @@ -634,6 +634,65 @@ const docTemplate = `{ } } }, + "/api/v1/metrics/telemetry": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get telemetry data", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Metric" + ], + "summary": "get telemetry data", + "operationId": "GetTelemetryData", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.APIResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.TelemetryReportResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.APIResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.APIResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.APIResponse" + } + } + } + } + }, "/api/v1/obclusters": { "get": { "security": [ @@ -2015,6 +2074,13 @@ const docTemplate = `{ "name": "name", "in": "path", "required": true + }, + { + "type": "string", + "default": "false", + "description": "force delete", + "name": "force", + "in": "query" } ], "responses": { @@ -2529,6 +2595,366 @@ const docTemplate = `{ } } }, + "models.K8sEvent": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "firstTimestamp": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "lastTimestamp": { + "type": "string" + }, + "message": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "resourceName": { + "type": "string" + } + } + }, + "models.OBBackupPolicy": { + "type": "object", + "properties": { + "archiveDestType": { + "type": "string" + }, + "archiveSwitchPieceInterval": { + "type": "string" + }, + "bakDataDestType": { + "type": "string" + }, + "bakDataFullCrontab": { + "type": "string" + }, + "bakDataIncrCrontab": { + "type": "string" + }, + "encryptBakData": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "runningFlow": { + "type": "string" + }, + "runningTask": { + "type": "string" + }, + "status": { + "type": "string" + }, + "taskStatus": { + "type": "string" + }, + "tenantCR": { + "type": "string" + }, + "tenantName": { + "type": "string" + }, + "uid": { + "type": "string" + } + } + }, + "models.OBCluster": { + "type": "object", + "properties": { + "clusterId": { + "type": "integer" + }, + "clusterMode": { + "type": "string" + }, + "clusterName": { + "type": "string" + }, + "configuredBackupVolume": { + "type": "boolean" + }, + "configuredMonitor": { + "type": "boolean" + }, + "cpu": { + "type": "integer" + }, + "dataStorage": { + "$ref": "#/definitions/models.StorageSpec" + }, + "image": { + "type": "string" + }, + "independentPVC": { + "type": "boolean" + }, + "memory": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "redoLogStorage": { + "$ref": "#/definitions/models.StorageSpec" + }, + "runningFlow": { + "type": "string" + }, + "runningTask": { + "type": "string" + }, + "singlePVC": { + "type": "boolean" + }, + "status": { + "type": "string" + }, + "sysLogStorage": { + "$ref": "#/definitions/models.StorageSpec" + }, + "taskStatus": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "zones": { + "type": "array", + "items": { + "$ref": "#/definitions/models.OBZoneStatus" + } + } + } + }, + "models.OBServer": { + "type": "object", + "properties": { + "clusterCR": { + "type": "string" + }, + "clusterId": { + "type": "integer" + }, + "clusterName": { + "type": "string" + }, + "cni": { + "type": "string" + }, + "image": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "podIPHash": { + "type": "string" + }, + "podPhase": { + "type": "string" + }, + "runningFlow": { + "type": "string" + }, + "runningTask": { + "type": "string" + }, + "serviceIPHash": { + "type": "string" + }, + "status": { + "type": "string" + }, + "taskStatus": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "zoneName": { + "type": "string" + } + } + }, + "models.OBTenant": { + "type": "object", + "properties": { + "archiveDestType": { + "type": "string" + }, + "bakDataDestType": { + "type": "string" + }, + "clusterName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "primaryTenant": { + "type": "string" + }, + "runningFlow": { + "type": "string" + }, + "runningTask": { + "type": "string" + }, + "status": { + "type": "string" + }, + "taskStatus": { + "type": "string" + }, + "tenantName": { + "type": "string" + }, + "tenantRole": { + "type": "string" + }, + "topology": { + "type": "array", + "items": { + "$ref": "#/definitions/models.OBTenantResourcePool" + } + }, + "uid": { + "type": "string" + }, + "unitNumber": { + "type": "integer" + } + } + }, + "models.OBTenantResourcePool": { + "type": "object", + "properties": { + "IOPSWeight": { + "type": "integer" + }, + "logDiskSize": { + "type": "integer" + }, + "maxCPU": { + "type": "integer" + }, + "maxIOPS": { + "type": "integer" + }, + "memorySize": { + "type": "integer" + }, + "minCPU": { + "type": "integer" + }, + "minIOPS": { + "type": "integer" + }, + "priority": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "unitNumber": { + "type": "integer" + }, + "zone": { + "type": "string" + } + } + }, + "models.OBZone": { + "type": "object", + "properties": { + "clusterCR": { + "type": "string" + }, + "clusterId": { + "type": "integer" + }, + "clusterName": { + "type": "string" + }, + "image": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "runningFlow": { + "type": "string" + }, + "runningTask": { + "type": "string" + }, + "status": { + "type": "string" + }, + "taskStatus": { + "type": "string" + }, + "uid": { + "type": "string" + } + } + }, + "models.OBZoneStatus": { + "type": "object", + "properties": { + "replica": { + "type": "integer" + }, + "status": { + "type": "string" + }, + "zoneName": { + "type": "string" + } + } + }, + "models.StorageSpec": { + "type": "object", + "properties": { + "storageClass": { + "type": "string" + }, + "storageSize": { + "type": "integer" + } + } + }, "param.ChangeTenantRole": { "type": "object", "properties": { @@ -2996,15 +3422,15 @@ const docTemplate = `{ "param.UpdateBackupPolicy": { "type": "object", "properties": { - "jobKeepWindow": { + "jobKeepDays": { "type": "integer", "example": 5 }, - "pieceInterval": { + "pieceIntervalDays": { "type": "integer", "example": 1 }, - "recoveryWindow": { + "recoveryDays": { "type": "integer", "example": 3 }, @@ -3914,6 +4340,64 @@ const docTemplate = `{ "type": "string" } } + }, + "response.TelemetryData": { + "type": "object", + "properties": { + "backupPolicies": { + "type": "array", + "items": { + "$ref": "#/definitions/models.OBBackupPolicy" + } + }, + "clusters": { + "type": "array", + "items": { + "$ref": "#/definitions/models.OBCluster" + } + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/definitions/models.OBServer" + } + }, + "tenants": { + "type": "array", + "items": { + "$ref": "#/definitions/models.OBTenant" + } + }, + "version": { + "type": "string" + }, + "warningEvents": { + "type": "array", + "items": { + "$ref": "#/definitions/models.K8sEvent" + } + }, + "zones": { + "type": "array", + "items": { + "$ref": "#/definitions/models.OBZone" + } + } + } + }, + "response.TelemetryReportResponse": { + "type": "object", + "properties": { + "component": { + "type": "string" + }, + "content": { + "$ref": "#/definitions/response.TelemetryData" + }, + "time": { + "type": "string" + } + } } }, "securityDefinitions": { diff --git a/internal/dashboard/generated/swagger/swagger.json b/internal/dashboard/generated/swagger/swagger.json index ef8755b44..8eae02ddf 100644 --- a/internal/dashboard/generated/swagger/swagger.json +++ b/internal/dashboard/generated/swagger/swagger.json @@ -627,6 +627,65 @@ } } }, + "/api/v1/metrics/telemetry": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get telemetry data", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Metric" + ], + "summary": "get telemetry data", + "operationId": "GetTelemetryData", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.APIResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.TelemetryReportResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.APIResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.APIResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.APIResponse" + } + } + } + } + }, "/api/v1/obclusters": { "get": { "security": [ @@ -2008,6 +2067,13 @@ "name": "name", "in": "path", "required": true + }, + { + "type": "string", + "default": "false", + "description": "force delete", + "name": "force", + "in": "query" } ], "responses": { @@ -2522,6 +2588,366 @@ } } }, + "models.K8sEvent": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "firstTimestamp": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "lastTimestamp": { + "type": "string" + }, + "message": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "resourceName": { + "type": "string" + } + } + }, + "models.OBBackupPolicy": { + "type": "object", + "properties": { + "archiveDestType": { + "type": "string" + }, + "archiveSwitchPieceInterval": { + "type": "string" + }, + "bakDataDestType": { + "type": "string" + }, + "bakDataFullCrontab": { + "type": "string" + }, + "bakDataIncrCrontab": { + "type": "string" + }, + "encryptBakData": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "runningFlow": { + "type": "string" + }, + "runningTask": { + "type": "string" + }, + "status": { + "type": "string" + }, + "taskStatus": { + "type": "string" + }, + "tenantCR": { + "type": "string" + }, + "tenantName": { + "type": "string" + }, + "uid": { + "type": "string" + } + } + }, + "models.OBCluster": { + "type": "object", + "properties": { + "clusterId": { + "type": "integer" + }, + "clusterMode": { + "type": "string" + }, + "clusterName": { + "type": "string" + }, + "configuredBackupVolume": { + "type": "boolean" + }, + "configuredMonitor": { + "type": "boolean" + }, + "cpu": { + "type": "integer" + }, + "dataStorage": { + "$ref": "#/definitions/models.StorageSpec" + }, + "image": { + "type": "string" + }, + "independentPVC": { + "type": "boolean" + }, + "memory": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "redoLogStorage": { + "$ref": "#/definitions/models.StorageSpec" + }, + "runningFlow": { + "type": "string" + }, + "runningTask": { + "type": "string" + }, + "singlePVC": { + "type": "boolean" + }, + "status": { + "type": "string" + }, + "sysLogStorage": { + "$ref": "#/definitions/models.StorageSpec" + }, + "taskStatus": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "zones": { + "type": "array", + "items": { + "$ref": "#/definitions/models.OBZoneStatus" + } + } + } + }, + "models.OBServer": { + "type": "object", + "properties": { + "clusterCR": { + "type": "string" + }, + "clusterId": { + "type": "integer" + }, + "clusterName": { + "type": "string" + }, + "cni": { + "type": "string" + }, + "image": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "podIPHash": { + "type": "string" + }, + "podPhase": { + "type": "string" + }, + "runningFlow": { + "type": "string" + }, + "runningTask": { + "type": "string" + }, + "serviceIPHash": { + "type": "string" + }, + "status": { + "type": "string" + }, + "taskStatus": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "zoneName": { + "type": "string" + } + } + }, + "models.OBTenant": { + "type": "object", + "properties": { + "archiveDestType": { + "type": "string" + }, + "bakDataDestType": { + "type": "string" + }, + "clusterName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "primaryTenant": { + "type": "string" + }, + "runningFlow": { + "type": "string" + }, + "runningTask": { + "type": "string" + }, + "status": { + "type": "string" + }, + "taskStatus": { + "type": "string" + }, + "tenantName": { + "type": "string" + }, + "tenantRole": { + "type": "string" + }, + "topology": { + "type": "array", + "items": { + "$ref": "#/definitions/models.OBTenantResourcePool" + } + }, + "uid": { + "type": "string" + }, + "unitNumber": { + "type": "integer" + } + } + }, + "models.OBTenantResourcePool": { + "type": "object", + "properties": { + "IOPSWeight": { + "type": "integer" + }, + "logDiskSize": { + "type": "integer" + }, + "maxCPU": { + "type": "integer" + }, + "maxIOPS": { + "type": "integer" + }, + "memorySize": { + "type": "integer" + }, + "minCPU": { + "type": "integer" + }, + "minIOPS": { + "type": "integer" + }, + "priority": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "unitNumber": { + "type": "integer" + }, + "zone": { + "type": "string" + } + } + }, + "models.OBZone": { + "type": "object", + "properties": { + "clusterCR": { + "type": "string" + }, + "clusterId": { + "type": "integer" + }, + "clusterName": { + "type": "string" + }, + "image": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "runningFlow": { + "type": "string" + }, + "runningTask": { + "type": "string" + }, + "status": { + "type": "string" + }, + "taskStatus": { + "type": "string" + }, + "uid": { + "type": "string" + } + } + }, + "models.OBZoneStatus": { + "type": "object", + "properties": { + "replica": { + "type": "integer" + }, + "status": { + "type": "string" + }, + "zoneName": { + "type": "string" + } + } + }, + "models.StorageSpec": { + "type": "object", + "properties": { + "storageClass": { + "type": "string" + }, + "storageSize": { + "type": "integer" + } + } + }, "param.ChangeTenantRole": { "type": "object", "properties": { @@ -2989,15 +3415,15 @@ "param.UpdateBackupPolicy": { "type": "object", "properties": { - "jobKeepWindow": { + "jobKeepDays": { "type": "integer", "example": 5 }, - "pieceInterval": { + "pieceIntervalDays": { "type": "integer", "example": 1 }, - "recoveryWindow": { + "recoveryDays": { "type": "integer", "example": 3 }, @@ -3907,6 +4333,64 @@ "type": "string" } } + }, + "response.TelemetryData": { + "type": "object", + "properties": { + "backupPolicies": { + "type": "array", + "items": { + "$ref": "#/definitions/models.OBBackupPolicy" + } + }, + "clusters": { + "type": "array", + "items": { + "$ref": "#/definitions/models.OBCluster" + } + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/definitions/models.OBServer" + } + }, + "tenants": { + "type": "array", + "items": { + "$ref": "#/definitions/models.OBTenant" + } + }, + "version": { + "type": "string" + }, + "warningEvents": { + "type": "array", + "items": { + "$ref": "#/definitions/models.K8sEvent" + } + }, + "zones": { + "type": "array", + "items": { + "$ref": "#/definitions/models.OBZone" + } + } + } + }, + "response.TelemetryReportResponse": { + "type": "object", + "properties": { + "component": { + "type": "string" + }, + "content": { + "$ref": "#/definitions/response.TelemetryData" + }, + "time": { + "type": "string" + } + } } }, "securityDefinitions": { diff --git a/internal/dashboard/generated/swagger/swagger.yaml b/internal/dashboard/generated/swagger/swagger.yaml index eb7705f58..e05dbd666 100644 --- a/internal/dashboard/generated/swagger/swagger.yaml +++ b/internal/dashboard/generated/swagger/swagger.yaml @@ -50,6 +50,243 @@ definitions: storageClass: type: string type: object + models.K8sEvent: + properties: + count: + type: integer + firstTimestamp: + type: string + kind: + type: string + lastTimestamp: + type: string + message: + type: string + name: + type: string + namespace: + type: string + reason: + type: string + resourceName: + type: string + type: object + models.OBBackupPolicy: + properties: + archiveDestType: + type: string + archiveSwitchPieceInterval: + type: string + bakDataDestType: + type: string + bakDataFullCrontab: + type: string + bakDataIncrCrontab: + type: string + encryptBakData: + type: boolean + name: + type: string + namespace: + type: string + runningFlow: + type: string + runningTask: + type: string + status: + type: string + taskStatus: + type: string + tenantCR: + type: string + tenantName: + type: string + uid: + type: string + type: object + models.OBCluster: + properties: + clusterId: + type: integer + clusterMode: + type: string + clusterName: + type: string + configuredBackupVolume: + type: boolean + configuredMonitor: + type: boolean + cpu: + type: integer + dataStorage: + $ref: '#/definitions/models.StorageSpec' + image: + type: string + independentPVC: + type: boolean + memory: + type: integer + name: + type: string + namespace: + type: string + redoLogStorage: + $ref: '#/definitions/models.StorageSpec' + runningFlow: + type: string + runningTask: + type: string + singlePVC: + type: boolean + status: + type: string + sysLogStorage: + $ref: '#/definitions/models.StorageSpec' + taskStatus: + type: string + uid: + type: string + zones: + items: + $ref: '#/definitions/models.OBZoneStatus' + type: array + type: object + models.OBServer: + properties: + clusterCR: + type: string + clusterId: + type: integer + clusterName: + type: string + cni: + type: string + image: + type: string + name: + type: string + namespace: + type: string + podIPHash: + type: string + podPhase: + type: string + runningFlow: + type: string + runningTask: + type: string + serviceIPHash: + type: string + status: + type: string + taskStatus: + type: string + uid: + type: string + zoneName: + type: string + type: object + models.OBTenant: + properties: + archiveDestType: + type: string + bakDataDestType: + type: string + clusterName: + type: string + name: + type: string + namespace: + type: string + primaryTenant: + type: string + runningFlow: + type: string + runningTask: + type: string + status: + type: string + taskStatus: + type: string + tenantName: + type: string + tenantRole: + type: string + topology: + items: + $ref: '#/definitions/models.OBTenantResourcePool' + type: array + uid: + type: string + unitNumber: + type: integer + type: object + models.OBTenantResourcePool: + properties: + IOPSWeight: + type: integer + logDiskSize: + type: integer + maxCPU: + type: integer + maxIOPS: + type: integer + memorySize: + type: integer + minCPU: + type: integer + minIOPS: + type: integer + priority: + type: integer + type: + type: string + unitNumber: + type: integer + zone: + type: string + type: object + models.OBZone: + properties: + clusterCR: + type: string + clusterId: + type: integer + clusterName: + type: string + image: + type: string + name: + type: string + namespace: + type: string + runningFlow: + type: string + runningTask: + type: string + status: + type: string + taskStatus: + type: string + uid: + type: string + type: object + models.OBZoneStatus: + properties: + replica: + type: integer + status: + type: string + zoneName: + type: string + type: object + models.StorageSpec: + properties: + storageClass: + type: string + storageSize: + type: integer + type: object param.ChangeTenantRole: properties: failover: @@ -369,13 +606,13 @@ definitions: type: object param.UpdateBackupPolicy: properties: - jobKeepWindow: + jobKeepDays: example: 5 type: integer - pieceInterval: + pieceIntervalDays: example: 1 type: integer - recoveryWindow: + recoveryDays: example: 3 type: integer scheduleDates: @@ -995,6 +1232,44 @@ definitions: volumeBindingMode: type: string type: object + response.TelemetryData: + properties: + backupPolicies: + items: + $ref: '#/definitions/models.OBBackupPolicy' + type: array + clusters: + items: + $ref: '#/definitions/models.OBCluster' + type: array + servers: + items: + $ref: '#/definitions/models.OBServer' + type: array + tenants: + items: + $ref: '#/definitions/models.OBTenant' + type: array + version: + type: string + warningEvents: + items: + $ref: '#/definitions/models.K8sEvent' + type: array + zones: + items: + $ref: '#/definitions/models.OBZone' + type: array + type: object + response.TelemetryReportResponse: + properties: + component: + type: string + content: + $ref: '#/definitions/response.TelemetryData' + time: + type: string + type: object info: contact: {} description: OceanBase Dashboard @@ -1383,6 +1658,41 @@ paths: summary: query metrics tags: - Metric + /api/v1/metrics/telemetry: + get: + consumes: + - application/json + description: get telemetry data + operationId: GetTelemetryData + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.APIResponse' + - properties: + data: + $ref: '#/definitions/response.TelemetryReportResponse' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.APIResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.APIResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.APIResponse' + security: + - ApiKeyAuth: [] + summary: get telemetry data + tags: + - Metric /api/v1/obclusters: get: consumes: @@ -2116,6 +2426,11 @@ paths: name: name required: true type: string + - default: "false" + description: force delete + in: query + name: force + type: string produces: - application/json responses: diff --git a/internal/dashboard/handler/obtenant_handler.go b/internal/dashboard/handler/obtenant_handler.go index 486640af6..4af52dfd8 100644 --- a/internal/dashboard/handler/obtenant_handler.go +++ b/internal/dashboard/handler/obtenant_handler.go @@ -474,6 +474,7 @@ func UpdateBackupPolicy(c *gin.Context) (*response.BackupPolicy, error) { // @Failure 500 object response.APIResponse // @Param namespace path string true "obtenant namespace" // @Param name path string true "obtenant name" +// @Param force query string false "force delete" default(false) // @Router /api/v1/obtenants/{namespace}/{name}/backupPolicy [DELETE] // @Security ApiKeyAuth func DeleteBackupPolicy(c *gin.Context) (*response.OBTenantDetail, error) { @@ -485,7 +486,7 @@ func DeleteBackupPolicy(c *gin.Context) (*response.OBTenantDetail, error) { err = oceanbase.DeleteTenantBackupPolicy(c, types.NamespacedName{ Namespace: nn.Namespace, Name: nn.Name, - }) + }, c.Query("force") == "true") if err != nil { return nil, httpErr.NewInternal(err.Error()) } diff --git a/internal/oceanbase/clients.go b/internal/oceanbase/clients.go index ba91aec2b..6a9d0620b 100644 --- a/internal/oceanbase/clients.go +++ b/internal/oceanbase/clients.go @@ -26,4 +26,5 @@ var ( BackupJobClient = client.NewDynamicResourceClient[*v1alpha1.OBTenantBackup](schema.OBTenantBackupGVR, schema.OBTenantBackupKind) OperationClient = client.NewDynamicResourceClient[*v1alpha1.OBTenantOperation](schema.OBTenantOperationGVR, schema.OBTenantOperationKind) BackupPolicyClient = client.NewDynamicResourceClient[*v1alpha1.OBTenantBackupPolicy](schema.OBTenantBackupPolicyGVR, schema.OBTenantBackupPolicyKind) + RescueClient = client.NewDynamicResourceClient[*v1alpha1.OBResourceRescue](schema.OBResourceRescueGVR, schema.OBResourceRescueResource) ) diff --git a/internal/oceanbase/obtenant.go b/internal/oceanbase/obtenant.go index 2739a0e81..3dd490a9e 100644 --- a/internal/oceanbase/obtenant.go +++ b/internal/oceanbase/obtenant.go @@ -21,6 +21,7 @@ import ( "github.com/oceanbase/ob-operator/api/v1alpha1" oceanbaseconst "github.com/oceanbase/ob-operator/internal/const/oceanbase" + "github.com/oceanbase/ob-operator/internal/oceanbase/schema" ) func CreateOBTenant(ctx context.Context, tenant *v1alpha1.OBTenant) (*v1alpha1.OBTenant, error) { @@ -79,6 +80,21 @@ func DeleteTenantBackupPolicy(ctx context.Context, nn types.NamespacedName) erro return BackupPolicyClient.Delete(ctx, nn.Namespace, nn.Name, metav1.DeleteOptions{}) } +func ForceDeleteTenantBackupPolicy(ctx context.Context, nn types.NamespacedName) error { + _, err := RescueClient.Create(ctx, &v1alpha1.OBResourceRescue{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "force-delete-", + }, + Spec: v1alpha1.OBResourceRescueSpec{ + TargetKind: schema.OBTenantBackupPolicyKind, + TargetResName: nn.Name, + Type: "delete", + Namespace: nn.Namespace, + }, + }, metav1.CreateOptions{}) + return err +} + func ListBackupJobs(ctx context.Context, listOption metav1.ListOptions) (*v1alpha1.OBTenantBackupList, error) { list := &v1alpha1.OBTenantBackupList{} err := BackupJobClient.List(ctx, "", list, listOption) diff --git a/internal/oceanbase/schema/obresourcerescue.go b/internal/oceanbase/schema/obresourcerescue.go new file mode 100644 index 000000000..f63923248 --- /dev/null +++ b/internal/oceanbase/schema/obresourcerescue.go @@ -0,0 +1,33 @@ +/* +Copyright (c) 2023 OceanBase +ob-operator is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. +*/ + +package schema + +import "k8s.io/apimachinery/pkg/runtime/schema" + +const ( + OBResourceRescueKind = "OBResourceRescue" + OBResourceRescueResource = "obresourcerescues" +) + +var ( + OBResourceRescueGVR = schema.GroupVersionResource{ + Group: Group, + Version: Version, + Resource: OBResourceRescueResource, + } + OBResourceRescueGVK = schema.GroupVersionKind{ + Group: Group, + Version: Version, + Kind: OBResourceRescueKind, + } +) From 40d9e142f063d5479ad3ea96295a8c025d3a8533 Mon Sep 17 00:00:00 2001 From: yuyi Date: Tue, 12 Mar 2024 15:33:35 +0800 Subject: [PATCH 08/10] fix(auth): uncomment checking username in cache or not --- internal/dashboard/middleware/authentication.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/dashboard/middleware/authentication.go b/internal/dashboard/middleware/authentication.go index edc43d94f..d90f36dff 100644 --- a/internal/dashboard/middleware/authentication.go +++ b/internal/dashboard/middleware/authentication.go @@ -41,13 +41,13 @@ func LoginRequired() gin.HandlerFunc { } username := session.Get("username").(string) - // _, exist := store.GetCache().Load(username) - // if !exist { - // c.AbortWithStatusJSON(401, gin.H{ - // "message": "login required", - // }) - // return - // } + _, exist := store.GetCache().Load(username) + if !exist { + c.AbortWithStatusJSON(401, gin.H{ + "message": "login required", + }) + return + } expr := session.Get("expiration") if expr == nil || expr.(int64) < 0 { From 2983d19fb20d50db947c447f3bb4891b1d8d2d8f Mon Sep 17 00:00:00 2001 From: yuyi Date: Tue, 12 Mar 2024 16:44:03 +0800 Subject: [PATCH 09/10] fix(naming): rename get statistic data handler --- internal/dashboard/generated/swagger/docs.go | 76 +++++------ .../dashboard/generated/swagger/swagger.json | 76 +++++------ .../dashboard/generated/swagger/swagger.yaml | 54 ++++---- internal/dashboard/handler/info_handler.go | 128 ++++++++++++++++++ internal/dashboard/handler/metric_handler.go | 127 ----------------- .../response/{telemetry.go => statistics.go} | 6 +- internal/dashboard/router/v1/metric_router.go | 2 +- 7 files changed, 235 insertions(+), 234 deletions(-) rename internal/dashboard/model/response/{telemetry.go => statistics.go} (90%) diff --git a/internal/dashboard/generated/swagger/docs.go b/internal/dashboard/generated/swagger/docs.go index beb278d20..1cdf60835 100644 --- a/internal/dashboard/generated/swagger/docs.go +++ b/internal/dashboard/generated/swagger/docs.go @@ -649,10 +649,10 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Metric" + "Info" ], "summary": "get telemetry data", - "operationId": "GetTelemetryData", + "operationId": "GetStatistics", "responses": { "200": { "description": "OK", @@ -665,7 +665,7 @@ const docTemplate = `{ "type": "object", "properties": { "data": { - "$ref": "#/definitions/response.TelemetryReportResponse" + "$ref": "#/definitions/response.StatisticDataResponse" } } } @@ -4309,39 +4309,7 @@ const docTemplate = `{ } } }, - "response.StorageClass": { - "type": "object", - "properties": { - "allowVolumeExpansion": { - "type": "boolean" - }, - "mountOptions": { - "type": "array", - "items": { - "type": "string" - } - }, - "name": { - "type": "string" - }, - "parameters": { - "type": "array", - "items": { - "$ref": "#/definitions/common.KVPair" - } - }, - "provisioner": { - "type": "string" - }, - "reclaimPolicy": { - "type": "string" - }, - "volumeBindingMode": { - "type": "string" - } - } - }, - "response.TelemetryData": { + "response.StatisticData": { "type": "object", "properties": { "backupPolicies": { @@ -4385,19 +4353,51 @@ const docTemplate = `{ } } }, - "response.TelemetryReportResponse": { + "response.StatisticDataResponse": { "type": "object", "properties": { "component": { "type": "string" }, "content": { - "$ref": "#/definitions/response.TelemetryData" + "$ref": "#/definitions/response.StatisticData" }, "time": { "type": "string" } } + }, + "response.StorageClass": { + "type": "object", + "properties": { + "allowVolumeExpansion": { + "type": "boolean" + }, + "mountOptions": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/common.KVPair" + } + }, + "provisioner": { + "type": "string" + }, + "reclaimPolicy": { + "type": "string" + }, + "volumeBindingMode": { + "type": "string" + } + } } }, "securityDefinitions": { diff --git a/internal/dashboard/generated/swagger/swagger.json b/internal/dashboard/generated/swagger/swagger.json index 8eae02ddf..beb95bb88 100644 --- a/internal/dashboard/generated/swagger/swagger.json +++ b/internal/dashboard/generated/swagger/swagger.json @@ -642,10 +642,10 @@ "application/json" ], "tags": [ - "Metric" + "Info" ], "summary": "get telemetry data", - "operationId": "GetTelemetryData", + "operationId": "GetStatistics", "responses": { "200": { "description": "OK", @@ -658,7 +658,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/definitions/response.TelemetryReportResponse" + "$ref": "#/definitions/response.StatisticDataResponse" } } } @@ -4302,39 +4302,7 @@ } } }, - "response.StorageClass": { - "type": "object", - "properties": { - "allowVolumeExpansion": { - "type": "boolean" - }, - "mountOptions": { - "type": "array", - "items": { - "type": "string" - } - }, - "name": { - "type": "string" - }, - "parameters": { - "type": "array", - "items": { - "$ref": "#/definitions/common.KVPair" - } - }, - "provisioner": { - "type": "string" - }, - "reclaimPolicy": { - "type": "string" - }, - "volumeBindingMode": { - "type": "string" - } - } - }, - "response.TelemetryData": { + "response.StatisticData": { "type": "object", "properties": { "backupPolicies": { @@ -4378,19 +4346,51 @@ } } }, - "response.TelemetryReportResponse": { + "response.StatisticDataResponse": { "type": "object", "properties": { "component": { "type": "string" }, "content": { - "$ref": "#/definitions/response.TelemetryData" + "$ref": "#/definitions/response.StatisticData" }, "time": { "type": "string" } } + }, + "response.StorageClass": { + "type": "object", + "properties": { + "allowVolumeExpansion": { + "type": "boolean" + }, + "mountOptions": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/common.KVPair" + } + }, + "provisioner": { + "type": "string" + }, + "reclaimPolicy": { + "type": "string" + }, + "volumeBindingMode": { + "type": "string" + } + } } }, "securityDefinitions": { diff --git a/internal/dashboard/generated/swagger/swagger.yaml b/internal/dashboard/generated/swagger/swagger.yaml index e05dbd666..9ab774531 100644 --- a/internal/dashboard/generated/swagger/swagger.yaml +++ b/internal/dashboard/generated/swagger/swagger.yaml @@ -1211,28 +1211,7 @@ definitions: until: type: string type: object - response.StorageClass: - properties: - allowVolumeExpansion: - type: boolean - mountOptions: - items: - type: string - type: array - name: - type: string - parameters: - items: - $ref: '#/definitions/common.KVPair' - type: array - provisioner: - type: string - reclaimPolicy: - type: string - volumeBindingMode: - type: string - type: object - response.TelemetryData: + response.StatisticData: properties: backupPolicies: items: @@ -1261,15 +1240,36 @@ definitions: $ref: '#/definitions/models.OBZone' type: array type: object - response.TelemetryReportResponse: + response.StatisticDataResponse: properties: component: type: string content: - $ref: '#/definitions/response.TelemetryData' + $ref: '#/definitions/response.StatisticData' time: type: string type: object + response.StorageClass: + properties: + allowVolumeExpansion: + type: boolean + mountOptions: + items: + type: string + type: array + name: + type: string + parameters: + items: + $ref: '#/definitions/common.KVPair' + type: array + provisioner: + type: string + reclaimPolicy: + type: string + volumeBindingMode: + type: string + type: object info: contact: {} description: OceanBase Dashboard @@ -1663,7 +1663,7 @@ paths: consumes: - application/json description: get telemetry data - operationId: GetTelemetryData + operationId: GetStatistics produces: - application/json responses: @@ -1674,7 +1674,7 @@ paths: - $ref: '#/definitions/response.APIResponse' - properties: data: - $ref: '#/definitions/response.TelemetryReportResponse' + $ref: '#/definitions/response.StatisticDataResponse' type: object "400": description: Bad Request @@ -1692,7 +1692,7 @@ paths: - ApiKeyAuth: [] summary: get telemetry data tags: - - Metric + - Info /api/v1/obclusters: get: consumes: diff --git a/internal/dashboard/handler/info_handler.go b/internal/dashboard/handler/info_handler.go index 7d921c9a5..91de93feb 100644 --- a/internal/dashboard/handler/info_handler.go +++ b/internal/dashboard/handler/info_handler.go @@ -13,12 +13,23 @@ See the Mulan PSL v2 for more details. package handler import ( + "fmt" "strings" + "time" "github.com/gin-gonic/gin" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/oceanbase/ob-operator/api/v1alpha1" "github.com/oceanbase/ob-operator/internal/dashboard/model/response" + "github.com/oceanbase/ob-operator/internal/oceanbase" + "github.com/oceanbase/ob-operator/internal/store" + "github.com/oceanbase/ob-operator/internal/telemetry" + "github.com/oceanbase/ob-operator/internal/telemetry/models" crypto "github.com/oceanbase/ob-operator/pkg/crypto" + httpErr "github.com/oceanbase/ob-operator/pkg/errors" + "github.com/oceanbase/ob-operator/pkg/k8s/client" ) var ( @@ -47,3 +58,120 @@ func GetProcessInfo(_ *gin.Context) (*response.DashboardInfo, error) { PublicKey: string(pubBytes), }, nil } + +// @ID GetStatistics +// @Summary get telemetry data +// @Description get telemetry data +// @Tags Info +// @Accept application/json +// @Produce application/json +// @Success 200 object response.APIResponse{data=response.StatisticDataResponse} +// @Failure 400 object response.APIResponse +// @Failure 401 object response.APIResponse +// @Failure 500 object response.APIResponse +// @Router /api/v1/metrics/telemetry [GET] +// @Security ApiKeyAuth +func GetStatistics(c *gin.Context) (*response.StatisticDataResponse, error) { + reportData := response.StatisticData{} + telemetryIpKey := fmt.Sprintf("get-telemetry-data:%s", c.RemoteIP()) + shouldFetch := true + + latestFetchTime, ok := store.GetCache().Load(telemetryIpKey) + if ok { + if timestamp, ok := latestFetchTime.(int64); ok { + latestFetchedAt := time.Unix(timestamp, 0) + if latestFetchedAt.Add(10 * time.Minute).After(time.Now()) { + shouldFetch = false + } + } + } + if !shouldFetch { + return nil, nil + } + + clusterList := v1alpha1.OBClusterList{} + err := oceanbase.ClusterClient.List(c, corev1.NamespaceAll, &clusterList, metav1.ListOptions{}) + if err != nil { + return nil, httpErr.NewInternal(err.Error()) + } + reportData.Clusters = make([]models.OBCluster, 0, len(clusterList.Items)) + for i := range clusterList.Items { + modelCluster := telemetry.TransformReportOBCluster(&clusterList.Items[i]) + reportData.Clusters = append(reportData.Clusters, *modelCluster) + } + + zoneList := v1alpha1.OBZoneList{} + err = oceanbase.ZoneClient.List(c, corev1.NamespaceAll, &zoneList, metav1.ListOptions{}) + if err != nil { + return nil, httpErr.NewInternal(err.Error()) + } + reportData.Zones = make([]models.OBZone, 0, len(zoneList.Items)) + for i := range zoneList.Items { + modelZone := telemetry.TransformReportOBZone(&zoneList.Items[i]) + reportData.Zones = append(reportData.Zones, *modelZone) + } + + serverList := v1alpha1.OBServerList{} + err = oceanbase.ServerClient.List(c, corev1.NamespaceAll, &serverList, metav1.ListOptions{}) + if err != nil { + return nil, httpErr.NewInternal(err.Error()) + } + reportData.Servers = make([]models.OBServer, 0, len(serverList.Items)) + for i := range serverList.Items { + modelServer := telemetry.TransformReportOBServer(&serverList.Items[i]) + reportData.Servers = append(reportData.Servers, *modelServer) + } + + tenantList := v1alpha1.OBTenantList{} + err = oceanbase.TenantClient.List(c, corev1.NamespaceAll, &tenantList, metav1.ListOptions{}) + if err != nil { + return nil, httpErr.NewInternal(err.Error()) + } + reportData.Tenants = make([]models.OBTenant, 0, len(tenantList.Items)) + for i := range tenantList.Items { + modelTenant := telemetry.TransformReportOBTenant(&tenantList.Items[i]) + reportData.Tenants = append(reportData.Tenants, *modelTenant) + } + + backupPolicyList := v1alpha1.OBTenantBackupPolicyList{} + err = oceanbase.BackupPolicyClient.List(c, corev1.NamespaceAll, &backupPolicyList, metav1.ListOptions{}) + if err != nil { + return nil, httpErr.NewInternal(err.Error()) + } + reportData.BackupPolicies = make([]models.OBBackupPolicy, 0, len(backupPolicyList.Items)) + for i := range backupPolicyList.Items { + modelBackupPolicy := telemetry.TransformReportOBBackupPolicy(&backupPolicyList.Items[i]) + reportData.BackupPolicies = append(reportData.BackupPolicies, *modelBackupPolicy) + } + + clt := client.GetClient() + eventList, err := clt.ClientSet.CoreV1().Events(corev1.NamespaceAll).List(c, metav1.ListOptions{FieldSelector: "type=Warning"}) + if err != nil { + return nil, httpErr.NewInternal(err.Error()) + } + reportData.WarningEvents = make([]models.K8sEvent, 0, len(eventList.Items)) + for i := range eventList.Items { + modelEvent := &models.K8sEvent{ + Reason: eventList.Items[i].Reason, + Message: eventList.Items[i].Message, + Name: eventList.Items[i].Name, + Namespace: eventList.Items[i].Namespace, + LastTimestamp: eventList.Items[i].LastTimestamp.Format(time.DateTime), + FirstTimestamp: eventList.Items[i].FirstTimestamp.Format(time.DateTime), + Count: eventList.Items[i].Count, + Kind: eventList.Items[i].InvolvedObject.Kind, + ResourceName: eventList.Items[i].InvolvedObject.Name, + } + reportData.WarningEvents = append(reportData.WarningEvents, *modelEvent) + } + reportData.Version = Version + + currentTime := time.Now() + store.GetCache().Store(telemetryIpKey, currentTime.Unix()) + + return &response.StatisticDataResponse{ + Component: telemetry.TelemetryComponentDashboard, + Time: currentTime.Format(time.DateTime), + Content: &reportData, + }, nil +} diff --git a/internal/dashboard/handler/metric_handler.go b/internal/dashboard/handler/metric_handler.go index 37e290d85..7f1926c5b 100644 --- a/internal/dashboard/handler/metric_handler.go +++ b/internal/dashboard/handler/metric_handler.go @@ -14,24 +14,14 @@ package handler import ( "errors" - "fmt" - "time" "github.com/gin-gonic/gin" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/oceanbase/ob-operator/api/v1alpha1" "github.com/oceanbase/ob-operator/internal/dashboard/business/metric" metricconst "github.com/oceanbase/ob-operator/internal/dashboard/business/metric/constant" "github.com/oceanbase/ob-operator/internal/dashboard/model/param" "github.com/oceanbase/ob-operator/internal/dashboard/model/response" - "github.com/oceanbase/ob-operator/internal/oceanbase" - "github.com/oceanbase/ob-operator/internal/store" - "github.com/oceanbase/ob-operator/internal/telemetry" - "github.com/oceanbase/ob-operator/internal/telemetry/models" httpErr "github.com/oceanbase/ob-operator/pkg/errors" - "github.com/oceanbase/ob-operator/pkg/k8s/client" ) // @ID ListAllMetrics @@ -84,120 +74,3 @@ func QueryMetrics(c *gin.Context) ([]response.MetricData, error) { metricDatas := metric.QueryMetricData(queryParam) return metricDatas, nil } - -// @ID GetTelemetryData -// @Summary get telemetry data -// @Description get telemetry data -// @Tags Metric -// @Accept application/json -// @Produce application/json -// @Success 200 object response.APIResponse{data=response.TelemetryReportResponse} -// @Failure 400 object response.APIResponse -// @Failure 401 object response.APIResponse -// @Failure 500 object response.APIResponse -// @Router /api/v1/metrics/telemetry [GET] -// @Security ApiKeyAuth -func GetTelemetryData(c *gin.Context) (*response.TelemetryReportResponse, error) { - reportData := response.TelemetryData{} - telemetryIpKey := fmt.Sprintf("get-telemetry-data:%s", c.RemoteIP()) - shouldFetch := true - - latestFetchTime, ok := store.GetCache().Load(telemetryIpKey) - if ok { - if timestamp, ok := latestFetchTime.(int64); ok { - latestFetchedAt := time.Unix(timestamp, 0) - if latestFetchedAt.Add(10 * time.Minute).After(time.Now()) { - shouldFetch = false - } - } - } - if !shouldFetch { - return nil, nil - } - - clusterList := v1alpha1.OBClusterList{} - err := oceanbase.ClusterClient.List(c, corev1.NamespaceAll, &clusterList, metav1.ListOptions{}) - if err != nil { - return nil, httpErr.NewInternal(err.Error()) - } - reportData.Clusters = make([]models.OBCluster, 0, len(clusterList.Items)) - for i := range clusterList.Items { - modelCluster := telemetry.TransformReportOBCluster(&clusterList.Items[i]) - reportData.Clusters = append(reportData.Clusters, *modelCluster) - } - - zoneList := v1alpha1.OBZoneList{} - err = oceanbase.ZoneClient.List(c, corev1.NamespaceAll, &zoneList, metav1.ListOptions{}) - if err != nil { - return nil, httpErr.NewInternal(err.Error()) - } - reportData.Zones = make([]models.OBZone, 0, len(zoneList.Items)) - for i := range zoneList.Items { - modelZone := telemetry.TransformReportOBZone(&zoneList.Items[i]) - reportData.Zones = append(reportData.Zones, *modelZone) - } - - serverList := v1alpha1.OBServerList{} - err = oceanbase.ServerClient.List(c, corev1.NamespaceAll, &serverList, metav1.ListOptions{}) - if err != nil { - return nil, httpErr.NewInternal(err.Error()) - } - reportData.Servers = make([]models.OBServer, 0, len(serverList.Items)) - for i := range serverList.Items { - modelServer := telemetry.TransformReportOBServer(&serverList.Items[i]) - reportData.Servers = append(reportData.Servers, *modelServer) - } - - tenantList := v1alpha1.OBTenantList{} - err = oceanbase.TenantClient.List(c, corev1.NamespaceAll, &tenantList, metav1.ListOptions{}) - if err != nil { - return nil, httpErr.NewInternal(err.Error()) - } - reportData.Tenants = make([]models.OBTenant, 0, len(tenantList.Items)) - for i := range tenantList.Items { - modelTenant := telemetry.TransformReportOBTenant(&tenantList.Items[i]) - reportData.Tenants = append(reportData.Tenants, *modelTenant) - } - - backupPolicyList := v1alpha1.OBTenantBackupPolicyList{} - err = oceanbase.BackupPolicyClient.List(c, corev1.NamespaceAll, &backupPolicyList, metav1.ListOptions{}) - if err != nil { - return nil, httpErr.NewInternal(err.Error()) - } - reportData.BackupPolicies = make([]models.OBBackupPolicy, 0, len(backupPolicyList.Items)) - for i := range backupPolicyList.Items { - modelBackupPolicy := telemetry.TransformReportOBBackupPolicy(&backupPolicyList.Items[i]) - reportData.BackupPolicies = append(reportData.BackupPolicies, *modelBackupPolicy) - } - - clt := client.GetClient() - eventList, err := clt.ClientSet.CoreV1().Events(corev1.NamespaceAll).List(c, metav1.ListOptions{FieldSelector: "type=Warning"}) - if err != nil { - return nil, httpErr.NewInternal(err.Error()) - } - reportData.WarningEvents = make([]models.K8sEvent, 0, len(eventList.Items)) - for i := range eventList.Items { - modelEvent := &models.K8sEvent{ - Reason: eventList.Items[i].Reason, - Message: eventList.Items[i].Message, - Name: eventList.Items[i].Name, - Namespace: eventList.Items[i].Namespace, - LastTimestamp: eventList.Items[i].LastTimestamp.Format(time.DateTime), - FirstTimestamp: eventList.Items[i].FirstTimestamp.Format(time.DateTime), - Count: eventList.Items[i].Count, - Kind: eventList.Items[i].InvolvedObject.Kind, - ResourceName: eventList.Items[i].InvolvedObject.Name, - } - reportData.WarningEvents = append(reportData.WarningEvents, *modelEvent) - } - reportData.Version = Version - - currentTime := time.Now() - store.GetCache().Store(telemetryIpKey, currentTime.Unix()) - - return &response.TelemetryReportResponse{ - Component: telemetry.TelemetryComponentDashboard, - Time: currentTime.Format(time.DateTime), - Content: &reportData, - }, nil -} diff --git a/internal/dashboard/model/response/telemetry.go b/internal/dashboard/model/response/statistics.go similarity index 90% rename from internal/dashboard/model/response/telemetry.go rename to internal/dashboard/model/response/statistics.go index 9c7f0316d..cc599f702 100644 --- a/internal/dashboard/model/response/telemetry.go +++ b/internal/dashboard/model/response/statistics.go @@ -14,7 +14,7 @@ package response import "github.com/oceanbase/ob-operator/internal/telemetry/models" -type TelemetryData struct { +type StatisticData struct { Version string `json:"version"` Clusters []models.OBCluster `json:"clusters"` Zones []models.OBZone `json:"zones"` @@ -24,8 +24,8 @@ type TelemetryData struct { WarningEvents []models.K8sEvent `json:"warningEvents"` } -type TelemetryReportResponse struct { +type StatisticDataResponse struct { Component string `json:"component"` Time string `json:"time"` - Content *TelemetryData `json:"content"` + Content *StatisticData `json:"content"` } diff --git a/internal/dashboard/router/v1/metric_router.go b/internal/dashboard/router/v1/metric_router.go index dc2a6cbe4..9b6b76f74 100644 --- a/internal/dashboard/router/v1/metric_router.go +++ b/internal/dashboard/router/v1/metric_router.go @@ -21,5 +21,5 @@ import ( func InitMetricRoutes(g *gin.RouterGroup) { g.GET("/metrics", h.Wrap(h.ListMetricMetas)) g.POST("/metrics/query", h.Wrap(h.QueryMetrics)) - g.GET("/metrics/telemetry", h.Wrap(h.GetTelemetryData)) + g.GET("/metrics/telemetry", h.Wrap(h.GetStatistics)) } From 7679e02266e410c7013d549e483b1e5d43e588cf Mon Sep 17 00:00:00 2001 From: yuyi Date: Tue, 12 Mar 2024 17:14:54 +0800 Subject: [PATCH 10/10] fix(info): added sending statistic in info api handler --- internal/dashboard/handler/info_handler.go | 27 ++++------------------ internal/dashboard/model/response/info.go | 7 +++--- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/internal/dashboard/handler/info_handler.go b/internal/dashboard/handler/info_handler.go index 91de93feb..c1b656179 100644 --- a/internal/dashboard/handler/info_handler.go +++ b/internal/dashboard/handler/info_handler.go @@ -13,7 +13,7 @@ See the Mulan PSL v2 for more details. package handler import ( - "fmt" + "os" "strings" "time" @@ -24,7 +24,6 @@ import ( "github.com/oceanbase/ob-operator/api/v1alpha1" "github.com/oceanbase/ob-operator/internal/dashboard/model/response" "github.com/oceanbase/ob-operator/internal/oceanbase" - "github.com/oceanbase/ob-operator/internal/store" "github.com/oceanbase/ob-operator/internal/telemetry" "github.com/oceanbase/ob-operator/internal/telemetry/models" crypto "github.com/oceanbase/ob-operator/pkg/crypto" @@ -53,9 +52,10 @@ func GetProcessInfo(_ *gin.Context) (*response.DashboardInfo, error) { return nil, err } return &response.DashboardInfo{ - AppName: "oceanbase-dashboard", - Version: strings.Join([]string{Version, CommitHash, BuildTime}, "-"), - PublicKey: string(pubBytes), + AppName: "oceanbase-dashboard", + Version: strings.Join([]string{Version, CommitHash, BuildTime}, "-"), + PublicKey: string(pubBytes), + ReportStatistics: os.Getenv("DISABLE_REPORT_STATISTICS") != "true", }, nil } @@ -73,22 +73,6 @@ func GetProcessInfo(_ *gin.Context) (*response.DashboardInfo, error) { // @Security ApiKeyAuth func GetStatistics(c *gin.Context) (*response.StatisticDataResponse, error) { reportData := response.StatisticData{} - telemetryIpKey := fmt.Sprintf("get-telemetry-data:%s", c.RemoteIP()) - shouldFetch := true - - latestFetchTime, ok := store.GetCache().Load(telemetryIpKey) - if ok { - if timestamp, ok := latestFetchTime.(int64); ok { - latestFetchedAt := time.Unix(timestamp, 0) - if latestFetchedAt.Add(10 * time.Minute).After(time.Now()) { - shouldFetch = false - } - } - } - if !shouldFetch { - return nil, nil - } - clusterList := v1alpha1.OBClusterList{} err := oceanbase.ClusterClient.List(c, corev1.NamespaceAll, &clusterList, metav1.ListOptions{}) if err != nil { @@ -167,7 +151,6 @@ func GetStatistics(c *gin.Context) (*response.StatisticDataResponse, error) { reportData.Version = Version currentTime := time.Now() - store.GetCache().Store(telemetryIpKey, currentTime.Unix()) return &response.StatisticDataResponse{ Component: telemetry.TelemetryComponentDashboard, diff --git a/internal/dashboard/model/response/info.go b/internal/dashboard/model/response/info.go index da5f01f3d..5f005cc71 100644 --- a/internal/dashboard/model/response/info.go +++ b/internal/dashboard/model/response/info.go @@ -13,7 +13,8 @@ See the Mulan PSL v2 for more details. package response type DashboardInfo struct { - AppName string `json:"appName"` - Version string `json:"version"` - PublicKey string `json:"publicKey"` + AppName string `json:"appName"` + Version string `json:"version"` + PublicKey string `json:"publicKey"` + ReportStatistics bool `json:"reportStatistics"` }