Skip to content

Commit

Permalink
Merge pull request #38 from profefe/feature/get_profiles_timerange
Browse files Browse the repository at this point in the history
fix(kubectl): better support for get profile timerange
  • Loading branch information
gianarb authored Apr 4, 2020
2 parents 8010a82 + 49d6452 commit 8110f79
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 9 deletions.
61 changes: 53 additions & 8 deletions pkg/cmd/get_profiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"context"
"errors"
"fmt"
"net/http"
"time"
Expand All @@ -14,16 +15,60 @@ import (
var (
service string
profileType string
from time.Duration
to time.Duration
fromRaw string
toRaw string

ErrFromAheadOfTo = fmt.Errorf("From value ahead of to")
)

func fromStringToTime(now time.Time, raw string) (t time.Time, err error) {
var d time.Duration
// If raw is a duration substract it from now and return
if d, err = time.ParseDuration(raw); err == nil {
t = now.Add(d)
return
}
// At this point is is not a duration so it has to be a valid RFC3339
t, err = time.Parse(time.RFC3339, raw)
return
}

func fromRawRangeToTime(now time.Time, fromRaw, toRaw string) (from, to time.Time, err error) {
from, err = fromStringToTime(now, fromRaw)
if err != nil {
return
}

to, err = fromStringToTime(now, toRaw)
if err != nil {
return
}
if to.Sub(from) <= 0 {
return time.Time{}, time.Time{}, ErrFromAheadOfTo
}

return
}
func NewGetProfilesCmd() *cobra.Command {
flags := pflag.NewFlagSet("kprofefe", pflag.ExitOnError)
var (
from time.Time
to time.Time
)

flags := pflag.NewFlagSet("kprofefe", pflag.ExitOnError)
cmd := &cobra.Command{
Use: "profiles",
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(cmd *cobra.Command, args []string) (err error) {
now := time.Now()

from, to, err = fromRawRangeToTime(now, fromRaw, toRaw)
if err != nil {
if errors.Is(err, ErrFromAheadOfTo) {
return fmt.Errorf("from %s is ahead of to %s", from.Format(time.RFC3339), to.Format(time.RFC3339))
}
return err
}

ctx := context.Background()
pClient := profefe.NewClient(profefe.Config{
HostPort: ProfefeHostPort,
Expand All @@ -39,8 +84,8 @@ func NewGetProfilesCmd() *cobra.Command {
}
}

req.To = time.Now().UTC()
req.From = req.To.Add(-from).UTC()
req.From = from.UTC()
req.To = to.UTC()

fmt.Printf("FROM: %s - TO: %s\n", req.From.Format(time.RFC1123), req.To.Format(time.RFC1123))

Expand All @@ -60,8 +105,8 @@ func NewGetProfilesCmd() *cobra.Command {
flags.StringVar(&ProfefeHostPort, "profefe-hostport", "http://localhost:10100", `where profefe is located`)
flags.StringVar(&profileType, "profile-type", "cpu", `The pprof profiles to retrieve`)
flags.StringVar(&service, "service", "", ``)
flags.DurationVar(&from, "from", 24*time.Hour, ``)
flags.DurationVar(&to, "to", 0, ``)
flags.StringVar(&fromRaw, "from", "24h", "Point in time for the first profile got gathered. From can be a duration or a RFC3339 (eg https://validator.w3.org/feed/docs/error/InvalidRFC3339Date.html)")
flags.StringVar(&toRaw, "to", "0m", "Point in time for the first profile got gathered. From can be a duration or a RFC3339 (eg https://validator.w3.org/feed/docs/error/InvalidRFC3339Date.html)")

cmd.Flags().AddFlagSet(flags)

Expand Down
126 changes: 126 additions & 0 deletions pkg/cmd/get_profiles_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package cmd

import (
"fmt"
"testing"
"time"
)

func mustParseTime(t *testing.T, s string) (c time.Time) {
var err error
if c, err = time.Parse(time.RFC3339, s); err != nil {
t.Fatal(err)
}
return
}

func Test_fromRawRangeToTime(t *testing.T) {
input := []struct {
now time.Time
name string
fromRaw string
toRaw string
from time.Time
to time.Time
err error
}{
{
now: mustParseTime(t, "2002-10-02T15:00:00Z"),
to: mustParseTime(t, "2002-10-02T13:00:00Z"),
from: mustParseTime(t, "2002-10-02T10:00:00Z"),
fromRaw: "-5h",
toRaw: "-2h",
err: nil,
},
{
now: mustParseTime(t, "2002-10-02T15:00:00Z"),
toRaw: "-5h",
fromRaw: "-2h",
err: ErrFromAheadOfTo,
},
{
now: mustParseTime(t, "2002-10-02T15:00:00Z"),
toRaw: "2002-10-01T03:00:00Z",
fromRaw: "2002-10-02T02:00:00Z",
err: ErrFromAheadOfTo,
},
{
now: mustParseTime(t, "2002-10-02T15:00:00Z"),
to: mustParseTime(t, "2002-10-02T03:00:00Z"),
from: mustParseTime(t, "2002-10-02T02:00:00Z"),
toRaw: "2002-10-02T03:00:00Z",
fromRaw: "2002-10-02T02:00:00Z",
err: nil,
},
}

for ii, v := range input {
t.Run(fmt.Sprintf("[%d] FROM %s, TO: %s", ii, v.from.Format(time.RFC3339), v.to.Format(time.RFC3339)), func(t *testing.T) {
from, to, err := fromRawRangeToTime(v.now, v.fromRaw, v.toRaw)
if err != v.err {
t.Fatalf("Expected err: %s got %s", v.err, err)
}

if from.String() != v.from.String() {
t.Errorf("Expected FROM %s got %s", v.from.Format(time.RFC3339), from.Format(time.RFC3339))
}

if to.String() != v.to.String() {
t.Errorf("Expected TO: %s got %s", v.to.Format(time.RFC3339), to.Format(time.RFC3339))
}
})
}
}

func Test_fromTimeToString(t *testing.T) {
input := []struct {
now time.Time
name string
fromRaw string
toRaw string
from time.Time
to time.Time
err error
}{
{
now: mustParseTime(t, "2002-10-02T15:00:00Z"),
to: mustParseTime(t, "2002-10-02T13:00:00Z"),
from: mustParseTime(t, "2002-10-02T10:00:00Z"),
fromRaw: "-5h",
toRaw: "-2h",
err: nil,
},
{
now: mustParseTime(t, "2002-10-02T15:00:00Z"),
to: mustParseTime(t, "2002-10-02T03:00:00Z"),
from: mustParseTime(t, "2002-10-02T02:00:00Z"),
toRaw: "2002-10-02T03:00:00Z",
fromRaw: "2002-10-02T02:00:00Z",
err: nil,
},
{
now: mustParseTime(t, "2020-03-01T17:00:00Z"),
from: mustParseTime(t, "2020-02-10T15:00:00Z"),
to: mustParseTime(t, "2020-03-01T15:00:00Z"),
fromRaw: "2020-02-10T15:00:00Z",
toRaw: "-2h",
err: nil,
},
}

for ii, v := range input {
t.Run(fmt.Sprintf("[%d] FROM %s, TO: %s", ii, v.from.Format(time.RFC3339), v.to.Format(time.RFC3339)), func(t *testing.T) {
from, err := fromStringToTime(v.now, v.fromRaw)
if err != nil {
}
if from.String() != v.from.String() {
t.Errorf("Expected FROM %s got %s", v.from.Format(time.RFC3339), from.Format(time.RFC3339))
}

to, err := fromStringToTime(v.now, v.toRaw)
if to.String() != v.to.String() {
t.Errorf("Expected TO: %s got %s", v.to.Format(time.RFC3339), to.Format(time.RFC3339))
}
})
}
}
2 changes: 1 addition & 1 deletion pkg/profefe/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (

type ProfileType int8

var timeFormat = "2006-01-02T15:04:05"
const timeFormat = "2006-01-02T15:04:05"

const (
UnknownProfile ProfileType = iota
Expand Down

0 comments on commit 8110f79

Please sign in to comment.