Skip to content

Commit

Permalink
chore: analyzer and ai interfacing (#200)
Browse files Browse the repository at this point in the history
* chore: analyzer and ai interfacing

Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu>

* fix: backend variable for aiProvider in cmd

Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu>

* fix: changed folders for analyzers

Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu>

* chore: renamed analyzers

Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu>

* fix: fixed ingress tests after rebase

Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu>

* fix: fixed ingress tests after rebase

Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu>

---------

Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu>
  • Loading branch information
thschue committed Apr 5, 2023
1 parent eeac731 commit 0195bfa
Show file tree
Hide file tree
Showing 18 changed files with 274 additions and 277 deletions.
39 changes: 17 additions & 22 deletions cmd/analyze/analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/analysis"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
"os"
"strings"

"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/schollz/progressbar/v3"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -53,50 +54,44 @@ var AnalyzeCmd = &cobra.Command{
os.Exit(1)
}

var aiClient ai.IAI
switch backendType {
case "openai":
aiClient = &ai.OpenAIClient{}
if err := aiClient.Configure(token, language); err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
default:
color.Red("Backend not supported")
aiClient := ai.NewClient(backendType)
if err := aiClient.Configure(token, language); err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}

ctx := context.Background()
// Get kubernetes client from viper
client := viper.Get("kubernetesClient").(*kubernetes.Client)
// Analysis configuration
config := &analyzer.AnalysisConfiguration{
// AnalysisResult configuration
config := &analysis.Analysis{
Namespace: namespace,
NoCache: nocache,
Explain: explain,
AIClient: aiClient,
Client: client,
Context: ctx,
}

var analysisResults *[]analyzer.Analysis = &[]analyzer.Analysis{}
if err := analyzer.RunAnalysis(ctx, filters, config, client,
aiClient, analysisResults); err != nil {
err := config.RunAnalysis()
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}

if len(*analysisResults) == 0 {
if len(config.Results) == 0 {
color.Green("{ \"status\": \"OK\" }")
os.Exit(0)
}
var bar = progressbar.Default(int64(len(*analysisResults)))
var bar = progressbar.Default(int64(len(config.Results)))
if !explain {
bar.Clear()
}
var printOutput []analyzer.Analysis

for _, analysis := range *analysisResults {
var printOutput []analyzer.Result

for _, analysis := range config.Results {
if explain {
parsedText, err := analyzer.ParseViaAI(ctx, config, aiClient, analysis.Error)
parsedText, err := aiClient.Parse(ctx, analysis.Error, nocache)
if err != nil {
// Check for exhaustion
if strings.Contains(err.Error(), "status code: 429") {
Expand Down
41 changes: 41 additions & 0 deletions pkg/ai/ai.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ package ai

import (
"context"
"encoding/base64"
"errors"
"fmt"
"github.com/fatih/color"
"github.com/spf13/viper"
"strings"

"github.com/sashabaranov/go-openai"
)
Expand Down Expand Up @@ -46,3 +50,40 @@ func (c *OpenAIClient) GetCompletion(ctx context.Context, prompt string) (string
}
return resp.Choices[0].Message.Content, nil
}

func (a *OpenAIClient) Parse(ctx context.Context, prompt []string, nocache bool) (string, error) {
// parse the text with the AI backend
inputKey := strings.Join(prompt, " ")
// Check for cached data
sEnc := base64.StdEncoding.EncodeToString([]byte(inputKey))
// find in viper cache
if viper.IsSet(sEnc) && !nocache {
// retrieve data from cache
response := viper.GetString(sEnc)
if response == "" {
color.Red("error retrieving cached data")
return "", nil
}
output, err := base64.StdEncoding.DecodeString(response)
if err != nil {
color.Red("error decoding cached data: %v", err)
return "", nil
}
return string(output), nil
}

response, err := a.GetCompletion(ctx, inputKey)
if err != nil {
color.Red("error getting completion: %v", err)
return "", err
}

if !viper.IsSet(sEnc) {
viper.Set(sEnc, base64.StdEncoding.EncodeToString([]byte(response)))
if err := viper.WriteConfig(); err != nil {
color.Red("error writing config: %v", err)
return "", nil
}
}
return response, nil
}
14 changes: 13 additions & 1 deletion pkg/ai/iai.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
package ai

import "context"
import (
"context"
)

type IAI interface {
Configure(token string, language string) error
GetCompletion(ctx context.Context, prompt string) (string, error)
Parse(ctx context.Context, prompt []string, nocache bool) (string, error)
}

func NewClient(provider string) IAI {
switch provider {
case "openai":
return &OpenAIClient{}
default:
return &OpenAIClient{}
}
}
72 changes: 72 additions & 0 deletions pkg/analysis/analysis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package analysis

import (
"context"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/spf13/viper"
)

type Analysis struct {
Context context.Context
Filters []string
Client *kubernetes.Client
AIClient ai.IAI
Results []analyzer.Result
Namespace string
NoCache bool
Explain bool
}

func (a *Analysis) RunAnalysis() error {

activeFilters := viper.GetStringSlice("active_filters")

analyzerMap := analyzer.GetAnalyzerMap()

analyzerConfig := analyzer.Analyzer{
Client: a.Client,
Context: a.Context,
Namespace: a.Namespace,
AIClient: a.AIClient,
}

// if there are no filters selected and no active_filters then run all of them
if len(a.Filters) == 0 && len(activeFilters) == 0 {
for _, analyzer := range analyzerMap {
results, err := analyzer.Analyze(analyzerConfig)
if err != nil {
return err
}
a.Results = append(a.Results, results...)
}
return nil
}

// if the filters flag is specified
if len(a.Filters) != 0 {
for _, filter := range a.Filters {
if analyzer, ok := analyzerMap[filter]; ok {
results, err := analyzer.Analyze(analyzerConfig)
if err != nil {
return err
}
a.Results = append(a.Results, results...)
}
}
return nil
}

// use active_filters
for _, filter := range activeFilters {
if analyzer, ok := analyzerMap[filter]; ok {
results, err := analyzer.Analyze(analyzerConfig)
if err != nil {
return err
}
a.Results = append(a.Results, results...)
}
}
return nil
}
94 changes: 4 additions & 90 deletions pkg/analyzer/analyzer.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
package analyzer

import (
"context"
"encoding/base64"
"strings"

"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/spf13/viper"
)
type IAnalyzer interface {
Analyze(analysis Analyzer) ([]Result, error)
}

var coreAnalyzerMap = map[string]IAnalyzer{
"Pod": PodAnalyzer{},
Expand All @@ -24,85 +17,6 @@ var additionalAnalyzerMap = map[string]IAnalyzer{
"PodDisruptionBudget": PdbAnalyzer{},
}

func RunAnalysis(ctx context.Context, filters []string, config *AnalysisConfiguration,
client *kubernetes.Client,
aiClient ai.IAI, analysisResults *[]Analysis) error {

activeFilters := viper.GetStringSlice("active_filters")

analyzerMap := getAnalyzerMap()

// if there are no filters selected and no active_filters then run all of them
if len(filters) == 0 && len(activeFilters) == 0 {
for _, analyzer := range analyzerMap {
if err := analyzer.RunAnalysis(ctx, config, client, aiClient, analysisResults); err != nil {
return err
}
}
return nil
}

// if the filters flag is specified
if len(filters) != 0 {
for _, filter := range filters {
if analyzer, ok := analyzerMap[filter]; ok {
if err := analyzer.RunAnalysis(ctx, config, client, aiClient, analysisResults); err != nil {
return err
}
}
}
return nil
}

// use active_filters
for _, filter := range activeFilters {
if analyzer, ok := analyzerMap[filter]; ok {
if err := analyzer.RunAnalysis(ctx, config, client, aiClient, analysisResults); err != nil {
return err
}
}
}
return nil
}

func ParseViaAI(ctx context.Context, config *AnalysisConfiguration,
aiClient ai.IAI, prompt []string) (string, error) {
// parse the text with the AI backend
inputKey := strings.Join(prompt, " ")
// Check for cached data
sEnc := base64.StdEncoding.EncodeToString([]byte(inputKey))
// find in viper cache
if viper.IsSet(sEnc) && !config.NoCache {
// retrieve data from cache
response := viper.GetString(sEnc)
if response == "" {
color.Red("error retrieving cached data")
return "", nil
}
output, err := base64.StdEncoding.DecodeString(response)
if err != nil {
color.Red("error decoding cached data: %v", err)
return "", nil
}
return string(output), nil
}

response, err := aiClient.GetCompletion(ctx, inputKey)
if err != nil {
color.Red("error getting completion: %v", err)
return "", err
}

if !viper.IsSet(sEnc) {
viper.Set(sEnc, base64.StdEncoding.EncodeToString([]byte(response)))
if err := viper.WriteConfig(); err != nil {
color.Red("error writing config: %v", err)
return "", nil
}
}
return response, nil
}

func ListFilters() ([]string, []string) {
coreKeys := make([]string, 0, len(coreAnalyzerMap))
for k := range coreAnalyzerMap {
Expand All @@ -116,7 +30,7 @@ func ListFilters() ([]string, []string) {
return coreKeys, additionalKeys
}

func getAnalyzerMap() map[string]IAnalyzer {
func GetAnalyzerMap() map[string]IAnalyzer {

mergedMap := make(map[string]IAnalyzer)

Expand Down
1 change: 0 additions & 1 deletion pkg/analyzer/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package analyzer

import (
"context"

"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down
Loading

0 comments on commit 0195bfa

Please sign in to comment.