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: add caching client #129

Closed
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ spec:
model: gpt-3.5-turbo
backend: openai
noCache: false
version: v0.3.0
version: v0.3.5
enableAI: true
# filters:
# - Ingress
Expand All @@ -65,6 +65,47 @@ you will be able to see the Results objects of the analysis after some minutes (
"details": "The error message means that the service in Kubernetes doesn't have any associated endpoints, which should have been labeled with \"control-plane=controller-manager\". \n\nTo solve this issue, you need to add the \"control-plane=controller-manager\" label to the endpoint that matches the service. Once the endpoint is labeled correctly, Kubernetes can associate it with the service, and the error should be resolved.",
```

## Remote Cache

<details>

<summary>S3</summary>

1. Install the operator from the [Installation](#installation) section.

2. Create secret:
```sh
kubectl create secret generic k8sgpt-sample-cache-secret --from-literal=access_key_id=<AWS_ACCESS_KEY_ID> --from-literal=secret_access_key=<AWS_SECRET_ACCESS_KEY> -n k8sgpt-
operator-system
```

3. Apply the K8sGPT configuration object:
```
kubectl apply -f - << EOF
apiVersion: core.k8sgpt.ai/v1alpha1
kind: K8sGPT
metadata:
name: k8sgpt-sample
namespace: k8sgpt-operator-system
spec:
model: gpt-3.5-turbo
backend: openai
noCache: false
version: v0.3.0
matthisholleville marked this conversation as resolved.
Show resolved Hide resolved
enableAI: true
secret:
name: k8sgpt-sample-secret
key: openai-api-key
remoteCache:
credentials:
name: k8sgpt-sample-cache-secret
bucketName: foo
region: us-west-1
EOF
```

</details>

## Other AI Backend Examples

<details>
Expand Down
27 changes: 20 additions & 7 deletions api/v1alpha1/k8sgpt_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,33 @@ type SecretRef struct {
Key string `json:"key,omitempty"`
}

type CredentialsRef struct {
AlexsJones marked this conversation as resolved.
Show resolved Hide resolved
Name string `json:"name,omitempty"`
AccessKeyID string `json:"access_key_id,omitempty"`
SecretAccessKey string `json:"secret_acess_key,omitempty"`
}

type RemoteCacheRef struct {
Credentials *CredentialsRef `json:"credentials,omitempty"`
BucketName string `json:"bucketName,omitempty"`
Region string `json:"region,omitempty"`
}

// K8sGPTSpec defines the desired state of K8sGPT
type K8sGPTSpec struct {
// +kubebuilder:default:=openai
// +kubebuilder:validation:Enum=openai;localai;azureopenai
Backend `json:"backend"`
BaseUrl string `json:"baseUrl,omitempty"`
// +kubebuilder:default:=gpt-3.5-turbo
Model string `json:"model,omitempty"`
Engine string `json:"engine,omitempty"`
Secret *SecretRef `json:"secret,omitempty"`
Version string `json:"version,omitempty"`
EnableAI bool `json:"enableAI,omitempty"`
NoCache bool `json:"noCache,omitempty"`
Filters []string `json:"filters,omitempty"`
Model string `json:"model,omitempty"`
Engine string `json:"engine,omitempty"`
Secret *SecretRef `json:"secret,omitempty"`
Version string `json:"version,omitempty"`
EnableAI bool `json:"enableAI,omitempty"`
NoCache bool `json:"noCache,omitempty"`
Filters []string `json:"filters,omitempty"`
RemoteCache *RemoteCacheRef `json:"remoteCache,omitempty"`
}

type Backend string
Expand Down
35 changes: 29 additions & 6 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions config/crd/bases/core.k8sgpt.ai_k8sgpts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,22 @@ spec:
type: string
noCache:
type: boolean
remoteCache:
properties:
bucketName:
type: string
credentials:
properties:
access_key_id:
type: string
name:
type: string
secret_acess_key:
type: string
type: object
region:
type: string
type: object
secret:
properties:
key:
Expand All @@ -71,6 +87,12 @@ spec:
type: object
status:
description: K8sGPTStatus defines the observed state of K8sGPT
properties:
initialized:
description: 'INSERT ADDITIONAL STATUS FIELD - define observed state
of cluster Important: Run "make" to regenerate code after modifying
this file'
type: boolean
type: object
type: object
served: true
Expand Down
17 changes: 16 additions & 1 deletion controllers/k8sgpt_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ func (r *K8sGPTReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
// The object is being deleted
if utils.ContainsString(k8sgptConfig.GetFinalizers(), FinalizerName) {

if k8sgptConfig.Spec.RemoteCache != nil {
err := r.k8sGPTClients[k8sgptConfig.Name].RemoveConfig(k8sgptConfig)
if err != nil {
k8sgptReconcileErrorCount.Inc()
return r.finishReconcile(err, false)
}
}
// Delete any external resources associated with the instance
err := resources.Sync(ctx, r.Client, *k8sgptConfig, resources.Destroy)
if err != nil {
Expand Down Expand Up @@ -199,7 +206,15 @@ func (r *K8sGPTReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
r.k8sGPTClients[k8sgptConfig.Name] = k8sgptClient
}

response, err := r.k8sGPTClients[k8sgptConfig.Name].ProcessAnalysis(deployment, k8sgptConfig)
if k8sgptConfig.Spec.RemoteCache != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be called once and only once right? Or do you think we should be updating the config pre-analysis? It might be a good thing I suppose

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I think it's necessary to call it in two cases: when the object is created and when it's modified. What do you think ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to call this on object creation, call it further up before the finalizer is created

err := r.k8sGPTClients[k8sgptConfig.Name].AddConfig(k8sgptConfig)
if err != nil {
k8sgptReconcileErrorCount.Inc()
return r.finishReconcile(err, false)
}
}

response, err := r.k8sGPTClients[k8sgptConfig.Name].ProcessAnalysis(k8sgptConfig)
if err != nil {
k8sgptReconcileErrorCount.Inc()
return r.finishReconcile(err, false)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/k8sgpt-ai/k8sgpt-operator
go 1.19

require (
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20230515081240-6b5b845c638e.1
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20230524215339-41d88e13ab7e.1
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.30.0-20230524215339-41d88e13ab7e.1
github.com/onsi/ginkgo/v2 v2.9.5
github.com/onsi/gomega v1.27.7
Expand Down
6 changes: 3 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20230515081240-6b5b845c638e.1 h1:u8CQODmTW0EYjXKt0ZSbA/FGuOkA+zRNicNCs97Ud/A=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20230515081240-6b5b845c638e.1/go.mod h1:EB1h/5OvQWTeT9JJ2x0NLaboeFOOm3fqkYWKp5ojO7o=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.28.1-20230515081240-6b5b845c638e.4/go.mod h1:i/s4ALHwKvjA1oGNKpoHg0FpEOTbufoOm/NdTE6YQAE=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20230524215339-41d88e13ab7e.1 h1:lvzkvTlk64ZKgqBlyI6wTkRcOCRwERbgvNNC739L2uw=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20230524215339-41d88e13ab7e.1/go.mod h1:gVjoI6SASls1kiAjJWFIYrT6midpN8pAT82q2EEVbGY=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.28.1-20230524215339-41d88e13ab7e.4/go.mod h1:i/s4ALHwKvjA1oGNKpoHg0FpEOTbufoOm/NdTE6YQAE=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.30.0-20230524215339-41d88e13ab7e.1 h1:Mx0Z+cXHStOU4lkemYYGhvNd40aU9g52sfS2W7D/gzA=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.30.0-20230524215339-41d88e13ab7e.1/go.mod h1:karV92RruD5davytOQq20lDyAqBnai8ajNolo98nu94=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
Expand Down
48 changes: 48 additions & 0 deletions pkg/client/analysis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package client

import (
"context"
"encoding/json"
"fmt"

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"
"github.com/k8sgpt-ai/k8sgpt-operator/api/v1alpha1"
"github.com/k8sgpt-ai/k8sgpt-operator/pkg/common"
)

func (c *Client) ProcessAnalysis(config *v1alpha1.K8sGPT) (*common.K8sGPTReponse, error) {

client := rpc.NewServerServiceClient(c.conn)

req := &schemav1.AnalyzeRequest{
Explain: config.Spec.EnableAI,
Nocache: config.Spec.NoCache,
Backend: string(config.Spec.Backend),
Filters: config.Spec.Filters,
}

res, err := client.Analyze(context.Background(), req)
if err != nil {
return nil, fmt.Errorf("failed to call Analyze RPC: %v", err)
}

var target []v1alpha1.ResultSpec

jsonBytes, err := json.Marshal(res.Results)
if err != nil {
return nil, err
}

err = json.Unmarshal(jsonBytes, &target)
if err != nil {
return nil, err
}

response := &common.K8sGPTReponse{
Status: res.Status,
Results: target,
Problems: int(res.Problems),
}
return response, nil
}
43 changes: 0 additions & 43 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,9 @@ limitations under the License.
package client

import (
"context"
"encoding/json"
"fmt"

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"
"github.com/k8sgpt-ai/k8sgpt-operator/api/v1alpha1"
"github.com/k8sgpt-ai/k8sgpt-operator/pkg/common"
"google.golang.org/grpc"
v1 "k8s.io/api/apps/v1"
)

// This is the client for communicating with the K8sGPT in cluster deployment
Expand All @@ -42,39 +35,3 @@ func NewClient(address string) (*Client, error) {

return client, nil
}

func (c *Client) ProcessAnalysis(deployment v1.Deployment, config *v1alpha1.K8sGPT) (*common.K8sGPTReponse, error) {

client := rpc.NewServerClient(c.conn)

req := &schemav1.AnalyzeRequest{
Explain: config.Spec.EnableAI,
Nocache: config.Spec.NoCache,
Backend: string(config.Spec.Backend),
Filters: config.Spec.Filters,
}

res, err := client.Analyze(context.Background(), req)
if err != nil {
return nil, fmt.Errorf("failed to call Analyze RPC: %v", err)
}

var target []v1alpha1.ResultSpec

jsonBytes, err := json.Marshal(res.Results)
if err != nil {
return nil, err
}

err = json.Unmarshal(jsonBytes, &target)
if err != nil {
return nil, err
}

response := &common.K8sGPTReponse{
Status: res.Status,
Results: target,
Problems: int(res.Problems),
}
return response, nil
}
Loading