Skip to content

Commit

Permalink
feat: PC-13613 PC-13391 remove replay from queue (#225)
Browse files Browse the repository at this point in the history
**Merge after the feature is released.**

## Motivation

Ability to control the upcoming feature through sloctl.

## Summary

- Upgraded to go 1.23
- Updated linter rules
- Added sloctl replay delete --all and sloctl replay delete sloName -p
projectName

## Testing

- Run replay (normal, without queue)
- Get agents (change in get agents command)
- Add replays to queue, then try deleting them using sloctl (both all
and for specific SLO)


Release notes intentionally left empty, to be filled in when the feature
is released.
## Release Notes
  • Loading branch information
marcinlawnik authored Oct 9, 2024
1 parent 12e0630 commit 86aa897
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 16 deletions.
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ linters:
- dogsled
- errcheck
- exhaustive
- exportloopref
- copyloopvar
- gochecknoinits
- gocognit
- gocritic
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/nobl9/sloctl

go 1.22
go 1.23

require (
github.com/go-playground/validator/v10 v10.22.1
Expand Down
1 change: 0 additions & 1 deletion internal/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,6 @@ func (g *GetCmd) getAgentsWithSecrets(ctx context.Context, objects []manifest.Ob
var mu sync.Mutex
eg, ctx := errgroup.WithContext(ctx)
for i := range objects {
i := i
eg.Go(func() error {
agent, ok := objects[i].(v1alpha.GenericObject)
if !ok {
Expand Down
18 changes: 5 additions & 13 deletions internal/replay.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type ReplayCmd struct {
configPaths []string
sloName string
project string
deleteAll bool
}

//go:embed replay_example.sh
Expand All @@ -47,7 +48,7 @@ func (r *RootCmd) NewReplayCmd() *cobra.Command {
Short: "Retrieve historical SLI data and recalculate their SLO error budgets.",
Long: "Replay pulls in the historical data while your SLO collects new data in real-time. " +
"The historical and current data are merged, producing an error budget calculated for the entire period. " +
"Refer to https://docs.nobl9.com/Features/replay?_highlight=replay for more details on Replay.\n\n" +
"Refer to https://docs.nobl9.com/replay for more details on Replay.\n\n" +
"The 'replay' command allows you to import data for multiple SLOs in bulk. " +
"Before running the Replays it will verify if the SLOs you've provided are eligible for Replay. " +
"It will only run a single Replay simultaneously (current limit for concurrent Replays). " +
Expand All @@ -71,6 +72,8 @@ func (r *RootCmd) NewReplayCmd() *cobra.Command {
`Specifies the Project for the SLOs you want to Replay.`)
cmd.Flags().Var(&replay.from, "from", "Sets the start of Replay time window.")

cmd.AddCommand(replay.AddDeleteCommand())

return cmd
}

Expand Down Expand Up @@ -187,17 +190,6 @@ func (r *ReplayCmd) prepareConfigs() ([]ReplayConfig, error) {
return replays, nil
}

var (
errReplayInvalidOptions = errors.New("you must either run 'sloctl replay' for a single SLO," +
" providing its name as an argument, or provide configuration file using '-f' flag, but not both")
errReplayTooManyArgs = errors.New("'replay' command accepts a single SLO name," +
" If you want to run it for multiple SLOs provide a configuration file instead using '-f' flag")
errReplayMissingFromArg = errors.Errorf("when running 'sloctl replay' for a single SLO,"+
" you must provide Replay window start time (%s layout) with '--from' flag", timeLayoutString)
errProjectWildcardIsNotAllowed = errors.New(
"wildcard Project is not allowed, you must provide specific Project name(s)")
)

func (r *ReplayCmd) arguments(cmd *cobra.Command, args []string) error {
if len(r.configPaths) == 0 && len(args) == 0 {
_ = cmd.Usage()
Expand Down Expand Up @@ -321,7 +313,6 @@ outer:
eg, ctx := errgroup.WithContext(ctx)
eg.SetLimit(10)
for i := range replays {
i := i
eg.Go(func() error {
c := replays[i]
timeNow := time.Now()
Expand Down Expand Up @@ -433,6 +424,7 @@ func (r *ReplayCmd) getReplayStatus(

const (
endpointReplayPost = "/timetravel"
endpointReplayDelete = "/timetravel"
endpointReplayGetStatus = "/timetravel/%s"
endpointReplayGetAvailability = "/internal/timemachine/availability"
endpointGetSLO = "/get/slo"
Expand Down
113 changes: 113 additions & 0 deletions internal/replay_delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package internal

import (
"fmt"
"net/http"

"github.com/mitchellh/colorstring"
"github.com/spf13/cobra"
)

// AddDeleteCommand returns cobra command delete, which allows to delete a queued Replay.
func (r *ReplayCmd) AddDeleteCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "delete <slo-name>",
Short: "Delete a queued Replay",
Args: r.deleteArguments,
RunE: func(cmd *cobra.Command, args []string) error {
if r.project != "" {
r.client.Config.Project = r.project
}
if r.deleteAll {
return r.deleteAllReplays(cmd)
} else {
return r.deleteReplaysForSLO(cmd, r.sloName)
}
},
}

cmd.Flags().StringVarP(&r.project, "project", "p", "",
`Specifies the Project of the SLO you want to remove queued Replays for.`)
cmd.Flags().BoolVar(&r.deleteAll, "all", false, "Delete ALL queued Replays.")

return cmd
}

func (r *ReplayCmd) deleteArguments(cmd *cobra.Command, args []string) error {
if !r.deleteAll && len(args) == 0 {
_ = cmd.Usage()
return errReplayDeleteInvalidOptions
}
if len(args) > 1 {
return errReplayTooManyArgs
}
if len(args) == 1 {
r.sloName = args[0]
}
return nil
}

type deleteReplayRequest struct {
Project string `json:"project,omitempty"`
Slo string `json:"slo,omitempty"`
All bool `json:"all,omitempty"`
}

func (r *ReplayCmd) deleteAllReplays(cmd *cobra.Command) error {
cmd.Println(colorstring.Color("[yellow]Deleting all queued Replays[reset]"))

_, err := r.doRequest(
cmd.Context(),
http.MethodDelete,
endpointReplayDelete,
"",
nil,
deleteReplayRequest{
All: true,
},
)

if err != nil {
return err
}

cmd.Println(colorstring.Color("[green]All queued Replays deleted successfully[reset]"))

return nil
}

func (r *ReplayCmd) deleteReplaysForSLO(cmd *cobra.Command, sloName string) error {
cmd.Println(
colorstring.Printf(
"[yellow]Deleting queued Replays for SLO '%s' in project '%s'[reset]",
sloName,
r.client.Config.Project,
))

_, err := r.doRequest(
cmd.Context(),
http.MethodDelete,
endpointReplayDelete,
r.client.Config.Project,
nil,
deleteReplayRequest{
Project: r.client.Config.Project,
Slo: sloName,
},
)

if err != nil {
return err
}

cmd.Println(
colorstring.Color(
fmt.Sprintf("[green]Queued Replays for SLO '%s' in project '%s' deleted successfully[reset]",
sloName,
r.client.Config.Project,
),
),
)

return nil
}
16 changes: 16 additions & 0 deletions internal/replay_errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package internal

import "github.com/pkg/errors"

var (
errReplayInvalidOptions = errors.New("you must either run 'sloctl replay' for a single SLO," +
" providing its name as an argument, or provide configuration file using '-f' flag, but not both")
errReplayDeleteInvalidOptions = errors.New("you must either run 'sloctl replay delete' for a single " +
"SLO, providing its name as an argument, or use the '--all' flag to delete all queued replays, but not both")
errReplayTooManyArgs = errors.New("'replay' command accepts a single SLO name," +
" If you want to run it for multiple SLOs provide a configuration file instead using '-f' flag")
errReplayMissingFromArg = errors.Errorf("when running 'sloctl replay' for a single SLO,"+
" you must provide Replay window start time (%s layout) with '--from' flag", timeLayoutString)
errProjectWildcardIsNotAllowed = errors.New(
"wildcard Project is not allowed, you must provide specific Project name(s)")
)

0 comments on commit 86aa897

Please sign in to comment.