From 686081e0a5e6009a0d22899b00b82c15ac27a951 Mon Sep 17 00:00:00 2001 From: Josh De Winne Date: Sat, 25 Jan 2025 21:44:46 -0800 Subject: [PATCH] Add network update outbound --- cli/cmd/network.go | 57 +++++++++++++++++++++++ cli/cmd/network_update.go | 56 ++++++++++++++++++++++ cli/cmd/network_update_outbound.go | 52 +++++++++++++++++++++ cli/cmd/root.go | 3 ++ cli/cmd/runner.go | 5 ++ cli/print/networks.go | 8 ++-- pkg/kotsclient/network_update_outbound.go | 41 ++++++++++++++++ pkg/types/network.go | 2 + 8 files changed, 220 insertions(+), 4 deletions(-) create mode 100644 cli/cmd/network_update.go create mode 100644 cli/cmd/network_update_outbound.go create mode 100644 pkg/kotsclient/network_update_outbound.go diff --git a/cli/cmd/network.go b/cli/cmd/network.go index 65d5733a..9e76e202 100644 --- a/cli/cmd/network.go +++ b/cli/cmd/network.go @@ -1,6 +1,9 @@ package cmd import ( + "github.com/replicatedhq/replicated/pkg/credentials" + "github.com/replicatedhq/replicated/pkg/kotsclient" + "github.com/replicatedhq/replicated/pkg/platformclient" "github.com/spf13/cobra" ) @@ -15,3 +18,57 @@ func (r *runners) InitNetworkCommand(parent *cobra.Command) *cobra.Command { return cmd } + +func (r *runners) initNetworkClient() error { + if apiToken == "" { + creds, err := credentials.GetCurrentCredentials() + if err != nil { + return err + } + + apiToken = creds.APIToken + } + + httpClient := platformclient.NewHTTPClient(platformOrigin, apiToken) + kotsAPI := &kotsclient.VendorV3Client{HTTPClient: *httpClient} + r.kotsAPI = kotsAPI + return nil +} + +func (r *runners) completeNetworkNames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + err := r.initNetworkClient() + if err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + networks, err := r.kotsAPI.ListNetworks(nil, nil) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + var names []string + for _, network := range networks { + if network.Name != "" { + names = append(names, network.Name) + } + } + return names, cobra.ShellCompDirectiveNoFileComp +} + +func (r *runners) completeNetworkIDs(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + err := r.initNetworkClient() + if err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + networks, err := r.kotsAPI.ListNetworks(nil, nil) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + var ids []string + for _, network := range networks { + ids = append(ids, network.ID) + } + return ids, cobra.ShellCompDirectiveNoFileComp +} diff --git a/cli/cmd/network_update.go b/cli/cmd/network_update.go new file mode 100644 index 00000000..50b09348 --- /dev/null +++ b/cli/cmd/network_update.go @@ -0,0 +1,56 @@ +package cmd + +import ( + "github.com/pkg/errors" + "github.com/replicatedhq/replicated/pkg/platformclient" + "github.com/spf13/cobra" +) + +func (r *runners) InitNetworkUpdateCommand(parent *cobra.Command) *cobra.Command { + cmd := &cobra.Command{ + Use: "update", + Short: "Update network settings.", + Long: `The 'update' command allows you to update various settings of a test network. + +You can either specify the network ID directly or provide the network name, and the command will resolve the corresponding network ID.`, + Example: `# Update a network using its ID +replicated network update --id [subcommand] + +# Update a network using its name +replicated network update --name [subcommand]`, + } + parent.AddCommand(cmd) + + cmd.PersistentFlags().StringVar(&r.args.updateNetworkName, "name", "", "Name of the network to update.") + cmd.RegisterFlagCompletionFunc("name", r.completeNetworkNames) + + cmd.PersistentFlags().StringVar(&r.args.updateNetworkID, "id", "", "id of the network to update (when name is not provided)") + cmd.RegisterFlagCompletionFunc("id", r.completeNetworkIDs) + + return cmd +} + +func (r *runners) ensureUpdateNetworkIDArg(args []string) error { + if len(args) > 0 { + r.args.updateNetworkID = args[0] + } else if r.args.updateNetworkName != "" { + networks, err := r.kotsAPI.ListNetworks(nil, nil) + if errors.Cause(err) == platformclient.ErrForbidden { + return ErrCompatibilityMatrixTermsNotAccepted + } else if err != nil { + return errors.Wrap(err, "list networks") + } + for _, network := range networks { + if network.Name == r.args.updateNetworkName { + r.args.updateNetworkID = network.ID + break + } + } + } else if r.args.updateNetworkID != "" { + // do nothing + } else { + return errors.New("must provide network id or name") + } + + return nil +} diff --git a/cli/cmd/network_update_outbound.go b/cli/cmd/network_update_outbound.go new file mode 100644 index 00000000..5e4f3354 --- /dev/null +++ b/cli/cmd/network_update_outbound.go @@ -0,0 +1,52 @@ +package cmd + +import ( + "github.com/pkg/errors" + "github.com/replicatedhq/replicated/cli/print" + "github.com/replicatedhq/replicated/pkg/kotsclient" + "github.com/replicatedhq/replicated/pkg/platformclient" + "github.com/spf13/cobra" +) + +func (r *runners) InitNetworkUpdateOutbound(parent *cobra.Command) *cobra.Command { + cmd := &cobra.Command{ + Use: "outbound [ID]", + Short: "Update outbound setting for a test network.", + Long: `The 'outbound' command allows you to update the outbound setting of a test network. The outbound setting can be either 'none' or 'any'.`, + Example: `# Update the outbound setting for a specific network +replicated network update outbound NETWORK_ID --outbound any`, + RunE: r.updateNetworkOutbound, + SilenceUsage: true, + ValidArgsFunction: r.completeNetworkIDs, + } + parent.AddCommand(cmd) + + cmd.Flags().StringVar(&r.args.updateNetworkOutbound, "outbound", "", "Update outbound setting (must be 'none' or 'any')") + cmd.Flags().StringVar(&r.outputFormat, "output", "table", "The output format to use. One of: json|table|wide (default: table)") + + cmd.MarkFlagRequired("outbound") + + return cmd +} + +func (r *runners) updateNetworkOutbound(cmd *cobra.Command, args []string) error { + if err := r.ensureUpdateNetworkIDArg(args); err != nil { + return errors.Wrap(err, "ensure network id arg") + } + + if r.args.updateNetworkOutbound != "none" && r.args.updateNetworkOutbound != "any" { + return errors.New("outbound must be either 'none' or 'any'") + } + + opts := kotsclient.UpdateNetworkOutboundOpts{ + Outbound: r.args.updateNetworkOutbound, + } + network, err := r.kotsAPI.UpdateNetworkOutbound(r.args.updateNetworkID, opts) + if errors.Cause(err) == platformclient.ErrForbidden { + return ErrCompatibilityMatrixTermsNotAccepted + } else if err != nil { + return errors.Wrap(err, "update network outbound") + } + + return print.Network(r.outputFormat, r.w, network) +} diff --git a/cli/cmd/root.go b/cli/cmd/root.go index c175a18a..f664c3cf 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -273,6 +273,9 @@ func Execute(rootCmd *cobra.Command, stdin io.Reader, stdout io.Writer, stderr i runCmds.InitNetworkList(networkCmd) runCmds.InitNetworkRemove(networkCmd) runCmds.InitNetworkJoin(networkCmd) + + networkUpdateCmd := runCmds.InitNetworkUpdateCommand(networkCmd) + runCmds.InitNetworkUpdateOutbound(networkUpdateCmd) runCmds.InitLoginCommand(runCmds.rootCmd) runCmds.InitLogoutCommand(runCmds.rootCmd) diff --git a/cli/cmd/runner.go b/cli/cmd/runner.go index 2b34f5a5..0750ee7d 100644 --- a/cli/cmd/runner.go +++ b/cli/cmd/runner.go @@ -245,6 +245,11 @@ type runnerArgs struct { lsNetworkEndTime string lsNetworkWatch bool + updateNetworkOutbound string + + updateNetworkName string + updateNetworkID string + clusterAddonCreateObjectStoreBucket string clusterAddonCreateObjectStoreClusterID string clusterAddonCreateObjectStoreDuration time.Duration diff --git a/cli/print/networks.go b/cli/print/networks.go index a8a30a5c..d52693b9 100644 --- a/cli/print/networks.go +++ b/cli/print/networks.go @@ -10,18 +10,18 @@ import ( ) // Table formatting -var networksTmplTableHeaderSrc = `ID NAME STATUS CREATED EXPIRES` +var networksTmplTableHeaderSrc = `ID NAME STATUS CREATED EXPIRES OUTBOUND` var networksTmplTableRowSrc = `{{ range . -}} -{{ .ID }} {{ padding .Name 27 }} {{ padding (printf "%s" .Status) 12 }} {{ padding (printf "%s" (localeTime .CreatedAt)) 30 }} {{if .ExpiresAt.IsZero}}{{ padding "-" 30 }}{{else}}{{ padding (printf "%s" (localeTime .ExpiresAt)) 30 }}{{end}} +{{ .ID }} {{ padding .Name 27 }} {{ padding (printf "%s" .Status) 12 }} {{ padding (printf "%s" (localeTime .CreatedAt)) 30 }} {{if .ExpiresAt.IsZero}}{{ padding "-" 30 }}{{else}}{{ padding (printf "%s" (localeTime .ExpiresAt)) 30 }}{{end}} {{if eq .Outbound ""}}{{ padding "-" 30 }}{{else}}{{ padding (printf "%s" (.Outbound)) 30 }}{{end}} {{ end }}` var networksTmplTableSrc = fmt.Sprintln(networksTmplTableHeaderSrc) + networksTmplTableRowSrc var networksTmplTable = template.Must(template.New("networks").Funcs(funcs).Parse(networksTmplTableSrc)) var networksTmplTableNoHeader = template.Must(template.New("networks").Funcs(funcs).Parse(networksTmplTableRowSrc)) // Wide table formatting -var networksTmplWideHeaderSrc = `ID NAME STATUS CREATED EXPIRES` +var networksTmplWideHeaderSrc = `ID NAME STATUS CREATED EXPIRES OUTBOUND` var networksTmplWideRowSrc = `{{ range . -}} -{{ .ID }} {{ padding .Name 27 }} {{ padding (printf "%s" .Status) 12 }} {{ padding (printf "%s" (localeTime .CreatedAt)) 30 }} {{if .ExpiresAt.IsZero}}{{ padding "-" 30 }}{{else}}{{ padding (printf "%s" (localeTime .ExpiresAt)) 30 }}{{end}} +{{ .ID }} {{ padding .Name 27 }} {{ padding (printf "%s" .Status) 12 }} {{ padding (printf "%s" (localeTime .CreatedAt)) 30 }} {{if .ExpiresAt.IsZero}}{{ padding "-" 30 }}{{else}}{{ padding (printf "%s" (localeTime .ExpiresAt)) 30 }}{{end}} {{if eq .Outbound ""}}{{ padding "-" 30 }}{{else}}{{ padding (printf "%s" (.Outbound)) 30 }}{{end}} {{ end }}` var networksTmplWideSrc = fmt.Sprintln(networksTmplWideHeaderSrc) + networksTmplWideRowSrc var networksTmplWide = template.Must(template.New("networks").Funcs(funcs).Parse(networksTmplWideSrc)) diff --git a/pkg/kotsclient/network_update_outbound.go b/pkg/kotsclient/network_update_outbound.go new file mode 100644 index 00000000..332bb768 --- /dev/null +++ b/pkg/kotsclient/network_update_outbound.go @@ -0,0 +1,41 @@ +package kotsclient + +import ( + "context" + "fmt" + "net/http" + + "github.com/replicatedhq/replicated/pkg/types" +) + +type UpdateNetworkOutboundRequest struct { + Outbound string `json:"outbound"` +} + +type UpdateNetworkOutboundResponse struct { + Network *types.Network `json:"network"` + Errors []string `json:"errors"` +} + +type UpdateNetworkOutboundOpts struct { + Outbound string +} + +func (c *VendorV3Client) UpdateNetworkOutbound(networkID string, opts UpdateNetworkOutboundOpts) (*types.Network, error) { + req := UpdateNetworkOutboundRequest{ + Outbound: opts.Outbound, + } + + return c.doUpdateNetworkOutboundRequest(networkID, req) +} + +func (c *VendorV3Client) doUpdateNetworkOutboundRequest(networkID string, req UpdateNetworkOutboundRequest) (*types.Network, error) { + resp := UpdateNetworkOutboundResponse{} + endpoint := fmt.Sprintf("/v3/network/%s/outbound", networkID) + err := c.DoJSON(context.TODO(), "PUT", endpoint, http.StatusOK, req, &resp) + if err != nil { + return nil, err + } + + return resp.Network, nil +} diff --git a/pkg/types/network.go b/pkg/types/network.go index 72c9fb4d..6a9c21f9 100644 --- a/pkg/types/network.go +++ b/pkg/types/network.go @@ -14,6 +14,8 @@ type Network struct { OverlayEndpoint string `json:"overlay_endpoint,omitempty"` OverlayToken string `json:"overlay_token,omitempty"` + + Outbound string `json:"outbound,omitempty"` } type NetworkStatus string