Skip to content

Commit

Permalink
omg you should always bind your flags in prerun else it overwrites be…
Browse files Browse the repository at this point in the history
…tween commands
  • Loading branch information
cterence committed Jun 11, 2023
1 parent 46c210c commit e81c750
Show file tree
Hide file tree
Showing 12 changed files with 207 additions and 213 deletions.
20 changes: 12 additions & 8 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@ on:
branches: [main]

env:
AWS_REGION: eu-west-3
BINARY_NAME: xit
TS_TAILNET: ${{ secrets.TS_TAILNET }}
TS_API_KEY: ${{ secrets.TS_API_KEY }}
TS_AUTH_KEY: ${{ secrets.TS_AUTH_KEY }}
# xit environment variables
XIT_REGION: eu-west-3
XIT_NON_INTERACTIVE: "true"
XIT_CONNECT: "true"
XIT_SHUTDOWN: 5m
XIT_TS_TAILNET: ${{ secrets.TS_TAILNET }}
XIT_TS_API_KEY: ${{ secrets.TS_API_KEY }}
XIT_TS_AUTH_KEY: ${{ secrets.TS_AUTH_KEY }}

permissions:
id-token: write # This is required for requesting the JWT
Expand All @@ -35,15 +39,15 @@ jobs:
- name: Setup environment
uses: ./.github/actions/setup
with:
region: ${{ env.AWS_REGION }}
region: ${{ env.XIT_REGION }}
role_arn: ${{ secrets.AWS_GITHUB_ACTIONS_XIT_ROLE_ARN }}
tailscale_authkey: ${{ secrets.TS_GITHUB_ACTIONS_AUTH_KEY }}
- name: Download xit
uses: ./.github/actions/download
with:
binary_name: ${{ env.BINARY_NAME }}
# - name: Run "xit run"
# run: ./xit run --region ${{ env.AWS_REGION }} --non-interactive --connect --shutdown 5m
# TODO: check if the public IP address matches the one from the new instance
- name: Run "xit run"
run: ./xit run
- name: Run xit status
run: ./xit status
# TODO: check if the public IP address matches the one from the new instance
23 changes: 23 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [

{
"name": "Launch file",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${file}"
},
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}"
}
]
}
93 changes: 13 additions & 80 deletions cmd/connect.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/
package cmd

