Skip to content

Commit 91e730d

Browse files
authored
K8sgpt poc (#439)
* Experimental POC: add k8sgpt permissions * Set customer data access to true * Feat(experimental): api-ErrorBudgetBurn AI input * Fix linter * Revert Makefile change
1 parent ce141d0 commit 91e730d

File tree

16 files changed

+4136
-369
lines changed

16 files changed

+4136
-369
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ dist
66
.idea
77
.vscode
88
cad_testing
9-
e2e-suite.test
9+
e2e-suite.test
10+
payload

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ help: # Display this help
1919

2020
##@ Global:
2121
.PHONY: all
22-
all: interceptor cadctl ## Generate, build, lint, test all subprojects
22+
all: interceptor cadctl ## Generate, build, lint, test all subprojects
2323

2424
.PHONY: build
2525
build: build-interceptor build-cadctl ## Build all subprojects in this repository
@@ -49,7 +49,7 @@ lint-cadctl: install-linter ## Lint cadctl subproject
4949
@echo
5050
@echo "Linting cadctl..."
5151
# Explicitly set GOROOT, see https://github.com/golangci/golangci-lint/issues/3107
52-
GOROOT=/usr/lib/golang GOLANGCI_LINT_CACHE=$$(mktemp -d) $(GOPATH)/bin/golangci-lint run -c .golangci.yml
52+
GOROOT=$$(go env GOROOT) GOLANGCI_LINT_CACHE=$$(mktemp -d) $(GOPATH)/bin/golangci-lint run -c .golangci.yml
5353

5454
.PHONY: test-cadctl
5555
test-cadctl: check-go121-install ## Run automated tests for cadctl
@@ -72,7 +72,7 @@ lint-interceptor: install-linter ## Lint interceptor subproject
7272
@echo
7373
@echo "Linting interceptor..."
7474
# Explicitly set GOROOT, see https://github.com/golangci/golangci-lint/issues/3107
75-
cd interceptor && GOROOT=/usr/lib/golang GOLANGCI_LINT_CACHE=$$(mktemp -d) $(GOPATH)/bin/golangci-lint run -c ../.golangci.yml
75+
cd interceptor && GOROOT=$$(go env GOROOT) GOLANGCI_LINT_CACHE=$$(mktemp -d) $(GOPATH)/bin/golangci-lint run -c ../.golangci.yml
7676

7777
.PHONY: test-interceptor
7878
test-interceptor: check-go121-install check-jq-install build-interceptor ## Run unit tests for interceptor

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,5 @@ Grafana dashboard configmaps are stored in the [Dashboards](./dashboards/) direc
182182
For Red Hat employees, these environment variables can be found in the SRE-P vault.
183183

184184
- `LOG_LEVEL`: refers to the CAD log level, if not set, the default is `info`. See
185+
186+
- `CAD_HCM_AI_TOKEN`: required for requests to the ai model

go.mod

Lines changed: 192 additions & 53 deletions
Large diffs are not rendered by default.

go.sum

Lines changed: 1918 additions & 119 deletions
Large diffs are not rendered by default.

interceptor/go.mod

Lines changed: 188 additions & 57 deletions
Large diffs are not rendered by default.

interceptor/go.sum

Lines changed: 1597 additions & 130 deletions
Large diffs are not rendered by default.

openshift/template.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,11 @@ objects:
305305
value: ${CAD_EXPERIMENTAL_ENABLED}
306306
- name: LOG_LEVEL
307307
value: ${LOG_LEVEL}
308+
- name: CAD_HCM_AI_TOKEN
309+
valueFrom:
310+
secretKeyRef:
311+
key: token
312+
name: srep-ai-sa-token
308313
envFrom:
309314
- secretRef:
310315
name: cad-ocm-client-secret

pkg/ai/k8sgpt/k8sgpt.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package k8sgpt
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"os"
8+
"strings"
9+
10+
k8sgpt_ai "github.com/k8sgpt-ai/k8sgpt/pkg/ai"
11+
"github.com/k8sgpt-ai/k8sgpt/pkg/analysis"
12+
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
13+
gptK8sClient "github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
14+
"k8s.io/apimachinery/pkg/runtime"
15+
"k8s.io/client-go/kubernetes"
16+
"k8s.io/client-go/rest"
17+
"sigs.k8s.io/controller-runtime/pkg/client"
18+
)
19+
20+
var model = "mistral-small-maas"
21+
22+
func K8sGptAnalysis(k8sRestConfig *rest.Config) (string, error) {
23+
ctrlClient, err := client.New(k8sRestConfig, client.Options{Scheme: runtime.NewScheme()})
24+
if err != nil {
25+
return "", errors.New("unable to init ctrlClient")
26+
}
27+
clientset := kubernetes.NewForConfigOrDie(k8sRestConfig)
28+
29+
client := &gptK8sClient.Client{CtrlClient: ctrlClient, Config: k8sRestConfig, Client: clientset}
30+
31+
aiToken := os.Getenv("CAD_HCM_AI_TOKEN")
32+
if aiToken == "" {
33+
return "", errors.New("could not find CAD_HCM_AI_TOKEN env")
34+
}
35+
36+
aiClient := k8sgpt_ai.NewClient("openai")
37+
aiProvider := &k8sgpt_ai.AIProvider{
38+
Name: "openai",
39+
Model: model,
40+
BaseURL: "https://mistral-small-maas-maas.apps.rosa.hcmaii01ue1.a9ro.p3.openshiftapps.com/v1", // TODO: Let's not hardcode this.
41+
Password: aiToken,
42+
}
43+
44+
if err = aiClient.Configure(aiProvider); err != nil {
45+
return "", fmt.Errorf("unable to configure ai provider: %w", err)
46+
}
47+
48+
cache, err := cache.GetCacheConfiguration()
49+
if err != nil {
50+
return "", fmt.Errorf("unable to get k8sgpt cache configuration: %w", err)
51+
}
52+
cache.DisableCache()
53+
54+
a := &analysis.Analysis{
55+
Context: context.Background(),
56+
Filters: []string{"Pod", "Deployment", "ReplicaSet", "PersistentVolumeClaim", "Service", "Ingress", "StatefulSet", "CronJob", "Node", "ValidatingWebhookConfiguration", "MutatingWebhookConfiguration"},
57+
Client: client,
58+
Language: "english",
59+
Namespace: "",
60+
LabelSelector: "",
61+
Cache: cache,
62+
Explain: true,
63+
MaxConcurrency: 10,
64+
WithDoc: false,
65+
WithStats: false,
66+
AIClient: aiClient,
67+
}
68+
69+
a.RunAnalysis()
70+
71+
var output string
72+
anonymize := false
73+
if err := a.GetAIResults(output, anonymize); err != nil {
74+
return "", fmt.Errorf("unable to get ai results: %w", err)
75+
}
76+
77+
return formatOutput(a)
78+
}
79+
80+
func formatOutput(a *analysis.Analysis) (string, error) {
81+
var output strings.Builder
82+
83+
output.WriteString("🤖🔧 AI Analysis Results 🔧🤖\n")
84+
output.WriteString(fmt.Sprintf("Model: %s\n", model))
85+
if len(a.Errors) != 0 {
86+
output.WriteString("⚠️ Analysis failures: \n")
87+
for _, aerror := range a.Errors {
88+
output.WriteString(fmt.Sprintf("- %s\n", aerror))
89+
}
90+
}
91+
if len(a.Results) == 0 {
92+
output.WriteString("✅ No cluster problems detected\n")
93+
return output.String(), nil
94+
}
95+
output.WriteString(fmt.Sprintf("🔍 %d cluster issues detected\n", len(a.Results)))
96+
output.WriteString("================\n\n")
97+
98+
for _, result := range a.Results {
99+
if result.Kind != "" {
100+
output.WriteString(fmt.Sprintf("Kind: %s\n", result.Kind))
101+
}
102+
103+
if result.Name != "" {
104+
output.WriteString(fmt.Sprintf("Name: %s\n", result.Name))
105+
}
106+
107+
if result.ParentObject != "" {
108+
output.WriteString(fmt.Sprintf("ParentObject: %s\n", result.ParentObject))
109+
}
110+
111+
if len(result.Error) > 0 {
112+
output.WriteString("Issues:\n")
113+
for _, err := range result.Error {
114+
output.WriteString(fmt.Sprintf("- %s\n", err.Text))
115+
if err.KubernetesDoc != "" {
116+
output.WriteString(fmt.Sprintf(" Kubernetes Doc: %s\n", err.KubernetesDoc))
117+
}
118+
}
119+
}
120+
121+
if result.Details != "" {
122+
output.WriteString(fmt.Sprintf("Details: %s\n", result.Details))
123+
}
124+
125+
output.WriteString("\n------------------------------------------------------------\n\n")
126+
}
127+
128+
return output.String(), nil
129+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# apierrorbudgetburn Investigation
2+
3+
POC Api-ErrorBudgetBurn investigation using k8sgpt.
4+
5+
## Testing
6+
7+
Refer to the [testing README](./testing/README.md) for instructions on testing this investigation
8+

0 commit comments

Comments
 (0)