mirror of
https://github.com/k8sgpt-ai/k8sgpt.git
synced 2025-04-28 11:25:03 +00:00
feat: enables remote custom analyzers (#906)
* feat: enables remote custom analyzers Signed-off-by: Alex Jones <alexsimonjones@gmail.com> * chore: fixed test that was broken Signed-off-by: Alex Jones <alexsimonjones@gmail.com> * chore: hiding custom analysis behind a flag Signed-off-by: Alex Jones <alexsimonjones@gmail.com> * chore: resolved govet issue Signed-off-by: Alex Jones <alexsimonjones@gmail.com> * chore: updated Signed-off-by: Alex Jones <alexsimonjones@gmail.com> * chore: updated Signed-off-by: Alex Jones <alexsimonjones@gmail.com> * chore: updated deps Signed-off-by: Alex Jones <alexsimonjones@gmail.com> * chore: updated deps Signed-off-by: Alex Jones <alexsimonjones@gmail.com> --------- Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
This commit is contained in:
parent
070aa7fdd0
commit
c8c9dbfadc
27
README.md
27
README.md
@ -459,6 +459,33 @@ k8sgpt cache remove
|
|||||||
```
|
```
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary> Custom Analyzers</summary>
|
||||||
|
|
||||||
|
There may be scenarios where you wish to write your own analyzer in a language of your choice.
|
||||||
|
K8sGPT now supports the ability to do so by abiding by the [schema](https://github.com/k8sgpt-ai/schemas/blob/main/protobuf/schema/v1/analyzer.proto) and serving the analyzer for consumption.
|
||||||
|
To do so, define the analyzer within the K8sGPT configuration and it will add it into the scanning process.
|
||||||
|
In addition to this you will need to enable the following flag on analysis:
|
||||||
|
```
|
||||||
|
k8sgpt analyze --custom-analysis
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is an example local host analyzer in [Rust](https://github.com/k8sgpt-ai/host-analyzer)
|
||||||
|
When this is run on `localhost:8080` the K8sGPT config can pick it up with the following additions:
|
||||||
|
|
||||||
|
```
|
||||||
|
custom_analyzers:
|
||||||
|
- name: host-analyzer
|
||||||
|
connection:
|
||||||
|
url: localhost
|
||||||
|
port: 8080
|
||||||
|
```
|
||||||
|
|
||||||
|
This now gives the ability to pass through hostOS information ( from this analyzer example ) to K8sGPT to use as context with normal analysis.
|
||||||
|
|
||||||
|
_See the docs on how to write a custom analyzer_
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ var (
|
|||||||
maxConcurrency int
|
maxConcurrency int
|
||||||
withDoc bool
|
withDoc bool
|
||||||
interactiveMode bool
|
interactiveMode bool
|
||||||
|
customAnalysis bool
|
||||||
)
|
)
|
||||||
|
|
||||||
// AnalyzeCmd represents the problems command
|
// AnalyzeCmd represents the problems command
|
||||||
@ -65,6 +66,9 @@ var AnalyzeCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
defer config.Close()
|
defer config.Close()
|
||||||
|
|
||||||
|
if customAnalysis {
|
||||||
|
config.RunCustomAnalysis()
|
||||||
|
}
|
||||||
config.RunAnalysis()
|
config.RunAnalysis()
|
||||||
|
|
||||||
if explain {
|
if explain {
|
||||||
@ -131,4 +135,7 @@ func init() {
|
|||||||
AnalyzeCmd.Flags().BoolVarP(&withDoc, "with-doc", "d", false, "Give me the official documentation of the involved field")
|
AnalyzeCmd.Flags().BoolVarP(&withDoc, "with-doc", "d", false, "Give me the official documentation of the involved field")
|
||||||
// interactive mode flag
|
// interactive mode flag
|
||||||
AnalyzeCmd.Flags().BoolVarP(&interactiveMode, "interactive", "i", false, "Enable interactive mode that allows further conversation with LLM about the problem. Works only with --explain flag")
|
AnalyzeCmd.Flags().BoolVarP(&interactiveMode, "interactive", "i", false, "Enable interactive mode that allows further conversation with LLM about the problem. Works only with --explain flag")
|
||||||
|
// custom analysis flag
|
||||||
|
AnalyzeCmd.Flags().BoolVarP(&customAnalysis, "custom-analysis", "z", false, "Enable custom analyzers")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import (
|
|||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
|
||||||
|
"github.com/k8sgpt-ai/k8sgpt/pkg/custom"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||||
"github.com/schollz/progressbar/v3"
|
"github.com/schollz/progressbar/v3"
|
||||||
@ -50,8 +51,10 @@ type Analysis struct {
|
|||||||
WithDoc bool
|
WithDoc bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type AnalysisStatus string
|
type (
|
||||||
type AnalysisErrors []string
|
AnalysisStatus string
|
||||||
|
AnalysisErrors []string
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
StateOK AnalysisStatus = "OK"
|
StateOK AnalysisStatus = "OK"
|
||||||
@ -147,6 +150,27 @@ func NewAnalysis(
|
|||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Analysis) RunCustomAnalysis() {
|
||||||
|
var customAnalyzers []custom.CustomAnalyzer
|
||||||
|
if err := viper.UnmarshalKey("custom_analyzers", &customAnalyzers); err != nil {
|
||||||
|
a.Errors = append(a.Errors, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cAnalyzer := range customAnalyzers {
|
||||||
|
|
||||||
|
canClient, err := custom.NewClient(cAnalyzer.Connection)
|
||||||
|
if err != nil {
|
||||||
|
a.Errors = append(a.Errors, fmt.Sprintf("Client creation error for %s analyzer", cAnalyzer.Name))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := canClient.Run()
|
||||||
|
if err != nil {
|
||||||
|
a.Results = append(a.Results, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Analysis) RunAnalysis() {
|
func (a *Analysis) RunAnalysis() {
|
||||||
activeFilters := viper.GetStringSlice("active_filters")
|
activeFilters := viper.GetStringSlice("active_filters")
|
||||||
|
|
||||||
|
57
pkg/custom/client.go
Normal file
57
pkg/custom/client.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package custom
|
||||||
|
|
||||||
|
import (
|
||||||
|
rpc "buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go/schema/v1/schemav1grpc"
|
||||||
|
schemav1 "buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go/schema/v1"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
c *grpc.ClientConn
|
||||||
|
analyzerClient rpc.AnalyzerServiceClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(c Connection) (*Client, error) {
|
||||||
|
|
||||||
|
conn, err := grpc.Dial(fmt.Sprintf("%s:%s", c.Url, c.Port), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
client := rpc.NewAnalyzerServiceClient(conn)
|
||||||
|
return &Client{
|
||||||
|
c: conn,
|
||||||
|
analyzerClient: client,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli *Client) Run() (common.Result, error) {
|
||||||
|
var result common.Result
|
||||||
|
req := &schemav1.AnalyzerRunRequest{}
|
||||||
|
res, err := cli.analyzerClient.Run(context.Background(), req)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
if res.Result != nil {
|
||||||
|
|
||||||
|
// We should refactor this, because Error and Failure do not map 1:1 from K8sGPT/schema
|
||||||
|
var errorsFound []common.Failure
|
||||||
|
for _, e := range res.Result.Error {
|
||||||
|
errorsFound = append(errorsFound, common.Failure{
|
||||||
|
Text: e.Text,
|
||||||
|
// TODO: Support sensitive data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Name = res.Result.Name
|
||||||
|
result.Kind = res.Result.Kind
|
||||||
|
result.Details = res.Result.Details
|
||||||
|
result.ParentObject = res.Result.ParentObject
|
||||||
|
result.Error = errorsFound
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
10
pkg/custom/types.go
Normal file
10
pkg/custom/types.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package custom
|
||||||
|
|
||||||
|
type Connection struct {
|
||||||
|
Url string `json:"url"`
|
||||||
|
Port string `json:"port"`
|
||||||
|
}
|
||||||
|
type CustomAnalyzer struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Connection Connection `json:"connection"`
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user