Skip to content

Commit

Permalink
make sure we can set an alias for configs. (#32)
Browse files Browse the repository at this point in the history
* make sure we can set an alias for configs.

* print the alias when generating project tree.

* make sure that all commands support index and alias when querying for the project.

* make sure that all commands support index and alias when querying for the project.

* update the args for delete.
  • Loading branch information
AnalogJ committed Aug 28, 2021
1 parent fbb8d59 commit 4ef69a9
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 65 deletions.
49 changes: 48 additions & 1 deletion README.md
Expand Up @@ -51,6 +51,7 @@ Drawbridge aims to solve this problem in a flexible and scalable way.
- Cleanup utility is built-in
- `drawbridge update` lets you update the binary inplace.
- Pretty colors. The CLI is all colorized to make it easy to skim for errors/warnings
- Assign memorable aliases to commonly used configurations

# Getting Started

Expand Down Expand Up @@ -87,6 +88,7 @@ COMMANDS:
create Create a drawbridge managed ssh config & associated files
list List all drawbridge managed ssh configs
connect Connect to a drawbridge managed ssh config
alias Create a named alias for a drawbridge config
download, scp Download a file from an internal server using drawbridge managed ssh config, syntax is similar to scp command.
delete Delete drawbridge managed ssh config(s)
proxy Build/Rebuild a Proxy auto-config (PAC) file to access websites through Drawbridge tunnels
Expand Down Expand Up @@ -166,7 +168,7 @@ Rendered Drawbridge Configs:
├── [9] shard_type: idle, username: aws
└── [10] shard_type: live, username: aws
Enter number of drawbridge config you would like to connect to (1-10):
Enter number of drawbridge config you would like to connect to (1-10, alias):
```

`drawbridge connect` will connect you to the bastion/jump host using a specified Drawbridge config file. It'll also add
Expand All @@ -176,6 +178,51 @@ If you want to connect directly to a internal server, you can do so by selecting

`drawbridge connect 1 database-1`

You can also connect directly to a environment using an alias

`drawbridge connect my_custom_alias database-1`

## Alias

You can assign an alias to a commonly used drawbridge configuration by using the `drawbridge alias` command.

```
$ drawbridge alias
...
└── [us-east-2] shard
├── [9] shard_type: idle, username: aws
└── [10] shard_type: live, username: aws
Enter drawbridge config number to create alias for (1-2, alias):
10
Please provide an alias for the configuration above (a-zA-Z0-9-_.):
my_new_alias
Setting alias (my_new_alias) for config (10)
```
Now when you run `drawbridge connect`, `drawbridge list` or most other drawbridge commands, you can use the alias instead of the id.

```
$ drawbridge list
...
└── [us-east-2] shard
├── [9] shard_type: idle, username: aws
└── [10, my_new_alias] shard_type: live, username: aws
$ drawbridge connect my_new_alias
...
```

You can also set the alias for a configuration in one command:

```
$ drawbridge alias 10 my_custom_alias
Setting alias (my_custom_alias) for config (10)
Warning: replacing existing alias (my_new_alias) with new value: my_custom_alias
```

## Delete

```
Expand Down
123 changes: 81 additions & 42 deletions cmd/drawbridge/drawbridge.go
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
log "github.com/sirupsen/logrus"
"os"
"regexp"
"time"

"github.com/analogj/drawbridge/pkg/actions"
Expand Down Expand Up @@ -113,7 +114,7 @@ OPTIONS:
answerData := map[string]interface{}{}
if projectList.Length() > 0 && utils.StdinQueryBoolean(fmt.Sprintf("Would you like to create a Drawbridge config using preconfigured answers? (%v available). [yes/no]", projectList.Length())) {

answerData, err = projectList.Prompt("Enter number to base your configuration from")
answerData, _, err = projectList.Prompt("Enter number to base your configuration from")
if err != nil {
return err
}
Expand All @@ -134,7 +135,7 @@ OPTIONS:
{
Name: "list",
Usage: "List all drawbridge managed ssh configs",
ArgsUsage: "[config_number]",
ArgsUsage: "[config_number/alias]",
Action: func(c *cli.Context) error {
fmt.Fprintln(c.App.Writer, c.Command.Usage)

Expand All @@ -145,18 +146,13 @@ OPTIONS:

var answerData map[string]interface{}
if c.NArg() > 0 {

index, err := utils.StringToInt(c.Args().Get(0))
if err != nil {
return err
}
answerData, err = projectList.GetIndex(index - 1)
answerData, _, err = projectList.GetWithAliasOrIndex(c.Args().Get(0))
if err != nil {
return err
}

} else {
answerData, err = projectList.Prompt("Enter drawbridge config number to retrieve full info")
answerData, _, err = projectList.Prompt("Enter drawbridge config number or alias to retrieve full info")
if err != nil {
return err
}
Expand All @@ -174,7 +170,7 @@ OPTIONS:
{
Name: "connect",
Usage: "Connect to a drawbridge managed ssh config",
ArgsUsage: "[config_number] [dest_server_hostname]",
ArgsUsage: "[config_number/alias] [dest_server_hostname]",
Action: func(c *cli.Context) error {
fmt.Fprintln(c.App.Writer, c.Command.Usage)

Expand All @@ -186,17 +182,13 @@ OPTIONS:
var answerData map[string]interface{}
if c.NArg() > 0 {

index, err := utils.StringToInt(c.Args().Get(0))
if err != nil {
return err
}
answerData, err = projectList.GetIndex(index - 1)
answerData, _, err = projectList.GetWithAliasOrIndex(c.Args().Get(0))
if err != nil {
return err
}

} else {
answerData, err = projectList.Prompt("Enter drawbridge config number to connect to")
answerData, _, err = projectList.Prompt("Enter drawbridge config number to connect to")
if err != nil {
return err
}
Expand Down Expand Up @@ -228,14 +220,76 @@ OPTIONS:
},
},
},
{
Name: "alias",
Usage: "Create a named alias for a drawbridge config",
ArgsUsage: "[config_number] [alias]",
Action: func(c *cli.Context) error {
fmt.Fprintln(c.App.Writer, c.Command.Usage)

projectList, err := project.CreateProjectListFromConfigDir(config)
if err != nil {
return err
}

var answerData map[string]interface{}
var answerIndex int
if c.NArg() > 0 {

answerData, _, err = projectList.GetWithAliasOrIndex(c.Args().Get(0))
if err != nil {
return err
}

} else {
answerData, answerIndex, err = projectList.Prompt("Enter drawbridge config number to create alias for")
if err != nil {
return err
}
}

fmt.Print("\nAnswer Data:\n")
for k, v := range answerData {
fmt.Printf("\t%v: %v\n", color.YellowString(k), v)
}

//get the alias name (if provided)
var configAlias string
if c.NArg() >= 2 {
configAlias = c.Args().Get(1)
isValid, err := regexp.MatchString(`^[A-Za-z][\w-\.]+$`, configAlias)
if err != nil || !isValid {
configAlias = utils.StdinQueryRegex("Please provide an alias for the configuration above", `^[A-Za-z][\w-\.]+$`, "a-zA-Z0-9-_.")
}
} else {
configAlias = utils.StdinQueryRegex("Please provide an alias for the configuration above", `^[A-Za-z][\w-\.]+$`, "a-zA-Z0-9-_.")
}

_, _, aliasErr := projectList.GetWithAlias(configAlias)
if aliasErr == nil {
return errors.ConfigValidationError("alias already exists.")
}

color.HiBlue("Setting alias (%s) for config (%d)\n", configAlias, answerIndex+1)

_, err = projectList.SetAliasForIndex(answerIndex, configAlias)

return err
},
},
{
Name: "download",
Aliases: []string{"scp"},
Usage: "Download a file from an internal server using drawbridge managed ssh config, syntax is similar to scp command. ",
ArgsUsage: "[config_number] destination_hostname:remote_filepath local_filepath",
ArgsUsage: "[config_number/alias] destination_hostname:remote_filepath local_filepath",
Action: func(c *cli.Context) error {
fmt.Fprintln(c.App.Writer, c.Command.Usage)

projectList, err := project.CreateProjectListFromConfigDir(config)
if err != nil {
return err
}

// PARSE ARGS
if c.NArg() < 2 || c.NArg() > 3 {
return errors.InvalidArgumentsError(fmt.Sprintf("2 or 3 arguments required. %v provided", c.Args().Len()))
Expand All @@ -248,10 +302,12 @@ OPTIONS:

args := c.Args().Slice()

var answerData map[string]interface{}

if c.NArg() == 3 {
index, err = utils.StringToInt(c.Args().First())
answerData, index, err = projectList.GetWithAliasOrIndex(c.Args().Get(0))
if err != nil {
return errors.InvalidArgumentsError("Invalid `config_id`, please specify a number")
return err
}
args = c.Args().Tail()
}
Expand All @@ -266,22 +322,9 @@ OPTIONS:

strLocalPath = args[1]

// select answer data.
projectList, err := project.CreateProjectListFromConfigDir(config)
if err != nil {
return err
}

var answerData map[string]interface{}
if index > 0 {

answerData, err = projectList.GetIndex(index - 1)
if err != nil {
return err
}

} else {
answerData, err = projectList.Prompt("Enter number of drawbridge config you would like to download from")
//index is unset, lets prompt for the answerData
if index == 0 {
answerData, _, err = projectList.Prompt("Enter number of drawbridge config you would like to download from")
if err != nil {
return err
}
Expand All @@ -295,7 +338,7 @@ OPTIONS:
{
Name: "delete",
Usage: "Delete drawbridge managed ssh config(s)",
ArgsUsage: "[config_number]",
ArgsUsage: "[config_number/alias]",
Action: func(c *cli.Context) error {
fmt.Fprintln(c.App.Writer, c.Command.Usage)

Expand All @@ -314,18 +357,14 @@ OPTIONS:
} else if c.NArg() > 0 {
//check if the user specified a config number in the args.

index, err := utils.StringToInt(c.Args().Get(0))
if err != nil {
return err
}
answerData, err = projectList.GetIndex(index - 1)
answerData, _, err = projectList.GetWithAliasOrIndex(c.Args().Get(0))
if err != nil {
return err
}

} else {
// prompt the user to determine which configs to delete.
answerData, err = projectList.Prompt("Enter drawbridge config number to delete")
answerData, _, err = projectList.Prompt("Enter drawbridge config number to delete")
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/project/factory_test.go
Expand Up @@ -53,7 +53,7 @@ func TestCreateProjectListFromProvidedAnswers(t *testing.T) {
require.NoError(t, err, "should correctly load project list")

actualSortedList := projList.GetAll()
actualFirstAnswer, err := projList.GetIndex(0)
actualFirstAnswer, _, err := projList.GetWithIndex(0)
require.NoError(t, err, "should correctly get item at index")

//assert
Expand Down Expand Up @@ -85,7 +85,7 @@ func TestCreateProjectListFromConfigDir(t *testing.T) {
require.NoError(t, err, "should correctly load project list")

actualSortedList := projList.GetAll()
actualFirstAnswer, err := projList.GetIndex(0)
actualFirstAnswer, _, err := projList.GetWithIndex(0)
require.NoError(t, err, "should correctly get item at index")

//assert
Expand Down

0 comments on commit 4ef69a9

Please sign in to comment.