Skip to content

Commit afe462b

Browse files
authored
Add cluster port command (#371)
* Add cluster port command * validate that a protocol is passed
1 parent b85d806 commit afe462b

File tree

12 files changed

+314
-0
lines changed

12 files changed

+314
-0
lines changed

cli/cmd/cluster_port.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package cmd
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
)
6+
7+
func (r *runners) InitClusterPort(parent *cobra.Command) *cobra.Command {
8+
cmd := &cobra.Command{
9+
Use: "port",
10+
SilenceUsage: true,
11+
Hidden: true, // this feature is not fully implemented and controlled behind a feature toggle in the api until ready
12+
}
13+
parent.AddCommand(cmd)
14+
15+
return cmd
16+
}

cli/cmd/cluster_port_expose.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package cmd
2+
3+
import (
4+
"github.com/pkg/errors"
5+
"github.com/replicatedhq/replicated/cli/print"
6+
"github.com/spf13/cobra"
7+
)
8+
9+
func (r *runners) InitClusterPortExpose(parent *cobra.Command) *cobra.Command {
10+
cmd := &cobra.Command{
11+
Use: "expose <cluster-id> --port <port> --protocol <protocol>",
12+
RunE: r.clusterPortExpose,
13+
Args: cobra.ExactArgs(1),
14+
}
15+
parent.AddCommand(cmd)
16+
17+
cmd.Flags().IntVar(&r.args.clusterExposePortPort, "port", 0, "Port to expose")
18+
cmd.MarkFlagRequired("port")
19+
20+
cmd.Flags().StringArrayVar(&r.args.clusterExposePortProtocols, "protocol", []string{"http", "https"}, "Protocol to expose")
21+
cmd.MarkFlagRequired("protocol")
22+
23+
return cmd
24+
}
25+
26+
func (r *runners) clusterPortExpose(_ *cobra.Command, args []string) error {
27+
clusterID := args[0]
28+
29+
if len(r.args.clusterExposePortProtocols) == 0 {
30+
return errors.New("at least one protocol must be specified")
31+
}
32+
33+
port, err := r.kotsAPI.ExposeClusterPort(clusterID, r.args.clusterExposePortPort, r.args.clusterExposePortProtocols)
34+
if err != nil {
35+
return err
36+
}
37+
38+
if err := print.ClusterPort(r.outputFormat, r.w, port); err != nil {
39+
return err
40+
}
41+
42+
return nil
43+
}

cli/cmd/cluster_port_ls.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package cmd
2+
3+
import (
4+
"github.com/replicatedhq/replicated/cli/print"
5+
"github.com/spf13/cobra"
6+
)
7+
8+
func (r *runners) InitClusterPortLs(parent *cobra.Command) *cobra.Command {
9+
cmd := &cobra.Command{
10+
Use: "ls",
11+
RunE: r.clusterPortList,
12+
Args: cobra.ExactArgs(1),
13+
}
14+
parent.AddCommand(cmd)
15+
16+
return cmd
17+
}
18+
19+
func (r *runners) clusterPortList(_ *cobra.Command, args []string) error {
20+
clusterID := args[0]
21+
22+
ports, err := r.kotsAPI.ListClusterPorts(clusterID)
23+
if err != nil {
24+
return err
25+
}
26+
27+
return print.ClusterPorts(r.outputFormat, r.w, ports, true)
28+
}

