Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 94 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,121 @@
# ketch-cli

The Ketch Command-Line Interface.
Ketch command line interface.

## Get the code
## Obtaining

### Downloading a release

The easiest way to obtain the Ketch CLI is to download from the [latest release](https://github.com/ketch-com/ketch-cli/releases/latest).
Binaries for MacOS(Darwin), Linux and Windows are available. Download and unzip the file for your operating system.

### Building from source code

You can also build from source code using the following:
```shell
git clone git@github.com:ketch-com/ketch-cli.git
cd ketch-cli
go build -o ketch ./cmd/ketch/main.go
```

## Usage

To obtain a token, run the following command (use `ketch.exe` on Windows):

```shell
ketch login
```

This will output something similar to the following:
```shell
Now, go to https://ketch.us.auth0.com/activate?user_code=HZZK-JZJM and confirm the following code:

+-----------+
| HZZK-JZJM |
+-----------+

```

Open the link in your web browser and enter the code. Once completed, the token is printed to the standard output, such as:
```shell
$ git clone https://github.com/ketch-com/ketch-cli
$ cd ketch-cli/
eyJhbGciOiJSUzI1NiIsInR5cCI6Ik8239879487298472xmM3pBWDlFU3NGZi05c1V6diJ9.eyJpc3Muytfaf76Af786aof76fo8sdf6osdf6so8f6sf7s6o8f76asofayf78s6fosiyasof87eas6YyNzc1MDk0LCJhenAiOiJqOWdlbWl6c1hpczVJY1VnOTMxc0JqR295R1N4YlQxYSJ9.os987foFLUIluzydflfyldisutflsdiuftslfiutuftdliut736r3l7ltd83l6
```

## Getting the development dependencies
For easy use later, you can set the key to an environment variable. For example, using the sample token above:
```shell
export KETCH_TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCI6Ik8239879487298472xmM3pBWDlFU3NGZi05c1V6diJ9.eyJpc3Muytfaf76Af786aof76fo8sdf6osdf6so8f6sf7s6o8f76asofayf78s6fosiyasof87eas6YyNzc1MDk0LCJhenAiOiJqOWdlbWl6c1hpczVJY1VnOTMxc0JqR295R1N4YlQxYSJ9.os987foFLUIluzydflfyldisutflsdiuftslfiutuftdliut736r3l7ltd83l6"
```

Setting an environment variable saves having to specify the token on subsequent calls to the CLI.

On Linux/MacOS, you can set an environment variable automatically using the following:
```shell
go get -u ./...
export KETCH_TOKEN=$(ketch login)
```

## Building
All further examples will assume that a `KETCH_TOKEN` environment variable has been set.

### Transponder

The Transponder is multi-tenanted, so you can use a single transponder for multiple organizations in the Ketch platform.
The token obtained via [login](#Login) will be scoped to a single organization (the one you logged into when creating the
token).

You can build this project using Go:
To configure a transponder, you need to know the URL of the transponder and the computer where you run the CLI needs to
be able to access that URL. You can first check if the URL is accessible in your web browser or using `curl`. Once you have
confirmed that the URL is correct, you can then set an environment variable to save from having to specify the transponder URL
on subsequent executions of the CLI.

For example, if your transponder URL is `https://transponder-stage-uswest2.mycompany.com/`, then you can use the following:
```shell
go build ./...
export KETCH_URL="https://transponder-stage-uswest2.mycompany.com/"
```

You can also produce Linux binaries suitable for Docker Compose using the following:
All further examples will assume that a `KETCH_URL` environment variable has been set.

#### List Connections

Connections belong to organizations and provide secrets and configuration details for establishing connections to data systems.

To list the connections belonging to the current organization, use the following command:

```shell
./scripts/build.sh
ketch transponder ls
```

## Distribution
#### Configure Connection

To configure a connection, two preparation steps are required:
1. [Create the connection](https://docs.ketch.com/hc/en-us/articles/5883595869335-Connecting-the-Ketch-Transponder-to-Ketch#install-database-provider-0-2) in the Ketch console and note the connection code (we will use `my_connection` in the examples below)
2. Collect the required [configuration properties](https://docs.ketch.com/hc/en-us/articles/5922260652439-Database-Provider-Configuration-Parameters) for the data system you are configuring

Once you have the prerequisite information, you can run configure such as:
```shell
ketch transponder configure my_connection -P 'username=myuser' -P 'password=*****' ....other properties....
```

The docker containers produced by this repository are contained in the `docker` folder.
Potential problems:
1. The connection hasn't been configured in the Ketch console
2. The connection has been configured in a different organization than the current KETCH_TOKEN
3. Configuration properties are incomplete or incorrect
4. The transponder is unable to access the data system

## Updating dependencies
#### Rotate Organization API Key

To update the dependencies, run the following:
To update an organization's API key used by the Transponder, use the following:

```shell
rm go.sum
go get -u -t ./...
ketch transponder rotate
```

This command will connect to the transponder and then the transponder will connect to the Ketch platform, attempting to
rotate the current API key configured. If successfully rotated, it will replace the old API key. If there is a problem
rotating the API key, then the previous API key will not be replaced and an error will be displayed. The access token
obtained via login will be used to create the API key. Therefore, your user account must have permissions to manage
API keys.

Potential problems:
1. The transponder is unable to connect to the Ketch platform
2. The organization code does not exist
3. The token does not have permission to manage API keys
4. The API key does not have the appropriate permissions
50 changes: 26 additions & 24 deletions cmd/ketch/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import (
"go.ketch.com/cli/ketch-cli/pkg/flags"
"go.ketch.com/cli/ketch-cli/pkg/transponder"
"go.ketch.com/cli/ketch-cli/version"
"go.ketch.com/lib/orlop/v2/cmd"
stdlog "log"
"log"
"os"
"path"
)
Expand All @@ -23,7 +22,7 @@ func main() {
PadLevelText: true,
})

stdlog.SetOutput(logrus.New().Writer())
log.SetOutput(logrus.New().Writer())

var rootCmd = &cobra.Command{
Use: version.Name,
Expand Down Expand Up @@ -70,16 +69,9 @@ Simply type ` + rootCmd.Name() + ` help [path to command] for full details.`,
},
})

var runner = cmd.NewRunner(version.Name)

rootCmd.PersistentFlags().String(flags.Token, runner.Getenv("TOKEN"), "auth token")
rootCmd.PersistentFlags().String(flags.Config, ".ketchrc", "environment file")
rootCmd.PersistentFlags().String(flags.URL, runner.Getenv("URL"), "url to Ketch API")
rootCmd.PersistentFlags().Bool(flags.TLSInsecure, runner.Getenv("INSECURE") == "true", "set true to skip certificate verification")
rootCmd.PersistentFlags().String(flags.TLSCert, runner.Getenv("TLS_CERT_FILE"), "TLS client certificate")
rootCmd.PersistentFlags().String(flags.TLSKey, runner.Getenv("TLS_KEY_FILE"), "TLS private key")
rootCmd.PersistentFlags().String(flags.TLSCACert, runner.Getenv("TLS_ROOTCA_FILE"), "TLS root CA certificate")
rootCmd.PersistentFlags().String(flags.TLSServerName, runner.Getenv("TLS_OVERRIDE"), "override the TLS server name")
rootCmd.PersistentFlags().String(flags.Token, "", "Ketch API authorization token")
rootCmd.PersistentFlags().String(flags.URL, "", "url to Ketch API")
rootCmd.SilenceUsage = true

//
Expand Down Expand Up @@ -107,24 +99,34 @@ Simply type ` + rootCmd.Name() + ` help [path to command] for full details.`,

rootCmd.AddCommand(transponderCmd)

var transponderListCmd = &cobra.Command{
Use: "ls",
Short: "list connections",
RunE: transponder.List,
var transponderRotateCmd = &cobra.Command{
Use: "rotate",
Short: "rotate API key",
RunE: transponder.Rotate,
}

transponderCmd.AddCommand(transponderRotateCmd)

var transponderConnListCmd = &cobra.Command{
Use: "ls",
Short: "list connections",
RunE: transponder.List,
Aliases: []string{"list"},
}

transponderCmd.AddCommand(transponderListCmd)
transponderCmd.AddCommand(transponderConnListCmd)

var transponderConfigureCmd = &cobra.Command{
Use: "configure",
Short: "configure a connection",
RunE: transponder.Configure,
Args: cobra.ExactArgs(1),
var transponderConnConfigureCmd = &cobra.Command{
Use: "configure",
Short: "configure a connection",
RunE: transponder.Configure,
Args: cobra.ExactArgs(1),
Aliases: []string{"conf", "config"},
}

transponderConfigureCmd.Flags().StringToStringP(flags.Parameter, "P", nil, "parameter key/value")
transponderConnConfigureCmd.Flags().StringToStringP(flags.Parameter, "P", nil, "parameter key/value")

transponderCmd.AddCommand(transponderConfigureCmd)
transponderCmd.AddCommand(transponderConnConfigureCmd)

if err := rootCmd.ExecuteContext(context.Background()); err != nil {
os.Exit(1)
Expand Down
4 changes: 2 additions & 2 deletions pkg/cli/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,7 @@ func Login(cmd *cobra.Command, args []string) error {
return err
}

fmt.Printf("Now, go to %s and enter the following code:\n", dc.VerificationUri)
fmt.Printf("%s\n", dc.UserCode)
fmt.Fprintf(os.Stderr, "Now, go to %s and confirm the following code:\n\n +-----------+\n | %s |\n +-----------+\n\n", dc.VerificationUriComplete, dc.UserCode)

timeout := time.After(time.Duration(dc.ExpiresInSec) * time.Second)
ticker := time.NewTicker(time.Duration(dc.IntervalInSec) * time.Second)
Expand All @@ -100,6 +99,7 @@ func Login(cmd *cobra.Command, args []string) error {
if tok, ok, err := check(ctx, cfg, dc); err != nil {
return err
} else if ok {
fmt.Fprintf(os.Stderr, "The following is your KETCH_TOKEN:\n")
fmt.Println(tok)
return nil
}
Expand Down
28 changes: 24 additions & 4 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"github.com/spf13/cobra"
"go.ketch.com/cli/ketch-cli/pkg/flags"
"go.ketch.com/cli/ketch-cli/version"
"go.ketch.com/lib/orlop/v2"
"go.ketch.com/lib/orlop/v2/config"
"go.ketch.com/lib/orlop/v2/errors"
"net/url"
)

type Config struct {
Expand All @@ -20,11 +22,20 @@ func NewConfig(cmd *cobra.Command) (*Config, error) {
Auth0Domain: "ketch.us.auth0.com",
ClientID: "j9gemizsXis5IcUg931sBjGoyGSxbT1a",
Audience: "https://global.ketchapi.com/rest",
URL: config.GetEnv(version.Name, "URL"),
Token: config.GetEnv(version.Name, "TOKEN"),
}

err := orlop.Unmarshal(version.Name, cfg)
if err != nil {
return nil, err
if domain := config.GetEnv(version.Name, "AUTH0_DOMAIN"); len(domain) > 0 {
cfg.Auth0Domain = domain
}

if clientID := config.GetEnv(version.Name, "CLIENT_ID"); len(clientID) > 0 {
cfg.ClientID = clientID
}

if audience := config.GetEnv(version.Name, "AUDIENCE"); len(audience) > 0 {
cfg.Audience = audience
}

if s, err := cmd.Flags().GetString(flags.URL); err != nil {
Expand All @@ -39,5 +50,14 @@ func NewConfig(cmd *cobra.Command) (*Config, error) {
cfg.Token = s
}

if len(cfg.URL) == 0 {
return nil, errors.Invalidf("url is required. either specify --url or set KETCH_URL environment variable")
}

u, err := url.Parse(cfg.URL)
if err != nil || u.Scheme != "https" {
return nil, errors.Invalidf("url is invalid. check the value of --url or KETCH_URL (url provided is '%s')", cfg.URL)
}

return cfg, nil
}
17 changes: 5 additions & 12 deletions pkg/flags/flags.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
package flags

const (
File = "file"
Token = "token"
Config = "config"
Org = "org"
URL = "url"
Version = "version"
TLSInsecure = "insecure"
TLSCert = "tls-cert"
TLSKey = "tls-key"
TLSCACert = "tls-cacert"
TLSServerName = "servername"
Parameter = "parameter"
ApiKey = "api-key"
Token = "token"
Config = "config"
URL = "url"
Parameter = "parameter"
)
2 changes: 2 additions & 0 deletions pkg/transponder/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@ func Configure(cmd *cobra.Command, args []string) error {
if strings.HasPrefix(value, "@") {
// load from file
fileName := strings.TrimPrefix(value, "@")

data, err := os.ReadFile(fileName)
if err != nil {
return err
}

in[key] = base64.URLEncoding.EncodeToString(data)
}
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/transponder/ls.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func List(cmd *cobra.Command, args []string) error {
return err
}

u.Path = path.Join(u.Path, "/captain", "connections")
u.Path = path.Join(u.Path, "captain", "connections")

client := http.Client{}

Expand Down
7 changes: 7 additions & 0 deletions pkg/transponder/rotate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package transponder

import "github.com/spf13/cobra"

func Rotate(cmd *cobra.Command, args []string) error {
return nil
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TODO obviously

}