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

feat: serve/integration capability #645

Merged
merged 11 commits into from
Sep 16, 2023
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
__debug*
.DS_Store
k8sgpt*
!charts/k8sgpt
Expand Down
11 changes: 9 additions & 2 deletions cmd/filters/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (

"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
"github.com/k8sgpt-ai/k8sgpt/pkg/integration"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand All @@ -30,7 +31,7 @@ var listCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
activeFilters := viper.GetStringSlice("active_filters")
coreFilters, additionalFilters, integrationFilters := analyzer.ListFilters()

integration := integration.NewIntegration()
availableFilters := append(append(coreFilters, additionalFilters...), integrationFilters...)

if len(activeFilters) == 0 {
Expand All @@ -41,10 +42,16 @@ var listCmd = &cobra.Command{
for _, filter := range activeFilters {

// if the filter is an integration, mark this differently
// but if the integration is inactive, remove
if util.SliceContainsString(integrationFilters, filter) {
fmt.Printf("> %s\n", color.BlueString("%s (integration)", filter))
} else {
fmt.Printf("> %s\n", color.GreenString(filter))
// This strange bit of logic will loop through every integration via
// OwnsAnalyzer subcommand to check the filter and as the integrationFilters...
// was no match, we know this isn't part of an active integration
if _, err := integration.AnalyzerByIntegration(filter); err != nil {
fmt.Printf("> %s\n", color.GreenString(filter))
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20230620082254-6f80f9533908.1/g
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.28.1-20230620082254-6f80f9533908.4/go.mod h1:i/s4ALHwKvjA1oGNKpoHg0FpEOTbufoOm/NdTE6YQAE=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.31.0-20230828112343-a9fd9ad20848.1 h1:fScSW5Gzu1OzUYylwpvQwgXX5J9YPKkOQpbkc5c8Q+8=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.31.0-20230828112343-a9fd9ad20848.1/go.mod h1:gtnk2yAUexdY5nTuUg0SH5WCCGvpKzr7pd3Xbi7MWjE=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.31.0-20230830164712-dc062a152c20.1 h1:oD5YCdsVnQgVLSHxium66FFfuxjjvn5u65mnQo6vAyc=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.31.0-20230830164712-dc062a152c20.1/go.mod h1:gtnk2yAUexdY5nTuUg0SH5WCCGvpKzr7pd3Xbi7MWjE=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
Expand Down
47 changes: 26 additions & 21 deletions pkg/integration/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@ package integration

import (
"errors"
"os"
"strings"
"fmt"

"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/integration/trivy"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
Expand All @@ -35,6 +33,8 @@ type IIntegration interface {

GetAnalyzerName() []string

OwnsAnalyzer(string) bool

IsActivate() bool
}

Expand Down Expand Up @@ -64,34 +64,38 @@ func (*Integration) Get(name string) (IIntegration, error) {
return integrations[name], nil
}

func (i *Integration) AnalyzerByIntegration(input string) (string, error) {

for _, name := range i.List() {
if integ, err := i.Get(name); err == nil {
if integ.OwnsAnalyzer(input) {
return name, nil
}
}
}
return "", errors.New("analyzerbyintegration: no matches found")
}

func (*Integration) Activate(name string, namespace string, activeFilters []string, skipInstall bool) error {
if _, ok := integrations[name]; !ok {
return errors.New("integration not found")
}

mergedFilters := activeFilters

mergedFilters = append(mergedFilters, integrations[name].GetAnalyzerName()...)

uniqueFilters, dupplicatedFilters := util.RemoveDuplicates(mergedFilters)

// Verify dupplicate
if len(dupplicatedFilters) != 0 {
color.Red("Integration already activated : %s", strings.Join(dupplicatedFilters, ", "))
os.Exit(1)
}

viper.Set("active_filters", uniqueFilters)

if !skipInstall {
if err := integrations[name].Deploy(namespace); err != nil {
return err
}
}

mergedFilters := activeFilters
mergedFilters = append(mergedFilters, integrations[name].GetAnalyzerName()...)
uniqueFilters, _ := util.RemoveDuplicates(mergedFilters)

viper.Set("active_filters", uniqueFilters)

if err := viper.WriteConfig(); err != nil {
color.Red("Error writing config file: %s", err.Error())
os.Exit(1)
return fmt.Errorf("error writing config file: %s", err.Error())

}

return nil
Expand All @@ -111,6 +115,7 @@ func (*Integration) Deactivate(name string, namespace string) error {
activeFilters = append(activeFilters[:x], activeFilters[x+1:]...)
}
}

}

if err := integrations[name].UnDeploy(namespace); err != nil {
Expand All @@ -120,8 +125,8 @@ func (*Integration) Deactivate(name string, namespace string) error {
viper.Set("active_filters", activeFilters)

if err := viper.WriteConfig(); err != nil {
color.Red("Error writing config file: %s", err.Error())
os.Exit(1)
return fmt.Errorf("error writing config file: %s", err.Error())

}

return nil
Expand Down
9 changes: 9 additions & 0 deletions pkg/integration/trivy/trivy.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ func (t *Trivy) GetAnalyzerName() []string {
}
}

func (t *Trivy) OwnsAnalyzer(analyzer string) bool {

for _, a := range t.GetAnalyzerName() {
if analyzer == a {
return true
}
}
return false
}
func (t *Trivy) Deploy(namespace string) error {

// Add the repository
Expand Down
17 changes: 17 additions & 0 deletions pkg/server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# serve

The serve commands allow you to run k8sgpt in a grpc server mode.
This would be enabled typically through `k8sgpt serve` and is how the in-cluster k8sgpt deployment functions when managed by the [k8sgpt-operator](https://github.com/k8sgpt-ai/k8sgpt-operator)

The grpc interface that is served is hosted on [buf](https://buf.build/k8sgpt-ai/schemas) and the repository for this is [here](https://github.com/k8sgpt-ai/schemas)

## grpcurl

A fantastic tool for local debugging and development is `grpcurl`
It allows you to form curl like requests that are http2
e.g.

```
grpcurl -plaintext -d '{"namespace": "k8sgpt", "explain" : "true"}' localhost:8080 schema.v1.ServerService/Analyze
```

42 changes: 35 additions & 7 deletions pkg/server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,46 @@
"errors"

schemav1 "buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go/schema/v1"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
"github.com/k8sgpt-ai/k8sgpt/pkg/integration"
"github.com/spf13/viper"
)

func (h *handler) AddConfig(ctx context.Context, i *schemav1.AddConfigRequest) (*schemav1.AddConfigResponse, error,
) {
if i.Cache.BucketName == "" || i.Cache.Region == "" {
return nil, errors.New("BucketName & Region are required")
}

err := cache.AddRemoteCache(i.Cache.BucketName, i.Cache.Region)
if err != nil {
return &schemav1.AddConfigResponse{}, err
if i.Integrations != nil {
coreFilters, _, _ := analyzer.ListFilters()
// Update filters
activeFilters := viper.GetStringSlice("active_filters")
if len(activeFilters) == 0 {
activeFilters = coreFilters
}
integration := integration.NewIntegration()

if i.Integrations.Trivy != nil {
// Enable/Disable Trivy
var err = integration.Activate("trivy", i.Integrations.Trivy.Namespace,

Check failure on line 28 in pkg/server/config.go

View workflow job for this annotation

GitHub Actions / build

i.Integrations.Trivy.Namespace undefined (type *schemav1.Trivy has no field or method Namespace)
activeFilters, i.Integrations.Trivy.SkipInstall)

Check failure on line 29 in pkg/server/config.go

View workflow job for this annotation

GitHub Actions / build

i.Integrations.Trivy.SkipInstall undefined (type *schemav1.Trivy has no field or method SkipInstall)
return &schemav1.AddConfigResponse{
Status: "",
}, err
}
}
if i.Cache != nil {
// Remote cache
if i.Cache.BucketName == "" || i.Cache.Region == "" {
return &schemav1.AddConfigResponse{}, errors.New("BucketName & Region are required")
}

err := cache.AddRemoteCache(i.Cache.BucketName, i.Cache.Region)
if err != nil {
return &schemav1.AddConfigResponse{
Status: err.Error(),
}, err
}
}
return &schemav1.AddConfigResponse{
Status: "Configuration updated.",
}, nil
Expand All @@ -28,7 +54,9 @@
) {
err := cache.RemoveRemoteCache(i.Cache.BucketName)
if err != nil {
return &schemav1.RemoveConfigResponse{}, err
return &schemav1.RemoveConfigResponse{
Status: err.Error(),
}, err
}

return &schemav1.RemoveConfigResponse{
Expand Down
2 changes: 1 addition & 1 deletion pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ package util

import (
"context"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"math/rand"
"os"
"regexp"

Expand Down
Loading