cli/cmd/cluster_port_rm.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package cmd
2+
3+
import (
4+
"github.com/pkg/errors"
5+
"github.com/replicatedhq/replicated/cli/print"
6+
"github.com/spf13/cobra"
7+
)
8+
9+
func (r *runners) InitClusterPortRm(parent *cobra.Command) *cobra.Command {
10+
cmd := &cobra.Command{
11+
Use: "rm",
12+
RunE: r.clusterPortRemove,
13+
Args: cobra.ExactArgs(1),
14+
}
15+
parent.AddCommand(cmd)
16+
17+
cmd.Flags().IntVar(&r.args.clusterPortRemovePort, "port", 0, "Port to remove")
18+
cmd.Flags().StringArrayVar(&r.args.clusterPortRemoveProtocols, "protocol", []string{"http"}, "Protocol to remove")
19+
20+
return cmd
21+
}
22+
23+
func (r *runners) clusterPortRemove(_ *cobra.Command, args []string) error {
24+
clusterID := args[0]
25+
26+
if len(r.args.clusterPortRemoveProtocols) == 0 {
27+
return errors.New("at least one protocol must be specified")
28+
}
29+
30+
ports, err := r.kotsAPI.RemoveClusterPort(clusterID, r.args.clusterPortRemovePort, r.args.clusterPortRemoveProtocols)
31+
if err != nil {
32+
return err
33+
}
34+
35+
print.ClusterPorts(r.outputFormat, r.w, ports, true)
36+
return nil
37+
}

