From 00aaae86d88812bd6b6be290ba440ea583ccc01c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20Stepp=C3=A9?= Date: Fri, 5 May 2023 10:26:31 +0200 Subject: [PATCH] feat: add auth commands (#369) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: adding commands to auth Signed-off-by: Alexandre Steppé * fix: fixing new backend password Signed-off-by: Alexandre Steppé * fix: fixing missing func in iai Signed-off-by: Alexandre Steppé * added engine + updated readme Signed-off-by: Alexandre Steppé --------- Signed-off-by: Alexandre Steppé --- README.md | 13 +++-- cmd/auth/auth.go | 109 ++++------------------------------------ cmd/auth/list.go | 47 ++++++++++++++++++ cmd/auth/new.go | 126 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 191 insertions(+), 104 deletions(-) create mode 100644 cmd/auth/list.go create mode 100644 cmd/auth/new.go diff --git a/README.md b/README.md index e1211dc22d..94caf6b301 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ _This mode of operation is ideal for continuous monitoring of your cluster and c * Currently the default AI provider is OpenAI, you will need to generate an API key from [OpenAI](https://openai.com) * You can do this by running `k8sgpt generate` to open a browser link to generate it -* Run `k8sgpt auth` to set it in k8sgpt. +* Run `k8sgpt auth new` to set it in k8sgpt. * You can provide the password directly using the `--password` flag. * Run `k8sgpt filters` to manage the active filters used by the analyzer. By default, all filters are executed during analysis. * Run `k8sgpt analyze` to run a scan. @@ -161,7 +161,7 @@ _Run a scan with the default analyzers_ ``` k8sgpt generate -k8sgpt auth +k8sgpt auth new k8sgpt analyze --explain ``` @@ -226,6 +226,11 @@ k8sgpt filters remove [filter(s)]
+_List configured backends_ + +``` +k8sgpt auth list +``` _List integrations_ @@ -300,10 +305,10 @@ To start the API server, follow the instruction in [LocalAI](https://github.com/ ### Run k8sgpt -To run k8sgpt, run `k8sgpt auth` with the `localai` backend: +To run k8sgpt, run `k8sgpt auth new` with the `localai` backend: ``` -k8sgpt auth --backend localai --model --baseurl http://localhost:8080/v1 +k8sgpt auth new --backend localai --model --baseurl http://localhost:8080/v1 ``` Now you can analyze with the `localai` backend: diff --git a/cmd/auth/auth.go b/cmd/auth/auth.go index 3aa29756d2..6c18645d80 100644 --- a/cmd/auth/auth.go +++ b/cmd/auth/auth.go @@ -14,16 +14,8 @@ limitations under the License. package auth import ( - "fmt" - "os" - "strings" - "syscall" - - "github.com/fatih/color" "github.com/k8sgpt-ai/k8sgpt/pkg/ai" "github.com/spf13/cobra" - "github.com/spf13/viper" - "golang.org/x/term" ) var ( @@ -34,107 +26,24 @@ var ( engine string ) +var configAI ai.AIConfiguration + // authCmd represents the auth command var AuthCmd = &cobra.Command{ Use: "auth", Short: "Authenticate with your chosen backend", Long: `Provide the necessary credentials to authenticate with your chosen backend.`, - PreRun: func(cmd *cobra.Command, args []string) { - backend, _ := cmd.Flags().GetString("backend") - if strings.ToLower(backend) == "azureopenai" { - cmd.MarkFlagRequired("engine") - cmd.MarkFlagRequired("baseurl") - } - }, Run: func(cmd *cobra.Command, args []string) { - - // get ai configuration - var configAI ai.AIConfiguration - err := viper.UnmarshalKey("ai", &configAI) - if err != nil { - color.Red("Error: %v", err) - os.Exit(1) - } - - // search for provider with same name - providerIndex := -1 - for i, provider := range configAI.Providers { - if backend == provider.Name { - providerIndex = i - break - } - } - - validBackend := func(validBackends []string, backend string) bool { - for _, b := range validBackends { - if b == backend { - return true - } - } - return false - } - - // check if backend is not empty and a valid value - if backend == "" || !validBackend(ai.Backends, backend) { - color.Red("Error: Backend AI cannot be empty and accepted values are '%v'", strings.Join(ai.Backends, ", ")) - os.Exit(1) - } - - color.Green("Using %s as backend AI provider", backend) - - // check if model is not empty - if model == "" { - color.Red("Error: Model cannot be empty.") - os.Exit(1) - } - - if ai.NeedPassword(backend) && password == "" { - fmt.Printf("Enter %s Key: ", backend) - bytePassword, err := term.ReadPassword(int(syscall.Stdin)) - if err != nil { - color.Red("Error reading %s Key from stdin: %s", backend, - err.Error()) - os.Exit(1) - } - password = strings.TrimSpace(string(bytePassword)) - } - - // create new provider object - newProvider := ai.AIProvider{ - Name: backend, - Model: model, - Password: password, - BaseURL: baseURL, - Engine: engine, - } - - if providerIndex == -1 { - // provider with same name does not exist, add new provider to list - configAI.Providers = append(configAI.Providers, newProvider) - color.Green("New provider added") - } else { - // provider with same name exists, update provider info - configAI.Providers[providerIndex] = newProvider - color.Green("Provider updated") - } - viper.Set("ai", configAI) - if err := viper.WriteConfig(); err != nil { - color.Red("Error writing config file: %s", err.Error()) - os.Exit(1) + if len(args) == 0 { + cmd.Help() + return } - color.Green("key added") }, } func init() { - // add flag for backend - AuthCmd.Flags().StringVarP(&backend, "backend", "b", "openai", "Backend AI provider") - // add flag for model - AuthCmd.Flags().StringVarP(&model, "model", "m", "gpt-3.5-turbo", "Backend AI model") - // add flag for password - AuthCmd.Flags().StringVarP(&password, "password", "p", "", "Backend AI password") - // add flag for url - AuthCmd.Flags().StringVarP(&baseURL, "baseurl", "u", "", "URL AI provider, (e.g `http://localhost:8080/v1`)") - // add flag for azure open ai engine/deployment name - AuthCmd.Flags().StringVarP(&engine, "engine", "e", "", "Azure AI deployment name") + // add subcommand to list backends + AuthCmd.AddCommand(listCmd) + // add subcommand to create new backend provider + AuthCmd.AddCommand(newCmd) } diff --git a/cmd/auth/list.go b/cmd/auth/list.go new file mode 100644 index 0000000000..41b7ecdae5 --- /dev/null +++ b/cmd/auth/list.go @@ -0,0 +1,47 @@ +/* +Copyright 2023 The K8sGPT Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package auth + +import ( + "fmt" + "os" + + "github.com/fatih/color" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +var listCmd = &cobra.Command{ + Use: "list", + Short: "List configured providers", + Long: "The list command displays a list of configured providers", + Run: func(cmd *cobra.Command, args []string) { + + // get ai configuration + err := viper.UnmarshalKey("ai", &configAI) + if err != nil { + color.Red("Error: %v", err) + os.Exit(1) + } + + // iterate over the provider list and prints each provider name + for _, provider := range configAI.Providers { + if len(configAI.Providers) == 0 { + color.Red("Provider list is currently empty.") + } else { + fmt.Printf("> %s\n", provider.Name) + } + } + }, +} diff --git a/cmd/auth/new.go b/cmd/auth/new.go new file mode 100644 index 0000000000..9dd1d56b3e --- /dev/null +++ b/cmd/auth/new.go @@ -0,0 +1,126 @@ +/* +Copyright 2023 The K8sGPT Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package auth + +import ( + "fmt" + "os" + "strings" + "syscall" + + "github.com/fatih/color" + "github.com/k8sgpt-ai/k8sgpt/pkg/ai" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "golang.org/x/term" +) + +var newCmd = &cobra.Command{ + Use: "new", + Short: "Configure new provider", + Long: "The new command allows to configure a new backend AI provider", + PreRun: func(cmd *cobra.Command, args []string) { + backend, _ := cmd.Flags().GetString("backend") + if strings.ToLower(backend) == "azureopenai" { + cmd.MarkFlagRequired("engine") + cmd.MarkFlagRequired("baseurl") + } + }, + Run: func(cmd *cobra.Command, args []string) { + + // get ai configuration + err := viper.UnmarshalKey("ai", &configAI) + if err != nil { + color.Red("Error: %v", err) + os.Exit(1) + } + + // search for provider with same name + providerIndex := -1 + for i, provider := range configAI.Providers { + if backend == provider.Name { + providerIndex = i + break + } + } + + validBackend := func(validBackends []string, backend string) bool { + for _, b := range validBackends { + if b == backend { + return true + } + } + return false + } + + // check if backend is not empty and a valid value + if backend == "" || !validBackend(ai.Backends, backend) { + color.Red("Error: Backend AI cannot be empty and accepted values are '%v'", strings.Join(ai.Backends, ", ")) + os.Exit(1) + } + + // check if model is not empty + if model == "" { + color.Red("Error: Model cannot be empty.") + os.Exit(1) + } + + if ai.NeedPassword(backend) && password == "" { + fmt.Printf("Enter %s Key: ", backend) + bytePassword, err := term.ReadPassword(int(syscall.Stdin)) + if err != nil { + color.Red("Error reading %s Key from stdin: %s", backend, + err.Error()) + os.Exit(1) + } + password = strings.TrimSpace(string(bytePassword)) + } + + // create new provider object + newProvider := ai.AIProvider{ + Name: backend, + Model: model, + Password: password, + BaseURL: baseURL, + Engine: engine, + } + + if providerIndex == -1 { + // provider with same name does not exist, add new provider to list + configAI.Providers = append(configAI.Providers, newProvider) + viper.Set("ai", configAI) + if err := viper.WriteConfig(); err != nil { + color.Red("Error writing config file: %s", err.Error()) + os.Exit(1) + } + color.Green("%s added to the AI backend provider list", backend) + } else { + // provider with same name exists, update provider info + color.Yellow("Provider with same name already exists, use update command to modify an existing provider configuration") + } + }, +} + +func init() { + // add flag for backend + newCmd.Flags().StringVarP(&backend, "backend", "b", "openai", "Backend AI provider") + // add flag for model + newCmd.Flags().StringVarP(&model, "model", "m", "gpt-3.5-turbo", "Backend AI model") + // add flag for password + newCmd.Flags().StringVarP(&password, "password", "p", "", "Backend AI password") + // add flag for url + newCmd.Flags().StringVarP(&baseURL, "baseurl", "u", "", "URL AI provider, (e.g `http://localhost:8080/v1`)") + // add flag for azure open ai engine/deployment name + AuthCmd.Flags().StringVarP(&engine, "engine", "e", "", "Azure AI deployment name") +}