Skip to content

Commit

Permalink
w
Browse files Browse the repository at this point in the history
  • Loading branch information
jdewinne committed Nov 15, 2024
1 parent 5957df4 commit 7dacf48
Show file tree
Hide file tree
Showing 12 changed files with 384 additions and 1 deletion.
5 changes: 5 additions & 0 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,11 @@ func Execute(rootCmd *cobra.Command, stdin io.Reader, stdout io.Writer, stderr i
vmUpdateCmd := runCmds.InitVMUpdateCommand(vmCmd)
runCmds.InitVMUpdateTTL(vmUpdateCmd)

vmPortCmd := runCmds.InitVMPort(vmCmd)
runCmds.InitVMPortLs(vmPortCmd)
runCmds.InitVMPortExpose(vmPortCmd)
runCmds.InitVMPortRm(vmPortCmd)

networkCmd := runCmds.InitNetworkCommand(runCmds.rootCmd)
runCmds.InitNetworkCreate(networkCmd)
runCmds.InitNetworkList(networkCmd)
Expand Down
5 changes: 5 additions & 0 deletions cli/cmd/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,4 +238,9 @@ type runnerArgs struct {

updateVMName string
updateVMID string

vmExposePortPort int
vmExposePortProtocols []string
vmExposePortIsWildcard bool
vmPortRemoveAddonID string
}
28 changes: 28 additions & 0 deletions cli/cmd/vm_port.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package cmd

import (
"github.com/spf13/cobra"
)

func (r *runners) InitVMPort(parent *cobra.Command) *cobra.Command {
cmd := &cobra.Command{
Use: "port",
Short: "Manage VM ports.",
Long: `The 'vm port' command is a parent command for managing ports in a vm. It allows users to list, remove, or expose specific ports used by the vm. Use the subcommands (such as 'ls', 'rm', and 'expose') to manage port configurations effectively.
This command provides flexibility for handling ports in various test vms, ensuring efficient management of vm networking settings.`,
Example: ` # List all exposed ports in a vm
replicated vm port ls [VM_ID]
# Remove an exposed port from a vm
replicated vm port rm [VM_ID] [PORT]
# Expose a new port in a vm
replicated vm port expose [VM_ID] [PORT]`,
SilenceUsage: true,
Hidden: false,
}
parent.AddCommand(cmd)

return cmd
}
63 changes: 63 additions & 0 deletions cli/cmd/vm_port_expose.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package cmd

import (
"github.com/pkg/errors"
"github.com/replicatedhq/replicated/cli/print"
"github.com/spf13/cobra"
)

func (r *runners) InitVMPortExpose(parent *cobra.Command) *cobra.Command {
cmd := &cobra.Command{
Use: "expose VM_ID --port PORT",
Short: "Expose a port on a vm to the public internet.",
Long: `The 'vm port expose' command is used to expose a specified port on a vm to the public internet. When exposing a port, the command automatically creates a DNS entry and, if using the "https" protocol, provisions a TLS certificate for secure communication.
You can also create a wildcard DNS entry and TLS certificate by specifying the "--wildcard" flag. Please note that creating a wildcard certificate may take additional time.
This command supports different protocols including "http", "https", "ws", and "wss" for web traffic and web socket communication.`,
Example: ` # Expose port 8080 with HTTPS protocol and wildcard DNS
replicated vm port expose VM_ID --port 8080 --protocol https --wildcard
# Expose port 3000 with HTTP protocol
replicated vm port expose VM_ID --port 3000 --protocol http
# Expose port 8080 with multiple protocols
replicated vm port expose VM_ID --port 8080 --protocol http,https
# Expose port 8080 and display the result in JSON format
replicated vm port expose VM_ID --port 8080 --protocol https --output json`,
RunE: r.vmPortExpose,
Args: cobra.ExactArgs(1),
ValidArgsFunction: r.completeVMIDs,
}
parent.AddCommand(cmd)

cmd.Flags().IntVar(&r.args.vmExposePortPort, "port", 0, "Port to expose (required)")
err := cmd.MarkFlagRequired("port")
if err != nil {
panic(err)
}
cmd.Flags().StringSliceVar(&r.args.vmExposePortProtocols, "protocol", []string{"http", "https"}, `Protocol to expose (valid values are "http", "https", "ws" and "wss")`)
cmd.Flags().BoolVar(&r.args.vmExposePortIsWildcard, "wildcard", false, "Create a wildcard DNS entry and TLS certificate for this port")
cmd.Flags().StringVar(&r.outputFormat, "output", "table", "The output format to use. One of: json|table|wide (default: table)")

return cmd
}

func (r *runners) vmPortExpose(_ *cobra.Command, args []string) error {
vmID := args[0]

if len(r.args.vmExposePortProtocols) == 0 {
return errors.New("at least one protocol must be specified")
}

port, err := r.kotsAPI.ExposeVMPort(
vmID,
r.args.vmExposePortPort, r.args.vmExposePortProtocols, r.args.vmExposePortIsWildcard,
)
if err != nil {
return err
}

return print.VMPort(r.outputFormat, r.w, port)
}
43 changes: 43 additions & 0 deletions cli/cmd/vm_port_ls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package cmd

import (
"github.com/replicatedhq/replicated/cli/print"
"github.com/spf13/cobra"
)

func (r *runners) InitVMPortLs(parent *cobra.Command) *cobra.Command {
cmd := &cobra.Command{
Use: "ls VM_ID",
Short: "List vm ports for a vm.",
Long: `The 'vm port ls' command lists all the ports configured for a specific vm. You must provide the vm ID to retrieve and display the ports.
This command is useful for viewing the current port configurations, protocols, and other related settings of your test vm. The output format can be customized to suit your needs, and the available formats include table, JSON, and wide views.`,
Example: ` # List ports for a vm in the default table format
replicated vm port ls VM_ID
# List ports for a vm in JSON format
replicated vm port ls VM_ID --output json
# List ports for a vm in wide format
replicated vm port ls VM_ID --output wide`,
RunE: r.vmPortList,
Args: cobra.ExactArgs(1),
ValidArgsFunction: r.completeVMIDs,
}
parent.AddCommand(cmd)

cmd.Flags().StringVar(&r.outputFormat, "output", "table", "The output format to use. One of: json|table|wide (default: table)")

return cmd
}

func (r *runners) vmPortList(_ *cobra.Command, args []string) error {
vmID := args[0]

ports, err := r.kotsAPI.ListVMPorts(vmID)
if err != nil {
return err
}

return print.VMPorts(r.outputFormat, r.w, ports, true)
}
51 changes: 51 additions & 0 deletions cli/cmd/vm_port_rm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package cmd

import (
"github.com/replicatedhq/replicated/cli/print"
"github.com/spf13/cobra"
)

func (r *runners) InitVMPortRm(parent *cobra.Command) *cobra.Command {
cmd := &cobra.Command{
Use: "rm VM_ID --id PORT_ID",
Short: "Remove vm port by ID.",
Long: `The 'vm port rm' command removes a specific port from a vm. You must provide the ID of the port to remove.
This command is useful for managing the network settings of your test vms by allowing you to clean up unused or incorrect ports. After removing a port, the updated list of ports will be displayed.`,
Example: ` # Remove a port using its ID
replicated vm port rm VM_ID --id PORT_ID
# Remove a port and display the result in JSON format
replicated vm port rm VM_ID --id PORT_ID --output json`,
RunE: r.vmPortRemove,
Args: cobra.ExactArgs(1),
ValidArgsFunction: r.completeVMIDs,
}
parent.AddCommand(cmd)

cmd.Flags().StringVar(&r.args.vmPortRemoveAddonID, "id", "", "ID of the port to remove (required)")
err := cmd.MarkFlagRequired("id")
if err != nil {
panic(err)
}
cmd.Flags().StringVar(&r.outputFormat, "output", "table", "The output format to use. One of: json|table|wide (default: table)")

return cmd
}

func (r *runners) vmPortRemove(_ *cobra.Command, args []string) error {
vmID := args[0]

err := r.kotsAPI.DeleteVMAddon(vmID, r.args.vmPortRemoveAddonID)
if err != nil {
return err
}

ports, err := r.kotsAPI.ListVMPorts(vmID)
if err != nil {
return err
}

return print.VMPorts(r.outputFormat, r.w, ports, true)

}
85 changes: 85 additions & 0 deletions cli/print/vm_ports.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package print

import (
"encoding/json"
"fmt"
"os"
"text/tabwriter"
"text/template"

"github.com/replicatedhq/replicated/pkg/types"
)

var vmPortsTmplHeaderSrc = `ID VM PORT PROTOCOL EXPOSED PORT WILDCARD STATUS`
var vmPortsTmplRowSrc = `{{- range . }}
{{- $id := .AddonID }}
{{- $upstreamPort := .UpstreamPort }}
{{- $hostname := .Hostname }}
{{- $isWildcard := .IsWildcard }}
{{- $state := .State }}
{{- range .ExposedPorts }}
{{ $id }} {{ $upstreamPort }} {{ .Protocol }} {{ formatURL .Protocol $hostname }} {{ $isWildcard }} {{ printf "%-12s" $state }}
{{ end }}
{{ end }}`
var vmPortsTmplSrc = fmt.Sprintln(vmPortsTmplHeaderSrc) + vmPortsTmplRowSrc
var vmPortsTmpl = template.Must(template.New("ports").Funcs(funcs).Parse(vmPortsTmplSrc))
var vmPortsTmplNoHeader = template.Must(template.New("ports").Funcs(funcs).Parse(vmPortsTmplRowSrc))

const (
vmPortsMinWidth = 16
vmPortsTabWidth = 8
vmPortsPadding = 4
vmPortsPadChar = ' '
)

func VMPorts(outputFormat string, w *tabwriter.Writer, ports []*types.VMPort, header bool) error {
// we need a custom tab writer here because our column widths are large
portsWriter := tabwriter.NewWriter(os.Stdout, vmPortsMinWidth, vmPortsTabWidth, vmPortsPadding, vmPortsPadChar, tabwriter.TabIndent)

switch outputFormat {
case "table", "wide":
if header {
if err := vmPortsTmpl.Execute(portsWriter, ports); err != nil {
return err
}
} else {
if err := vmPortsTmplNoHeader.Execute(portsWriter, ports); err != nil {
return err
}
}
case "json":
cAsByte, err := json.MarshalIndent(ports, "", " ")
if err != nil {
return err
}
if _, err := fmt.Fprintln(portsWriter, string(cAsByte)); err != nil {
return err
}
default:
return fmt.Errorf("unsupported output format: %s", outputFormat)
}
return w.Flush()
}

func VMPort(outputFormat string, w *tabwriter.Writer, port *types.VMPort) error {
// we need a custom tab writer here because our column widths are large
portsWriter := tabwriter.NewWriter(os.Stdout, vmPortsMinWidth, vmPortsTabWidth, vmPortsPadding, vmPortsPadChar, tabwriter.TabIndent)

switch outputFormat {
case "table":
if err := vmPortsTmpl.Execute(portsWriter, []*types.VMPort{port}); err != nil {
return err
}
case "json":
cAsByte, err := json.MarshalIndent(port, "", " ")
if err != nil {
return err
}
if _, err := fmt.Fprintln(portsWriter, string(cAsByte)); err != nil {
return err
}
default:
return fmt.Errorf("unsupported output format: %s", outputFormat)
}
return w.Flush()
}
2 changes: 1 addition & 1 deletion pkg/kotsclient/cluster_port_expose.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type ExposeClusterPortResponse struct {
}

func (c *VendorV3Client) ExposeClusterPort(clusterID string, portNumber int, protocols []string, isWildcard bool) (*types.ClusterPort, error) {
req := ExportClusterPortRequest{
req := ExportVMPortRequest{
Port: portNumber,
Protocols: protocols,
IsWildcard: isWildcard,
Expand Down
17 changes: 17 additions & 0 deletions pkg/kotsclient/vm_addon_rm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package kotsclient

import (
"context"
"fmt"
"net/http"
)

func (c *VendorV3Client) DeleteVMAddon(vmID, addonID string) error {
endpoint := fmt.Sprintf("/v3/vm/%s/addons/%s", vmID, addonID)
err := c.DoJSON(context.TODO(), "DELETE", endpoint, http.StatusNoContent, nil, nil)
if err != nil {
return err
}

return nil
}
35 changes: 35 additions & 0 deletions pkg/kotsclient/vm_port_expose.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package kotsclient

import (
"context"
"fmt"
"net/http"

"github.com/replicatedhq/replicated/pkg/types"
)

type ExportVMPortRequest struct {
Port int `json:"port"`
Protocols []string `json:"protocols"`
IsWildcard bool `json:"is_wildcard"`
}

type ExposeVMPortResponse struct {
Port *types.VMPort `json:"port"`
}

func (c *VendorV3Client) ExposeVMPort(vmID string, portNumber int, protocols []string, isWildcard bool) (*types.VMPort, error) {
req := ExportVMPortRequest{
Port: portNumber,
Protocols: protocols,
IsWildcard: isWildcard,
}

resp := ExposeVMPortResponse{}
err := c.DoJSON(context.TODO(), "POST", fmt.Sprintf("/v3/vm/%s/port", vmID), http.StatusCreated, req, &resp)
if err != nil {
return nil, err
}

return resp.Port, nil
}
23 changes: 23 additions & 0 deletions pkg/kotsclient/vm_port_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package kotsclient

import (
"context"
"fmt"
"net/http"

"github.com/replicatedhq/replicated/pkg/types"
)

type ListVMPortsResponse struct {
Ports []*types.VMPort `json:"ports"`
}

func (c *VendorV3Client) ListVMPorts(vmID string) ([]*types.VMPort, error) {
resp := ListVMPortsResponse{}
err := c.DoJSON(context.TODO(), "GET", fmt.Sprintf("/v3/vm/%s/ports", vmID), http.StatusOK, nil, &resp)
if err != nil {
return nil, err
}

return resp.Ports, nil
}
Loading

0 comments on commit 7dacf48

Please sign in to comment.