Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test advise package and more tests for kube package #1000

Merged
merged 2 commits into from
Jun 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 70 additions & 1 deletion internal/scout/advise/advise.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package advise

import "github.com/sourcegraph/src-cli/internal/scout"
import (
"context"
"fmt"
"os"

"github.com/sourcegraph/sourcegraph/lib/errors"
"github.com/sourcegraph/src-cli/internal/scout"
)

type Option = func(config *scout.Config)

Expand Down Expand Up @@ -28,3 +35,65 @@ func WithOutput(pathToFile string) Option {
config.Output = pathToFile
}
}

func CheckUsage(usage float64, resourceType string, container string) string {
var message string
switch {
case usage >= 100:
message = fmt.Sprintf(
OVER_100,
scout.FlashingLightEmoji,
container,
resourceType,
usage,
resourceType,
)
case usage >= 80 && usage < 100:
message = fmt.Sprintf(
OVER_80,
scout.WarningSign,
container,
resourceType,
usage,
)
case usage >= 40 && usage < 80:
message = fmt.Sprintf(
OVER_40,
scout.SuccessEmoji,
container,
resourceType,
usage,
resourceType,
)
default:
message = fmt.Sprintf(
UNDER_40,
scout.WarningSign,
container,
resourceType,
usage,
)
}

return message
}

// outputToFile writes resource allocation advice for a Kubernetes pod to a file.
func OutputToFile(ctx context.Context, cfg *scout.Config, name string, advice []string) error {
file, err := os.OpenFile(cfg.Output, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return errors.Wrap(err, "failed to open file")
}
defer file.Close()

if _, err := fmt.Fprintf(file, "- %s\n", name); err != nil {
return errors.Wrap(err, "failed to write service name to file")
}

for _, msg := range advice {
if _, err := fmt.Fprintf(file, "%s\n", msg); err != nil {
return errors.Wrap(err, "failed to write container advice to file")
}
}
return nil
}
128 changes: 128 additions & 0 deletions internal/scout/advise/advise_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package advise

import (
"bufio"
"context"
"os"
"testing"
"time"

"github.com/sourcegraph/src-cli/internal/scout"
)

func TestCheckUsage(t *testing.T) {
cases := []struct {
name string
usage float64
resourceType string
container string
want string
}{
{
name: "should return correct message for usage over 100",
usage: 110,
resourceType: "cpu",
container: "gitserver-0",
want: "\t🚨 gitserver-0: Your cpu usage is over 100% (110.00%). Add more cpu.",
},
{
name: "should return correct message for usage over 80 and under 100",
usage: 87,
resourceType: "memory",
container: "gitserver-0",
want: "\t⚠️ gitserver-0: Your memory usage is over 80% (87.00%). Consider raising limits.",
},
{
name: "should return correct message for usage over 40 and under 80",
usage: 63.4,
resourceType: "memory",
container: "gitserver-0",
want: "\t✅ gitserver-0: Your memory usage is under 80% (63.40%). Keep memory allocation the same.",
},
{
name: "should return correct message for usage under 40",
usage: 22.33,
resourceType: "memory",
container: "gitserver-0",
want: "\t⚠️ gitserver-0: Your memory usage is under 40% (22.33%). Consider lowering limits.",
},
}

for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
got := CheckUsage(tc.usage, tc.resourceType, tc.container)

if got != tc.want {
t.Errorf("got: '%s' want '%s'", got, tc.want)
}
})
}
}

func TestOutputToFile(t *testing.T) {
cfg := &scout.Config{
Output: os.TempDir() + string(os.PathSeparator) + "test.txt",
}
name := "gitserver-0"
advice := []string{
"Add more CPU",
"Add more memory",
}

err := OutputToFile(context.Background(), cfg, name, advice)
if err != nil {
t.Fatal(err)
}

lines := readOutputFile(t, cfg)

cases := []struct {
lineNum int
want string
}{
{1, "- gitserver-0"},
{2, "Add more CPU"},
{3, "Add more memory"},
}

for _, tc := range cases {
tc := tc
if lines[tc.lineNum-1] != tc.want {
t.Errorf("Expected %q, got %q", tc.want, lines[tc.lineNum-1])
}
}

if len(lines) > 3 {
t.Error("Expected only 3 lines, got more")
}

if err != nil {
t.Fatal(err)
}
}

func readOutputFile(t *testing.T, cfg *scout.Config) []string {
file, err := os.Open(cfg.Output)
if err != nil {
t.Fatal(err)
}

var lines []string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}

