diff --git a/README.md b/README.md index 8ba801ab..308c438f 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,7 @@ helm install release k8sgpt/k8sgpt-operator -n k8sgpt-operator-system --create-n 2. Create secret: ```sh -kubectl create secret generic k8sgpt-sample-secret --from-literal=openai-api-key=$OPENAI_TOKEN -n k8sgpt- -operator-system +kubectl create secret generic k8sgpt-sample-secret --from-literal=openai-api-key=$OPENAI_TOKEN -n k8sgpt-operator-system ``` 3. Apply the K8sGPT configuration object: @@ -37,11 +36,15 @@ metadata: name: k8sgpt-sample namespace: k8sgpt-operator-system spec: - model: gpt-3.5-turbo - backend: openai + ai: + enabled: true + model: gpt-3.5-turbo + backend: openai + secret: + name: k8sgpt-sample-secret + key: openai-api-key noCache: false version: v0.3.0 - enableAI: true # filters: # - Ingress # sink: @@ -50,9 +53,6 @@ spec: # extraOptions: # backstage: # enabled: true - secret: - name: k8sgpt-sample-secret - key: openai-api-key EOF ``` @@ -81,8 +81,7 @@ you will be able to see the Results objects of the analysis after some minutes ( 2. Create secret: ```sh -kubectl create secret generic k8sgpt-sample-secret --from-literal=azure-api-key=$AZURE_TOKEN -n k8sgpt- -operator-system +kubectl create secret generic k8sgpt-sample-secret --from-literal=azure-api-key=$AZURE_TOKEN -n k8sgpt-operator-system ``` 3. Apply the K8sGPT configuration object: @@ -94,16 +93,17 @@ metadata: name: k8sgpt-sample namespace: k8sgpt-operator-system spec: - model: gpt-35-turbo - backend: azureopenai - baseUrl: https://k8sgpt.openai.azure.com/ - engine: llm + ai: + enabled: true + secret: + name: k8sgpt-sample-secret + key: azure-api-key + model: gpt-35-turbo + backend: azureopenai + baseUrl: https://k8sgpt.openai.azure.com/ + engine: llm noCache: false version: v0.3.2 - enableAI: true - secret: - name: k8sgpt-sample-secret - key: azure-api-key EOF ``` @@ -127,12 +127,13 @@ metadata: name: k8sgpt-local-ai namespace: default spec: - model: ggml-gpt4all-j - backend: localai - baseUrl: http://local-ai.local-ai.svc.cluster.local:8080/v1 + ai: + enabled: true + model: ggml-gpt4all-j + backend: localai + baseUrl: http://local-ai.local-ai.svc.cluster.local:8080/v1 noCache: false version: v0.3.4 - enableAI: true EOF ``` Note: ensure that the value of `baseUrl` is a properly constructed [DNS name](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#services) for the LocalAI Service. It should take the form: `http://local-ai..svc.cluster.local:8080/v1`. diff --git a/api/v1alpha1/k8sgpt_types.go b/api/v1alpha1/k8sgpt_types.go index 3643a1d5..8c6b4fc8 100644 --- a/api/v1alpha1/k8sgpt_types.go +++ b/api/v1alpha1/k8sgpt_types.go @@ -42,22 +42,26 @@ type WebhookRef struct { Endpoint string `json:"webhook,omitempty"` } -// K8sGPTSpec defines the desired state of K8sGPT -type K8sGPTSpec struct { +type AISpec struct { // +kubebuilder:default:=openai // +kubebuilder:validation:Enum=openai;localai;azureopenai Backend string `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"` + Model string `json:"model,omitempty"` + Engine string `json:"engine,omitempty"` + Secret *SecretRef `json:"secret,omitempty"` + Enabled bool `json:"enabled,omitempty"` +} + +// K8sGPTSpec defines the desired state of K8sGPT +type K8sGPTSpec struct { Version string `json:"version,omitempty"` - EnableAI bool `json:"enableAI,omitempty"` NoCache bool `json:"noCache,omitempty"` Filters []string `json:"filters,omitempty"` ExtraOptions *ExtraOptionsRef `json:"extraOptions,omitempty"` Sink *WebhookRef `json:"sink,omitempty"` + AI *AISpec `json:"ai,omitempty"` } const ( diff --git a/api/v1alpha1/k8sgpt_types_test.go b/api/v1alpha1/k8sgpt_types_test.go index 81029b3b..52c055ff 100644 --- a/api/v1alpha1/k8sgpt_types_test.go +++ b/api/v1alpha1/k8sgpt_types_test.go @@ -50,13 +50,16 @@ var _ = Describe("The test cases for the K8sGPT CRDs", func() { Namespace: Namespace, }, Spec: K8sGPTSpec{ - Backend: OpenAI, - BaseUrl: baseUrl, - Model: model, - Secret: &secretRef, - Version: version, - EnableAI: true, - NoCache: true, + AI: &AISpec{ + Backend: OpenAI, + BaseUrl: baseUrl, + Model: model, + Enabled: true, + Secret: &secretRef, + }, + Version: version, + + NoCache: true, }, } @@ -69,13 +72,16 @@ var _ = Describe("The test cases for the K8sGPT CRDs", func() { Namespace: Namespace, }, Spec: K8sGPTSpec{ - Backend: AzureOpenAI, - BaseUrl: baseUrl, - Model: model, - Secret: &secretRef, - Version: version, - EnableAI: false, - NoCache: false, + AI: &AISpec{ + Backend: OpenAI, + BaseUrl: baseUrl, + Model: model, + Secret: &secretRef, + Enabled: false, + }, + Version: version, + + NoCache: false, }, } @@ -107,7 +113,7 @@ var _ = Describe("The test cases for the K8sGPT CRDs", func() { // Check the K8sGPT CRDs object's name and the APIVersion. Expect(k8sGPTObject.Name).Should(Equal("k8s-gpt")) Expect(k8sGPTObject.APIVersion).Should(Equal(GroupVersion.String())) - Expect(k8sGPTObject.Spec.EnableAI).Should(Equal(true)) + Expect(k8sGPTObject.Spec.AI.Enabled).Should(Equal(true)) //get K8sGPT CRD by resource name Expect(fakeClient.Get(ctx, types.NamespacedName{Name: "k8s-gpt-2", Namespace: Namespace}, &k8sGPTObject)).Should(Succeed()) @@ -132,10 +138,10 @@ var _ = Describe("The test cases for the K8sGPT CRDs", func() { // Get the K8sGPT CRDs by the name and namespace. Expect(fakeClient.Get(ctx, typeNamespace, &k8sGPTObject)).Should(Succeed()) // Update the K8sGPT CRDs. - k8sGPTObject.Spec.EnableAI = false + k8sGPTObject.Spec.AI.Enabled = false Expect(fakeClient.Update(ctx, &k8sGPTObject)).Should(Succeed()) // check the K8sGPT CRDs should be equal to false - Expect(k8sGPTObject.Spec.EnableAI).Should(Equal(false)) + Expect(k8sGPTObject.Spec.AI.Enabled).Should(Equal(false)) }) // Delete the K8sGPT CRDs. diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 1d307070..7b507896 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -25,6 +25,26 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AISpec) DeepCopyInto(out *AISpec) { + *out = *in + if in.Secret != nil { + in, out := &in.Secret, &out.Secret + *out = new(SecretRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AISpec. +func (in *AISpec) DeepCopy() *AISpec { + if in == nil { + return nil + } + out := new(AISpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Backstage) DeepCopyInto(out *Backstage) { *out = *in @@ -142,11 +162,6 @@ func (in *K8sGPTList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *K8sGPTSpec) DeepCopyInto(out *K8sGPTSpec) { *out = *in - if in.Secret != nil { - in, out := &in.Secret, &out.Secret - *out = new(SecretRef) - **out = **in - } if in.Filters != nil { in, out := &in.Filters, &out.Filters *out = make([]string, len(*in)) @@ -162,6 +177,11 @@ func (in *K8sGPTSpec) DeepCopyInto(out *K8sGPTSpec) { *out = new(WebhookRef) **out = **in } + if in.AI != nil { + in, out := &in.AI, &out.AI + *out = new(AISpec) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8sGPTSpec. diff --git a/chart/operator/templates/k8sgpt-crd.yaml b/chart/operator/templates/k8sgpt-crd.yaml index bcb93dd3..52193238 100644 --- a/chart/operator/templates/k8sgpt-crd.yaml +++ b/chart/operator/templates/k8sgpt-crd.yaml @@ -35,19 +35,34 @@ spec: spec: description: K8sGPTSpec defines the desired state of K8sGPT properties: - backend: - default: openai - enum: - - openai - - localai - - azureopenai - type: string - baseUrl: - type: string - enableAI: - type: boolean - engine: - type: string + ai: + properties: + backend: + default: openai + enum: + - openai + - localai + - azureopenai + type: string + baseUrl: + type: string + enabled: + type: boolean + engine: + type: string + model: + default: gpt-3.5-turbo + type: string + secret: + properties: + key: + type: string + name: + type: string + type: object + required: + - backend + type: object extraOptions: properties: backstage: @@ -56,22 +71,12 @@ spec: type: boolean type: object type: object - model: - default: gpt-3.5-turbo - type: string - noCache: - type: boolean filters: items: type: string type: array - secret: - properties: - key: - type: string - name: - type: string - type: object + noCache: + type: boolean sink: properties: type: @@ -97,4 +102,4 @@ status: kind: "" plural: "" conditions: [] - storedVersions: [] + storedVersions: [] \ No newline at end of file diff --git a/chart/operator/templates/result-crd.yaml b/chart/operator/templates/result-crd.yaml index b7389781..dbfb69f7 100644 --- a/chart/operator/templates/result-crd.yaml +++ b/chart/operator/templates/result-crd.yaml @@ -15,7 +15,16 @@ spec: singular: result scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - description: Kind + jsonPath: .spec.kind + name: Kind + type: string + - description: Backend + jsonPath: .spec.backend + name: Backend + type: string + name: v1alpha1 schema: openAPIV3Schema: description: Result is the Schema for the results API @@ -35,6 +44,8 @@ spec: spec: description: ResultSpec defines the desired state of Result properties: + backend: + type: string details: type: string error: @@ -54,14 +65,13 @@ spec: type: object type: array kind: - description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - Important: Run "make" to regenerate code after modifying this file' type: string name: type: string parentObject: type: string required: + - backend - details - error - kind @@ -86,4 +96,4 @@ status: kind: "" plural: "" conditions: [] - storedVersions: [] + storedVersions: [] \ No newline at end of file diff --git a/config/crd/bases/core.k8sgpt.ai_k8sgpts.yaml b/config/crd/bases/core.k8sgpt.ai_k8sgpts.yaml index 3104878a..6ada0338 100644 --- a/config/crd/bases/core.k8sgpt.ai_k8sgpts.yaml +++ b/config/crd/bases/core.k8sgpt.ai_k8sgpts.yaml @@ -35,19 +35,34 @@ spec: spec: description: K8sGPTSpec defines the desired state of K8sGPT properties: - backend: - default: openai - enum: - - openai - - localai - - azureopenai - type: string - baseUrl: - type: string - enableAI: - type: boolean - engine: - type: string + ai: + properties: + backend: + default: openai + enum: + - openai + - localai + - azureopenai + type: string + baseUrl: + type: string + enabled: + type: boolean + engine: + type: string + model: + default: gpt-3.5-turbo + type: string + secret: + properties: + key: + type: string + name: + type: string + type: object + required: + - backend + type: object extraOptions: properties: backstage: @@ -60,18 +75,8 @@ spec: items: type: string type: array - model: - default: gpt-3.5-turbo - type: string noCache: type: boolean - secret: - properties: - key: - type: string - name: - type: string - type: object sink: properties: type: @@ -83,8 +88,6 @@ spec: type: object version: type: string - required: - - backend type: object status: description: K8sGPTStatus defines the observed state of K8sGPT diff --git a/config/samples/core_v1alpha1_k8sgpt.yaml b/config/samples/core_v1alpha1_k8sgpt.yaml index 677a698e..6d214b28 100644 --- a/config/samples/core_v1alpha1_k8sgpt.yaml +++ b/config/samples/core_v1alpha1_k8sgpt.yaml @@ -4,12 +4,12 @@ metadata: name: k8sgpt-sample namespace: k8sgpt-operator-system spec: - model: gpt-3.5-turbo - backend: openai + ai: + model: gpt-3.5-turbo + backend: openai + enabled: true + secret: + name: k8sgpt-sample-secret + key: openai-api-key noCache: false version: v0.3.0 - enableAI: true - secret: - name: k8sgpt-sample-secret - key: openai-api-key - # TODO(user): Add fields here diff --git a/config/samples/k8sgpt_localai.yaml b/config/samples/k8sgpt_localai.yaml index 40d28f62..f8eca715 100644 --- a/config/samples/k8sgpt_localai.yaml +++ b/config/samples/k8sgpt_localai.yaml @@ -4,9 +4,10 @@ metadata: name: k8sgpt-sample-localai namespace: k8sgpt-operator-system spec: - model: ggml-gpt4all-j-v1.3-groovy.bin - backend: localai - baseUrl: http://local-ai.local-ai.svc.cluster.local:8080/v1 - noCache: false - enableAI: true - version: v0.3.0 \ No newline at end of file + ai: + model: ggml-gpt4all-j-v1.3-groovy.bin + backend: localai + baseUrl: http://local-ai.local-ai.svc.cluster.local:8080/v1 + enabled: true + version: v0.3.0 + noCache: false \ No newline at end of file diff --git a/pkg/client/client.go b/pkg/client/client.go index 6382bcc0..eda28625 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -48,9 +48,9 @@ func (c *Client) ProcessAnalysis(deployment v1.Deployment, config *v1alpha1.K8sG client := rpc.NewServerClient(c.conn) req := &schemav1.AnalyzeRequest{ - Explain: config.Spec.EnableAI, + Explain: config.Spec.AI.Enabled, Nocache: config.Spec.NoCache, - Backend: string(config.Spec.Backend), + Backend: string(config.Spec.AI.Backend), Filters: config.Spec.Filters, } diff --git a/pkg/resources/k8sgpt.go b/pkg/resources/k8sgpt.go index 8d01c172..87cc3aef 100644 --- a/pkg/resources/k8sgpt.go +++ b/pkg/resources/k8sgpt.go @@ -188,11 +188,11 @@ func GetDeployment(config v1alpha1.K8sGPT) (*appsv1.Deployment, error) { Env: []v1.EnvVar{ { Name: "K8SGPT_MODEL", - Value: config.Spec.Model, + Value: config.Spec.AI.Model, }, { Name: "K8SGPT_BACKEND", - Value: string(config.Spec.Backend), + Value: string(config.Spec.AI.Backend), }, { Name: "XDG_CONFIG_HOME", @@ -236,15 +236,15 @@ func GetDeployment(config v1alpha1.K8sGPT) (*appsv1.Deployment, error) { }, }, } - if config.Spec.Secret != nil { + if config.Spec.AI.Secret != nil { password := v1.EnvVar{ Name: "K8SGPT_PASSWORD", ValueFrom: &v1.EnvVarSource{ SecretKeyRef: &v1.SecretKeySelector{ LocalObjectReference: v1.LocalObjectReference{ - Name: config.Spec.Secret.Name, + Name: config.Spec.AI.Secret.Name, }, - Key: config.Spec.Secret.Key, + Key: config.Spec.AI.Secret.Key, }, }, } @@ -252,25 +252,25 @@ func GetDeployment(config v1alpha1.K8sGPT) (*appsv1.Deployment, error) { deployment.Spec.Template.Spec.Containers[0].Env, password, ) } - if config.Spec.BaseUrl != "" { + if config.Spec.AI.BaseUrl != "" { baseUrl := v1.EnvVar{ Name: "K8SGPT_BASEURL", - Value: config.Spec.BaseUrl, + Value: config.Spec.AI.BaseUrl, } deployment.Spec.Template.Spec.Containers[0].Env = append( deployment.Spec.Template.Spec.Containers[0].Env, baseUrl, ) } // Engine is required only when azureopenai is the ai backend - if config.Spec.Engine != "" && config.Spec.Backend == v1alpha1.AzureOpenAI { + if config.Spec.AI.Engine != "" && config.Spec.AI.Backend == v1alpha1.AzureOpenAI { engine := v1.EnvVar{ Name: "K8SGPT_ENGINE", - Value: config.Spec.Engine, + Value: config.Spec.AI.Engine, } deployment.Spec.Template.Spec.Containers[0].Env = append( deployment.Spec.Template.Spec.Containers[0].Env, engine, ) - } else if config.Spec.Engine != "" && config.Spec.Backend != v1alpha1.AzureOpenAI { + } else if config.Spec.AI.Engine != "" && config.Spec.AI.Backend != v1alpha1.AzureOpenAI { return &appsv1.Deployment{}, err.New("Engine is supported only by azureopenai provider.") } return &deployment, nil @@ -322,10 +322,10 @@ func Sync(ctx context.Context, c client.Client, case Create: // before creation we will check to see if the secret exists if used as a ref - if config.Spec.Secret != nil { + if config.Spec.AI.Secret != nil { secret := &v1.Secret{} - er := c.Get(ctx, types.NamespacedName{Name: config.Spec.Secret.Name, + er := c.Get(ctx, types.NamespacedName{Name: config.Spec.AI.Secret.Name, Namespace: config.Namespace}, secret) if er != nil { return err.New("references secret does not exist, cannot create deployment") diff --git a/pkg/resources/results.go b/pkg/resources/results.go index 97804538..70e89e18 100644 --- a/pkg/resources/results.go +++ b/pkg/resources/results.go @@ -22,7 +22,7 @@ const ( func MapResults(i integrations.Integrations, resultsSpec []v1alpha1.ResultSpec, config v1alpha1.K8sGPT) (map[string]v1alpha1.Result, error) { namespace := config.Namespace - backend := config.Spec.Backend + backend := config.Spec.AI.Backend backstageEnabled := config.Spec.ExtraOptions != nil && config.Spec.ExtraOptions.Backstage.Enabled rawResults := make(map[string]v1alpha1.Result) for _, resultSpec := range resultsSpec {