Skip to content

Commit

Permalink
feat(kubectl): add get commands
Browse files Browse the repository at this point in the history
Now you can do `kubectl get`:

* profiles: to retrieve profiles
* profile-types: to retrieve the list of supported profiles

Signed-off-by: Gianluca Arbezzano <gianarb92@gmail.com>
  • Loading branch information
Gianluca Arbezzano committed Dec 9, 2019
1 parent c0ff993 commit 71a6287
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 0 deletions.
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
Expand Down
17 changes: 17 additions & 0 deletions pkg/cmd/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package cmd

import (
"github.com/spf13/cobra"
)

func NewGetCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "get",
Short: "",
Run: func(cmd *cobra.Command, args []string) {},
}

cmd.AddCommand(NewGetProfilesCmd())
cmd.AddCommand(NewGetProfileTypesCmd())
return cmd
}
20 changes: 20 additions & 0 deletions pkg/cmd/get_profile_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package cmd

import (
"github.com/gianarb/kube-profefe/pkg/profefe"
"github.com/spf13/cobra"
)

func NewGetProfileTypesCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "profile-types",
Short: "Retrieve supported profile types",
RunE: func(cmd *cobra.Command, args []string) error {
for _, v := range profefe.AllProfileTypes() {
println(v)
}
return nil
},
}
return cmd
}
68 changes: 68 additions & 0 deletions pkg/cmd/get_profiles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package cmd

import (
"context"
"fmt"
"net/http"
"time"

"github.com/gianarb/kube-profefe/pkg/profefe"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

var (
service string
profileType string
from time.Duration
to time.Duration
)

func NewGetProfilesCmd() *cobra.Command {
flags := pflag.NewFlagSet("kprofefe", pflag.ExitOnError)

cmd := &cobra.Command{
Use: "profiles",
Short: "Retrieve profiles from profefe",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
pClient := profefe.NewClient(profefe.Config{
HostPort: ProfefeHostPort,
}, http.Client{})

req := profefe.GetProfilesRequest{}
req.Service = service

if profileType != "" {
pt := profefe.NewProfileTypeFromString(profileType)
if pt != profefe.UnknownProfile {
req.Type = pt
}
}

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

resp, err := pClient.GetProfiles(ctx, req)
if err != nil {
return err
}

for _, v := range resp.Body {
println(fmt.Sprintf("ID: %s Type: %s Service: %s CreateAt: %s", v.ID, v.Type, v.Service, v.CreatedAt.Format(time.RFC1123)))
}
return nil
},
}

flags.AddFlagSet(cmd.PersistentFlags())
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, ``)

cmd.Flags().AddFlagSet(flags)

return cmd
}
1 change: 1 addition & 0 deletions pkg/cmd/profefe.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func NewProfefeCmd(streams genericclioptions.IOStreams) *cobra.Command {
flagsCapture.StringVar(&OutputDir, "output-dir", "/tmp", "Directory where to place the profiles")
captureCmd.Flags().AddFlagSet(flagsCapture)
rootCmd.AddCommand(captureCmd)
rootCmd.AddCommand(NewGetCmd())

return rootCmd
}
63 changes: 63 additions & 0 deletions pkg/profefe/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (

type ProfileType int8

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

const (
UnknownProfile ProfileType = iota
CPUProfile
Expand All @@ -26,6 +28,18 @@ const (
OtherProfile = 127
)

func AllProfileTypes() []string {
return []string{
UnknownProfile.String(),
CPUProfile.String(),
HeapProfile.String(),
BlockProfile.String(),
MutexProfile.String(),
GoroutineProfile.String(),
ThreadcreateProfile.String(),
}
}

func (ptype ProfileType) String() string {
switch ptype {
case UnknownProfile:
Expand Down Expand Up @@ -100,6 +114,55 @@ func GetProfileType() []string {
}
}

// GET
// /api/0/profiles?service=<service>&type=<type>from=<created_from>&to=<created_to>&labels=<key=value,key=value>
func (c *Client) GetProfiles(ctx context.Context, req GetProfilesRequest) (*GetProfilesResponse, error) {
buf := bytes.NewBuffer([]byte{})
r, err := http.NewRequestWithContext(ctx, "GET", c.HostPort+"/api/0/profiles", buf)

q := r.URL.Query()
q.Add("from", req.From.Format(timeFormat))
q.Add("to", req.To.Format(timeFormat))
q.Add("type", req.Type.String())
q.Add("service", req.Service)
r.URL.RawQuery = q.Encode()

resp, err := c.Do(r)
defer resp.Body.Close()
rr := &GetProfilesResponse{}

err = json.NewDecoder(resp.Body).Decode(rr)
if err != nil {
return nil, err
}
if resp.StatusCode == http.StatusOK {
return rr, nil
}
return nil, fmt.Errorf(rr.Error)
}

type GetProfilesRequest struct {
// service name
Service string
// cpu, heap, block, mutex, or goroutine
Type ProfileType
// a set of key-value pairs, e.g. "region=europe-west3,dc=fra,ip=1.2.3.4,version=1.0"
Labels map[string]string

From, To time.Time
}

type GetProfilesResponse struct {
Code int `json:"code"`
Error string `json:"error"`
Body []struct {
ID string `json:"id"`
Type string `json:"type"`
Service string `json:"service"`
CreatedAt time.Time `json:"created_at"`
} `json:"body"`
}

// https://github.com/profefe/profefe#save-pprof-data
// POST /api/0/profiles?service=<service>&instance_id=<iid>&type=[cpu|heap]&labels=<key=value,key=value>
// body pprof.pb.gz
Expand Down

0 comments on commit 71a6287

Please sign in to comment.