Skip to content

Commit

Permalink
feature: [PC-13433]: add get Adjustment Events
Browse files Browse the repository at this point in the history
  • Loading branch information
piotrkazulak committed Oct 7, 2024
1 parent 925d57d commit 34b34be
Show file tree
Hide file tree
Showing 6 changed files with 295 additions and 2 deletions.
12 changes: 12 additions & 0 deletions internal/budgetadjustments/examples/get_example.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Get Adjustment Events for 'sample-adjustment-name' from 2024-09-23T00:45:00 UTC to 2024-09-23T20:46:00 UTC.
sloctl budgetadjustments get --adjustment-name=sample-adjustment-name --from=2024-09-23T00:45:00Z --to=2024-09-23T20:46:00Z


# Get Adjustment Events for 'sample-adjustment-name' from 2024-09-23T00:45:00 UTC to 2024-09-23T20:46:00 UTC
# only for one slo with sloName and project filters.
sloctl budgetadjustments get \
--adjustment-name=sample-adjustment-name \
--from=2024-09-23T00:45:00Z \
--to=2024-09-23T20:46:00Z \
--project=sample-project-name \
--slo-name=sample-slo-name
96 changes: 96 additions & 0 deletions internal/budgetadjustments/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package budgetadjustments

import (
"time"

"github.com/spf13/cobra"

"github.com/nobl9/sloctl/internal/csv"
)

const (
flagAdjustment = "adjustment-name"
flagFrom = "from"
flagTo = "to"
flagProject = "project"
flagSloName = "slo-name"
)

func registerOutputFormatFlags(
cmd *cobra.Command,
outputFormat, fieldSeparator, recordSeparator *string,
) {
cmd.PersistentFlags().StringVarP(outputFormat, "output", "o", "yaml",
`Output format: one of yaml|json|csv.`)

cmd.PersistentFlags().StringVarP(fieldSeparator, csv.FieldSeparatorFlag, "",
csv.DefaultFieldSeparator, "Field Separator for CSV.")

cmd.PersistentFlags().StringVarP(recordSeparator, csv.RecordSeparatorFlag, "",
csv.DefaultRecordSeparator, "Record Separator for CSV.")

if err := cmd.PersistentFlags().MarkHidden(csv.RecordSeparatorFlag); err != nil {
panic(err)
}
}

func registerAdjustmentFlag(cmd *cobra.Command, storeIn *string) {
cmd.Flags().StringVar(storeIn, flagAdjustment, "", "Name of the Adjustment.")
if err := cmd.MarkFlagRequired(flagAdjustment); err != nil {
panic(err)
}
}

func registerProjectFlag(cmd *cobra.Command, storeIn *string) {
cmd.Flags().StringVarP(storeIn, flagProject, "", "",
"Name of the project. Required when sloName is defined.")
}

func registerSloNameFlag(cmd *cobra.Command, storeIn *string) {
cmd.Flags().StringVarP(storeIn, flagSloName, "", "",
"Name of the SLO. Required when sloName is defined.")
}

type TimeValue struct{ time.Time }

const (
timeLayout = time.RFC3339
timeLayoutString = "RFC3339"
)

func (t *TimeValue) String() string {
if t.IsZero() {
return ""
}
return t.Format(timeLayout)
}

func (t *TimeValue) Set(s string) (err error) {
t.Time, err = time.Parse(timeLayout, s)
return
}

func (t *TimeValue) Type() string {
return "time"
}

func registerFromFlag(
cmd *cobra.Command,
storeIn *TimeValue,
) {
cmd.Flags().
Var(storeIn, flagFrom, "Specifies the start date and time for the data range (in UTC).")
if err := cmd.MarkFlagRequired(flagFrom); err != nil {
panic(err)
}
}

func registerToFlag(
cmd *cobra.Command,
storeIn *TimeValue,
) {
cmd.Flags().Var(storeIn, flagTo, "Specifies the end date and time for the data range (in UTC).")
if err := cmd.MarkFlagRequired(flagTo); err != nil {
panic(err)
}
}
129 changes: 129 additions & 0 deletions internal/budgetadjustments/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package budgetadjustments

import (
_ "embed"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"time"

"github.com/nobl9/nobl9-go/sdk"
"github.com/pkg/errors"
"github.com/spf13/cobra"

"github.com/nobl9/sloctl/internal/printer"
)

type GetCmd struct {
client *sdk.Client
filepath string

Check failure on line 22 in internal/budgetadjustments/get.go

View workflow job for this annotation

GitHub Actions / Run all checks for static analysis

field `filepath` is unused (unused)
dryRun bool
outputFormat string
fieldSeparator string
recordSeparator string
out io.Writer
adjustment string
from, to TimeValue
project, sloName string
}

type SLO struct {
Project string `json:"project" validate:"required"`
Name string `json:"name" validate:"required"`
}

type Event struct {
EventStart time.Time `json:"eventStart"`
EventEnd time.Time `json:"eventEnd"`
Slos []SLO `json:"slos"`
}

//go:embed examples/get_example.sh
var example string

