Skip to content

Commit

Permalink
Add webspace ports commands
Browse files Browse the repository at this point in the history
  • Loading branch information
devplayer0 committed Oct 5, 2020
1 parent b608ace commit 25c8d70
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 2 deletions.
107 changes: 107 additions & 0 deletions pkg/cmd/webspace/ports.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package webspace

import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"strings"
"text/template"

"github.com/spf13/cobra"
"gopkg.in/yaml.v2"

"github.com/netsoc/cli/pkg/config"
"github.com/netsoc/cli/pkg/util"
webspaced "github.com/netsoc/webspaced/client"
)

type portsOptions struct {
Config func() (*config.Config, error)
WebspacedClient func() (*webspaced.APIClient, error)

OutputFormat string
User string
}

// NewCmdPorts creates a new webspace ports command
func NewCmdPorts(f *util.CmdFactory) *cobra.Command {
opts := portsOptions{
Config: f.Config,
WebspacedClient: f.WebspacedClient,
}
cmd := &cobra.Command{
Use: "ports",
Short: "Configure webspace port forwards",
RunE: func(cmd *cobra.Command, args []string) error {
return portsRun(opts)
},
}

cmd.Flags().StringVarP(&opts.OutputFormat, "output", "o", "text", "output format `text|yaml|json|template=<Go template>`")
util.AddOptUser(cmd, &opts.User)

cmd.AddCommand(NewCmdPortsAdd(f), NewCmdPortsRemove(f))

return cmd
}

func printPorts(ports map[string]int32, outputType string) error {
if strings.HasPrefix(outputType, "template=") {
tpl, err := template.New("anonymous").Parse(strings.TrimPrefix(outputType, "template="))
if err != nil {
return fmt.Errorf("failed to parse template: %w", err)
}

if err := tpl.Execute(os.Stdout, ports); err != nil {
return fmt.Errorf("failed to execute template: %w", err)
}

return nil
}

switch outputType {
case "json":
if err := json.NewEncoder(os.Stdout).Encode(ports); err != nil {
return fmt.Errorf("failed to encode JSON: %w", err)
}
case "yaml":
if err := yaml.NewEncoder(os.Stdout).Encode(ports); err != nil {
return fmt.Errorf("failed to encode YAML: %w", err)
}
case "text":
fmt.Println("Webspace port forwards:")
for i, e := range ports {
fmt.Printf(" - %v -> %v\n", i, e)
}
default:
return fmt.Errorf(`unknown output format "%v"`, outputType)
}

return nil
}

func portsRun(opts portsOptions) error {
c, err := opts.Config()
if err != nil {
return err
}

if c.Token == "" {
return errors.New("not logged in")
}

client, err := opts.WebspacedClient()
if err != nil {
return err
}
ctx := context.WithValue(context.Background(), webspaced.ContextAccessToken, c.Token)

ports, _, err := client.PortsApi.GetPorts(ctx, opts.User)
if err != nil {
return util.APIError(err)
}

return printPorts(ports, opts.OutputFormat)
}
89 changes: 89 additions & 0 deletions pkg/cmd/webspace/ports_add.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package webspace

import (
"context"
"errors"
"fmt"
"strconv"

"github.com/spf13/cobra"

"github.com/netsoc/cli/pkg/config"
"github.com/netsoc/cli/pkg/util"
webspaced "github.com/netsoc/webspaced/client"
)

type portsAddOptions struct {
Config func() (*config.Config, error)
WebspacedClient func() (*webspaced.APIClient, error)

User string
ExternalPort uint16
InternalPort uint16
}

// NewCmdPortsAdd creates a new webspace ports add command
func NewCmdPortsAdd(f *util.CmdFactory) *cobra.Command {
opts := portsAddOptions{
Config: f.Config,
WebspacedClient: f.WebspacedClient,
}

cmd := &cobra.Command{
Use: "add <internal port>",
Short: "Add port forward",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
p, err := strconv.ParseUint(args[0], 10, 16)
if err != nil {
return fmt.Errorf("failed to parse internal port: %w", err)
}
opts.InternalPort = uint16(p)

return portsAddRun(opts)
},
}

util.AddOptUser(cmd, &opts.User)
cmd.Flags().Uint16VarP(&opts.ExternalPort, "external-port", "p", 0, "external port (0 means random)")