cli/cmd/root.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,11 @@ func Execute(rootCmd *cobra.Command, stdin io.Reader, stdout io.Writer, stderr i
231231
clusterAddOnIngressCmd := runCmds.InitClusterAddOnIngress(clusterAddOnCmd)
232232
runCmds.InitClusterAddOnIngressCreate(clusterAddOnIngressCmd)
233233

234+
clusterPortCmd := runCmds.InitClusterPort(clusterCmd)
235+
runCmds.InitClusterPortLs(clusterPortCmd)
236+
runCmds.InitClusterPortExpose(clusterPortCmd)
237+
runCmds.InitClusterPortRm(clusterPortCmd)
238+
234239
clusterPrepareCmd := runCmds.InitClusterPrepare(clusterCmd)
235240

236241
clusterUpdateCmd := runCmds.InitClusterUpdateCommand(clusterCmd)

cli/cmd/runner.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,12 @@ type runnerArgs struct {
235235
clusterCreateIngressPort int
236236
clusterCreateIngressNamespace string
237237

238+
clusterExposePortPort int
239+
clusterExposePortProtocols []string
240+
241+
clusterPortRemovePort int
242+
clusterPortRemoveProtocols []string
243+
238244
loginEndpoint string
239245

240246
apiPostBody string

cli/print/cluster_ports.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package print
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"os"
7+
"text/tabwriter"
8+
"text/template"
9+
10+
"github.com/replicatedhq/replicated/pkg/types"
11+
)
12+
13+
var portsTmplHeaderSrc = `CLUSTER PORT PROTOCOL EXPOSED PORT STATUS`
14+
var portsTmplRowSrc = `{{- range . }}
15+
{{- $upstreamPort := .UpstreamPort }}
16+
{{- $hostname := .Hostname }}
17+
{{- $state := .State }}
18+
{{- range .ExposedPorts }}
19+
{{ $upstreamPort }} {{ .Protocol }} {{ formatURL .Protocol $hostname }} {{ printf "%-12s" $state }}
20+
{{ end }}
21+
{{ end }}`
22+
var portsTmplSrc = fmt.Sprintln(portsTmplHeaderSrc) + portsTmplRowSrc
23+
var portsTmpl = template.Must(template.New("ports").Funcs(funcs).Parse(portsTmplSrc))
24+
var portsTmplNoHeader = template.Must(template.New("ports").Funcs(funcs).Parse(portsTmplRowSrc))
25+
26+
const (
27+
clusterPortsMinWidth = 16
28+
clusterPortsTabWidth = 8
29+
clusterPortsPadding = 4
30+
clusterPortsPadChar = ' '
31+
)
32+
33+
func ClusterPorts(outputFormat string, w *tabwriter.Writer, ports []*types.ClusterPort, header bool) error {
34+
// we need a custom tab writer here because our column widths are large
35+
portsWriter := tabwriter.NewWriter(os.Stdout, clusterPortsMinWidth, clusterPortsTabWidth, clusterPortsPadding, clusterPortsPadChar, tabwriter.TabIndent)
36+
37+
switch outputFormat {
38+
case "table":
39+
if header {
40+
if err := portsTmpl.Execute(portsWriter, ports); err != nil {
41+
return err
42+
}
43+
} else {
44+
if err := portsTmplNoHeader.Execute(portsWriter, ports); err != nil {
45+
return err
46+
}
47+
}
48+
case "json":
49+
cAsByte, err := json.MarshalIndent(ports, "", " ")
50+
if err != nil {
51+
return err
52+
}
53+
if _, err := fmt.Fprintln(portsWriter, string(cAsByte)); err != nil {
54+
return err
55+
}
56+
default:
57+
return fmt.Errorf("unsupported output format: %s", outputFormat)
58+
}
59+
return w.Flush()
60+
}
61+
62+
func ClusterPort(outputFormat string, w *tabwriter.Writer, port *types.ClusterPort) error {
63+
// we need a custom tab writer here because our column widths are large
64+
portsWriter := tabwriter.NewWriter(os.Stdout, clusterPortsMinWidth, clusterPortsTabWidth, clusterPortsPadding, clusterPortsPadChar, tabwriter.TabIndent)
65+
66+
switch outputFormat {
67+
case "table":
68+
if err := portsTmpl.Execute(portsWriter, []*types.ClusterPort{port}); err != nil {
69+
return err
70+
}
71+
case "json":
72+
cAsByte, err := json.MarshalIndent(port, "", " ")
73+
if err != nil {
74+
return err
75+
}
76+
if _, err := fmt.Fprintln(portsWriter, string(cAsByte)); err != nil {
77+
return err
78+
}
79+
default:
80+
return fmt.Errorf("unsupported output format: %s", outputFormat)
81+
}
82+
return w.Flush()
83+
}

cli/print/util.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package print
22

33
import (
4+
"fmt"
45
"text/template"
56
"time"
67
)
@@ -19,4 +20,7 @@ var funcs = template.FuncMap{
1920
"add": func(a, b int) int {
2021
return a + b
2122
},
23+
"formatURL": func(protocol, hostname string) string {
24+
return fmt.Sprintf("%s://%s", protocol, hostname)
25+
},
2226
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package kotsclient
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
7+
"github.com/replicatedhq/replicated/pkg/types"
8+
)
9+
10+
type ExportClusterPortRequest struct {
11+
Port int `json:"port"`
12+
Protocols []string `json:"protocols"`
13+
}
14+
15+
type ExposeClusterPortResponse struct {
16+
Port *types.ClusterPort `json:"port"`
17+
}
18+
19+
func (c *VendorV3Client) ExposeClusterPort(clusterID string, portNumber int, protocols []string) (*types.ClusterPort, error) {
20+
req := ExportClusterPortRequest{
21+
Port: portNumber,
22+
Protocols: protocols,
23+
}
24+
25+
resp := ExposeClusterPortResponse{}
26+
err := c.DoJSON("POST", fmt.Sprintf("/v3/cluster/%s/port", clusterID), http.StatusCreated, req, &resp)
27+
if err != nil {
28+
return nil, err
29+
}
30+
31+
return resp.Port, nil
32+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package kotsclient
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
7+
"github.com/replicatedhq/replicated/pkg/types"
8+
)
9+
10+
type ListClusterPortsResponse struct {
11+
Ports []*types.ClusterPort `json:"ports"`
12+
}
13+
14+
func (c *VendorV3Client) ListClusterPorts(clusterID string) ([]*types.ClusterPort, error) {
15+
resp := ListClusterPortsResponse{}
16+
err := c.DoJSON("GET", fmt.Sprintf("/v3/cluster/%s/ports", clusterID), http.StatusOK, nil, &resp)
17+
if err != nil {
18+
return nil, err
19+
}
20+
21+
return resp.Ports, nil
22+
}

0 commit comments

Comments
 (0)