Skip to content

Commit

Permalink
chore: scheduled maintenance query-service impl (#4834)
Browse files Browse the repository at this point in the history
  • Loading branch information
srikanthccv authored May 31, 2024
1 parent f927969 commit 67779d6
Show file tree
Hide file tree
Showing 9 changed files with 927 additions and 11 deletions.
16 changes: 16 additions & 0 deletions pkg/query-service/app/dashboards/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,22 @@ func InitDB(dataSourceName string) (*sqlx.DB, error) {
return nil, fmt.Errorf("error in creating notification_channles table: %s", err.Error())
}

tableSchema := `CREATE TABLE IF NOT EXISTS planned_maintenance (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT,
alert_ids TEXT,
schedule TEXT NOT NULL,
created_at datetime NOT NULL,
created_by TEXT NOT NULL,
updated_at datetime NOT NULL,
updated_by TEXT NOT NULL
);`
_, err = db.Exec(tableSchema)
if err != nil {
return nil, fmt.Errorf("error in creating planned_maintenance table: %s", err.Error())
}

table_schema = `CREATE TABLE IF NOT EXISTS ttl_status (
id INTEGER PRIMARY KEY AUTOINCREMENT,
transaction_id TEXT NOT NULL,
Expand Down
102 changes: 102 additions & 0 deletions pkg/query-service/app/http_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,12 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router, am *AuthMiddleware) {
router.HandleFunc("/api/v1/rules/{id}", am.EditAccess(aH.patchRule)).Methods(http.MethodPatch)
router.HandleFunc("/api/v1/testRule", am.EditAccess(aH.testRule)).Methods(http.MethodPost)

router.HandleFunc("/api/v1/downtime_schedules", am.OpenAccess(aH.listDowntimeSchedules)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/downtime_schedules/{id}", am.OpenAccess(aH.getDowntimeSchedule)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/downtime_schedules", am.OpenAccess(aH.createDowntimeSchedule)).Methods(http.MethodPost)
router.HandleFunc("/api/v1/downtime_schedules/{id}", am.OpenAccess(aH.editDowntimeSchedule)).Methods(http.MethodPut)
router.HandleFunc("/api/v1/downtime_schedules/{id}", am.OpenAccess(aH.deleteDowntimeSchedule)).Methods(http.MethodDelete)

router.HandleFunc("/api/v1/dashboards", am.ViewAccess(aH.getDashboards)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/dashboards", am.EditAccess(aH.createDashboards)).Methods(http.MethodPost)
router.HandleFunc("/api/v1/dashboards/grafana", am.EditAccess(aH.createDashboardsTransform)).Methods(http.MethodPost)
Expand Down Expand Up @@ -535,6 +541,102 @@ func (aH *APIHandler) populateTemporality(ctx context.Context, qp *v3.QueryRange
return nil
}

func (aH *APIHandler) listDowntimeSchedules(w http.ResponseWriter, r *http.Request) {
schedules, err := aH.ruleManager.RuleDB().GetAllPlannedMaintenance(r.Context())
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil)
return
}

// The schedules are stored as JSON in the database, so we need to filter them here
// Since the number of schedules is expected to be small, this should be fine

if r.URL.Query().Get("active") != "" {
activeSchedules := make([]rules.PlannedMaintenance, 0)
active, _ := strconv.ParseBool(r.URL.Query().Get("active"))
for _, schedule := range schedules {
now := time.Now().In(time.FixedZone(schedule.Schedule.Timezone, 0))
if schedule.IsActive(now) == active {
activeSchedules = append(activeSchedules, schedule)
}
}
schedules = activeSchedules
}

if r.URL.Query().Get("recurring") != "" {
recurringSchedules := make([]rules.PlannedMaintenance, 0)
recurring, _ := strconv.ParseBool(r.URL.Query().Get("recurring"))
for _, schedule := range schedules {
if schedule.IsRecurring() == recurring {
recurringSchedules = append(recurringSchedules, schedule)
}
}
schedules = recurringSchedules
}

aH.Respond(w, schedules)
}

func (aH *APIHandler) getDowntimeSchedule(w http.ResponseWriter, r *http.Request) {
id := mux.Vars(r)["id"]
schedule, err := aH.ruleManager.RuleDB().GetPlannedMaintenanceByID(r.Context(), id)
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil)
return
}
aH.Respond(w, schedule)
}

func (aH *APIHandler) createDowntimeSchedule(w http.ResponseWriter, r *http.Request) {
var schedule rules.PlannedMaintenance
err := json.NewDecoder(r.Body).Decode(&schedule)
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, nil)
return
}
if err := schedule.Validate(); err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, nil)
return
}

_, err = aH.ruleManager.RuleDB().CreatePlannedMaintenance(r.Context(), schedule)
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil)
return
}
aH.Respond(w, nil)
}

func (aH *APIHandler) editDowntimeSchedule(w http.ResponseWriter, r *http.Request) {
id := mux.Vars(r)["id"]
var schedule rules.PlannedMaintenance
err := json.NewDecoder(r.Body).Decode(&schedule)
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, nil)
return
}
if err := schedule.Validate(); err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, nil)
return
}
_, err = aH.ruleManager.RuleDB().EditPlannedMaintenance(r.Context(), schedule, id)
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil)
return
}
aH.Respond(w, nil)
}

func (aH *APIHandler) deleteDowntimeSchedule(w http.ResponseWriter, r *http.Request) {
id := mux.Vars(r)["id"]
_, err := aH.ruleManager.RuleDB().DeletePlannedMaintenance(r.Context(), id)
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil)
return
}
aH.Respond(w, nil)
}

func (aH *APIHandler) listRules(w http.ResponseWriter, r *http.Request) {

rules, err := aH.ruleManager.ListRuleStates(r.Context())
Expand Down
93 changes: 93 additions & 0 deletions pkg/query-service/rules/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"time"

"github.com/jmoiron/sqlx"
"go.signoz.io/signoz/pkg/query-service/auth"
"go.signoz.io/signoz/pkg/query-service/common"
"go.uber.org/zap"
)
Expand All @@ -27,6 +28,21 @@ type RuleDB interface {

// GetStoredRule for a given ID from DB
GetStoredRule(ctx context.Context, id string) (*StoredRule, error)

// CreatePlannedMaintenance stores a given maintenance in db
CreatePlannedMaintenance(ctx context.Context, maintenance PlannedMaintenance) (int64, error)

// DeletePlannedMaintenance deletes the given maintenance in the db
DeletePlannedMaintenance(ctx context.Context, id string) (string, error)

// GetPlannedMaintenanceByID fetches the maintenance definition from db by id
GetPlannedMaintenanceByID(ctx context.Context, id string) (*PlannedMaintenance, error)

// EditPlannedMaintenance updates the given maintenance in the db
EditPlannedMaintenance(ctx context.Context, maintenance PlannedMaintenance, id string) (string, error)

// GetAllPlannedMaintenance fetches the maintenance definitions from db
GetAllPlannedMaintenance(ctx context.Context) ([]PlannedMaintenance, error)
}

type StoredRule struct {
Expand Down Expand Up @@ -202,3 +218,80 @@ func (r *ruleDB) GetStoredRule(ctx context.Context, id string) (*StoredRule, err

return rule, nil
}

func (r *ruleDB) GetAllPlannedMaintenance(ctx context.Context) ([]PlannedMaintenance, error) {
maintenances := []PlannedMaintenance{}

query := "SELECT id, name, description, schedule, alert_ids, created_at, created_by, updated_at, updated_by FROM planned_maintenance"

err := r.Select(&maintenances, query)

if err != nil {
zap.L().Error("Error in processing sql query", zap.Error(err))
return nil, err
}

return maintenances, nil
}

func (r *ruleDB) GetPlannedMaintenanceByID(ctx context.Context, id string) (*PlannedMaintenance, error) {
maintenance := &PlannedMaintenance{}

query := "SELECT id, name, description, schedule, alert_ids, created_at, created_by, updated_at, updated_by FROM planned_maintenance WHERE id=$1"
err := r.Get(maintenance, query, id)

if err != nil {
zap.L().Error("Error in processing sql query", zap.Error(err))
return nil, err
}

return maintenance, nil
}

func (r *ruleDB) CreatePlannedMaintenance(ctx context.Context, maintenance PlannedMaintenance) (int64, error) {

email, _ := auth.GetEmailFromJwt(ctx)
maintenance.CreatedBy = email
maintenance.CreatedAt = time.Now()
maintenance.UpdatedBy = email
maintenance.UpdatedAt = time.Now()

query := "INSERT INTO planned_maintenance (name, description, schedule, alert_ids, created_at, created_by, updated_at, updated_by) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)"

result, err := r.Exec(query, maintenance.Name, maintenance.Description, maintenance.Schedule, maintenance.AlertIds, maintenance.CreatedAt, maintenance.CreatedBy, maintenance.UpdatedAt, maintenance.UpdatedBy)

if err != nil {
zap.L().Error("Error in processing sql query", zap.Error(err))
return 0, err
}

return result.LastInsertId()
}

func (r *ruleDB) DeletePlannedMaintenance(ctx context.Context, id string) (string, error) {
query := "DELETE FROM planned_maintenance WHERE id=$1"
_, err := r.Exec(query, id)

if err != nil {
zap.L().Error("Error in processing sql query", zap.Error(err))
return "", err
}

return "", nil
}

func (r *ruleDB) EditPlannedMaintenance(ctx context.Context, maintenance PlannedMaintenance, id string) (string, error) {
email, _ := auth.GetEmailFromJwt(ctx)
maintenance.UpdatedBy = email
maintenance.UpdatedAt = time.Now()

query := "UPDATE planned_maintenance SET name=$1, description=$2, schedule=$3, alert_ids=$4, updated_at=$5, updated_by=$6 WHERE id=$7"
_, err := r.Exec(query, maintenance.Name, maintenance.Description, maintenance.Schedule, maintenance.AlertIds, maintenance.UpdatedAt, maintenance.UpdatedBy, id)

if err != nil {
zap.L().Error("Error in processing sql query", zap.Error(err))
return "", err
}

return "", nil
}
Loading

0 comments on commit 67779d6

Please sign in to comment.