Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions internal/command/local/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package local

import (
"context"
"errors"
"fmt"
"io"
"log/slog"
Expand All @@ -27,15 +28,15 @@ func newProxy(localConfig *config.Local) *cobra.Command {
Use: "proxy [--sandbox SANDBOX|--routegroup ROUTEGROUP|--cluster CLUSTER] --map <target-protocol>://<target-addr>@<bind-addr> [--map <target-protocol>://<target-addr>@<bind-addr>]",
Short: "Proxy connections based on the specified mappings",
RunE: func(cmd *cobra.Command, args []string) error {
return runProxy(cmd, cmd.OutOrStdout(), cfg, args)
return runProxy(cmd.OutOrStdout(), cfg)
},
}
cfg.AddFlags(cmd)

return cmd
}

func runProxy(cmd *cobra.Command, out io.Writer, cfg *config.LocalProxy, args []string) error {
func runProxy(out io.Writer, cfg *config.LocalProxy) error {
ctx := context.Background()

if err := cfg.InitLocalProxyConfig(); err != nil {
Expand All @@ -48,7 +49,8 @@ func runProxy(cmd *cobra.Command, out io.Writer, cfg *config.LocalProxy, args []
// define the cluster and routing key to use
var cluster, routingKey string

if cfg.Sandbox != "" {
switch {
case cfg.Sandbox != "":
// resolve the sandbox
params := sandboxes.NewGetSandboxParams().
WithOrgName(cfg.Org).WithSandboxName(cfg.Sandbox)
Expand All @@ -59,7 +61,8 @@ func runProxy(cmd *cobra.Command, out io.Writer, cfg *config.LocalProxy, args []

cluster = *resp.Payload.Spec.Cluster
routingKey = resp.Payload.RoutingKey
} else if cfg.RouteGroup != "" {

case cfg.RouteGroup != "":
// resolve the routegroup
params := routegroups.NewGetRoutegroupParams().
WithOrgName(cfg.Org).WithRoutegroupName(cfg.RouteGroup)
Expand All @@ -70,7 +73,21 @@ func runProxy(cmd *cobra.Command, out io.Writer, cfg *config.LocalProxy, args []

cluster = resp.Payload.Spec.Cluster
routingKey = resp.Payload.RoutingKey
} else {
if cluster == "" {
// this is a multi-cluster RG, the cluster must be explicitly defined
if cfg.Cluster == "" {
return errors.New("--cluster must be specified in multi-cluster route groups")
}
// validate the cluster
params := clusters.NewGetClusterParams().
WithOrgName(cfg.Org).WithClusterName(cfg.Cluster)
if _, err := cfg.Client.Cluster.GetCluster(params, nil); err != nil {
return err
}
cluster = cfg.Cluster
}

default:
// validate the cluster
params := clusters.NewGetClusterParams().
WithOrgName(cfg.Org).WithClusterName(cfg.Cluster)
Expand Down
11 changes: 8 additions & 3 deletions internal/command/routegroup/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,13 @@ func apply(cfg *config.RouteGroupApply, out, log io.Writer, args []string) error
}
resp := result.Payload

fmt.Fprintf(log, "Created routegroup %q (routing key: %s) in cluster %q.\n\n",
req.Name, resp.RoutingKey, req.Spec.Cluster)
if req.Spec.Cluster != "" {
fmt.Fprintf(log, "Created routegroup %q (routing key: %s) in cluster %q.\n\n",
req.Name, resp.RoutingKey, req.Spec.Cluster)
} else {
fmt.Fprintf(log, "Created multi-cluster routegroup %q (routing key: %s).\n\n",
req.Name, resp.RoutingKey)
}

if cfg.Wait {
// Wait for the routegroup to be ready.
Expand All @@ -79,7 +84,7 @@ func writeOutput(cfg *config.RouteGroupApply, out io.Writer, resp *models.RouteG
fmt.Fprintf(out, "\nDashboard page: %v\n\n", sbURL)

if len(resp.Endpoints) > 0 {
if err := printEndpointTable(out, resp.Endpoints); err != nil {
if err := printEndpointTable(out, resp); err != nil {
return err
}
}
Expand Down
55 changes: 45 additions & 10 deletions internal/command/routegroup/printers.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func printRouteGroupTable(cfg *config.RouteGroupList, out io.Writer, rgs []*mode
t.AddRow(routegroupRow{
Name: rg.Name,
RoutingKey: rg.RoutingKey,
Cluster: rg.Spec.Cluster,
Cluster: getCluster(rg.Spec.Cluster),
Created: timeago.NoMax(timeago.English).Format(createdAt),
Status: readiness(rg.Status),
Ready: sbxStatus,
Expand All @@ -55,7 +55,7 @@ func printRouteGroupDetails(cfg *config.RouteGroup, out io.Writer, rg *models.Ro

fmt.Fprintf(tw, "Name:\t%s\n", rg.Name)
fmt.Fprintf(tw, "Routing Key:\t%s\n", rg.RoutingKey)
fmt.Fprintf(tw, "Cluster:\t%s\n", rg.Spec.Cluster)
fmt.Fprintf(tw, "Cluster:\t%s\n", getCluster(rg.Spec.Cluster))
fmt.Fprintf(tw, "Created:\t%s\n", utils.FormatTimestamp(rg.CreatedAt))
fmt.Fprintf(tw, "TTL:\t%s\n", formatTTL(rg.Spec, rg.Status.ScheduledDeleteTime))
fmt.Fprintf(tw, "Dashboard page:\t%s\n", cfg.DashboardURL)
Expand All @@ -67,14 +67,21 @@ func printRouteGroupDetails(cfg *config.RouteGroup, out io.Writer, rg *models.Ro

if len(rg.Endpoints) > 0 {
fmt.Fprintln(out)
if err := printEndpointTable(out, rg.Endpoints); err != nil {
if err := printEndpointTable(out, rg); err != nil {
return err
}
}

return nil
}

func getCluster(cluster string) string {
if cluster != "" {
return cluster
}
return "(multi-cluster)"
}

func readiness(status *models.RouteGroupStatus) string {
if status.Ready {
return "Ready"
Expand Down Expand Up @@ -119,21 +126,49 @@ func formatTTL(spec *models.RouteGroupSpec, deletionTime string) string {
return fmt.Sprintf("%s (%s)", local, timeago.NoMax(timeago.English).Format(t))
}

type endpointRow struct {
Name string `sdtab:"ROUTEGROUP ENDPOINT"`
Target string `sdtab:"TARGET"`
URL string `sdtab:"URL"`
func printEndpointTable(out io.Writer, rg *models.RouteGroup) error {
if rg.Spec.Cluster == "" {
return printMultiClusterRGEndpointTable(out, rg.Endpoints)
}
return printRegularRGEndpointTable(out, rg.Endpoints)
}

func printEndpointTable(out io.Writer, endpoints []*models.RoutegroupsEndpointURL) error {
t := sdtab.New[endpointRow](out)
func printRegularRGEndpointTable(out io.Writer, endpoints []*models.RoutegroupsEndpointURL) error {
type row struct {
Name string `sdtab:"ROUTEGROUP ENDPOINT"`
Target string `sdtab:"TARGET"`
URL string `sdtab:"URL"`
}

t := sdtab.New[row](out)
t.AddHeader()
for _, ep := range endpoints {
t.AddRow(endpointRow{
t.AddRow(row{
Name: ep.Name,
Target: ep.Target,
URL: ep.URL,
})
}
return t.Flush()
}

func printMultiClusterRGEndpointTable(out io.Writer, endpoints []*models.RoutegroupsEndpointURL) error {
type row struct {
Name string `sdtab:"ROUTEGROUP ENDPOINT"`
Cluster string `sdtab:"CLUSTER"`
Target string `sdtab:"TARGET"`
URL string `sdtab:"URL"`
}

t := sdtab.New[row](out)
t.AddHeader()
for _, ep := range endpoints {
t.AddRow(row{
Name: ep.Name,
Cluster: ep.Cluster,
Target: ep.Target,
URL: ep.URL,
})
}
return t.Flush()
}
48 changes: 27 additions & 21 deletions internal/command/smarttest/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package smarttest

import (
"context"
"errors"
"fmt"
"io"
"os"
Expand Down Expand Up @@ -99,26 +100,14 @@ func validateRun(cfg *config.SmartTestRun) error {
if err := validateList(cfg.SmartTestList); err != nil {
return err
}
count := 0
if cfg.Cluster != "" {
count++
}
if cfg.Sandbox != "" {
count++
}
if cfg.RouteGroup != "" {
count++
}

if count == 0 {
return fmt.Errorf("you must specify one of '--cluster', '--sandbox' or '--route-group'")
}
if count > 1 {
return fmt.Errorf("only one of '--cluster', '--sandbox' or '--route-group' should be specified")
if err := cfg.Validate(); err != nil {
return err
}

// load the corresponding entity from the API
if cfg.Sandbox != "" {
switch {
case cfg.Sandbox != "":
// validate that the sandbox exists and resolve the cluster based on it
params := sandboxes.NewGetSandboxParams().
WithOrgName(cfg.Org).WithSandboxName(cfg.Sandbox)
resp, err := cfg.Client.Sandboxes.GetSandbox(params, nil)
Expand All @@ -127,16 +116,33 @@ func validateRun(cfg *config.SmartTestRun) error {
}
// store the cluster for later use
cfg.Cluster = *resp.Payload.Spec.Cluster
} else if cfg.RouteGroup != "" {

case cfg.RouteGroup != "":
// validate that the route group exists and try resolving the cluster
// based on it
params := routegroups.NewGetRoutegroupParams().
WithOrgName(cfg.Org).WithRoutegroupName(cfg.RouteGroup)
resp, err := cfg.Client.RouteGroups.GetRoutegroup(params, nil)
if err != nil {
return fmt.Errorf("failed to load routegroup %q: %v", cfg.RouteGroup, err)
}
// store the cluster for later use
cfg.Cluster = resp.Payload.Spec.Cluster
} else {
if resp.Payload.Spec.Cluster != "" {
// store the cluster for later use
cfg.Cluster = resp.Payload.Spec.Cluster
} else {
// this is a multi-cluster RG, the cluster must be explicitly defined
if cfg.Cluster == "" {
return errors.New("--cluster must be specified in multi-cluster route groups")
}
// validate the cluster exists
params := clusters.NewGetClusterParams().
WithOrgName(cfg.Org).WithClusterName(cfg.Cluster)
if _, err := cfg.Client.Cluster.GetCluster(params, nil); err != nil {
return fmt.Errorf("failed to load cluster %q: %v", cfg.Cluster, err)
}
}

default:
// validate the cluster exists
params := clusters.NewGetClusterParams().
WithOrgName(cfg.Org).WithClusterName(cfg.Cluster)
Expand Down
7 changes: 7 additions & 0 deletions internal/config/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,17 @@ func (lp *LocalProxy) Validate() error {
if c == 0 {
return errors.New("you should specify one of '--sandbox', '--routegroup' or '--cluster'")
}
// Allow routeGroup + cluster combination
if c == 2 && lp.RouteGroup != "" && lp.Cluster != "" {
return lp.validateProxyMappings()
}
if c > 1 {
return errors.New("only one of '--sandbox', '--routegroup' or '--cluster' should be specified")
}
return lp.validateProxyMappings()
}

func (lp *LocalProxy) validateProxyMappings() error {
for i := range lp.ProxyMappings {
pm := &lp.ProxyMappings[i]
if err := pm.Validate(); err != nil {
Expand Down
26 changes: 26 additions & 0 deletions internal/config/smarttest.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package config

import (
"bytes"
"errors"
"fmt"
"sort"
"strings"
Expand Down Expand Up @@ -45,6 +46,31 @@ type SmartTestRun struct {
NoWait bool
}

func (smr *SmartTestRun) Validate() error {
c := 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably renaming this var?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well.. I think this is a matter of taste. I named it like that because we already had this code:

func (lp *LocalProxy) Validate() error {
c := 0
if lp.Sandbox != "" {
c += 1
}
if lp.RouteGroup != "" {
c += 1
}
if lp.Cluster != "" {
c += 1
}

And I wanted it to look consistent.

if smr.Cluster != "" {
c++
}
if smr.Sandbox != "" {
c++
}
if smr.RouteGroup != "" {
c++
}

if c == 0 {
return errors.New("you must specify one of '--cluster', '--sandbox' or '--route-group'")
}
// Allow routeGroup + cluster combination
if c == 2 && smr.RouteGroup != "" && smr.Cluster != "" {
return nil
}
if c > 1 {
return errors.New("only one of '--cluster', '--sandbox' or '--route-group' should be specified")
}
return nil
}

type TestExecLabels map[string]string

func (rl TestExecLabels) String() string {
Expand Down