mirror of
https://github.com/k8sgpt-ai/k8sgpt.git
synced 2025-08-10 20:21:38 +00:00
interfaced out ai clients
Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
This commit is contained in:
parent
ca8ce8188a
commit
bdb2e739d4
@ -3,7 +3,7 @@
|
|||||||
<img alt="Text changing depending on mode. Light: 'So light!' Dark: 'So dark!'" src="./images/banner-black.png" width="600px;">
|
<img alt="Text changing depending on mode. Light: 'So light!' Dark: 'So dark!'" src="./images/banner-black.png" width="600px;">
|
||||||
</picture>
|
</picture>
|
||||||
|
|
||||||
_Try it out now_
|
_Install it now_
|
||||||
|
|
||||||
```
|
```
|
||||||
brew tap k8sgpt-ai/k8sgpt
|
brew tap k8sgpt-ai/k8sgpt
|
||||||
@ -20,12 +20,11 @@ It has SRE experience codified into it's analyzers and helps to pull out the mos
|
|||||||
|
|
||||||
```
|
```
|
||||||
# Ensure KUBECONFIG env is set to an active Kubernetes cluster
|
# Ensure KUBECONFIG env is set to an active Kubernetes cluster
|
||||||
k8sgpt auth key <Your OpenAI key>
|
k8sgpt init
|
||||||
|
k8sgpt auth
|
||||||
k8sgpt find problems
|
k8sgpt find problems
|
||||||
# for more detail
|
# for more detail
|
||||||
k8s find problems --explain
|
k8s find problems --explain
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### What about kubectl-ai?
|
### What about kubectl-ai?
|
||||||
|
@ -1,21 +1,41 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
// authCmd represents the auth command
|
// authCmd represents the auth command
|
||||||
var AuthCmd = &cobra.Command{
|
var AuthCmd = &cobra.Command{
|
||||||
Use: "auth",
|
Use: "auth",
|
||||||
Short: "A brief description of your command",
|
Short: "Authenticate with your chosen backend",
|
||||||
Long: `A longer description that spans multiple lines and likely contains examples
|
Long: `Provide the necessary credentials to authenticate with your chosen backend.`,
|
||||||
and usage of using your command. For example:
|
|
||||||
|
|
||||||
Cobra is a CLI library for Go that empowers applications.
|
|
||||||
This application is a tool to generate the needed files
|
|
||||||
to quickly create a Cobra application.`,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
cmd.Help()
|
|
||||||
|
backendType := viper.GetString("backend_type")
|
||||||
|
|
||||||
|
fmt.Printf("Enter %s Key: ", backendType)
|
||||||
|
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
|
||||||
|
if err != nil {
|
||||||
|
color.Red("Error reading %s Key from stdin: %s", backendType,
|
||||||
|
err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
password := strings.TrimSpace(string(bytePassword))
|
||||||
|
|
||||||
|
viper.Set(fmt.Sprintf("%s_key", backendType), password)
|
||||||
|
if err := viper.WriteConfig(); err != nil {
|
||||||
|
color.Red("Error writing config file: %s", err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
color.Green("key added")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
package auth
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/fatih/color"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"golang.org/x/term"
|
|
||||||
)
|
|
||||||
|
|
||||||
// keyCmd represents the key command
|
|
||||||
var keyCmd = &cobra.Command{
|
|
||||||
Use: "key",
|
|
||||||
Short: "Add a key to OpenAI",
|
|
||||||
Long: `This command will add a key from OpenAI to enable you to interact with the API`,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
|
|
||||||
fmt.Print("Enter OpenAI API Key: ")
|
|
||||||
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
|
|
||||||
if err != nil {
|
|
||||||
color.Red("Error reading OpenAI API Key from stdin: %s", err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
password := strings.TrimSpace(string(bytePassword))
|
|
||||||
|
|
||||||
viper.Set("openai_api_key", password)
|
|
||||||
if err := viper.WriteConfig(); err != nil {
|
|
||||||
color.Red("Error writing config file: %s", err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
color.Green("key added")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
AuthCmd.AddCommand(keyCmd)
|
|
||||||
|
|
||||||
}
|
|
@ -2,6 +2,7 @@ package find
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
@ -22,10 +23,30 @@ var problemsCmd = &cobra.Command{
|
|||||||
provide you with a list of issues that need to be resolved`,
|
provide you with a list of issues that need to be resolved`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
// Initialise the openAI client
|
// get backend from file
|
||||||
openAIClient, err := ai.NewClient()
|
backendType := viper.GetString("backend_type")
|
||||||
if err != nil {
|
if backendType == "" {
|
||||||
color.Red("Error: %v", err)
|
color.Red("No backend set. Please run k8sgpt init")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
// get the token with viper
|
||||||
|
token := viper.GetString(fmt.Sprintf("%s_key", backendType))
|
||||||
|
// check if nil
|
||||||
|
if token == "" {
|
||||||
|
color.Red("No %s key set. Please run k8sgpt auth", backendType)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var aiClient ai.IAI
|
||||||
|
switch backendType {
|
||||||
|
case "openai":
|
||||||
|
aiClient = &ai.OpenAIClient{}
|
||||||
|
if err := aiClient.Configure(token); err != nil {
|
||||||
|
color.Red("Error: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
color.Red("Backend not supported")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +54,7 @@ var problemsCmd = &cobra.Command{
|
|||||||
// Get kubernetes client from viper
|
// Get kubernetes client from viper
|
||||||
client := viper.Get("kubernetesClient").(*kubernetes.Client)
|
client := viper.Get("kubernetesClient").(*kubernetes.Client)
|
||||||
|
|
||||||
if err := analyzer.RunAnalysis(ctx, client, openAIClient, explain); err != nil {
|
if err := analyzer.RunAnalysis(ctx, client, aiClient, explain); err != nil {
|
||||||
color.Red("Error: %v", err)
|
color.Red("Error: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
42
cmd/init/init.go
Normal file
42
cmd/init/init.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package init
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
backend string
|
||||||
|
)
|
||||||
|
|
||||||
|
// authCmd represents the auth command
|
||||||
|
var InitCmd = &cobra.Command{
|
||||||
|
Use: "init",
|
||||||
|
Short: "Initialise k8sgpt with a backend AI provider",
|
||||||
|
Long: `Currently only OpenAI is supported.`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is largely a placeholder for now. In the future we will support
|
||||||
|
multiple backends and this will allow us to set the backend we want to use.
|
||||||
|
*/
|
||||||
|
if backend != "openai" {
|
||||||
|
color.Yellow("Only OpenAI is supported at the moment")
|
||||||
|
}
|
||||||
|
viper.Set("backend_type", backend)
|
||||||
|
if err := viper.WriteConfig(); err != nil {
|
||||||
|
color.Red("Error writing config file: %s", err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
color.Green("Backend set to %s", backend)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// add flag for backend
|
||||||
|
InitCmd.Flags().StringVarP(&backend, "backend", "b", "openai", "Backend AI provider")
|
||||||
|
}
|
@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/cmd/auth"
|
"github.com/k8sgpt-ai/k8sgpt/cmd/auth"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/cmd/find"
|
"github.com/k8sgpt-ai/k8sgpt/cmd/find"
|
||||||
|
i "github.com/k8sgpt-ai/k8sgpt/cmd/init"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
@ -44,7 +45,7 @@ func init() {
|
|||||||
// will be global for your application.
|
// will be global for your application.
|
||||||
rootCmd.AddCommand(auth.AuthCmd)
|
rootCmd.AddCommand(auth.AuthCmd)
|
||||||
rootCmd.AddCommand(find.FindCmd)
|
rootCmd.AddCommand(find.FindCmd)
|
||||||
|
rootCmd.AddCommand(i.InitCmd)
|
||||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.k8sgpt.git.yaml)")
|
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.k8sgpt.git.yaml)")
|
||||||
rootCmd.PersistentFlags().StringVar(&masterURL, "master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")
|
rootCmd.PersistentFlags().StringVar(&masterURL, "master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")
|
||||||
rootCmd.PersistentFlags().StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
|
rootCmd.PersistentFlags().StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
|
||||||
@ -59,6 +60,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
viper.Set("kubernetesClient", kubernetesClient)
|
viper.Set("kubernetesClient", kubernetesClient)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// initConfig reads in config file and ENV variables if set.
|
// initConfig reads in config file and ENV variables if set.
|
||||||
|
29
pkg/ai/ai.go
29
pkg/ai/ai.go
@ -2,36 +2,25 @@ package ai
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"errors"
|
||||||
|
|
||||||
"github.com/sashabaranov/go-openai"
|
"github.com/sashabaranov/go-openai"
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type OpenAIClient struct {
|
||||||
client *openai.Client
|
client *openai.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetClient() *openai.Client {
|
func (c *OpenAIClient) Configure(token string) error {
|
||||||
return c.client
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewClient() (*Client, error) {
|
|
||||||
|
|
||||||
// get the token with viper
|
|
||||||
token := viper.GetString("openai_api_key")
|
|
||||||
// check if nil
|
|
||||||
if token == "" {
|
|
||||||
return nil, fmt.Errorf("no OpenAI API Key found")
|
|
||||||
}
|
|
||||||
|
|
||||||
client := openai.NewClient(token)
|
client := openai.NewClient(token)
|
||||||
return &Client{
|
|
||||||
client: client,
|
if client == nil {
|
||||||
}, nil
|
return errors.New("error creating OpenAI client")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetCompletion(ctx context.Context, prompt string) (string, error) {
|
func (c *OpenAIClient) GetCompletion(ctx context.Context, prompt string) (string, error) {
|
||||||
// Create a completion request
|
// Create a completion request
|
||||||
resp, err := c.client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{
|
resp, err := c.client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{
|
||||||
Model: openai.GPT3Dot5Turbo,
|
Model: openai.GPT3Dot5Turbo,
|
||||||
|
8
pkg/ai/iai.go
Normal file
8
pkg/ai/iai.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package ai
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
type IAI interface {
|
||||||
|
Configure(token string) error
|
||||||
|
GetCompletion(ctx context.Context, prompt string) (string, error)
|
||||||
|
}
|
@ -2,11 +2,12 @@ package analyzer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RunAnalysis(ctx context.Context, client *kubernetes.Client, aiClient *ai.Client, explain bool) error {
|
func RunAnalysis(ctx context.Context, client *kubernetes.Client, aiClient ai.IAI, explain bool) error {
|
||||||
err := AnalyzePod(ctx, client, aiClient, explain)
|
err := AnalyzePod(ctx, client, aiClient, explain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -4,17 +4,18 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/briandowns/spinner"
|
"github.com/briandowns/spinner"
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func AnalyzePod(ctx context.Context, client *kubernetes.Client, aiClient *ai.Client, explain bool) error {
|
func AnalyzePod(ctx context.Context, client *kubernetes.Client, aiClient ai.IAI, explain bool) error {
|
||||||
|
|
||||||
// search all namespaces for pods that are not running
|
// search all namespaces for pods that are not running
|
||||||
list, err := client.GetClient().CoreV1().Pods("").List(ctx, metav1.ListOptions{})
|
list, err := client.GetClient().CoreV1().Pods("").List(ctx, metav1.ListOptions{})
|
||||||
|
@ -4,17 +4,18 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/briandowns/spinner"
|
"github.com/briandowns/spinner"
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func AnalyzeReplicaSet(ctx context.Context, client *kubernetes.Client, aiClient *ai.Client, explain bool) error {
|
func AnalyzeReplicaSet(ctx context.Context, client *kubernetes.Client, aiClient ai.IAI, explain bool) error {
|
||||||
|
|
||||||
// search all namespaces for pods that are not running
|
// search all namespaces for pods that are not running
|
||||||
list, err := client.GetClient().AppsV1().ReplicaSets("").List(ctx, metav1.ListOptions{})
|
list, err := client.GetClient().AppsV1().ReplicaSets("").List(ctx, metav1.ListOptions{})
|
||||||
|
Loading…
Reference in New Issue
Block a user