Skip to content

Commit

Permalink
Read API key from environment (#17)
Browse files Browse the repository at this point in the history
## Problem
Currently, there are two methods of authenticating with Pinecone when
using the CLI:

- Manually logging in to generate an auth token with `pinecone login`.
- Manually configuring an API key for the CLI to use with `pinecone
config set-api-key "YOUR_KEY"`.

There is also one method of swapping the environment that the CLI is
targeting ("production" vs. "staging"):

- Manually configuring your environment using `pinecone config
set-environment "production"`.

We've gotten feedback that it would be helpful to allow configuring both
API key and the environment value through environment variables rather
than needing to explicitly configure them through commands.

## Solution
- Update `SecretsViper` and `ConfigViper` to properly bind to
environment variables. In both cases we use
`viper.SetEnvPrefix("pinecone")` and `viper.BindEnv()` to bind the
associated `KeyName`. You can find details about how Viper works here:
  - https://pkg.go.dev/github.com/spf13/viper#SetEnvPrefix
  - https://pkg.go.dev/github.com/spf13/viper#BindEnv
- What the above does is allows the specific configuration values to
read from `PINECONE_API_KEY` and `PINECONE_ENVIRONMENT` as needed. If a
user calls `pinecone config set-environment` or `pinecone config
set-api-key`, that value will take precedence over the environment
value.
- Update `README` to be a bit more explicit about different ways of
authenticating with the CLI, and which takes precedence.

## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [X] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] This change requires a documentation update
- [ ] Infrastructure change (CI configs, etc)
- [ ] Non-code change (docs, etc)
- [ ] None of the above: (explain here)

## Test Plan
Pull this branch down, use `goreleaser` to build, and try various
options for authenticating with the CLI.

```
# build CLI locally
goreleaser build --single-target --snapshot --clean

# try with environment API key
export PINECONE_API_KEY="MY_KEY"
./dist/pinecone_darwin_arm64/pinecone index list

# try with config API key
./dist/pinecone_darwin_arm64/pinecone config set-api-key "MY_KEY"
./dist/pinecone_darwin_arm64/pinecone index list

# try setting the environment
export PINECONE_ENVIRONMENT="staging"
./dist/pinecone_darwin_arm64/pinecone index list
# above should fail if you haven't swapped your API key to a staging API key
unset PINECONE_ENVIRONMENT

# clear things and try with login flow
./dist/pinecone_darwin_arm64/pinecone logout
unset PINECONE_API_KEY
./dist/pinecone_darwin_arm64/pinecone login
``` 


---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1207872106532407
  • Loading branch information
austin-denoble authored Sep 30, 2024
1 parent a9a6066 commit 7d1c69f
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 5 deletions.
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Pinecone CLI

`pinecone` is Pinecone on the command line.
`pinecone` is Pinecone on the command line.

This CLI is still in an alpha state and does not support every operation available through our REST apis. Please try it out and give us your feedback, but also be prepared to upgrade as we continue building out the feature set and improving the UX.

Expand Down Expand Up @@ -34,12 +34,28 @@ To learn about the steps involved in building from source, see [CONTRIBUTING](./

## Usage

In order to use the Pinecone CLI you will need to authenticate with Pinecone services. This can be done either with an API key, or using the `pinecone login` flow to authenticate with a Pinecone account via your browser.

```shell
pinecone --help

# If you have PINECONE_API_KEY set in your environment you can begin working with the CLI
pinecone index list

# To set an API key manually, you can use the config command
pinecone config set-api-key "YOUR_API_KEY"

# Additionally, you can authenticate through the browser using the login command
pinecone login

# To clear your current login state or configured API key, you can use the logout command
pinecone logout
```

If an API key is configured along with using `pinecone login`, the CLI will default to using the API key over the authentication token.

If there has been an API key set using `pinecone config set-api-key`, and `PINECONE_API_KEY` is also present in the environment, the API set in the CLI config will be used over the environment key.

### Managing indexes

```sh
Expand Down
30 changes: 30 additions & 0 deletions internal/pkg/utils/configuration/config/config.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package config

import (
"fmt"
"strings"

"github.com/pinecone-io/cli/internal/pkg/utils/configuration"
"github.com/pinecone-io/cli/internal/pkg/utils/exit"
"github.com/spf13/viper"
)

Expand Down Expand Up @@ -33,4 +37,30 @@ var configFile = configuration.ConfigFile{

func init() {
configFile.Init()

ConfigViper.SetEnvPrefix("pinecone")
err := ConfigViper.BindEnv(Environment.KeyName)
if err != nil {
exit.Error(err)
}

err = validateEnvironment(Environment.Get())
fmt.Printf("ERROR? %v\n", err)
if err != nil {
exit.Error(err)
}
}

func validateEnvironment(env string) error {
validEnvs := []string{"production", "staging"}
for _, validEnv := range validEnvs {
if env == validEnv {
return nil
}
}
quotedEnvs := make([]string, len(validEnvs))
for i, validEnv := range validEnvs {
quotedEnvs[i] = fmt.Sprintf("\"%s\"", validEnv)
}
return fmt.Errorf("invalid environment: \"%s\", must be one of %s", env, strings.Join(quotedEnvs, ", "))
}
7 changes: 7 additions & 0 deletions internal/pkg/utils/configuration/secrets/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package secrets

import (
"github.com/pinecone-io/cli/internal/pkg/utils/configuration"
"github.com/pinecone-io/cli/internal/pkg/utils/exit"
"github.com/spf13/viper"
"golang.org/x/oauth2"
)
Expand Down Expand Up @@ -36,4 +37,10 @@ var ConfigFile = configuration.ConfigFile{

func init() {
ConfigFile.Init()

SecretsViper.SetEnvPrefix("pinecone")
err := SecretsViper.BindEnv(ApiKey.KeyName)
if err != nil {
exit.Error(err)
}
}
7 changes: 3 additions & 4 deletions internal/pkg/utils/sdk/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,15 @@ func newClientForUserFromTarget() *pinecone.Client {
Str("targetProjectId", targetProjectId).
Msg("Loading target context")

apiKey := secrets.ApiKey.Get()
oauth2Token := secrets.OAuth2Token.Get()

if apiKey != "" {
if secrets.ApiKey.Get() != "" {
if oauth2Token.AccessToken != "" {
msg.WarnMsg("You are currently logged in and also have an API key set in your configuration. The API key (which is linked to a specific project) will be used in preference to any user authentication and target context that may be present.\n")
msg.WarnMsg("You are currently logged in and also have an API key set in your environment and/or local configuration. The API key (which is linked to a specific project) will be used in preference to any user authentication and target context that may be present.\n")
}

log.Debug().Msg("Creating client for machine using stored API key")
return NewClientForMachine(apiKey)
return NewClientForMachine(secrets.ApiKey.Get())
}

log.Debug().Msg("No API key is stored in configuration, so attempting to create a client using user access token")
Expand Down

0 comments on commit 7d1c69f

Please sign in to comment.