return cmd
}

func portsAddRun(opts portsAddOptions) error {
c, err := opts.Config()
if err != nil {
return err
}

if c.Token == "" {
return errors.New("not logged in")
}

client, err := opts.WebspacedClient()
if err != nil {
return err
}
ctx := context.WithValue(context.Background(), webspaced.ContextAccessToken, c.Token)

if opts.ExternalPort == 0 {
i, _, err := client.PortsApi.AddRandomPort(ctx, opts.User, int32(opts.InternalPort))
if err != nil {
return util.APIError(err)
}

opts.ExternalPort = uint16(i.EPort)
} else {
if _, err := client.PortsApi.AddPort(ctx, opts.User, int32(opts.ExternalPort), int32(opts.InternalPort)); err != nil {
return util.APIError(err)
}
}

if util.IsInteractive() {
fmt.Printf("Port %v in webspace is now accessible externally via port %v\n", opts.InternalPort, opts.ExternalPort)
} else {
fmt.Println(opts.ExternalPort)
}

return nil
}
75 changes: 75 additions & 0 deletions pkg/cmd/webspace/ports_remove.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package webspace

import (
"context"
"errors"
"fmt"
"log"
"strconv"

"github.com/spf13/cobra"

"github.com/netsoc/cli/pkg/config"
"github.com/netsoc/cli/pkg/util"
webspaced "github.com/netsoc/webspaced/client"
)

type portsRemoveOptions struct {
Config func() (*config.Config, error)
WebspacedClient func() (*webspaced.APIClient, error)

User string
Port uint16
}

// NewCmdPortsRemove creates a new webspace ports remove command
func NewCmdPortsRemove(f *util.CmdFactory) *cobra.Command {
opts := portsRemoveOptions{
Config: f.Config,
WebspacedClient: f.WebspacedClient,
}

cmd := &cobra.Command{
Use: "remove <external port>",
Aliases: []string{"delete"},
Short: "Remove port forward",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
p, err := strconv.ParseUint(args[0], 10, 16)
if err != nil {
return fmt.Errorf("failed to parse port: %w", err)
}
opts.Port = uint16(p)

return portsRemoveRun(opts)
},
}

util.AddOptUser(cmd, &opts.User)

return cmd
}

func portsRemoveRun(opts portsRemoveOptions) error {
c, err := opts.Config()
if err != nil {
return err
}

if c.Token == "" {
return errors.New("not logged in")
}

client, err := opts.WebspacedClient()
if err != nil {
return err
}
ctx := context.WithValue(context.Background(), webspaced.ContextAccessToken, c.Token)

if _, err := client.PortsApi.RemovePort(ctx, opts.User, int32(opts.Port)); err != nil {
return util.APIError(err)
}

log.Print("Removed successfully")
return nil
}
2 changes: 2 additions & 0 deletions pkg/cmd/webspace/webspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ func NewCmdWebspace(f *util.CmdFactory) *cobra.Command {
cmd.AddCommand(NewCmdStatus(f), NewCmdStart(f), NewCmdSync(f), NewCmdReboot(f), NewCmdStop(f))
// domains
cmd.AddCommand(NewCmdDomains(f))
// ports
cmd.AddCommand(NewCmdPorts(f))
// console
cmd.AddCommand(NewCmdLog(f))

Expand Down
9 changes: 7 additions & 2 deletions pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,14 @@ func APIError(err error) error {
return err
}

// IsInteractive returns true if the CLI is being run interactively
func IsInteractive() bool {
return isatty.IsTerminal(os.Stdin.Fd())
}

// ReadPassword reads a password from stdin
func ReadPassword(confirm bool) (string, error) {
if !isatty.IsTerminal(os.Stdin.Fd()) {
if !IsInteractive() {
r := bufio.NewReader(os.Stdin)

p := make([]byte, 1024)
Expand Down Expand Up @@ -274,7 +279,7 @@ func CheckUpdate() (string, error) {

// SimpleProgress renders a simple progress
func SimpleProgress(message string, eta time.Duration) (func(), progress.Writer, *progress.Tracker) {
if !isatty.IsTerminal(os.Stdin.Fd()) {
if !IsInteractive() {
return func() {}, progress.NewWriter(), &progress.Tracker{}
}

Expand Down

0 comments on commit 25c8d70

Please sign in to comment.