Skip to content

Commit 1d7dc6f

Browse files
author
Gianluca Arbezzano
committed
fix: fixed concurrency issue with profile collection
The GatherAll profiles function was blocking the correct execution of the procedure because the channel used to collect all the profiles from a particular pod was not well managing errors Signed-off-by: Gianluca Arbezzano <gianarb92@gmail.com>
1 parent a5a0874 commit 1d7dc6f

File tree

6 files changed

+27
-23
lines changed

6 files changed

+27
-23
lines changed

cmd/kubectl-profefe/main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import (
44
"os"
55

66
"github.com/gianarb/kube-profefe/pkg/cmd"
7+
"go.uber.org/zap"
78
"k8s.io/cli-runtime/pkg/genericclioptions"
89
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
910
)
1011

1112
func main() {
12-
rootCmd := cmd.NewProfefeCmd(genericclioptions.IOStreams{
13+
logger, _ := zap.NewDevelopment()
14+
rootCmd := cmd.NewProfefeCmd(logger, genericclioptions.IOStreams{
1315
In: os.Stdin,
1416
Out: os.Stdout,
1517
ErrOut: os.Stderr,

pkg/cmd/capture.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/google/pprof/profile"
1717
"github.com/spf13/cobra"
1818
"github.com/spf13/pflag"
19+
"go.uber.org/zap"
1920
corev1 "k8s.io/api/core/v1"
2021
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2122
"k8s.io/cli-runtime/pkg/genericclioptions"
@@ -32,7 +33,7 @@ var (
3233
ProfefeHostPortE string
3334
)
3435

35-
func NewCaptureCmd(configFlag *genericclioptions.ConfigFlags, rbFlags *genericclioptions.ResourceBuilderFlags, streams genericclioptions.IOStreams) *cobra.Command {
36+
func NewCaptureCmd(logger *zap.Logger, configFlag *genericclioptions.ConfigFlags, rbFlags *genericclioptions.ResourceBuilderFlags, streams genericclioptions.IOStreams) *cobra.Command {
3637
captureCmd := &cobra.Command{
3738
Use: "capture",
3839
Short: "Capture gathers profiles for a pod or a set of them. If can filter by namespace and via label selector.",
@@ -134,7 +135,7 @@ func NewCaptureCmd(configFlag *genericclioptions.ConfigFlags, rbFlags *genericcl
134135

135136
println("gathering profiles for pod: " + target.Name)
136137

137-
profiles, err := pprofutil.GatherAllByPod(context.Background(), DefaultForwardHost, target, localPort)
138+
profiles, err := pprofutil.GatherAllByPod(context.Background(), logger, DefaultForwardHost, target, localPort)
138139
if err != nil {
139140
panic(err)
140141
}

pkg/cmd/get_profiles.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ func NewGetProfilesCmd() *cobra.Command {
4242
req.To = time.Now().UTC()
4343
req.From = req.To.Add(-from).UTC()
4444

45+
fmt.Printf("FROM: %s - TO: %s\n", req.From.Format(time.RFC1123), req.To.Format(time.RFC1123))
46+
4547
resp, err := pClient.GetProfiles(ctx, req)
4648
if err != nil {
4749
return err

pkg/cmd/kprofefe.go

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import (
55
"fmt"
66
"net/http"
77
"sync"
8+
"time"
89

9-
backoff "github.com/cenkalti/backoff/v3"
1010
"github.com/gianarb/kube-profefe/pkg/kubeutil"
1111
"github.com/gianarb/kube-profefe/pkg/pprofutil"
1212
"github.com/gianarb/kube-profefe/pkg/profefe"
@@ -112,13 +112,15 @@ func NewKProfefeCmd(logger *zap.Logger, streams genericclioptions.IOStreams) *co
112112
for {
113113
pod, more := <-c
114114
if more == false {
115+
logger.Info("there are not pods to process. Closing goroutine...")
115116
wg.Done()
116117
return
117118
}
119+
ctx, cancel := context.WithTimeout(ctx, time.Second*40)
120+
defer cancel()
118121
do(ctx, logger, pClient, pod)
119122
}
120123
}(poolC)
121-
122124
}
123125

124126
for _, target := range selectedPods {
@@ -146,21 +148,15 @@ func do(ctx context.Context, l *zap.Logger, pClient *profefe.Client, target core
146148
targetPort := pprofutil.GetProfefePortByPod(target)
147149
var profiles map[pprofutil.Profile]*profile.Profile
148150
var err error
149-
err = backoff.Retry(func() error {
150-
profiles, err = pprofutil.GatherAllByPod(context.Background(), fmt.Sprintf("http://%s", target.Status.PodIP), target, targetPort)
151-
if err != nil {
152-
return err
153-
}
154-
return nil
155-
}, backoff.NewExponentialBackOff())
151+
profiles, err = pprofutil.GatherAllByPod(ctx, logger, fmt.Sprintf("http://%s", target.Status.PodIP), target, targetPort)
156152
if err != nil {
157153
logger.Error("impossible to gather profiles", zap.Error(err))
158154
return
159155
}
160156
for profileType, profile := range profiles {
161157
profefeType := profefe.NewProfileTypeFromString(profileType.String())
162158
if profefeType == profefe.UnknownProfile {
163-
logger.Warn("Unknown profile type it can not be sent to profefe. Skip this profile")
159+
logger.Warn("Unknown profile type it can not be sent to profefe. Skip this profile", zap.String("profile_type", profileType.String()))
164160
continue
165161
}
166162
req := profefe.SavePprofRequest{

pkg/cmd/profefe.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cmd
33
import (
44
"github.com/spf13/cobra"
55
"github.com/spf13/pflag"
6+
"go.uber.org/zap"
67
"k8s.io/cli-runtime/pkg/genericclioptions"
78
)
89

@@ -12,7 +13,7 @@ type ProfefeCmdOptions struct {
1213
genericclioptions.IOStreams
1314
}
1415

15-
func NewProfefeCmd(streams genericclioptions.IOStreams) *cobra.Command {
16+
func NewProfefeCmd(logger *zap.Logger, streams genericclioptions.IOStreams) *cobra.Command {
1617
flags := pflag.NewFlagSet("kubectl-profefe", pflag.ExitOnError)
1718
pflag.CommandLine = flags
1819

@@ -36,7 +37,7 @@ func NewProfefeCmd(streams genericclioptions.IOStreams) *cobra.Command {
3637
kubeResouceBuilderFlags.WithAllNamespaces(false)
3738
kubeResouceBuilderFlags.AddFlags(flags)
3839

39-
captureCmd := NewCaptureCmd(kubeConfigFlags, kubeResouceBuilderFlags, streams)
40+
captureCmd := NewCaptureCmd(logger, kubeConfigFlags, kubeResouceBuilderFlags, streams)
4041
flagsCapture := pflag.NewFlagSet("kubectl-profefe-capture", pflag.ExitOnError)
4142
flagsCapture.StringVar(&OutputDir, "output-dir", "/tmp", "Directory where to place the profiles")
4243
captureCmd.Flags().AddFlagSet(flagsCapture)

pkg/pprofutil/pprof.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"strconv"
77

8+
"go.uber.org/zap"
89
corev1 "k8s.io/api/core/v1"
910

1011
"github.com/google/pprof/profile"
@@ -15,43 +16,44 @@ const (
1516
DefaultProfilePort = 6060
1617
)
1718

18-
func GatherAllByPod(ctx context.Context, host string, pod corev1.Pod, forwardedPort int) (map[Profile]*profile.Profile, error) {
19+
func GatherAllByPod(ctx context.Context, logger *zap.Logger, host string, pod corev1.Pod, forwardedPort int) (map[Profile]*profile.Profile, error) {
1920
path := DefaultProfilePath
2021
if rawPath, ok := pod.Annotations["profefe.com/path"]; ok && rawPath != "" {
2122
path = rawPath
2223
}
23-
return GatherAll(ctx, fmt.Sprintf("%s:%d%s", host, forwardedPort, path))
24+
return GatherAll(ctx, logger, fmt.Sprintf("%s:%d%s", host, forwardedPort, path))
2425
}
2526

2627
// GatherAll downloads all profile types from address.
27-
func GatherAll(ctx context.Context, addr string) (map[Profile]*profile.Profile, error) {
28+
func GatherAll(ctx context.Context, logger *zap.Logger, addr string) (map[Profile]*profile.Profile, error) {
2829
type res struct {
2930
prof *profile.Profile
3031
profileType Profile
3132
err error
3233
}
3334
profileTypes := Profiles()
3435
profiles := make(chan res, len(profileTypes))
36+
3537
for _, p := range profileTypes {
3638
go func(ctx context.Context, addr string, p Profile) {
3739
prof, err := Gather(ctx, addr, p)
40+
if err != nil {
41+
logger.With(zap.String("profefe_profile_type", p.String())).With(zap.Error(err)).Error("Impossible to gather the profile")
42+
}
3843
profiles <- res{prof, p, err}
3944
}(ctx, addr, p)
4045
}
4146

42-
var err error
4347
profs := map[Profile]*profile.Profile{}
4448
for i := 0; i < len(profileTypes); i++ {
4549
p := <-profiles
4650
if p.prof != nil {
4751
profs[p.profileType] = p.prof
4852
}
49-
if p.err != nil {
50-
err = p.err
51-
}
5253
}
5354

54-
return profs, err
55+
close(profiles)
56+
return profs, nil
5557
}
5658

5759
func GetProfefePortByPod(pod corev1.Pod) int {

0 commit comments

Comments
 (0)