Skip to content

Commit d1b3f20

Browse files
bulk advise on all pods
1 parent a972390 commit d1b3f20

File tree

3 files changed

+132
-10
lines changed

3 files changed

+132
-10
lines changed

internal/scout/advise/advise.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ import "github.com/sourcegraph/src-cli/internal/scout"
44

55
type Option = func(config *scout.Config)
66

7+
const (
8+
OVER_100 = "\t%s %s: Your %s usage is over 100%% (%.2f%%). Add more %s."
9+
OVER_80 = "\t%s %s: Your %s usage is over 80%% (%.2f%%). Consider raising limits."
10+
OVER_40 = "\t%s %s: Your %s usage is under 80%% (%.2f%%). Keep %s allocation the same."
11+
UNDER_40 = "\t%s %s: Your %s usage is under 40%% (%.2f%%). Consider lowering limits."
12+
)
13+
714
func WithNamespace(namespace string) Option {
815
return func(config *scout.Config) {
916
config.Namespace = namespace

internal/scout/advise/k8s.go

Lines changed: 118 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import (
44
"context"
55
"fmt"
66

7+
"github.com/sourcegraph/sourcegraph/lib/errors"
78
"github.com/sourcegraph/src-cli/internal/scout"
9+
"github.com/sourcegraph/src-cli/internal/scout/kube"
10+
v1 "k8s.io/api/core/v1"
811
"k8s.io/client-go/kubernetes"
912
"k8s.io/client-go/rest"
1013
metricsv "k8s.io/metrics/pkg/client/clientset/versioned"
@@ -33,23 +36,129 @@ func K8s(
3336
opt(cfg)
3437
}
3538

36-
/* pods, err := kube.GetPods(ctx, cfg)
39+
pods, err := kube.GetPods(ctx, cfg)
3740
if err != nil {
3841
return errors.Wrap(err, "could not get list of pods")
39-
} */
42+
}
4043

41-
/* if cfg.Pod != "" {
44+
if cfg.Pod != "" {
4245
pod, err := kube.GetPod(cfg.Pod, pods)
4346
if err != nil {
4447
return errors.Wrap(err, "could not get pod")
4548
}
4649

47-
} */
48-
fmt.Println("advise.K8s needs code")
50+
err = Advise(ctx, cfg, pod)
51+
if err != nil {
52+
return errors.Wrap(err, "could not advise")
53+
}
54+
return nil
55+
}
56+
57+
for _, pod := range pods {
58+
fmt.Println(scout.EmojiFingerPointRight, pod.Name)
59+
err = Advise(ctx, cfg, pod)
60+
if err != nil {
61+
return errors.Wrap(err, "could not advise")
62+
}
63+
}
64+
4965
return nil
5066
}
5167

52-
/* func Advise(ctx context.Context, cfg *scout.Config, pod v1.Pod) error {
53-
fmt.Println(pod.Name)
54-
return nil
55-
} */
68+
func Advise(ctx context.Context, cfg *scout.Config, pod v1.Pod) error {
69+
var advice []string
70+
usageMetrics, err := getUsageMetrics(ctx, cfg, pod)
71+
if err != nil {
72+
return errors.Wrap(err, "could not get usage metrics")
73+
}
74+
75+
for _, metrics := range usageMetrics {
76+
cpuAdvice := checkUsage(metrics.CpuUsage, "CPU", metrics.ContainerName, pod.Name)
77+
advice = append(advice, cpuAdvice)
78+
79+
memoryAdvice := checkUsage(metrics.MemoryUsage, "memory", metrics.ContainerName, pod.Name)
80+
advice = append(advice, memoryAdvice)
81+
82+
if metrics.Storage != nil {
83+
storageAdvice := checkUsage(metrics.StorageUsage, "storage", metrics.ContainerName, pod.Name)
84+
advice = append(advice, storageAdvice)
85+
}
86+
87+
for _, msg := range advice {
88+
fmt.Println(msg)
89+
}
90+
}
91+
92+
return nil
93+
}
94+
95+
func getUsageMetrics(ctx context.Context, cfg *scout.Config, pod v1.Pod) ([]scout.UsageStats, error) {
96+
var usages []scout.UsageStats
97+
var usage scout.UsageStats
98+
podMetrics, err := kube.GetPodMetrics(ctx, cfg, pod)
99+
if err != nil {
100+
return usages, errors.Wrap(err, "while attempting to fetch pod metrics")
101+
}
102+
103+
containerMetrics := &scout.ContainerMetrics{
104+
PodName: cfg.Pod,
105+
Limits: map[string]scout.Resources{},
106+
}
107+
108+
if err = kube.GetLimits(ctx, cfg, &pod, containerMetrics); err != nil {
109+
return usages, errors.Wrap(err, "failed to get get container metrics")
110+
}
111+
112+
for _, container := range podMetrics.Containers {
113+
usage, err = kube.GetUsage(ctx, cfg, *containerMetrics, pod, container)
114+
if err != nil {
115+
return usages, errors.Wrapf(err, "could not compile usages data for row: %s\n", container.Name)
116+
}
117+
usages = append(usages, usage)
118+
}
119+
120+
return usages, nil
121+
}
122+
123+
func checkUsage(usage float64, resourceType, container, pod string) string {
124+
var message string
125+
126+
switch {
127+
case usage >= 100:
128+
message = fmt.Sprintf(
129+
OVER_100,
130+
scout.FlashingLightEmoji,
131+
container,
132+
resourceType,
133+
usage,
134+
resourceType,
135+
)
136+
case usage >= 80 && usage < 100:
137+
message = fmt.Sprintf(
138+
OVER_80,
139+
scout.WarningSign,
140+
container,
141+
resourceType,
142+
usage,
143+
)
144+
case usage >= 40 && usage < 80:
145+
message = fmt.Sprintf(
146+
OVER_40,
147+
scout.SuccessEmoji,
148+
container,
149+
resourceType,
150+
usage,
151+
resourceType,
152+
)
153+
default:
154+
message = fmt.Sprintf(
155+
UNDER_40,
156+
scout.WarningSign,
157+
container,
158+
usage,
159+
resourceType,
160+
)
161+
}
162+
163+
return message
164+
}

internal/scout/constants.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package scout
22

33
const (
4-
ABillion float64 = 1000000000
4+
ABillion float64 = 1_000_000_000
5+
EmojiFingerPointRight = "👉"
6+
FailureEmoji = "🛑"
7+
FlashingLightEmoji = "🚨"
8+
HourglassEmoji = "⌛"
9+
SuccessEmoji = "✅"
10+
WarningSign = "⚠️ " // why does this need an extra space to align?!?!
511
)

0 commit comments

Comments
 (0)