file.Close()
err = os.Remove(cfg.Output)
if err != nil {
// try again after waiting a bit
time.Sleep(100 * time.Millisecond)
err = os.Remove(cfg.Output)
if err != nil {
t.Fatal(err)
}
}
return lines
}
72 changes: 4 additions & 68 deletions internal/scout/advise/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package advise
import (
"context"
"fmt"
"os"

"github.com/sourcegraph/sourcegraph/lib/errors"
jasonhawkharris marked this conversation as resolved.
Show resolved Hide resolved
"github.com/sourcegraph/src-cli/internal/scout"
Expand Down Expand Up @@ -75,19 +74,19 @@ func Advise(ctx context.Context, cfg *scout.Config, pod v1.Pod) error {
}

for _, metrics := range usageMetrics {
cpuAdvice := checkUsage(metrics.CpuUsage, "CPU", metrics.ContainerName, pod.Name)
cpuAdvice := CheckUsage(metrics.CpuUsage, "CPU", metrics.ContainerName)
advice = append(advice, cpuAdvice)

memoryAdvice := checkUsage(metrics.MemoryUsage, "memory", metrics.ContainerName, pod.Name)
memoryAdvice := CheckUsage(metrics.MemoryUsage, "memory", metrics.ContainerName)
advice = append(advice, memoryAdvice)

if metrics.Storage != nil {
storageAdvice := checkUsage(metrics.StorageUsage, "storage", metrics.ContainerName, pod.Name)
storageAdvice := CheckUsage(metrics.StorageUsage, "storage", metrics.ContainerName)
advice = append(advice, storageAdvice)
}

if cfg.Output != "" {
outputToFile(ctx, cfg, pod, advice)
OutputToFile(ctx, cfg, pod.Name, advice)
} else {
for _, msg := range advice {
fmt.Println(msg)
Expand All @@ -98,26 +97,6 @@ func Advise(ctx context.Context, cfg *scout.Config, pod v1.Pod) error {
return nil
}

// outputToFile writes resource allocation advice for a Kubernetes pod to a file.
func outputToFile(ctx context.Context, cfg *scout.Config, pod v1.Pod, advice []string) error {
file, err := os.OpenFile(cfg.Output, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return errors.Wrap(err, "failed to open file")
}
defer file.Close()

if _, err := fmt.Fprintf(file, "- %s\n", pod.Name); err != nil {
return errors.Wrap(err, "failed to write pod name to file")
}

for _, msg := range advice {
if _, err := fmt.Fprintf(file, "%s\n", msg); err != nil {
return errors.Wrap(err, "failed to write container advice to file")
}
}
return nil
}

// getUsageMetrics generates resource usage statistics for containers in a Kubernetes pod.
func getUsageMetrics(ctx context.Context, cfg *scout.Config, pod v1.Pod) ([]scout.UsageStats, error) {
var usages []scout.UsageStats
Expand Down Expand Up @@ -146,46 +125,3 @@ func getUsageMetrics(ctx context.Context, cfg *scout.Config, pod v1.Pod) ([]scou

return usages, nil
}

func checkUsage(usage float64, resourceType, container, pod string) string {
var message string

switch {
case usage >= 100:
message = fmt.Sprintf(
OVER_100,
scout.FlashingLightEmoji,
container,
resourceType,
usage,
resourceType,
)
case usage >= 80 && usage < 100:
message = fmt.Sprintf(
OVER_80,
scout.WarningSign,
container,
resourceType,
usage,
)
case usage >= 40 && usage < 80:
message = fmt.Sprintf(
OVER_40,
scout.SuccessEmoji,
container,
resourceType,
usage,
resourceType,
)
default:
message = fmt.Sprintf(
UNDER_40,
scout.WarningSign,
container,
resourceType,
usage,
)
}

return message
}
54 changes: 53 additions & 1 deletion internal/scout/kube/kube_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package kube

import "testing"
import (
"testing"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestAcceptedFileSystem(t *testing.T) {
cases := []struct {
Expand Down Expand Up @@ -30,3 +35,50 @@ func TestAcceptedFileSystem(t *testing.T) {
})
}
}

func TestGetPod(t *testing.T) {
cases := []struct {
name string
podList []corev1.Pod
wantPod string
}{
{
name: "should return correct pod",
podList: []corev1.Pod{
*testPod("sg", "soucegraph-frontend-0", "sourcegraph-frontend"),
*testPod("sg", "gitserver-0", "gitserver"),
*testPod("sg", "indexed-search-0", "indexed-search"),
},
wantPod: "gitserver-0",
},
{
name: "should return empty pod if pod not found",
podList: []corev1.Pod{
*testPod("sg", "soucegraph-frontend-0", "sourcegraph-frontend"),
*testPod("sg", "indexed-search-0", "indexed-search"),
},
wantPod: "",
},
}

for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
got, _ := GetPod("gitserver-0", tc.podList)
gotPod := got.Name

if gotPod != tc.wantPod {
t.Errorf("want pod %s, got pod %s", tc.wantPod, gotPod)
}
})
}
}

func testPod(namespace, podName, containerName string) *corev1.Pod {
return &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: podName,
},
}
}