func NewGetCmd(client *sdk.Client) *cobra.Command {
get := &GetCmd{out: os.Stdout}

cmd := &cobra.Command{
Use: "get",
Short: "Return a list of events for given Adjustment with related SLOs.",
Long: "This endpoint only return past and ongoing events (events that are already started)." +
"Please see Editing budget adjustments." +
"Maximum 500 events can be returned." +
"Optional filtering for specific SLO (only one). If SLO is defined we will return only events for that SLO and the result will also include other SLOs that this events have. Sorted by eventStart.",
Example: example,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
get.client = client
project, _ := cmd.Flags().GetString(flagProject)
sloName, _ := cmd.Flags().GetString(flagSloName)
if project != "" {
cmd.MarkFlagRequired(flagSloName)

Check failure on line 63 in internal/budgetadjustments/get.go

View workflow job for this annotation

GitHub Actions / Run all checks for static analysis

Error return value of `cmd.MarkFlagRequired` is not checked (errcheck)
}
if sloName != "" {
cmd.MarkFlagRequired(flagProject)

Check failure on line 66 in internal/budgetadjustments/get.go

View workflow job for this annotation

GitHub Actions / Run all checks for static analysis

Error return value of `cmd.MarkFlagRequired` is not checked (errcheck)
}
},
RunE: func(cmd *cobra.Command, args []string) error { return get.run(cmd) },
}

registerOutputFormatFlags(cmd, &get.outputFormat, &get.fieldSeparator, &get.recordSeparator)
registerAdjustmentFlag(cmd, &get.adjustment)
registerProjectFlag(cmd, &get.project)
registerSloNameFlag(cmd, &get.sloName)
registerFromFlag(cmd, &get.from)
registerToFlag(cmd, &get.to)

return cmd
}

func (g *GetCmd) run(cmd *cobra.Command) error {
if g.dryRun {
g.client.WithDryRun()
}
values := url.Values{"from": {g.from.String()}, "to": {g.to.String()}}
if g.sloName != "" {
values.Add("sloName", g.sloName)
}
if g.project != "" {
values.Add("project", g.project)
}

resBody, err := doRequest(
g.client,
cmd.Context(),
http.MethodGet,
fmt.Sprintf("%s/%s/events", budgetAdjustmentAPI, g.adjustment),
values,
)
if err != nil {
return errors.Wrap(err, "failed to get")
}

var events []Event
if err := json.Unmarshal(resBody, &events); err != nil {
return errors.Wrap(err, "failed parse response")
}

g.printObjects(events)

Check failure on line 110 in internal/budgetadjustments/get.go

View workflow job for this annotation

GitHub Actions / Run all checks for static analysis

Error return value of `g.printObjects` is not checked (errcheck)

return nil
}

func (g *GetCmd) printObjects(objects interface{}) error {
p, err := printer.New(
g.out,
printer.Format(g.outputFormat),
g.fieldSeparator,
g.recordSeparator,
)
if err != nil {
return err
}
if err = p.Print(objects); err != nil {
return err
}
return nil
}
35 changes: 35 additions & 0 deletions internal/budgetadjustments/request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package budgetadjustments

import (
"context"
_ "embed"
"io"
"net/url"

"github.com/nobl9/nobl9-go/sdk"
"github.com/pkg/errors"
)

const budgetAdjustmentAPI = "/v1/budgetadjustments"

func doRequest(
client *sdk.Client,
ctx context.Context,
method, endpoint string,
values url.Values,
) ([]byte, error) {
req, err := client.CreateRequest(ctx, method, endpoint, nil, values, nil)
if err != nil {
return nil, err
}
resp, err := client.HTTP.Do(req)
if err != nil {
return nil, err
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode >= 300 {
data, _ := io.ReadAll(resp.Body)
return nil, errors.Errorf("bad response (status: %d): %s", resp.StatusCode, string(data))
}
return io.ReadAll(resp.Body)
}
19 changes: 19 additions & 0 deletions internal/budgetadjustments/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package budgetadjustments

import (
_ "embed"
"fmt"

"github.com/nobl9/nobl9-go/sdk"
"github.com/spf13/cobra"
)

func NewRootCmd(client *sdk.Client) *cobra.Command {
cmd := &cobra.Command{
Use: "budgetadjustments",
Short: "Manage budgetadjustment.",
}
cmd.PersistentFlags().BoolP("help", "h", false, fmt.Sprintf("Help for %s.", cmd.Name()))
cmd.AddCommand(NewGetCmd(client))
return cmd
}
6 changes: 4 additions & 2 deletions internal/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import (
"os"
"runtime"

"github.com/spf13/cobra"

v1alphaParser "github.com/nobl9/nobl9-go/manifest/v1alpha/parser"
"github.com/nobl9/nobl9-go/sdk"
"github.com/spf13/cobra"

"github.com/nobl9/sloctl/internal/budgetadjustments"
)

const programName = "sloctl"
Expand Down Expand Up @@ -54,6 +55,7 @@ For every command more detailed help is available.`,
rootCmd.AddCommand(root.NewConfigCmd())
rootCmd.AddCommand(root.NewReplayCmd())
rootCmd.AddCommand(root.NewAwsIamIds())
rootCmd.AddCommand(budgetadjustments.NewRootCmd(root.GetClient()))
return rootCmd
}

Expand Down

0 comments on commit 34b34be

Please sign in to comment.