import (
"fmt"
"os/exec"
"regexp"
"strings"

"github.com/cterence/xit/common"
"github.com/manifoldco/promptui"
Expand All @@ -25,18 +19,22 @@ var connectCmd = &cobra.Command{
This command will run tailscale up and choose the exit node with the machine name provided.
Example : xit connect xit-eu-west-3-i-048afd4880f66c596`,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlag("ts_api_key", cmd.PersistentFlags().Lookup("ts-api-key"))
viper.BindPFlag("ts_tailnet", cmd.PersistentFlags().Lookup("ts-tailnet"))
viper.BindPFlag("non_interactive", cmd.PersistentFlags().Lookup("non-interactive"))
},
Run: func(cmd *cobra.Command, args []string) {
// Using the CLI on the host, run tailscale up and choose the exit node with the machine name provided
tsApiKey := viper.GetString("ts_api_key")
tailnet := viper.GetString("ts_tailnet")
nonInteractive := viper.GetBool("non_interactive")
// Create ternary operator to choose between sudo and sudo -n

var machineConnect string

if len(args) != 0 {
machineConnect = args[0]
} else {
} else if !nonInteractive {
xitDevices, err := common.FindActiveXitDevices(tsApiKey, tailnet)
if err != nil {
fmt.Println("Failed to find active xit devices:", err)
Expand Down Expand Up @@ -68,76 +66,15 @@ var connectCmd = &cobra.Command{
}

machineConnect = xitDevices[idx].Hostname
} else {
fmt.Println("No machine name provided")
return
}

fmt.Printf("Will run the command:\nsudo tailscale up --exit-node=%s\n", machineConnect)

// Create a confirmation prompt
var result string

// Use promptui for the confirmation prompt
if !nonInteractive {
prompt := promptui.Select{
Label: "Are you sure you want to connect to this machine?",
Items: []string{"yes", "no"},
}

_, result, err := prompt.Run()
if err != nil {
fmt.Println("Failed to read input:", err)
return
}

if result != "yes" {
fmt.Println("Aborting...")
return
}
}

sudoNonInteractive := ""
if nonInteractive {
sudoNonInteractive = "-n"
}

// FIXME: Have correct errors when sudo is not available
// Run the command and parse the output
out, err := exec.Command("sudo", sudoNonInteractive, "tailscale", "up", "--exit-node="+machineConnect).CombinedOutput()
// If the command was unsuccessful, extract tailscale up command from error message with a regex and run it
err := common.RunTailscaleUpCommand("tailscale up --exit-node="+machineConnect, nonInteractive)
if err != nil {
// extract latest "tailscale up" command from output with a regex and run it
regexp := regexp.MustCompile(`tailscale up .*`)
tailscaleUpCommand := regexp.FindString(string(out))

fmt.Printf("\nExisting configuration found, will run updated tailscale up command:\nsudo %s\n\n", tailscaleUpCommand)

// Use promptui for the confirmation prompt
if !nonInteractive {
prompt := promptui.Select{
Label: "Are you sure you want to connect to this machine?",
Items: []string{"yes", "no"},
}

_, result, err = prompt.Run()
if err != nil {
fmt.Println("Failed to read input:", err)
return
}

if result != "yes" {
fmt.Println("Aborting...")
return
}
}

tailscaleUpCommandSplitted := strings.Split(tailscaleUpCommand, " ")
tailscaleUpCommandSplitted = append([]string{sudoNonInteractive}, tailscaleUpCommandSplitted...)

_, err = exec.Command("sudo", tailscaleUpCommandSplitted...).CombinedOutput()
if err != nil {
fmt.Println("Failed to run command:", err)
}

fmt.Println("Connected.")
fmt.Println("Failed to run tailscale up command:", err)
return
}

fmt.Println("Connected.")
Expand All @@ -149,9 +86,5 @@ func init() {

connectCmd.PersistentFlags().StringP("ts-api-key", "", "", "TailScale API Key")
connectCmd.PersistentFlags().StringP("ts-tailnet", "", "", "TailScale Tailnet")
connectCmd.PersistentFlags().BoolP("non-interactive", "n", false, "Do not prompt for confirmation")

viper.BindPFlag("ts_api_key", connectCmd.PersistentFlags().Lookup("ts-api-key"))
viper.BindPFlag("ts_tailnet", connectCmd.PersistentFlags().Lookup("ts-tailnet"))
viper.BindPFlag("non_interactive", runCmd.PersistentFlags().Lookup("non-interactive"))
connectCmd.PersistentFlags().BoolP("non-interactive", "", false, "Do not prompt for confirmation")
}
67 changes: 10 additions & 57 deletions cmd/disconnect.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/
package cmd

import (
"encoding/json"
"fmt"
"os/exec"
"regexp"
"strings"

"github.com/cterence/xit/common"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

// disconnectCmd represents the disconnect command
var disconnectCmd = &cobra.Command{
Use: "disconnect",
Short: "Disconnect from the current exit node",
Long: ``,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlag("non_interactive", cmd.PersistentFlags().Lookup("non-interactive"))
},
Run: func(cmd *cobra.Command, args []string) {
nonInteractive := viper.GetBool("non_interactive")

var status common.TailscaleStatus

out, err := exec.Command("tailscale", "debug", "prefs").CombinedOutput()
Expand All @@ -36,65 +36,18 @@ var disconnectCmd = &cobra.Command{
return
}

command := "tailscale up --exit-node="

// Use promptui for the confirmation prompt
prompt := promptui.Select{
Label: "Are you sure you want to disconnect from this machine?",
Items: []string{"yes", "no"},
}

_, result, err := prompt.Run()
err = common.RunTailscaleUpCommand("tailscale up --exit-node=", nonInteractive)
if err != nil {
fmt.Println("Failed to read input:", err)
fmt.Println("Failed to run tailscale up command:", err)
return
}

if result != "yes" {
fmt.Println("Aborting...")
return
}

// Run the command and parse the output

out, err = exec.Command("sudo", strings.Split(command, " ")...).CombinedOutput()
// If the command was unsuccessful, extract tailscale up command from error message with a regex and run it
if err != nil {
// extract latest "tailscale up" command from output with a regex and run it
regexp := regexp.MustCompile(`tailscale up .*`)
command = regexp.FindString(string(out))

fmt.Printf("\nExisting configuration found, will run updated tailscale up command:\nsudo %s\n", command)

// Use promptui for the confirmation prompt
prompt = promptui.Select{
Label: "Are you sure you want to disconnect from this machine?",
Items: []string{"yes", "no"},
}

_, result, err = prompt.Run()
if err != nil {
fmt.Println("Failed to read input:", err)
return
}

if result != "yes" {
fmt.Println("Aborting...")
return
}

_, err = exec.Command("sudo", strings.Split(command, " ")...).CombinedOutput()
if err != nil {
fmt.Println("Failed to run command:", err)
}

fmt.Println("Disconnected.")
}

fmt.Println("Disconnected.")
},
}

func init() {
rootCmd.AddCommand(disconnectCmd)

disconnectCmd.PersistentFlags().BoolP("non-interactive", "n", false, "Do not prompt for confirmation")
}
13 changes: 0 additions & 13 deletions cmd/docs.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/
package cmd

import (
Expand Down Expand Up @@ -29,14 +26,4 @@ var docsCmd = &cobra.Command{

func init() {
rootCmd.AddCommand(docsCmd)

// Here you will define your flags and configuration settings.

// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// docsCmd.PersistentFlags().String("foo", "", "A help for foo")

// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// docsCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
33 changes: 20 additions & 13 deletions cmd/init.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/
package cmd

import (
Expand All @@ -21,10 +18,17 @@ In details it will:
- add a tag:xit to your policy
- update autoapprovers to allow exit nodes to be created
- add a ssh configuration allowing users to ssh into tagged xit machines`,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlag("ts_api_key", cmd.PersistentFlags().Lookup("ts-api-key"))
viper.BindPFlag("ts_tailnet", cmd.PersistentFlags().Lookup("ts-tailnet"))
viper.BindPFlag("non_interactive", cmd.PersistentFlags().Lookup("non-interactive"))
viper.BindPFlag("dry_run", cmd.PersistentFlags().Lookup("dry-run"))
},
Run: func(cmd *cobra.Command, args []string) {
tsApiKey := viper.GetString("ts_api_key")
tailnet := viper.GetString("ts_tailnet")
dryRun := viper.GetBool("dry_run")
nonInteractive := viper.GetBool("non_interactive")

// Get the policy configuration
policy, err := common.GetPolicy(tsApiKey, tailnet)
Expand Down Expand Up @@ -79,15 +83,19 @@ Add a ssh configuration allowing users to ssh into tagged xit machines
Your new policy document will look like this:
%s
Do you want to continue? [y/N]
`, policyJSON)

var answer string
fmt.Scanln(&answer)
if answer != "y" {
fmt.Println("Aborting")
return
if !nonInteractive {
result, err := common.PromptYesNo("Do you want to continue?")
if err != nil {
fmt.Println("Failed to prompt for confirmation:", err)
return
}

if !result {
fmt.Println("Aborting...")
return
}
}

err = common.UpdatePolicy(tsApiKey, tailnet, policy)
Expand All @@ -106,7 +114,6 @@ func init() {

initCmd.PersistentFlags().StringP("ts-api-key", "", "", "TailScale API Key")
initCmd.PersistentFlags().StringP("ts-tailnet", "", "", "TailScale Tailnet")

viper.BindPFlag("ts_api_key", initCmd.PersistentFlags().Lookup("ts-api-key"))
viper.BindPFlag("ts_tailnet", initCmd.PersistentFlags().Lookup("ts-tailnet"))
initCmd.PersistentFlags().BoolP("non-interactive", "n", false, "Do not prompt for confirmation")
initCmd.PersistentFlags().BoolP("dry-run", "d", false, "Do not actually terminate instances")
}
Loading

0 comments on commit e81c750

Please sign in to comment.