Skip to content

Commit

Permalink
feat: add auth commands (#369)
Browse files Browse the repository at this point in the history
* feat: adding commands to auth

Signed-off-by: Alexandre Steppé <alexandre.steppe@gmail.com>

* fix: fixing new backend password

Signed-off-by: Alexandre Steppé <alexandre.steppe@gmail.com>

* fix: fixing missing func in iai

Signed-off-by: Alexandre Steppé <alexandre.steppe@gmail.com>

* added engine + updated readme

Signed-off-by: Alexandre Steppé <alexandre.steppe@gmail.com>

---------

Signed-off-by: Alexandre Steppé <alexandre.steppe@gmail.com>
  • Loading branch information
HatiCode committed May 5, 2023
1 parent d6bcb96 commit 00aaae8
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 104 deletions.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -161,7 +161,7 @@ _Run a scan with the default analyzers_

```
k8sgpt generate
k8sgpt auth
k8sgpt auth new
k8sgpt analyze --explain
```

Expand Down Expand Up @@ -226,6 +226,11 @@ k8sgpt filters remove [filter(s)]

<details>

_List configured backends_

```
k8sgpt auth list
```

_List integrations_

Expand Down Expand Up @@ -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 <model_name> --baseurl http://localhost:8080/v1
k8sgpt auth new --backend localai --model <model_name> --baseurl http://localhost:8080/v1
```

Now you can analyze with the `localai` backend:
Expand Down
109 changes: 9 additions & 100 deletions cmd/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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)
}
47 changes: 47 additions & 0 deletions cmd/auth/list.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
},
}
126 changes: 126 additions & 0 deletions cmd/auth/new.go
Original file line number Diff line number Diff line change
@@ -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")
}

0 comments on commit 00aaae8

Please sign in to comment.