Skip to content

Commit

Permalink
[#21355] [yugabyted][UI]: List the Scheduled PITR on yugabyted UI.
Browse files Browse the repository at this point in the history
Summary: Create a new /pitr endpoint for sending the list of scheduled PITRs.

Test Plan: Manual Testing

Reviewers: sgarg-yb, nikhil

Reviewed By: nikhil

Subscribers: djiang, yugabyted-dev, shikhar.sahay

Differential Revision: https://phorge.dev.yugabyte.com/D32992
  • Loading branch information
ShikharSahay committed Mar 20, 2024
1 parent a7cfb26 commit d8b27b1
Show file tree
Hide file tree
Showing 15 changed files with 361 additions and 1 deletion.
61 changes: 61 additions & 0 deletions yugabyted-ui/apiserver/cmd/server/.docs/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ tags:
name: cluster
- description: APIs for getting information about an existing cluster
name: cluster-info
- description: APIs for getting Point-in-Time Recovery (PITR) schedules
name: pitr-info
- description: APIs for getting information about Voyager migrations
name: voyager-info
- description: APIs for getting Voyager data migrations metrics
Expand Down Expand Up @@ -807,6 +809,32 @@ paths:
summary: Get the node address for the current node
tags:
- cluster-info
/pitr:
get:
description: Retrieve the list of PITR schedules in the YugabyteDB cluster.
operationId: getPITRSchedules
responses:
"200":
content:
application/json:
schema:
$ref: '#/components/schemas/PITRScheduleListResponse'
description: List of PITR Schedules in the cluster
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
description: API Error
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
description: API Error
summary: Get PITR Schedules
tags:
- pitr-info
components:
requestBodies:
ClusterSpec:
Expand Down Expand Up @@ -936,6 +964,12 @@ components:
schema:
type: string
description: Node address
PITRScheduleListResponse:
content:
application/json:
schema:
$ref: '#/components/schemas/PITRSchedule'
description: List of PITR Schedules in the cluster
schemas:
VoyagerMigrationDetails:
description: Information regarding a specific voyager migration task
Expand Down Expand Up @@ -2580,6 +2614,33 @@ components:
type: object
title: YSQL connection manager stats
type: object
PITRSchedule:
description: Details of a Point-in-Time Recovery (PITR) schedule
example:
databaseKeyspace: databaseKeyspace
interval: interval
id: 0
retention: retention
earliestRecoverableTime: earliestRecoverableTime
properties:
id:
type: integer
databaseKeyspace:
type: string
interval:
type: string
retention:
type: string
earliestRecoverableTime:
type: string
required:
- databaseKeyspace
- earliestRecoverableTime
- id
- interval
- retention
title: List of PITR Schedules in the cluster
type: object
API_for_Plan_and_Asses_page:
example:
data:
Expand Down
2 changes: 2 additions & 0 deletions yugabyted-ui/apiserver/cmd/server/.openapi-generator/FILES
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ models/model_gflags_info.go
models/model_health_check_info.go
models/model_health_check_response.go
models/model_individual_migration_task_info.go
models/model_schedules_response.go
models/model_is_load_balancer_idle.go
models/model_live_query_response_data.go
models/model_live_query_response_schema.go
Expand All @@ -49,6 +50,7 @@ models/model_node_data.go
models/model_node_data_cloud_info.go
models/model_node_data_metrics.go
models/model_node_data_metrics_active_connections.go
models/model_pitr_schedule.go
models/model_placement_block.go
models/model_placement_cloud_info.go
models/model_placement_info.go
Expand Down
75 changes: 75 additions & 0 deletions yugabyted-ui/apiserver/cmd/server/handlers/api_pitr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package handlers

import (
"apiserver/cmd/server/helpers"
"apiserver/cmd/server/models"
"fmt"
"net/http"
"strconv"
"strings"
"time"

"github.com/labstack/echo/v4"
)

const (
_24h_FORMAT = "2006-01-02 15:04:05"
_12h_FORMAT = "2006-01-02 03:04:05 PM"
)

// Get PITR Configs
func (c *Container) GetPITRConfigurations(ctx echo.Context) error {
pitrConfigFuture := make(chan helpers.PITRConfigFuture)
go c.helper.GetPITRConfigFuture(pitrConfigFuture)
pitrConfigResult := <-pitrConfigFuture
if pitrConfigResult.Error != nil {
errorMessage := fmt.Sprintf("Error retrieving PITR configuration: %v",
pitrConfigResult.Error)
return ctx.String(http.StatusInternalServerError, errorMessage)
}
var response models.PITRScheduleInfo
activeSchedules := false
for i := len(pitrConfigResult.Config.Schedules) - 1; i >= 0; i-- {
schedule := pitrConfigResult.Config.Schedules[i]
if schedule.Options.DeleteTime == "" {
activeSchedules = true
break
}
}
if activeSchedules {
var schedule_entry int32 = 0
for _, schedule := range pitrConfigResult.Config.Schedules {
if schedule.Options.DeleteTime != "" {
continue
}
schedule_entry++
filter := schedule.Options.Filter
name := strings.Split(filter, ".")[1]
interval := "24 hours"
retentionInMinutes,_ := strconv.Atoi(strings.Split(schedule.Options.Retention, " ")[0])
retentionInDays := retentionInMinutes / (24 * 60)
retention := fmt.Sprintf("%d days", retentionInDays)
earliest_recoverable_time := schedule.Snapshots[0].SnapshotTime
parsedTime, err := time.Parse(_24h_FORMAT, earliest_recoverable_time)
if err != nil {
return ctx.JSON(http.StatusInternalServerError, map[string]string{
"error": "Failed to parse snapshot time: " + err.Error(),
})
}
modifiedTime := parsedTime.Add(time.Second).Truncate(time.Second)
formattedTime := modifiedTime.Format(_12h_FORMAT)

scheduleEntry := models.PitrSchedule{
Id: schedule_entry,
DatabaseKeyspace: name,
Interval: interval,
Retention: retention,
EarliestRecoverableTime: formattedTime,
}
response.Schedules = append(response.Schedules, scheduleEntry)
}
} else {
return ctx.JSON(http.StatusOK, []interface{}{})
}
return ctx.JSON(http.StatusOK, response)
}
74 changes: 74 additions & 0 deletions yugabyted-ui/apiserver/cmd/server/helpers/parse_pitr_configs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package helpers

import (
"encoding/json"
"strings"
"fmt"
)

// PITRConfig represents the entire JSON output from the yb-admin command.
type PITRConfig struct {
Schedules []Schedule `json:"schedules"`
}

// Schedule represents each individual schedule in the output.
type Schedule struct {
Options Options `json:"options"`
Snapshots []Snapshot `json:"snapshots"`
}

// Options represents the options for each schedule.
type Options struct {
Filter string `json:"filter"`
Interval string `json:"interval"`
Retention string `json:"retention"`
DeleteTime string `json:"delete_time,omitempty"`
}

// Snapshot represents each snapshot within a schedule.
type Snapshot struct {
SnapshotTime string `json:"snapshot_time"`
}

type PITRConfigFuture struct {
Config PITRConfig
Error error
}

func (h *HelperContainer) GetPITRConfigFuture(future chan PITRConfigFuture) {
pitrConfigResult := PITRConfigFuture{
Config: PITRConfig{},
Error: nil,
}
masterAddresses := MasterAddressCache.Get()
masterAddressesString := strings.Join(masterAddresses, ",")
ybAdminResult, ybAdminError := h.ListSnapshotSchedules(masterAddressesString)
if ybAdminError != nil {
// Fetch updated master addresses and retry
updatedMasterAddressesFuture := make(chan MasterAddressesFuture)
go h.GetMasterAddressesFuture(updatedMasterAddressesFuture)
updatedMasterAddressesResult := <-updatedMasterAddressesFuture
if updatedMasterAddressesResult.Error != nil {
// If fetching updated addresses also fails, return an error
pitrConfigResult.Error = fmt.Errorf("failed to fetch updated master addresses: %v",
updatedMasterAddressesResult.Error)
future <- pitrConfigResult
return
}
// Try with updated addresses
updatedMasterAddressesString := strings.Join(updatedMasterAddressesResult.HostList, ",")
ybAdminResult, ybAdminError = h.ListSnapshotSchedules(updatedMasterAddressesString)
if ybAdminError != nil {
// If the second attempt also fails, return an error
pitrConfigResult.Error = fmt.Errorf("failed to list snapshot schedules: %v",
ybAdminError)
future <- pitrConfigResult
return
}
}
err := json.Unmarshal([]byte(ybAdminResult), &pitrConfigResult.Config)
if err != nil {
pitrConfigResult.Error = fmt.Errorf("failed to unmarshal snapshot schedules: %v", err)
}
future <- pitrConfigResult
}
2 changes: 1 addition & 1 deletion yugabyted-ui/apiserver/cmd/server/helpers/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func (h *HelperContainer) GetBytesFromString(sizeString string) (int64, error) {
}

func (h *HelperContainer) FindBinaryLocation(binaryName string) (string, error) {
YUGABYTE_DIR := filepath.Join("..", "..")
YUGABYTE_DIR, _ := filepath.Abs(filepath.Join("..", "..","..",".."))

dirCandidates := []string{
// Default if tar is downloaded
Expand Down
8 changes: 8 additions & 0 deletions yugabyted-ui/apiserver/cmd/server/helpers/yb_admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ type YBAdminFuture struct {
Error error
}

func (h *HelperContainer) ListSnapshotSchedules(masterAddresses string) (string,error) {
ybAdminFuture := make(chan YBAdminFuture)
params := []string{"-master_addresses", masterAddresses, "list_snapshot_schedules"}
go h.RunYBAdminFuture(params, ybAdminFuture)
ybAdminResult := <-ybAdminFuture
return ybAdminResult.Result, ybAdminResult.Error
}

func (h *HelperContainer) RunYBAdminFuture(params []string, future chan YBAdminFuture) {
ybAdminFuture := YBAdminFuture{
Result: "",
Expand Down
3 changes: 3 additions & 0 deletions yugabyted-ui/apiserver/cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ func main() {
// GetClusterConnections - Get the node address for the current node
e.GET("/api/node_address", c.GetNodeAddress)

// GetPITRConfig - Get the PITR configuration for YugabyteDB cluster
e.GET("/api/pitr", c.GetPITRConfigurations)

render_htmls := templates.NewTemplate()

// Code for rendering UI Without embedding the files
Expand Down
15 changes: 15 additions & 0 deletions yugabyted-ui/apiserver/cmd/server/models/model_pitr_schedule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package models

// PitrSchedule - Details of a Point-in-Time Recovery (PITR) schedule
type PitrSchedule struct {

Id int32 `json:"id"`

DatabaseKeyspace string `json:"databaseKeyspace"`

Interval string `json:"interval"`

Retention string `json:"retention"`

EarliestRecoverableTime string `json:"earliestRecoverableTime"`
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package models

type PITRScheduleInfo struct {

Schedules []PitrSchedule `json:"schedules"`
}
51 changes: 51 additions & 0 deletions yugabyted-ui/apiserver/conf/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ tags:
description: APIs for cluster CRUD
- name: cluster-info
description: APIs for getting information about an existing cluster
- name: pitr-info
description: APIs for getting Point-in-Time Recovery (PITR) schedules
- name: voyager-info
description: APIs for getting information about Voyager migrations
- name: voyager-metrics
Expand Down Expand Up @@ -555,6 +557,20 @@ paths:
$ref: '#/components/responses/ApiError'
'500':
$ref: '#/components/responses/ApiError'
/pitr:
get:
summary: Get PITR Schedules
description: Retrieve the list of PITR schedules in the YugabyteDB cluster.
operationId: getPITRSchedules
tags:
- pitr-info
responses:
'200':
$ref: '#/components/responses/PITRScheduleListResponse'
'400':
$ref: '#/components/responses/ApiError'
'500':
$ref: '#/components/responses/ApiError'
components:
schemas:
VoyagerMigrationDetails:
Expand Down Expand Up @@ -1617,6 +1633,27 @@ components:
properties:
data:
$ref: '#/components/schemas/ConnectionsStatsData'
PITRSchedule:
title: List of PITR Schedules in the cluster
description: Details of a Point-in-Time Recovery (PITR) schedule
type: object
properties:
id:
type: integer
databaseKeyspace:
type: string
interval:
type: string
retention:
type: string
earliestRecoverableTime:
type: string
required:
- id
- databaseKeyspace
- interval
- retention
- earliestRecoverableTime
requestBodies:
ClusterSpec:
description: DB Cluster to be updated
Expand Down Expand Up @@ -1819,6 +1856,20 @@ components:
text/plain:
schema:
type: string
PITRScheduleListResponse:
description: List of PITR Schedules in the cluster
content:
application/json:
schema:
type: object
properties:
schedules:
type: array
uniqueItems: true
items:
$ref: '#/components/schemas/PITRSchedule'
required:
- schedules
securitySchemes:
BearerAuthToken:
type: http
Expand Down
Loading

0 comments on commit d8b27b1

Please sign in to comment.